highcharts-gantt.src.js 2.4 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984
  1. /**
  2. * @license Highcharts Gantt JS v8.2.0 (2020-08-20)
  3. *
  4. * (c) 2017-2018 Lars Cabrera, Torstein Honsi, Jon Arild Nygard & Oystein Moseng
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. (function (root, factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. factory['default'] = factory;
  12. module.exports = root.document ?
  13. factory(root) :
  14. factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/highcharts-gantt', function () {
  17. return factory(root);
  18. });
  19. } else {
  20. if (root.Highcharts) {
  21. root.Highcharts.error(16, true);
  22. }
  23. root.Highcharts = factory(root);
  24. }
  25. }(typeof window !== 'undefined' ? window : this, function (win) {
  26. var _modules = {};
  27. function _registerModule(obj, path, args, fn) {
  28. if (!obj.hasOwnProperty(path)) {
  29. obj[path] = fn.apply(null, args);
  30. }
  31. }
  32. _registerModule(_modules, 'Core/Globals.js', [], function () {
  33. /* *
  34. *
  35. * (c) 2010-2020 Torstein Honsi
  36. *
  37. * License: www.highcharts.com/license
  38. *
  39. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40. *
  41. * */
  42. /* globals Image, window */
  43. /**
  44. * Reference to the global SVGElement class as a workaround for a name conflict
  45. * in the Highcharts namespace.
  46. *
  47. * @global
  48. * @typedef {global.SVGElement} GlobalSVGElement
  49. *
  50. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  51. */
  52. // glob is a temporary fix to allow our es-modules to work.
  53. var glob = ( // @todo UMD variable named `window`, and glob named `win`
  54. typeof win !== 'undefined' ?
  55. win :
  56. typeof window !== 'undefined' ?
  57. window :
  58. {}), doc = glob.document, SVG_NS = 'http://www.w3.org/2000/svg', userAgent = (glob.navigator && glob.navigator.userAgent) || '', svg = (doc &&
  59. doc.createElementNS &&
  60. !!doc.createElementNS(SVG_NS, 'svg').createSVGRect), isMS = /(edge|msie|trident)/i.test(userAgent) && !glob.opera, isFirefox = userAgent.indexOf('Firefox') !== -1, isChrome = userAgent.indexOf('Chrome') !== -1, hasBidiBug = (isFirefox &&
  61. parseInt(userAgent.split('Firefox/')[1], 10) < 4 // issue #38
  62. );
  63. var H = {
  64. product: 'Highcharts',
  65. version: '8.2.0',
  66. deg2rad: Math.PI * 2 / 360,
  67. doc: doc,
  68. hasBidiBug: hasBidiBug,
  69. hasTouch: !!glob.TouchEvent,
  70. isMS: isMS,
  71. isWebKit: userAgent.indexOf('AppleWebKit') !== -1,
  72. isFirefox: isFirefox,
  73. isChrome: isChrome,
  74. isSafari: !isChrome && userAgent.indexOf('Safari') !== -1,
  75. isTouchDevice: /(Mobile|Android|Windows Phone)/.test(userAgent),
  76. SVG_NS: SVG_NS,
  77. chartCount: 0,
  78. seriesTypes: {},
  79. symbolSizes: {},
  80. svg: svg,
  81. win: glob,
  82. marginNames: ['plotTop', 'marginRight', 'marginBottom', 'plotLeft'],
  83. noop: function () { },
  84. /**
  85. * Theme options that should get applied to the chart. In module mode it
  86. * might not be possible to change this property because of read-only
  87. * restrictions, instead use {@link Highcharts.setOptions}.
  88. *
  89. * @name Highcharts.theme
  90. * @type {Highcharts.Options}
  91. */
  92. /**
  93. * An array containing the current chart objects in the page. A chart's
  94. * position in the array is preserved throughout the page's lifetime. When
  95. * a chart is destroyed, the array item becomes `undefined`.
  96. *
  97. * @name Highcharts.charts
  98. * @type {Array<Highcharts.Chart|undefined>}
  99. */
  100. charts: [],
  101. /**
  102. * A hook for defining additional date format specifiers. New
  103. * specifiers are defined as key-value pairs by using the
  104. * specifier as key, and a function which takes the timestamp as
  105. * value. This function returns the formatted portion of the
  106. * date.
  107. *
  108. * @sample highcharts/global/dateformats/
  109. * Adding support for week number
  110. *
  111. * @name Highcharts.dateFormats
  112. * @type {Highcharts.Dictionary<Highcharts.TimeFormatCallbackFunction>}
  113. */
  114. dateFormats: {}
  115. };
  116. return H;
  117. });
  118. _registerModule(_modules, 'Core/Utilities.js', [_modules['Core/Globals.js']], function (H) {
  119. /* *
  120. *
  121. * (c) 2010-2020 Torstein Honsi
  122. *
  123. * License: www.highcharts.com/license
  124. *
  125. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  126. *
  127. * */
  128. /**
  129. * An animation configuration. Animation configurations can also be defined as
  130. * booleans, where `false` turns off animation and `true` defaults to a duration
  131. * of 500ms and defer of 0ms.
  132. *
  133. * @interface Highcharts.AnimationOptionsObject
  134. */ /**
  135. * A callback function to exectute when the animation finishes.
  136. * @name Highcharts.AnimationOptionsObject#complete
  137. * @type {Function|undefined}
  138. */ /**
  139. * The animation defer in milliseconds.
  140. * @name Highcharts.AnimationOptionsObject#defer
  141. * @type {number|undefined}
  142. */ /**
  143. * The animation duration in milliseconds.
  144. * @name Highcharts.AnimationOptionsObject#duration
  145. * @type {number|undefined}
  146. */ /**
  147. * The name of an easing function as defined on the `Math` object.
  148. * @name Highcharts.AnimationOptionsObject#easing
  149. * @type {string|Function|undefined}
  150. */ /**
  151. * A callback function to execute on each step of each attribute or CSS property
  152. * that's being animated. The first argument contains information about the
  153. * animation and progress.
  154. * @name Highcharts.AnimationOptionsObject#step
  155. * @type {Function|undefined}
  156. */
  157. /**
  158. * Creates a frame for the animated SVG element.
  159. *
  160. * @callback Highcharts.AnimationStepCallbackFunction
  161. *
  162. * @param {Highcharts.SVGElement} this
  163. * The SVG element to animate.
  164. *
  165. * @return {void}
  166. */
  167. /**
  168. * Interface description for a class.
  169. *
  170. * @interface Highcharts.Class<T>
  171. * @extends Function
  172. */ /**
  173. * Class costructor.
  174. * @function Highcharts.Class<T>#new
  175. * @param {...Array<*>} args
  176. * Constructor arguments.
  177. * @return {T}
  178. * Class instance.
  179. */
  180. /**
  181. * A style object with camel case property names to define visual appearance of
  182. * a SVG element or HTML element. The properties can be whatever styles are
  183. * supported on the given SVG or HTML element.
  184. *
  185. * @example
  186. * {
  187. * fontFamily: 'monospace',
  188. * fontSize: '1.2em'
  189. * }
  190. *
  191. * @interface Highcharts.CSSObject
  192. */ /**
  193. * @name Highcharts.CSSObject#[key:string]
  194. * @type {boolean|number|string|undefined}
  195. */ /**
  196. * Background style for the element.
  197. * @name Highcharts.CSSObject#background
  198. * @type {string|undefined}
  199. */ /**
  200. * Background color of the element.
  201. * @name Highcharts.CSSObject#backgroundColor
  202. * @type {Highcharts.ColorString|undefined}
  203. */ /**
  204. * Border style for the element.
  205. * @name Highcharts.CSSObject#border
  206. * @type {string|undefined}
  207. */ /**
  208. * Radius of the element border.
  209. * @name Highcharts.CSSObject#borderRadius
  210. * @type {number|undefined}
  211. */ /**
  212. * Color used in the element. The 'contrast' option is a Highcharts custom
  213. * property that results in black or white, depending on the background of the
  214. * element.
  215. * @name Highcharts.CSSObject#color
  216. * @type {'contrast'|Highcharts.ColorString|undefined}
  217. */ /**
  218. * Style of the mouse cursor when resting over the element.
  219. * @name Highcharts.CSSObject#cursor
  220. * @type {Highcharts.CursorValue|undefined}
  221. */ /**
  222. * Font family of the element text. Multiple values have to be in decreasing
  223. * preference order and separated by comma.
  224. * @name Highcharts.CSSObject#fontFamily
  225. * @type {string|undefined}
  226. */ /**
  227. * Font size of the element text.
  228. * @name Highcharts.CSSObject#fontSize
  229. * @type {string|undefined}
  230. */ /**
  231. * Font weight of the element text.
  232. * @name Highcharts.CSSObject#fontWeight
  233. * @type {string|undefined}
  234. */ /**
  235. * Height of the element.
  236. * @name Highcharts.CSSObject#height
  237. * @type {number|undefined}
  238. */ /**
  239. * Width of the element border.
  240. * @name Highcharts.CSSObject#lineWidth
  241. * @type {number|undefined}
  242. */ /**
  243. * Opacity of the element.
  244. * @name Highcharts.CSSObject#opacity
  245. * @type {number|undefined}
  246. */ /**
  247. * Space around the element content.
  248. * @name Highcharts.CSSObject#padding
  249. * @type {string|undefined}
  250. */ /**
  251. * Behaviour of the element when the mouse cursor rests over it.
  252. * @name Highcharts.CSSObject#pointerEvents
  253. * @type {string|undefined}
  254. */ /**
  255. * Positioning of the element.
  256. * @name Highcharts.CSSObject#position
  257. * @type {string|undefined}
  258. */ /**
  259. * Alignment of the element text.
  260. * @name Highcharts.CSSObject#textAlign
  261. * @type {string|undefined}
  262. */ /**
  263. * Additional decoration of the element text.
  264. * @name Highcharts.CSSObject#textDecoration
  265. * @type {string|undefined}
  266. */ /**
  267. * Outline style of the element text.
  268. * @name Highcharts.CSSObject#textOutline
  269. * @type {string|undefined}
  270. */ /**
  271. * Line break style of the element text. Highcharts SVG elements support
  272. * `ellipsis` when a `width` is set.
  273. * @name Highcharts.CSSObject#textOverflow
  274. * @type {string|undefined}
  275. */ /**
  276. * Top spacing of the element relative to the parent element.
  277. * @name Highcharts.CSSObject#top
  278. * @type {string|undefined}
  279. */ /**
  280. * Animated transition of selected element properties.
  281. * @name Highcharts.CSSObject#transition
  282. * @type {string|undefined}
  283. */ /**
  284. * Line break style of the element text.
  285. * @name Highcharts.CSSObject#whiteSpace
  286. * @type {string|undefined}
  287. */ /**
  288. * Width of the element.
  289. * @name Highcharts.CSSObject#width
  290. * @type {number|undefined}
  291. */
  292. /**
  293. * All possible cursor styles.
  294. *
  295. * @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
  296. */
  297. /**
  298. * All possible dash styles.
  299. *
  300. * @typedef {'Dash'|'DashDot'|'Dot'|'LongDash'|'LongDashDot'|'LongDashDotDot'|'ShortDash'|'ShortDashDot'|'ShortDashDotDot'|'ShortDot'|'Solid'} Highcharts.DashStyleValue
  301. */
  302. /**
  303. * Generic dictionary in TypeScript notation.
  304. * Use the native `Record<string, any>` instead.
  305. *
  306. * @deprecated
  307. * @interface Highcharts.Dictionary<T>
  308. */ /**
  309. * @name Highcharts.Dictionary<T>#[key:string]
  310. * @type {T}
  311. */
  312. /**
  313. * The function callback to execute when the event is fired. The `this` context
  314. * contains the instance, that fired the event.
  315. *
  316. * @callback Highcharts.EventCallbackFunction<T>
  317. *
  318. * @param {T} this
  319. *
  320. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  321. * Event arguments.
  322. *
  323. * @return {boolean|void}
  324. */
  325. /**
  326. * The event options for adding function callback.
  327. *
  328. * @interface Highcharts.EventOptionsObject
  329. */ /**
  330. * The order the event handler should be called. This opens for having one
  331. * handler be called before another, independent of in which order they were
  332. * added.
  333. * @name Highcharts.EventOptionsObject#order
  334. * @type {number}
  335. */
  336. /**
  337. * Formats data as a string. Usually the data is accessible throught the `this`
  338. * keyword.
  339. *
  340. * @callback Highcharts.FormatterCallbackFunction<T>
  341. *
  342. * @param {T} this
  343. * Context to format
  344. *
  345. * @return {string}
  346. * Formatted text
  347. */
  348. /**
  349. * An object of key-value pairs for HTML attributes.
  350. *
  351. * @typedef {Highcharts.Dictionary<boolean|number|string|Function>} Highcharts.HTMLAttributes
  352. */
  353. /**
  354. * An HTML DOM element. The type is a reference to the regular HTMLElement in
  355. * the global scope.
  356. *
  357. * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
  358. *
  359. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
  360. */
  361. /**
  362. * The iterator callback.
  363. *
  364. * @callback Highcharts.ObjectEachCallbackFunction<T>
  365. *
  366. * @param {T} this
  367. * The context.
  368. *
  369. * @param {*} value
  370. * The property value.
  371. *
  372. * @param {string} key
  373. * The property key.
  374. *
  375. * @param {*} obj
  376. * The object that objectEach is being applied to.
  377. */
  378. /**
  379. * An object containing `left` and `top` properties for the position in the
  380. * page.
  381. *
  382. * @interface Highcharts.OffsetObject
  383. */ /**
  384. * Left distance to the page border.
  385. * @name Highcharts.OffsetObject#left
  386. * @type {number}
  387. */ /**
  388. * Top distance to the page border.
  389. * @name Highcharts.OffsetObject#top
  390. * @type {number}
  391. */
  392. /**
  393. * Describes a range.
  394. *
  395. * @interface Highcharts.RangeObject
  396. */ /**
  397. * Maximum number of the range.
  398. * @name Highcharts.RangeObject#max
  399. * @type {number}
  400. */ /**
  401. * Minimum number of the range.
  402. * @name Highcharts.RangeObject#min
  403. * @type {number}
  404. */
  405. /**
  406. * If a number is given, it defines the pixel length. If a percentage string is
  407. * given, like for example `'50%'`, the setting defines a length relative to a
  408. * base size, for example the size of a container.
  409. *
  410. * @typedef {number|string} Highcharts.RelativeSize
  411. */
  412. /**
  413. * Proceed function to call original (wrapped) function.
  414. *
  415. * @callback Highcharts.WrapProceedFunction
  416. *
  417. * @param {*} [arg1]
  418. * Optional argument. Without any arguments defaults to first argument of
  419. * the wrapping function.
  420. *
  421. * @param {*} [arg2]
  422. * Optional argument. Without any arguments defaults to second argument
  423. * of the wrapping function.
  424. *
  425. * @param {*} [arg3]
  426. * Optional argument. Without any arguments defaults to third argument of
  427. * the wrapping function.
  428. *
  429. * @return {*}
  430. * Return value of the original function.
  431. */
  432. /**
  433. * The Highcharts object is the placeholder for all other members, and various
  434. * utility functions. The most important member of the namespace would be the
  435. * chart constructor.
  436. *
  437. * @example
  438. * var chart = Highcharts.chart('container', { ... });
  439. *
  440. * @namespace Highcharts
  441. */
  442. H.timers = [];
  443. var charts = H.charts,
  444. doc = H.doc,
  445. win = H.win;
  446. /**
  447. * Provide error messages for debugging, with links to online explanation. This
  448. * function can be overridden to provide custom error handling.
  449. *
  450. * @sample highcharts/chart/highcharts-error/
  451. * Custom error handler
  452. *
  453. * @function Highcharts.error
  454. *
  455. * @param {number|string} code
  456. * The error code. See
  457. * [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
  458. * for available codes. If it is a string, the error message is printed
  459. * directly in the console.
  460. *
  461. * @param {boolean} [stop=false]
  462. * Whether to throw an error or just log a warning in the console.
  463. *
  464. * @param {Highcharts.Chart} [chart]
  465. * Reference to the chart that causes the error. Used in 'debugger'
  466. * module to display errors directly on the chart.
  467. * Important note: This argument is undefined for errors that lack
  468. * access to the Chart instance.
  469. *
  470. * @param {Highcharts.Dictionary<string>} [params]
  471. * Additional parameters for the generated message.
  472. *
  473. * @return {void}
  474. */
  475. function error(code, stop, chart, params) {
  476. var severity = stop ? 'Highcharts error' : 'Highcharts warning';
  477. if (code === 32) {
  478. code = severity + ": Deprecated member";
  479. }
  480. var isCode = isNumber(code),
  481. message = isCode ?
  482. severity + " #" + code + ": www.highcharts.com/errors/" + code + "/" :
  483. code.toString(),
  484. defaultHandler = function () {
  485. if (stop) {
  486. throw new Error(message);
  487. }
  488. // else ...
  489. if (win.console &&
  490. error.messages.indexOf(message) === -1 // prevent console flooting
  491. ) {
  492. console.log(message); // eslint-disable-line no-console
  493. }
  494. };
  495. if (typeof params !== 'undefined') {
  496. var additionalMessages_1 = '';
  497. if (isCode) {
  498. message += '?';
  499. }
  500. objectEach(params, function (value, key) {
  501. additionalMessages_1 += "\n - " + key + ": " + value;
  502. if (isCode) {
  503. message += encodeURI(key) + '=' + encodeURI(value);
  504. }
  505. });
  506. message += additionalMessages_1;
  507. }
  508. if (chart) {
  509. fireEvent(chart, 'displayError', { code: code, message: message, params: params }, defaultHandler);
  510. }
  511. else {
  512. defaultHandler();
  513. }
  514. error.messages.push(message);
  515. }
  516. (function (error) {
  517. error.messages = [];
  518. })(error || (error = {}));
  519. H.error = error;
  520. /* eslint-disable no-invalid-this, valid-jsdoc */
  521. /**
  522. * An animator object used internally. One instance applies to one property
  523. * (attribute or style prop) on one element. Animation is always initiated
  524. * through {@link SVGElement#animate}.
  525. *
  526. * @example
  527. * var rect = renderer.rect(0, 0, 10, 10).add();
  528. * rect.animate({ width: 100 });
  529. *
  530. * @private
  531. * @class
  532. * @name Highcharts.Fx
  533. */
  534. var Fx = /** @class */ (function () {
  535. /* *
  536. *
  537. * Constructors
  538. *
  539. * */
  540. /**
  541. *
  542. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
  543. * The element to animate.
  544. *
  545. * @param {Partial<Highcharts.AnimationOptionsObject>} options
  546. * Animation options.
  547. *
  548. * @param {string} prop
  549. * The single attribute or CSS property to animate.
  550. */
  551. function Fx(elem, options, prop) {
  552. this.options = options;
  553. this.elem = elem;
  554. this.prop = prop;
  555. }
  556. /* *
  557. *
  558. * Functions
  559. *
  560. * */
  561. /**
  562. * Set the current step of a path definition on SVGElement.
  563. *
  564. * @function Highcharts.Fx#dSetter
  565. *
  566. * @return {void}
  567. */
  568. Fx.prototype.dSetter = function () {
  569. var paths = this.paths,
  570. start = paths && paths[0],
  571. end = paths && paths[1],
  572. path = [],
  573. now = this.now || 0;
  574. // Land on the final path without adjustment points appended in the ends
  575. if (now === 1 || !start || !end) {
  576. path = this.toD || [];
  577. }
  578. else if (start.length === end.length && now < 1) {
  579. for (var i = 0; i < end.length; i++) {
  580. // Tween between the start segment and the end segment. Start
  581. // with a copy of the end segment and tween the appropriate
  582. // numerics
  583. var startSeg = start[i];
  584. var endSeg = end[i];
  585. var tweenSeg = [];
  586. for (var j = 0; j < endSeg.length; j++) {
  587. var startItem = startSeg[j];
  588. var endItem = endSeg[j];
  589. // Tween numbers
  590. if (typeof startItem === 'number' &&
  591. typeof endItem === 'number' &&
  592. // Arc boolean flags
  593. !(endSeg[0] === 'A' && (j === 4 || j === 5))) {
  594. tweenSeg[j] = startItem + now * (endItem - startItem);
  595. // Strings, take directly from the end segment
  596. }
  597. else {
  598. tweenSeg[j] = endItem;
  599. }
  600. }
  601. path.push(tweenSeg);
  602. }
  603. // If animation is finished or length not matching, land on right value
  604. }
  605. else {
  606. path = end;
  607. }
  608. this.elem.attr('d', path, void 0, true);
  609. };
  610. /**
  611. * Update the element with the current animation step.
  612. *
  613. * @function Highcharts.Fx#update
  614. *
  615. * @return {void}
  616. */
  617. Fx.prototype.update = function () {
  618. var elem = this.elem,
  619. prop = this.prop, // if destroyed, it is null
  620. now = this.now,
  621. step = this.options.step;
  622. // Animation setter defined from outside
  623. if (this[prop + 'Setter']) {
  624. this[prop + 'Setter']();
  625. // Other animations on SVGElement
  626. }
  627. else if (elem.attr) {
  628. if (elem.element) {
  629. elem.attr(prop, now, null, true);
  630. }
  631. // HTML styles, raw HTML content like container size
  632. }
  633. else {
  634. elem.style[prop] = now + this.unit;
  635. }
  636. if (step) {
  637. step.call(elem, now, this);
  638. }
  639. };
  640. /**
  641. * Run an animation.
  642. *
  643. * @function Highcharts.Fx#run
  644. *
  645. * @param {number} from
  646. * The current value, value to start from.
  647. *
  648. * @param {number} to
  649. * The end value, value to land on.
  650. *
  651. * @param {string} unit
  652. * The property unit, for example `px`.
  653. *
  654. * @return {void}
  655. */
  656. Fx.prototype.run = function (from, to, unit) {
  657. var self = this,
  658. options = self.options,
  659. timer = function (gotoEnd) {
  660. return timer.stopped ? false : self.step(gotoEnd);
  661. }, requestAnimationFrame = win.requestAnimationFrame ||
  662. function (step) {
  663. setTimeout(step, 13);
  664. }, step = function () {
  665. for (var i = 0; i < H.timers.length; i++) {
  666. if (!H.timers[i]()) {
  667. H.timers.splice(i--, 1);
  668. }
  669. }
  670. if (H.timers.length) {
  671. requestAnimationFrame(step);
  672. }
  673. };
  674. if (from === to && !this.elem['forceAnimate:' + this.prop]) {
  675. delete options.curAnim[this.prop];
  676. if (options.complete && Object.keys(options.curAnim).length === 0) {
  677. options.complete.call(this.elem);
  678. }
  679. }
  680. else { // #7166
  681. this.startTime = +new Date();
  682. this.start = from;
  683. this.end = to;
  684. this.unit = unit;
  685. this.now = this.start;
  686. this.pos = 0;
  687. timer.elem = this.elem;
  688. timer.prop = this.prop;
  689. if (timer() && H.timers.push(timer) === 1) {
  690. requestAnimationFrame(step);
  691. }
  692. }
  693. };
  694. /**
  695. * Run a single step in the animation.
  696. *
  697. * @function Highcharts.Fx#step
  698. *
  699. * @param {boolean} [gotoEnd]
  700. * Whether to go to the endpoint of the animation after abort.
  701. *
  702. * @return {boolean}
  703. * Returns `true` if animation continues.
  704. */
  705. Fx.prototype.step = function (gotoEnd) {
  706. var t = +new Date(),
  707. ret,
  708. done,
  709. options = this.options,
  710. elem = this.elem,
  711. complete = options.complete,
  712. duration = options.duration,
  713. curAnim = options.curAnim;
  714. if (elem.attr && !elem.element) { // #2616, element is destroyed
  715. ret = false;
  716. }
  717. else if (gotoEnd || t >= duration + this.startTime) {
  718. this.now = this.end;
  719. this.pos = 1;
  720. this.update();
  721. curAnim[this.prop] = true;
  722. done = true;
  723. objectEach(curAnim, function (val) {
  724. if (val !== true) {
  725. done = false;
  726. }
  727. });
  728. if (done && complete) {
  729. complete.call(elem);
  730. }
  731. ret = false;
  732. }
  733. else {
  734. this.pos = options.easing((t - this.startTime) / duration);
  735. this.now = this.start + ((this.end - this.start) * this.pos);
  736. this.update();
  737. ret = true;
  738. }
  739. return ret;
  740. };
  741. /**
  742. * Prepare start and end values so that the path can be animated one to one.
  743. *
  744. * @function Highcharts.Fx#initPath
  745. *
  746. * @param {Highcharts.SVGElement} elem
  747. * The SVGElement item.
  748. *
  749. * @param {Highcharts.SVGPathArray|undefined} fromD
  750. * Starting path definition.
  751. *
  752. * @param {Highcharts.SVGPathArray} toD
  753. * Ending path definition.
  754. *
  755. * @return {Array<Highcharts.SVGPathArray,Highcharts.SVGPathArray>}
  756. * An array containing start and end paths in array form so that
  757. * they can be animated in parallel.
  758. */
  759. Fx.prototype.initPath = function (elem, fromD, toD) {
  760. var shift,
  761. startX = elem.startX,
  762. endX = elem.endX,
  763. fullLength,
  764. i,
  765. start = fromD && fromD.slice(), // copy
  766. end = toD.slice(), // copy
  767. isArea = elem.isArea,
  768. positionFactor = isArea ? 2 : 1,
  769. reverse;
  770. if (!start) {
  771. return [end, end];
  772. }
  773. /**
  774. * If shifting points, prepend a dummy point to the end path.
  775. * @private
  776. * @param {Highcharts.SVGPathArray} arr - array
  777. * @param {Highcharts.SVGPathArray} other - array
  778. * @return {void}
  779. */
  780. function prepend(arr, other) {
  781. while (arr.length < fullLength) {
  782. // Move to, line to or curve to?
  783. var moveSegment = arr[0],
  784. otherSegment = other[fullLength - arr.length];
  785. if (otherSegment && moveSegment[0] === 'M') {
  786. if (otherSegment[0] === 'C') {
  787. arr[0] = [
  788. 'C',
  789. moveSegment[1],
  790. moveSegment[2],
  791. moveSegment[1],
  792. moveSegment[2],
  793. moveSegment[1],
  794. moveSegment[2]
  795. ];
  796. }
  797. else {
  798. arr[0] = ['L', moveSegment[1], moveSegment[2]];
  799. }
  800. }
  801. // Prepend a copy of the first point
  802. arr.unshift(moveSegment);
  803. // For areas, the bottom path goes back again to the left, so we
  804. // need to append a copy of the last point.
  805. if (isArea) {
  806. arr.push(arr[arr.length - 1]);
  807. }
  808. }
  809. }
  810. /**
  811. * Copy and append last point until the length matches the end length.
  812. * @private
  813. * @param {Highcharts.SVGPathArray} arr - array
  814. * @param {Highcharts.SVGPathArray} other - array
  815. * @return {void}
  816. */
  817. function append(arr, other) {
  818. while (arr.length < fullLength) {
  819. // Pull out the slice that is going to be appended or inserted.
  820. // In a line graph, the positionFactor is 1, and the last point
  821. // is sliced out. In an area graph, the positionFactor is 2,
  822. // causing the middle two points to be sliced out, since an area
  823. // path starts at left, follows the upper path then turns and
  824. // follows the bottom back.
  825. var segmentToAdd = arr[arr.length / positionFactor - 1].slice();
  826. // Disable the first control point of curve segments
  827. if (segmentToAdd[0] === 'C') {
  828. segmentToAdd[1] = segmentToAdd[5];
  829. segmentToAdd[2] = segmentToAdd[6];
  830. }
  831. if (!isArea) {
  832. arr.push(segmentToAdd);
  833. }
  834. else {
  835. var lowerSegmentToAdd = arr[arr.length / positionFactor].slice();
  836. arr.splice(arr.length / 2, 0, segmentToAdd, lowerSegmentToAdd);
  837. }
  838. }
  839. }
  840. // For sideways animation, find out how much we need to shift to get the
  841. // start path Xs to match the end path Xs.
  842. if (startX && endX) {
  843. for (i = 0; i < startX.length; i++) {
  844. // Moving left, new points coming in on right
  845. if (startX[i] === endX[0]) {
  846. shift = i;
  847. break;
  848. // Moving right
  849. }
  850. else if (startX[0] ===
  851. endX[endX.length - startX.length + i]) {
  852. shift = i;
  853. reverse = true;
  854. break;
  855. // Fixed from the right side, "scaling" left
  856. }
  857. else if (startX[startX.length - 1] ===
  858. endX[endX.length - startX.length + i]) {
  859. shift = startX.length - i;
  860. break;
  861. }
  862. }
  863. if (typeof shift === 'undefined') {
  864. start = [];
  865. }
  866. }
  867. if (start.length && isNumber(shift)) {
  868. // The common target length for the start and end array, where both
  869. // arrays are padded in opposite ends
  870. fullLength = end.length + shift * positionFactor;
  871. if (!reverse) {
  872. prepend(end, start);
  873. append(start, end);
  874. }
  875. else {
  876. prepend(start, end);
  877. append(end, start);
  878. }
  879. }
  880. return [start, end];
  881. };
  882. /**
  883. * Handle animation of the color attributes directly.
  884. *
  885. * @function Highcharts.Fx#fillSetter
  886. *
  887. * @return {void}
  888. */
  889. Fx.prototype.fillSetter = function () {
  890. Fx.prototype.strokeSetter.apply(this, arguments);
  891. };
  892. /**
  893. * Handle animation of the color attributes directly.
  894. *
  895. * @function Highcharts.Fx#strokeSetter
  896. *
  897. * @return {void}
  898. */
  899. Fx.prototype.strokeSetter = function () {
  900. this.elem.attr(this.prop, H.color(this.start).tweenTo(H.color(this.end), this.pos), null, true);
  901. };
  902. return Fx;
  903. }());
  904. H.Fx = Fx;
  905. /* eslint-disable valid-jsdoc */
  906. /**
  907. * Utility function to deep merge two or more objects and return a third object.
  908. * If the first argument is true, the contents of the second object is copied
  909. * into the first object. The merge function can also be used with a single
  910. * object argument to create a deep copy of an object.
  911. *
  912. * @function Highcharts.merge<T>
  913. *
  914. * @param {boolean} extend
  915. * Whether to extend the left-side object (a) or return a whole new
  916. * object.
  917. *
  918. * @param {T|undefined} a
  919. * The first object to extend. When only this is given, the function
  920. * returns a deep copy.
  921. *
  922. * @param {...Array<object|undefined>} [n]
  923. * An object to merge into the previous one.
  924. *
  925. * @return {T}
  926. * The merged object. If the first argument is true, the return is the
  927. * same as the second argument.
  928. */ /**
  929. * Utility function to deep merge two or more objects and return a third object.
  930. * The merge function can also be used with a single object argument to create a
  931. * deep copy of an object.
  932. *
  933. * @function Highcharts.merge<T>
  934. *
  935. * @param {T|undefined} a
  936. * The first object to extend. When only this is given, the function
  937. * returns a deep copy.
  938. *
  939. * @param {...Array<object|undefined>} [n]
  940. * An object to merge into the previous one.
  941. *
  942. * @return {T}
  943. * The merged object. If the first argument is true, the return is the
  944. * same as the second argument.
  945. */
  946. function merge() {
  947. /* eslint-enable valid-jsdoc */
  948. var i,
  949. args = arguments,
  950. len,
  951. ret = {},
  952. doCopy = function (copy,
  953. original) {
  954. // An object is replacing a primitive
  955. if (typeof copy !== 'object') {
  956. copy = {};
  957. }
  958. objectEach(original, function (value, key) {
  959. // Copy the contents of objects, but not arrays or DOM nodes
  960. if (isObject(value, true) &&
  961. !isClass(value) &&
  962. !isDOMElement(value)) {
  963. copy[key] = doCopy(copy[key] || {}, value);
  964. // Primitives and arrays are copied over directly
  965. }
  966. else {
  967. copy[key] = original[key];
  968. }
  969. });
  970. return copy;
  971. };
  972. // If first argument is true, copy into the existing object. Used in
  973. // setOptions.
  974. if (args[0] === true) {
  975. ret = args[1];
  976. args = Array.prototype.slice.call(args, 2);
  977. }
  978. // For each argument, extend the return
  979. len = args.length;
  980. for (i = 0; i < len; i++) {
  981. ret = doCopy(ret, args[i]);
  982. }
  983. return ret;
  984. }
  985. H.merge = merge;
  986. /**
  987. * Constrain a value to within a lower and upper threshold.
  988. *
  989. * @private
  990. * @param {number} value The initial value
  991. * @param {number} min The lower threshold
  992. * @param {number} max The upper threshold
  993. * @return {number} Returns a number value within min and max.
  994. */
  995. function clamp(value, min, max) {
  996. return value > min ? value < max ? value : max : min;
  997. }
  998. /**
  999. * Shortcut for parseInt
  1000. *
  1001. * @private
  1002. * @function Highcharts.pInt
  1003. *
  1004. * @param {*} s
  1005. * any
  1006. *
  1007. * @param {number} [mag]
  1008. * Magnitude
  1009. *
  1010. * @return {number}
  1011. * number
  1012. */
  1013. var pInt = H.pInt = function pInt(s,
  1014. mag) {
  1015. return parseInt(s,
  1016. mag || 10);
  1017. };
  1018. /**
  1019. * Utility function to check for string type.
  1020. *
  1021. * @function Highcharts.isString
  1022. *
  1023. * @param {*} s
  1024. * The item to check.
  1025. *
  1026. * @return {boolean}
  1027. * True if the argument is a string.
  1028. */
  1029. var isString = H.isString = function isString(s) {
  1030. return typeof s === 'string';
  1031. };
  1032. /**
  1033. * Utility function to check if an item is an array.
  1034. *
  1035. * @function Highcharts.isArray
  1036. *
  1037. * @param {*} obj
  1038. * The item to check.
  1039. *
  1040. * @return {boolean}
  1041. * True if the argument is an array.
  1042. */
  1043. var isArray = H.isArray = function isArray(obj) {
  1044. var str = Object.prototype.toString.call(obj);
  1045. return str === '[object Array]' || str === '[object Array Iterator]';
  1046. };
  1047. /**
  1048. * Utility function to check if an item is of type object.
  1049. *
  1050. * @function Highcharts.isObject
  1051. *
  1052. * @param {*} obj
  1053. * The item to check.
  1054. *
  1055. * @param {boolean} [strict=false]
  1056. * Also checks that the object is not an array.
  1057. *
  1058. * @return {boolean}
  1059. * True if the argument is an object.
  1060. */
  1061. function isObject(obj, strict) {
  1062. return (!!obj &&
  1063. typeof obj === 'object' &&
  1064. (!strict || !isArray(obj))); // eslint-disable-line @typescript-eslint/no-explicit-any
  1065. }
  1066. H.isObject = isObject;
  1067. /**
  1068. * Utility function to check if an Object is a HTML Element.
  1069. *
  1070. * @function Highcharts.isDOMElement
  1071. *
  1072. * @param {*} obj
  1073. * The item to check.
  1074. *
  1075. * @return {boolean}
  1076. * True if the argument is a HTML Element.
  1077. */
  1078. var isDOMElement = H.isDOMElement = function isDOMElement(obj) {
  1079. return isObject(obj) && typeof obj.nodeType === 'number';
  1080. };
  1081. /**
  1082. * Utility function to check if an Object is a class.
  1083. *
  1084. * @function Highcharts.isClass
  1085. *
  1086. * @param {object|undefined} obj
  1087. * The item to check.
  1088. *
  1089. * @return {boolean}
  1090. * True if the argument is a class.
  1091. */
  1092. var isClass = H.isClass = function isClass(obj) {
  1093. var c = obj && obj.constructor;
  1094. return !!(isObject(obj, true) &&
  1095. !isDOMElement(obj) &&
  1096. (c && c.name && c.name !== 'Object'));
  1097. };
  1098. /**
  1099. * Utility function to check if an item is a number and it is finite (not NaN,
  1100. * Infinity or -Infinity).
  1101. *
  1102. * @function Highcharts.isNumber
  1103. *
  1104. * @param {*} n
  1105. * The item to check.
  1106. *
  1107. * @return {boolean}
  1108. * True if the item is a finite number
  1109. */
  1110. var isNumber = H.isNumber = function isNumber(n) {
  1111. return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
  1112. };
  1113. /**
  1114. * Remove the last occurence of an item from an array.
  1115. *
  1116. * @function Highcharts.erase
  1117. *
  1118. * @param {Array<*>} arr
  1119. * The array.
  1120. *
  1121. * @param {*} item
  1122. * The item to remove.
  1123. *
  1124. * @return {void}
  1125. */
  1126. var erase = H.erase = function erase(arr,
  1127. item) {
  1128. var i = arr.length;
  1129. while (i--) {
  1130. if (arr[i] === item) {
  1131. arr.splice(i, 1);
  1132. break;
  1133. }
  1134. }
  1135. };
  1136. /**
  1137. * Check if an object is null or undefined.
  1138. *
  1139. * @function Highcharts.defined
  1140. *
  1141. * @param {*} obj
  1142. * The object to check.
  1143. *
  1144. * @return {boolean}
  1145. * False if the object is null or undefined, otherwise true.
  1146. */
  1147. var defined = H.defined = function defined(obj) {
  1148. return typeof obj !== 'undefined' && obj !== null;
  1149. };
  1150. /**
  1151. * Set or get an attribute or an object of attributes. To use as a setter, pass
  1152. * a key and a value, or let the second argument be a collection of keys and
  1153. * values. To use as a getter, pass only a string as the second argument.
  1154. *
  1155. * @function Highcharts.attr
  1156. *
  1157. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
  1158. * The DOM element to receive the attribute(s).
  1159. *
  1160. * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [prop]
  1161. * The property or an object of key-value pairs.
  1162. *
  1163. * @param {number|string} [value]
  1164. * The value if a single property is set.
  1165. *
  1166. * @return {string|null|undefined}
  1167. * When used as a getter, return the value.
  1168. */
  1169. function attr(elem, prop, value) {
  1170. var ret;
  1171. // if the prop is a string
  1172. if (isString(prop)) {
  1173. // set the value
  1174. if (defined(value)) {
  1175. elem.setAttribute(prop, value);
  1176. // get the value
  1177. }
  1178. else if (elem && elem.getAttribute) {
  1179. ret = elem.getAttribute(prop);
  1180. // IE7 and below cannot get class through getAttribute (#7850)
  1181. if (!ret && prop === 'class') {
  1182. ret = elem.getAttribute(prop + 'Name');
  1183. }
  1184. }
  1185. // else if prop is defined, it is a hash of key/value pairs
  1186. }
  1187. else {
  1188. objectEach(prop, function (val, key) {
  1189. elem.setAttribute(key, val);
  1190. });
  1191. }
  1192. return ret;
  1193. }
  1194. H.attr = attr;
  1195. /**
  1196. * Check if an element is an array, and if not, make it into an array.
  1197. *
  1198. * @function Highcharts.splat
  1199. *
  1200. * @param {*} obj
  1201. * The object to splat.
  1202. *
  1203. * @return {Array}
  1204. * The produced or original array.
  1205. */
  1206. var splat = H.splat = function splat(obj) {
  1207. return isArray(obj) ? obj : [obj];
  1208. };
  1209. /**
  1210. * Set a timeout if the delay is given, otherwise perform the function
  1211. * synchronously.
  1212. *
  1213. * @function Highcharts.syncTimeout
  1214. *
  1215. * @param {Function} fn
  1216. * The function callback.
  1217. *
  1218. * @param {number} delay
  1219. * Delay in milliseconds.
  1220. *
  1221. * @param {*} [context]
  1222. * An optional context to send to the function callback.
  1223. *
  1224. * @return {number}
  1225. * An identifier for the timeout that can later be cleared with
  1226. * Highcharts.clearTimeout. Returns -1 if there is no timeout.
  1227. */
  1228. var syncTimeout = H.syncTimeout = function syncTimeout(fn,
  1229. delay,
  1230. context) {
  1231. if (delay > 0) {
  1232. return setTimeout(fn,
  1233. delay,
  1234. context);
  1235. }
  1236. fn.call(0, context);
  1237. return -1;
  1238. };
  1239. /**
  1240. * Internal clear timeout. The function checks that the `id` was not removed
  1241. * (e.g. by `chart.destroy()`). For the details see
  1242. * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
  1243. *
  1244. * @function Highcharts.clearTimeout
  1245. *
  1246. * @param {number} id
  1247. * Id of a timeout.
  1248. *
  1249. * @return {void}
  1250. */
  1251. var internalClearTimeout = H.clearTimeout = function (id) {
  1252. if (defined(id)) {
  1253. clearTimeout(id);
  1254. }
  1255. };
  1256. /* eslint-disable valid-jsdoc */
  1257. /**
  1258. * Utility function to extend an object with the members of another.
  1259. *
  1260. * @function Highcharts.extend<T>
  1261. *
  1262. * @param {T|undefined} a
  1263. * The object to be extended.
  1264. *
  1265. * @param {object} b
  1266. * The object to add to the first one.
  1267. *
  1268. * @return {T}
  1269. * Object a, the original object.
  1270. */
  1271. var extend = H.extend = function extend(a,
  1272. b) {
  1273. /* eslint-enable valid-jsdoc */
  1274. var n;
  1275. if (!a) {
  1276. a = {};
  1277. }
  1278. for (n in b) { // eslint-disable-line guard-for-in
  1279. a[n] = b[n];
  1280. }
  1281. return a;
  1282. };
  1283. /* eslint-disable valid-jsdoc */
  1284. /**
  1285. * Return the first value that is not null or undefined.
  1286. *
  1287. * @function Highcharts.pick<T>
  1288. *
  1289. * @param {...Array<T|null|undefined>} items
  1290. * Variable number of arguments to inspect.
  1291. *
  1292. * @return {T}
  1293. * The value of the first argument that is not null or undefined.
  1294. */
  1295. function pick() {
  1296. var args = arguments;
  1297. var length = args.length;
  1298. for (var i = 0; i < length; i++) {
  1299. var arg = args[i];
  1300. if (typeof arg !== 'undefined' && arg !== null) {
  1301. return arg;
  1302. }
  1303. }
  1304. }
  1305. H.pick = pick;
  1306. /**
  1307. * Set CSS on a given element.
  1308. *
  1309. * @function Highcharts.css
  1310. *
  1311. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} el
  1312. * An HTML DOM element.
  1313. *
  1314. * @param {Highcharts.CSSObject} styles
  1315. * Style object with camel case property names.
  1316. *
  1317. * @return {void}
  1318. */
  1319. var css = H.css = function css(el,
  1320. styles) {
  1321. if (H.isMS && !H.svg) { // #2686
  1322. if (styles && typeof styles.opacity !== 'undefined') {
  1323. styles.filter =
  1324. 'alpha(opacity=' + (styles.opacity * 100) + ')';
  1325. }
  1326. }
  1327. extend(el.style, styles);
  1328. };
  1329. /**
  1330. * Utility function to create an HTML element with attributes and styles.
  1331. *
  1332. * @function Highcharts.createElement
  1333. *
  1334. * @param {string} tag
  1335. * The HTML tag.
  1336. *
  1337. * @param {Highcharts.HTMLAttributes} [attribs]
  1338. * Attributes as an object of key-value pairs.
  1339. *
  1340. * @param {Highcharts.CSSObject} [styles]
  1341. * Styles as an object of key-value pairs.
  1342. *
  1343. * @param {Highcharts.HTMLDOMElement} [parent]
  1344. * The parent HTML object.
  1345. *
  1346. * @param {boolean} [nopad=false]
  1347. * If true, remove all padding, border and margin.
  1348. *
  1349. * @return {Highcharts.HTMLDOMElement}
  1350. * The created DOM element.
  1351. */
  1352. var createElement = H.createElement = function createElement(tag,
  1353. attribs,
  1354. styles,
  1355. parent,
  1356. nopad) {
  1357. var el = doc.createElement(tag);
  1358. if (attribs) {
  1359. extend(el, attribs);
  1360. }
  1361. if (nopad) {
  1362. css(el, { padding: '0', border: 'none', margin: '0' });
  1363. }
  1364. if (styles) {
  1365. css(el, styles);
  1366. }
  1367. if (parent) {
  1368. parent.appendChild(el);
  1369. }
  1370. return el;
  1371. };
  1372. // eslint-disable-next-line valid-jsdoc
  1373. /**
  1374. * Extend a prototyped class by new members.
  1375. *
  1376. * @function Highcharts.extendClass<T>
  1377. *
  1378. * @param {Highcharts.Class<T>} parent
  1379. * The parent prototype to inherit.
  1380. *
  1381. * @param {Highcharts.Dictionary<*>} members
  1382. * A collection of prototype members to add or override compared to the
  1383. * parent prototype.
  1384. *
  1385. * @return {Highcharts.Class<T>}
  1386. * A new prototype.
  1387. */
  1388. var extendClass = H.extendClass = function extendClass(parent,
  1389. members) {
  1390. var obj = (function () { });
  1391. obj.prototype = new parent(); // eslint-disable-line new-cap
  1392. extend(obj.prototype, members);
  1393. return obj;
  1394. };
  1395. /**
  1396. * Left-pad a string to a given length by adding a character repetetively.
  1397. *
  1398. * @function Highcharts.pad
  1399. *
  1400. * @param {number} number
  1401. * The input string or number.
  1402. *
  1403. * @param {number} [length]
  1404. * The desired string length.
  1405. *
  1406. * @param {string} [padder=0]
  1407. * The character to pad with.
  1408. *
  1409. * @return {string}
  1410. * The padded string.
  1411. */
  1412. var pad = H.pad = function pad(number, length, padder) {
  1413. return new Array((length || 2) +
  1414. 1 -
  1415. String(number)
  1416. .replace('-', '')
  1417. .length).join(padder || '0') + number;
  1418. };
  1419. /**
  1420. * Return a length based on either the integer value, or a percentage of a base.
  1421. *
  1422. * @function Highcharts.relativeLength
  1423. *
  1424. * @param {Highcharts.RelativeSize} value
  1425. * A percentage string or a number.
  1426. *
  1427. * @param {number} base
  1428. * The full length that represents 100%.
  1429. *
  1430. * @param {number} [offset=0]
  1431. * A pixel offset to apply for percentage values. Used internally in
  1432. * axis positioning.
  1433. *
  1434. * @return {number}
  1435. * The computed length.
  1436. */
  1437. var relativeLength = H.relativeLength = function relativeLength(value,
  1438. base,
  1439. offset) {
  1440. return (/%$/).test(value) ?
  1441. (base * parseFloat(value) / 100) + (offset || 0) :
  1442. parseFloat(value);
  1443. };
  1444. /**
  1445. * Wrap a method with extended functionality, preserving the original function.
  1446. *
  1447. * @function Highcharts.wrap
  1448. *
  1449. * @param {*} obj
  1450. * The context object that the method belongs to. In real cases, this is
  1451. * often a prototype.
  1452. *
  1453. * @param {string} method
  1454. * The name of the method to extend.
  1455. *
  1456. * @param {Highcharts.WrapProceedFunction} func
  1457. * A wrapper function callback. This function is called with the same
  1458. * arguments as the original function, except that the original function
  1459. * is unshifted and passed as the first argument.
  1460. */
  1461. var wrap = H.wrap = function wrap(obj,
  1462. method,
  1463. func) {
  1464. var proceed = obj[method];
  1465. obj[method] = function () {
  1466. var args = Array.prototype.slice.call(arguments),
  1467. outerArgs = arguments,
  1468. ctx = this,
  1469. ret;
  1470. ctx.proceed = function () {
  1471. proceed.apply(ctx, arguments.length ? arguments : outerArgs);
  1472. };
  1473. args.unshift(proceed);
  1474. ret = func.apply(this, args);
  1475. ctx.proceed = null;
  1476. return ret;
  1477. };
  1478. };
  1479. /**
  1480. * Format a string according to a subset of the rules of Python's String.format
  1481. * method.
  1482. *
  1483. * @example
  1484. * var s = Highcharts.format(
  1485. * 'The {color} fox was {len:.2f} feet long',
  1486. * { color: 'red', len: Math.PI }
  1487. * );
  1488. * // => The red fox was 3.14 feet long
  1489. *
  1490. * @function Highcharts.format
  1491. *
  1492. * @param {string} str
  1493. * The string to format.
  1494. *
  1495. * @param {Record<string, *>} ctx
  1496. * The context, a collection of key-value pairs where each key is
  1497. * replaced by its value.
  1498. *
  1499. * @param {Highcharts.Chart} [chart]
  1500. * A `Chart` instance used to get numberFormatter and time.
  1501. *
  1502. * @return {string}
  1503. * The formatted string.
  1504. */
  1505. var format = H.format = function (str,
  1506. ctx,
  1507. chart) {
  1508. var splitter = '{',
  1509. isInside = false,
  1510. segment,
  1511. valueAndFormat,
  1512. ret = [],
  1513. val,
  1514. index;
  1515. var floatRegex = /f$/;
  1516. var decRegex = /\.([0-9])/;
  1517. var lang = H.defaultOptions.lang;
  1518. var time = chart && chart.time || H.time;
  1519. var numberFormatter = chart && chart.numberFormatter || numberFormat;
  1520. while (str) {
  1521. index = str.indexOf(splitter);
  1522. if (index === -1) {
  1523. break;
  1524. }
  1525. segment = str.slice(0, index);
  1526. if (isInside) { // we're on the closing bracket looking back
  1527. valueAndFormat = segment.split(':');
  1528. val = getNestedProperty(valueAndFormat.shift() || '', ctx);
  1529. // Format the replacement
  1530. if (valueAndFormat.length && typeof val === 'number') {
  1531. segment = valueAndFormat.join(':');
  1532. if (floatRegex.test(segment)) { // float
  1533. var decimals = parseInt((segment.match(decRegex) || ['', '-1'])[1], 10);
  1534. if (val !== null) {
  1535. val = numberFormatter(val, decimals, lang.decimalPoint, segment.indexOf(',') > -1 ? lang.thousandsSep : '');
  1536. }
  1537. }
  1538. else {
  1539. val = time.dateFormat(segment, val);
  1540. }
  1541. }
  1542. // Push the result and advance the cursor
  1543. ret.push(val);
  1544. }
  1545. else {
  1546. ret.push(segment);
  1547. }
  1548. str = str.slice(index + 1); // the rest
  1549. isInside = !isInside; // toggle
  1550. splitter = isInside ? '}' : '{'; // now look for next matching bracket
  1551. }
  1552. ret.push(str);
  1553. return ret.join('');
  1554. };
  1555. /**
  1556. * Get the magnitude of a number.
  1557. *
  1558. * @function Highcharts.getMagnitude
  1559. *
  1560. * @param {number} num
  1561. * The number.
  1562. *
  1563. * @return {number}
  1564. * The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
  1565. */
  1566. var getMagnitude = H.getMagnitude = function (num) {
  1567. return Math.pow(10,
  1568. Math.floor(Math.log(num) / Math.LN10));
  1569. };
  1570. /**
  1571. * Take an interval and normalize it to multiples of round numbers.
  1572. *
  1573. * @deprecated
  1574. * @function Highcharts.normalizeTickInterval
  1575. *
  1576. * @param {number} interval
  1577. * The raw, un-rounded interval.
  1578. *
  1579. * @param {Array<*>} [multiples]
  1580. * Allowed multiples.
  1581. *
  1582. * @param {number} [magnitude]
  1583. * The magnitude of the number.
  1584. *
  1585. * @param {boolean} [allowDecimals]
  1586. * Whether to allow decimals.
  1587. *
  1588. * @param {boolean} [hasTickAmount]
  1589. * If it has tickAmount, avoid landing on tick intervals lower than
  1590. * original.
  1591. *
  1592. * @return {number}
  1593. * The normalized interval.
  1594. *
  1595. * @todo
  1596. * Move this function to the Axis prototype. It is here only for historical
  1597. * reasons.
  1598. */
  1599. var normalizeTickInterval = H.normalizeTickInterval = function (interval,
  1600. multiples,
  1601. magnitude,
  1602. allowDecimals,
  1603. hasTickAmount) {
  1604. var normalized,
  1605. i,
  1606. retInterval = interval;
  1607. // round to a tenfold of 1, 2, 2.5 or 5
  1608. magnitude = pick(magnitude, 1);
  1609. normalized = interval / magnitude;
  1610. // multiples for a linear scale
  1611. if (!multiples) {
  1612. multiples = hasTickAmount ?
  1613. // Finer grained ticks when the tick amount is hard set, including
  1614. // when alignTicks is true on multiple axes (#4580).
  1615. [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
  1616. // Else, let ticks fall on rounder numbers
  1617. [1, 2, 2.5, 5, 10];
  1618. // the allowDecimals option
  1619. if (allowDecimals === false) {
  1620. if (magnitude === 1) {
  1621. multiples = multiples.filter(function (num) {
  1622. return num % 1 === 0;
  1623. });
  1624. }
  1625. else if (magnitude <= 0.1) {
  1626. multiples = [1 / magnitude];
  1627. }
  1628. }
  1629. }
  1630. // normalize the interval to the nearest multiple
  1631. for (i = 0; i < multiples.length; i++) {
  1632. retInterval = multiples[i];
  1633. // only allow tick amounts smaller than natural
  1634. if ((hasTickAmount &&
  1635. retInterval * magnitude >= interval) ||
  1636. (!hasTickAmount &&
  1637. (normalized <=
  1638. (multiples[i] +
  1639. (multiples[i + 1] || multiples[i])) / 2))) {
  1640. break;
  1641. }
  1642. }
  1643. // Multiply back to the correct magnitude. Correct floats to appropriate
  1644. // precision (#6085).
  1645. retInterval = correctFloat(retInterval * magnitude, -Math.round(Math.log(0.001) / Math.LN10));
  1646. return retInterval;
  1647. };
  1648. /**
  1649. * Sort an object array and keep the order of equal items. The ECMAScript
  1650. * standard does not specify the behaviour when items are equal.
  1651. *
  1652. * @function Highcharts.stableSort
  1653. *
  1654. * @param {Array<*>} arr
  1655. * The array to sort.
  1656. *
  1657. * @param {Function} sortFunction
  1658. * The function to sort it with, like with regular Array.prototype.sort.
  1659. *
  1660. * @return {void}
  1661. */
  1662. var stableSort = H.stableSort = function stableSort(arr,
  1663. sortFunction) {
  1664. // @todo It seems like Chrome since v70 sorts in a stable way internally,
  1665. // plus all other browsers do it, so over time we may be able to remove this
  1666. // function
  1667. var length = arr.length,
  1668. sortValue,
  1669. i;
  1670. // Add index to each item
  1671. for (i = 0; i < length; i++) {
  1672. arr[i].safeI = i; // stable sort index
  1673. }
  1674. arr.sort(function (a, b) {
  1675. sortValue = sortFunction(a, b);
  1676. return sortValue === 0 ? a.safeI - b.safeI : sortValue;
  1677. });
  1678. // Remove index from items
  1679. for (i = 0; i < length; i++) {
  1680. delete arr[i].safeI; // stable sort index
  1681. }
  1682. };
  1683. /**
  1684. * Non-recursive method to find the lowest member of an array. `Math.min` raises
  1685. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1686. * than 150.000 points. This method is slightly slower, but safe.
  1687. *
  1688. * @function Highcharts.arrayMin
  1689. *
  1690. * @param {Array<*>} data
  1691. * An array of numbers.
  1692. *
  1693. * @return {number}
  1694. * The lowest number.
  1695. */
  1696. var arrayMin = H.arrayMin = function arrayMin(data) {
  1697. var i = data.length,
  1698. min = data[0];
  1699. while (i--) {
  1700. if (data[i] < min) {
  1701. min = data[i];
  1702. }
  1703. }
  1704. return min;
  1705. };
  1706. /**
  1707. * Non-recursive method to find the lowest member of an array. `Math.max` raises
  1708. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1709. * than 150.000 points. This method is slightly slower, but safe.
  1710. *
  1711. * @function Highcharts.arrayMax
  1712. *
  1713. * @param {Array<*>} data
  1714. * An array of numbers.
  1715. *
  1716. * @return {number}
  1717. * The highest number.
  1718. */
  1719. var arrayMax = H.arrayMax = function arrayMax(data) {
  1720. var i = data.length,
  1721. max = data[0];
  1722. while (i--) {
  1723. if (data[i] > max) {
  1724. max = data[i];
  1725. }
  1726. }
  1727. return max;
  1728. };
  1729. /**
  1730. * Utility method that destroys any SVGElement instances that are properties on
  1731. * the given object. It loops all properties and invokes destroy if there is a
  1732. * destroy method. The property is then delete.
  1733. *
  1734. * @function Highcharts.destroyObjectProperties
  1735. *
  1736. * @param {*} obj
  1737. * The object to destroy properties on.
  1738. *
  1739. * @param {*} [except]
  1740. * Exception, do not destroy this property, only delete it.
  1741. *
  1742. * @return {void}
  1743. */
  1744. var destroyObjectProperties = H.destroyObjectProperties =
  1745. function destroyObjectProperties(obj,
  1746. except) {
  1747. objectEach(obj,
  1748. function (val,
  1749. n) {
  1750. // If the object is non-null and destroy is defined
  1751. if (val && val !== except && val.destroy) {
  1752. // Invoke the destroy
  1753. val.destroy();
  1754. }
  1755. // Delete the property from the object.
  1756. delete obj[n];
  1757. });
  1758. };
  1759. /**
  1760. * Discard a HTML element by moving it to the bin and delete.
  1761. *
  1762. * @function Highcharts.discardElement
  1763. *
  1764. * @param {Highcharts.HTMLDOMElement} element
  1765. * The HTML node to discard.
  1766. *
  1767. * @return {void}
  1768. */
  1769. var discardElement = H.discardElement = function discardElement(element) {
  1770. var garbageBin = H.garbageBin;
  1771. // create a garbage bin element, not part of the DOM
  1772. if (!garbageBin) {
  1773. garbageBin = createElement('div');
  1774. }
  1775. // move the node and empty bin
  1776. if (element) {
  1777. garbageBin.appendChild(element);
  1778. }
  1779. garbageBin.innerHTML = '';
  1780. };
  1781. /**
  1782. * Fix JS round off float errors.
  1783. *
  1784. * @function Highcharts.correctFloat
  1785. *
  1786. * @param {number} num
  1787. * A float number to fix.
  1788. *
  1789. * @param {number} [prec=14]
  1790. * The precision.
  1791. *
  1792. * @return {number}
  1793. * The corrected float number.
  1794. */
  1795. var correctFloat = H.correctFloat = function correctFloat(num,
  1796. prec) {
  1797. return parseFloat(num.toPrecision(prec || 14));
  1798. };
  1799. /**
  1800. * Set the global animation to either a given value, or fall back to the given
  1801. * chart's animation option.
  1802. *
  1803. * @function Highcharts.setAnimation
  1804. *
  1805. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>|undefined} animation
  1806. * The animation object.
  1807. *
  1808. * @param {Highcharts.Chart} chart
  1809. * The chart instance.
  1810. *
  1811. * @return {void}
  1812. *
  1813. * @todo
  1814. * This function always relates to a chart, and sets a property on the renderer,
  1815. * so it should be moved to the SVGRenderer.
  1816. */
  1817. var setAnimation = H.setAnimation = function setAnimation(animation,
  1818. chart) {
  1819. chart.renderer.globalAnimation = pick(animation,
  1820. chart.options.chart.animation,
  1821. true);
  1822. };
  1823. /**
  1824. * Get the animation in object form, where a disabled animation is always
  1825. * returned as `{ duration: 0 }`.
  1826. *
  1827. * @function Highcharts.animObject
  1828. *
  1829. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=0]
  1830. * An animation setting. Can be an object with duration, complete and
  1831. * easing properties, or a boolean to enable or disable.
  1832. *
  1833. * @return {Highcharts.AnimationOptionsObject}
  1834. * An object with at least a duration property.
  1835. */
  1836. var animObject = H.animObject = function animObject(animation) {
  1837. return isObject(animation) ?
  1838. H.merge({ duration: 500,
  1839. defer: 0 },
  1840. animation) :
  1841. { duration: animation ? 500 : 0,
  1842. defer: 0 };
  1843. };
  1844. /**
  1845. * The time unit lookup
  1846. *
  1847. * @ignore
  1848. */
  1849. var timeUnits = H.timeUnits = {
  1850. millisecond: 1,
  1851. second: 1000,
  1852. minute: 60000,
  1853. hour: 3600000,
  1854. day: 24 * 3600000,
  1855. week: 7 * 24 * 3600000,
  1856. month: 28 * 24 * 3600000,
  1857. year: 364 * 24 * 3600000
  1858. };
  1859. /**
  1860. * Format a number and return a string based on input settings.
  1861. *
  1862. * @sample highcharts/members/highcharts-numberformat/
  1863. * Custom number format
  1864. *
  1865. * @function Highcharts.numberFormat
  1866. *
  1867. * @param {number} number
  1868. * The input number to format.
  1869. *
  1870. * @param {number} decimals
  1871. * The amount of decimals. A value of -1 preserves the amount in the
  1872. * input number.
  1873. *
  1874. * @param {string} [decimalPoint]
  1875. * The decimal point, defaults to the one given in the lang options, or
  1876. * a dot.
  1877. *
  1878. * @param {string} [thousandsSep]
  1879. * The thousands separator, defaults to the one given in the lang
  1880. * options, or a space character.
  1881. *
  1882. * @return {string}
  1883. * The formatted number.
  1884. */
  1885. var numberFormat = H.numberFormat = function numberFormat(number,
  1886. decimals,
  1887. decimalPoint,
  1888. thousandsSep) {
  1889. number = +number || 0;
  1890. decimals = +decimals;
  1891. var lang = H.defaultOptions.lang, origDec = (number.toString().split('.')[1] || '').split('e')[0].length, strinteger, thousands, ret, roundedNumber, exponent = number.toString().split('e'), fractionDigits;
  1892. if (decimals === -1) {
  1893. // Preserve decimals. Not huge numbers (#3793).
  1894. decimals = Math.min(origDec, 20);
  1895. }
  1896. else if (!isNumber(decimals)) {
  1897. decimals = 2;
  1898. }
  1899. else if (decimals && exponent[1] && exponent[1] < 0) {
  1900. // Expose decimals from exponential notation (#7042)
  1901. fractionDigits = decimals + +exponent[1];
  1902. if (fractionDigits >= 0) {
  1903. // remove too small part of the number while keeping the notation
  1904. exponent[0] = (+exponent[0]).toExponential(fractionDigits)
  1905. .split('e')[0];
  1906. decimals = fractionDigits;
  1907. }
  1908. else {
  1909. // fractionDigits < 0
  1910. exponent[0] = exponent[0].split('.')[0] || 0;
  1911. if (decimals < 20) {
  1912. // use number instead of exponential notation (#7405)
  1913. number = (exponent[0] * Math.pow(10, exponent[1]))
  1914. .toFixed(decimals);
  1915. }
  1916. else {
  1917. // or zero
  1918. number = 0;
  1919. }
  1920. exponent[1] = 0;
  1921. }
  1922. }
  1923. // Add another decimal to avoid rounding errors of float numbers. (#4573)
  1924. // Then use toFixed to handle rounding.
  1925. roundedNumber = (Math.abs(exponent[1] ? exponent[0] : number) +
  1926. Math.pow(10, -Math.max(decimals, origDec) - 1)).toFixed(decimals);
  1927. // A string containing the positive integer component of the number
  1928. strinteger = String(pInt(roundedNumber));
  1929. // Leftover after grouping into thousands. Can be 0, 1 or 2.
  1930. thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
  1931. // Language
  1932. decimalPoint = pick(decimalPoint, lang.decimalPoint);
  1933. thousandsSep = pick(thousandsSep, lang.thousandsSep);
  1934. // Start building the return
  1935. ret = number < 0 ? '-' : '';
  1936. // Add the leftover after grouping into thousands. For example, in the
  1937. // number 42 000 000, this line adds 42.
  1938. ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
  1939. // Add the remaining thousands groups, joined by the thousands separator
  1940. ret += strinteger
  1941. .substr(thousands)
  1942. .replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
  1943. // Add the decimal point and the decimal component
  1944. if (decimals) {
  1945. // Get the decimal component
  1946. ret += decimalPoint + roundedNumber.slice(-decimals);
  1947. }
  1948. if (exponent[1] && +ret !== 0) {
  1949. ret += 'e' + exponent[1];
  1950. }
  1951. return ret;
  1952. };
  1953. /**
  1954. * Easing definition
  1955. *
  1956. * @private
  1957. * @function Math.easeInOutSine
  1958. *
  1959. * @param {number} pos
  1960. * Current position, ranging from 0 to 1.
  1961. *
  1962. * @return {number}
  1963. * Ease result
  1964. */
  1965. Math.easeInOutSine = function (pos) {
  1966. return -0.5 * (Math.cos(Math.PI * pos) - 1);
  1967. };
  1968. /**
  1969. * Returns the value of a property path on a given object.
  1970. *
  1971. * @private
  1972. * @function getNestedProperty
  1973. *
  1974. * @param {string} path
  1975. * Path to the property, for example `custom.myValue`.
  1976. *
  1977. * @param {unknown} obj
  1978. * Instance containing the property on the specific path.
  1979. *
  1980. * @return {unknown}
  1981. * The unknown property value.
  1982. */
  1983. function getNestedProperty(path, obj) {
  1984. if (!path) {
  1985. return obj;
  1986. }
  1987. var pathElements = path.split('.').reverse();
  1988. var subProperty = obj;
  1989. if (pathElements.length === 1) {
  1990. return subProperty[path];
  1991. }
  1992. var pathElement = pathElements.pop();
  1993. while (typeof pathElement !== 'undefined' &&
  1994. typeof subProperty !== 'undefined' &&
  1995. subProperty !== null) {
  1996. subProperty = subProperty[pathElement];
  1997. pathElement = pathElements.pop();
  1998. }
  1999. return subProperty;
  2000. }
  2001. /**
  2002. * Get the computed CSS value for given element and property, only for numerical
  2003. * properties. For width and height, the dimension of the inner box (excluding
  2004. * padding) is returned. Used for fitting the chart within the container.
  2005. *
  2006. * @function Highcharts.getStyle
  2007. *
  2008. * @param {Highcharts.HTMLDOMElement} el
  2009. * An HTML element.
  2010. *
  2011. * @param {string} prop
  2012. * The property name.
  2013. *
  2014. * @param {boolean} [toInt=true]
  2015. * Parse to integer.
  2016. *
  2017. * @return {number|string}
  2018. * The numeric value.
  2019. */
  2020. var getStyle = H.getStyle = function (el,
  2021. prop,
  2022. toInt) {
  2023. var style;
  2024. // For width and height, return the actual inner pixel size (#4913)
  2025. if (prop === 'width') {
  2026. var offsetWidth = Math.min(el.offsetWidth,
  2027. el.scrollWidth);
  2028. // In flex boxes, we need to use getBoundingClientRect and floor it,
  2029. // because scrollWidth doesn't support subpixel precision (#6427) ...
  2030. var boundingClientRectWidth = el.getBoundingClientRect &&
  2031. el.getBoundingClientRect().width;
  2032. // ...unless if the containing div or its parents are transform-scaled
  2033. // down, in which case the boundingClientRect can't be used as it is
  2034. // also scaled down (#9871, #10498).
  2035. if (boundingClientRectWidth < offsetWidth &&
  2036. boundingClientRectWidth >= offsetWidth - 1) {
  2037. offsetWidth = Math.floor(boundingClientRectWidth);
  2038. }
  2039. return Math.max(0, // #8377
  2040. (offsetWidth -
  2041. H.getStyle(el, 'padding-left') -
  2042. H.getStyle(el, 'padding-right')));
  2043. }
  2044. if (prop === 'height') {
  2045. return Math.max(0, // #8377
  2046. Math.min(el.offsetHeight, el.scrollHeight) -
  2047. H.getStyle(el, 'padding-top') -
  2048. H.getStyle(el, 'padding-bottom'));
  2049. }
  2050. if (!win.getComputedStyle) {
  2051. // SVG not supported, forgot to load oldie.js?
  2052. error(27, true);
  2053. }
  2054. // Otherwise, get the computed style
  2055. style = win.getComputedStyle(el, undefined); // eslint-disable-line no-undefined
  2056. if (style) {
  2057. style = style.getPropertyValue(prop);
  2058. if (pick(toInt, prop !== 'opacity')) {
  2059. style = pInt(style);
  2060. }
  2061. }
  2062. return style;
  2063. };
  2064. /**
  2065. * Get the defer as a number value from series animation options.
  2066. *
  2067. * @function Highcharts.getDeferredAnimation
  2068. *
  2069. * @param {Highcharts.Chart} chart
  2070. * The chart instance.
  2071. *
  2072. * @return {number}
  2073. * The numeric value.
  2074. */
  2075. var getDeferredAnimation = H.getDeferredAnimation = function (chart,
  2076. animation,
  2077. series) {
  2078. var labelAnimation = animObject(animation);
  2079. var s = series ? [series] : chart.series;
  2080. var defer = 0;
  2081. var duration = 0;
  2082. s.forEach(function (series) {
  2083. var seriesAnim = animObject(series.options.animation);
  2084. defer = animation && defined(animation.defer) ?
  2085. labelAnimation.defer :
  2086. Math.max(defer, seriesAnim.duration + seriesAnim.defer);
  2087. duration = Math.min(labelAnimation.duration, seriesAnim.duration);
  2088. });
  2089. // Disable defer for exporting
  2090. if (chart.renderer.forExport) {
  2091. defer = 0;
  2092. }
  2093. var anim = {
  2094. defer: Math.max(0,
  2095. defer - duration),
  2096. duration: Math.min(defer,
  2097. duration)
  2098. };
  2099. return anim;
  2100. };
  2101. /**
  2102. * Search for an item in an array.
  2103. *
  2104. * @function Highcharts.inArray
  2105. *
  2106. * @deprecated
  2107. *
  2108. * @param {*} item
  2109. * The item to search for.
  2110. *
  2111. * @param {Array<*>} arr
  2112. * The array or node collection to search in.
  2113. *
  2114. * @param {number} [fromIndex=0]
  2115. * The index to start searching from.
  2116. *
  2117. * @return {number}
  2118. * The index within the array, or -1 if not found.
  2119. */
  2120. var inArray = H.inArray = function (item,
  2121. arr,
  2122. fromIndex) {
  2123. error(32,
  2124. false,
  2125. void 0, { 'Highcharts.inArray': 'use Array.indexOf' });
  2126. return arr.indexOf(item, fromIndex);
  2127. };
  2128. /* eslint-disable valid-jsdoc */
  2129. /**
  2130. * Return the value of the first element in the array that satisfies the
  2131. * provided testing function.
  2132. *
  2133. * @function Highcharts.find<T>
  2134. *
  2135. * @param {Array<T>} arr
  2136. * The array to test.
  2137. *
  2138. * @param {Function} callback
  2139. * The callback function. The function receives the item as the first
  2140. * argument. Return `true` if this item satisfies the condition.
  2141. *
  2142. * @return {T|undefined}
  2143. * The value of the element.
  2144. */
  2145. var find = H.find = Array.prototype.find ?
  2146. /* eslint-enable valid-jsdoc */
  2147. function (arr,
  2148. callback) {
  2149. return arr.find(callback);
  2150. } :
  2151. // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
  2152. function (arr, callback) {
  2153. var i,
  2154. length = arr.length;
  2155. for (i = 0; i < length; i++) {
  2156. if (callback(arr[i], i)) { // eslint-disable-line callback-return
  2157. return arr[i];
  2158. }
  2159. }
  2160. };
  2161. /**
  2162. * Returns an array of a given object's own properties.
  2163. *
  2164. * @function Highcharts.keys
  2165. * @deprecated
  2166. *
  2167. * @param {*} obj
  2168. * The object of which the properties are to be returned.
  2169. *
  2170. * @return {Array<string>}
  2171. * An array of strings that represents all the properties.
  2172. */
  2173. H.keys = function (obj) {
  2174. error(32, false, void 0, { 'Highcharts.keys': 'use Object.keys' });
  2175. return Object.keys(obj);
  2176. };
  2177. /**
  2178. * Get the element's offset position, corrected for `overflow: auto`.
  2179. *
  2180. * @function Highcharts.offset
  2181. *
  2182. * @param {global.Element} el
  2183. * The DOM element.
  2184. *
  2185. * @return {Highcharts.OffsetObject}
  2186. * An object containing `left` and `top` properties for the position in
  2187. * the page.
  2188. */
  2189. var offset = H.offset = function offset(el) {
  2190. var docElem = doc.documentElement,
  2191. box = (el.parentElement || el.parentNode) ?
  2192. el.getBoundingClientRect() :
  2193. { top: 0,
  2194. left: 0 };
  2195. return {
  2196. top: box.top + (win.pageYOffset || docElem.scrollTop) -
  2197. (docElem.clientTop || 0),
  2198. left: box.left + (win.pageXOffset || docElem.scrollLeft) -
  2199. (docElem.clientLeft || 0)
  2200. };
  2201. };
  2202. /**
  2203. * Stop running animation.
  2204. *
  2205. * @function Highcharts.stop
  2206. *
  2207. * @param {Highcharts.SVGElement} el
  2208. * The SVGElement to stop animation on.
  2209. *
  2210. * @param {string} [prop]
  2211. * The property to stop animating. If given, the stop method will stop a
  2212. * single property from animating, while others continue.
  2213. *
  2214. * @return {void}
  2215. *
  2216. * @todo
  2217. * A possible extension to this would be to stop a single property, when
  2218. * we want to continue animating others. Then assign the prop to the timer
  2219. * in the Fx.run method, and check for the prop here. This would be an
  2220. * improvement in all cases where we stop the animation from .attr. Instead of
  2221. * stopping everything, we can just stop the actual attributes we're setting.
  2222. */
  2223. var stop = H.stop = function (el,
  2224. prop) {
  2225. var i = H.timers.length;
  2226. // Remove timers related to this element (#4519)
  2227. while (i--) {
  2228. if (H.timers[i].elem === el && (!prop || prop === H.timers[i].prop)) {
  2229. H.timers[i].stopped = true; // #4667
  2230. }
  2231. }
  2232. };
  2233. /* eslint-disable valid-jsdoc */
  2234. /**
  2235. * Iterate over object key pairs in an object.
  2236. *
  2237. * @function Highcharts.objectEach<T>
  2238. *
  2239. * @param {*} obj
  2240. * The object to iterate over.
  2241. *
  2242. * @param {Highcharts.ObjectEachCallbackFunction<T>} fn
  2243. * The iterator callback. It passes three arguments:
  2244. * * value - The property value.
  2245. * * key - The property key.
  2246. * * obj - The object that objectEach is being applied to.
  2247. *
  2248. * @param {T} [ctx]
  2249. * The context.
  2250. *
  2251. * @return {void}
  2252. */
  2253. var objectEach = H.objectEach = function objectEach(obj,
  2254. fn,
  2255. ctx) {
  2256. /* eslint-enable valid-jsdoc */
  2257. for (var key in obj) {
  2258. if (Object.hasOwnProperty.call(obj,
  2259. key)) {
  2260. fn.call(ctx || obj[key],
  2261. obj[key],
  2262. key,
  2263. obj);
  2264. }
  2265. }
  2266. };
  2267. /**
  2268. * Iterate over an array.
  2269. *
  2270. * @deprecated
  2271. * @function Highcharts.each
  2272. *
  2273. * @param {Array<*>} arr
  2274. * The array to iterate over.
  2275. *
  2276. * @param {Function} fn
  2277. * The iterator callback. It passes three arguments:
  2278. * - `item`: The array item.
  2279. * - `index`: The item's index in the array.
  2280. * - `arr`: The array that each is being applied to.
  2281. *
  2282. * @param {*} [ctx]
  2283. * The context.
  2284. *
  2285. * @return {void}
  2286. */
  2287. /**
  2288. * Filter an array by a callback.
  2289. *
  2290. * @deprecated
  2291. * @function Highcharts.grep
  2292. *
  2293. * @param {Array<*>} arr
  2294. * The array to filter.
  2295. *
  2296. * @param {Function} callback
  2297. * The callback function. The function receives the item as the first
  2298. * argument. Return `true` if the item is to be preserved.
  2299. *
  2300. * @return {Array<*>}
  2301. * A new, filtered array.
  2302. */
  2303. /**
  2304. * Map an array by a callback.
  2305. *
  2306. * @deprecated
  2307. * @function Highcharts.map
  2308. *
  2309. * @param {Array<*>} arr
  2310. * The array to map.
  2311. *
  2312. * @param {Function} fn
  2313. * The callback function. Return the new value for the new array.
  2314. *
  2315. * @return {Array<*>}
  2316. * A new array item with modified items.
  2317. */
  2318. /**
  2319. * Reduce an array to a single value.
  2320. *
  2321. * @deprecated
  2322. * @function Highcharts.reduce
  2323. *
  2324. * @param {Array<*>} arr
  2325. * The array to reduce.
  2326. *
  2327. * @param {Function} fn
  2328. * The callback function. Return the reduced value. Receives 4
  2329. * arguments: Accumulated/reduced value, current value, current array
  2330. * index, and the array.
  2331. *
  2332. * @param {*} initialValue
  2333. * The initial value of the accumulator.
  2334. *
  2335. * @return {*}
  2336. * The reduced value.
  2337. */
  2338. /**
  2339. * Test whether at least one element in the array passes the test implemented by
  2340. * the provided function.
  2341. *
  2342. * @deprecated
  2343. * @function Highcharts.some
  2344. *
  2345. * @param {Array<*>} arr
  2346. * The array to test
  2347. *
  2348. * @param {Function} fn
  2349. * The function to run on each item. Return truty to pass the test.
  2350. * Receives arguments `currentValue`, `index` and `array`.
  2351. *
  2352. * @param {*} ctx
  2353. * The context.
  2354. *
  2355. * @return {boolean}
  2356. */
  2357. objectEach({
  2358. map: 'map',
  2359. each: 'forEach',
  2360. grep: 'filter',
  2361. reduce: 'reduce',
  2362. some: 'some'
  2363. }, function (val, key) {
  2364. H[key] = function (arr) {
  2365. var _a;
  2366. error(32, false, void 0, (_a = {}, _a["Highcharts." + key] = "use Array." + val, _a));
  2367. return Array.prototype[val].apply(arr, [].slice.call(arguments, 1));
  2368. };
  2369. });
  2370. /* eslint-disable valid-jsdoc */
  2371. /**
  2372. * Add an event listener.
  2373. *
  2374. * @function Highcharts.addEvent<T>
  2375. *
  2376. * @param {Highcharts.Class<T>|T} el
  2377. * The element or object to add a listener to. It can be a
  2378. * {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
  2379. *
  2380. * @param {string} type
  2381. * The event type.
  2382. *
  2383. * @param {Highcharts.EventCallbackFunction<T>|Function} fn
  2384. * The function callback to execute when the event is fired.
  2385. *
  2386. * @param {Highcharts.EventOptionsObject} [options]
  2387. * Options for adding the event.
  2388. *
  2389. * @return {Function}
  2390. * A callback function to remove the added event.
  2391. */
  2392. var addEvent = H.addEvent = function (el,
  2393. type,
  2394. fn,
  2395. options) {
  2396. if (options === void 0) { options = {}; }
  2397. /* eslint-enable valid-jsdoc */
  2398. var events,
  2399. addEventListener = (el.addEventListener || H.addEventListenerPolyfill);
  2400. // If we're setting events directly on the constructor, use a separate
  2401. // collection, `protoEvents` to distinguish it from the item events in
  2402. // `hcEvents`.
  2403. if (typeof el === 'function' && el.prototype) {
  2404. events = el.prototype.protoEvents = el.prototype.protoEvents || {};
  2405. }
  2406. else {
  2407. events = el.hcEvents = el.hcEvents || {};
  2408. }
  2409. // Allow click events added to points, otherwise they will be prevented by
  2410. // the TouchPointer.pinch function after a pinch zoom operation (#7091).
  2411. if (H.Point &&
  2412. el instanceof H.Point &&
  2413. el.series &&
  2414. el.series.chart) {
  2415. el.series.chart.runTrackerClick = true;
  2416. }
  2417. // Handle DOM events
  2418. if (addEventListener) {
  2419. addEventListener.call(el, type, fn, false);
  2420. }
  2421. if (!events[type]) {
  2422. events[type] = [];
  2423. }
  2424. var eventObject = {
  2425. fn: fn,
  2426. order: typeof options.order === 'number' ? options.order : Infinity
  2427. };
  2428. events[type].push(eventObject);
  2429. // Order the calls
  2430. events[type].sort(function (a, b) {
  2431. return a.order - b.order;
  2432. });
  2433. // Return a function that can be called to remove this event.
  2434. return function () {
  2435. removeEvent(el, type, fn);
  2436. };
  2437. };
  2438. /* eslint-disable valid-jsdoc */
  2439. /**
  2440. * Remove an event that was added with {@link Highcharts#addEvent}.
  2441. *
  2442. * @function Highcharts.removeEvent<T>
  2443. *
  2444. * @param {Highcharts.Class<T>|T} el
  2445. * The element to remove events on.
  2446. *
  2447. * @param {string} [type]
  2448. * The type of events to remove. If undefined, all events are removed
  2449. * from the element.
  2450. *
  2451. * @param {Highcharts.EventCallbackFunction<T>} [fn]
  2452. * The specific callback to remove. If undefined, all events that match
  2453. * the element and optionally the type are removed.
  2454. *
  2455. * @return {void}
  2456. */
  2457. var removeEvent = H.removeEvent = function removeEvent(el,
  2458. type,
  2459. fn) {
  2460. /* eslint-enable valid-jsdoc */
  2461. var events;
  2462. /**
  2463. * @private
  2464. * @param {string} type - event type
  2465. * @param {Highcharts.EventCallbackFunction<T>} fn - callback
  2466. * @return {void}
  2467. */
  2468. function removeOneEvent(type, fn) {
  2469. var removeEventListener = (el.removeEventListener || H.removeEventListenerPolyfill);
  2470. if (removeEventListener) {
  2471. removeEventListener.call(el, type, fn, false);
  2472. }
  2473. }
  2474. /**
  2475. * @private
  2476. * @param {any} eventCollection - collection
  2477. * @return {void}
  2478. */
  2479. function removeAllEvents(eventCollection) {
  2480. var types,
  2481. len;
  2482. if (!el.nodeName) {
  2483. return; // break on non-DOM events
  2484. }
  2485. if (type) {
  2486. types = {};
  2487. types[type] = true;
  2488. }
  2489. else {
  2490. types = eventCollection;
  2491. }
  2492. objectEach(types, function (_val, n) {
  2493. if (eventCollection[n]) {
  2494. len = eventCollection[n].length;
  2495. while (len--) {
  2496. removeOneEvent(n, eventCollection[n][len].fn);
  2497. }
  2498. }
  2499. });
  2500. }
  2501. ['protoEvents', 'hcEvents'].forEach(function (coll, i) {
  2502. var eventElem = i ? el : el.prototype;
  2503. var eventCollection = eventElem && eventElem[coll];
  2504. if (eventCollection) {
  2505. if (type) {
  2506. events = (eventCollection[type] || []);
  2507. if (fn) {
  2508. eventCollection[type] = events.filter(function (obj) {
  2509. return fn !== obj.fn;
  2510. });
  2511. removeOneEvent(type, fn);
  2512. }
  2513. else {
  2514. removeAllEvents(eventCollection);
  2515. eventCollection[type] = [];
  2516. }
  2517. }
  2518. else {
  2519. removeAllEvents(eventCollection);
  2520. eventElem[coll] = {};
  2521. }
  2522. }
  2523. });
  2524. };
  2525. /* eslint-disable valid-jsdoc */
  2526. /**
  2527. * Fire an event that was registered with {@link Highcharts#addEvent}.
  2528. *
  2529. * @function Highcharts.fireEvent<T>
  2530. *
  2531. * @param {T} el
  2532. * The object to fire the event on. It can be a {@link HTMLDOMElement},
  2533. * an {@link SVGElement} or any other object.
  2534. *
  2535. * @param {string} type
  2536. * The type of event.
  2537. *
  2538. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  2539. * Custom event arguments that are passed on as an argument to the event
  2540. * handler.
  2541. *
  2542. * @param {Highcharts.EventCallbackFunction<T>|Function} [defaultFunction]
  2543. * The default function to execute if the other listeners haven't
  2544. * returned false.
  2545. *
  2546. * @return {void}
  2547. */
  2548. var fireEvent = H.fireEvent = function (el,
  2549. type,
  2550. eventArguments,
  2551. defaultFunction) {
  2552. /* eslint-enable valid-jsdoc */
  2553. var e,
  2554. i;
  2555. eventArguments = eventArguments || {};
  2556. if (doc.createEvent &&
  2557. (el.dispatchEvent || el.fireEvent)) {
  2558. e = doc.createEvent('Events');
  2559. e.initEvent(type, true, true);
  2560. extend(e, eventArguments);
  2561. if (el.dispatchEvent) {
  2562. el.dispatchEvent(e);
  2563. }
  2564. else {
  2565. el.fireEvent(type, e);
  2566. }
  2567. }
  2568. else {
  2569. if (!eventArguments.target) {
  2570. // We're running a custom event
  2571. extend(eventArguments, {
  2572. // Attach a simple preventDefault function to skip
  2573. // default handler if called. The built-in
  2574. // defaultPrevented property is not overwritable (#5112)
  2575. preventDefault: function () {
  2576. eventArguments.defaultPrevented = true;
  2577. },
  2578. // Setting target to native events fails with clicking
  2579. // the zoom-out button in Chrome.
  2580. target: el,
  2581. // If the type is not set, we're running a custom event
  2582. // (#2297). If it is set, we're running a browser event,
  2583. // and setting it will cause en error in IE8 (#2465).
  2584. type: type
  2585. });
  2586. }
  2587. var fireInOrder = function (protoEvents,
  2588. hcEvents) {
  2589. if (protoEvents === void 0) { protoEvents = []; }
  2590. if (hcEvents === void 0) { hcEvents = []; }
  2591. var iA = 0;
  2592. var iB = 0;
  2593. var length = protoEvents.length + hcEvents.length;
  2594. for (i = 0; i < length; i++) {
  2595. var obj = (!protoEvents[iA] ?
  2596. hcEvents[iB++] :
  2597. !hcEvents[iB] ?
  2598. protoEvents[iA++] :
  2599. protoEvents[iA].order <= hcEvents[iB].order ?
  2600. protoEvents[iA++] :
  2601. hcEvents[iB++]);
  2602. // If the event handler return false, prevent the default
  2603. // handler from executing
  2604. if (obj.fn.call(el, eventArguments) === false) {
  2605. eventArguments.preventDefault();
  2606. }
  2607. }
  2608. };
  2609. fireInOrder(el.protoEvents && el.protoEvents[type], el.hcEvents && el.hcEvents[type]);
  2610. }
  2611. // Run the default if not prevented
  2612. if (defaultFunction && !eventArguments.defaultPrevented) {
  2613. defaultFunction.call(el, eventArguments);
  2614. }
  2615. };
  2616. /**
  2617. * The global animate method, which uses Fx to create individual animators.
  2618. *
  2619. * @function Highcharts.animate
  2620. *
  2621. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
  2622. * The element to animate.
  2623. *
  2624. * @param {Highcharts.CSSObject|Highcharts.SVGAttributes} params
  2625. * An object containing key-value pairs of the properties to animate.
  2626. * Supports numeric as pixel-based CSS properties for HTML objects and
  2627. * attributes for SVGElements.
  2628. *
  2629. * @param {Partial<Highcharts.AnimationOptionsObject>} [opt]
  2630. * Animation options.
  2631. *
  2632. * @return {void}
  2633. */
  2634. var animate = H.animate = function (el,
  2635. params,
  2636. opt) {
  2637. var start,
  2638. unit = '',
  2639. end,
  2640. fx,
  2641. args;
  2642. if (!isObject(opt)) { // Number or undefined/null
  2643. args = arguments;
  2644. opt = {
  2645. duration: args[2],
  2646. easing: args[3],
  2647. complete: args[4]
  2648. };
  2649. }
  2650. if (!isNumber(opt.duration)) {
  2651. opt.duration = 400;
  2652. }
  2653. opt.easing = typeof opt.easing === 'function' ?
  2654. opt.easing :
  2655. (Math[opt.easing] || Math.easeInOutSine);
  2656. opt.curAnim = merge(params);
  2657. objectEach(params, function (val, prop) {
  2658. // Stop current running animation of this property
  2659. stop(el, prop);
  2660. fx = new Fx(el, opt, prop);
  2661. end = null;
  2662. if (prop === 'd' && isArray(params.d)) {
  2663. fx.paths = fx.initPath(el, el.pathArray, params.d);
  2664. fx.toD = params.d;
  2665. start = 0;
  2666. end = 1;
  2667. }
  2668. else if (el.attr) {
  2669. start = el.attr(prop);
  2670. }
  2671. else {
  2672. start = parseFloat(getStyle(el, prop)) || 0;
  2673. if (prop !== 'opacity') {
  2674. unit = 'px';
  2675. }
  2676. }
  2677. if (!end) {
  2678. end = val;
  2679. }
  2680. if (end && end.match && end.match('px')) {
  2681. end = end.replace(/px/g, ''); // #4351
  2682. }
  2683. fx.run(start, end, unit);
  2684. });
  2685. };
  2686. /**
  2687. * Factory to create new series prototypes.
  2688. *
  2689. * @function Highcharts.seriesType
  2690. *
  2691. * @param {string} type
  2692. * The series type name.
  2693. *
  2694. * @param {string} parent
  2695. * The parent series type name. Use `line` to inherit from the basic
  2696. * {@link Series} object.
  2697. *
  2698. * @param {Highcharts.SeriesOptionsType|Highcharts.Dictionary<*>} options
  2699. * The additional default options that are merged with the parent's
  2700. * options.
  2701. *
  2702. * @param {Highcharts.Dictionary<*>} [props]
  2703. * The properties (functions and primitives) to set on the new
  2704. * prototype.
  2705. *
  2706. * @param {Highcharts.Dictionary<*>} [pointProps]
  2707. * Members for a series-specific extension of the {@link Point}
  2708. * prototype if needed.
  2709. *
  2710. * @return {Highcharts.Series}
  2711. * The newly created prototype as extended from {@link Series} or its
  2712. * derivatives.
  2713. */
  2714. // docs: add to API + extending Highcharts
  2715. var seriesType = H.seriesType = function (type,
  2716. parent,
  2717. options,
  2718. props,
  2719. pointProps) {
  2720. var defaultOptions = getOptions(),
  2721. seriesTypes = H.seriesTypes;
  2722. // Merge the options
  2723. defaultOptions.plotOptions[type] = merge(defaultOptions.plotOptions[parent], options);
  2724. // Create the class
  2725. seriesTypes[type] = extendClass(seriesTypes[parent] || function () { }, props);
  2726. seriesTypes[type].prototype.type = type;
  2727. // Create the point class if needed
  2728. if (pointProps) {
  2729. seriesTypes[type].prototype.pointClass =
  2730. extendClass(H.Point, pointProps);
  2731. }
  2732. return seriesTypes[type];
  2733. };
  2734. var serialMode;
  2735. /**
  2736. * Get a unique key for using in internal element id's and pointers. The key is
  2737. * composed of a random hash specific to this Highcharts instance, and a
  2738. * counter.
  2739. *
  2740. * @example
  2741. * var id = uniqueKey(); // => 'highcharts-x45f6hp-0'
  2742. *
  2743. * @function Highcharts.uniqueKey
  2744. *
  2745. * @return {string}
  2746. * A unique key.
  2747. */
  2748. var uniqueKey = H.uniqueKey = (function () {
  2749. var hash = Math.random().toString(36).substring(2, 9) + '-';
  2750. var id = 0;
  2751. return function () {
  2752. return 'highcharts-' + (serialMode ? '' : hash) + id++;
  2753. };
  2754. }());
  2755. /**
  2756. * Activates a serial mode for element IDs provided by
  2757. * {@link Highcharts.uniqueKey}. This mode can be used in automated tests, where
  2758. * a simple comparison of two rendered SVG graphics is needed.
  2759. *
  2760. * **Note:** This is only for testing purposes and will break functionality in
  2761. * webpages with multiple charts.
  2762. *
  2763. * @example
  2764. * if (
  2765. * process &&
  2766. * process.env.NODE_ENV === 'development'
  2767. * ) {
  2768. * Highcharts.useSerialIds(true);
  2769. * }
  2770. *
  2771. * @function Highcharts.useSerialIds
  2772. *
  2773. * @param {boolean} [mode]
  2774. * Changes the state of serial mode.
  2775. *
  2776. * @return {boolean|undefined}
  2777. * State of the serial mode.
  2778. */
  2779. var useSerialIds = H.useSerialIds = function (mode) {
  2780. return (serialMode = pick(mode,
  2781. serialMode));
  2782. };
  2783. var isFunction = H.isFunction = function (obj) {
  2784. return typeof obj === 'function';
  2785. };
  2786. /**
  2787. * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
  2788. * for outside modules wasn't enough because the setOptions method created a new
  2789. * object.
  2790. *
  2791. * @function Highcharts.getOptions
  2792. *
  2793. * @return {Highcharts.Options}
  2794. */
  2795. var getOptions = H.getOptions = function () {
  2796. return H.defaultOptions;
  2797. };
  2798. /**
  2799. * Merge the default options with custom options and return the new options
  2800. * structure. Commonly used for defining reusable templates.
  2801. *
  2802. * @sample highcharts/global/useutc-false Setting a global option
  2803. * @sample highcharts/members/setoptions Applying a global theme
  2804. *
  2805. * @function Highcharts.setOptions
  2806. *
  2807. * @param {Highcharts.Options} options
  2808. * The new custom chart options.
  2809. *
  2810. * @return {Highcharts.Options}
  2811. * Updated options.
  2812. */
  2813. var setOptions = H.setOptions = function (options) {
  2814. // Copy in the default options
  2815. H.defaultOptions = merge(true,
  2816. H.defaultOptions,
  2817. options);
  2818. // Update the time object
  2819. if (options.time || options.global) {
  2820. H.time.update(merge(H.defaultOptions.global, H.defaultOptions.time, options.global, options.time));
  2821. }
  2822. return H.defaultOptions;
  2823. };
  2824. // Register Highcharts as a plugin in jQuery
  2825. if (win.jQuery) {
  2826. /**
  2827. * Highcharts-extended JQuery.
  2828. *
  2829. * @external JQuery
  2830. */
  2831. /**
  2832. * Helper function to return the chart of the current JQuery selector
  2833. * element.
  2834. *
  2835. * @function external:JQuery#highcharts
  2836. *
  2837. * @return {Highcharts.Chart}
  2838. * The chart that is linked to the JQuery selector element.
  2839. */ /**
  2840. * Factory function to create a chart in the current JQuery selector
  2841. * element.
  2842. *
  2843. * @function external:JQuery#highcharts
  2844. *
  2845. * @param {'Chart'|'Map'|'StockChart'|string} [className]
  2846. * Name of the factory class in the Highcharts namespace.
  2847. *
  2848. * @param {Highcharts.Options} [options]
  2849. * The chart options structure.
  2850. *
  2851. * @param {Highcharts.ChartCallbackFunction} [callback]
  2852. * Function to run when the chart has loaded and and all external
  2853. * images are loaded. Defining a
  2854. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  2855. * handler is equivalent.
  2856. *
  2857. * @return {JQuery}
  2858. * The current JQuery selector.
  2859. */
  2860. win.jQuery.fn.highcharts = function () {
  2861. var args = [].slice.call(arguments);
  2862. if (this[0]) { // this[0] is the renderTo div
  2863. // Create the chart
  2864. if (args[0]) {
  2865. new H[ // eslint-disable-line computed-property-spacing, no-new
  2866. // Constructor defaults to Chart
  2867. isString(args[0]) ? args.shift() : 'Chart'](this[0], args[0], args[1]);
  2868. return this;
  2869. }
  2870. // When called without parameters or with the return argument,
  2871. // return an existing chart
  2872. return charts[attr(this[0], 'data-highcharts-chart')];
  2873. }
  2874. };
  2875. }
  2876. // TODO use named exports when supported.
  2877. var utilitiesModule = {
  2878. Fx: H.Fx,
  2879. addEvent: addEvent,
  2880. animate: animate,
  2881. animObject: animObject,
  2882. arrayMax: arrayMax,
  2883. arrayMin: arrayMin,
  2884. attr: attr,
  2885. clamp: clamp,
  2886. clearTimeout: internalClearTimeout,
  2887. correctFloat: correctFloat,
  2888. createElement: createElement,
  2889. css: css,
  2890. defined: defined,
  2891. destroyObjectProperties: destroyObjectProperties,
  2892. discardElement: discardElement,
  2893. erase: erase,
  2894. error: error,
  2895. extend: extend,
  2896. extendClass: extendClass,
  2897. find: find,
  2898. fireEvent: fireEvent,
  2899. format: format,
  2900. getDeferredAnimation: getDeferredAnimation,
  2901. getMagnitude: getMagnitude,
  2902. getNestedProperty: getNestedProperty,
  2903. getOptions: getOptions,
  2904. getStyle: getStyle,
  2905. inArray: inArray,
  2906. isArray: isArray,
  2907. isClass: isClass,
  2908. isDOMElement: isDOMElement,
  2909. isFunction: isFunction,
  2910. isNumber: isNumber,
  2911. isObject: isObject,
  2912. isString: isString,
  2913. merge: merge,
  2914. normalizeTickInterval: normalizeTickInterval,
  2915. numberFormat: numberFormat,
  2916. objectEach: objectEach,
  2917. offset: offset,
  2918. pad: pad,
  2919. pick: pick,
  2920. pInt: pInt,
  2921. relativeLength: relativeLength,
  2922. removeEvent: removeEvent,
  2923. seriesType: seriesType,
  2924. setAnimation: setAnimation,
  2925. setOptions: setOptions,
  2926. splat: splat,
  2927. stableSort: stableSort,
  2928. stop: stop,
  2929. syncTimeout: syncTimeout,
  2930. timeUnits: timeUnits,
  2931. uniqueKey: uniqueKey,
  2932. useSerialIds: useSerialIds,
  2933. wrap: wrap
  2934. };
  2935. return utilitiesModule;
  2936. });
  2937. _registerModule(_modules, 'Core/Color.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  2938. /* *
  2939. *
  2940. * (c) 2010-2020 Torstein Honsi
  2941. *
  2942. * License: www.highcharts.com/license
  2943. *
  2944. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2945. *
  2946. * */
  2947. /**
  2948. * A valid color to be parsed and handled by Highcharts. Highcharts internally
  2949. * supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
  2950. * rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
  2951. * browsers and displayed correctly, but Highcharts is not able to process them
  2952. * and apply concepts like opacity and brightening.
  2953. *
  2954. * @typedef {string} Highcharts.ColorString
  2955. */
  2956. /**
  2957. * A valid color type than can be parsed and handled by Highcharts. It can be a
  2958. * color string, a gradient object, or a pattern object.
  2959. *
  2960. * @typedef {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} Highcharts.ColorType
  2961. */
  2962. /**
  2963. * Gradient options instead of a solid color.
  2964. *
  2965. * @example
  2966. * // Linear gradient used as a color option
  2967. * color: {
  2968. * linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
  2969. * stops: [
  2970. * [0, '#003399'], // start
  2971. * [0.5, '#ffffff'], // middle
  2972. * [1, '#3366AA'] // end
  2973. * ]
  2974. * }
  2975. *
  2976. * @interface Highcharts.GradientColorObject
  2977. */ /**
  2978. * Holds an object that defines the start position and the end position relative
  2979. * to the shape.
  2980. * @name Highcharts.GradientColorObject#linearGradient
  2981. * @type {Highcharts.LinearGradientColorObject|undefined}
  2982. */ /**
  2983. * Holds an object that defines the center position and the radius.
  2984. * @name Highcharts.GradientColorObject#radialGradient
  2985. * @type {Highcharts.RadialGradientColorObject|undefined}
  2986. */ /**
  2987. * The first item in each tuple is the position in the gradient, where 0 is the
  2988. * start of the gradient and 1 is the end of the gradient. Multiple stops can be
  2989. * applied. The second item is the color for each stop. This color can also be
  2990. * given in the rgba format.
  2991. * @name Highcharts.GradientColorObject#stops
  2992. * @type {Array<Highcharts.GradientColorStopObject>}
  2993. */
  2994. /**
  2995. * Color stop tuple.
  2996. *
  2997. * @see Highcharts.GradientColorObject
  2998. *
  2999. * @interface Highcharts.GradientColorStopObject
  3000. */ /**
  3001. * @name Highcharts.GradientColorStopObject#0
  3002. * @type {number}
  3003. */ /**
  3004. * @name Highcharts.GradientColorStopObject#1
  3005. * @type {Highcharts.ColorString}
  3006. */ /**
  3007. * @name Highcharts.GradientColorStopObject#color
  3008. * @type {Highcharts.Color|undefined}
  3009. */
  3010. /**
  3011. * Defines the start position and the end position for a gradient relative
  3012. * to the shape. Start position (x1, y1) and end position (x2, y2) are relative
  3013. * to the shape, where 0 means top/left and 1 is bottom/right.
  3014. *
  3015. * @interface Highcharts.LinearGradientColorObject
  3016. */ /**
  3017. * Start horizontal position of the gradient. Float ranges 0-1.
  3018. * @name Highcharts.LinearGradientColorObject#x1
  3019. * @type {number}
  3020. */ /**
  3021. * End horizontal position of the gradient. Float ranges 0-1.
  3022. * @name Highcharts.LinearGradientColorObject#x2
  3023. * @type {number}
  3024. */ /**
  3025. * Start vertical position of the gradient. Float ranges 0-1.
  3026. * @name Highcharts.LinearGradientColorObject#y1
  3027. * @type {number}
  3028. */ /**
  3029. * End vertical position of the gradient. Float ranges 0-1.
  3030. * @name Highcharts.LinearGradientColorObject#y2
  3031. * @type {number}
  3032. */
  3033. /**
  3034. * Defines the center position and the radius for a gradient.
  3035. *
  3036. * @interface Highcharts.RadialGradientColorObject
  3037. */ /**
  3038. * Center horizontal position relative to the shape. Float ranges 0-1.
  3039. * @name Highcharts.RadialGradientColorObject#cx
  3040. * @type {number}
  3041. */ /**
  3042. * Center vertical position relative to the shape. Float ranges 0-1.
  3043. * @name Highcharts.RadialGradientColorObject#cy
  3044. * @type {number}
  3045. */ /**
  3046. * Radius relative to the shape. Float ranges 0-1.
  3047. * @name Highcharts.RadialGradientColorObject#r
  3048. * @type {number}
  3049. */
  3050. var isNumber = U.isNumber,
  3051. merge = U.merge,
  3052. pInt = U.pInt;
  3053. /* eslint-disable no-invalid-this, valid-jsdoc */
  3054. /**
  3055. * Handle color operations. Some object methods are chainable.
  3056. *
  3057. * @class
  3058. * @name Highcharts.Color
  3059. *
  3060. * @param {Highcharts.ColorType} input
  3061. * The input color in either rbga or hex format
  3062. */
  3063. var Color = /** @class */ (function () {
  3064. /* *
  3065. *
  3066. * Constructors
  3067. *
  3068. * */
  3069. function Color(input) {
  3070. // Collection of parsers. This can be extended from the outside by pushing
  3071. // parsers to Highcharts.Color.prototype.parsers.
  3072. this.parsers = [{
  3073. // RGBA color
  3074. // eslint-disable-next-line max-len
  3075. 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*\)/,
  3076. parse: function (result) {
  3077. return [
  3078. pInt(result[1]),
  3079. pInt(result[2]),
  3080. pInt(result[3]),
  3081. parseFloat(result[4], 10)
  3082. ];
  3083. }
  3084. }, {
  3085. // RGB color
  3086. regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
  3087. parse: function (result) {
  3088. return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
  3089. }
  3090. }];
  3091. this.rgba = [];
  3092. // Backwards compatibility, allow instanciation without new (#13053)
  3093. if (!(this instanceof Color)) {
  3094. return new Color(input);
  3095. }
  3096. this.init(input);
  3097. }
  3098. /* *
  3099. *
  3100. * Static Functions
  3101. *
  3102. * */
  3103. /**
  3104. * Creates a color instance out of a color string or object.
  3105. *
  3106. * @function Highcharts.Color.parse
  3107. *
  3108. * @param {Highcharts.ColorType} input
  3109. * The input color in either rbga or hex format.
  3110. *
  3111. * @return {Highcharts.Color}
  3112. * Color instance.
  3113. */
  3114. Color.parse = function (input) {
  3115. return new Color(input);
  3116. };
  3117. /* *
  3118. *
  3119. * Functions
  3120. *
  3121. * */
  3122. /**
  3123. * Parse the input color to rgba array
  3124. *
  3125. * @private
  3126. * @function Highcharts.Color#init
  3127. *
  3128. * @param {Highcharts.ColorType} input
  3129. * The input color in either rbga or hex format
  3130. *
  3131. * @return {void}
  3132. */
  3133. Color.prototype.init = function (input) {
  3134. var result,
  3135. rgba,
  3136. i,
  3137. parser,
  3138. len;
  3139. this.input = input = Color.names[input && input.toLowerCase ?
  3140. input.toLowerCase() :
  3141. ''] || input;
  3142. // Gradients
  3143. if (input && input.stops) {
  3144. this.stops = input.stops.map(function (stop) {
  3145. return new Color(stop[1]);
  3146. });
  3147. // Solid colors
  3148. }
  3149. else {
  3150. // Bitmasking as input[0] is not working for legacy IE.
  3151. if (input &&
  3152. input.charAt &&
  3153. input.charAt() === '#') {
  3154. len = input.length;
  3155. input = parseInt(input.substr(1), 16);
  3156. // Handle long-form, e.g. #AABBCC
  3157. if (len === 7) {
  3158. rgba = [
  3159. (input & 0xFF0000) >> 16,
  3160. (input & 0xFF00) >> 8,
  3161. (input & 0xFF),
  3162. 1
  3163. ];
  3164. // Handle short-form, e.g. #ABC
  3165. // In short form, the value is assumed to be the same
  3166. // for both nibbles for each component. e.g. #ABC = #AABBCC
  3167. }
  3168. else if (len === 4) {
  3169. rgba = [
  3170. (((input & 0xF00) >> 4) |
  3171. (input & 0xF00) >> 8),
  3172. (((input & 0xF0) >> 4) |
  3173. (input & 0xF0)),
  3174. ((input & 0xF) << 4) | (input & 0xF),
  3175. 1
  3176. ];
  3177. }
  3178. }
  3179. // Otherwise, check regex parsers
  3180. if (!rgba) {
  3181. i = this.parsers.length;
  3182. while (i-- && !rgba) {
  3183. parser = this.parsers[i];
  3184. result = parser.regex.exec(input);
  3185. if (result) {
  3186. rgba = parser.parse(result);
  3187. }
  3188. }
  3189. }
  3190. }
  3191. this.rgba = rgba || [];
  3192. };
  3193. /**
  3194. * Return the color or gradient stops in the specified format
  3195. *
  3196. * @function Highcharts.Color#get
  3197. *
  3198. * @param {string} [format]
  3199. * Possible values are 'a', 'rgb', 'rgba' (default).
  3200. *
  3201. * @return {Highcharts.ColorType}
  3202. * This color as a string or gradient stops.
  3203. */
  3204. Color.prototype.get = function (format) {
  3205. var input = this.input,
  3206. rgba = this.rgba,
  3207. ret;
  3208. if (typeof this.stops !== 'undefined') {
  3209. ret = merge(input);
  3210. ret.stops = [].concat(ret.stops);
  3211. this.stops.forEach(function (stop, i) {
  3212. ret.stops[i] = [
  3213. ret.stops[i][0],
  3214. stop.get(format)
  3215. ];
  3216. });
  3217. // it's NaN if gradient colors on a column chart
  3218. }
  3219. else if (rgba && isNumber(rgba[0])) {
  3220. if (format === 'rgb' || (!format && rgba[3] === 1)) {
  3221. ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
  3222. }
  3223. else if (format === 'a') {
  3224. ret = rgba[3];
  3225. }
  3226. else {
  3227. ret = 'rgba(' + rgba.join(',') + ')';
  3228. }
  3229. }
  3230. else {
  3231. ret = input;
  3232. }
  3233. return ret;
  3234. };
  3235. /**
  3236. * Brighten the color instance.
  3237. *
  3238. * @function Highcharts.Color#brighten
  3239. *
  3240. * @param {number} alpha
  3241. * The alpha value.
  3242. *
  3243. * @return {Highcharts.Color}
  3244. * This color with modifications.
  3245. */
  3246. Color.prototype.brighten = function (alpha) {
  3247. var i,
  3248. rgba = this.rgba;
  3249. if (this.stops) {
  3250. this.stops.forEach(function (stop) {
  3251. stop.brighten(alpha);
  3252. });
  3253. }
  3254. else if (isNumber(alpha) && alpha !== 0) {
  3255. for (i = 0; i < 3; i++) {
  3256. rgba[i] += pInt(alpha * 255);
  3257. if (rgba[i] < 0) {
  3258. rgba[i] = 0;
  3259. }
  3260. if (rgba[i] > 255) {
  3261. rgba[i] = 255;
  3262. }
  3263. }
  3264. }
  3265. return this;
  3266. };
  3267. /**
  3268. * Set the color's opacity to a given alpha value.
  3269. *
  3270. * @function Highcharts.Color#setOpacity
  3271. *
  3272. * @param {number} alpha
  3273. * Opacity between 0 and 1.
  3274. *
  3275. * @return {Highcharts.Color}
  3276. * Color with modifications.
  3277. */
  3278. Color.prototype.setOpacity = function (alpha) {
  3279. this.rgba[3] = alpha;
  3280. return this;
  3281. };
  3282. /**
  3283. * Return an intermediate color between two colors.
  3284. *
  3285. * @function Highcharts.Color#tweenTo
  3286. *
  3287. * @param {Highcharts.Color} to
  3288. * The color object to tween to.
  3289. *
  3290. * @param {number} pos
  3291. * The intermediate position, where 0 is the from color (current
  3292. * color item), and 1 is the `to` color.
  3293. *
  3294. * @return {Highcharts.ColorString}
  3295. * The intermediate color in rgba notation.
  3296. */
  3297. Color.prototype.tweenTo = function (to, pos) {
  3298. // Check for has alpha, because rgba colors perform worse due to lack of
  3299. // support in WebKit.
  3300. var fromRgba = this.rgba,
  3301. toRgba = to.rgba,
  3302. hasAlpha,
  3303. ret;
  3304. // Unsupported color, return to-color (#3920, #7034)
  3305. if (!toRgba.length || !fromRgba || !fromRgba.length) {
  3306. ret = to.input || 'none';
  3307. // Interpolate
  3308. }
  3309. else {
  3310. hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
  3311. ret = (hasAlpha ? 'rgba(' : 'rgb(') +
  3312. Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
  3313. ',' +
  3314. Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
  3315. ',' +
  3316. Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
  3317. (hasAlpha ?
  3318. (',' +
  3319. (toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))) :
  3320. '') +
  3321. ')';
  3322. }
  3323. return ret;
  3324. };
  3325. /* *
  3326. *
  3327. * Static Properties
  3328. *
  3329. * */
  3330. // Collection of named colors. Can be extended from the outside by adding
  3331. // colors to Highcharts.Color.names.
  3332. Color.names = {
  3333. white: '#ffffff',
  3334. black: '#000000'
  3335. };
  3336. return Color;
  3337. }());
  3338. H.Color = Color;
  3339. /**
  3340. * Creates a color instance out of a color string.
  3341. *
  3342. * @function Highcharts.color
  3343. *
  3344. * @param {Highcharts.ColorType} input
  3345. * The input color in either rbga or hex format
  3346. *
  3347. * @return {Highcharts.Color}
  3348. * Color instance
  3349. */
  3350. H.color = Color.parse;
  3351. return H.Color;
  3352. });
  3353. _registerModule(_modules, 'Core/Renderer/SVG/SVGElement.js', [_modules['Core/Color.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Color, H, U) {
  3354. /* *
  3355. *
  3356. * (c) 2010-2020 Torstein Honsi
  3357. *
  3358. * License: www.highcharts.com/license
  3359. *
  3360. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3361. *
  3362. * */
  3363. var deg2rad = H.deg2rad,
  3364. doc = H.doc,
  3365. hasTouch = H.hasTouch,
  3366. isFirefox = H.isFirefox,
  3367. noop = H.noop,
  3368. svg = H.svg,
  3369. SVG_NS = H.SVG_NS,
  3370. win = H.win;
  3371. var animate = U.animate,
  3372. animObject = U.animObject,
  3373. attr = U.attr,
  3374. createElement = U.createElement,
  3375. css = U.css,
  3376. defined = U.defined,
  3377. erase = U.erase,
  3378. extend = U.extend,
  3379. fireEvent = U.fireEvent,
  3380. isArray = U.isArray,
  3381. isFunction = U.isFunction,
  3382. isNumber = U.isNumber,
  3383. isString = U.isString,
  3384. merge = U.merge,
  3385. objectEach = U.objectEach,
  3386. pick = U.pick,
  3387. pInt = U.pInt,
  3388. stop = U.stop,
  3389. syncTimeout = U.syncTimeout,
  3390. uniqueKey = U.uniqueKey;
  3391. /**
  3392. * The horizontal alignment of an element.
  3393. *
  3394. * @typedef {"center"|"left"|"right"} Highcharts.AlignValue
  3395. */
  3396. /**
  3397. * Options to align the element relative to the chart or another box.
  3398. *
  3399. * @interface Highcharts.AlignObject
  3400. */ /**
  3401. * Horizontal alignment. Can be one of `left`, `center` and `right`.
  3402. *
  3403. * @name Highcharts.AlignObject#align
  3404. * @type {Highcharts.AlignValue|undefined}
  3405. *
  3406. * @default left
  3407. */ /**
  3408. * Vertical alignment. Can be one of `top`, `middle` and `bottom`.
  3409. *
  3410. * @name Highcharts.AlignObject#verticalAlign
  3411. * @type {Highcharts.VerticalAlignValue|undefined}
  3412. *
  3413. * @default top
  3414. */ /**
  3415. * Horizontal pixel offset from alignment.
  3416. *
  3417. * @name Highcharts.AlignObject#x
  3418. * @type {number|undefined}
  3419. *
  3420. * @default 0
  3421. */ /**
  3422. * Vertical pixel offset from alignment.
  3423. *
  3424. * @name Highcharts.AlignObject#y
  3425. * @type {number|undefined}
  3426. *
  3427. * @default 0
  3428. */ /**
  3429. * Use the `transform` attribute with translateX and translateY custom
  3430. * attributes to align this elements rather than `x` and `y` attributes.
  3431. *
  3432. * @name Highcharts.AlignObject#alignByTranslate
  3433. * @type {boolean|undefined}
  3434. *
  3435. * @default false
  3436. */
  3437. /**
  3438. * Bounding box of an element.
  3439. *
  3440. * @interface Highcharts.BBoxObject
  3441. * @extends Highcharts.PositionObject
  3442. */ /**
  3443. * Height of the bounding box.
  3444. *
  3445. * @name Highcharts.BBoxObject#height
  3446. * @type {number}
  3447. */ /**
  3448. * Width of the bounding box.
  3449. *
  3450. * @name Highcharts.BBoxObject#width
  3451. * @type {number}
  3452. */ /**
  3453. * Horizontal position of the bounding box.
  3454. *
  3455. * @name Highcharts.BBoxObject#x
  3456. * @type {number}
  3457. */ /**
  3458. * Vertical position of the bounding box.
  3459. *
  3460. * @name Highcharts.BBoxObject#y
  3461. * @type {number}
  3462. */
  3463. /**
  3464. * An object of key-value pairs for SVG attributes. Attributes in Highcharts
  3465. * elements for the most parts correspond to SVG, but some are specific to
  3466. * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
  3467. * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
  3468. * attributes containing a hyphen are _not_ camel-cased, they should be
  3469. * quoted to preserve the hyphen.
  3470. *
  3471. * @example
  3472. * {
  3473. * 'stroke': '#ff0000', // basic
  3474. * 'stroke-width': 2, // hyphenated
  3475. * 'rotation': 45 // custom
  3476. * 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
  3477. * }
  3478. *
  3479. * @interface Highcharts.SVGAttributes
  3480. */ /**
  3481. * @name Highcharts.SVGAttributes#[key:string]
  3482. * @type {*}
  3483. */ /**
  3484. * @name Highcharts.SVGAttributes#d
  3485. * @type {string|Highcharts.SVGPathArray|undefined}
  3486. */ /**
  3487. * @name Highcharts.SVGAttributes#fill
  3488. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  3489. */ /**
  3490. * @name Highcharts.SVGAttributes#inverted
  3491. * @type {boolean|undefined}
  3492. */ /**
  3493. * @name Highcharts.SVGAttributes#matrix
  3494. * @type {Array<number>|undefined}
  3495. */ /**
  3496. * @name Highcharts.SVGAttributes#rotation
  3497. * @type {number|undefined}
  3498. */ /**
  3499. * @name Highcharts.SVGAttributes#rotationOriginX
  3500. * @type {number|undefined}
  3501. */ /**
  3502. * @name Highcharts.SVGAttributes#rotationOriginY
  3503. * @type {number|undefined}
  3504. */ /**
  3505. * @name Highcharts.SVGAttributes#scaleX
  3506. * @type {number|undefined}
  3507. */ /**
  3508. * @name Highcharts.SVGAttributes#scaleY
  3509. * @type {number|undefined}
  3510. */ /**
  3511. * @name Highcharts.SVGAttributes#stroke
  3512. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  3513. */ /**
  3514. * @name Highcharts.SVGAttributes#style
  3515. * @type {string|Highcharts.CSSObject|undefined}
  3516. */ /**
  3517. * @name Highcharts.SVGAttributes#translateX
  3518. * @type {number|undefined}
  3519. */ /**
  3520. * @name Highcharts.SVGAttributes#translateY
  3521. * @type {number|undefined}
  3522. */ /**
  3523. * @name Highcharts.SVGAttributes#zIndex
  3524. * @type {number|undefined}
  3525. */
  3526. /**
  3527. * An SVG DOM element. The type is a reference to the regular SVGElement in the
  3528. * global scope.
  3529. *
  3530. * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
  3531. *
  3532. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  3533. */
  3534. /**
  3535. * The vertical alignment of an element.
  3536. *
  3537. * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignValue
  3538. */
  3539. ''; // detach doclets above
  3540. /* eslint-disable no-invalid-this, valid-jsdoc */
  3541. /**
  3542. * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
  3543. * rendering layer of Highcharts. Combined with the
  3544. * {@link Highcharts.SVGRenderer}
  3545. * object, these prototypes allow freeform annotation in the charts or even in
  3546. * HTML pages without instanciating a chart. The SVGElement can also wrap HTML
  3547. * labels, when `text` or `label` elements are created with the `useHTML`
  3548. * parameter.
  3549. *
  3550. * The SVGElement instances are created through factory functions on the
  3551. * {@link Highcharts.SVGRenderer}
  3552. * object, like
  3553. * {@link Highcharts.SVGRenderer#rect|rect},
  3554. * {@link Highcharts.SVGRenderer#path|path},
  3555. * {@link Highcharts.SVGRenderer#text|text},
  3556. * {@link Highcharts.SVGRenderer#label|label},
  3557. * {@link Highcharts.SVGRenderer#g|g}
  3558. * and more.
  3559. *
  3560. * @class
  3561. * @name Highcharts.SVGElement
  3562. */
  3563. var SVGElement = /** @class */ (function () {
  3564. function SVGElement() {
  3565. /* *
  3566. *
  3567. * Properties
  3568. *
  3569. * */
  3570. this.element = void 0;
  3571. this.height = void 0;
  3572. this.opacity = 1; // Default base for animation
  3573. this.renderer = void 0;
  3574. this.SVG_NS = SVG_NS;
  3575. // Custom attributes used for symbols, these should be filtered out when
  3576. // setting SVGElement attributes (#9375).
  3577. this.symbolCustomAttribs = [
  3578. 'x',
  3579. 'y',
  3580. 'width',
  3581. 'height',
  3582. 'r',
  3583. 'start',
  3584. 'end',
  3585. 'innerR',
  3586. 'anchorX',
  3587. 'anchorY',
  3588. 'rounded'
  3589. ];
  3590. this.width = void 0;
  3591. }
  3592. /* *
  3593. *
  3594. * Functions
  3595. *
  3596. * */
  3597. /**
  3598. * Get the current value of an attribute or pseudo attribute,
  3599. * used mainly for animation. Called internally from
  3600. * the {@link Highcharts.SVGRenderer#attr} function.
  3601. *
  3602. * @private
  3603. * @function Highcharts.SVGElement#_defaultGetter
  3604. *
  3605. * @param {string} key
  3606. * Property key.
  3607. *
  3608. * @return {number|string}
  3609. * Property value.
  3610. */
  3611. SVGElement.prototype._defaultGetter = function (key) {
  3612. var ret = pick(this[key + 'Value'], // align getter
  3613. this[key],
  3614. this.element ? this.element.getAttribute(key) : null, 0);
  3615. if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
  3616. ret = parseFloat(ret);
  3617. }
  3618. return ret;
  3619. };
  3620. /**
  3621. * @private
  3622. * @function Highcharts.SVGElement#_defaultSetter
  3623. *
  3624. * @param {string} value
  3625. *
  3626. * @param {string} key
  3627. *
  3628. * @param {Highcharts.SVGDOMElement} element
  3629. *
  3630. * @return {void}
  3631. */
  3632. SVGElement.prototype._defaultSetter = function (value, key, element) {
  3633. element.setAttribute(key, value);
  3634. };
  3635. /**
  3636. * Add the element to the DOM. All elements must be added this way.
  3637. *
  3638. * @sample highcharts/members/renderer-g
  3639. * Elements added to a group
  3640. *
  3641. * @function Highcharts.SVGElement#add
  3642. *
  3643. * @param {Highcharts.SVGElement} [parent]
  3644. * The parent item to add it to. If undefined, the element is added
  3645. * to the {@link Highcharts.SVGRenderer.box}.
  3646. *
  3647. * @return {Highcharts.SVGElement}
  3648. * Returns the SVGElement for chaining.
  3649. */
  3650. SVGElement.prototype.add = function (parent) {
  3651. var renderer = this.renderer,
  3652. element = this.element,
  3653. inserted;
  3654. if (parent) {
  3655. this.parentGroup = parent;
  3656. }
  3657. // Mark as inverted
  3658. this.parentInverted = parent && parent.inverted;
  3659. // Build formatted text
  3660. if (typeof this.textStr !== 'undefined' &&
  3661. this.element.nodeName === 'text' // Not for SVGLabel instances
  3662. ) {
  3663. renderer.buildText(this);
  3664. }
  3665. // Mark as added
  3666. this.added = true;
  3667. // If we're adding to renderer root, or other elements in the group
  3668. // have a z index, we need to handle it
  3669. if (!parent || parent.handleZ || this.zIndex) {
  3670. inserted = this.zIndexSetter();
  3671. }
  3672. // If zIndex is not handled, append at the end
  3673. if (!inserted) {
  3674. (parent ?
  3675. parent.element :
  3676. renderer.box).appendChild(element);
  3677. }
  3678. // fire an event for internal hooks
  3679. if (this.onAdd) {
  3680. this.onAdd();
  3681. }
  3682. return this;
  3683. };
  3684. /**
  3685. * Add a class name to an element.
  3686. *
  3687. * @function Highcharts.SVGElement#addClass
  3688. *
  3689. * @param {string} className
  3690. * The new class name to add.
  3691. *
  3692. * @param {boolean} [replace=false]
  3693. * When true, the existing class name(s) will be overwritten with the new
  3694. * one. When false, the new one is added.
  3695. *
  3696. * @return {Highcharts.SVGElement}
  3697. * Return the SVG element for chainability.
  3698. */
  3699. SVGElement.prototype.addClass = function (className, replace) {
  3700. var currentClassName = replace ? '' : (this.attr('class') || '');
  3701. // Trim the string and remove duplicates
  3702. className = (className || '')
  3703. .split(/ /g)
  3704. .reduce(function (newClassName, name) {
  3705. if (currentClassName.indexOf(name) === -1) {
  3706. newClassName.push(name);
  3707. }
  3708. return newClassName;
  3709. }, (currentClassName ?
  3710. [currentClassName] :
  3711. []))
  3712. .join(' ');
  3713. if (className !== currentClassName) {
  3714. this.attr('class', className);
  3715. }
  3716. return this;
  3717. };
  3718. /**
  3719. * This method is executed in the end of `attr()`, after setting all
  3720. * attributes in the hash. In can be used to efficiently consolidate
  3721. * multiple attributes in one SVG property -- e.g., translate, rotate and
  3722. * scale are merged in one "transform" attribute in the SVG node.
  3723. *
  3724. * @private
  3725. * @function Highcharts.SVGElement#afterSetters
  3726. */
  3727. SVGElement.prototype.afterSetters = function () {
  3728. // Update transform. Do this outside the loop to prevent redundant
  3729. // updating for batch setting of attributes.
  3730. if (this.doTransform) {
  3731. this.updateTransform();
  3732. this.doTransform = false;
  3733. }
  3734. };
  3735. /**
  3736. * Align the element relative to the chart or another box.
  3737. *
  3738. * @function Highcharts.SVGElement#align
  3739. *
  3740. * @param {Highcharts.AlignObject} [alignOptions]
  3741. * The alignment options. The function can be called without this
  3742. * parameter in order to re-align an element after the box has been
  3743. * updated.
  3744. *
  3745. * @param {boolean} [alignByTranslate]
  3746. * Align element by translation.
  3747. *
  3748. * @param {string|Highcharts.BBoxObject} [box]
  3749. * The box to align to, needs a width and height. When the box is a
  3750. * string, it refers to an object in the Renderer. For example, when
  3751. * box is `spacingBox`, it refers to `Renderer.spacingBox` which
  3752. * holds `width`, `height`, `x` and `y` properties.
  3753. *
  3754. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  3755. */
  3756. SVGElement.prototype.align = function (alignOptions, alignByTranslate, box) {
  3757. var align,
  3758. vAlign,
  3759. x,
  3760. y,
  3761. attribs = {},
  3762. alignTo,
  3763. renderer = this.renderer,
  3764. alignedObjects = renderer.alignedObjects,
  3765. alignFactor,
  3766. vAlignFactor;
  3767. // First call on instanciate
  3768. if (alignOptions) {
  3769. this.alignOptions = alignOptions;
  3770. this.alignByTranslate = alignByTranslate;
  3771. if (!box || isString(box)) {
  3772. this.alignTo = alignTo = box || 'renderer';
  3773. // prevent duplicates, like legendGroup after resize
  3774. erase(alignedObjects, this);
  3775. alignedObjects.push(this);
  3776. box = void 0; // reassign it below
  3777. }
  3778. // When called on resize, no arguments are supplied
  3779. }
  3780. else {
  3781. alignOptions = this.alignOptions;
  3782. alignByTranslate = this.alignByTranslate;
  3783. alignTo = this.alignTo;
  3784. }
  3785. box = pick(box, renderer[alignTo], renderer);
  3786. // Assign variables
  3787. align = alignOptions.align;
  3788. vAlign = alignOptions.verticalAlign;
  3789. // default: left align
  3790. x = (box.x || 0) + (alignOptions.x || 0);
  3791. // default: top align
  3792. y = (box.y || 0) + (alignOptions.y || 0);
  3793. // Align
  3794. if (align === 'right') {
  3795. alignFactor = 1;
  3796. }
  3797. else if (align === 'center') {
  3798. alignFactor = 2;
  3799. }
  3800. if (alignFactor) {
  3801. x += (box.width - (alignOptions.width || 0)) /
  3802. alignFactor;
  3803. }
  3804. attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
  3805. // Vertical align
  3806. if (vAlign === 'bottom') {
  3807. vAlignFactor = 1;
  3808. }
  3809. else if (vAlign === 'middle') {
  3810. vAlignFactor = 2;
  3811. }
  3812. if (vAlignFactor) {
  3813. y += (box.height - (alignOptions.height || 0)) /
  3814. vAlignFactor;
  3815. }
  3816. attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
  3817. // Animate only if already placed
  3818. this[this.placed ? 'animate' : 'attr'](attribs);
  3819. this.placed = true;
  3820. this.alignAttr = attribs;
  3821. return this;
  3822. };
  3823. /**
  3824. * @private
  3825. * @function Highcharts.SVGElement#alignSetter
  3826. * @param {"left"|"center"|"right"} value
  3827. */
  3828. SVGElement.prototype.alignSetter = function (value) {
  3829. var convert = {
  3830. left: 'start',
  3831. center: 'middle',
  3832. right: 'end'
  3833. };
  3834. if (convert[value]) {
  3835. this.alignValue = value;
  3836. this.element.setAttribute('text-anchor', convert[value]);
  3837. }
  3838. };
  3839. /**
  3840. * Animate to given attributes or CSS properties.
  3841. *
  3842. * @sample highcharts/members/element-on/
  3843. * Setting some attributes by animation
  3844. *
  3845. * @function Highcharts.SVGElement#animate
  3846. *
  3847. * @param {Highcharts.SVGAttributes} params
  3848. * SVG attributes or CSS to animate.
  3849. *
  3850. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [options]
  3851. * Animation options.
  3852. *
  3853. * @param {Function} [complete]
  3854. * Function to perform at the end of animation.
  3855. *
  3856. * @return {Highcharts.SVGElement}
  3857. * Returns the SVGElement for chaining.
  3858. */
  3859. SVGElement.prototype.animate = function (params, options, complete) {
  3860. var _this = this;
  3861. var animOptions = animObject(pick(options,
  3862. this.renderer.globalAnimation,
  3863. true)),
  3864. deferTime = animOptions.defer;
  3865. // When the page is hidden save resources in the background by not
  3866. // running animation at all (#9749).
  3867. if (pick(doc.hidden, doc.msHidden, doc.webkitHidden, false)) {
  3868. animOptions.duration = 0;
  3869. }
  3870. if (animOptions.duration !== 0) {
  3871. // allows using a callback with the global animation without
  3872. // overwriting it
  3873. if (complete) {
  3874. animOptions.complete = complete;
  3875. }
  3876. // If defer option is defined delay the animation #12901
  3877. syncTimeout(function () {
  3878. if (_this.element) {
  3879. animate(_this, params, animOptions);
  3880. }
  3881. }, deferTime);
  3882. }
  3883. else {
  3884. this.attr(params, void 0, complete);
  3885. // Call the end step synchronously
  3886. objectEach(params, function (val, prop) {
  3887. if (animOptions.step) {
  3888. animOptions.step.call(this, val, { prop: prop, pos: 1 });
  3889. }
  3890. }, this);
  3891. }
  3892. return this;
  3893. };
  3894. /**
  3895. * Apply a text outline through a custom CSS property, by copying the text
  3896. * element and apply stroke to the copy. Used internally. Contrast checks at
  3897. * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
  3898. *
  3899. * @example
  3900. * // Specific color
  3901. * text.css({
  3902. * textOutline: '1px black'
  3903. * });
  3904. * // Automatic contrast
  3905. * text.css({
  3906. * color: '#000000', // black text
  3907. * textOutline: '1px contrast' // => white outline
  3908. * });
  3909. *
  3910. * @private
  3911. * @function Highcharts.SVGElement#applyTextOutline
  3912. *
  3913. * @param {string} textOutline
  3914. * A custom CSS `text-outline` setting, defined by `width color`.
  3915. */
  3916. SVGElement.prototype.applyTextOutline = function (textOutline) {
  3917. var elem = this.element,
  3918. tspans,
  3919. hasContrast = textOutline.indexOf('contrast') !== -1,
  3920. styles = {},
  3921. color,
  3922. strokeWidth,
  3923. firstRealChild;
  3924. // When the text shadow is set to contrast, use dark stroke for light
  3925. // text and vice versa.
  3926. if (hasContrast) {
  3927. styles.textOutline = textOutline = textOutline.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
  3928. }
  3929. // Extract the stroke width and color
  3930. textOutline = textOutline.split(' ');
  3931. color = textOutline[textOutline.length - 1];
  3932. strokeWidth = textOutline[0];
  3933. if (strokeWidth && strokeWidth !== 'none' && H.svg) {
  3934. this.fakeTS = true; // Fake text shadow
  3935. tspans = [].slice.call(elem.getElementsByTagName('tspan'));
  3936. // In order to get the right y position of the clone,
  3937. // copy over the y setter
  3938. this.ySetter = this.xSetter;
  3939. // Since the stroke is applied on center of the actual outline, we
  3940. // need to double it to get the correct stroke-width outside the
  3941. // glyphs.
  3942. strokeWidth = strokeWidth.replace(/(^[\d\.]+)(.*?)$/g, function (match, digit, unit) {
  3943. return (2 * digit) + unit;
  3944. });
  3945. // Remove shadows from previous runs.
  3946. this.removeTextOutline(tspans);
  3947. // Check if the element contains RTL characters.
  3948. // Comparing against Hebrew and Arabic characters,
  3949. // excluding Arabic digits. Source:
  3950. // https://www.unicode.org/Public/UNIDATA/extracted/DerivedBidiClass.txt
  3951. var isRTL_1 = elem.textContent ?
  3952. /^[\u0591-\u065F\u066A-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/
  3953. .test(elem.textContent) : false;
  3954. // For each of the tspans, create a stroked copy behind it.
  3955. firstRealChild = elem.firstChild;
  3956. tspans.forEach(function (tspan, y) {
  3957. var clone;
  3958. // Let the first line start at the correct X position
  3959. if (y === 0) {
  3960. tspan.setAttribute('x', elem.getAttribute('x'));
  3961. y = elem.getAttribute('y');
  3962. tspan.setAttribute('y', y || 0);
  3963. if (y === null) {
  3964. elem.setAttribute('y', 0);
  3965. }
  3966. }
  3967. // Create the clone and apply outline properties.
  3968. // For RTL elements apply outline properties for orginal element
  3969. // to prevent outline from overlapping the text.
  3970. // For RTL in Firefox keep the orginal order (#10162).
  3971. clone = tspan.cloneNode(true);
  3972. attr((isRTL_1 && !isFirefox) ? tspan : clone, {
  3973. 'class': 'highcharts-text-outline',
  3974. fill: color,
  3975. stroke: color,
  3976. 'stroke-width': strokeWidth,
  3977. 'stroke-linejoin': 'round'
  3978. });
  3979. elem.insertBefore(clone, firstRealChild);
  3980. });
  3981. // Create a whitespace between tspan and clone,
  3982. // to fix the display of Arabic characters in Firefox.
  3983. if (isRTL_1 && isFirefox && tspans[0]) {
  3984. var whitespace = tspans[0].cloneNode(true);
  3985. whitespace.textContent = ' ';
  3986. elem.insertBefore(whitespace, firstRealChild);
  3987. }
  3988. }
  3989. };
  3990. /**
  3991. * @function Highcharts.SVGElement#attr
  3992. * @param {string} key
  3993. * @return {number|string}
  3994. */ /**
  3995. * Apply native and custom attributes to the SVG elements.
  3996. *
  3997. * In order to set the rotation center for rotation, set x and y to 0 and
  3998. * use `translateX` and `translateY` attributes to position the element
  3999. * instead.
  4000. *
  4001. * Attributes frequently used in Highcharts are `fill`, `stroke`,
  4002. * `stroke-width`.
  4003. *
  4004. * @sample highcharts/members/renderer-rect/
  4005. * Setting some attributes
  4006. *
  4007. * @example
  4008. * // Set multiple attributes
  4009. * element.attr({
  4010. * stroke: 'red',
  4011. * fill: 'blue',
  4012. * x: 10,
  4013. * y: 10
  4014. * });
  4015. *
  4016. * // Set a single attribute
  4017. * element.attr('stroke', 'red');
  4018. *
  4019. * // Get an attribute
  4020. * element.attr('stroke'); // => 'red'
  4021. *
  4022. * @function Highcharts.SVGElement#attr
  4023. *
  4024. * @param {string|Highcharts.SVGAttributes} [hash]
  4025. * The native and custom SVG attributes.
  4026. *
  4027. * @param {number|string|Highcharts.SVGPathArray} [val]
  4028. * If the type of the first argument is `string`, the second can be a
  4029. * value, which will serve as a single attribute setter. If the first
  4030. * argument is a string and the second is undefined, the function
  4031. * serves as a getter and the current value of the property is
  4032. * returned.
  4033. *
  4034. * @param {Function} [complete]
  4035. * A callback function to execute after setting the attributes. This
  4036. * makes the function compliant and interchangeable with the
  4037. * {@link SVGElement#animate} function.
  4038. *
  4039. * @param {boolean} [continueAnimation=true]
  4040. * Used internally when `.attr` is called as part of an animation
  4041. * step. Otherwise, calling `.attr` for an attribute will stop
  4042. * animation for that attribute.
  4043. *
  4044. * @return {Highcharts.SVGElement}
  4045. * If used as a setter, it returns the current
  4046. * {@link Highcharts.SVGElement} so the calls can be chained. If
  4047. * used as a getter, the current value of the attribute is returned.
  4048. */
  4049. SVGElement.prototype.attr = function (hash, val, complete, continueAnimation) {
  4050. var key,
  4051. element = this.element,
  4052. hasSetSymbolSize,
  4053. ret = this,
  4054. skipAttr,
  4055. setter,
  4056. symbolCustomAttribs = this.symbolCustomAttribs;
  4057. // single key-value pair
  4058. if (typeof hash === 'string' && typeof val !== 'undefined') {
  4059. key = hash;
  4060. hash = {};
  4061. hash[key] = val;
  4062. }
  4063. // used as a getter: first argument is a string, second is undefined
  4064. if (typeof hash === 'string') {
  4065. ret = (this[hash + 'Getter'] ||
  4066. this._defaultGetter).call(this, hash, element);
  4067. // setter
  4068. }
  4069. else {
  4070. objectEach(hash, function eachAttribute(val, key) {
  4071. skipAttr = false;
  4072. // Unless .attr is from the animator update, stop current
  4073. // running animation of this property
  4074. if (!continueAnimation) {
  4075. stop(this, key);
  4076. }
  4077. // Special handling of symbol attributes
  4078. if (this.symbolName &&
  4079. symbolCustomAttribs.indexOf(key) !== -1) {
  4080. if (!hasSetSymbolSize) {
  4081. this.symbolAttr(hash);
  4082. hasSetSymbolSize = true;
  4083. }
  4084. skipAttr = true;
  4085. }
  4086. if (this.rotation && (key === 'x' || key === 'y')) {
  4087. this.doTransform = true;
  4088. }
  4089. if (!skipAttr) {
  4090. setter = (this[key + 'Setter'] ||
  4091. this._defaultSetter);
  4092. setter.call(this, val, key, element);
  4093. // Let the shadow follow the main element
  4094. if (!this.styledMode &&
  4095. this.shadows &&
  4096. /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(key)) {
  4097. this.updateShadows(key, val, setter);
  4098. }
  4099. }
  4100. }, this);
  4101. this.afterSetters();
  4102. }
  4103. // In accordance with animate, run a complete callback
  4104. if (complete) {
  4105. complete.call(this);
  4106. }
  4107. return ret;
  4108. };
  4109. /**
  4110. * Apply a clipping rectangle to this element.
  4111. *
  4112. * @function Highcharts.SVGElement#clip
  4113. *
  4114. * @param {Highcharts.ClipRectElement} [clipRect]
  4115. * The clipping rectangle. If skipped, the current clip is removed.
  4116. *
  4117. * @return {Highcharts.SVGElement}
  4118. * Returns the SVG element to allow chaining.
  4119. */
  4120. SVGElement.prototype.clip = function (clipRect) {
  4121. return this.attr('clip-path', clipRect ?
  4122. 'url(' + this.renderer.url + '#' + clipRect.id + ')' :
  4123. 'none');
  4124. };
  4125. /**
  4126. * Calculate the coordinates needed for drawing a rectangle crisply and
  4127. * return the calculated attributes.
  4128. *
  4129. * @function Highcharts.SVGElement#crisp
  4130. *
  4131. * @param {Highcharts.RectangleObject} rect
  4132. * Rectangle to crisp.
  4133. *
  4134. * @param {number} [strokeWidth]
  4135. * The stroke width to consider when computing crisp positioning. It can
  4136. * also be set directly on the rect parameter.
  4137. *
  4138. * @return {Highcharts.RectangleObject}
  4139. * The modified rectangle arguments.
  4140. */
  4141. SVGElement.prototype.crisp = function (rect, strokeWidth) {
  4142. var wrapper = this,
  4143. normalizer;
  4144. strokeWidth = strokeWidth || rect.strokeWidth || 0;
  4145. // Math.round because strokeWidth can sometimes have roundoff errors
  4146. normalizer = Math.round(strokeWidth) % 2 / 2;
  4147. // normalize for crisp edges
  4148. rect.x = Math.floor(rect.x || wrapper.x || 0) + normalizer;
  4149. rect.y = Math.floor(rect.y || wrapper.y || 0) + normalizer;
  4150. rect.width = Math.floor((rect.width || wrapper.width || 0) - 2 * normalizer);
  4151. rect.height = Math.floor((rect.height || wrapper.height || 0) - 2 * normalizer);
  4152. if (defined(rect.strokeWidth)) {
  4153. rect.strokeWidth = strokeWidth;
  4154. }
  4155. return rect;
  4156. };
  4157. /**
  4158. * Build and apply an SVG gradient out of a common JavaScript configuration
  4159. * object. This function is called from the attribute setters. An event
  4160. * hook is added for supporting other complex color types.
  4161. *
  4162. * @private
  4163. * @function Highcharts.SVGElement#complexColor
  4164. *
  4165. * @param {Highcharts.GradientColorObject|Highcharts.PatternObject} colorOptions
  4166. * The gradient or pattern options structure.
  4167. *
  4168. * @param {string} prop
  4169. * The property to apply, can either be `fill` or `stroke`.
  4170. *
  4171. * @param {Highcharts.SVGDOMElement} elem
  4172. * SVG element to apply the gradient on.
  4173. */
  4174. SVGElement.prototype.complexColor = function (colorOptions, prop, elem) {
  4175. var renderer = this.renderer,
  4176. colorObject,
  4177. gradName,
  4178. gradAttr,
  4179. radAttr,
  4180. gradients,
  4181. stops,
  4182. stopColor,
  4183. stopOpacity,
  4184. radialReference,
  4185. id,
  4186. key = [],
  4187. value;
  4188. fireEvent(this.renderer, 'complexColor', {
  4189. args: arguments
  4190. }, function () {
  4191. // Apply linear or radial gradients
  4192. if (colorOptions.radialGradient) {
  4193. gradName = 'radialGradient';
  4194. }
  4195. else if (colorOptions.linearGradient) {
  4196. gradName = 'linearGradient';
  4197. }
  4198. if (gradName) {
  4199. gradAttr = colorOptions[gradName];
  4200. gradients = renderer.gradients;
  4201. stops = colorOptions.stops;
  4202. radialReference = elem.radialReference;
  4203. // Keep < 2.2 kompatibility
  4204. if (isArray(gradAttr)) {
  4205. colorOptions[gradName] = gradAttr = {
  4206. x1: gradAttr[0],
  4207. y1: gradAttr[1],
  4208. x2: gradAttr[2],
  4209. y2: gradAttr[3],
  4210. gradientUnits: 'userSpaceOnUse'
  4211. };
  4212. }
  4213. // Correct the radial gradient for the radial reference system
  4214. if (gradName === 'radialGradient' &&
  4215. radialReference &&
  4216. !defined(gradAttr.gradientUnits)) {
  4217. // Save the radial attributes for updating
  4218. radAttr = gradAttr;
  4219. gradAttr = merge(gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' });
  4220. }
  4221. // Build the unique key to detect whether we need to create a
  4222. // new element (#1282)
  4223. objectEach(gradAttr, function (val, n) {
  4224. if (n !== 'id') {
  4225. key.push(n, val);
  4226. }
  4227. });
  4228. objectEach(stops, function (val) {
  4229. key.push(val);
  4230. });
  4231. key = key.join(',');
  4232. // Check if a gradient object with the same config object is
  4233. // created within this renderer
  4234. if (gradients[key]) {
  4235. id = gradients[key].attr('id');
  4236. }
  4237. else {
  4238. // Set the id and create the element
  4239. gradAttr.id = id = uniqueKey();
  4240. var gradientObject_1 = gradients[key] =
  4241. renderer.createElement(gradName)
  4242. .attr(gradAttr)
  4243. .add(renderer.defs);
  4244. gradientObject_1.radAttr = radAttr;
  4245. // The gradient needs to keep a list of stops to be able to
  4246. // destroy them
  4247. gradientObject_1.stops = [];
  4248. stops.forEach(function (stop) {
  4249. var stopObject;
  4250. if (stop[1].indexOf('rgba') === 0) {
  4251. colorObject = Color.parse(stop[1]);
  4252. stopColor = colorObject.get('rgb');
  4253. stopOpacity = colorObject.get('a');
  4254. }
  4255. else {
  4256. stopColor = stop[1];
  4257. stopOpacity = 1;
  4258. }
  4259. stopObject = renderer.createElement('stop').attr({
  4260. offset: stop[0],
  4261. 'stop-color': stopColor,
  4262. 'stop-opacity': stopOpacity
  4263. }).add(gradientObject_1);
  4264. // Add the stop element to the gradient
  4265. gradientObject_1.stops.push(stopObject);
  4266. });
  4267. }
  4268. // Set the reference to the gradient object
  4269. value = 'url(' + renderer.url + '#' + id + ')';
  4270. elem.setAttribute(prop, value);
  4271. elem.gradient = key;
  4272. // Allow the color to be concatenated into tooltips formatters
  4273. // etc. (#2995)
  4274. colorOptions.toString = function () {
  4275. return value;
  4276. };
  4277. }
  4278. });
  4279. };
  4280. /**
  4281. * Set styles for the element. In addition to CSS styles supported by
  4282. * native SVG and HTML elements, there are also some custom made for
  4283. * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
  4284. * elements.
  4285. *
  4286. * @sample highcharts/members/renderer-text-on-chart/
  4287. * Styled text
  4288. *
  4289. * @function Highcharts.SVGElement#css
  4290. *
  4291. * @param {Highcharts.CSSObject} styles
  4292. * The new CSS styles.
  4293. *
  4294. * @return {Highcharts.SVGElement}
  4295. * Return the SVG element for chaining.
  4296. */
  4297. SVGElement.prototype.css = function (styles) {
  4298. var oldStyles = this.styles, newStyles = {}, elem = this.element, textWidth, serializedCss = '', hyphenate, hasNew = !oldStyles,
  4299. // These CSS properties are interpreted internally by the SVG
  4300. // renderer, but are not supported by SVG and should not be added to
  4301. // the DOM. In styled mode, no CSS should find its way to the DOM
  4302. // whatsoever (#6173, #6474).
  4303. svgPseudoProps = ['textOutline', 'textOverflow', 'width'];
  4304. // convert legacy
  4305. if (styles && styles.color) {
  4306. styles.fill = styles.color;
  4307. }
  4308. // Filter out existing styles to increase performance (#2640)
  4309. if (oldStyles) {
  4310. objectEach(styles, function (style, n) {
  4311. if (oldStyles && oldStyles[n] !== style) {
  4312. newStyles[n] = style;
  4313. hasNew = true;
  4314. }
  4315. });
  4316. }
  4317. if (hasNew) {
  4318. // Merge the new styles with the old ones
  4319. if (oldStyles) {
  4320. styles = extend(oldStyles, newStyles);
  4321. }
  4322. // Get the text width from style
  4323. if (styles) {
  4324. // Previously set, unset it (#8234)
  4325. if (styles.width === null || styles.width === 'auto') {
  4326. delete this.textWidth;
  4327. // Apply new
  4328. }
  4329. else if (elem.nodeName.toLowerCase() === 'text' &&
  4330. styles.width) {
  4331. textWidth = this.textWidth = pInt(styles.width);
  4332. }
  4333. }
  4334. // store object
  4335. this.styles = styles;
  4336. if (textWidth && (!svg && this.renderer.forExport)) {
  4337. delete styles.width;
  4338. }
  4339. // Serialize and set style attribute
  4340. if (elem.namespaceURI === this.SVG_NS) { // #7633
  4341. hyphenate = function (a, b) {
  4342. return '-' + b.toLowerCase();
  4343. };
  4344. objectEach(styles, function (style, n) {
  4345. if (svgPseudoProps.indexOf(n) === -1) {
  4346. serializedCss +=
  4347. n.replace(/([A-Z])/g, hyphenate) + ':' +
  4348. style + ';';
  4349. }
  4350. });
  4351. if (serializedCss) {
  4352. attr(elem, 'style', serializedCss); // #1881
  4353. }
  4354. }
  4355. else {
  4356. css(elem, styles);
  4357. }
  4358. if (this.added) {
  4359. // Rebuild text after added. Cache mechanisms in the buildText
  4360. // will prevent building if there are no significant changes.
  4361. if (this.element.nodeName === 'text') {
  4362. this.renderer.buildText(this);
  4363. }
  4364. // Apply text outline after added
  4365. if (styles && styles.textOutline) {
  4366. this.applyTextOutline(styles.textOutline);
  4367. }
  4368. }
  4369. }
  4370. return this;
  4371. };
  4372. /**
  4373. * @private
  4374. * @function Highcharts.SVGElement#dashstyleSetter
  4375. * @param {string} value
  4376. */
  4377. SVGElement.prototype.dashstyleSetter = function (value) {
  4378. var i,
  4379. strokeWidth = this['stroke-width'];
  4380. // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
  4381. // strokeWidth function, we should be able to use that instead.
  4382. if (strokeWidth === 'inherit') {
  4383. strokeWidth = 1;
  4384. }
  4385. value = value && value.toLowerCase();
  4386. if (value) {
  4387. var v = value
  4388. .replace('shortdashdotdot', '3,1,1,1,1,1,')
  4389. .replace('shortdashdot', '3,1,1,1')
  4390. .replace('shortdot', '1,1,')
  4391. .replace('shortdash', '3,1,')
  4392. .replace('longdash', '8,3,')
  4393. .replace(/dot/g, '1,3,')
  4394. .replace('dash', '4,3,')
  4395. .replace(/,$/, '')
  4396. .split(','); // ending comma
  4397. i = v.length;
  4398. while (i--) {
  4399. v[i] = '' + (pInt(v[i]) * pick(strokeWidth, NaN));
  4400. }
  4401. value = v.join(',').replace(/NaN/g, 'none'); // #3226
  4402. this.element.setAttribute('stroke-dasharray', value);
  4403. }
  4404. };
  4405. /**
  4406. * Destroy the element and element wrapper and clear up the DOM and event
  4407. * hooks.
  4408. *
  4409. * @function Highcharts.SVGElement#destroy
  4410. */
  4411. SVGElement.prototype.destroy = function () {
  4412. var wrapper = this,
  4413. element = wrapper.element || {},
  4414. renderer = wrapper.renderer,
  4415. parentToClean = (renderer.isSVG &&
  4416. element.nodeName === 'SPAN' &&
  4417. wrapper.parentGroup ||
  4418. void 0),
  4419. grandParent,
  4420. ownerSVGElement = element.ownerSVGElement,
  4421. i;
  4422. // remove events
  4423. element.onclick = element.onmouseout = element.onmouseover =
  4424. element.onmousemove = element.point = null;
  4425. stop(wrapper); // stop running animations
  4426. if (wrapper.clipPath && ownerSVGElement) {
  4427. var clipPath_1 = wrapper.clipPath;
  4428. // Look for existing references to this clipPath and remove them
  4429. // before destroying the element (#6196).
  4430. // The upper case version is for Edge
  4431. [].forEach.call(ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'), function (el) {
  4432. var clipPathAttr = el.getAttribute('clip-path');
  4433. if (clipPathAttr.indexOf(clipPath_1.element.id) > -1) {
  4434. el.removeAttribute('clip-path');
  4435. }
  4436. });
  4437. wrapper.clipPath = clipPath_1.destroy();
  4438. }
  4439. // Destroy stops in case this is a gradient object @todo old code?
  4440. if (wrapper.stops) {
  4441. for (i = 0; i < wrapper.stops.length; i++) {
  4442. wrapper.stops[i].destroy();
  4443. }
  4444. wrapper.stops.length = 0;
  4445. wrapper.stops = void 0;
  4446. }
  4447. // remove element
  4448. wrapper.safeRemoveChild(element);
  4449. if (!renderer.styledMode) {
  4450. wrapper.destroyShadows();
  4451. }
  4452. // In case of useHTML, clean up empty containers emulating SVG groups
  4453. // (#1960, #2393, #2697).
  4454. while (parentToClean &&
  4455. parentToClean.div &&
  4456. parentToClean.div.childNodes.length === 0) {
  4457. grandParent = parentToClean.parentGroup;
  4458. wrapper.safeRemoveChild(parentToClean.div);
  4459. delete parentToClean.div;
  4460. parentToClean = grandParent;
  4461. }
  4462. // remove from alignObjects
  4463. if (wrapper.alignTo) {
  4464. erase(renderer.alignedObjects, wrapper);
  4465. }
  4466. objectEach(wrapper, function (val, key) {
  4467. // Destroy child elements of a group
  4468. if (wrapper[key] &&
  4469. wrapper[key].parentGroup === wrapper &&
  4470. wrapper[key].destroy) {
  4471. wrapper[key].destroy();
  4472. }
  4473. // Delete all properties
  4474. delete wrapper[key];
  4475. });
  4476. return;
  4477. };
  4478. /**
  4479. * Destroy shadows on the element.
  4480. *
  4481. * @private
  4482. * @function Highcharts.SVGElement#destroyShadows
  4483. *
  4484. * @return {void}
  4485. */
  4486. SVGElement.prototype.destroyShadows = function () {
  4487. (this.shadows || []).forEach(function (shadow) {
  4488. this.safeRemoveChild(shadow);
  4489. }, this);
  4490. this.shadows = void 0;
  4491. };
  4492. /**
  4493. * @private
  4494. */
  4495. SVGElement.prototype.destroyTextPath = function (elem, path) {
  4496. var textElement = elem.getElementsByTagName('text')[0];
  4497. var tspans;
  4498. if (textElement) {
  4499. // Remove textPath attributes
  4500. textElement.removeAttribute('dx');
  4501. textElement.removeAttribute('dy');
  4502. // Remove ID's:
  4503. path.element.setAttribute('id', '');
  4504. // Check if textElement includes textPath,
  4505. if (this.textPathWrapper &&
  4506. textElement.getElementsByTagName('textPath').length) {
  4507. // Move nodes to <text>
  4508. tspans = this.textPathWrapper.element.childNodes;
  4509. // Now move all <tspan>'s to the <textPath> node
  4510. while (tspans.length) {
  4511. textElement.appendChild(tspans[0]);
  4512. }
  4513. // Remove <textPath> from the DOM
  4514. textElement.removeChild(this.textPathWrapper.element);
  4515. }
  4516. }
  4517. else if (elem.getAttribute('dx') || elem.getAttribute('dy')) {
  4518. // Remove textPath attributes from elem
  4519. // to get correct text-outline position
  4520. elem.removeAttribute('dx');
  4521. elem.removeAttribute('dy');
  4522. }
  4523. if (this.textPathWrapper) {
  4524. // Set textPathWrapper to undefined and destroy it
  4525. this.textPathWrapper = this.textPathWrapper.destroy();
  4526. }
  4527. };
  4528. /**
  4529. * @private
  4530. * @function Highcharts.SVGElement#dSettter
  4531. * @param {number|string|Highcharts.SVGPathArray} value
  4532. * @param {string} key
  4533. * @param {Highcharts.SVGDOMElement} element
  4534. */
  4535. SVGElement.prototype.dSetter = function (value, key, element) {
  4536. if (isArray(value)) {
  4537. // Backwards compatibility, convert one-dimensional array into an
  4538. // array of segments
  4539. if (typeof value[0] === 'string') {
  4540. value = this.renderer.pathToSegments(value);
  4541. }
  4542. this.pathArray = value;
  4543. value = value.reduce(function (acc, seg, i) {
  4544. if (!seg || !seg.join) {
  4545. return (seg || '').toString();
  4546. }
  4547. return (i ? acc + ' ' : '') + seg.join(' ');
  4548. }, '');
  4549. }
  4550. if (/(NaN| {2}|^$)/.test(value)) {
  4551. value = 'M 0 0';
  4552. }
  4553. // Check for cache before resetting. Resetting causes disturbance in the
  4554. // DOM, causing flickering in some cases in Edge/IE (#6747). Also
  4555. // possible performance gain.
  4556. if (this[key] !== value) {
  4557. element.setAttribute(key, value);
  4558. this[key] = value;
  4559. }
  4560. };
  4561. /**
  4562. * Fade out an element by animating its opacity down to 0, and hide it on
  4563. * complete. Used internally for the tooltip.
  4564. *
  4565. * @function Highcharts.SVGElement#fadeOut
  4566. *
  4567. * @param {number} [duration=150]
  4568. * The fade duration in milliseconds.
  4569. */
  4570. SVGElement.prototype.fadeOut = function (duration) {
  4571. var elemWrapper = this;
  4572. elemWrapper.animate({
  4573. opacity: 0
  4574. }, {
  4575. duration: pick(duration, 150),
  4576. complete: function () {
  4577. // #3088, assuming we're only using this for tooltips
  4578. elemWrapper.attr({ y: -9999 }).hide();
  4579. }
  4580. });
  4581. };
  4582. /**
  4583. * @private
  4584. * @function Highcharts.SVGElement#fillSetter
  4585. * @param {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} value
  4586. * @param {string} key
  4587. * @param {Highcharts.SVGDOMElement} element
  4588. */
  4589. SVGElement.prototype.fillSetter = function (value, key, element) {
  4590. if (typeof value === 'string') {
  4591. element.setAttribute(key, value);
  4592. }
  4593. else if (value) {
  4594. this.complexColor(value, key, element);
  4595. }
  4596. };
  4597. /**
  4598. * Get the bounding box (width, height, x and y) for the element. Generally
  4599. * used to get rendered text size. Since this is called a lot in charts,
  4600. * the results are cached based on text properties, in order to save DOM
  4601. * traffic. The returned bounding box includes the rotation, so for example
  4602. * a single text line of rotation 90 will report a greater height, and a
  4603. * width corresponding to the line-height.
  4604. *
  4605. * @sample highcharts/members/renderer-on-chart/
  4606. * Draw a rectangle based on a text's bounding box
  4607. *
  4608. * @function Highcharts.SVGElement#getBBox
  4609. *
  4610. * @param {boolean} [reload]
  4611. * Skip the cache and get the updated DOM bouding box.
  4612. *
  4613. * @param {number} [rot]
  4614. * Override the element's rotation. This is internally used on axis
  4615. * labels with a value of 0 to find out what the bounding box would
  4616. * be have been if it were not rotated.
  4617. *
  4618. * @return {Highcharts.BBoxObject}
  4619. * The bounding box with `x`, `y`, `width` and `height` properties.
  4620. */
  4621. SVGElement.prototype.getBBox = function (reload, rot) {
  4622. var wrapper = this,
  4623. bBox, // = wrapper.bBox,
  4624. renderer = wrapper.renderer,
  4625. width,
  4626. height,
  4627. element = wrapper.element,
  4628. styles = wrapper.styles,
  4629. fontSize,
  4630. textStr = wrapper.textStr,
  4631. toggleTextShadowShim,
  4632. cache = renderer.cache,
  4633. cacheKeys = renderer.cacheKeys,
  4634. isSVG = element.namespaceURI === wrapper.SVG_NS,
  4635. cacheKey;
  4636. var rotation = pick(rot,
  4637. wrapper.rotation, 0);
  4638. fontSize = renderer.styledMode ? (element &&
  4639. SVGElement.prototype.getStyle.call(element, 'font-size')) : (styles && styles.fontSize);
  4640. // Avoid undefined and null (#7316)
  4641. if (defined(textStr)) {
  4642. cacheKey = textStr.toString();
  4643. // Since numbers are monospaced, and numerical labels appear a lot
  4644. // in a chart, we assume that a label of n characters has the same
  4645. // bounding box as others of the same length. Unless there is inner
  4646. // HTML in the label. In that case, leave the numbers as is (#5899).
  4647. if (cacheKey.indexOf('<') === -1) {
  4648. cacheKey = cacheKey.replace(/[0-9]/g, '0');
  4649. }
  4650. // Properties that affect bounding box
  4651. cacheKey += [
  4652. '',
  4653. rotation,
  4654. fontSize,
  4655. wrapper.textWidth,
  4656. styles && styles.textOverflow,
  4657. styles && styles.fontWeight // #12163
  4658. ].join(',');
  4659. }
  4660. if (cacheKey && !reload) {
  4661. bBox = cache[cacheKey];
  4662. }
  4663. // No cache found
  4664. if (!bBox) {
  4665. // SVG elements
  4666. if (isSVG || renderer.forExport) {
  4667. try { // Fails in Firefox if the container has display: none.
  4668. // When the text shadow shim is used, we need to hide the
  4669. // fake shadows to get the correct bounding box (#3872)
  4670. toggleTextShadowShim = this.fakeTS && function (display) {
  4671. [].forEach.call(element.querySelectorAll('.highcharts-text-outline'), function (tspan) {
  4672. tspan.style.display = display;
  4673. });
  4674. };
  4675. // Workaround for #3842, Firefox reporting wrong bounding
  4676. // box for shadows
  4677. if (isFunction(toggleTextShadowShim)) {
  4678. toggleTextShadowShim('none');
  4679. }
  4680. bBox = element.getBBox ?
  4681. // SVG: use extend because IE9 is not allowed to change
  4682. // width and height in case of rotation (below)
  4683. extend({}, element.getBBox()) : {
  4684. // Legacy IE in export mode
  4685. width: element.offsetWidth,
  4686. height: element.offsetHeight
  4687. };
  4688. // #3842
  4689. if (isFunction(toggleTextShadowShim)) {
  4690. toggleTextShadowShim('');
  4691. }
  4692. }
  4693. catch (e) {
  4694. '';
  4695. }
  4696. // If the bBox is not set, the try-catch block above failed. The
  4697. // other condition is for Opera that returns a width of
  4698. // -Infinity on hidden elements.
  4699. if (!bBox || bBox.width < 0) {
  4700. bBox = { width: 0, height: 0 };
  4701. }
  4702. // VML Renderer or useHTML within SVG
  4703. }
  4704. else {
  4705. bBox = wrapper.htmlGetBBox();
  4706. }
  4707. // True SVG elements as well as HTML elements in modern browsers
  4708. // using the .useHTML option need to compensated for rotation
  4709. if (renderer.isSVG) {
  4710. width = bBox.width;
  4711. height = bBox.height;
  4712. // Workaround for wrong bounding box in IE, Edge and Chrome on
  4713. // Windows. With Highcharts' default font, IE and Edge report
  4714. // a box height of 16.899 and Chrome rounds it to 17. If this
  4715. // stands uncorrected, it results in more padding added below
  4716. // the text than above when adding a label border or background.
  4717. // Also vertical positioning is affected.
  4718. // https://jsfiddle.net/highcharts/em37nvuj/
  4719. // (#1101, #1505, #1669, #2568, #6213).
  4720. if (isSVG) {
  4721. bBox.height = height = ({
  4722. '11px,17': 14,
  4723. '13px,20': 16
  4724. }[styles &&
  4725. styles.fontSize + ',' + Math.round(height)] ||
  4726. height);
  4727. }
  4728. // Adjust for rotated text
  4729. if (rotation) {
  4730. var rad = rotation * deg2rad;
  4731. bBox.width = Math.abs(height * Math.sin(rad)) +
  4732. Math.abs(width * Math.cos(rad));
  4733. bBox.height = Math.abs(height * Math.cos(rad)) +
  4734. Math.abs(width * Math.sin(rad));
  4735. }
  4736. }
  4737. // Cache it. When loading a chart in a hidden iframe in Firefox and
  4738. // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
  4739. if (cacheKey && bBox.height > 0) {
  4740. // Rotate (#4681)
  4741. while (cacheKeys.length > 250) {
  4742. delete cache[cacheKeys.shift()];
  4743. }
  4744. if (!cache[cacheKey]) {
  4745. cacheKeys.push(cacheKey);
  4746. }
  4747. cache[cacheKey] = bBox;
  4748. }
  4749. }
  4750. return bBox;
  4751. };
  4752. /**
  4753. * Get the computed style. Only in styled mode.
  4754. *
  4755. * @example
  4756. * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
  4757. *
  4758. * @function Highcharts.SVGElement#getStyle
  4759. *
  4760. * @param {string} prop
  4761. * The property name to check for.
  4762. *
  4763. * @return {string}
  4764. * The current computed value.
  4765. */
  4766. SVGElement.prototype.getStyle = function (prop) {
  4767. return win
  4768. .getComputedStyle(this.element || this, '')
  4769. .getPropertyValue(prop);
  4770. };
  4771. /**
  4772. * Check if an element has the given class name.
  4773. *
  4774. * @function Highcharts.SVGElement#hasClass
  4775. *
  4776. * @param {string} className
  4777. * The class name to check for.
  4778. *
  4779. * @return {boolean}
  4780. * Whether the class name is found.
  4781. */
  4782. SVGElement.prototype.hasClass = function (className) {
  4783. return ('' + this.attr('class'))
  4784. .split(' ')
  4785. .indexOf(className) !== -1;
  4786. };
  4787. /**
  4788. * Hide the element, similar to setting the `visibility` attribute to
  4789. * `hidden`.
  4790. *
  4791. * @function Highcharts.SVGElement#hide
  4792. *
  4793. * @param {boolean} [hideByTranslation=false]
  4794. * The flag to determine if element should be hidden by moving out
  4795. * of the viewport. Used for example for dataLabels.
  4796. *
  4797. * @return {Highcharts.SVGElement}
  4798. * Returns the SVGElement for chaining.
  4799. */
  4800. SVGElement.prototype.hide = function (hideByTranslation) {
  4801. if (hideByTranslation) {
  4802. this.attr({ y: -9999 });
  4803. }
  4804. else {
  4805. this.attr({ visibility: 'hidden' });
  4806. }
  4807. return this;
  4808. };
  4809. /**
  4810. * @private
  4811. */
  4812. SVGElement.prototype.htmlGetBBox = function () {
  4813. return { height: 0, width: 0, x: 0, y: 0 };
  4814. };
  4815. /**
  4816. * Initialize the SVG element. This function only exists to make the
  4817. * initialization process overridable. It should not be called directly.
  4818. *
  4819. * @function Highcharts.SVGElement#init
  4820. *
  4821. * @param {Highcharts.SVGRenderer} renderer
  4822. * The SVGRenderer instance to initialize to.
  4823. *
  4824. * @param {string} nodeName
  4825. * The SVG node name.
  4826. */
  4827. SVGElement.prototype.init = function (renderer, nodeName) {
  4828. /**
  4829. * The primary DOM node. Each `SVGElement` instance wraps a main DOM
  4830. * node, but may also represent more nodes.
  4831. *
  4832. * @name Highcharts.SVGElement#element
  4833. * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  4834. */
  4835. this.element = nodeName === 'span' ?
  4836. createElement(nodeName) :
  4837. doc.createElementNS(this.SVG_NS, nodeName);
  4838. /**
  4839. * The renderer that the SVGElement belongs to.
  4840. *
  4841. * @name Highcharts.SVGElement#renderer
  4842. * @type {Highcharts.SVGRenderer}
  4843. */
  4844. this.renderer = renderer;
  4845. fireEvent(this, 'afterInit');
  4846. };
  4847. /**
  4848. * Invert a group, rotate and flip. This is used internally on inverted
  4849. * charts, where the points and graphs are drawn as if not inverted, then
  4850. * the series group elements are inverted.
  4851. *
  4852. * @function Highcharts.SVGElement#invert
  4853. *
  4854. * @param {boolean} inverted
  4855. * Whether to invert or not. An inverted shape can be un-inverted by
  4856. * setting it to false.
  4857. *
  4858. * @return {Highcharts.SVGElement}
  4859. * Return the SVGElement for chaining.
  4860. */
  4861. SVGElement.prototype.invert = function (inverted) {
  4862. var wrapper = this;
  4863. wrapper.inverted = inverted;
  4864. wrapper.updateTransform();
  4865. return wrapper;
  4866. };
  4867. /**
  4868. * Add an event listener. This is a simple setter that replaces all other
  4869. * events of the same type, opposed to the {@link Highcharts#addEvent}
  4870. * function.
  4871. *
  4872. * @sample highcharts/members/element-on/
  4873. * A clickable rectangle
  4874. *
  4875. * @function Highcharts.SVGElement#on
  4876. *
  4877. * @param {string} eventType
  4878. * The event type. If the type is `click`, Highcharts will internally
  4879. * translate it to a `touchstart` event on touch devices, to prevent the
  4880. * browser from waiting for a click event from firing.
  4881. *
  4882. * @param {Function} handler
  4883. * The handler callback.
  4884. *
  4885. * @return {Highcharts.SVGElement}
  4886. * The SVGElement for chaining.
  4887. */
  4888. SVGElement.prototype.on = function (eventType, handler) {
  4889. var svgElement = this,
  4890. element = svgElement.element,
  4891. touchStartPos,
  4892. touchEventFired;
  4893. // touch
  4894. if (hasTouch && eventType === 'click') {
  4895. element.ontouchstart = function (e) {
  4896. // save touch position for later calculation
  4897. touchStartPos = {
  4898. clientX: e.touches[0].clientX,
  4899. clientY: e.touches[0].clientY
  4900. };
  4901. };
  4902. // Instead of ontouchstart, event handlers should be called
  4903. // on touchend - similar to how current mouseup events are called
  4904. element.ontouchend = function (e) {
  4905. // hasMoved is a boolean variable containing logic if page
  4906. // was scrolled, so if touch position changed more than
  4907. // ~4px (value borrowed from general touch handler)
  4908. var hasMoved = touchStartPos.clientX ? Math.sqrt(Math.pow(touchStartPos.clientX - e.changedTouches[0].clientX, 2) +
  4909. Math.pow(touchStartPos.clientY - e.changedTouches[0].clientY, 2)) >= 4 : false;
  4910. if (!hasMoved) { // only call handlers if page was not scrolled
  4911. handler.call(element, e);
  4912. }
  4913. touchEventFired = true;
  4914. // prevent other events from being fired. #9682
  4915. e.preventDefault();
  4916. };
  4917. element.onclick = function (e) {
  4918. // Do not call onclick handler if touch event was fired already.
  4919. if (!touchEventFired) {
  4920. handler.call(element, e);
  4921. }
  4922. };
  4923. }
  4924. else {
  4925. // simplest possible event model for internal use
  4926. element['on' + eventType] = handler;
  4927. }
  4928. return this;
  4929. };
  4930. /**
  4931. * @private
  4932. * @function Highcharts.SVGElement#opacitySetter
  4933. * @param {string} value
  4934. * @param {string} key
  4935. * @param {Highcharts.SVGDOMElement} element
  4936. */
  4937. SVGElement.prototype.opacitySetter = function (value, key, element) {
  4938. this[key] = value;
  4939. element.setAttribute(key, value);
  4940. };
  4941. /**
  4942. * Remove a class name from the element.
  4943. *
  4944. * @function Highcharts.SVGElement#removeClass
  4945. *
  4946. * @param {string|RegExp} className
  4947. * The class name to remove.
  4948. *
  4949. * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
  4950. */
  4951. SVGElement.prototype.removeClass = function (className) {
  4952. return this.attr('class', ('' + this.attr('class'))
  4953. .replace(isString(className) ?
  4954. new RegExp("(^| )" + className + "( |$)") : // #12064, #13590
  4955. className, ' ')
  4956. .replace(/ +/g, ' ')
  4957. .trim());
  4958. };
  4959. /**
  4960. * @private
  4961. * @param {Array<Highcharts.SVGDOMElement>} tspans
  4962. * Text spans.
  4963. */
  4964. SVGElement.prototype.removeTextOutline = function (tspans) {
  4965. // Iterate from the end to
  4966. // support removing items inside the cycle (#6472).
  4967. var i = tspans.length,
  4968. tspan;
  4969. while (i--) {
  4970. tspan = tspans[i];
  4971. if (tspan.getAttribute('class') === 'highcharts-text-outline') {
  4972. // Remove then erase
  4973. erase(tspans, this.element.removeChild(tspan));
  4974. }
  4975. }
  4976. };
  4977. /**
  4978. * Removes an element from the DOM.
  4979. *
  4980. * @private
  4981. * @function Highcharts.SVGElement#safeRemoveChild
  4982. *
  4983. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  4984. * The DOM node to remove.
  4985. */
  4986. SVGElement.prototype.safeRemoveChild = function (element) {
  4987. var parentNode = element.parentNode;
  4988. if (parentNode) {
  4989. parentNode.removeChild(element);
  4990. }
  4991. };
  4992. /**
  4993. * Set the coordinates needed to draw a consistent radial gradient across
  4994. * a shape regardless of positioning inside the chart. Used on pie slices
  4995. * to make all the slices have the same radial reference point.
  4996. *
  4997. * @function Highcharts.SVGElement#setRadialReference
  4998. *
  4999. * @param {Array<number>} coordinates
  5000. * The center reference. The format is `[centerX, centerY, diameter]` in
  5001. * pixels.
  5002. *
  5003. * @return {Highcharts.SVGElement}
  5004. * Returns the SVGElement for chaining.
  5005. */
  5006. SVGElement.prototype.setRadialReference = function (coordinates) {
  5007. var existingGradient = (this.element.gradient &&
  5008. this.renderer.gradients[this.element.gradient]);
  5009. this.element.radialReference = coordinates;
  5010. // On redrawing objects with an existing gradient, the gradient needs
  5011. // to be repositioned (#3801)
  5012. if (existingGradient && existingGradient.radAttr) {
  5013. existingGradient.animate(this.renderer.getRadialAttr(coordinates, existingGradient.radAttr));
  5014. }
  5015. return this;
  5016. };
  5017. /**
  5018. * @private
  5019. * @function Highcharts.SVGElement#setTextPath
  5020. * @param {Highcharts.SVGElement} path
  5021. * Path to follow.
  5022. * @param {Highcharts.DataLabelsTextPathOptionsObject} textPathOptions
  5023. * Options.
  5024. * @return {Highcharts.SVGElement}
  5025. * Returns the SVGElement for chaining.
  5026. */
  5027. SVGElement.prototype.setTextPath = function (path, textPathOptions) {
  5028. var elem = this.element,
  5029. attribsMap = {
  5030. textAnchor: 'text-anchor'
  5031. },
  5032. attrs,
  5033. adder = false,
  5034. textPathElement,
  5035. textPathId,
  5036. textPathWrapper = this.textPathWrapper,
  5037. tspans,
  5038. firstTime = !textPathWrapper;
  5039. // Defaults
  5040. textPathOptions = merge(true, {
  5041. enabled: true,
  5042. attributes: {
  5043. dy: -5,
  5044. startOffset: '50%',
  5045. textAnchor: 'middle'
  5046. }
  5047. }, textPathOptions);
  5048. attrs = textPathOptions.attributes;
  5049. if (path && textPathOptions && textPathOptions.enabled) {
  5050. // In case of fixed width for a text, string is rebuilt
  5051. // (e.g. ellipsis is applied), so we need to rebuild textPath too
  5052. if (textPathWrapper &&
  5053. textPathWrapper.element.parentNode === null) {
  5054. // When buildText functionality was triggered again
  5055. // and deletes textPathWrapper parentNode
  5056. firstTime = true;
  5057. textPathWrapper = textPathWrapper.destroy();
  5058. }
  5059. else if (textPathWrapper) {
  5060. // Case after drillup when spans were added into
  5061. // the DOM outside the textPathWrapper parentGroup
  5062. this.removeTextOutline.call(textPathWrapper.parentGroup, [].slice.call(elem.getElementsByTagName('tspan')));
  5063. }
  5064. // label() has padding, text() doesn't
  5065. if (this.options && this.options.padding) {
  5066. attrs.dx = -this.options.padding;
  5067. }
  5068. if (!textPathWrapper) {
  5069. // Create <textPath>, defer the DOM adder
  5070. this.textPathWrapper = textPathWrapper =
  5071. this.renderer.createElement('textPath');
  5072. adder = true;
  5073. }
  5074. textPathElement = textPathWrapper.element;
  5075. // Set ID for the path
  5076. textPathId = path.element.getAttribute('id');
  5077. if (!textPathId) {
  5078. path.element.setAttribute('id', textPathId = uniqueKey());
  5079. }
  5080. // Change DOM structure, by placing <textPath> tag in <text>
  5081. if (firstTime) {
  5082. tspans = elem.getElementsByTagName('tspan');
  5083. // Now move all <tspan>'s to the <textPath> node
  5084. while (tspans.length) {
  5085. // Remove "y" from tspans, as Firefox translates them
  5086. tspans[0].setAttribute('y', 0);
  5087. // Remove "x" from tspans
  5088. if (isNumber(attrs.dx)) {
  5089. tspans[0].setAttribute('x', -attrs.dx);
  5090. }
  5091. textPathElement.appendChild(tspans[0]);
  5092. }
  5093. }
  5094. // Add <textPath> to the DOM
  5095. if (adder &&
  5096. textPathWrapper) {
  5097. textPathWrapper.add({
  5098. // label() is placed in a group, text() is standalone
  5099. element: this.text ? this.text.element : elem
  5100. });
  5101. }
  5102. // Set basic options:
  5103. // Use `setAttributeNS` because Safari needs this..
  5104. textPathElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.renderer.url + '#' + textPathId);
  5105. // Presentation attributes:
  5106. // dx/dy options must by set on <text> (parent),
  5107. // the rest should be set on <textPath>
  5108. if (defined(attrs.dy)) {
  5109. textPathElement.parentNode
  5110. .setAttribute('dy', attrs.dy);
  5111. delete attrs.dy;
  5112. }
  5113. if (defined(attrs.dx)) {
  5114. textPathElement.parentNode
  5115. .setAttribute('dx', attrs.dx);
  5116. delete attrs.dx;
  5117. }
  5118. // Additional attributes
  5119. objectEach(attrs, function (val, key) {
  5120. textPathElement.setAttribute(attribsMap[key] || key, val);
  5121. });
  5122. // Remove translation, text that follows path does not need that
  5123. elem.removeAttribute('transform');
  5124. // Remove shadows and text outlines
  5125. this.removeTextOutline.call(textPathWrapper, [].slice.call(elem.getElementsByTagName('tspan')));
  5126. // Remove background and border for label(), see #10545
  5127. // Alternatively, we can disable setting background rects in
  5128. // series.drawDataLabels()
  5129. if (this.text && !this.renderer.styledMode) {
  5130. this.attr({
  5131. fill: 'none',
  5132. 'stroke-width': 0
  5133. });
  5134. }
  5135. // Disable some functions
  5136. this.updateTransform = noop;
  5137. this.applyTextOutline = noop;
  5138. }
  5139. else if (textPathWrapper) {
  5140. // Reset to prototype
  5141. delete this.updateTransform;
  5142. delete this.applyTextOutline;
  5143. // Restore DOM structure:
  5144. this.destroyTextPath(elem, path);
  5145. // Bring attributes back
  5146. this.updateTransform();
  5147. // Set textOutline back for text()
  5148. if (this.options && this.options.rotation) {
  5149. this.applyTextOutline(this.options.style.textOutline);
  5150. }
  5151. }
  5152. return this;
  5153. };
  5154. /**
  5155. * Add a shadow to the element. Must be called after the element is added to
  5156. * the DOM. In styled mode, this method is not used, instead use `defs` and
  5157. * filters.
  5158. *
  5159. * @example
  5160. * renderer.rect(10, 100, 100, 100)
  5161. * .attr({ fill: 'red' })
  5162. * .shadow(true);
  5163. *
  5164. * @function Highcharts.SVGElement#shadow
  5165. *
  5166. * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions]
  5167. * The shadow options. If `true`, the default options are applied. If
  5168. * `false`, the current shadow will be removed.
  5169. *
  5170. * @param {Highcharts.SVGElement} [group]
  5171. * The SVG group element where the shadows will be applied. The
  5172. * default is to add it to the same parent as the current element.
  5173. * Internally, this is ised for pie slices, where all the shadows are
  5174. * added to an element behind all the slices.
  5175. *
  5176. * @param {boolean} [cutOff]
  5177. * Used internally for column shadows.
  5178. *
  5179. * @return {Highcharts.SVGElement}
  5180. * Returns the SVGElement for chaining.
  5181. */
  5182. SVGElement.prototype.shadow = function (shadowOptions, group, cutOff) {
  5183. var shadows = [],
  5184. i,
  5185. shadow,
  5186. element = this.element,
  5187. strokeWidth,
  5188. shadowElementOpacity,
  5189. update = false,
  5190. oldShadowOptions = this.oldShadowOptions,
  5191. // compensate for inverted plot area
  5192. transform;
  5193. var defaultShadowOptions = {
  5194. color: '#000000',
  5195. offsetX: 1,
  5196. offsetY: 1,
  5197. opacity: 0.15,
  5198. width: 3
  5199. };
  5200. var options;
  5201. if (shadowOptions === true) {
  5202. options = defaultShadowOptions;
  5203. }
  5204. else if (typeof shadowOptions === 'object') {
  5205. options = extend(defaultShadowOptions, shadowOptions);
  5206. }
  5207. // Update shadow when options change (#12091).
  5208. if (options) {
  5209. // Go over each key to look for change
  5210. if (options && oldShadowOptions) {
  5211. objectEach(options, function (value, key) {
  5212. if (value !== oldShadowOptions[key]) {
  5213. update = true;
  5214. }
  5215. });
  5216. }
  5217. if (update) {
  5218. this.destroyShadows();
  5219. }
  5220. this.oldShadowOptions = options;
  5221. }
  5222. if (!options) {
  5223. this.destroyShadows();
  5224. }
  5225. else if (!this.shadows) {
  5226. shadowElementOpacity = options.opacity / options.width;
  5227. transform = this.parentInverted ?
  5228. 'translate(-1,-1)' :
  5229. "translate(" + options.offsetX + ", " + options.offsetY + ")";
  5230. for (i = 1; i <= options.width; i++) {
  5231. shadow = element.cloneNode(false);
  5232. strokeWidth = (options.width * 2) + 1 - (2 * i);
  5233. attr(shadow, {
  5234. stroke: (shadowOptions.color ||
  5235. '#000000'),
  5236. 'stroke-opacity': shadowElementOpacity * i,
  5237. 'stroke-width': strokeWidth,
  5238. transform: transform,
  5239. fill: 'none'
  5240. });
  5241. shadow.setAttribute('class', (shadow.getAttribute('class') || '') + ' highcharts-shadow');
  5242. if (cutOff) {
  5243. attr(shadow, 'height', Math.max(attr(shadow, 'height') - strokeWidth, 0));
  5244. shadow.cutHeight = strokeWidth;
  5245. }
  5246. if (group) {
  5247. group.element.appendChild(shadow);
  5248. }
  5249. else if (element.parentNode) {
  5250. element.parentNode.insertBefore(shadow, element);
  5251. }
  5252. shadows.push(shadow);
  5253. }
  5254. this.shadows = shadows;
  5255. }
  5256. return this;
  5257. };
  5258. /**
  5259. * Show the element after it has been hidden.
  5260. *
  5261. * @function Highcharts.SVGElement#show
  5262. *
  5263. * @param {boolean} [inherit=false]
  5264. * Set the visibility attribute to `inherit` rather than `visible`.
  5265. * The difference is that an element with `visibility="visible"`
  5266. * will be visible even if the parent is hidden.
  5267. *
  5268. * @return {Highcharts.SVGElement}
  5269. * Returns the SVGElement for chaining.
  5270. */
  5271. SVGElement.prototype.show = function (inherit) {
  5272. return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
  5273. };
  5274. /**
  5275. * WebKit and Batik have problems with a stroke-width of zero, so in this
  5276. * case we remove the stroke attribute altogether. #1270, #1369, #3065,
  5277. * #3072.
  5278. *
  5279. * @private
  5280. * @function Highcharts.SVGElement#strokeSetter
  5281. * @param {number|string} value
  5282. * @param {string} key
  5283. * @param {Highcharts.SVGDOMElement} element
  5284. */
  5285. SVGElement.prototype.strokeSetter = function (value, key, element) {
  5286. this[key] = value;
  5287. // Only apply the stroke attribute if the stroke width is defined and
  5288. // larger than 0
  5289. if (this.stroke && this['stroke-width']) {
  5290. // Use prototype as instance may be overridden
  5291. SVGElement.prototype.fillSetter.call(this, this.stroke, 'stroke', element);
  5292. element.setAttribute('stroke-width', this['stroke-width']);
  5293. this.hasStroke = true;
  5294. }
  5295. else if (key === 'stroke-width' && value === 0 && this.hasStroke) {
  5296. element.removeAttribute('stroke');
  5297. this.hasStroke = false;
  5298. }
  5299. else if (this.renderer.styledMode && this['stroke-width']) {
  5300. element.setAttribute('stroke-width', this['stroke-width']);
  5301. this.hasStroke = true;
  5302. }
  5303. };
  5304. /**
  5305. * Get the computed stroke width in pixel values. This is used extensively
  5306. * when drawing shapes to ensure the shapes are rendered crisp and
  5307. * positioned correctly relative to each other. Using
  5308. * `shape-rendering: crispEdges` leaves us less control over positioning,
  5309. * for example when we want to stack columns next to each other, or position
  5310. * things pixel-perfectly within the plot box.
  5311. *
  5312. * The common pattern when placing a shape is:
  5313. * - Create the SVGElement and add it to the DOM. In styled mode, it will
  5314. * now receive a stroke width from the style sheet. In classic mode we
  5315. * will add the `stroke-width` attribute.
  5316. * - Read the computed `elem.strokeWidth()`.
  5317. * - Place it based on the stroke width.
  5318. *
  5319. * @function Highcharts.SVGElement#strokeWidth
  5320. *
  5321. * @return {number}
  5322. * The stroke width in pixels. Even if the given stroke widtch (in CSS or by
  5323. * attributes) is based on `em` or other units, the pixel size is returned.
  5324. */
  5325. SVGElement.prototype.strokeWidth = function () {
  5326. // In non-styled mode, read the stroke width as set by .attr
  5327. if (!this.renderer.styledMode) {
  5328. return this['stroke-width'] || 0;
  5329. }
  5330. // In styled mode, read computed stroke width
  5331. var val = this.getStyle('stroke-width'),
  5332. ret = 0,
  5333. dummy;
  5334. // Read pixel values directly
  5335. if (val.indexOf('px') === val.length - 2) {
  5336. ret = pInt(val);
  5337. // Other values like em, pt etc need to be measured
  5338. }
  5339. else if (val !== '') {
  5340. dummy = doc.createElementNS(SVG_NS, 'rect');
  5341. attr(dummy, {
  5342. width: val,
  5343. 'stroke-width': 0
  5344. });
  5345. this.element.parentNode.appendChild(dummy);
  5346. ret = dummy.getBBox().width;
  5347. dummy.parentNode.removeChild(dummy);
  5348. }
  5349. return ret;
  5350. };
  5351. /**
  5352. * If one of the symbol size affecting parameters are changed,
  5353. * check all the others only once for each call to an element's
  5354. * .attr() method
  5355. *
  5356. * @private
  5357. * @function Highcharts.SVGElement#symbolAttr
  5358. *
  5359. * @param {Highcharts.SVGAttributes} hash
  5360. * The attributes to set.
  5361. */
  5362. SVGElement.prototype.symbolAttr = function (hash) {
  5363. var wrapper = this;
  5364. [
  5365. 'x',
  5366. 'y',
  5367. 'r',
  5368. 'start',
  5369. 'end',
  5370. 'width',
  5371. 'height',
  5372. 'innerR',
  5373. 'anchorX',
  5374. 'anchorY',
  5375. 'clockwise'
  5376. ].forEach(function (key) {
  5377. wrapper[key] = pick(hash[key], wrapper[key]);
  5378. });
  5379. wrapper.attr({
  5380. d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
  5381. });
  5382. };
  5383. /**
  5384. * @private
  5385. * @function Highcharts.SVGElement#textSetter
  5386. * @param {string} value
  5387. */
  5388. SVGElement.prototype.textSetter = function (value) {
  5389. if (value !== this.textStr) {
  5390. // Delete size caches when the text changes
  5391. // delete this.bBox; // old code in series-label
  5392. delete this.textPxLength;
  5393. this.textStr = value;
  5394. if (this.added) {
  5395. this.renderer.buildText(this);
  5396. }
  5397. }
  5398. };
  5399. /**
  5400. * @private
  5401. * @function Highcharts.SVGElement#titleSetter
  5402. * @param {string} value
  5403. */
  5404. SVGElement.prototype.titleSetter = function (value) {
  5405. var titleNode = this.element.getElementsByTagName('title')[0];
  5406. if (!titleNode) {
  5407. titleNode = doc.createElementNS(this.SVG_NS, 'title');
  5408. this.element.appendChild(titleNode);
  5409. }
  5410. // Remove text content if it exists
  5411. if (titleNode.firstChild) {
  5412. titleNode.removeChild(titleNode.firstChild);
  5413. }
  5414. titleNode.appendChild(doc.createTextNode(
  5415. // #3276, #3895
  5416. String(pick(value, ''))
  5417. .replace(/<[^>]*>/g, '')
  5418. .replace(/&lt;/g, '<')
  5419. .replace(/&gt;/g, '>')));
  5420. };
  5421. /**
  5422. * Bring the element to the front. Alternatively, a new zIndex can be set.
  5423. *
  5424. * @sample highcharts/members/element-tofront/
  5425. * Click an element to bring it to front
  5426. *
  5427. * @function Highcharts.SVGElement#toFront
  5428. *
  5429. * @return {Highcharts.SVGElement}
  5430. * Returns the SVGElement for chaining.
  5431. */
  5432. SVGElement.prototype.toFront = function () {
  5433. var element = this.element;
  5434. element.parentNode.appendChild(element);
  5435. return this;
  5436. };
  5437. /**
  5438. * Move an object and its children by x and y values.
  5439. *
  5440. * @function Highcharts.SVGElement#translate
  5441. *
  5442. * @param {number} x
  5443. * The x value.
  5444. *
  5445. * @param {number} y
  5446. * The y value.
  5447. *
  5448. * @return {Highcharts.SVGElement}
  5449. */
  5450. SVGElement.prototype.translate = function (x, y) {
  5451. return this.attr({
  5452. translateX: x,
  5453. translateY: y
  5454. });
  5455. };
  5456. /**
  5457. * Update the shadow elements with new attributes.
  5458. *
  5459. * @private
  5460. * @function Highcharts.SVGElement#updateShadows
  5461. *
  5462. * @param {string} key
  5463. * The attribute name.
  5464. *
  5465. * @param {number} value
  5466. * The value of the attribute.
  5467. *
  5468. * @param {Function} setter
  5469. * The setter function, inherited from the parent wrapper.
  5470. */
  5471. SVGElement.prototype.updateShadows = function (key, value, setter) {
  5472. var shadows = this.shadows;
  5473. if (shadows) {
  5474. var i = shadows.length;
  5475. while (i--) {
  5476. setter.call(shadows[i], key === 'height' ?
  5477. Math.max(value - (shadows[i].cutHeight || 0), 0) :
  5478. key === 'd' ? this.d : value, key, shadows[i]);
  5479. }
  5480. }
  5481. };
  5482. /**
  5483. * Update the transform attribute based on internal properties. Deals with
  5484. * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
  5485. * attributes and updates the SVG `transform` attribute.
  5486. *
  5487. * @private
  5488. * @function Highcharts.SVGElement#updateTransform
  5489. */
  5490. SVGElement.prototype.updateTransform = function () {
  5491. var wrapper = this,
  5492. translateX = wrapper.translateX || 0,
  5493. translateY = wrapper.translateY || 0,
  5494. scaleX = wrapper.scaleX,
  5495. scaleY = wrapper.scaleY,
  5496. inverted = wrapper.inverted,
  5497. rotation = wrapper.rotation,
  5498. matrix = wrapper.matrix,
  5499. element = wrapper.element,
  5500. transform;
  5501. // Flipping affects translate as adjustment for flipping around the
  5502. // group's axis
  5503. if (inverted) {
  5504. translateX += wrapper.width;
  5505. translateY += wrapper.height;
  5506. }
  5507. // Apply translate. Nearly all transformed elements have translation,
  5508. // so instead of checking for translate = 0, do it always (#1767,
  5509. // #1846).
  5510. transform = ['translate(' + translateX + ',' + translateY + ')'];
  5511. // apply matrix
  5512. if (defined(matrix)) {
  5513. transform.push('matrix(' + matrix.join(',') + ')');
  5514. }
  5515. // apply rotation
  5516. if (inverted) {
  5517. transform.push('rotate(90) scale(-1,1)');
  5518. }
  5519. else if (rotation) { // text rotation
  5520. transform.push('rotate(' + rotation + ' ' +
  5521. pick(this.rotationOriginX, element.getAttribute('x'), 0) +
  5522. ' ' +
  5523. pick(this.rotationOriginY, element.getAttribute('y') || 0) + ')');
  5524. }
  5525. // apply scale
  5526. if (defined(scaleX) || defined(scaleY)) {
  5527. transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
  5528. }
  5529. if (transform.length) {
  5530. element.setAttribute('transform', transform.join(' '));
  5531. }
  5532. };
  5533. /**
  5534. * @private
  5535. * @function Highcharts.SVGElement#visibilitySetter
  5536. *
  5537. * @param {string} value
  5538. *
  5539. * @param {string} key
  5540. *
  5541. * @param {Highcharts.SVGDOMElement} element
  5542. *
  5543. * @return {void}
  5544. */
  5545. SVGElement.prototype.visibilitySetter = function (value, key, element) {
  5546. // IE9-11 doesn't handle visibilty:inherit well, so we remove the
  5547. // attribute instead (#2881, #3909)
  5548. if (value === 'inherit') {
  5549. element.removeAttribute(key);
  5550. }
  5551. else if (this[key] !== value) { // #6747
  5552. element.setAttribute(key, value);
  5553. }
  5554. this[key] = value;
  5555. };
  5556. /**
  5557. * @private
  5558. * @function Highcharts.SVGElement#xGetter
  5559. *
  5560. * @param {string} key
  5561. *
  5562. * @return {number|string|null}
  5563. */
  5564. SVGElement.prototype.xGetter = function (key) {
  5565. if (this.element.nodeName === 'circle') {
  5566. if (key === 'x') {
  5567. key = 'cx';
  5568. }
  5569. else if (key === 'y') {
  5570. key = 'cy';
  5571. }
  5572. }
  5573. return this._defaultGetter(key);
  5574. };
  5575. /**
  5576. * @private
  5577. * @function Highcharts.SVGElement#zIndexSetter
  5578. * @param {number} [value]
  5579. * @param {string} [key]
  5580. * @return {boolean}
  5581. */
  5582. SVGElement.prototype.zIndexSetter = function (value, key) {
  5583. var renderer = this.renderer,
  5584. parentGroup = this.parentGroup,
  5585. parentWrapper = parentGroup || renderer,
  5586. parentNode = parentWrapper.element || renderer.box,
  5587. childNodes,
  5588. otherElement,
  5589. otherZIndex,
  5590. element = this.element,
  5591. inserted = false,
  5592. undefinedOtherZIndex,
  5593. svgParent = parentNode === renderer.box,
  5594. run = this.added,
  5595. i;
  5596. if (defined(value)) {
  5597. // So we can read it for other elements in the group
  5598. element.setAttribute('data-z-index', value);
  5599. value = +value;
  5600. if (this[key] === value) {
  5601. // Only update when needed (#3865)
  5602. run = false;
  5603. }
  5604. }
  5605. else if (defined(this[key])) {
  5606. element.removeAttribute('data-z-index');
  5607. }
  5608. this[key] = value;
  5609. // Insert according to this and other elements' zIndex. Before .add() is
  5610. // called, nothing is done. Then on add, or by later calls to
  5611. // zIndexSetter, the node is placed on the right place in the DOM.
  5612. if (run) {
  5613. value = this.zIndex;
  5614. if (value && parentGroup) {
  5615. parentGroup.handleZ = true;
  5616. }
  5617. childNodes = parentNode.childNodes;
  5618. for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
  5619. otherElement = childNodes[i];
  5620. otherZIndex = otherElement.getAttribute('data-z-index');
  5621. undefinedOtherZIndex = !defined(otherZIndex);
  5622. if (otherElement !== element) {
  5623. if (
  5624. // Negative zIndex versus no zIndex:
  5625. // On all levels except the highest. If the parent is
  5626. // <svg>, then we don't want to put items before <desc>
  5627. // or <defs>
  5628. value < 0 &&
  5629. undefinedOtherZIndex &&
  5630. !svgParent &&
  5631. !i) {
  5632. parentNode.insertBefore(element, childNodes[i]);
  5633. inserted = true;
  5634. }
  5635. else if (
  5636. // Insert after the first element with a lower zIndex
  5637. pInt(otherZIndex) <= value ||
  5638. // If negative zIndex, add this before first undefined
  5639. // zIndex element
  5640. (undefinedOtherZIndex &&
  5641. (!defined(value) || value >= 0))) {
  5642. parentNode.insertBefore(element, childNodes[i + 1] || null // null for oldIE export
  5643. );
  5644. inserted = true;
  5645. }
  5646. }
  5647. }
  5648. if (!inserted) {
  5649. parentNode.insertBefore(element, childNodes[svgParent ? 3 : 0] || null // null for oldIE
  5650. );
  5651. inserted = true;
  5652. }
  5653. }
  5654. return inserted;
  5655. };
  5656. return SVGElement;
  5657. }());
  5658. // Some shared setters and getters
  5659. SVGElement.prototype['stroke-widthSetter'] = SVGElement.prototype.strokeSetter;
  5660. SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
  5661. SVGElement.prototype.matrixSetter =
  5662. SVGElement.prototype.rotationOriginXSetter =
  5663. SVGElement.prototype.rotationOriginYSetter =
  5664. SVGElement.prototype.rotationSetter =
  5665. SVGElement.prototype.scaleXSetter =
  5666. SVGElement.prototype.scaleYSetter =
  5667. SVGElement.prototype.translateXSetter =
  5668. SVGElement.prototype.translateYSetter =
  5669. SVGElement.prototype.verticalAlignSetter = function (value, key) {
  5670. this[key] = value;
  5671. this.doTransform = true;
  5672. };
  5673. H.SVGElement = SVGElement;
  5674. return H.SVGElement;
  5675. });
  5676. _registerModule(_modules, 'Core/Renderer/SVG/SVGLabel.js', [_modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (SVGElement, U) {
  5677. /* *
  5678. *
  5679. * (c) 2010-2020 Torstein Honsi
  5680. *
  5681. * License: www.highcharts.com/license
  5682. *
  5683. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5684. *
  5685. * */
  5686. var __extends = (this && this.__extends) || (function () {
  5687. var extendStatics = function (d,
  5688. b) {
  5689. extendStatics = Object.setPrototypeOf ||
  5690. ({ __proto__: [] } instanceof Array && function (d,
  5691. b) { d.__proto__ = b; }) ||
  5692. function (d,
  5693. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  5694. return extendStatics(d, b);
  5695. };
  5696. return function (d, b) {
  5697. extendStatics(d, b);
  5698. function __() { this.constructor = d; }
  5699. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  5700. };
  5701. })();
  5702. var defined = U.defined,
  5703. extend = U.extend,
  5704. isNumber = U.isNumber,
  5705. merge = U.merge,
  5706. removeEvent = U.removeEvent;
  5707. /**
  5708. * SVG label to render text.
  5709. * @private
  5710. * @class
  5711. * @name Highcharts.SVGLabel
  5712. * @augments Highcharts.SVGElement
  5713. */
  5714. var SVGLabel = /** @class */ (function (_super) {
  5715. __extends(SVGLabel, _super);
  5716. /* *
  5717. *
  5718. * Constructors
  5719. *
  5720. * */
  5721. function SVGLabel(renderer, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  5722. var _this = _super.call(this) || this;
  5723. _this.init(renderer, 'g');
  5724. _this.textStr = str;
  5725. _this.x = x;
  5726. _this.y = y;
  5727. _this.anchorX = anchorX;
  5728. _this.anchorY = anchorY;
  5729. _this.baseline = baseline;
  5730. _this.className = className;
  5731. if (className !== 'button') {
  5732. _this.addClass('highcharts-label');
  5733. }
  5734. if (className) {
  5735. _this.addClass('highcharts-' + className);
  5736. }
  5737. _this.text = renderer.text('', 0, 0, useHTML)
  5738. .attr({
  5739. zIndex: 1
  5740. });
  5741. // Validate the shape argument
  5742. var hasBGImage;
  5743. if (typeof shape === 'string') {
  5744. hasBGImage = /^url\((.*?)\)$/.test(shape);
  5745. if (_this.renderer.symbols[shape] || hasBGImage) {
  5746. _this.symbolKey = shape;
  5747. }
  5748. }
  5749. _this.bBox = SVGLabel.emptyBBox;
  5750. _this.padding = 3;
  5751. _this.paddingLeft = 0;
  5752. _this.baselineOffset = 0;
  5753. _this.needsBox = renderer.styledMode || hasBGImage;
  5754. _this.deferredAttr = {};
  5755. _this.alignFactor = 0;
  5756. return _this;
  5757. }
  5758. /* *
  5759. *
  5760. * Functions
  5761. *
  5762. * */
  5763. SVGLabel.prototype.alignSetter = function (value) {
  5764. var alignFactor = {
  5765. left: 0,
  5766. center: 0.5,
  5767. right: 1
  5768. }[value];
  5769. if (alignFactor !== this.alignFactor) {
  5770. this.alignFactor = alignFactor;
  5771. // Bounding box exists, means we're dynamically changing
  5772. if (this.bBox && isNumber(this.xSetting)) {
  5773. this.attr({ x: this.xSetting }); // #5134
  5774. }
  5775. }
  5776. };
  5777. SVGLabel.prototype.anchorXSetter = function (value, key) {
  5778. this.anchorX = value;
  5779. this.boxAttr(key, Math.round(value) - this.getCrispAdjust() - this.xSetting);
  5780. };
  5781. SVGLabel.prototype.anchorYSetter = function (value, key) {
  5782. this.anchorY = value;
  5783. this.boxAttr(key, value - this.ySetting);
  5784. };
  5785. /*
  5786. * Set a box attribute, or defer it if the box is not yet created
  5787. */
  5788. SVGLabel.prototype.boxAttr = function (key, value) {
  5789. if (this.box) {
  5790. this.box.attr(key, value);
  5791. }
  5792. else {
  5793. this.deferredAttr[key] = value;
  5794. }
  5795. };
  5796. /*
  5797. * Pick up some properties and apply them to the text instead of the
  5798. * wrapper.
  5799. */
  5800. SVGLabel.prototype.css = function (styles) {
  5801. if (styles) {
  5802. var textStyles = {},
  5803. isWidth,
  5804. isFontStyle;
  5805. // Create a copy to avoid altering the original object
  5806. // (#537)
  5807. styles = merge(styles);
  5808. SVGLabel.textProps.forEach(function (prop) {
  5809. if (typeof styles[prop] !== 'undefined') {
  5810. textStyles[prop] = styles[prop];
  5811. delete styles[prop];
  5812. }
  5813. });
  5814. this.text.css(textStyles);
  5815. isWidth = 'width' in textStyles;
  5816. isFontStyle = 'fontSize' in textStyles ||
  5817. 'fontWeight' in textStyles;
  5818. // Update existing text, box (#9400, #12163)
  5819. if (isWidth || isFontStyle) {
  5820. this.updateBoxSize();
  5821. // Keep updated (#9400, #12163)
  5822. if (isFontStyle) {
  5823. this.updateTextPadding();
  5824. }
  5825. }
  5826. }
  5827. return SVGElement.prototype.css.call(this, styles);
  5828. };
  5829. /*
  5830. * Destroy and release memory.
  5831. */
  5832. SVGLabel.prototype.destroy = function () {
  5833. // Added by button implementation
  5834. removeEvent(this.element, 'mouseenter');
  5835. removeEvent(this.element, 'mouseleave');
  5836. if (this.text) {
  5837. this.text.destroy();
  5838. }
  5839. if (this.box) {
  5840. this.box = this.box.destroy();
  5841. }
  5842. // Call base implementation to destroy the rest
  5843. SVGElement.prototype.destroy.call(this);
  5844. return void 0;
  5845. };
  5846. SVGLabel.prototype.fillSetter = function (value, key) {
  5847. if (value) {
  5848. this.needsBox = true;
  5849. }
  5850. // for animation getter (#6776)
  5851. this.fill = value;
  5852. this.boxAttr(key, value);
  5853. };
  5854. /*
  5855. * Return the bounding box of the box, not the group.
  5856. */
  5857. SVGLabel.prototype.getBBox = function () {
  5858. var bBox = this.bBox;
  5859. var padding = this.padding;
  5860. return {
  5861. width: bBox.width + 2 * padding,
  5862. height: bBox.height + 2 * padding,
  5863. x: bBox.x - padding,
  5864. y: bBox.y - padding
  5865. };
  5866. };
  5867. SVGLabel.prototype.getCrispAdjust = function () {
  5868. return this.renderer.styledMode && this.box ?
  5869. this.box.strokeWidth() % 2 / 2 :
  5870. (this['stroke-width'] ? parseInt(this['stroke-width'], 10) : 0) % 2 / 2;
  5871. };
  5872. SVGLabel.prototype.heightSetter = function (value) {
  5873. this.heightSetting = value;
  5874. };
  5875. // Event handling. In case of useHTML, we need to make sure that events
  5876. // are captured on the span as well, and that mouseenter/mouseleave
  5877. // between the SVG group and the HTML span are not treated as real
  5878. // enter/leave events. #13310.
  5879. SVGLabel.prototype.on = function (eventType, handler) {
  5880. var label = this;
  5881. var text = label.text;
  5882. var span = text && text.element.tagName === 'SPAN' ? text : void 0;
  5883. var selectiveHandler;
  5884. if (span) {
  5885. selectiveHandler = function (e) {
  5886. if ((eventType === 'mouseenter' ||
  5887. eventType === 'mouseleave') &&
  5888. e.relatedTarget instanceof Element &&
  5889. (label.element.contains(e.relatedTarget) ||
  5890. span.element.contains(e.relatedTarget))) {
  5891. return;
  5892. }
  5893. handler.call(label.element, e);
  5894. };
  5895. span.on(eventType, selectiveHandler);
  5896. }
  5897. SVGElement.prototype.on.call(label, eventType, selectiveHandler || handler);
  5898. return label;
  5899. };
  5900. /*
  5901. * After the text element is added, get the desired size of the border
  5902. * box and add it before the text in the DOM.
  5903. */
  5904. SVGLabel.prototype.onAdd = function () {
  5905. var str = this.textStr;
  5906. this.text.add(this);
  5907. this.attr({
  5908. // Alignment is available now (#3295, 0 not rendered if given
  5909. // as a value)
  5910. text: (defined(str) ? str : ''),
  5911. x: this.x,
  5912. y: this.y
  5913. });
  5914. if (this.box && defined(this.anchorX)) {
  5915. this.attr({
  5916. anchorX: this.anchorX,
  5917. anchorY: this.anchorY
  5918. });
  5919. }
  5920. };
  5921. SVGLabel.prototype.paddingSetter = function (value) {
  5922. if (defined(value) && value !== this.padding) {
  5923. this.padding = value;
  5924. this.updateTextPadding();
  5925. }
  5926. };
  5927. SVGLabel.prototype.paddingLeftSetter = function (value) {
  5928. if (defined(value) && value !== this.paddingLeft) {
  5929. this.paddingLeft = value;
  5930. this.updateTextPadding();
  5931. }
  5932. };
  5933. SVGLabel.prototype.rSetter = function (value, key) {
  5934. this.boxAttr(key, value);
  5935. };
  5936. SVGLabel.prototype.shadow = function (b) {
  5937. if (b && !this.renderer.styledMode) {
  5938. this.updateBoxSize();
  5939. if (this.box) {
  5940. this.box.shadow(b);
  5941. }
  5942. }
  5943. return this;
  5944. };
  5945. SVGLabel.prototype.strokeSetter = function (value, key) {
  5946. // for animation getter (#6776)
  5947. this.stroke = value;
  5948. this.boxAttr(key, value);
  5949. };
  5950. SVGLabel.prototype['stroke-widthSetter'] = function (value, key) {
  5951. if (value) {
  5952. this.needsBox = true;
  5953. }
  5954. this['stroke-width'] = value;
  5955. this.boxAttr(key, value);
  5956. };
  5957. SVGLabel.prototype['text-alignSetter'] = function (value) {
  5958. this.textAlign = value;
  5959. };
  5960. SVGLabel.prototype.textSetter = function (text) {
  5961. if (typeof text !== 'undefined') {
  5962. // Must use .attr to ensure transforms are done (#10009)
  5963. this.text.attr({ text: text });
  5964. }
  5965. this.updateBoxSize();
  5966. this.updateTextPadding();
  5967. };
  5968. /*
  5969. * This function runs after the label is added to the DOM (when the bounding
  5970. * box is available), and after the text of the label is updated to detect
  5971. * the new bounding box and reflect it in the border box.
  5972. */
  5973. SVGLabel.prototype.updateBoxSize = function () {
  5974. var style = this.text.element.style,
  5975. crispAdjust,
  5976. attribs = {};
  5977. var padding = this.padding;
  5978. var paddingLeft = this.paddingLeft;
  5979. // #12165 error when width is null (auto)
  5980. // #12163 when fontweight: bold, recalculate bBox withot cache
  5981. // #3295 && 3514 box failure when string equals 0
  5982. var bBox = ((!isNumber(this.widthSetting) || !isNumber(this.heightSetting) || this.textAlign) &&
  5983. defined(this.text.textStr)) ?
  5984. this.text.getBBox() : SVGLabel.emptyBBox;
  5985. this.width = ((this.widthSetting || bBox.width || 0) +
  5986. 2 * padding +
  5987. paddingLeft);
  5988. this.height = (this.heightSetting || bBox.height || 0) + 2 * padding;
  5989. // Update the label-scoped y offset. Math.min because of inline
  5990. // style (#9400)
  5991. this.baselineOffset = padding + Math.min(this.renderer.fontMetrics(style && style.fontSize, this.text).b,
  5992. // When the height is 0, there is no bBox, so go with the font
  5993. // metrics. Highmaps CSS demos.
  5994. bBox.height || Infinity);
  5995. if (this.needsBox) {
  5996. // Create the border box if it is not already present
  5997. if (!this.box) {
  5998. // Symbol definition exists (#5324)
  5999. var box = this.box = this.symbolKey ?
  6000. this.renderer.symbol(this.symbolKey) :
  6001. this.renderer.rect();
  6002. box.addClass(// Don't use label className for buttons
  6003. (this.className === 'button' ? '' : 'highcharts-label-box') +
  6004. (this.className ? ' highcharts-' + this.className + '-box' : ''));
  6005. box.add(this);
  6006. crispAdjust = this.getCrispAdjust();
  6007. attribs.x = crispAdjust;
  6008. attribs.y = (this.baseline ? -this.baselineOffset : 0) + crispAdjust;
  6009. }
  6010. // Apply the box attributes
  6011. attribs.width = Math.round(this.width);
  6012. attribs.height = Math.round(this.height);
  6013. this.box.attr(extend(attribs, this.deferredAttr));
  6014. this.deferredAttr = {};
  6015. }
  6016. this.bBox = bBox;
  6017. };
  6018. /*
  6019. * This function runs after setting text or padding, but only if padding
  6020. * is changed.
  6021. */
  6022. SVGLabel.prototype.updateTextPadding = function () {
  6023. var text = this.text;
  6024. // Determine y based on the baseline
  6025. var textY = this.baseline ? 0 : this.baselineOffset;
  6026. var textX = this.paddingLeft + this.padding;
  6027. // compensate for alignment
  6028. if (defined(this.widthSetting) &&
  6029. this.bBox &&
  6030. (this.textAlign === 'center' || this.textAlign === 'right')) {
  6031. textX += { center: 0.5, right: 1 }[this.textAlign] *
  6032. (this.widthSetting - this.bBox.width);
  6033. }
  6034. // update if anything changed
  6035. if (textX !== text.x || textY !== text.y) {
  6036. text.attr('x', textX);
  6037. // #8159 - prevent misplaced data labels in treemap
  6038. // (useHTML: true)
  6039. if (text.hasBoxWidthChanged) {
  6040. this.bBox = text.getBBox(true);
  6041. this.updateBoxSize();
  6042. }
  6043. if (typeof textY !== 'undefined') {
  6044. text.attr('y', textY);
  6045. }
  6046. }
  6047. // record current values
  6048. text.x = textX;
  6049. text.y = textY;
  6050. };
  6051. SVGLabel.prototype.widthSetter = function (value) {
  6052. // width:auto => null
  6053. this.widthSetting = isNumber(value) ? value : void 0;
  6054. };
  6055. SVGLabel.prototype.xSetter = function (value) {
  6056. this.x = value; // for animation getter
  6057. if (this.alignFactor) {
  6058. value -= this.alignFactor * ((this.widthSetting || this.bBox.width) +
  6059. 2 * this.padding);
  6060. // Force animation even when setting to the same value (#7898)
  6061. this['forceAnimate:x'] = true;
  6062. }
  6063. this.xSetting = Math.round(value);
  6064. this.attr('translateX', this.xSetting);
  6065. };
  6066. SVGLabel.prototype.ySetter = function (value) {
  6067. this.ySetting = this.y = Math.round(value);
  6068. this.attr('translateY', this.ySetting);
  6069. };
  6070. /* *
  6071. *
  6072. * Static Properties
  6073. *
  6074. * */
  6075. SVGLabel.emptyBBox = { width: 0, height: 0, x: 0, y: 0 };
  6076. /* *
  6077. *
  6078. * Properties
  6079. *
  6080. * */
  6081. /**
  6082. * For labels, these CSS properties are applied to the `text` node directly.
  6083. *
  6084. * @private
  6085. * @name Highcharts.SVGLabel#textProps
  6086. * @type {Array<string>}
  6087. */
  6088. SVGLabel.textProps = [
  6089. 'color', 'cursor', 'direction', 'fontFamily', 'fontSize', 'fontStyle',
  6090. 'fontWeight', 'lineHeight', 'textAlign', 'textDecoration',
  6091. 'textOutline', 'textOverflow', 'width'
  6092. ];
  6093. return SVGLabel;
  6094. }(SVGElement));
  6095. return SVGLabel;
  6096. });
  6097. _registerModule(_modules, 'Core/Renderer/SVG/SVGRenderer.js', [_modules['Core/Color.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGLabel.js'], _modules['Core/Utilities.js']], function (Color, H, SVGElement, SVGLabel, U) {
  6098. /* *
  6099. *
  6100. * (c) 2010-2020 Torstein Honsi
  6101. *
  6102. * License: www.highcharts.com/license
  6103. *
  6104. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6105. *
  6106. * */
  6107. var addEvent = U.addEvent,
  6108. attr = U.attr,
  6109. createElement = U.createElement,
  6110. css = U.css,
  6111. defined = U.defined,
  6112. destroyObjectProperties = U.destroyObjectProperties,
  6113. extend = U.extend,
  6114. isArray = U.isArray,
  6115. isNumber = U.isNumber,
  6116. isObject = U.isObject,
  6117. isString = U.isString,
  6118. merge = U.merge,
  6119. objectEach = U.objectEach,
  6120. pick = U.pick,
  6121. pInt = U.pInt,
  6122. splat = U.splat,
  6123. uniqueKey = U.uniqueKey;
  6124. /**
  6125. * A clipping rectangle that can be applied to one or more {@link SVGElement}
  6126. * instances. It is instanciated with the {@link SVGRenderer#clipRect} function
  6127. * and applied with the {@link SVGElement#clip} function.
  6128. *
  6129. * @example
  6130. * var circle = renderer.circle(100, 100, 100)
  6131. * .attr({ fill: 'red' })
  6132. * .add();
  6133. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  6134. *
  6135. * // Leave only the lower right quarter visible
  6136. * circle.clip(clipRect);
  6137. *
  6138. * @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
  6139. */
  6140. /**
  6141. * The font metrics.
  6142. *
  6143. * @interface Highcharts.FontMetricsObject
  6144. */ /**
  6145. * The baseline relative to the top of the box.
  6146. *
  6147. * @name Highcharts.FontMetricsObject#b
  6148. * @type {number}
  6149. */ /**
  6150. * The font size.
  6151. *
  6152. * @name Highcharts.FontMetricsObject#f
  6153. * @type {number}
  6154. */ /**
  6155. * The line height.
  6156. *
  6157. * @name Highcharts.FontMetricsObject#h
  6158. * @type {number}
  6159. */
  6160. /**
  6161. * An object containing `x` and `y` properties for the position of an element.
  6162. *
  6163. * @interface Highcharts.PositionObject
  6164. */ /**
  6165. * X position of the element.
  6166. * @name Highcharts.PositionObject#x
  6167. * @type {number}
  6168. */ /**
  6169. * Y position of the element.
  6170. * @name Highcharts.PositionObject#y
  6171. * @type {number}
  6172. */
  6173. /**
  6174. * A rectangle.
  6175. *
  6176. * @interface Highcharts.RectangleObject
  6177. */ /**
  6178. * Height of the rectangle.
  6179. * @name Highcharts.RectangleObject#height
  6180. * @type {number}
  6181. */ /**
  6182. * Width of the rectangle.
  6183. * @name Highcharts.RectangleObject#width
  6184. * @type {number}
  6185. */ /**
  6186. * Horizontal position of the rectangle.
  6187. * @name Highcharts.RectangleObject#x
  6188. * @type {number}
  6189. */ /**
  6190. * Vertical position of the rectangle.
  6191. * @name Highcharts.RectangleObject#y
  6192. * @type {number}
  6193. */
  6194. /**
  6195. * The shadow options.
  6196. *
  6197. * @interface Highcharts.ShadowOptionsObject
  6198. */ /**
  6199. * The shadow color.
  6200. * @name Highcharts.ShadowOptionsObject#color
  6201. * @type {Highcharts.ColorString|undefined}
  6202. * @default #000000
  6203. */ /**
  6204. * The horizontal offset from the element.
  6205. *
  6206. * @name Highcharts.ShadowOptionsObject#offsetX
  6207. * @type {number|undefined}
  6208. * @default 1
  6209. */ /**
  6210. * The vertical offset from the element.
  6211. * @name Highcharts.ShadowOptionsObject#offsetY
  6212. * @type {number|undefined}
  6213. * @default 1
  6214. */ /**
  6215. * The shadow opacity.
  6216. *
  6217. * @name Highcharts.ShadowOptionsObject#opacity
  6218. * @type {number|undefined}
  6219. * @default 0.15
  6220. */ /**
  6221. * The shadow width or distance from the element.
  6222. * @name Highcharts.ShadowOptionsObject#width
  6223. * @type {number|undefined}
  6224. * @default 3
  6225. */
  6226. /**
  6227. * @interface Highcharts.SizeObject
  6228. */ /**
  6229. * @name Highcharts.SizeObject#height
  6230. * @type {number}
  6231. */ /**
  6232. * @name Highcharts.SizeObject#width
  6233. * @type {number}
  6234. */
  6235. /**
  6236. * Serialized form of an SVG definition, including children. Some key
  6237. * property names are reserved: tagName, textContent, and children.
  6238. *
  6239. * @interface Highcharts.SVGDefinitionObject
  6240. */ /**
  6241. * @name Highcharts.SVGDefinitionObject#[key:string]
  6242. * @type {boolean|number|string|Array<Highcharts.SVGDefinitionObject>|undefined}
  6243. */ /**
  6244. * @name Highcharts.SVGDefinitionObject#children
  6245. * @type {Array<Highcharts.SVGDefinitionObject>|undefined}
  6246. */ /**
  6247. * @name Highcharts.SVGDefinitionObject#tagName
  6248. * @type {string|undefined}
  6249. */ /**
  6250. * @name Highcharts.SVGDefinitionObject#textContent
  6251. * @type {string|undefined}
  6252. */
  6253. /**
  6254. * Array of path commands, that will go into the `d` attribute of an SVG
  6255. * element.
  6256. *
  6257. * @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
  6258. */
  6259. /**
  6260. * Possible path commands in an SVG path array. Valid values are `A`, `C`, `H`,
  6261. * `L`, `M`, `Q`, `S`, `T`, `V`, `Z`.
  6262. *
  6263. * @typedef {string} Highcharts.SVGPathCommand
  6264. * @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
  6265. */
  6266. /**
  6267. * An extendable collection of functions for defining symbol paths. Symbols are
  6268. * used internally for point markers, button and label borders and backgrounds,
  6269. * or custom shapes. Extendable by adding to {@link SVGRenderer#symbols}.
  6270. *
  6271. * @interface Highcharts.SymbolDictionary
  6272. */ /**
  6273. * @name Highcharts.SymbolDictionary#[key:string]
  6274. * @type {Function|undefined}
  6275. */ /**
  6276. * @name Highcharts.SymbolDictionary#arc
  6277. * @type {Function|undefined}
  6278. */ /**
  6279. * @name Highcharts.SymbolDictionary#callout
  6280. * @type {Function|undefined}
  6281. */ /**
  6282. * @name Highcharts.SymbolDictionary#circle
  6283. * @type {Function|undefined}
  6284. */ /**
  6285. * @name Highcharts.SymbolDictionary#diamond
  6286. * @type {Function|undefined}
  6287. */ /**
  6288. * @name Highcharts.SymbolDictionary#square
  6289. * @type {Function|undefined}
  6290. */ /**
  6291. * @name Highcharts.SymbolDictionary#triangle
  6292. * @type {Function|undefined}
  6293. */
  6294. /**
  6295. * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`, `triangle`,
  6296. * and `triangle-down`. Symbols are used internally for point markers, button
  6297. * and label borders and backgrounds, or custom shapes. Extendable by adding to
  6298. * {@link SVGRenderer#symbols}.
  6299. *
  6300. * @typedef {"arc"|"callout"|"circle"|"diamond"|"square"|"triangle"|"triangle-down"} Highcharts.SymbolKeyValue
  6301. */
  6302. /**
  6303. * Additional options, depending on the actual symbol drawn.
  6304. *
  6305. * @interface Highcharts.SymbolOptionsObject
  6306. */ /**
  6307. * The anchor X position for the `callout` symbol. This is where the chevron
  6308. * points to.
  6309. *
  6310. * @name Highcharts.SymbolOptionsObject#anchorX
  6311. * @type {number|undefined}
  6312. */ /**
  6313. * The anchor Y position for the `callout` symbol. This is where the chevron
  6314. * points to.
  6315. *
  6316. * @name Highcharts.SymbolOptionsObject#anchorY
  6317. * @type {number|undefined}
  6318. */ /**
  6319. * The end angle of an `arc` symbol.
  6320. *
  6321. * @name Highcharts.SymbolOptionsObject#end
  6322. * @type {number|undefined}
  6323. */ /**
  6324. * Whether to draw `arc` symbol open or closed.
  6325. *
  6326. * @name Highcharts.SymbolOptionsObject#open
  6327. * @type {boolean|undefined}
  6328. */ /**
  6329. * The radius of an `arc` symbol, or the border radius for the `callout` symbol.
  6330. *
  6331. * @name Highcharts.SymbolOptionsObject#r
  6332. * @type {number|undefined}
  6333. */ /**
  6334. * The start angle of an `arc` symbol.
  6335. *
  6336. * @name Highcharts.SymbolOptionsObject#start
  6337. * @type {number|undefined}
  6338. */
  6339. /* eslint-disable no-invalid-this, valid-jsdoc */
  6340. var charts = H.charts,
  6341. deg2rad = H.deg2rad,
  6342. doc = H.doc,
  6343. isFirefox = H.isFirefox,
  6344. isMS = H.isMS,
  6345. isWebKit = H.isWebKit,
  6346. noop = H.noop,
  6347. svg = H.svg,
  6348. SVG_NS = H.SVG_NS,
  6349. symbolSizes = H.symbolSizes,
  6350. win = H.win;
  6351. /**
  6352. * Allows direct access to the Highcharts rendering layer in order to draw
  6353. * primitive shapes like circles, rectangles, paths or text directly on a chart,
  6354. * or independent from any chart. The SVGRenderer represents a wrapper object
  6355. * for SVG in modern browsers. Through the VMLRenderer, part of the `oldie.js`
  6356. * module, it also brings vector graphics to IE <= 8.
  6357. *
  6358. * An existing chart's renderer can be accessed through {@link Chart.renderer}.
  6359. * The renderer can also be used completely decoupled from a chart.
  6360. *
  6361. * @sample highcharts/members/renderer-on-chart
  6362. * Annotating a chart programmatically.
  6363. * @sample highcharts/members/renderer-basic
  6364. * Independent SVG drawing.
  6365. *
  6366. * @example
  6367. * // Use directly without a chart object.
  6368. * var renderer = new Highcharts.Renderer(parentNode, 600, 400);
  6369. *
  6370. * @class
  6371. * @name Highcharts.SVGRenderer
  6372. *
  6373. * @param {Highcharts.HTMLDOMElement} container
  6374. * Where to put the SVG in the web page.
  6375. *
  6376. * @param {number} width
  6377. * The width of the SVG.
  6378. *
  6379. * @param {number} height
  6380. * The height of the SVG.
  6381. *
  6382. * @param {Highcharts.CSSObject} [style]
  6383. * The box style, if not in styleMode
  6384. *
  6385. * @param {boolean} [forExport=false]
  6386. * Whether the rendered content is intended for export.
  6387. *
  6388. * @param {boolean} [allowHTML=true]
  6389. * Whether the renderer is allowed to include HTML text, which will be
  6390. * projected on top of the SVG.
  6391. *
  6392. * @param {boolean} [styledMode=false]
  6393. * Whether the renderer belongs to a chart that is in styled mode.
  6394. * If it does, it will avoid setting presentational attributes in
  6395. * some cases, but not when set explicitly through `.attr` and `.css`
  6396. * etc.
  6397. */
  6398. var SVGRenderer = /** @class */ (function () {
  6399. /* *
  6400. *
  6401. * Constructors
  6402. *
  6403. * */
  6404. function SVGRenderer(container, width, height, style, forExport, allowHTML, styledMode) {
  6405. /* *
  6406. *
  6407. * Properties
  6408. *
  6409. * */
  6410. this.alignedObjects = void 0;
  6411. /**
  6412. * The root `svg` node of the renderer.
  6413. *
  6414. * @name Highcharts.SVGRenderer#box
  6415. * @type {Highcharts.SVGDOMElement}
  6416. */
  6417. this.box = void 0;
  6418. /**
  6419. * The wrapper for the root `svg` node of the renderer.
  6420. *
  6421. * @name Highcharts.SVGRenderer#boxWrapper
  6422. * @type {Highcharts.SVGElement}
  6423. */
  6424. this.boxWrapper = void 0;
  6425. this.cache = void 0;
  6426. this.cacheKeys = void 0;
  6427. this.chartIndex = void 0;
  6428. /**
  6429. * A pointer to the `defs` node of the root SVG.
  6430. *
  6431. * @name Highcharts.SVGRenderer#defs
  6432. * @type {Highcharts.SVGElement}
  6433. */
  6434. this.defs = void 0;
  6435. this.globalAnimation = void 0;
  6436. this.gradients = void 0;
  6437. this.height = void 0;
  6438. this.imgCount = void 0;
  6439. this.isSVG = void 0;
  6440. this.style = void 0;
  6441. /**
  6442. * Page url used for internal references.
  6443. *
  6444. * @private
  6445. * @name Highcharts.SVGRenderer#url
  6446. * @type {string}
  6447. */
  6448. this.url = void 0;
  6449. this.width = void 0;
  6450. this.init(container, width, height, style, forExport, allowHTML, styledMode);
  6451. }
  6452. /* *
  6453. *
  6454. * Functions
  6455. *
  6456. * */
  6457. /**
  6458. * Initialize the SVGRenderer. Overridable initializer function that takes
  6459. * the same parameters as the constructor.
  6460. *
  6461. * @function Highcharts.SVGRenderer#init
  6462. *
  6463. * @param {Highcharts.HTMLDOMElement} container
  6464. * Where to put the SVG in the web page.
  6465. *
  6466. * @param {number} width
  6467. * The width of the SVG.
  6468. *
  6469. * @param {number} height
  6470. * The height of the SVG.
  6471. *
  6472. * @param {Highcharts.CSSObject} [style]
  6473. * The box style, if not in styleMode
  6474. *
  6475. * @param {boolean} [forExport=false]
  6476. * Whether the rendered content is intended for export.
  6477. *
  6478. * @param {boolean} [allowHTML=true]
  6479. * Whether the renderer is allowed to include HTML text, which will be
  6480. * projected on top of the SVG.
  6481. *
  6482. * @param {boolean} [styledMode=false]
  6483. * Whether the renderer belongs to a chart that is in styled mode. If it
  6484. * does, it will avoid setting presentational attributes in some cases, but
  6485. * not when set explicitly through `.attr` and `.css` etc.
  6486. */
  6487. SVGRenderer.prototype.init = function (container, width, height, style, forExport, allowHTML, styledMode) {
  6488. var renderer = this,
  6489. boxWrapper,
  6490. element,
  6491. desc;
  6492. boxWrapper = renderer.createElement('svg')
  6493. .attr({
  6494. version: '1.1',
  6495. 'class': 'highcharts-root'
  6496. });
  6497. if (!styledMode) {
  6498. boxWrapper.css(this.getStyle(style));
  6499. }
  6500. element = boxWrapper.element;
  6501. container.appendChild(element);
  6502. // Always use ltr on the container, otherwise text-anchor will be
  6503. // flipped and text appear outside labels, buttons, tooltip etc (#3482)
  6504. attr(container, 'dir', 'ltr');
  6505. // For browsers other than IE, add the namespace attribute (#1978)
  6506. if (container.innerHTML.indexOf('xmlns') === -1) {
  6507. attr(element, 'xmlns', this.SVG_NS);
  6508. }
  6509. // object properties
  6510. renderer.isSVG = true;
  6511. this.box = element;
  6512. this.boxWrapper = boxWrapper;
  6513. renderer.alignedObjects = [];
  6514. // #24, #672, #1070
  6515. this.url = ((isFirefox || isWebKit) &&
  6516. doc.getElementsByTagName('base').length) ?
  6517. win.location.href
  6518. .split('#')[0] // remove the hash
  6519. .replace(/<[^>]*>/g, '') // wing cut HTML
  6520. // escape parantheses and quotes
  6521. .replace(/([\('\)])/g, '\\$1')
  6522. // replace spaces (needed for Safari only)
  6523. .replace(/ /g, '%20') :
  6524. '';
  6525. // Add description
  6526. desc = this.createElement('desc').add();
  6527. desc.element.appendChild(doc.createTextNode('Created with Highcharts 8.2.0'));
  6528. renderer.defs = this.createElement('defs').add();
  6529. renderer.allowHTML = allowHTML;
  6530. renderer.forExport = forExport;
  6531. renderer.styledMode = styledMode;
  6532. renderer.gradients = {}; // Object where gradient SvgElements are stored
  6533. renderer.cache = {}; // Cache for numerical bounding boxes
  6534. renderer.cacheKeys = [];
  6535. renderer.imgCount = 0;
  6536. renderer.setSize(width, height, false);
  6537. // Issue 110 workaround:
  6538. // In Firefox, if a div is positioned by percentage, its pixel position
  6539. // may land between pixels. The container itself doesn't display this,
  6540. // but an SVG element inside this container will be drawn at subpixel
  6541. // precision. In order to draw sharp lines, this must be compensated
  6542. // for. This doesn't seem to work inside iframes though (like in
  6543. // jsFiddle).
  6544. var subPixelFix,
  6545. rect;
  6546. if (isFirefox && container.getBoundingClientRect) {
  6547. subPixelFix = function () {
  6548. css(container, { left: 0, top: 0 });
  6549. rect = container.getBoundingClientRect();
  6550. css(container, {
  6551. left: (Math.ceil(rect.left) - rect.left) + 'px',
  6552. top: (Math.ceil(rect.top) - rect.top) + 'px'
  6553. });
  6554. };
  6555. // run the fix now
  6556. subPixelFix();
  6557. // run it on resize
  6558. renderer.unSubPixelFix = addEvent(win, 'resize', subPixelFix);
  6559. }
  6560. };
  6561. /**
  6562. * General method for adding a definition to the SVG `defs` tag. Can be used
  6563. * for gradients, fills, filters etc. Styled mode only. A hook for adding
  6564. * general definitions to the SVG's defs tag. Definitions can be referenced
  6565. * from the CSS by its `id`. Read more in
  6566. * [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  6567. * Styled mode only.
  6568. *
  6569. * @function Highcharts.SVGRenderer#definition
  6570. *
  6571. * @param {Highcharts.SVGDefinitionObject} def
  6572. * A serialized form of an SVG definition, including children.
  6573. *
  6574. * @return {Highcharts.SVGElement}
  6575. * The inserted node.
  6576. */
  6577. SVGRenderer.prototype.definition = function (def) {
  6578. var ren = this;
  6579. /**
  6580. * @private
  6581. * @param {Highcharts.SVGDefinitionObject} config - SVG definition
  6582. * @param {Highcharts.SVGElement} [parent] - parent node
  6583. */
  6584. function recurse(config, parent) {
  6585. var ret;
  6586. splat(config).forEach(function (item) {
  6587. var node = ren.createElement(item.tagName),
  6588. attr = {};
  6589. // Set attributes
  6590. objectEach(item, function (val, key) {
  6591. if (key !== 'tagName' &&
  6592. key !== 'children' &&
  6593. key !== 'textContent') {
  6594. attr[key] = val;
  6595. }
  6596. });
  6597. node.attr(attr);
  6598. // Add to the tree
  6599. node.add(parent || ren.defs);
  6600. // Add text content
  6601. if (item.textContent) {
  6602. node.element.appendChild(doc.createTextNode(item.textContent));
  6603. }
  6604. // Recurse
  6605. recurse(item.children || [], node);
  6606. ret = node;
  6607. });
  6608. // Return last node added (on top level it's the only one)
  6609. return ret;
  6610. }
  6611. return recurse(def);
  6612. };
  6613. /**
  6614. * Get the global style setting for the renderer.
  6615. *
  6616. * @private
  6617. * @function Highcharts.SVGRenderer#getStyle
  6618. *
  6619. * @param {Highcharts.CSSObject} style
  6620. * Style settings.
  6621. *
  6622. * @return {Highcharts.CSSObject}
  6623. * The style settings mixed with defaults.
  6624. */
  6625. SVGRenderer.prototype.getStyle = function (style) {
  6626. this.style = extend({
  6627. fontFamily: '"Lucida Grande", "Lucida Sans Unicode", ' +
  6628. 'Arial, Helvetica, sans-serif',
  6629. fontSize: '12px'
  6630. }, style);
  6631. return this.style;
  6632. };
  6633. /**
  6634. * Apply the global style on the renderer, mixed with the default styles.
  6635. *
  6636. * @function Highcharts.SVGRenderer#setStyle
  6637. *
  6638. * @param {Highcharts.CSSObject} style
  6639. * CSS to apply.
  6640. */
  6641. SVGRenderer.prototype.setStyle = function (style) {
  6642. this.boxWrapper.css(this.getStyle(style));
  6643. };
  6644. /**
  6645. * Detect whether the renderer is hidden. This happens when one of the
  6646. * parent elements has `display: none`. Used internally to detect when we
  6647. * needto render preliminarily in another div to get the text bounding boxes
  6648. * right.
  6649. *
  6650. * @function Highcharts.SVGRenderer#isHidden
  6651. *
  6652. * @return {boolean}
  6653. * True if it is hidden.
  6654. */
  6655. SVGRenderer.prototype.isHidden = function () {
  6656. return !this.boxWrapper.getBBox().width;
  6657. };
  6658. /**
  6659. * Destroys the renderer and its allocated members.
  6660. *
  6661. * @function Highcharts.SVGRenderer#destroy
  6662. *
  6663. * @return {null}
  6664. */
  6665. SVGRenderer.prototype.destroy = function () {
  6666. var renderer = this,
  6667. rendererDefs = renderer.defs;
  6668. renderer.box = null;
  6669. renderer.boxWrapper = renderer.boxWrapper.destroy();
  6670. // Call destroy on all gradient elements
  6671. destroyObjectProperties(renderer.gradients || {});
  6672. renderer.gradients = null;
  6673. // Defs are null in VMLRenderer
  6674. // Otherwise, destroy them here.
  6675. if (rendererDefs) {
  6676. renderer.defs = rendererDefs.destroy();
  6677. }
  6678. // Remove sub pixel fix handler (#982)
  6679. if (renderer.unSubPixelFix) {
  6680. renderer.unSubPixelFix();
  6681. }
  6682. renderer.alignedObjects = null;
  6683. return null;
  6684. };
  6685. /**
  6686. * Create a wrapper for an SVG element. Serves as a factory for
  6687. * {@link SVGElement}, but this function is itself mostly called from
  6688. * primitive factories like {@link SVGRenderer#path}, {@link
  6689. * SVGRenderer#rect} or {@link SVGRenderer#text}.
  6690. *
  6691. * @function Highcharts.SVGRenderer#createElement
  6692. *
  6693. * @param {string} nodeName
  6694. * The node name, for example `rect`, `g` etc.
  6695. *
  6696. * @return {Highcharts.SVGElement}
  6697. * The generated SVGElement.
  6698. */
  6699. SVGRenderer.prototype.createElement = function (nodeName) {
  6700. var wrapper = new this.Element();
  6701. wrapper.init(this, nodeName);
  6702. return wrapper;
  6703. };
  6704. /**
  6705. * Get converted radial gradient attributes according to the radial
  6706. * reference. Used internally from the {@link SVGElement#colorGradient}
  6707. * function.
  6708. *
  6709. * @private
  6710. * @function Highcharts.SVGRenderer#getRadialAttr
  6711. */
  6712. SVGRenderer.prototype.getRadialAttr = function (radialReference, gradAttr) {
  6713. return {
  6714. cx: (radialReference[0] - radialReference[2] / 2) +
  6715. gradAttr.cx * radialReference[2],
  6716. cy: (radialReference[1] - radialReference[2] / 2) +
  6717. gradAttr.cy * radialReference[2],
  6718. r: gradAttr.r * radialReference[2]
  6719. };
  6720. };
  6721. /**
  6722. * Truncate the text node contents to a given length. Used when the css
  6723. * width is set. If the `textOverflow` is `ellipsis`, the text is truncated
  6724. * character by character to the given length. If not, the text is
  6725. * word-wrapped line by line.
  6726. *
  6727. * @private
  6728. * @function Highcharts.SVGRenderer#truncate
  6729. *
  6730. * @return {boolean}
  6731. * True if tspan is too long.
  6732. */
  6733. SVGRenderer.prototype.truncate = function (wrapper, tspan, text, words, startAt, width, getString) {
  6734. var renderer = this,
  6735. rotation = wrapper.rotation,
  6736. str,
  6737. // Word wrap can not be truncated to shorter than one word, ellipsis
  6738. // text can be completely blank.
  6739. minIndex = words ? 1 : 0,
  6740. maxIndex = (text || words).length,
  6741. currentIndex = maxIndex,
  6742. // Cache the lengths to avoid checking the same twice
  6743. lengths = [],
  6744. updateTSpan = function (s) {
  6745. if (tspan.firstChild) {
  6746. tspan.removeChild(tspan.firstChild);
  6747. }
  6748. if (s) {
  6749. tspan.appendChild(doc.createTextNode(s));
  6750. }
  6751. }, getSubStringLength = function (charEnd, concatenatedEnd) {
  6752. // charEnd is useed when finding the character-by-character
  6753. // break for ellipsis, concatenatedEnd is used for word-by-word
  6754. // break for word wrapping.
  6755. var end = concatenatedEnd || charEnd;
  6756. if (typeof lengths[end] === 'undefined') {
  6757. // Modern browsers
  6758. if (tspan.getSubStringLength) {
  6759. // Fails with DOM exception on unit-tests/legend/members
  6760. // of unknown reason. Desired width is 0, text content
  6761. // is "5" and end is 1.
  6762. try {
  6763. lengths[end] = startAt +
  6764. tspan.getSubStringLength(0, words ? end + 1 : end);
  6765. }
  6766. catch (e) {
  6767. '';
  6768. }
  6769. // Legacy
  6770. }
  6771. else if (renderer.getSpanWidth) { // #9058 jsdom
  6772. updateTSpan(getString(text || words, charEnd));
  6773. lengths[end] = startAt +
  6774. renderer.getSpanWidth(wrapper, tspan);
  6775. }
  6776. }
  6777. return lengths[end];
  6778. }, actualWidth, truncated;
  6779. wrapper.rotation = 0; // discard rotation when computing box
  6780. actualWidth = getSubStringLength(tspan.textContent.length);
  6781. truncated = startAt + actualWidth > width;
  6782. if (truncated) {
  6783. // Do a binary search for the index where to truncate the text
  6784. while (minIndex <= maxIndex) {
  6785. currentIndex = Math.ceil((minIndex + maxIndex) / 2);
  6786. // When checking words for word-wrap, we need to build the
  6787. // string and measure the subStringLength at the concatenated
  6788. // word length.
  6789. if (words) {
  6790. str = getString(words, currentIndex);
  6791. }
  6792. actualWidth = getSubStringLength(currentIndex, str && str.length - 1);
  6793. if (minIndex === maxIndex) {
  6794. // Complete
  6795. minIndex = maxIndex + 1;
  6796. }
  6797. else if (actualWidth > width) {
  6798. // Too large. Set max index to current.
  6799. maxIndex = currentIndex - 1;
  6800. }
  6801. else {
  6802. // Within width. Set min index to current.
  6803. minIndex = currentIndex;
  6804. }
  6805. }
  6806. // If max index was 0 it means the shortest possible text was also
  6807. // too large. For ellipsis that means only the ellipsis, while for
  6808. // word wrap it means the whole first word.
  6809. if (maxIndex === 0) {
  6810. // Remove ellipsis
  6811. updateTSpan('');
  6812. // If the new text length is one less than the original, we don't
  6813. // need the ellipsis
  6814. }
  6815. else if (!(text && maxIndex === text.length - 1)) {
  6816. updateTSpan(str || getString(text || words, currentIndex));
  6817. }
  6818. }
  6819. // When doing line wrapping, prepare for the next line by removing the
  6820. // items from this line.
  6821. if (words) {
  6822. words.splice(0, currentIndex);
  6823. }
  6824. wrapper.actualWidth = actualWidth;
  6825. wrapper.rotation = rotation; // Apply rotation again.
  6826. return truncated;
  6827. };
  6828. /**
  6829. * Parse a simple HTML string into SVG tspans. Called internally when text
  6830. * is set on an SVGElement. The function supports a subset of HTML tags, CSS
  6831. * text features like `width`, `text-overflow`, `white-space`, and also
  6832. * attributes like `href` and `style`.
  6833. *
  6834. * @private
  6835. * @function Highcharts.SVGRenderer#buildText
  6836. *
  6837. * @param {Highcharts.SVGElement} wrapper
  6838. * The parent SVGElement.
  6839. */
  6840. SVGRenderer.prototype.buildText = function (wrapper) {
  6841. var textNode = wrapper.element, renderer = this, forExport = renderer.forExport, textStr = pick(wrapper.textStr, '').toString(), hasMarkup = textStr.indexOf('<') !== -1, lines, childNodes = textNode.childNodes, truncated, parentX = attr(textNode, 'x'), textStyles = wrapper.styles, width = wrapper.textWidth, textLineHeight = textStyles && textStyles.lineHeight, textOutline = textStyles && textStyles.textOutline, ellipsis = textStyles && textStyles.textOverflow === 'ellipsis', noWrap = textStyles && textStyles.whiteSpace === 'nowrap', fontSize = textStyles && textStyles.fontSize, textCache, isSubsequentLine, i = childNodes.length, tempParent = width && !wrapper.added && this.box, getLineHeight = function (tspan) {
  6842. var fontSizeStyle;
  6843. if (!renderer.styledMode) {
  6844. fontSizeStyle =
  6845. /(px|em)$/.test(tspan && tspan.style.fontSize) ?
  6846. tspan.style.fontSize :
  6847. (fontSize || renderer.style.fontSize || 12);
  6848. }
  6849. return textLineHeight ?
  6850. pInt(textLineHeight) :
  6851. renderer.fontMetrics(fontSizeStyle,
  6852. // Get the computed size from parent if not explicit
  6853. (tspan.getAttribute('style') ? tspan : textNode)).h;
  6854. }, unescapeEntities = function (inputStr, except) {
  6855. objectEach(renderer.escapes, function (value, key) {
  6856. if (!except || except.indexOf(value) === -1) {
  6857. inputStr = inputStr.toString().replace(new RegExp(value, 'g'), key);
  6858. }
  6859. });
  6860. return inputStr;
  6861. }, parseAttribute = function (s, attr) {
  6862. var start,
  6863. delimiter;
  6864. start = s.indexOf('<');
  6865. s = s.substring(start, s.indexOf('>') - start);
  6866. start = s.indexOf(attr + '=');
  6867. if (start !== -1) {
  6868. start = start + attr.length + 1;
  6869. delimiter = s.charAt(start);
  6870. if (delimiter === '"' || delimiter === "'") { // eslint-disable-line quotes
  6871. s = s.substring(start + 1);
  6872. return s.substring(0, s.indexOf(delimiter));
  6873. }
  6874. }
  6875. };
  6876. var regexMatchBreaks = /<br.*?>/g;
  6877. // The buildText code is quite heavy, so if we're not changing something
  6878. // that affects the text, skip it (#6113).
  6879. textCache = [
  6880. textStr,
  6881. ellipsis,
  6882. noWrap,
  6883. textLineHeight,
  6884. textOutline,
  6885. fontSize,
  6886. width
  6887. ].join(',');
  6888. if (textCache === wrapper.textCache) {
  6889. return;
  6890. }
  6891. wrapper.textCache = textCache;
  6892. // Remove old text
  6893. while (i--) {
  6894. textNode.removeChild(childNodes[i]);
  6895. }
  6896. // Skip tspans, add text directly to text node. The forceTSpan is a hook
  6897. // used in text outline hack.
  6898. if (!hasMarkup &&
  6899. !textOutline &&
  6900. !ellipsis &&
  6901. !width &&
  6902. (textStr.indexOf(' ') === -1 ||
  6903. (noWrap && !regexMatchBreaks.test(textStr)))) {
  6904. textNode.appendChild(doc.createTextNode(unescapeEntities(textStr)));
  6905. // Complex strings, add more logic
  6906. }
  6907. else {
  6908. if (tempParent) {
  6909. // attach it to the DOM to read offset width
  6910. tempParent.appendChild(textNode);
  6911. }
  6912. if (hasMarkup) {
  6913. lines = renderer.styledMode ? (textStr
  6914. .replace(/<(b|strong)>/g, '<span class="highcharts-strong">')
  6915. .replace(/<(i|em)>/g, '<span class="highcharts-emphasized">')) : (textStr
  6916. .replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
  6917. .replace(/<(i|em)>/g, '<span style="font-style:italic">'));
  6918. lines = lines
  6919. .replace(/<a/g, '<span')
  6920. .replace(/<\/(b|strong|i|em|a)>/g, '</span>')
  6921. .split(regexMatchBreaks);
  6922. }
  6923. else {
  6924. lines = [textStr];
  6925. }
  6926. // Trim empty lines (#5261)
  6927. lines = lines.filter(function (line) {
  6928. return line !== '';
  6929. });
  6930. // build the lines
  6931. lines.forEach(function (line, lineNo) {
  6932. var spans,
  6933. spanNo = 0,
  6934. lineLength = 0;
  6935. line = line
  6936. // Trim to prevent useless/costly process on the spaces
  6937. // (#5258)
  6938. .replace(/^\s+|\s+$/g, '')
  6939. .replace(/<span/g, '|||<span')
  6940. .replace(/<\/span>/g, '</span>|||');
  6941. spans = line.split('|||');
  6942. spans.forEach(function buildTextSpans(span) {
  6943. if (span !== '' || spans.length === 1) {
  6944. var attributes = {},
  6945. tspan = doc.createElementNS(renderer.SVG_NS, 'tspan'),
  6946. a,
  6947. classAttribute,
  6948. styleAttribute, // #390
  6949. hrefAttribute;
  6950. classAttribute = parseAttribute(span, 'class');
  6951. if (classAttribute) {
  6952. attr(tspan, 'class', classAttribute);
  6953. }
  6954. styleAttribute = parseAttribute(span, 'style');
  6955. if (styleAttribute) {
  6956. styleAttribute = styleAttribute.replace(/(;| |^)color([ :])/, '$1fill$2');
  6957. attr(tspan, 'style', styleAttribute);
  6958. }
  6959. // For anchors, wrap the tspan in an <a> tag and apply
  6960. // the href attribute as is (#13559). Not for export
  6961. // (#1529)
  6962. hrefAttribute = parseAttribute(span, 'href');
  6963. if (hrefAttribute && !forExport) {
  6964. if (
  6965. // Stop JavaScript links, vulnerable to XSS
  6966. hrefAttribute.split(':')[0].toLowerCase()
  6967. .indexOf('javascript') === -1) {
  6968. a = doc.createElementNS(renderer.SVG_NS, 'a');
  6969. attr(a, 'href', hrefAttribute);
  6970. attr(tspan, 'class', 'highcharts-anchor');
  6971. a.appendChild(tspan);
  6972. if (!renderer.styledMode) {
  6973. css(tspan, { cursor: 'pointer' });
  6974. }
  6975. }
  6976. }
  6977. // Strip away unsupported HTML tags (#7126)
  6978. span = unescapeEntities(span.replace(/<[a-zA-Z\/](.|\n)*?>/g, '') || ' ');
  6979. // Nested tags aren't supported, and cause crash in
  6980. // Safari (#1596)
  6981. if (span !== ' ') {
  6982. // add the text node
  6983. tspan.appendChild(doc.createTextNode(span));
  6984. // First span in a line, align it to the left
  6985. if (!spanNo) {
  6986. if (lineNo && parentX !== null) {
  6987. attributes.x = parentX;
  6988. }
  6989. }
  6990. else {
  6991. attributes.dx = 0; // #16
  6992. }
  6993. // add attributes
  6994. attr(tspan, attributes);
  6995. // Append it
  6996. textNode.appendChild(a || tspan);
  6997. // first span on subsequent line, add the line
  6998. // height
  6999. if (!spanNo && isSubsequentLine) {
  7000. // allow getting the right offset height in
  7001. // exporting in IE
  7002. if (!svg && forExport) {
  7003. css(tspan, { display: 'block' });
  7004. }
  7005. // Set the line height based on the font size of
  7006. // either the text element or the tspan element
  7007. attr(tspan, 'dy', getLineHeight(tspan));
  7008. }
  7009. // Check width and apply soft breaks or ellipsis
  7010. if (width) {
  7011. var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
  7012. hasWhiteSpace = !noWrap && (spans.length > 1 ||
  7013. lineNo ||
  7014. words.length > 1),
  7015. wrapLineNo = 0,
  7016. dy = getLineHeight(tspan);
  7017. if (ellipsis) {
  7018. truncated = renderer.truncate(wrapper, tspan, span, void 0, 0,
  7019. // Target width
  7020. Math.max(0,
  7021. // Substract the font face to make
  7022. // room for the ellipsis itself
  7023. width - parseInt(fontSize || 12, 10)),
  7024. // Build the text to test for
  7025. function (text, currentIndex) {
  7026. return text.substring(0, currentIndex) + '\u2026';
  7027. });
  7028. }
  7029. else if (hasWhiteSpace) {
  7030. while (words.length) {
  7031. // For subsequent lines, create tspans
  7032. // with the same style attributes as the
  7033. // parent text node.
  7034. if (words.length &&
  7035. !noWrap &&
  7036. wrapLineNo > 0) {
  7037. tspan = doc.createElementNS(SVG_NS, 'tspan');
  7038. attr(tspan, {
  7039. dy: dy,
  7040. x: parentX
  7041. });
  7042. if (styleAttribute) { // #390
  7043. attr(tspan, 'style', styleAttribute);
  7044. }
  7045. // Start by appending the full
  7046. // remaining text
  7047. tspan.appendChild(doc.createTextNode(words.join(' ')
  7048. .replace(/- /g, '-')));
  7049. textNode.appendChild(tspan);
  7050. }
  7051. // For each line, truncate the remaining
  7052. // words into the line length.
  7053. renderer.truncate(wrapper, tspan, null, words, wrapLineNo === 0 ? lineLength : 0, width,
  7054. // Build the text to test for
  7055. function (text, currentIndex) {
  7056. return words
  7057. .slice(0, currentIndex)
  7058. .join(' ')
  7059. .replace(/- /g, '-');
  7060. });
  7061. lineLength = wrapper.actualWidth;
  7062. wrapLineNo++;
  7063. }
  7064. }
  7065. }
  7066. spanNo++;
  7067. }
  7068. }
  7069. });
  7070. // To avoid beginning lines that doesn't add to the textNode
  7071. // (#6144)
  7072. isSubsequentLine = (isSubsequentLine ||
  7073. textNode.childNodes.length);
  7074. });
  7075. if (ellipsis && truncated) {
  7076. wrapper.attr('title', unescapeEntities(wrapper.textStr || '', ['&lt;', '&gt;']) // #7179
  7077. );
  7078. }
  7079. if (tempParent) {
  7080. tempParent.removeChild(textNode);
  7081. }
  7082. // Apply the text outline
  7083. if (isString(textOutline) && wrapper.applyTextOutline) {
  7084. wrapper.applyTextOutline(textOutline);
  7085. }
  7086. }
  7087. };
  7088. /**
  7089. * Returns white for dark colors and black for bright colors.
  7090. *
  7091. * @function Highcharts.SVGRenderer#getContrast
  7092. *
  7093. * @param {Highcharts.ColorString} rgba
  7094. * The color to get the contrast for.
  7095. *
  7096. * @return {Highcharts.ColorString}
  7097. * The contrast color, either `#000000` or `#FFFFFF`.
  7098. */
  7099. SVGRenderer.prototype.getContrast = function (rgba) {
  7100. rgba = Color.parse(rgba).rgba;
  7101. // The threshold may be discussed. Here's a proposal for adding
  7102. // different weight to the color channels (#6216)
  7103. rgba[0] *= 1; // red
  7104. rgba[1] *= 1.2; // green
  7105. rgba[2] *= 0.5; // blue
  7106. return rgba[0] + rgba[1] + rgba[2] >
  7107. 1.8 * 255 ?
  7108. '#000000' :
  7109. '#FFFFFF';
  7110. };
  7111. /**
  7112. * Create a button with preset states.
  7113. *
  7114. * @function Highcharts.SVGRenderer#button
  7115. *
  7116. * @param {string} text
  7117. * The text or HTML to draw.
  7118. *
  7119. * @param {number} x
  7120. * The x position of the button's left side.
  7121. *
  7122. * @param {number} y
  7123. * The y position of the button's top side.
  7124. *
  7125. * @param {Highcharts.EventCallbackFunction<Highcharts.SVGElement>} callback
  7126. * The function to execute on button click or touch.
  7127. *
  7128. * @param {Highcharts.SVGAttributes} [normalState]
  7129. * SVG attributes for the normal state.
  7130. *
  7131. * @param {Highcharts.SVGAttributes} [hoverState]
  7132. * SVG attributes for the hover state.
  7133. *
  7134. * @param {Highcharts.SVGAttributes} [pressedState]
  7135. * SVG attributes for the pressed state.
  7136. *
  7137. * @param {Highcharts.SVGAttributes} [disabledState]
  7138. * SVG attributes for the disabled state.
  7139. *
  7140. * @param {Highcharts.SymbolKeyValue} [shape=rect]
  7141. * The shape type.
  7142. *
  7143. * @param {boolean} [useHTML=false]
  7144. * Wether to use HTML to render the label.
  7145. *
  7146. * @return {Highcharts.SVGElement}
  7147. * The button element.
  7148. */
  7149. SVGRenderer.prototype.button = function (text, x, y, callback, normalState, hoverState, pressedState, disabledState, shape, useHTML) {
  7150. var label = this.label(text,
  7151. x,
  7152. y,
  7153. shape,
  7154. void 0,
  7155. void 0,
  7156. useHTML,
  7157. void 0, 'button'),
  7158. curState = 0,
  7159. styledMode = this.styledMode,
  7160. // Make a copy of normalState (#13798)
  7161. // (reference to options.rangeSelector.buttonTheme)
  7162. normalState = normalState ? merge(normalState) : normalState,
  7163. userNormalStyle = normalState && normalState.style || {};
  7164. // Remove stylable attributes
  7165. if (normalState && normalState.style) {
  7166. delete normalState.style;
  7167. }
  7168. // Default, non-stylable attributes
  7169. label.attr(merge({ padding: 8, r: 2 }, normalState));
  7170. if (!styledMode) {
  7171. // Presentational
  7172. var normalStyle,
  7173. hoverStyle,
  7174. pressedStyle,
  7175. disabledStyle;
  7176. // Normal state - prepare the attributes
  7177. normalState = merge({
  7178. fill: '#f7f7f7',
  7179. stroke: '#cccccc',
  7180. 'stroke-width': 1,
  7181. style: {
  7182. color: '#333333',
  7183. cursor: 'pointer',
  7184. fontWeight: 'normal'
  7185. }
  7186. }, {
  7187. style: userNormalStyle
  7188. }, normalState);
  7189. normalStyle = normalState.style;
  7190. delete normalState.style;
  7191. // Hover state
  7192. hoverState = merge(normalState, {
  7193. fill: '#e6e6e6'
  7194. }, hoverState);
  7195. hoverStyle = hoverState.style;
  7196. delete hoverState.style;
  7197. // Pressed state
  7198. pressedState = merge(normalState, {
  7199. fill: '#e6ebf5',
  7200. style: {
  7201. color: '#000000',
  7202. fontWeight: 'bold'
  7203. }
  7204. }, pressedState);
  7205. pressedStyle = pressedState.style;
  7206. delete pressedState.style;
  7207. // Disabled state
  7208. disabledState = merge(normalState, {
  7209. style: {
  7210. color: '#cccccc'
  7211. }
  7212. }, disabledState);
  7213. disabledStyle = disabledState.style;
  7214. delete disabledState.style;
  7215. }
  7216. // Add the events. IE9 and IE10 need mouseover and mouseout to funciton
  7217. // (#667).
  7218. addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
  7219. if (curState !== 3) {
  7220. label.setState(1);
  7221. }
  7222. });
  7223. addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
  7224. if (curState !== 3) {
  7225. label.setState(curState);
  7226. }
  7227. });
  7228. label.setState = function (state) {
  7229. // Hover state is temporary, don't record it
  7230. if (state !== 1) {
  7231. label.state = curState = state;
  7232. }
  7233. // Update visuals
  7234. label
  7235. .removeClass(/highcharts-button-(normal|hover|pressed|disabled)/)
  7236. .addClass('highcharts-button-' +
  7237. ['normal', 'hover', 'pressed', 'disabled'][state || 0]);
  7238. if (!styledMode) {
  7239. label
  7240. .attr([
  7241. normalState,
  7242. hoverState,
  7243. pressedState,
  7244. disabledState
  7245. ][state || 0])
  7246. .css([
  7247. normalStyle,
  7248. hoverStyle,
  7249. pressedStyle,
  7250. disabledStyle
  7251. ][state || 0]);
  7252. }
  7253. };
  7254. // Presentational attributes
  7255. if (!styledMode) {
  7256. label
  7257. .attr(normalState)
  7258. .css(extend({ cursor: 'default' }, normalStyle));
  7259. }
  7260. return label
  7261. .on('click', function (e) {
  7262. if (curState !== 3) {
  7263. callback.call(label, e);
  7264. }
  7265. });
  7266. };
  7267. /**
  7268. * Make a straight line crisper by not spilling out to neighbour pixels.
  7269. *
  7270. * @function Highcharts.SVGRenderer#crispLine
  7271. *
  7272. * @param {Highcharts.SVGPathArray} points
  7273. * The original points on the format `[['M', 0, 0], ['L', 100, 0]]`.
  7274. *
  7275. * @param {number} width
  7276. * The width of the line.
  7277. *
  7278. * @param {string} roundingFunction
  7279. * The rounding function name on the `Math` object, can be one of
  7280. * `round`, `floor` or `ceil`.
  7281. *
  7282. * @return {Highcharts.SVGPathArray}
  7283. * The original points array, but modified to render crisply.
  7284. */
  7285. SVGRenderer.prototype.crispLine = function (points, width, roundingFunction) {
  7286. if (roundingFunction === void 0) { roundingFunction = 'round'; }
  7287. var start = points[0];
  7288. var end = points[1];
  7289. // Normalize to a crisp line
  7290. if (start[1] === end[1]) {
  7291. // Substract due to #1129. Now bottom and left axis gridlines behave
  7292. // the same.
  7293. start[1] = end[1] =
  7294. Math[roundingFunction](start[1]) - (width % 2 / 2);
  7295. }
  7296. if (start[2] === end[2]) {
  7297. start[2] = end[2] =
  7298. Math[roundingFunction](start[2]) + (width % 2 / 2);
  7299. }
  7300. return points;
  7301. };
  7302. /**
  7303. * Draw a path, wraps the SVG `path` element.
  7304. *
  7305. * @sample highcharts/members/renderer-path-on-chart/
  7306. * Draw a path in a chart
  7307. * @sample highcharts/members/renderer-path/
  7308. * Draw a path independent from a chart
  7309. *
  7310. * @example
  7311. * var path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
  7312. * .attr({ stroke: '#ff00ff' })
  7313. * .add();
  7314. *
  7315. * @function Highcharts.SVGRenderer#path
  7316. *
  7317. * @param {Highcharts.SVGPathArray} [path]
  7318. * An SVG path definition in array form.
  7319. *
  7320. * @return {Highcharts.SVGElement}
  7321. * The generated wrapper element.
  7322. *
  7323. */ /**
  7324. * Draw a path, wraps the SVG `path` element.
  7325. *
  7326. * @function Highcharts.SVGRenderer#path
  7327. *
  7328. * @param {Highcharts.SVGAttributes} [attribs]
  7329. * The initial attributes.
  7330. *
  7331. * @return {Highcharts.SVGElement}
  7332. * The generated wrapper element.
  7333. */
  7334. SVGRenderer.prototype.path = function (path) {
  7335. var attribs = (this.styledMode ? {} : {
  7336. fill: 'none'
  7337. });
  7338. if (isArray(path)) {
  7339. attribs.d = path;
  7340. }
  7341. else if (isObject(path)) { // attributes
  7342. extend(attribs, path);
  7343. }
  7344. return this.createElement('path').attr(attribs);
  7345. };
  7346. /**
  7347. * Draw a circle, wraps the SVG `circle` element.
  7348. *
  7349. * @sample highcharts/members/renderer-circle/
  7350. * Drawing a circle
  7351. *
  7352. * @function Highcharts.SVGRenderer#circle
  7353. *
  7354. * @param {number} [x]
  7355. * The center x position.
  7356. *
  7357. * @param {number} [y]
  7358. * The center y position.
  7359. *
  7360. * @param {number} [r]
  7361. * The radius.
  7362. *
  7363. * @return {Highcharts.SVGElement}
  7364. * The generated wrapper element.
  7365. */ /**
  7366. * Draw a circle, wraps the SVG `circle` element.
  7367. *
  7368. * @function Highcharts.SVGRenderer#circle
  7369. *
  7370. * @param {Highcharts.SVGAttributes} [attribs]
  7371. * The initial attributes.
  7372. *
  7373. * @return {Highcharts.SVGElement}
  7374. * The generated wrapper element.
  7375. */
  7376. SVGRenderer.prototype.circle = function (x, y, r) {
  7377. var attribs = (isObject(x) ?
  7378. x :
  7379. typeof x === 'undefined' ? {} : { x: x, y: y, r: r }), wrapper = this.createElement('circle');
  7380. // Setting x or y translates to cx and cy
  7381. wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
  7382. element.setAttribute('c' + key, value);
  7383. };
  7384. return wrapper.attr(attribs);
  7385. };
  7386. /**
  7387. * Draw and return an arc.
  7388. *
  7389. * @sample highcharts/members/renderer-arc/
  7390. * Drawing an arc
  7391. *
  7392. * @function Highcharts.SVGRenderer#arc
  7393. *
  7394. * @param {number} [x=0]
  7395. * Center X position.
  7396. *
  7397. * @param {number} [y=0]
  7398. * Center Y position.
  7399. *
  7400. * @param {number} [r=0]
  7401. * The outer radius' of the arc.
  7402. *
  7403. * @param {number} [innerR=0]
  7404. * Inner radius like used in donut charts.
  7405. *
  7406. * @param {number} [start=0]
  7407. * The starting angle of the arc in radians, where 0 is to the right and
  7408. * `-Math.PI/2` is up.
  7409. *
  7410. * @param {number} [end=0]
  7411. * The ending angle of the arc in radians, where 0 is to the right and
  7412. * `-Math.PI/2` is up.
  7413. *
  7414. * @return {Highcharts.SVGElement}
  7415. * The generated wrapper element.
  7416. */ /**
  7417. * Draw and return an arc. Overloaded function that takes arguments object.
  7418. *
  7419. * @function Highcharts.SVGRenderer#arc
  7420. *
  7421. * @param {Highcharts.SVGAttributes} attribs
  7422. * Initial SVG attributes.
  7423. *
  7424. * @return {Highcharts.SVGElement}
  7425. * The generated wrapper element.
  7426. */
  7427. SVGRenderer.prototype.arc = function (x, y, r, innerR, start, end) {
  7428. var arc,
  7429. options;
  7430. if (isObject(x)) {
  7431. options = x;
  7432. y = options.y;
  7433. r = options.r;
  7434. innerR = options.innerR;
  7435. start = options.start;
  7436. end = options.end;
  7437. x = options.x;
  7438. }
  7439. else {
  7440. options = {
  7441. innerR: innerR,
  7442. start: start,
  7443. end: end
  7444. };
  7445. }
  7446. // Arcs are defined as symbols for the ability to set
  7447. // attributes in attr and animate
  7448. arc = this.symbol('arc', x, y, r, r, options);
  7449. arc.r = r; // #959
  7450. return arc;
  7451. };
  7452. /**
  7453. * Draw and return a rectangle.
  7454. *
  7455. * @function Highcharts.SVGRenderer#rect
  7456. *
  7457. * @param {number} [x]
  7458. * Left position.
  7459. *
  7460. * @param {number} [y]
  7461. * Top position.
  7462. *
  7463. * @param {number} [width]
  7464. * Width of the rectangle.
  7465. *
  7466. * @param {number} [height]
  7467. * Height of the rectangle.
  7468. *
  7469. * @param {number} [r]
  7470. * Border corner radius.
  7471. *
  7472. * @param {number} [strokeWidth]
  7473. * A stroke width can be supplied to allow crisp drawing.
  7474. *
  7475. * @return {Highcharts.SVGElement}
  7476. * The generated wrapper element.
  7477. */ /**
  7478. * Draw and return a rectangle.
  7479. *
  7480. * @sample highcharts/members/renderer-rect-on-chart/
  7481. * Draw a rectangle in a chart
  7482. * @sample highcharts/members/renderer-rect/
  7483. * Draw a rectangle independent from a chart
  7484. *
  7485. * @function Highcharts.SVGRenderer#rect
  7486. *
  7487. * @param {Highcharts.SVGAttributes} [attributes]
  7488. * General SVG attributes for the rectangle.
  7489. *
  7490. * @return {Highcharts.SVGElement}
  7491. * The generated wrapper element.
  7492. */
  7493. SVGRenderer.prototype.rect = function (x, y, width, height, r, strokeWidth) {
  7494. r = isObject(x) ? x.r : r;
  7495. var wrapper = this.createElement('rect'),
  7496. attribs = isObject(x) ?
  7497. x :
  7498. typeof x === 'undefined' ?
  7499. {} :
  7500. {
  7501. x: x,
  7502. y: y,
  7503. width: Math.max(width, 0),
  7504. height: Math.max(height, 0)
  7505. };
  7506. if (!this.styledMode) {
  7507. if (typeof strokeWidth !== 'undefined') {
  7508. attribs.strokeWidth = strokeWidth;
  7509. attribs = wrapper.crisp(attribs);
  7510. }
  7511. attribs.fill = 'none';
  7512. }
  7513. if (r) {
  7514. attribs.r = r;
  7515. }
  7516. wrapper.rSetter = function (value, key, element) {
  7517. wrapper.r = value;
  7518. attr(element, {
  7519. rx: value,
  7520. ry: value
  7521. });
  7522. };
  7523. wrapper.rGetter = function () {
  7524. return wrapper.r;
  7525. };
  7526. return wrapper.attr(attribs);
  7527. };
  7528. /**
  7529. * Resize the {@link SVGRenderer#box} and re-align all aligned child
  7530. * elements.
  7531. *
  7532. * @sample highcharts/members/renderer-g/
  7533. * Show and hide grouped objects
  7534. *
  7535. * @function Highcharts.SVGRenderer#setSize
  7536. *
  7537. * @param {number} width
  7538. * The new pixel width.
  7539. *
  7540. * @param {number} height
  7541. * The new pixel height.
  7542. *
  7543. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animate=true]
  7544. * Whether and how to animate.
  7545. */
  7546. SVGRenderer.prototype.setSize = function (width, height, animate) {
  7547. var renderer = this,
  7548. alignedObjects = renderer.alignedObjects,
  7549. i = alignedObjects.length;
  7550. renderer.width = width;
  7551. renderer.height = height;
  7552. renderer.boxWrapper.animate({
  7553. width: width,
  7554. height: height
  7555. }, {
  7556. step: function () {
  7557. this.attr({
  7558. viewBox: '0 0 ' + this.attr('width') + ' ' +
  7559. this.attr('height')
  7560. });
  7561. },
  7562. duration: pick(animate, true) ? void 0 : 0
  7563. });
  7564. while (i--) {
  7565. alignedObjects[i].align();
  7566. }
  7567. };
  7568. /**
  7569. * Create and return an svg group element. Child
  7570. * {@link Highcharts.SVGElement} objects are added to the group by using the
  7571. * group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
  7572. *
  7573. * @function Highcharts.SVGRenderer#g
  7574. *
  7575. * @param {string} [name]
  7576. * The group will be given a class name of `highcharts-{name}`. This
  7577. * can be used for styling and scripting.
  7578. *
  7579. * @return {Highcharts.SVGElement}
  7580. * The generated wrapper element.
  7581. */
  7582. SVGRenderer.prototype.g = function (name) {
  7583. var elem = this.createElement('g');
  7584. return name ?
  7585. elem.attr({ 'class': 'highcharts-' + name }) :
  7586. elem;
  7587. };
  7588. /**
  7589. * Display an image.
  7590. *
  7591. * @sample highcharts/members/renderer-image-on-chart/
  7592. * Add an image in a chart
  7593. * @sample highcharts/members/renderer-image/
  7594. * Add an image independent of a chart
  7595. *
  7596. * @function Highcharts.SVGRenderer#image
  7597. *
  7598. * @param {string} src
  7599. * The image source.
  7600. *
  7601. * @param {number} [x]
  7602. * The X position.
  7603. *
  7604. * @param {number} [y]
  7605. * The Y position.
  7606. *
  7607. * @param {number} [width]
  7608. * The image width. If omitted, it defaults to the image file width.
  7609. *
  7610. * @param {number} [height]
  7611. * The image height. If omitted it defaults to the image file
  7612. * height.
  7613. *
  7614. * @param {Function} [onload]
  7615. * Event handler for image load.
  7616. *
  7617. * @return {Highcharts.SVGElement}
  7618. * The generated wrapper element.
  7619. */
  7620. SVGRenderer.prototype.image = function (src, x, y, width, height, onload) {
  7621. var attribs = { preserveAspectRatio: 'none' }, elemWrapper, dummy, setSVGImageSource = function (el, src) {
  7622. // Set the href in the xlink namespace
  7623. if (el.setAttributeNS) {
  7624. el.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src);
  7625. }
  7626. else {
  7627. // could be exporting in IE
  7628. // using href throws "not supported" in ie7 and under,
  7629. // requries regex shim to fix later
  7630. el.setAttribute('hc-svg-href', src);
  7631. }
  7632. }, onDummyLoad = function (e) {
  7633. setSVGImageSource(elemWrapper.element, src);
  7634. onload.call(elemWrapper, e);
  7635. };
  7636. // optional properties
  7637. if (arguments.length > 1) {
  7638. extend(attribs, {
  7639. x: x,
  7640. y: y,
  7641. width: width,
  7642. height: height
  7643. });
  7644. }
  7645. elemWrapper = this.createElement('image').attr(attribs);
  7646. // Add load event if supplied
  7647. if (onload) {
  7648. // We have to use a dummy HTML image since IE support for SVG image
  7649. // load events is very buggy. First set a transparent src, wait for
  7650. // dummy to load, and then add the real src to the SVG image.
  7651. setSVGImageSource(elemWrapper.element, 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' /* eslint-disable-line */);
  7652. dummy = new win.Image();
  7653. addEvent(dummy, 'load', onDummyLoad);
  7654. dummy.src = src;
  7655. if (dummy.complete) {
  7656. onDummyLoad({});
  7657. }
  7658. }
  7659. else {
  7660. setSVGImageSource(elemWrapper.element, src);
  7661. }
  7662. return elemWrapper;
  7663. };
  7664. /**
  7665. * Draw a symbol out of pre-defined shape paths from
  7666. * {@link SVGRenderer#symbols}.
  7667. * It is used in Highcharts for point makers, which cake a `symbol` option,
  7668. * and label and button backgrounds like in the tooltip and stock flags.
  7669. *
  7670. * @function Highcharts.SVGRenderer#symbol
  7671. *
  7672. * @param {string} symbol
  7673. * The symbol name.
  7674. *
  7675. * @param {number} [x]
  7676. * The X coordinate for the top left position.
  7677. *
  7678. * @param {number} [y]
  7679. * The Y coordinate for the top left position.
  7680. *
  7681. * @param {number} [width]
  7682. * The pixel width.
  7683. *
  7684. * @param {number} [height]
  7685. * The pixel height.
  7686. *
  7687. * @param {Highcharts.SymbolOptionsObject} [options]
  7688. * Additional options, depending on the actual symbol drawn.
  7689. *
  7690. * @return {Highcharts.SVGElement}
  7691. */
  7692. SVGRenderer.prototype.symbol = function (symbol, x, y, width, height, options) {
  7693. var ren = this,
  7694. obj,
  7695. imageRegex = /^url\((.*?)\)$/,
  7696. isImage = imageRegex.test(symbol),
  7697. sym = (!isImage && (this.symbols[symbol] ? symbol : 'circle')),
  7698. // get the symbol definition function
  7699. symbolFn = (sym && this.symbols[sym]),
  7700. path,
  7701. imageSrc,
  7702. centerImage;
  7703. if (symbolFn) {
  7704. // Check if there's a path defined for this symbol
  7705. if (typeof x === 'number') {
  7706. path = symbolFn.call(this.symbols, Math.round(x || 0), Math.round(y || 0), width || 0, height || 0, options);
  7707. }
  7708. obj = this.path(path);
  7709. if (!ren.styledMode) {
  7710. obj.attr('fill', 'none');
  7711. }
  7712. // expando properties for use in animate and attr
  7713. extend(obj, {
  7714. symbolName: sym,
  7715. x: x,
  7716. y: y,
  7717. width: width,
  7718. height: height
  7719. });
  7720. if (options) {
  7721. extend(obj, options);
  7722. }
  7723. // Image symbols
  7724. }
  7725. else if (isImage) {
  7726. imageSrc = symbol.match(imageRegex)[1];
  7727. // Create the image synchronously, add attribs async
  7728. obj = this.image(imageSrc);
  7729. // The image width is not always the same as the symbol width. The
  7730. // image may be centered within the symbol, as is the case when
  7731. // image shapes are used as label backgrounds, for example in flags.
  7732. obj.imgwidth = pick(symbolSizes[imageSrc] && symbolSizes[imageSrc].width, options && options.width);
  7733. obj.imgheight = pick(symbolSizes[imageSrc] && symbolSizes[imageSrc].height, options && options.height);
  7734. /**
  7735. * Set the size and position
  7736. */
  7737. centerImage = function () {
  7738. obj.attr({
  7739. width: obj.width,
  7740. height: obj.height
  7741. });
  7742. };
  7743. /**
  7744. * Width and height setters that take both the image's physical size
  7745. * and the label size into consideration, and translates the image
  7746. * to center within the label.
  7747. */
  7748. ['width', 'height'].forEach(function (key) {
  7749. obj[key + 'Setter'] = function (value, key) {
  7750. var attribs = {}, imgSize = this['img' + key], trans = key === 'width' ? 'translateX' : 'translateY';
  7751. this[key] = value;
  7752. if (defined(imgSize)) {
  7753. // Scale and center the image within its container.
  7754. // The name `backgroundSize` is taken from the CSS spec,
  7755. // but the value `within` is made up. Other possible
  7756. // values in the spec, `cover` and `contain`, can be
  7757. // implemented if needed.
  7758. if (options &&
  7759. options.backgroundSize === 'within' &&
  7760. this.width &&
  7761. this.height) {
  7762. imgSize = Math.round(imgSize * Math.min(this.width / this.imgwidth, this.height / this.imgheight));
  7763. }
  7764. if (this.element) {
  7765. this.element.setAttribute(key, imgSize);
  7766. }
  7767. if (!this.alignByTranslate) {
  7768. attribs[trans] = ((this[key] || 0) - imgSize) / 2;
  7769. this.attr(attribs);
  7770. }
  7771. }
  7772. };
  7773. });
  7774. if (defined(x)) {
  7775. obj.attr({
  7776. x: x,
  7777. y: y
  7778. });
  7779. }
  7780. obj.isImg = true;
  7781. if (defined(obj.imgwidth) && defined(obj.imgheight)) {
  7782. centerImage();
  7783. }
  7784. else {
  7785. // Initialize image to be 0 size so export will still function
  7786. // if there's no cached sizes.
  7787. obj.attr({ width: 0, height: 0 });
  7788. // Create a dummy JavaScript image to get the width and height.
  7789. createElement('img', {
  7790. onload: function () {
  7791. var chart = charts[ren.chartIndex];
  7792. // Special case for SVGs on IE11, the width is not
  7793. // accessible until the image is part of the DOM
  7794. // (#2854).
  7795. if (this.width === 0) {
  7796. css(this, {
  7797. position: 'absolute',
  7798. top: '-999em'
  7799. });
  7800. doc.body.appendChild(this);
  7801. }
  7802. // Center the image
  7803. symbolSizes[imageSrc] = {
  7804. width: this.width,
  7805. height: this.height
  7806. };
  7807. obj.imgwidth = this.width;
  7808. obj.imgheight = this.height;
  7809. if (obj.element) {
  7810. centerImage();
  7811. }
  7812. // Clean up after #2854 workaround.
  7813. if (this.parentNode) {
  7814. this.parentNode.removeChild(this);
  7815. }
  7816. // Fire the load event when all external images are
  7817. // loaded
  7818. ren.imgCount--;
  7819. if (!ren.imgCount && chart && !chart.hasLoaded) {
  7820. chart.onload();
  7821. }
  7822. },
  7823. src: imageSrc
  7824. });
  7825. this.imgCount++;
  7826. }
  7827. }
  7828. return obj;
  7829. };
  7830. /**
  7831. * Define a clipping rectangle. The clipping rectangle is later applied
  7832. * to {@link SVGElement} objects through the {@link SVGElement#clip}
  7833. * function.
  7834. *
  7835. * @example
  7836. * var circle = renderer.circle(100, 100, 100)
  7837. * .attr({ fill: 'red' })
  7838. * .add();
  7839. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  7840. *
  7841. * // Leave only the lower right quarter visible
  7842. * circle.clip(clipRect);
  7843. *
  7844. * @function Highcharts.SVGRenderer#clipRect
  7845. *
  7846. * @param {number} [x]
  7847. *
  7848. * @param {number} [y]
  7849. *
  7850. * @param {number} [width]
  7851. *
  7852. * @param {number} [height]
  7853. *
  7854. * @return {Highcharts.ClipRectElement}
  7855. * A clipping rectangle.
  7856. */
  7857. SVGRenderer.prototype.clipRect = function (x, y, width, height) {
  7858. var wrapper,
  7859. // Add a hyphen at the end to avoid confusion in testing indexes
  7860. // -1 and -10, -11 etc (#6550)
  7861. id = uniqueKey() + '-', clipPath = this.createElement('clipPath').attr({
  7862. id: id
  7863. }).add(this.defs);
  7864. wrapper = this.rect(x, y, width, height, 0).add(clipPath);
  7865. wrapper.id = id;
  7866. wrapper.clipPath = clipPath;
  7867. wrapper.count = 0;
  7868. return wrapper;
  7869. };
  7870. /**
  7871. * Draw text. The text can contain a subset of HTML, like spans and anchors
  7872. * and some basic text styling of these. For more advanced features like
  7873. * border and background, use {@link Highcharts.SVGRenderer#label} instead.
  7874. * To update the text after render, run `text.attr({ text: 'New text' })`.
  7875. *
  7876. * @sample highcharts/members/renderer-text-on-chart/
  7877. * Annotate the chart freely
  7878. * @sample highcharts/members/renderer-on-chart/
  7879. * Annotate with a border and in response to the data
  7880. * @sample highcharts/members/renderer-text/
  7881. * Formatted text
  7882. *
  7883. * @function Highcharts.SVGRenderer#text
  7884. *
  7885. * @param {string} [str]
  7886. * The text of (subset) HTML to draw.
  7887. *
  7888. * @param {number} [x]
  7889. * The x position of the text's lower left corner.
  7890. *
  7891. * @param {number} [y]
  7892. * The y position of the text's lower left corner.
  7893. *
  7894. * @param {boolean} [useHTML=false]
  7895. * Use HTML to render the text.
  7896. *
  7897. * @return {Highcharts.SVGElement}
  7898. * The text object.
  7899. */
  7900. SVGRenderer.prototype.text = function (str, x, y, useHTML) {
  7901. // declare variables
  7902. var renderer = this,
  7903. wrapper,
  7904. attribs = {};
  7905. if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
  7906. return renderer.html(str, x, y);
  7907. }
  7908. attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
  7909. if (y) {
  7910. attribs.y = Math.round(y);
  7911. }
  7912. if (defined(str)) {
  7913. attribs.text = str;
  7914. }
  7915. wrapper = renderer.createElement('text')
  7916. .attr(attribs);
  7917. if (!useHTML) {
  7918. wrapper.xSetter = function (value, key, element) {
  7919. var tspans = element.getElementsByTagName('tspan'),
  7920. tspan,
  7921. parentVal = element.getAttribute(key),
  7922. i;
  7923. for (i = 0; i < tspans.length; i++) {
  7924. tspan = tspans[i];
  7925. // If the x values are equal, the tspan represents a
  7926. // linebreak
  7927. if (tspan.getAttribute(key) === parentVal) {
  7928. tspan.setAttribute(key, value);
  7929. }
  7930. }
  7931. element.setAttribute(key, value);
  7932. };
  7933. }
  7934. return wrapper;
  7935. };
  7936. /**
  7937. * Utility to return the baseline offset and total line height from the font
  7938. * size.
  7939. *
  7940. * @function Highcharts.SVGRenderer#fontMetrics
  7941. *
  7942. * @param {number|string} [fontSize]
  7943. * The current font size to inspect. If not given, the font size
  7944. * will be found from the DOM element.
  7945. *
  7946. * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement} [elem]
  7947. * The element to inspect for a current font size.
  7948. *
  7949. * @return {Highcharts.FontMetricsObject}
  7950. * The font metrics.
  7951. */
  7952. SVGRenderer.prototype.fontMetrics = function (fontSize, elem) {
  7953. var lineHeight,
  7954. baseline;
  7955. if ((this.styledMode || !/px/.test(fontSize)) &&
  7956. win.getComputedStyle // old IE doesn't support it
  7957. ) {
  7958. fontSize = elem && SVGElement.prototype.getStyle.call(elem, 'font-size');
  7959. }
  7960. else {
  7961. fontSize = fontSize ||
  7962. // When the elem is a DOM element (#5932)
  7963. (elem && elem.style && elem.style.fontSize) ||
  7964. // Fall back on the renderer style default
  7965. (this.style && this.style.fontSize);
  7966. }
  7967. // Handle different units
  7968. if (/px/.test(fontSize)) {
  7969. fontSize = pInt(fontSize);
  7970. }
  7971. else {
  7972. fontSize = 12;
  7973. }
  7974. // Empirical values found by comparing font size and bounding box
  7975. // height. Applies to the default font family.
  7976. // https://jsfiddle.net/highcharts/7xvn7/
  7977. lineHeight = fontSize < 24 ? fontSize + 3 : Math.round(fontSize * 1.2);
  7978. baseline = Math.round(lineHeight * 0.8);
  7979. return {
  7980. h: lineHeight,
  7981. b: baseline,
  7982. f: fontSize
  7983. };
  7984. };
  7985. /**
  7986. * Correct X and Y positioning of a label for rotation (#1764).
  7987. *
  7988. * @private
  7989. * @function Highcharts.SVGRenderer#rotCorr
  7990. *
  7991. * @param {number} baseline
  7992. *
  7993. * @param {number} rotation
  7994. *
  7995. * @param {boolean} [alterY]
  7996. *
  7997. * @param {Highcharts.PositionObject}
  7998. */
  7999. SVGRenderer.prototype.rotCorr = function (baseline, rotation, alterY) {
  8000. var y = baseline;
  8001. if (rotation && alterY) {
  8002. y = Math.max(y * Math.cos(rotation * deg2rad), 4);
  8003. }
  8004. return {
  8005. x: (-baseline / 3) * Math.sin(rotation * deg2rad),
  8006. y: y
  8007. };
  8008. };
  8009. /**
  8010. * Compatibility function to convert the legacy one-dimensional path array
  8011. * into an array of segments.
  8012. *
  8013. * It is used in maps to parse the `path` option, and in SVGRenderer.dSetter
  8014. * to support legacy paths from demos.
  8015. *
  8016. * @private
  8017. * @function Highcharts.SVGRenderer#pathToSegments
  8018. */
  8019. SVGRenderer.prototype.pathToSegments = function (path) {
  8020. var ret = [];
  8021. var segment = [];
  8022. var commandLength = {
  8023. A: 8,
  8024. C: 7,
  8025. H: 2,
  8026. L: 3,
  8027. M: 3,
  8028. Q: 5,
  8029. S: 5,
  8030. T: 3,
  8031. V: 2
  8032. };
  8033. // Short, non-typesafe parsing of the one-dimensional array. It splits
  8034. // the path on any string. This is not type checked against the tuple
  8035. // types, but is shorter, and doesn't require specific checks for any
  8036. // command type in SVG.
  8037. for (var i = 0; i < path.length; i++) {
  8038. // Command skipped, repeat previous or insert L/l for M/m
  8039. if (isString(segment[0]) &&
  8040. isNumber(path[i]) &&
  8041. segment.length === commandLength[(segment[0].toUpperCase())]) {
  8042. path.splice(i, 0, segment[0].replace('M', 'L').replace('m', 'l'));
  8043. }
  8044. // Split on string
  8045. if (typeof path[i] === 'string') {
  8046. if (segment.length) {
  8047. ret.push(segment.slice(0));
  8048. }
  8049. segment.length = 0;
  8050. }
  8051. segment.push(path[i]);
  8052. }
  8053. ret.push(segment.slice(0));
  8054. return ret;
  8055. /*
  8056. // Fully type-safe version where each tuple type is checked. The
  8057. // downside is filesize and a lack of flexibility for unsupported
  8058. // commands
  8059. const ret: SVGPath = [],
  8060. commands = {
  8061. A: 7,
  8062. C: 6,
  8063. H: 1,
  8064. L: 2,
  8065. M: 2,
  8066. Q: 4,
  8067. S: 4,
  8068. T: 2,
  8069. V: 1,
  8070. Z: 0
  8071. };
  8072. let i = 0,
  8073. lastI = 0,
  8074. lastCommand;
  8075. while (i < path.length) {
  8076. const item = path[i];
  8077. let command;
  8078. if (typeof item === 'string') {
  8079. command = item;
  8080. i += 1;
  8081. } else {
  8082. command = lastCommand || 'M';
  8083. }
  8084. // Upper case
  8085. const commandUC = command.toUpperCase();
  8086. if (commandUC in commands) {
  8087. // No numeric parameters
  8088. if (command === 'Z' || command === 'z') {
  8089. ret.push([command]);
  8090. // One numeric parameter
  8091. } else {
  8092. const val0 = path[i];
  8093. if (typeof val0 === 'number') {
  8094. // Horizontal line to
  8095. if (command === 'H' || command === 'h') {
  8096. ret.push([command, val0]);
  8097. i += 1;
  8098. // Vertical line to
  8099. } else if (command === 'V' || command === 'v') {
  8100. ret.push([command, val0]);
  8101. i += 1;
  8102. // Two numeric parameters
  8103. } else {
  8104. const val1 = path[i + 1];
  8105. if (typeof val1 === 'number') {
  8106. // lineTo
  8107. if (command === 'L' || command === 'l') {
  8108. ret.push([command, val0, val1]);
  8109. i += 2;
  8110. // moveTo
  8111. } else if (command === 'M' || command === 'm') {
  8112. ret.push([command, val0, val1]);
  8113. i += 2;
  8114. // Smooth quadratic bezier
  8115. } else if (command === 'T' || command === 't') {
  8116. ret.push([command, val0, val1]);
  8117. i += 2;
  8118. // Four numeric parameters
  8119. } else {
  8120. const val2 = path[i + 2],
  8121. val3 = path[i + 3];
  8122. if (
  8123. typeof val2 === 'number' &&
  8124. typeof val3 === 'number'
  8125. ) {
  8126. // Quadratic bezier to
  8127. if (
  8128. command === 'Q' ||
  8129. command === 'q'
  8130. ) {
  8131. ret.push([
  8132. command,
  8133. val0,
  8134. val1,
  8135. val2,
  8136. val3
  8137. ]);
  8138. i += 4;
  8139. // Smooth cubic bezier to
  8140. } else if (
  8141. command === 'S' ||
  8142. command === 's'
  8143. ) {
  8144. ret.push([
  8145. command,
  8146. val0,
  8147. val1,
  8148. val2,
  8149. val3
  8150. ]);
  8151. i += 4;
  8152. // Six numeric parameters
  8153. } else {
  8154. const val4 = path[i + 4],
  8155. val5 = path[i + 5];
  8156. if (
  8157. typeof val4 === 'number' &&
  8158. typeof val5 === 'number'
  8159. ) {
  8160. // Curve to
  8161. if (
  8162. command === 'C' ||
  8163. command === 'c'
  8164. ) {
  8165. ret.push([
  8166. command,
  8167. val0,
  8168. val1,
  8169. val2,
  8170. val3,
  8171. val4,
  8172. val5
  8173. ]);
  8174. i += 6;
  8175. // Seven numeric parameters
  8176. } else {
  8177. const val6 = path[i + 6];
  8178. // Arc to
  8179. if (
  8180. typeof val6 ===
  8181. 'number' &&
  8182. (
  8183. command === 'A' ||
  8184. command === 'a'
  8185. )
  8186. ) {
  8187. ret.push([
  8188. command,
  8189. val0,
  8190. val1,
  8191. val2,
  8192. val3,
  8193. val4,
  8194. val5,
  8195. val6
  8196. ]);
  8197. i += 7;
  8198. }
  8199. }
  8200. }
  8201. }
  8202. }
  8203. }
  8204. }
  8205. }
  8206. }
  8207. }
  8208. }
  8209. // An unmarked command following a moveTo is a lineTo
  8210. lastCommand = command === 'M' ? 'L' : command;
  8211. if (i === lastI) {
  8212. break;
  8213. }
  8214. lastI = i;
  8215. }
  8216. return ret;
  8217. */
  8218. };
  8219. /**
  8220. * Draw a label, which is an extended text element with support for border
  8221. * and background. Highcharts creates a `g` element with a text and a `path`
  8222. * or `rect` inside, to make it behave somewhat like a HTML div. Border and
  8223. * background are set through `stroke`, `stroke-width` and `fill` attributes
  8224. * using the {@link Highcharts.SVGElement#attr|attr} method. To update the
  8225. * text after render, run `label.attr({ text: 'New text' })`.
  8226. *
  8227. * @sample highcharts/members/renderer-label-on-chart/
  8228. * A label on the chart
  8229. *
  8230. * @function Highcharts.SVGRenderer#label
  8231. *
  8232. * @param {string} str
  8233. * The initial text string or (subset) HTML to render.
  8234. *
  8235. * @param {number} x
  8236. * The x position of the label's left side.
  8237. *
  8238. * @param {number} [y]
  8239. * The y position of the label's top side or baseline, depending on
  8240. * the `baseline` parameter.
  8241. *
  8242. * @param {string} [shape='rect']
  8243. * The shape of the label's border/background, if any. Defaults to
  8244. * `rect`. Other possible values are `callout` or other shapes
  8245. * defined in {@link Highcharts.SVGRenderer#symbols}.
  8246. *
  8247. * @param {number} [anchorX]
  8248. * In case the `shape` has a pointer, like a flag, this is the
  8249. * coordinates it should be pinned to.
  8250. *
  8251. * @param {number} [anchorY]
  8252. * In case the `shape` has a pointer, like a flag, this is the
  8253. * coordinates it should be pinned to.
  8254. *
  8255. * @param {boolean} [useHTML=false]
  8256. * Wether to use HTML to render the label.
  8257. *
  8258. * @param {boolean} [baseline=false]
  8259. * Whether to position the label relative to the text baseline,
  8260. * like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
  8261. * upper border of the rectangle.
  8262. *
  8263. * @param {string} [className]
  8264. * Class name for the group.
  8265. *
  8266. * @return {Highcharts.SVGElement}
  8267. * The generated label.
  8268. */
  8269. SVGRenderer.prototype.label = function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  8270. return new SVGLabel(this, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className);
  8271. };
  8272. return SVGRenderer;
  8273. }());
  8274. /**
  8275. * A pointer to the renderer's associated Element class. The VMLRenderer
  8276. * will have a pointer to VMLElement here.
  8277. *
  8278. * @name Highcharts.SVGRenderer#Element
  8279. * @type {Highcharts.SVGElement}
  8280. */
  8281. SVGRenderer.prototype.Element = SVGElement;
  8282. /**
  8283. * @private
  8284. */
  8285. SVGRenderer.prototype.SVG_NS = SVG_NS;
  8286. /**
  8287. * Dummy function for plugins, called every time the renderer is updated.
  8288. * Prior to Highcharts 5, this was used for the canvg renderer.
  8289. *
  8290. * @deprecated
  8291. * @function Highcharts.SVGRenderer#draw
  8292. */
  8293. SVGRenderer.prototype.draw = noop;
  8294. /**
  8295. * A collection of characters mapped to HTML entities. When `useHTML` on an
  8296. * element is true, these entities will be rendered correctly by HTML. In
  8297. * the SVG pseudo-HTML, they need to be unescaped back to simple characters,
  8298. * so for example `&lt;` will render as `<`.
  8299. *
  8300. * @example
  8301. * // Add support for unescaping quotes
  8302. * Highcharts.SVGRenderer.prototype.escapes['"'] = '&quot;';
  8303. *
  8304. * @name Highcharts.SVGRenderer#escapes
  8305. * @type {Highcharts.Dictionary<string>}
  8306. */
  8307. SVGRenderer.prototype.escapes = {
  8308. '&': '&amp;',
  8309. '<': '&lt;',
  8310. '>': '&gt;',
  8311. "'": '&#39;',
  8312. '"': '&quot;'
  8313. };
  8314. /**
  8315. * An extendable collection of functions for defining symbol paths.
  8316. *
  8317. * @name Highcharts.SVGRenderer#symbols
  8318. * @type {Highcharts.SymbolDictionary}
  8319. */
  8320. SVGRenderer.prototype.symbols = {
  8321. circle: function (x, y, w, h) {
  8322. // Return a full arc
  8323. return this.arc(x + w / 2, y + h / 2, w / 2, h / 2, {
  8324. start: Math.PI * 0.5,
  8325. end: Math.PI * 2.5,
  8326. open: false
  8327. });
  8328. },
  8329. square: function (x, y, w, h) {
  8330. return [
  8331. ['M', x, y],
  8332. ['L', x + w, y],
  8333. ['L', x + w, y + h],
  8334. ['L', x, y + h],
  8335. ['Z']
  8336. ];
  8337. },
  8338. triangle: function (x, y, w, h) {
  8339. return [
  8340. ['M', x + w / 2, y],
  8341. ['L', x + w, y + h],
  8342. ['L', x, y + h],
  8343. ['Z']
  8344. ];
  8345. },
  8346. 'triangle-down': function (x, y, w, h) {
  8347. return [
  8348. ['M', x, y],
  8349. ['L', x + w, y],
  8350. ['L', x + w / 2, y + h],
  8351. ['Z']
  8352. ];
  8353. },
  8354. diamond: function (x, y, w, h) {
  8355. return [
  8356. ['M', x + w / 2, y],
  8357. ['L', x + w, y + h / 2],
  8358. ['L', x + w / 2, y + h],
  8359. ['L', x, y + h / 2],
  8360. ['Z']
  8361. ];
  8362. },
  8363. arc: function (x, y, w, h, options) {
  8364. var arc = [];
  8365. if (options) {
  8366. var start = options.start || 0,
  8367. end = options.end || 0,
  8368. rx = options.r || w,
  8369. ry = options.r || h || w,
  8370. proximity = 0.001,
  8371. fullCircle = Math.abs(end - start - 2 * Math.PI) <
  8372. proximity,
  8373. // Substract a small number to prevent cos and sin of start and
  8374. // end from becoming equal on 360 arcs (related: #1561)
  8375. end = end - proximity,
  8376. innerRadius = options.innerR,
  8377. open = pick(options.open,
  8378. fullCircle),
  8379. cosStart = Math.cos(start),
  8380. sinStart = Math.sin(start),
  8381. cosEnd = Math.cos(end),
  8382. sinEnd = Math.sin(end),
  8383. // Proximity takes care of rounding errors around PI (#6971)
  8384. longArc = pick(options.longArc,
  8385. end - start - Math.PI < proximity ? 0 : 1);
  8386. arc.push([
  8387. 'M',
  8388. x + rx * cosStart,
  8389. y + ry * sinStart
  8390. ], [
  8391. 'A',
  8392. rx,
  8393. ry,
  8394. 0,
  8395. longArc,
  8396. pick(options.clockwise, 1),
  8397. x + rx * cosEnd,
  8398. y + ry * sinEnd
  8399. ]);
  8400. if (defined(innerRadius)) {
  8401. arc.push(open ?
  8402. [
  8403. 'M',
  8404. x + innerRadius * cosEnd,
  8405. y + innerRadius * sinEnd
  8406. ] : [
  8407. 'L',
  8408. x + innerRadius * cosEnd,
  8409. y + innerRadius * sinEnd
  8410. ], [
  8411. 'A',
  8412. innerRadius,
  8413. innerRadius,
  8414. 0,
  8415. longArc,
  8416. // Clockwise - opposite to the outer arc clockwise
  8417. defined(options.clockwise) ? 1 - options.clockwise : 0,
  8418. x + innerRadius * cosStart,
  8419. y + innerRadius * sinStart
  8420. ]);
  8421. }
  8422. if (!open) {
  8423. arc.push(['Z']);
  8424. }
  8425. }
  8426. return arc;
  8427. },
  8428. /**
  8429. * Callout shape used for default tooltips, also used for rounded
  8430. * rectangles in VML
  8431. */
  8432. callout: function (x, y, w, h, options) {
  8433. var arrowLength = 6,
  8434. halfDistance = 6,
  8435. r = Math.min((options && options.r) || 0,
  8436. w,
  8437. h),
  8438. safeDistance = r + halfDistance,
  8439. anchorX = options && options.anchorX || 0,
  8440. anchorY = options && options.anchorY || 0,
  8441. path;
  8442. path = [
  8443. ['M', x + r, y],
  8444. ['L', x + w - r, y],
  8445. ['C', x + w, y, x + w, y, x + w, y + r],
  8446. ['L', x + w, y + h - r],
  8447. ['C', x + w, y + h, x + w, y + h, x + w - r, y + h],
  8448. ['L', x + r, y + h],
  8449. ['C', x, y + h, x, y + h, x, y + h - r],
  8450. ['L', x, y + r],
  8451. ['C', x, y, x, y, x + r, y] // top-left corner
  8452. ];
  8453. // Anchor on right side
  8454. if (anchorX && anchorX > w) {
  8455. // Chevron
  8456. if (anchorY > y + safeDistance &&
  8457. anchorY < y + h - safeDistance) {
  8458. 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]);
  8459. // Simple connector
  8460. }
  8461. else {
  8462. path.splice(3, 1, ['L', x + w, h / 2], ['L', anchorX, anchorY], ['L', x + w, h / 2], ['L', x + w, y + h - r]);
  8463. }
  8464. // Anchor on left side
  8465. }
  8466. else if (anchorX && anchorX < 0) {
  8467. // Chevron
  8468. if (anchorY > y + safeDistance &&
  8469. anchorY < y + h - safeDistance) {
  8470. path.splice(7, 1, ['L', x, anchorY + halfDistance], ['L', x - arrowLength, anchorY], ['L', x, anchorY - halfDistance], ['L', x, y + r]);
  8471. // Simple connector
  8472. }
  8473. else {
  8474. path.splice(7, 1, ['L', x, h / 2], ['L', anchorX, anchorY], ['L', x, h / 2], ['L', x, y + r]);
  8475. }
  8476. }
  8477. else if ( // replace bottom
  8478. anchorY &&
  8479. anchorY > h &&
  8480. anchorX > x + safeDistance &&
  8481. anchorX < x + w - safeDistance) {
  8482. path.splice(5, 1, ['L', anchorX + halfDistance, y + h], ['L', anchorX, y + h + arrowLength], ['L', anchorX - halfDistance, y + h], ['L', x + r, y + h]);
  8483. }
  8484. else if ( // replace top
  8485. anchorY &&
  8486. anchorY < 0 &&
  8487. anchorX > x + safeDistance &&
  8488. anchorX < x + w - safeDistance) {
  8489. path.splice(1, 1, ['L', anchorX - halfDistance, y], ['L', anchorX, y - arrowLength], ['L', anchorX + halfDistance, y], ['L', w - r, y]);
  8490. }
  8491. return path;
  8492. }
  8493. };
  8494. H.SVGRenderer = SVGRenderer;
  8495. H.Renderer = H.SVGRenderer;
  8496. return H.Renderer;
  8497. });
  8498. _registerModule(_modules, 'Core/Renderer/HTML/HTML.js', [_modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (H, SVGElement, SVGRenderer, U) {
  8499. /* *
  8500. *
  8501. * (c) 2010-2020 Torstein Honsi
  8502. *
  8503. * License: www.highcharts.com/license
  8504. *
  8505. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8506. *
  8507. * */
  8508. var attr = U.attr,
  8509. createElement = U.createElement,
  8510. css = U.css,
  8511. defined = U.defined,
  8512. extend = U.extend,
  8513. pick = U.pick,
  8514. pInt = U.pInt;
  8515. var isFirefox = H.isFirefox,
  8516. isMS = H.isMS,
  8517. isWebKit = H.isWebKit,
  8518. win = H.win;
  8519. /* eslint-disable valid-jsdoc */
  8520. // Extend SvgElement for useHTML option.
  8521. extend(SVGElement.prototype, /** @lends SVGElement.prototype */ {
  8522. /**
  8523. * Apply CSS to HTML elements. This is used in text within SVG rendering and
  8524. * by the VML renderer
  8525. *
  8526. * @private
  8527. * @function Highcharts.SVGElement#htmlCss
  8528. *
  8529. * @param {Highcharts.CSSObject} styles
  8530. *
  8531. * @return {Highcharts.SVGElement}
  8532. */
  8533. htmlCss: function (styles) {
  8534. var wrapper = this,
  8535. element = wrapper.element,
  8536. // When setting or unsetting the width style, we need to update
  8537. // transform (#8809)
  8538. isSettingWidth = (element.tagName === 'SPAN' &&
  8539. styles &&
  8540. 'width' in styles),
  8541. textWidth = pick(isSettingWidth && styles.width,
  8542. void 0),
  8543. doTransform;
  8544. if (isSettingWidth) {
  8545. delete styles.width;
  8546. wrapper.textWidth = textWidth;
  8547. doTransform = true;
  8548. }
  8549. if (styles && styles.textOverflow === 'ellipsis') {
  8550. styles.whiteSpace = 'nowrap';
  8551. styles.overflow = 'hidden';
  8552. }
  8553. wrapper.styles = extend(wrapper.styles, styles);
  8554. css(wrapper.element, styles);
  8555. // Now that all styles are applied, to the transform
  8556. if (doTransform) {
  8557. wrapper.htmlUpdateTransform();
  8558. }
  8559. return wrapper;
  8560. },
  8561. /**
  8562. * VML and useHTML method for calculating the bounding box based on offsets.
  8563. *
  8564. * @private
  8565. * @function Highcharts.SVGElement#htmlGetBBox
  8566. *
  8567. * @param {boolean} refresh
  8568. * Whether to force a fresh value from the DOM or to use the cached
  8569. * value.
  8570. *
  8571. * @return {Highcharts.BBoxObject}
  8572. * A hash containing values for x, y, width and height.
  8573. */
  8574. htmlGetBBox: function () {
  8575. var wrapper = this,
  8576. element = wrapper.element;
  8577. return {
  8578. x: element.offsetLeft,
  8579. y: element.offsetTop,
  8580. width: element.offsetWidth,
  8581. height: element.offsetHeight
  8582. };
  8583. },
  8584. /**
  8585. * VML override private method to update elements based on internal
  8586. * properties based on SVG transform.
  8587. *
  8588. * @private
  8589. * @function Highcharts.SVGElement#htmlUpdateTransform
  8590. * @return {void}
  8591. */
  8592. htmlUpdateTransform: function () {
  8593. // aligning non added elements is expensive
  8594. if (!this.added) {
  8595. this.alignOnAdd = true;
  8596. return;
  8597. }
  8598. var wrapper = this,
  8599. renderer = wrapper.renderer,
  8600. elem = wrapper.element,
  8601. translateX = wrapper.translateX || 0,
  8602. translateY = wrapper.translateY || 0,
  8603. x = wrapper.x || 0,
  8604. y = wrapper.y || 0,
  8605. align = wrapper.textAlign || 'left',
  8606. alignCorrection = {
  8607. left: 0,
  8608. center: 0.5,
  8609. right: 1
  8610. }[align],
  8611. styles = wrapper.styles,
  8612. whiteSpace = styles && styles.whiteSpace;
  8613. /**
  8614. * @private
  8615. * @return {number}
  8616. */
  8617. function getTextPxLength() {
  8618. // Reset multiline/ellipsis in order to read width (#4928,
  8619. // #5417)
  8620. css(elem, {
  8621. width: '',
  8622. whiteSpace: whiteSpace || 'nowrap'
  8623. });
  8624. return elem.offsetWidth;
  8625. }
  8626. // apply translate
  8627. css(elem, {
  8628. marginLeft: translateX,
  8629. marginTop: translateY
  8630. });
  8631. if (!renderer.styledMode && wrapper.shadows) { // used in labels/tooltip
  8632. wrapper.shadows.forEach(function (shadow) {
  8633. css(shadow, {
  8634. marginLeft: translateX + 1,
  8635. marginTop: translateY + 1
  8636. });
  8637. });
  8638. }
  8639. // apply inversion
  8640. if (wrapper.inverted) { // wrapper is a group
  8641. [].forEach.call(elem.childNodes, function (child) {
  8642. renderer.invertChild(child, elem);
  8643. });
  8644. }
  8645. if (elem.tagName === 'SPAN') {
  8646. var rotation = wrapper.rotation, baseline, textWidth = wrapper.textWidth && pInt(wrapper.textWidth), currentTextTransform = [
  8647. rotation,
  8648. align,
  8649. elem.innerHTML,
  8650. wrapper.textWidth,
  8651. wrapper.textAlign
  8652. ].join(',');
  8653. // Update textWidth. Use the memoized textPxLength if possible, to
  8654. // avoid the getTextPxLength function using elem.offsetWidth.
  8655. // Calling offsetWidth affects rendering time as it forces layout
  8656. // (#7656).
  8657. if (textWidth !== wrapper.oldTextWidth &&
  8658. ((textWidth > wrapper.oldTextWidth) ||
  8659. (wrapper.textPxLength || getTextPxLength()) > textWidth) && (
  8660. // Only set the width if the text is able to word-wrap, or
  8661. // text-overflow is ellipsis (#9537)
  8662. /[ \-]/.test(elem.textContent || elem.innerText) ||
  8663. elem.style.textOverflow === 'ellipsis')) { // #983, #1254
  8664. css(elem, {
  8665. width: textWidth + 'px',
  8666. display: 'block',
  8667. whiteSpace: whiteSpace || 'normal' // #3331
  8668. });
  8669. wrapper.oldTextWidth = textWidth;
  8670. wrapper.hasBoxWidthChanged = true; // #8159
  8671. }
  8672. else {
  8673. wrapper.hasBoxWidthChanged = false; // #8159
  8674. }
  8675. // Do the calculations and DOM access only if properties changed
  8676. if (currentTextTransform !== wrapper.cTT) {
  8677. baseline = renderer.fontMetrics(elem.style.fontSize, elem).b;
  8678. // Renderer specific handling of span rotation, but only if we
  8679. // have something to update.
  8680. if (defined(rotation) &&
  8681. ((rotation !== (wrapper.oldRotation || 0)) ||
  8682. (align !== wrapper.oldAlign))) {
  8683. wrapper.setSpanRotation(rotation, alignCorrection, baseline);
  8684. }
  8685. wrapper.getSpanCorrection(
  8686. // Avoid elem.offsetWidth if we can, it affects rendering
  8687. // time heavily (#7656)
  8688. ((!defined(rotation) && wrapper.textPxLength) || // #7920
  8689. elem.offsetWidth), baseline, alignCorrection, rotation, align);
  8690. }
  8691. // apply position with correction
  8692. css(elem, {
  8693. left: (x + (wrapper.xCorr || 0)) + 'px',
  8694. top: (y + (wrapper.yCorr || 0)) + 'px'
  8695. });
  8696. // record current text transform
  8697. wrapper.cTT = currentTextTransform;
  8698. wrapper.oldRotation = rotation;
  8699. wrapper.oldAlign = align;
  8700. }
  8701. },
  8702. /**
  8703. * Set the rotation of an individual HTML span.
  8704. *
  8705. * @private
  8706. * @function Highcharts.SVGElement#setSpanRotation
  8707. * @param {number} rotation
  8708. * @param {number} alignCorrection
  8709. * @param {number} baseline
  8710. * @return {void}
  8711. */
  8712. setSpanRotation: function (rotation, alignCorrection, baseline) {
  8713. var rotationStyle = {},
  8714. cssTransformKey = this.renderer.getTransformKey();
  8715. rotationStyle[cssTransformKey] = rotationStyle.transform =
  8716. 'rotate(' + rotation + 'deg)';
  8717. rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] =
  8718. rotationStyle.transformOrigin =
  8719. (alignCorrection * 100) + '% ' + baseline + 'px';
  8720. css(this.element, rotationStyle);
  8721. },
  8722. /**
  8723. * Get the correction in X and Y positioning as the element is rotated.
  8724. *
  8725. * @private
  8726. * @function Highcharts.SVGElement#getSpanCorrection
  8727. * @param {number} width
  8728. * @param {number} baseline
  8729. * @param {number} alignCorrection
  8730. * @return {void}
  8731. */
  8732. getSpanCorrection: function (width, baseline, alignCorrection) {
  8733. this.xCorr = -width * alignCorrection;
  8734. this.yCorr = -baseline;
  8735. }
  8736. });
  8737. // Extend SvgRenderer for useHTML option.
  8738. extend(SVGRenderer.prototype, /** @lends SVGRenderer.prototype */ {
  8739. /**
  8740. * @private
  8741. * @function Highcharts.SVGRenderer#getTransformKey
  8742. *
  8743. * @return {string}
  8744. */
  8745. getTransformKey: function () {
  8746. return isMS && !/Edge/.test(win.navigator.userAgent) ?
  8747. '-ms-transform' :
  8748. isWebKit ?
  8749. '-webkit-transform' :
  8750. isFirefox ?
  8751. 'MozTransform' :
  8752. win.opera ?
  8753. '-o-transform' :
  8754. '';
  8755. },
  8756. /**
  8757. * Create HTML text node. This is used by the VML renderer as well as the
  8758. * SVG renderer through the useHTML option.
  8759. *
  8760. * @private
  8761. * @function Highcharts.SVGRenderer#html
  8762. *
  8763. * @param {string} str
  8764. * The text of (subset) HTML to draw.
  8765. *
  8766. * @param {number} x
  8767. * The x position of the text's lower left corner.
  8768. *
  8769. * @param {number} y
  8770. * The y position of the text's lower left corner.
  8771. *
  8772. * @return {Highcharts.HTMLDOMElement}
  8773. */
  8774. html: function (str, x, y) {
  8775. var wrapper = this.createElement('span'), element = wrapper.element, renderer = wrapper.renderer, isSVG = renderer.isSVG, addSetters = function (gWrapper, style) {
  8776. // These properties are set as attributes on the SVG group, and
  8777. // as identical CSS properties on the div. (#3542)
  8778. ['opacity', 'visibility'].forEach(function (prop) {
  8779. gWrapper[prop + 'Setter'] = function (value, key, elem) {
  8780. var styleObject = gWrapper.div ?
  8781. gWrapper.div.style :
  8782. style;
  8783. SVGElement.prototype[prop + 'Setter']
  8784. .call(this, value, key, elem);
  8785. if (styleObject) {
  8786. styleObject[key] = value;
  8787. }
  8788. };
  8789. });
  8790. gWrapper.addedSetters = true;
  8791. };
  8792. // Text setter
  8793. wrapper.textSetter = function (value) {
  8794. if (value !== element.innerHTML) {
  8795. delete this.bBox;
  8796. delete this.oldTextWidth;
  8797. }
  8798. this.textStr = value;
  8799. element.innerHTML = pick(value, '');
  8800. wrapper.doTransform = true;
  8801. };
  8802. // Add setters for the element itself (#4938)
  8803. if (isSVG) { // #4938, only for HTML within SVG
  8804. addSetters(wrapper, wrapper.element.style);
  8805. }
  8806. // Various setters which rely on update transform
  8807. wrapper.xSetter =
  8808. wrapper.ySetter =
  8809. wrapper.alignSetter =
  8810. wrapper.rotationSetter =
  8811. function (value, key) {
  8812. if (key === 'align') {
  8813. // Do not overwrite the SVGElement.align method. Same as VML.
  8814. wrapper.alignValue = wrapper.textAlign = value;
  8815. }
  8816. else {
  8817. wrapper[key] = value;
  8818. }
  8819. wrapper.doTransform = true;
  8820. };
  8821. // Runs at the end of .attr()
  8822. wrapper.afterSetters = function () {
  8823. // Update transform. Do this outside the loop to prevent redundant
  8824. // updating for batch setting of attributes.
  8825. if (this.doTransform) {
  8826. this.htmlUpdateTransform();
  8827. this.doTransform = false;
  8828. }
  8829. };
  8830. // Set the default attributes
  8831. wrapper
  8832. .attr({
  8833. text: str,
  8834. x: Math.round(x),
  8835. y: Math.round(y)
  8836. })
  8837. .css({
  8838. position: 'absolute'
  8839. });
  8840. if (!renderer.styledMode) {
  8841. wrapper.css({
  8842. fontFamily: this.style.fontFamily,
  8843. fontSize: this.style.fontSize
  8844. });
  8845. }
  8846. // Keep the whiteSpace style outside the wrapper.styles collection
  8847. element.style.whiteSpace = 'nowrap';
  8848. // Use the HTML specific .css method
  8849. wrapper.css = wrapper.htmlCss;
  8850. // This is specific for HTML within SVG
  8851. if (isSVG) {
  8852. wrapper.add = function (svgGroupWrapper) {
  8853. var htmlGroup,
  8854. container = renderer.box.parentNode,
  8855. parentGroup,
  8856. parents = [];
  8857. this.parentGroup = svgGroupWrapper;
  8858. // Create a mock group to hold the HTML elements
  8859. if (svgGroupWrapper) {
  8860. htmlGroup = svgGroupWrapper.div;
  8861. if (!htmlGroup) {
  8862. // Read the parent chain into an array and read from top
  8863. // down
  8864. parentGroup = svgGroupWrapper;
  8865. while (parentGroup) {
  8866. parents.push(parentGroup);
  8867. // Move up to the next parent group
  8868. parentGroup = parentGroup.parentGroup;
  8869. }
  8870. // Ensure dynamically updating position when any parent
  8871. // is translated
  8872. parents.reverse().forEach(function (parentGroup) {
  8873. var htmlGroupStyle,
  8874. cls = attr(parentGroup.element, 'class');
  8875. /**
  8876. * Common translate setter for X and Y on the HTML
  8877. * group. Reverted the fix for #6957 du to
  8878. * positioning problems and offline export (#7254,
  8879. * #7280, #7529)
  8880. * @private
  8881. * @param {*} value
  8882. * @param {string} key
  8883. * @return {void}
  8884. */
  8885. function translateSetter(value, key) {
  8886. parentGroup[key] = value;
  8887. if (key === 'translateX') {
  8888. htmlGroupStyle.left = value + 'px';
  8889. }
  8890. else {
  8891. htmlGroupStyle.top = value + 'px';
  8892. }
  8893. parentGroup.doTransform = true;
  8894. }
  8895. // Create a HTML div and append it to the parent div
  8896. // to emulate the SVG group structure
  8897. htmlGroup =
  8898. parentGroup.div =
  8899. parentGroup.div || createElement('div', cls ? { className: cls } : void 0, {
  8900. position: 'absolute',
  8901. left: (parentGroup.translateX || 0) + 'px',
  8902. top: (parentGroup.translateY || 0) + 'px',
  8903. display: parentGroup.display,
  8904. opacity: parentGroup.opacity,
  8905. pointerEvents: (parentGroup.styles &&
  8906. parentGroup.styles.pointerEvents) // #5595
  8907. // the top group is appended to container
  8908. }, htmlGroup || container);
  8909. // Shortcut
  8910. htmlGroupStyle = htmlGroup.style;
  8911. // Set listeners to update the HTML div's position
  8912. // whenever the SVG group position is changed.
  8913. extend(parentGroup, {
  8914. // (#7287) Pass htmlGroup to use
  8915. // the related group
  8916. classSetter: (function (htmlGroup) {
  8917. return function (value) {
  8918. this.element.setAttribute('class', value);
  8919. htmlGroup.className = value;
  8920. };
  8921. }(htmlGroup)),
  8922. on: function () {
  8923. if (parents[0].div) { // #6418
  8924. wrapper.on.apply({ element: parents[0].div }, arguments);
  8925. }
  8926. return parentGroup;
  8927. },
  8928. translateXSetter: translateSetter,
  8929. translateYSetter: translateSetter
  8930. });
  8931. if (!parentGroup.addedSetters) {
  8932. addSetters(parentGroup);
  8933. }
  8934. });
  8935. }
  8936. }
  8937. else {
  8938. htmlGroup = container;
  8939. }
  8940. htmlGroup.appendChild(element);
  8941. // Shared with VML:
  8942. wrapper.added = true;
  8943. if (wrapper.alignOnAdd) {
  8944. wrapper.htmlUpdateTransform();
  8945. }
  8946. return wrapper;
  8947. };
  8948. }
  8949. return wrapper;
  8950. }
  8951. });
  8952. });
  8953. _registerModule(_modules, 'Core/Axis/Tick.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  8954. /* *
  8955. *
  8956. * (c) 2010-2020 Torstein Honsi
  8957. *
  8958. * License: www.highcharts.com/license
  8959. *
  8960. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8961. *
  8962. * */
  8963. /**
  8964. * Optional parameters for the tick.
  8965. * @private
  8966. * @interface Highcharts.TickParametersObject
  8967. */ /**
  8968. * Set category for the tick.
  8969. * @name Highcharts.TickParametersObject#category
  8970. * @type {string|undefined}
  8971. */ /**
  8972. * @name Highcharts.TickParametersObject#options
  8973. * @type {Highcharts.Dictionary<any>|undefined}
  8974. */ /**
  8975. * Set tickmarkOffset for the tick.
  8976. * @name Highcharts.TickParametersObject#tickmarkOffset
  8977. * @type {number|undefined}
  8978. */
  8979. var clamp = U.clamp,
  8980. correctFloat = U.correctFloat,
  8981. defined = U.defined,
  8982. destroyObjectProperties = U.destroyObjectProperties,
  8983. extend = U.extend,
  8984. fireEvent = U.fireEvent,
  8985. isNumber = U.isNumber,
  8986. merge = U.merge,
  8987. objectEach = U.objectEach,
  8988. pick = U.pick;
  8989. var deg2rad = H.deg2rad;
  8990. /* eslint-disable no-invalid-this, valid-jsdoc */
  8991. /**
  8992. * The Tick class.
  8993. *
  8994. * @class
  8995. * @name Highcharts.Tick
  8996. *
  8997. * @param {Highcharts.Axis} axis
  8998. * The axis of the tick.
  8999. *
  9000. * @param {number} pos
  9001. * The position of the tick on the axis in terms of axis values.
  9002. *
  9003. * @param {string} [type]
  9004. * The type of tick, either 'minor' or an empty string
  9005. *
  9006. * @param {boolean} [noLabel=false]
  9007. * Whether to disable the label or not. Defaults to false.
  9008. *
  9009. * @param {object} [parameters]
  9010. * Optional parameters for the tick.
  9011. */
  9012. var Tick = /** @class */ (function () {
  9013. /* *
  9014. *
  9015. * Constructors
  9016. *
  9017. * */
  9018. function Tick(axis, pos, type, noLabel, parameters) {
  9019. this.isNew = true;
  9020. this.isNewLabel = true;
  9021. /**
  9022. * The related axis of the tick.
  9023. * @name Highcharts.Tick#axis
  9024. * @type {Highcharts.Axis}
  9025. */
  9026. this.axis = axis;
  9027. /**
  9028. * The logical position of the tick on the axis in terms of axis values.
  9029. * @name Highcharts.Tick#pos
  9030. * @type {number}
  9031. */
  9032. this.pos = pos;
  9033. /**
  9034. * The tick type, which can be `"minor"`, or an empty string.
  9035. * @name Highcharts.Tick#type
  9036. * @type {string}
  9037. */
  9038. this.type = type || '';
  9039. this.parameters = parameters || {};
  9040. /**
  9041. * The mark offset of the tick on the axis. Usually `undefined`, numeric
  9042. * for grid axes.
  9043. * @name Highcharts.Tick#tickmarkOffset
  9044. * @type {number|undefined}
  9045. */
  9046. this.tickmarkOffset = this.parameters.tickmarkOffset;
  9047. this.options = this.parameters.options;
  9048. fireEvent(this, 'init');
  9049. if (!type && !noLabel) {
  9050. this.addLabel();
  9051. }
  9052. }
  9053. /* *
  9054. *
  9055. * Functions
  9056. *
  9057. * */
  9058. /**
  9059. * Write the tick label.
  9060. *
  9061. * @private
  9062. * @function Highcharts.Tick#addLabel
  9063. * @return {void}
  9064. */
  9065. Tick.prototype.addLabel = function () {
  9066. var tick = this,
  9067. axis = tick.axis,
  9068. options = axis.options,
  9069. chart = axis.chart,
  9070. categories = axis.categories,
  9071. log = axis.logarithmic,
  9072. names = axis.names,
  9073. pos = tick.pos,
  9074. labelOptions = pick(tick.options && tick.options.labels,
  9075. options.labels),
  9076. str,
  9077. tickPositions = axis.tickPositions,
  9078. isFirst = pos === tickPositions[0],
  9079. isLast = pos === tickPositions[tickPositions.length - 1],
  9080. value = this.parameters.category || (categories ?
  9081. pick(categories[pos],
  9082. names[pos],
  9083. pos) :
  9084. pos),
  9085. label = tick.label,
  9086. animateLabels = (!labelOptions.step || labelOptions.step === 1) &&
  9087. axis.tickInterval === 1,
  9088. tickPositionInfo = tickPositions.info,
  9089. dateTimeLabelFormat,
  9090. dateTimeLabelFormats,
  9091. i,
  9092. list;
  9093. // Set the datetime label format. If a higher rank is set for this
  9094. // position, use that. If not, use the general format.
  9095. if (axis.dateTime && tickPositionInfo) {
  9096. dateTimeLabelFormats = chart.time.resolveDTLFormat(options.dateTimeLabelFormats[(!options.grid &&
  9097. tickPositionInfo.higherRanks[pos]) ||
  9098. tickPositionInfo.unitName]);
  9099. dateTimeLabelFormat = dateTimeLabelFormats.main;
  9100. }
  9101. // set properties for access in render method
  9102. /**
  9103. * True if the tick is the first one on the axis.
  9104. * @name Highcharts.Tick#isFirst
  9105. * @readonly
  9106. * @type {boolean|undefined}
  9107. */
  9108. tick.isFirst = isFirst;
  9109. /**
  9110. * True if the tick is the last one on the axis.
  9111. * @name Highcharts.Tick#isLast
  9112. * @readonly
  9113. * @type {boolean|undefined}
  9114. */
  9115. tick.isLast = isLast;
  9116. // Get the string
  9117. tick.formatCtx = {
  9118. axis: axis,
  9119. chart: chart,
  9120. isFirst: isFirst,
  9121. isLast: isLast,
  9122. dateTimeLabelFormat: dateTimeLabelFormat,
  9123. tickPositionInfo: tickPositionInfo,
  9124. value: log ? correctFloat(log.lin2log(value)) : value,
  9125. pos: pos
  9126. };
  9127. str = axis.labelFormatter.call(tick.formatCtx, this.formatCtx);
  9128. // Set up conditional formatting based on the format list if existing.
  9129. list = dateTimeLabelFormats && dateTimeLabelFormats.list;
  9130. if (list) {
  9131. tick.shortenLabel = function () {
  9132. for (i = 0; i < list.length; i++) {
  9133. label.attr({
  9134. text: axis.labelFormatter.call(extend(tick.formatCtx, { dateTimeLabelFormat: list[i] }))
  9135. });
  9136. if (label.getBBox().width <
  9137. axis.getSlotWidth(tick) - 2 *
  9138. pick(labelOptions.padding, 5)) {
  9139. return;
  9140. }
  9141. }
  9142. label.attr({
  9143. text: ''
  9144. });
  9145. };
  9146. }
  9147. // Call only after first render
  9148. if (animateLabels && axis._addedPlotLB) {
  9149. tick.moveLabel(str, labelOptions);
  9150. }
  9151. // First call
  9152. if (!defined(label) && !tick.movedLabel) {
  9153. /**
  9154. * The rendered text label of the tick.
  9155. * @name Highcharts.Tick#label
  9156. * @type {Highcharts.SVGElement|undefined}
  9157. */
  9158. tick.label = label = tick.createLabel({ x: 0, y: 0 }, str, labelOptions);
  9159. // Base value to detect change for new calls to getBBox
  9160. tick.rotation = 0;
  9161. // update
  9162. }
  9163. else if (label && label.textStr !== str && !animateLabels) {
  9164. // When resetting text, also reset the width if dynamically set
  9165. // (#8809)
  9166. if (label.textWidth &&
  9167. !(labelOptions.style && labelOptions.style.width) &&
  9168. !label.styles.width) {
  9169. label.css({ width: null });
  9170. }
  9171. label.attr({ text: str });
  9172. label.textPxLength = label.getBBox().width;
  9173. }
  9174. };
  9175. /**
  9176. * Render and return the label of the tick.
  9177. *
  9178. * @private
  9179. * @function Highcharts.Tick#createLabel
  9180. * @param {Highcharts.PositionObject} xy
  9181. * @param {string} str
  9182. * @param {Highcharts.XAxisLabelsOptions} labelOptions
  9183. * @return {Highcharts.SVGElement|undefined}
  9184. */
  9185. Tick.prototype.createLabel = function (xy, str, labelOptions) {
  9186. var axis = this.axis,
  9187. chart = axis.chart,
  9188. label = defined(str) && labelOptions.enabled ?
  9189. chart.renderer
  9190. .text(str,
  9191. xy.x,
  9192. xy.y,
  9193. labelOptions.useHTML)
  9194. .add(axis.labelGroup) :
  9195. null;
  9196. // Un-rotated length
  9197. if (label) {
  9198. // Without position absolute, IE export sometimes is wrong
  9199. if (!chart.styledMode) {
  9200. label.css(merge(labelOptions.style));
  9201. }
  9202. label.textPxLength = label.getBBox().width;
  9203. }
  9204. return label;
  9205. };
  9206. /**
  9207. * Destructor for the tick prototype
  9208. *
  9209. * @private
  9210. * @function Highcharts.Tick#destroy
  9211. * @return {void}
  9212. */
  9213. Tick.prototype.destroy = function () {
  9214. destroyObjectProperties(this, this.axis);
  9215. };
  9216. /**
  9217. * Gets the x and y positions for ticks in terms of pixels.
  9218. *
  9219. * @private
  9220. * @function Highcharts.Tick#getPosition
  9221. *
  9222. * @param {boolean} horiz
  9223. * Whether the tick is on an horizontal axis or not.
  9224. *
  9225. * @param {number} tickPos
  9226. * Position of the tick.
  9227. *
  9228. * @param {number} tickmarkOffset
  9229. * Tickmark offset for all ticks.
  9230. *
  9231. * @param {boolean} [old]
  9232. * Whether the axis has changed or not.
  9233. *
  9234. * @return {Highcharts.PositionObject}
  9235. * The tick position.
  9236. *
  9237. * @fires Highcharts.Tick#event:afterGetPosition
  9238. */
  9239. Tick.prototype.getPosition = function (horiz, tickPos, tickmarkOffset, old) {
  9240. var axis = this.axis,
  9241. chart = axis.chart,
  9242. cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
  9243. pos;
  9244. pos = {
  9245. x: horiz ?
  9246. correctFloat(axis.translate(tickPos + tickmarkOffset, null, null, old) +
  9247. axis.transB) :
  9248. (axis.left +
  9249. axis.offset +
  9250. (axis.opposite ?
  9251. (((old && chart.oldChartWidth) ||
  9252. chart.chartWidth) -
  9253. axis.right -
  9254. axis.left) :
  9255. 0)),
  9256. y: horiz ?
  9257. (cHeight -
  9258. axis.bottom +
  9259. axis.offset -
  9260. (axis.opposite ? axis.height : 0)) :
  9261. correctFloat(cHeight -
  9262. axis.translate(tickPos + tickmarkOffset, null, null, old) -
  9263. axis.transB)
  9264. };
  9265. // Chrome workaround for #10516
  9266. pos.y = clamp(pos.y, -1e5, 1e5);
  9267. fireEvent(this, 'afterGetPosition', { pos: pos });
  9268. return pos;
  9269. };
  9270. /**
  9271. * Get the x, y position of the tick label
  9272. *
  9273. * @private
  9274. * @return {Highcharts.PositionObject}
  9275. */
  9276. Tick.prototype.getLabelPosition = function (x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  9277. var axis = this.axis,
  9278. transA = axis.transA,
  9279. reversed = ( // #7911
  9280. axis.isLinked && axis.linkedParent ?
  9281. axis.linkedParent.reversed :
  9282. axis.reversed),
  9283. staggerLines = axis.staggerLines,
  9284. rotCorr = axis.tickRotCorr || { x: 0,
  9285. y: 0 },
  9286. yOffset = labelOptions.y,
  9287. // Adjust for label alignment if we use reserveSpace: true (#5286)
  9288. labelOffsetCorrection = (!horiz && !axis.reserveSpaceDefault ?
  9289. -axis.labelOffset * (axis.labelAlign === 'center' ? 0.5 : 1) :
  9290. 0),
  9291. line,
  9292. pos = {};
  9293. if (!defined(yOffset)) {
  9294. if (axis.side === 0) {
  9295. yOffset = label.rotation ? -8 : -label.getBBox().height;
  9296. }
  9297. else if (axis.side === 2) {
  9298. yOffset = rotCorr.y + 8;
  9299. }
  9300. else {
  9301. // #3140, #3140
  9302. yOffset = Math.cos(label.rotation * deg2rad) *
  9303. (rotCorr.y - label.getBBox(false, 0).height / 2);
  9304. }
  9305. }
  9306. x = x +
  9307. labelOptions.x +
  9308. labelOffsetCorrection +
  9309. rotCorr.x -
  9310. (tickmarkOffset && horiz ?
  9311. tickmarkOffset * transA * (reversed ? -1 : 1) :
  9312. 0);
  9313. y = y + yOffset - (tickmarkOffset && !horiz ?
  9314. tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
  9315. // Correct for staggered labels
  9316. if (staggerLines) {
  9317. line = (index / (step || 1) % staggerLines);
  9318. if (axis.opposite) {
  9319. line = staggerLines - line - 1;
  9320. }
  9321. y += line * (axis.labelOffset / staggerLines);
  9322. }
  9323. pos.x = x;
  9324. pos.y = Math.round(y);
  9325. fireEvent(this, 'afterGetLabelPosition', { pos: pos, tickmarkOffset: tickmarkOffset, index: index });
  9326. return pos;
  9327. };
  9328. /**
  9329. * Get the offset height or width of the label
  9330. *
  9331. * @private
  9332. * @function Highcharts.Tick#getLabelSize
  9333. * @return {number}
  9334. */
  9335. Tick.prototype.getLabelSize = function () {
  9336. return this.label ?
  9337. this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
  9338. 0;
  9339. };
  9340. /**
  9341. * Extendible method to return the path of the marker
  9342. *
  9343. * @private
  9344. *
  9345. */
  9346. Tick.prototype.getMarkPath = function (x, y, tickLength, tickWidth, horiz, renderer) {
  9347. return renderer.crispLine([[
  9348. 'M',
  9349. x,
  9350. y
  9351. ], [
  9352. 'L',
  9353. x + (horiz ? 0 : -tickLength),
  9354. y + (horiz ? tickLength : 0)
  9355. ]], tickWidth);
  9356. };
  9357. /**
  9358. * Handle the label overflow by adjusting the labels to the left and right
  9359. * edge, or hide them if they collide into the neighbour label.
  9360. *
  9361. * @private
  9362. * @function Highcharts.Tick#handleOverflow
  9363. * @param {Highcharts.PositionObject} xy
  9364. * @return {void}
  9365. */
  9366. Tick.prototype.handleOverflow = function (xy) {
  9367. var tick = this,
  9368. axis = this.axis,
  9369. labelOptions = axis.options.labels,
  9370. pxPos = xy.x,
  9371. chartWidth = axis.chart.chartWidth,
  9372. spacing = axis.chart.spacing,
  9373. leftBound = pick(axis.labelLeft,
  9374. Math.min(axis.pos,
  9375. spacing[3])),
  9376. rightBound = pick(axis.labelRight,
  9377. Math.max(!axis.isRadial ? axis.pos + axis.len : 0,
  9378. chartWidth - spacing[1])),
  9379. label = this.label,
  9380. rotation = this.rotation,
  9381. factor = {
  9382. left: 0,
  9383. center: 0.5,
  9384. right: 1
  9385. }[axis.labelAlign || label.attr('align')],
  9386. labelWidth = label.getBBox().width,
  9387. slotWidth = axis.getSlotWidth(tick),
  9388. modifiedSlotWidth = slotWidth,
  9389. xCorrection = factor,
  9390. goRight = 1,
  9391. leftPos,
  9392. rightPos,
  9393. textWidth,
  9394. css = {};
  9395. // Check if the label overshoots the chart spacing box. If it does, move
  9396. // it. If it now overshoots the slotWidth, add ellipsis.
  9397. if (!rotation &&
  9398. pick(labelOptions.overflow, 'justify') === 'justify') {
  9399. leftPos = pxPos - factor * labelWidth;
  9400. rightPos = pxPos + (1 - factor) * labelWidth;
  9401. if (leftPos < leftBound) {
  9402. modifiedSlotWidth =
  9403. xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
  9404. }
  9405. else if (rightPos > rightBound) {
  9406. modifiedSlotWidth =
  9407. rightBound - xy.x + modifiedSlotWidth * factor;
  9408. goRight = -1;
  9409. }
  9410. modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
  9411. if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
  9412. xy.x += (goRight *
  9413. (slotWidth -
  9414. modifiedSlotWidth -
  9415. xCorrection * (slotWidth - Math.min(labelWidth, modifiedSlotWidth))));
  9416. }
  9417. // If the label width exceeds the available space, set a text width
  9418. // to be picked up below. Also, if a width has been set before, we
  9419. // need to set a new one because the reported labelWidth will be
  9420. // limited by the box (#3938).
  9421. if (labelWidth > modifiedSlotWidth ||
  9422. (axis.autoRotation && (label.styles || {}).width)) {
  9423. textWidth = modifiedSlotWidth;
  9424. }
  9425. // Add ellipsis to prevent rotated labels to be clipped against the edge
  9426. // of the chart
  9427. }
  9428. else if (rotation < 0 &&
  9429. pxPos - factor * labelWidth < leftBound) {
  9430. textWidth = Math.round(pxPos / Math.cos(rotation * deg2rad) - leftBound);
  9431. }
  9432. else if (rotation > 0 &&
  9433. pxPos + factor * labelWidth > rightBound) {
  9434. textWidth = Math.round((chartWidth - pxPos) /
  9435. Math.cos(rotation * deg2rad));
  9436. }
  9437. if (textWidth) {
  9438. if (tick.shortenLabel) {
  9439. tick.shortenLabel();
  9440. }
  9441. else {
  9442. css.width = Math.floor(textWidth) + 'px';
  9443. if (!(labelOptions.style || {}).textOverflow) {
  9444. css.textOverflow = 'ellipsis';
  9445. }
  9446. label.css(css);
  9447. }
  9448. }
  9449. };
  9450. /**
  9451. * Try to replace the label if the same one already exists.
  9452. *
  9453. * @private
  9454. * @function Highcharts.Tick#moveLabel
  9455. * @param {string} str
  9456. * @param {Highcharts.XAxisLabelsOptions} labelOptions
  9457. *
  9458. * @return {void}
  9459. */
  9460. Tick.prototype.moveLabel = function (str, labelOptions) {
  9461. var tick = this,
  9462. label = tick.label,
  9463. moved = false,
  9464. axis = tick.axis,
  9465. labelPos,
  9466. reversed = axis.reversed,
  9467. xPos,
  9468. yPos;
  9469. if (label && label.textStr === str) {
  9470. tick.movedLabel = label;
  9471. moved = true;
  9472. delete tick.label;
  9473. }
  9474. else { // Find a label with the same string
  9475. objectEach(axis.ticks, function (currentTick) {
  9476. if (!moved &&
  9477. !currentTick.isNew &&
  9478. currentTick !== tick &&
  9479. currentTick.label &&
  9480. currentTick.label.textStr === str) {
  9481. tick.movedLabel = currentTick.label;
  9482. moved = true;
  9483. currentTick.labelPos = tick.movedLabel.xy;
  9484. delete currentTick.label;
  9485. }
  9486. });
  9487. }
  9488. // Create new label if the actual one is moved
  9489. if (!moved && (tick.labelPos || label)) {
  9490. labelPos = tick.labelPos || label.xy;
  9491. xPos = axis.horiz ?
  9492. (reversed ? 0 : axis.width + axis.left) : labelPos.x;
  9493. yPos = axis.horiz ?
  9494. labelPos.y : (reversed ? (axis.width + axis.left) : 0);
  9495. tick.movedLabel = tick.createLabel({ x: xPos, y: yPos }, str, labelOptions);
  9496. if (tick.movedLabel) {
  9497. tick.movedLabel.attr({ opacity: 0 });
  9498. }
  9499. }
  9500. };
  9501. /**
  9502. * Put everything in place
  9503. *
  9504. * @private
  9505. * @param {number} index
  9506. * @param {boolean} [old]
  9507. * Use old coordinates to prepare an animation into new position
  9508. * @param {number} [opacity]
  9509. * @return {voids}
  9510. */
  9511. Tick.prototype.render = function (index, old, opacity) {
  9512. var tick = this,
  9513. axis = tick.axis,
  9514. horiz = axis.horiz,
  9515. pos = tick.pos,
  9516. tickmarkOffset = pick(tick.tickmarkOffset,
  9517. axis.tickmarkOffset),
  9518. xy = tick.getPosition(horiz,
  9519. pos,
  9520. tickmarkOffset,
  9521. old),
  9522. x = xy.x,
  9523. y = xy.y,
  9524. reverseCrisp = ((horiz && x === axis.pos + axis.len) ||
  9525. (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
  9526. opacity = pick(opacity, 1);
  9527. this.isActive = true;
  9528. // Create the grid line
  9529. this.renderGridLine(old, opacity, reverseCrisp);
  9530. // create the tick mark
  9531. this.renderMark(xy, opacity, reverseCrisp);
  9532. // the label is created on init - now move it into place
  9533. this.renderLabel(xy, old, opacity, index);
  9534. tick.isNew = false;
  9535. fireEvent(this, 'afterRender');
  9536. };
  9537. /**
  9538. * Renders the gridLine.
  9539. *
  9540. * @private
  9541. * @param {boolean} old Whether or not the tick is old
  9542. * @param {number} opacity The opacity of the grid line
  9543. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  9544. * @return {void}
  9545. */
  9546. Tick.prototype.renderGridLine = function (old, opacity, reverseCrisp) {
  9547. var tick = this, axis = tick.axis, options = axis.options, gridLine = tick.gridLine, gridLinePath, attribs = {}, pos = tick.pos, type = tick.type, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), renderer = axis.chart.renderer, gridPrefix = type ? type + 'Grid' : 'grid', gridLineWidth = options[gridPrefix + 'LineWidth'], gridLineColor = options[gridPrefix + 'LineColor'], dashStyle = options[gridPrefix + 'LineDashStyle'];
  9548. if (!gridLine) {
  9549. if (!axis.chart.styledMode) {
  9550. attribs.stroke = gridLineColor;
  9551. attribs['stroke-width'] = gridLineWidth;
  9552. if (dashStyle) {
  9553. attribs.dashstyle = dashStyle;
  9554. }
  9555. }
  9556. if (!type) {
  9557. attribs.zIndex = 1;
  9558. }
  9559. if (old) {
  9560. opacity = 0;
  9561. }
  9562. /**
  9563. * The rendered grid line of the tick.
  9564. * @name Highcharts.Tick#gridLine
  9565. * @type {Highcharts.SVGElement|undefined}
  9566. */
  9567. tick.gridLine = gridLine = renderer.path()
  9568. .attr(attribs)
  9569. .addClass('highcharts-' + (type ? type + '-' : '') + 'grid-line')
  9570. .add(axis.gridGroup);
  9571. }
  9572. if (gridLine) {
  9573. gridLinePath = axis.getPlotLinePath({
  9574. value: pos + tickmarkOffset,
  9575. lineWidth: gridLine.strokeWidth() * reverseCrisp,
  9576. force: 'pass',
  9577. old: old
  9578. });
  9579. // If the parameter 'old' is set, the current call will be followed
  9580. // by another call, therefore do not do any animations this time
  9581. if (gridLinePath) {
  9582. gridLine[old || tick.isNew ? 'attr' : 'animate']({
  9583. d: gridLinePath,
  9584. opacity: opacity
  9585. });
  9586. }
  9587. }
  9588. };
  9589. /**
  9590. * Renders the tick mark.
  9591. *
  9592. * @private
  9593. * @param {Highcharts.PositionObject} xy The position vector of the mark
  9594. * @param {number} opacity The opacity of the mark
  9595. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  9596. * @return {void}
  9597. */
  9598. Tick.prototype.renderMark = function (xy, opacity, reverseCrisp) {
  9599. var tick = this, axis = tick.axis, options = axis.options, renderer = axis.chart.renderer, type = tick.type, tickPrefix = type ? type + 'Tick' : 'tick', tickSize = axis.tickSize(tickPrefix), mark = tick.mark, isNewMark = !mark, x = xy.x, y = xy.y, tickWidth = pick(options[tickPrefix + 'Width'], !type && axis.isXAxis ? 1 : 0), // X axis defaults to 1
  9600. tickColor = options[tickPrefix + 'Color'];
  9601. if (tickSize) {
  9602. // negate the length
  9603. if (axis.opposite) {
  9604. tickSize[0] = -tickSize[0];
  9605. }
  9606. // First time, create it
  9607. if (isNewMark) {
  9608. /**
  9609. * The rendered mark of the tick.
  9610. * @name Highcharts.Tick#mark
  9611. * @type {Highcharts.SVGElement|undefined}
  9612. */
  9613. tick.mark = mark = renderer.path()
  9614. .addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
  9615. .add(axis.axisGroup);
  9616. if (!axis.chart.styledMode) {
  9617. mark.attr({
  9618. stroke: tickColor,
  9619. 'stroke-width': tickWidth
  9620. });
  9621. }
  9622. }
  9623. mark[isNewMark ? 'attr' : 'animate']({
  9624. d: tick.getMarkPath(x, y, tickSize[0], mark.strokeWidth() * reverseCrisp, axis.horiz, renderer),
  9625. opacity: opacity
  9626. });
  9627. }
  9628. };
  9629. /**
  9630. * Renders the tick label.
  9631. * Note: The label should already be created in init(), so it should only
  9632. * have to be moved into place.
  9633. *
  9634. * @private
  9635. * @param {Highcharts.PositionObject} xy The position vector of the label
  9636. * @param {boolean} old Whether or not the tick is old
  9637. * @param {number} opacity The opacity of the label
  9638. * @param {number} index The index of the tick
  9639. * @return {void}
  9640. */
  9641. Tick.prototype.renderLabel = function (xy, old, opacity, index) {
  9642. var tick = this,
  9643. axis = tick.axis,
  9644. horiz = axis.horiz,
  9645. options = axis.options,
  9646. label = tick.label,
  9647. labelOptions = options.labels,
  9648. step = labelOptions.step,
  9649. tickmarkOffset = pick(tick.tickmarkOffset,
  9650. axis.tickmarkOffset),
  9651. show = true,
  9652. x = xy.x,
  9653. y = xy.y;
  9654. if (label && isNumber(x)) {
  9655. label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
  9656. // Apply show first and show last. If the tick is both first and
  9657. // last, it is a single centered tick, in which case we show the
  9658. // label anyway (#2100).
  9659. if ((tick.isFirst &&
  9660. !tick.isLast &&
  9661. !pick(options.showFirstLabel, 1)) ||
  9662. (tick.isLast &&
  9663. !tick.isFirst &&
  9664. !pick(options.showLastLabel, 1))) {
  9665. show = false;
  9666. // Handle label overflow and show or hide accordingly
  9667. }
  9668. else if (horiz &&
  9669. !labelOptions.step &&
  9670. !labelOptions.rotation &&
  9671. !old &&
  9672. opacity !== 0) {
  9673. tick.handleOverflow(xy);
  9674. }
  9675. // apply step
  9676. if (step && index % step) {
  9677. // show those indices dividable by step
  9678. show = false;
  9679. }
  9680. // Set the new position, and show or hide
  9681. if (show && isNumber(xy.y)) {
  9682. xy.opacity = opacity;
  9683. label[tick.isNewLabel ? 'attr' : 'animate'](xy);
  9684. tick.isNewLabel = false;
  9685. }
  9686. else {
  9687. label.attr('y', -9999); // #1338
  9688. tick.isNewLabel = true;
  9689. }
  9690. }
  9691. };
  9692. /**
  9693. * Replace labels with the moved ones to perform animation. Additionally
  9694. * destroy unused labels.
  9695. *
  9696. * @private
  9697. * @function Highcharts.Tick#replaceMovedLabel
  9698. * @return {void}
  9699. */
  9700. Tick.prototype.replaceMovedLabel = function () {
  9701. var tick = this,
  9702. label = tick.label,
  9703. axis = tick.axis,
  9704. reversed = axis.reversed,
  9705. x,
  9706. y;
  9707. // Animate and destroy
  9708. if (label && !tick.isNew) {
  9709. x = axis.horiz ? (reversed ? axis.left : axis.width + axis.left) : label.xy.x;
  9710. y = axis.horiz ?
  9711. label.xy.y :
  9712. (reversed ? axis.width + axis.top : axis.top);
  9713. label.animate({ x: x, y: y, opacity: 0 }, void 0, label.destroy);
  9714. delete tick.label;
  9715. }
  9716. axis.isDirty = true;
  9717. tick.label = tick.movedLabel;
  9718. delete tick.movedLabel;
  9719. };
  9720. return Tick;
  9721. }());
  9722. H.Tick = Tick;
  9723. return H.Tick;
  9724. });
  9725. _registerModule(_modules, 'Core/Time.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Highcharts, U) {
  9726. /* *
  9727. *
  9728. * (c) 2010-2020 Torstein Honsi
  9729. *
  9730. * License: www.highcharts.com/license
  9731. *
  9732. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  9733. *
  9734. * */
  9735. /**
  9736. * Normalized interval.
  9737. *
  9738. * @interface Highcharts.TimeNormalizedObject
  9739. */ /**
  9740. * The count.
  9741. *
  9742. * @name Highcharts.TimeNormalizedObject#count
  9743. * @type {number}
  9744. */ /**
  9745. * The interval in axis values (ms).
  9746. *
  9747. * @name Highcharts.TimeNormalizedObject#unitRange
  9748. * @type {number}
  9749. */
  9750. /**
  9751. * Function of an additional date format specifier.
  9752. *
  9753. * @callback Highcharts.TimeFormatCallbackFunction
  9754. *
  9755. * @param {number} timestamp
  9756. * The time to format.
  9757. *
  9758. * @return {string}
  9759. * The formatted portion of the date.
  9760. */
  9761. /**
  9762. * Additonal time tick information.
  9763. *
  9764. * @interface Highcharts.TimeTicksInfoObject
  9765. * @extends Highcharts.TimeNormalizedObject
  9766. */ /**
  9767. * @name Highcharts.TimeTicksInfoObject#higherRanks
  9768. * @type {Array<string>}
  9769. */ /**
  9770. * @name Highcharts.TimeTicksInfoObject#totalRange
  9771. * @type {number}
  9772. */
  9773. /**
  9774. * Time ticks.
  9775. *
  9776. * @interface Highcharts.AxisTickPositionsArray
  9777. * @extends global.Array<number>
  9778. */ /**
  9779. * @name Highcharts.AxisTickPositionsArray#info
  9780. * @type {Highcharts.TimeTicksInfoObject|undefined}
  9781. */
  9782. /**
  9783. * A callback to return the time zone offset for a given datetime. It
  9784. * takes the timestamp in terms of milliseconds since January 1 1970,
  9785. * and returns the timezone offset in minutes. This provides a hook
  9786. * for drawing time based charts in specific time zones using their
  9787. * local DST crossover dates, with the help of external libraries.
  9788. *
  9789. * @callback Highcharts.TimezoneOffsetCallbackFunction
  9790. *
  9791. * @param {number} timestamp
  9792. * Timestamp in terms of milliseconds since January 1 1970.
  9793. *
  9794. * @return {number}
  9795. * Timezone offset in minutes.
  9796. */
  9797. /**
  9798. * Allows to manually load the `moment.js` library from Highcharts options
  9799. * instead of the `window`.
  9800. * In case of loading the library from a `script` tag,
  9801. * this option is not needed, it will be loaded from there by default.
  9802. *
  9803. * @type {function}
  9804. * @since 8.2.0
  9805. * @apioption time.moment
  9806. */
  9807. var defined = U.defined,
  9808. error = U.error,
  9809. extend = U.extend,
  9810. isObject = U.isObject,
  9811. merge = U.merge,
  9812. objectEach = U.objectEach,
  9813. pad = U.pad,
  9814. pick = U.pick,
  9815. splat = U.splat,
  9816. timeUnits = U.timeUnits;
  9817. var H = Highcharts,
  9818. win = H.win;
  9819. /* eslint-disable no-invalid-this, valid-jsdoc */
  9820. /**
  9821. * The Time class. Time settings are applied in general for each page using
  9822. * `Highcharts.setOptions`, or individually for each Chart item through the
  9823. * [time](https://api.highcharts.com/highcharts/time) options set.
  9824. *
  9825. * The Time object is available from {@link Highcharts.Chart#time},
  9826. * which refers to `Highcharts.time` if no individual time settings are
  9827. * applied.
  9828. *
  9829. * @example
  9830. * // Apply time settings globally
  9831. * Highcharts.setOptions({
  9832. * time: {
  9833. * timezone: 'Europe/London'
  9834. * }
  9835. * });
  9836. *
  9837. * // Apply time settings by instance
  9838. * var chart = Highcharts.chart('container', {
  9839. * time: {
  9840. * timezone: 'America/New_York'
  9841. * },
  9842. * series: [{
  9843. * data: [1, 4, 3, 5]
  9844. * }]
  9845. * });
  9846. *
  9847. * // Use the Time object
  9848. * console.log(
  9849. * 'Current time in New York',
  9850. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  9851. * );
  9852. *
  9853. * @since 6.0.5
  9854. *
  9855. * @class
  9856. * @name Highcharts.Time
  9857. *
  9858. * @param {Highcharts.TimeOptions} options
  9859. * Time options as defined in [chart.options.time](/highcharts/time).
  9860. */
  9861. var Time = /** @class */ (function () {
  9862. /* *
  9863. *
  9864. * Constructors
  9865. *
  9866. * */
  9867. function Time(options) {
  9868. /* *
  9869. *
  9870. * Properties
  9871. *
  9872. * */
  9873. this.options = {};
  9874. this.useUTC = false;
  9875. this.variableTimezone = false;
  9876. this.Date = win.Date;
  9877. /**
  9878. * Get the time zone offset based on the current timezone information as
  9879. * set in the global options.
  9880. *
  9881. * @function Highcharts.Time#getTimezoneOffset
  9882. *
  9883. * @param {number} timestamp
  9884. * The JavaScript timestamp to inspect.
  9885. *
  9886. * @return {number}
  9887. * The timezone offset in minutes compared to UTC.
  9888. */
  9889. this.getTimezoneOffset = this.timezoneOffsetFunction();
  9890. this.update(options);
  9891. }
  9892. /* *
  9893. *
  9894. * Functions
  9895. *
  9896. * */
  9897. /**
  9898. * Time units used in `Time.get` and `Time.set`
  9899. *
  9900. * @typedef {"Date"|"Day"|"FullYear"|"Hours"|"Milliseconds"|"Minutes"|"Month"|"Seconds"} Highcharts.TimeUnitValue
  9901. */
  9902. /**
  9903. * Get the value of a date object in given units, and subject to the Time
  9904. * object's current timezone settings. This function corresponds directly to
  9905. * JavaScripts `Date.getXXX / Date.getUTCXXX`, so instead of calling
  9906. * `date.getHours()` or `date.getUTCHours()` we will call
  9907. * `time.get('Hours')`.
  9908. *
  9909. * @function Highcharts.Time#get
  9910. *
  9911. * @param {Highcharts.TimeUnitValue} unit
  9912. * @param {Date} date
  9913. *
  9914. * @return {number}
  9915. * The given time unit
  9916. */
  9917. Time.prototype.get = function (unit, date) {
  9918. if (this.variableTimezone || this.timezoneOffset) {
  9919. var realMs = date.getTime();
  9920. var ms = realMs - this.getTimezoneOffset(date);
  9921. date.setTime(ms); // Temporary adjust to timezone
  9922. var ret = date['getUTC' + unit]();
  9923. date.setTime(realMs); // Reset
  9924. return ret;
  9925. }
  9926. // UTC time with no timezone handling
  9927. if (this.useUTC) {
  9928. return date['getUTC' + unit]();
  9929. }
  9930. // Else, local time
  9931. return date['get' + unit]();
  9932. };
  9933. /**
  9934. * Set the value of a date object in given units, and subject to the Time
  9935. * object's current timezone settings. This function corresponds directly to
  9936. * JavaScripts `Date.setXXX / Date.setUTCXXX`, so instead of calling
  9937. * `date.setHours(0)` or `date.setUTCHours(0)` we will call
  9938. * `time.set('Hours', 0)`.
  9939. *
  9940. * @function Highcharts.Time#set
  9941. *
  9942. * @param {Highcharts.TimeUnitValue} unit
  9943. * @param {Date} date
  9944. * @param {number} value
  9945. *
  9946. * @return {number}
  9947. * The epoch milliseconds of the updated date
  9948. */
  9949. Time.prototype.set = function (unit, date, value) {
  9950. // UTC time with timezone handling
  9951. if (this.variableTimezone || this.timezoneOffset) {
  9952. // For lower order time units, just set it directly using UTC
  9953. // time
  9954. if (unit === 'Milliseconds' ||
  9955. unit === 'Seconds' ||
  9956. unit === 'Minutes') {
  9957. return date['setUTC' + unit](value);
  9958. }
  9959. // Higher order time units need to take the time zone into
  9960. // account
  9961. // Adjust by timezone
  9962. var offset = this.getTimezoneOffset(date);
  9963. var ms = date.getTime() - offset;
  9964. date.setTime(ms);
  9965. date['setUTC' + unit](value);
  9966. var newOffset = this.getTimezoneOffset(date);
  9967. ms = date.getTime() + newOffset;
  9968. return date.setTime(ms);
  9969. }
  9970. // UTC time with no timezone handling
  9971. if (this.useUTC) {
  9972. return date['setUTC' + unit](value);
  9973. }
  9974. // Else, local time
  9975. return date['set' + unit](value);
  9976. };
  9977. /**
  9978. * Update the Time object with current options. It is called internally on
  9979. * initializing Highcharts, after running `Highcharts.setOptions` and on
  9980. * `Chart.update`.
  9981. *
  9982. * @private
  9983. * @function Highcharts.Time#update
  9984. *
  9985. * @param {Highcharts.TimeOptions} options
  9986. *
  9987. * @return {void}
  9988. */
  9989. Time.prototype.update = function (options) {
  9990. var useUTC = pick(options && options.useUTC,
  9991. true),
  9992. time = this;
  9993. this.options = options = merge(true, this.options || {}, options);
  9994. // Allow using a different Date class
  9995. this.Date = options.Date || win.Date || Date;
  9996. this.useUTC = useUTC;
  9997. this.timezoneOffset = (useUTC && options.timezoneOffset);
  9998. this.getTimezoneOffset = this.timezoneOffsetFunction();
  9999. /*
  10000. * The time object has options allowing for variable time zones, meaning
  10001. * the axis ticks or series data needs to consider this.
  10002. */
  10003. this.variableTimezone = !!(!useUTC ||
  10004. options.getTimezoneOffset ||
  10005. options.timezone);
  10006. };
  10007. /**
  10008. * Make a time and returns milliseconds. Interprets the inputs as UTC time,
  10009. * local time or a specific timezone time depending on the current time
  10010. * settings.
  10011. *
  10012. * @function Highcharts.Time#makeTime
  10013. *
  10014. * @param {number} year
  10015. * The year
  10016. *
  10017. * @param {number} month
  10018. * The month. Zero-based, so January is 0.
  10019. *
  10020. * @param {number} [date=1]
  10021. * The day of the month
  10022. *
  10023. * @param {number} [hours=0]
  10024. * The hour of the day, 0-23.
  10025. *
  10026. * @param {number} [minutes=0]
  10027. * The minutes
  10028. *
  10029. * @param {number} [seconds=0]
  10030. * The seconds
  10031. *
  10032. * @return {number}
  10033. * The time in milliseconds since January 1st 1970.
  10034. */
  10035. Time.prototype.makeTime = function (year, month, date, hours, minutes, seconds) {
  10036. var d,
  10037. offset,
  10038. newOffset;
  10039. if (this.useUTC) {
  10040. d = this.Date.UTC.apply(0, arguments);
  10041. offset = this.getTimezoneOffset(d);
  10042. d += offset;
  10043. newOffset = this.getTimezoneOffset(d);
  10044. if (offset !== newOffset) {
  10045. d += newOffset - offset;
  10046. // A special case for transitioning from summer time to winter time.
  10047. // When the clock is set back, the same time is repeated twice, i.e.
  10048. // 02:30 am is repeated since the clock is set back from 3 am to
  10049. // 2 am. We need to make the same time as local Date does.
  10050. }
  10051. else if (offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
  10052. !H.isSafari) {
  10053. d -= 36e5;
  10054. }
  10055. }
  10056. else {
  10057. d = new this.Date(year, month, pick(date, 1), pick(hours, 0), pick(minutes, 0), pick(seconds, 0)).getTime();
  10058. }
  10059. return d;
  10060. };
  10061. /**
  10062. * Sets the getTimezoneOffset function. If the `timezone` option is set, a
  10063. * default getTimezoneOffset function with that timezone is returned. If
  10064. * a `getTimezoneOffset` option is defined, it is returned. If neither are
  10065. * specified, the function using the `timezoneOffset` option or 0 offset is
  10066. * returned.
  10067. *
  10068. * @private
  10069. * @function Highcharts.Time#timezoneOffsetFunction
  10070. *
  10071. * @return {Function}
  10072. * A getTimezoneOffset function
  10073. */
  10074. Time.prototype.timezoneOffsetFunction = function () {
  10075. var time = this,
  10076. options = this.options,
  10077. moment = options.moment || win.moment;
  10078. if (!this.useUTC) {
  10079. return function (timestamp) {
  10080. return new Date(timestamp.toString()).getTimezoneOffset() * 60000;
  10081. };
  10082. }
  10083. if (options.timezone) {
  10084. if (!moment) {
  10085. // getTimezoneOffset-function stays undefined because it depends
  10086. // on Moment.js
  10087. error(25);
  10088. }
  10089. else {
  10090. return function (timestamp) {
  10091. return -moment.tz(timestamp, options.timezone).utcOffset() * 60000;
  10092. };
  10093. }
  10094. }
  10095. // If not timezone is set, look for the getTimezoneOffset callback
  10096. if (this.useUTC && options.getTimezoneOffset) {
  10097. return function (timestamp) {
  10098. return options.getTimezoneOffset(timestamp.valueOf()) * 60000;
  10099. };
  10100. }
  10101. // Last, use the `timezoneOffset` option if set
  10102. return function () {
  10103. return (time.timezoneOffset || 0) * 60000;
  10104. };
  10105. };
  10106. /**
  10107. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970)
  10108. * into a human readable date string. The available format keys are listed
  10109. * below. Additional formats can be given in the
  10110. * {@link Highcharts.dateFormats} hook.
  10111. *
  10112. * Supported format keys:
  10113. * - `%a`: Short weekday, like 'Mon'
  10114. * - `%A`: Long weekday, like 'Monday'
  10115. * - `%d`: Two digit day of the month, 01 to 31
  10116. * - `%e`: Day of the month, 1 through 31
  10117. * - `%w`: Day of the week, 0 through 6
  10118. * - `%b`: Short month, like 'Jan'
  10119. * - `%B`: Long month, like 'January'
  10120. * - `%m`: Two digit month number, 01 through 12
  10121. * - `%y`: Two digits year, like 09 for 2009
  10122. * - `%Y`: Four digits year, like 2009
  10123. * - `%H`: Two digits hours in 24h format, 00 through 23
  10124. * - `%k`: Hours in 24h format, 0 through 23
  10125. * - `%I`: Two digits hours in 12h format, 00 through 11
  10126. * - `%l`: Hours in 12h format, 1 through 12
  10127. * - `%M`: Two digits minutes, 00 through 59
  10128. * - `%p`: Upper case AM or PM
  10129. * - `%P`: Lower case AM or PM
  10130. * - `%S`: Two digits seconds, 00 through 59
  10131. * - `%L`: Milliseconds (naming from Ruby)
  10132. *
  10133. * @example
  10134. * const time = new Highcharts.Time();
  10135. * const s = time.dateFormat('%Y-%m-%d %H:%M:%S', Date.UTC(2020, 0, 1));
  10136. * console.log(s); // => 2020-01-01 00:00:00
  10137. *
  10138. * @function Highcharts.Time#dateFormat
  10139. *
  10140. * @param {string} format
  10141. * The desired format where various time representations are
  10142. * prefixed with %.
  10143. *
  10144. * @param {number} timestamp
  10145. * The JavaScript timestamp.
  10146. *
  10147. * @param {boolean} [capitalize=false]
  10148. * Upper case first letter in the return.
  10149. *
  10150. * @return {string}
  10151. * The formatted date.
  10152. */
  10153. Time.prototype.dateFormat = function (format, timestamp, capitalize) {
  10154. var _a;
  10155. if (!defined(timestamp) || isNaN(timestamp)) {
  10156. return ((_a = H.defaultOptions.lang) === null || _a === void 0 ? void 0 : _a.invalidDate) || '';
  10157. }
  10158. format = pick(format, '%Y-%m-%d %H:%M:%S');
  10159. var time = this, date = new this.Date(timestamp),
  10160. // get the basic time values
  10161. 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 === null || lang === void 0 ? void 0 : lang.weekdays, shortWeekdays = lang === null || lang === void 0 ? void 0 : lang.shortWeekdays,
  10162. // List all format keys. Custom formats can be added from the
  10163. // outside.
  10164. replacements = extend({
  10165. // Day
  10166. // Short weekday, like 'Mon'
  10167. a: shortWeekdays ?
  10168. shortWeekdays[day] :
  10169. langWeekdays[day].substr(0, 3),
  10170. // Long weekday, like 'Monday'
  10171. A: langWeekdays[day],
  10172. // Two digit day of the month, 01 to 31
  10173. d: pad(dayOfMonth),
  10174. // Day of the month, 1 through 31
  10175. e: pad(dayOfMonth, 2, ' '),
  10176. // Day of the week, 0 through 6
  10177. w: day,
  10178. // Week (none implemented)
  10179. // 'W': weekNumber(),
  10180. // Month
  10181. // Short month, like 'Jan'
  10182. b: lang.shortMonths[month],
  10183. // Long month, like 'January'
  10184. B: lang.months[month],
  10185. // Two digit month number, 01 through 12
  10186. m: pad(month + 1),
  10187. // Month number, 1 through 12 (#8150)
  10188. o: month + 1,
  10189. // Year
  10190. // Two digits year, like 09 for 2009
  10191. y: fullYear.toString().substr(2, 2),
  10192. // Four digits year, like 2009
  10193. Y: fullYear,
  10194. // Time
  10195. // Two digits hours in 24h format, 00 through 23
  10196. H: pad(hours),
  10197. // Hours in 24h format, 0 through 23
  10198. k: hours,
  10199. // Two digits hours in 12h format, 00 through 11
  10200. I: pad((hours % 12) || 12),
  10201. // Hours in 12h format, 1 through 12
  10202. l: (hours % 12) || 12,
  10203. // Two digits minutes, 00 through 59
  10204. M: pad(this.get('Minutes', date)),
  10205. // Upper case AM or PM
  10206. p: hours < 12 ? 'AM' : 'PM',
  10207. // Lower case AM or PM
  10208. P: hours < 12 ? 'am' : 'pm',
  10209. // Two digits seconds, 00 through 59
  10210. S: pad(date.getSeconds()),
  10211. // Milliseconds (naming from Ruby)
  10212. L: pad(Math.floor(timestamp % 1000), 3)
  10213. }, H.dateFormats);
  10214. // Do the replaces
  10215. objectEach(replacements, function (val, key) {
  10216. // Regex would do it in one line, but this is faster
  10217. while (format.indexOf('%' + key) !== -1) {
  10218. format = format.replace('%' + key, typeof val === 'function' ? val.call(time, timestamp) : val);
  10219. }
  10220. });
  10221. // Optionally capitalize the string and return
  10222. return capitalize ?
  10223. (format.substr(0, 1).toUpperCase() +
  10224. format.substr(1)) :
  10225. format;
  10226. };
  10227. /**
  10228. * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
  10229. * an object.
  10230. * @private
  10231. * @param {string|Array<T>|Highcharts.Dictionary<T>} f - General format description
  10232. * @return {Highcharts.Dictionary<T>} - The object definition
  10233. */
  10234. Time.prototype.resolveDTLFormat = function (f) {
  10235. if (!isObject(f, true)) { // check for string or array
  10236. f = splat(f);
  10237. return {
  10238. main: f[0],
  10239. from: f[1],
  10240. to: f[2]
  10241. };
  10242. }
  10243. return f;
  10244. };
  10245. /**
  10246. * Return an array with time positions distributed on round time values
  10247. * right and right after min and max. Used in datetime axes as well as for
  10248. * grouping data on a datetime axis.
  10249. *
  10250. * @function Highcharts.Time#getTimeTicks
  10251. *
  10252. * @param {Highcharts.TimeNormalizedObject} normalizedInterval
  10253. * The interval in axis values (ms) and the count
  10254. *
  10255. * @param {number} [min]
  10256. * The minimum in axis values
  10257. *
  10258. * @param {number} [max]
  10259. * The maximum in axis values
  10260. *
  10261. * @param {number} [startOfWeek=1]
  10262. *
  10263. * @return {Highcharts.AxisTickPositionsArray}
  10264. */
  10265. Time.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek) {
  10266. var time = this,
  10267. Date = time.Date,
  10268. tickPositions = [],
  10269. i,
  10270. higherRanks = {},
  10271. minYear, // used in months and years as a basis for Date.UTC()
  10272. // When crossing DST, use the max. Resolves #6278.
  10273. minDate = new Date(min),
  10274. interval = normalizedInterval.unitRange,
  10275. count = normalizedInterval.count || 1,
  10276. variableDayLength,
  10277. minDay;
  10278. startOfWeek = pick(startOfWeek, 1);
  10279. if (defined(min)) { // #1300
  10280. time.set('Milliseconds', minDate, interval >= timeUnits.second ?
  10281. 0 : // #3935
  10282. count * Math.floor(time.get('Milliseconds', minDate) / count)); // #3652, #3654
  10283. if (interval >= timeUnits.second) { // second
  10284. time.set('Seconds', minDate, interval >= timeUnits.minute ?
  10285. 0 : // #3935
  10286. count * Math.floor(time.get('Seconds', minDate) / count));
  10287. }
  10288. if (interval >= timeUnits.minute) { // minute
  10289. time.set('Minutes', minDate, interval >= timeUnits.hour ?
  10290. 0 :
  10291. count * Math.floor(time.get('Minutes', minDate) / count));
  10292. }
  10293. if (interval >= timeUnits.hour) { // hour
  10294. time.set('Hours', minDate, interval >= timeUnits.day ?
  10295. 0 :
  10296. count * Math.floor(time.get('Hours', minDate) / count));
  10297. }
  10298. if (interval >= timeUnits.day) { // day
  10299. time.set('Date', minDate, interval >= timeUnits.month ?
  10300. 1 :
  10301. Math.max(1, count * Math.floor(time.get('Date', minDate) / count)));
  10302. }
  10303. if (interval >= timeUnits.month) { // month
  10304. time.set('Month', minDate, interval >= timeUnits.year ? 0 :
  10305. count * Math.floor(time.get('Month', minDate) / count));
  10306. minYear = time.get('FullYear', minDate);
  10307. }
  10308. if (interval >= timeUnits.year) { // year
  10309. minYear -= minYear % count;
  10310. time.set('FullYear', minDate, minYear);
  10311. }
  10312. // week is a special case that runs outside the hierarchy
  10313. if (interval === timeUnits.week) {
  10314. // get start of current week, independent of count
  10315. minDay = time.get('Day', minDate);
  10316. time.set('Date', minDate, (time.get('Date', minDate) -
  10317. minDay + startOfWeek +
  10318. // We don't want to skip days that are before
  10319. // startOfWeek (#7051)
  10320. (minDay < startOfWeek ? -7 : 0)));
  10321. }
  10322. // Get basics for variable time spans
  10323. minYear = time.get('FullYear', minDate);
  10324. var minMonth = time.get('Month', minDate), minDateDate = time.get('Date', minDate), minHours = time.get('Hours', minDate);
  10325. // Redefine min to the floored/rounded minimum time (#7432)
  10326. min = minDate.getTime();
  10327. // Handle local timezone offset
  10328. if (time.variableTimezone) {
  10329. // Detect whether we need to take the DST crossover into
  10330. // consideration. If we're crossing over DST, the day length may
  10331. // be 23h or 25h and we need to compute the exact clock time for
  10332. // each tick instead of just adding hours. This comes at a cost,
  10333. // so first we find out if it is needed (#4951).
  10334. variableDayLength = (
  10335. // Long range, assume we're crossing over.
  10336. max - min > 4 * timeUnits.month ||
  10337. // Short range, check if min and max are in different time
  10338. // zones.
  10339. time.getTimezoneOffset(min) !==
  10340. time.getTimezoneOffset(max));
  10341. }
  10342. // Iterate and add tick positions at appropriate values
  10343. var t = minDate.getTime();
  10344. i = 1;
  10345. while (t < max) {
  10346. tickPositions.push(t);
  10347. // if the interval is years, use Date.UTC to increase years
  10348. if (interval === timeUnits.year) {
  10349. t = time.makeTime(minYear + i * count, 0);
  10350. // if the interval is months, use Date.UTC to increase months
  10351. }
  10352. else if (interval === timeUnits.month) {
  10353. t = time.makeTime(minYear, minMonth + i * count);
  10354. // if we're using global time, the interval is not fixed as it
  10355. // jumps one hour at the DST crossover
  10356. }
  10357. else if (variableDayLength &&
  10358. (interval === timeUnits.day || interval === timeUnits.week)) {
  10359. t = time.makeTime(minYear, minMonth, minDateDate +
  10360. i * count * (interval === timeUnits.day ? 1 : 7));
  10361. }
  10362. else if (variableDayLength &&
  10363. interval === timeUnits.hour &&
  10364. count > 1) {
  10365. // make sure higher ranks are preserved across DST (#6797,
  10366. // #7621)
  10367. t = time.makeTime(minYear, minMonth, minDateDate, minHours + i * count);
  10368. // else, the interval is fixed and we use simple addition
  10369. }
  10370. else {
  10371. t += interval * count;
  10372. }
  10373. i++;
  10374. }
  10375. // push the last time
  10376. tickPositions.push(t);
  10377. // Handle higher ranks. Mark new days if the time is on midnight
  10378. // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
  10379. // to prevent looping over dense data grouping (#6156).
  10380. if (interval <= timeUnits.hour && tickPositions.length < 10000) {
  10381. tickPositions.forEach(function (t) {
  10382. if (
  10383. // Speed optimization, no need to run dateFormat unless
  10384. // we're on a full or half hour
  10385. t % 1800000 === 0 &&
  10386. // Check for local or global midnight
  10387. time.dateFormat('%H%M%S%L', t) === '000000000') {
  10388. higherRanks[t] = 'day';
  10389. }
  10390. });
  10391. }
  10392. }
  10393. // record information on the chosen unit - for dynamic label formatter
  10394. tickPositions.info = extend(normalizedInterval, {
  10395. higherRanks: higherRanks,
  10396. totalRange: interval * count
  10397. });
  10398. return tickPositions;
  10399. };
  10400. return Time;
  10401. }());
  10402. H.Time = Time;
  10403. return H.Time;
  10404. });
  10405. _registerModule(_modules, 'Core/Options.js', [_modules['Core/Globals.js'], _modules['Core/Time.js'], _modules['Core/Color.js'], _modules['Core/Utilities.js']], function (H, Time, Color, U) {
  10406. /* *
  10407. *
  10408. * (c) 2010-2020 Torstein Honsi
  10409. *
  10410. * License: www.highcharts.com/license
  10411. *
  10412. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10413. *
  10414. * */
  10415. /**
  10416. * @typedef {"plotBox"|"spacingBox"} Highcharts.ButtonRelativeToValue
  10417. */
  10418. /**
  10419. * Gets fired when a series is added to the chart after load time, using the
  10420. * `addSeries` method. Returning `false` prevents the series from being added.
  10421. *
  10422. * @callback Highcharts.ChartAddSeriesCallbackFunction
  10423. *
  10424. * @param {Highcharts.Chart} this
  10425. * The chart on which the event occured.
  10426. *
  10427. * @param {Highcharts.ChartAddSeriesEventObject} event
  10428. * The event that occured.
  10429. */
  10430. /**
  10431. * Contains common event information. Through the `options` property you can
  10432. * access the series options that were passed to the `addSeries` method.
  10433. *
  10434. * @interface Highcharts.ChartAddSeriesEventObject
  10435. */ /**
  10436. * The series options that were passed to the `addSeries` method.
  10437. * @name Highcharts.ChartAddSeriesEventObject#options
  10438. * @type {Highcharts.SeriesOptionsType}
  10439. */ /**
  10440. * Prevents the default behaviour of the event.
  10441. * @name Highcharts.ChartAddSeriesEventObject#preventDefault
  10442. * @type {Function}
  10443. */ /**
  10444. * The event target.
  10445. * @name Highcharts.ChartAddSeriesEventObject#target
  10446. * @type {Highcharts.Chart}
  10447. */ /**
  10448. * The event type.
  10449. * @name Highcharts.ChartAddSeriesEventObject#type
  10450. * @type {"addSeries"}
  10451. */
  10452. /**
  10453. * Gets fired when clicking on the plot background.
  10454. *
  10455. * @callback Highcharts.ChartClickCallbackFunction
  10456. *
  10457. * @param {Highcharts.Chart} this
  10458. * The chart on which the event occured.
  10459. *
  10460. * @param {Highcharts.PointerEventObject} event
  10461. * The event that occured.
  10462. */
  10463. /**
  10464. * Contains an axes of the clicked spot.
  10465. *
  10466. * @interface Highcharts.ChartClickEventAxisObject
  10467. */ /**
  10468. * Axis at the clicked spot.
  10469. * @name Highcharts.ChartClickEventAxisObject#axis
  10470. * @type {Highcharts.Axis}
  10471. */ /**
  10472. * Axis value at the clicked spot.
  10473. * @name Highcharts.ChartClickEventAxisObject#value
  10474. * @type {number}
  10475. */
  10476. /**
  10477. * Contains information about the clicked spot on the chart. Remember the unit
  10478. * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
  10479. *
  10480. * @interface Highcharts.ChartClickEventObject
  10481. * @extends Highcharts.PointerEventObject
  10482. */ /**
  10483. * Information about the x-axis on the clicked spot.
  10484. * @name Highcharts.ChartClickEventObject#xAxis
  10485. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  10486. */ /**
  10487. * Information about the y-axis on the clicked spot.
  10488. * @name Highcharts.ChartClickEventObject#yAxis
  10489. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  10490. */ /**
  10491. * Information about the z-axis on the clicked spot.
  10492. * @name Highcharts.ChartClickEventObject#zAxis
  10493. * @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
  10494. */
  10495. /**
  10496. * Gets fired when the chart is finished loading.
  10497. *
  10498. * @callback Highcharts.ChartLoadCallbackFunction
  10499. *
  10500. * @param {Highcharts.Chart} this
  10501. * The chart on which the event occured.
  10502. *
  10503. * @param {global.Event} event
  10504. * The event that occured.
  10505. */
  10506. /**
  10507. * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
  10508. * after an axis, series or point is modified with the `redraw` option set to
  10509. * `true`.
  10510. *
  10511. * @callback Highcharts.ChartRedrawCallbackFunction
  10512. *
  10513. * @param {Highcharts.Chart} this
  10514. * The chart on which the event occured.
  10515. *
  10516. * @param {global.Event} event
  10517. * The event that occured.
  10518. */
  10519. /**
  10520. * Gets fired after initial load of the chart (directly after the `load` event),
  10521. * and after each redraw (directly after the `redraw` event).
  10522. *
  10523. * @callback Highcharts.ChartRenderCallbackFunction
  10524. *
  10525. * @param {Highcharts.Chart} this
  10526. * The chart on which the event occured.
  10527. *
  10528. * @param {global.Event} event
  10529. * The event that occured.
  10530. */
  10531. /**
  10532. * Gets fired when an area of the chart has been selected. The default action
  10533. * for the selection event is to zoom the chart to the selected area. It can be
  10534. * prevented by calling `event.preventDefault()` or return false.
  10535. *
  10536. * @callback Highcharts.ChartSelectionCallbackFunction
  10537. *
  10538. * @param {Highcharts.Chart} this
  10539. * The chart on which the event occured.
  10540. *
  10541. * @param {global.ChartSelectionContextObject} event
  10542. * Event informations
  10543. *
  10544. * @return {boolean|undefined}
  10545. * Return false to prevent the default action, usually zoom.
  10546. */
  10547. /**
  10548. * The primary axes are `xAxis[0]` and `yAxis[0]`. Remember the unit of a
  10549. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  10550. *
  10551. * @interface Highcharts.ChartSelectionContextObject
  10552. * @extends global.Event
  10553. */ /**
  10554. * Arrays containing the axes of each dimension and each axis' min and max
  10555. * values.
  10556. * @name Highcharts.ChartSelectionContextObject#xAxis
  10557. * @type {Array<Highcharts.ChartSelectionAxisContextObject>}
  10558. */ /**
  10559. * Arrays containing the axes of each dimension and each axis' min and max
  10560. * values.
  10561. * @name Highcharts.ChartSelectionContextObject#yAxis
  10562. * @type {Array<Highcharts.ChartSelectionAxisContextObject>}
  10563. */
  10564. /**
  10565. * Axis context of the selection.
  10566. *
  10567. * @interface Highcharts.ChartSelectionAxisContextObject
  10568. */ /**
  10569. * The selected Axis.
  10570. * @name Highcharts.ChartSelectionAxisContextObject#axis
  10571. * @type {Highcharts.Axis}
  10572. */ /**
  10573. * The maximum axis value, either automatic or set manually.
  10574. * @name Highcharts.ChartSelectionAxisContextObject#max
  10575. * @type {number}
  10576. */ /**
  10577. * The minimum axis value, either automatic or set manually.
  10578. * @name Highcharts.ChartSelectionAxisContextObject#min
  10579. * @type {number}
  10580. */
  10581. var color = Color.parse;
  10582. var merge = U.merge;
  10583. var isTouchDevice = H.isTouchDevice,
  10584. svg = H.svg;
  10585. /* ************************************************************************** *
  10586. * Handle the options *
  10587. * ************************************************************************** */
  10588. /**
  10589. * Global default settings.
  10590. *
  10591. * @name Highcharts.defaultOptions
  10592. * @type {Highcharts.Options}
  10593. */ /**
  10594. * @optionparent
  10595. */
  10596. H.defaultOptions = {
  10597. /**
  10598. * An array containing the default colors for the chart's series. When
  10599. * all colors are used, new colors are pulled from the start again.
  10600. *
  10601. * Default colors can also be set on a series or series.type basis,
  10602. * see [column.colors](#plotOptions.column.colors),
  10603. * [pie.colors](#plotOptions.pie.colors).
  10604. *
  10605. * In styled mode, the colors option doesn't exist. Instead, colors
  10606. * are defined in CSS and applied either through series or point class
  10607. * names, or through the [chart.colorCount](#chart.colorCount) option.
  10608. *
  10609. *
  10610. * ### Legacy
  10611. *
  10612. * In Highcharts 3.x, the default colors were:
  10613. * ```js
  10614. * colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce',
  10615. * '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']
  10616. * ```
  10617. *
  10618. * In Highcharts 2.x, the default colors were:
  10619. * ```js
  10620. * colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE',
  10621. * '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92']
  10622. * ```
  10623. *
  10624. * @sample {highcharts} highcharts/chart/colors/
  10625. * Assign a global color theme
  10626. *
  10627. * @type {Array<Highcharts.ColorString>}
  10628. * @default ["#7cb5ec", "#434348", "#90ed7d", "#f7a35c", "#8085e9",
  10629. * "#f15c80", "#e4d354", "#2b908f", "#f45b5b", "#91e8e1"]
  10630. */
  10631. colors: '#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1'.split(' '),
  10632. /**
  10633. * Styled mode only. Configuration object for adding SVG definitions for
  10634. * reusable elements. See [gradients, shadows and
  10635. * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
  10636. * for more information and code examples.
  10637. *
  10638. * @type {*}
  10639. * @since 5.0.0
  10640. * @apioption defs
  10641. */
  10642. /**
  10643. * @ignore-option
  10644. */
  10645. symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
  10646. /**
  10647. * The language object is global and it can't be set on each chart
  10648. * initialization. Instead, use `Highcharts.setOptions` to set it before any
  10649. * chart is initialized.
  10650. *
  10651. * ```js
  10652. * Highcharts.setOptions({
  10653. * lang: {
  10654. * months: [
  10655. * 'Janvier', 'Février', 'Mars', 'Avril',
  10656. * 'Mai', 'Juin', 'Juillet', 'Août',
  10657. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  10658. * ],
  10659. * weekdays: [
  10660. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  10661. * 'Jeudi', 'Vendredi', 'Samedi'
  10662. * ]
  10663. * }
  10664. * });
  10665. * ```
  10666. */
  10667. lang: {
  10668. /**
  10669. * The loading text that appears when the chart is set into the loading
  10670. * state following a call to `chart.showLoading`.
  10671. */
  10672. loading: 'Loading...',
  10673. /**
  10674. * An array containing the months names. Corresponds to the `%B` format
  10675. * in `Highcharts.dateFormat()`.
  10676. *
  10677. * @type {Array<string>}
  10678. * @default ["January", "February", "March", "April", "May", "June",
  10679. * "July", "August", "September", "October", "November",
  10680. * "December"]
  10681. */
  10682. months: [
  10683. 'January', 'February', 'March', 'April', 'May', 'June', 'July',
  10684. 'August', 'September', 'October', 'November', 'December'
  10685. ],
  10686. /**
  10687. * An array containing the months names in abbreviated form. Corresponds
  10688. * to the `%b` format in `Highcharts.dateFormat()`.
  10689. *
  10690. * @type {Array<string>}
  10691. * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  10692. * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  10693. */
  10694. shortMonths: [
  10695. 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
  10696. 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
  10697. ],
  10698. /**
  10699. * An array containing the weekday names.
  10700. *
  10701. * @type {Array<string>}
  10702. * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  10703. * "Friday", "Saturday"]
  10704. */
  10705. weekdays: [
  10706. 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
  10707. 'Thursday', 'Friday', 'Saturday'
  10708. ],
  10709. /**
  10710. * Short week days, starting Sunday. If not specified, Highcharts uses
  10711. * the first three letters of the `lang.weekdays` option.
  10712. *
  10713. * @sample highcharts/lang/shortweekdays/
  10714. * Finnish two-letter abbreviations
  10715. *
  10716. * @type {Array<string>}
  10717. * @since 4.2.4
  10718. * @apioption lang.shortWeekdays
  10719. */
  10720. /**
  10721. * What to show in a date field for invalid dates. Defaults to an empty
  10722. * string.
  10723. *
  10724. * @type {string}
  10725. * @since 4.1.8
  10726. * @product highcharts highstock
  10727. * @apioption lang.invalidDate
  10728. */
  10729. /**
  10730. * The title appearing on hovering the zoom in button. The text itself
  10731. * defaults to "+" and can be changed in the button options.
  10732. *
  10733. * @type {string}
  10734. * @default Zoom in
  10735. * @product highmaps
  10736. * @apioption lang.zoomIn
  10737. */
  10738. /**
  10739. * The title appearing on hovering the zoom out button. The text itself
  10740. * defaults to "-" and can be changed in the button options.
  10741. *
  10742. * @type {string}
  10743. * @default Zoom out
  10744. * @product highmaps
  10745. * @apioption lang.zoomOut
  10746. */
  10747. /**
  10748. * The default decimal point used in the `Highcharts.numberFormat`
  10749. * method unless otherwise specified in the function arguments.
  10750. *
  10751. * @since 1.2.2
  10752. */
  10753. decimalPoint: '.',
  10754. /**
  10755. * [Metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix) used
  10756. * to shorten high numbers in axis labels. Replacing any of the
  10757. * positions with `null` causes the full number to be written. Setting
  10758. * `numericSymbols` to `null` disables shortening altogether.
  10759. *
  10760. * @sample {highcharts} highcharts/lang/numericsymbols/
  10761. * Replacing the symbols with text
  10762. * @sample {highstock} highcharts/lang/numericsymbols/
  10763. * Replacing the symbols with text
  10764. *
  10765. * @type {Array<string>}
  10766. * @default ["k", "M", "G", "T", "P", "E"]
  10767. * @since 2.3.0
  10768. */
  10769. numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
  10770. /**
  10771. * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
  10772. * Use 10000 for Japanese, Korean and various Chinese locales, which
  10773. * use symbols for 10^4, 10^8 and 10^12.
  10774. *
  10775. * @sample highcharts/lang/numericsymbolmagnitude/
  10776. * 10000 magnitude for Japanese
  10777. *
  10778. * @type {number}
  10779. * @default 1000
  10780. * @since 5.0.3
  10781. * @apioption lang.numericSymbolMagnitude
  10782. */
  10783. /**
  10784. * The text for the label appearing when a chart is zoomed.
  10785. *
  10786. * @since 1.2.4
  10787. */
  10788. resetZoom: 'Reset zoom',
  10789. /**
  10790. * The tooltip title for the label appearing when a chart is zoomed.
  10791. *
  10792. * @since 1.2.4
  10793. */
  10794. resetZoomTitle: 'Reset zoom level 1:1',
  10795. /**
  10796. * The default thousands separator used in the `Highcharts.numberFormat`
  10797. * method unless otherwise specified in the function arguments. Defaults
  10798. * to a single space character, which is recommended in
  10799. * [ISO 31-0](https://en.wikipedia.org/wiki/ISO_31-0#Numbers) and works
  10800. * across Anglo-American and continental European languages.
  10801. *
  10802. * @default \u0020
  10803. * @since 1.2.2
  10804. */
  10805. thousandsSep: ' '
  10806. },
  10807. /**
  10808. * Global options that don't apply to each chart. These options, like
  10809. * the `lang` options, must be set using the `Highcharts.setOptions`
  10810. * method.
  10811. *
  10812. * ```js
  10813. * Highcharts.setOptions({
  10814. * global: {
  10815. * useUTC: false
  10816. * }
  10817. * });
  10818. * ```
  10819. */
  10820. /**
  10821. * _Canvg rendering for Android 2.x is removed as of Highcharts 5.0\.
  10822. * Use the [libURL](#exporting.libURL) option to configure exporting._
  10823. *
  10824. * The URL to the additional file to lazy load for Android 2.x devices.
  10825. * These devices don't support SVG, so we download a helper file that
  10826. * contains [canvg](https://github.com/canvg/canvg), its dependency
  10827. * rbcolor, and our own CanVG Renderer class. To avoid hotlinking to
  10828. * our site, you can install canvas-tools.js on your own server and
  10829. * change this option accordingly.
  10830. *
  10831. * @deprecated
  10832. *
  10833. * @type {string}
  10834. * @default https://code.highcharts.com/{version}/modules/canvas-tools.js
  10835. * @product highcharts highmaps
  10836. * @apioption global.canvasToolsURL
  10837. */
  10838. /**
  10839. * This option is deprecated since v6.0.5. Instead, use
  10840. * [time.useUTC](#time.useUTC) that supports individual time settings
  10841. * per chart.
  10842. *
  10843. * @deprecated
  10844. *
  10845. * @type {boolean}
  10846. * @apioption global.useUTC
  10847. */
  10848. /**
  10849. * This option is deprecated since v6.0.5. Instead, use
  10850. * [time.Date](#time.Date) that supports individual time settings
  10851. * per chart.
  10852. *
  10853. * @deprecated
  10854. *
  10855. * @type {Function}
  10856. * @product highcharts highstock
  10857. * @apioption global.Date
  10858. */
  10859. /**
  10860. * This option is deprecated since v6.0.5. Instead, use
  10861. * [time.getTimezoneOffset](#time.getTimezoneOffset) that supports
  10862. * individual time settings per chart.
  10863. *
  10864. * @deprecated
  10865. *
  10866. * @type {Function}
  10867. * @product highcharts highstock
  10868. * @apioption global.getTimezoneOffset
  10869. */
  10870. /**
  10871. * This option is deprecated since v6.0.5. Instead, use
  10872. * [time.timezone](#time.timezone) that supports individual time
  10873. * settings per chart.
  10874. *
  10875. * @deprecated
  10876. *
  10877. * @type {string}
  10878. * @product highcharts highstock
  10879. * @apioption global.timezone
  10880. */
  10881. /**
  10882. * This option is deprecated since v6.0.5. Instead, use
  10883. * [time.timezoneOffset](#time.timezoneOffset) that supports individual
  10884. * time settings per chart.
  10885. *
  10886. * @deprecated
  10887. *
  10888. * @type {number}
  10889. * @product highcharts highstock
  10890. * @apioption global.timezoneOffset
  10891. */
  10892. global: {},
  10893. /**
  10894. * Time options that can apply globally or to individual charts. These
  10895. * settings affect how `datetime` axes are laid out, how tooltips are
  10896. * formatted, how series
  10897. * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
  10898. * the Highstock range selector handles time.
  10899. *
  10900. * The common use case is that all charts in the same Highcharts object
  10901. * share the same time settings, in which case the global settings are set
  10902. * using `setOptions`.
  10903. *
  10904. * ```js
  10905. * // Apply time settings globally
  10906. * Highcharts.setOptions({
  10907. * time: {
  10908. * timezone: 'Europe/London'
  10909. * }
  10910. * });
  10911. * // Apply time settings by instance
  10912. * var chart = Highcharts.chart('container', {
  10913. * time: {
  10914. * timezone: 'America/New_York'
  10915. * },
  10916. * series: [{
  10917. * data: [1, 4, 3, 5]
  10918. * }]
  10919. * });
  10920. *
  10921. * // Use the Time object
  10922. * console.log(
  10923. * 'Current time in New York',
  10924. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  10925. * );
  10926. * ```
  10927. *
  10928. * Since v6.0.5, the time options were moved from the `global` obect to the
  10929. * `time` object, and time options can be set on each individual chart.
  10930. *
  10931. * @sample {highcharts|highstock}
  10932. * highcharts/time/timezone/
  10933. * Set the timezone globally
  10934. * @sample {highcharts}
  10935. * highcharts/time/individual/
  10936. * Set the timezone per chart instance
  10937. * @sample {highstock}
  10938. * stock/time/individual/
  10939. * Set the timezone per chart instance
  10940. *
  10941. * @since 6.0.5
  10942. * @optionparent time
  10943. */
  10944. time: {
  10945. /**
  10946. * A custom `Date` class for advanced date handling. For example,
  10947. * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
  10948. * handle Jalali dates.
  10949. *
  10950. * @type {*}
  10951. * @since 4.0.4
  10952. * @product highcharts highstock gantt
  10953. */
  10954. Date: void 0,
  10955. /**
  10956. * A callback to return the time zone offset for a given datetime. It
  10957. * takes the timestamp in terms of milliseconds since January 1 1970,
  10958. * and returns the timezone offset in minutes. This provides a hook
  10959. * for drawing time based charts in specific time zones using their
  10960. * local DST crossover dates, with the help of external libraries.
  10961. *
  10962. * @see [global.timezoneOffset](#global.timezoneOffset)
  10963. *
  10964. * @sample {highcharts|highstock} highcharts/time/gettimezoneoffset/
  10965. * Use moment.js to draw Oslo time regardless of browser locale
  10966. *
  10967. * @type {Highcharts.TimezoneOffsetCallbackFunction}
  10968. * @since 4.1.0
  10969. * @product highcharts highstock gantt
  10970. */
  10971. getTimezoneOffset: void 0,
  10972. /**
  10973. * Requires [moment.js](https://momentjs.com/). If the timezone option
  10974. * is specified, it creates a default
  10975. * [getTimezoneOffset](#time.getTimezoneOffset) function that looks
  10976. * up the specified timezone in moment.js. If moment.js is not included,
  10977. * this throws a Highcharts error in the console, but does not crash the
  10978. * chart.
  10979. *
  10980. * @see [getTimezoneOffset](#time.getTimezoneOffset)
  10981. *
  10982. * @sample {highcharts|highstock} highcharts/time/timezone/
  10983. * Europe/Oslo
  10984. *
  10985. * @type {string}
  10986. * @since 5.0.7
  10987. * @product highcharts highstock gantt
  10988. */
  10989. timezone: void 0,
  10990. /**
  10991. * The timezone offset in minutes. Positive values are west, negative
  10992. * values are east of UTC, as in the ECMAScript
  10993. * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
  10994. * method. Use this to display UTC based data in a predefined time zone.
  10995. *
  10996. * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
  10997. *
  10998. * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
  10999. * Timezone offset
  11000. *
  11001. * @since 3.0.8
  11002. * @product highcharts highstock gantt
  11003. */
  11004. timezoneOffset: 0,
  11005. /**
  11006. * Whether to use UTC time for axis scaling, tickmark placement and
  11007. * time display in `Highcharts.dateFormat`. Advantages of using UTC
  11008. * is that the time displays equally regardless of the user agent's
  11009. * time zone settings. Local time can be used when the data is loaded
  11010. * in real time or when correct Daylight Saving Time transitions are
  11011. * required.
  11012. *
  11013. * @sample {highcharts} highcharts/time/useutc-true/
  11014. * True by default
  11015. * @sample {highcharts} highcharts/time/useutc-false/
  11016. * False
  11017. */
  11018. useUTC: true
  11019. },
  11020. /**
  11021. * General options for the chart.
  11022. */
  11023. chart: {
  11024. /**
  11025. * Default `mapData` for all series. If set to a string, it functions
  11026. * as an index into the `Highcharts.maps` array. Otherwise it is
  11027. * interpreted as map data.
  11028. *
  11029. * @see [mapData](#series.map.mapData)
  11030. *
  11031. * @sample maps/demo/geojson
  11032. * Loading geoJSON data
  11033. * @sample maps/chart/topojson
  11034. * Loading topoJSON converted to geoJSON
  11035. *
  11036. * @type {string|Array<*>|Highcharts.GeoJSON}
  11037. * @since 5.0.0
  11038. * @product highmaps
  11039. * @apioption chart.map
  11040. */
  11041. /**
  11042. * Set lat/lon transformation definitions for the chart. If not defined,
  11043. * these are extracted from the map data.
  11044. *
  11045. * @type {*}
  11046. * @since 5.0.0
  11047. * @product highmaps
  11048. * @apioption chart.mapTransforms
  11049. */
  11050. /**
  11051. * When using multiple axis, the ticks of two or more opposite axes
  11052. * will automatically be aligned by adding ticks to the axis or axes
  11053. * with the least ticks, as if `tickAmount` were specified.
  11054. *
  11055. * This can be prevented by setting `alignTicks` to false. If the grid
  11056. * lines look messy, it's a good idea to hide them for the secondary
  11057. * axis by setting `gridLineWidth` to 0.
  11058. *
  11059. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  11060. * then the `alignTicks ` will be disabled for the Axis.
  11061. *
  11062. * Disabled for logarithmic axes.
  11063. *
  11064. * @sample {highcharts} highcharts/chart/alignticks-true/
  11065. * True by default
  11066. * @sample {highcharts} highcharts/chart/alignticks-false/
  11067. * False
  11068. * @sample {highstock} stock/chart/alignticks-true/
  11069. * True by default
  11070. * @sample {highstock} stock/chart/alignticks-false/
  11071. * False
  11072. *
  11073. * @type {boolean}
  11074. * @default true
  11075. * @product highcharts highstock gantt
  11076. * @apioption chart.alignTicks
  11077. */
  11078. /**
  11079. * Set the overall animation for all chart updating. Animation can be
  11080. * disabled throughout the chart by setting it to false here. It can
  11081. * be overridden for each individual API method as a function parameter.
  11082. * The only animation not affected by this option is the initial series
  11083. * animation, see [plotOptions.series.animation](
  11084. * #plotOptions.series.animation).
  11085. *
  11086. * The animation can either be set as a boolean or a configuration
  11087. * object. If `true`, it will use the 'swing' jQuery easing and a
  11088. * duration of 500 ms. If used as a configuration object, the following
  11089. * properties are supported:
  11090. *
  11091. * - `defer`: The animation delay time in milliseconds.
  11092. *
  11093. * - `duration`: The duration of the animation in milliseconds.
  11094. *
  11095. * - `easing`: A string reference to an easing function set on the
  11096. * `Math` object. See
  11097. * [the easing demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
  11098. *
  11099. * When zooming on a series with less than 100 points, the chart redraw
  11100. * will be done with animation, but in case of more data points, it is
  11101. * necessary to set this option to ensure animation on zoom.
  11102. *
  11103. * @sample {highcharts} highcharts/chart/animation-none/
  11104. * Updating with no animation
  11105. * @sample {highcharts} highcharts/chart/animation-duration/
  11106. * With a longer duration
  11107. * @sample {highcharts} highcharts/chart/animation-easing/
  11108. * With a jQuery UI easing
  11109. * @sample {highmaps} maps/chart/animation-none/
  11110. * Updating with no animation
  11111. * @sample {highmaps} maps/chart/animation-duration/
  11112. * With a longer duration
  11113. *
  11114. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  11115. * @default undefined
  11116. * @apioption chart.animation
  11117. */
  11118. /**
  11119. * A CSS class name to apply to the charts container `div`, allowing
  11120. * unique CSS styling for each chart.
  11121. *
  11122. * @type {string}
  11123. * @apioption chart.className
  11124. */
  11125. /**
  11126. * Event listeners for the chart.
  11127. *
  11128. * @apioption chart.events
  11129. */
  11130. /**
  11131. * Fires when a series is added to the chart after load time, using the
  11132. * `addSeries` method. One parameter, `event`, is passed to the
  11133. * function, containing common event information. Through
  11134. * `event.options` you can access the series options that were passed to
  11135. * the `addSeries` method. Returning false prevents the series from
  11136. * being added.
  11137. *
  11138. * @sample {highcharts} highcharts/chart/events-addseries/
  11139. * Alert on add series
  11140. * @sample {highstock} stock/chart/events-addseries/
  11141. * Alert on add series
  11142. *
  11143. * @type {Highcharts.ChartAddSeriesCallbackFunction}
  11144. * @since 1.2.0
  11145. * @context Highcharts.Chart
  11146. * @apioption chart.events.addSeries
  11147. */
  11148. /**
  11149. * Fires when clicking on the plot background. One parameter, `event`,
  11150. * is passed to the function, containing common event information.
  11151. *
  11152. * Information on the clicked spot can be found through `event.xAxis`
  11153. * and `event.yAxis`, which are arrays containing the axes of each
  11154. * dimension and each axis' value at the clicked spot. The primary axes
  11155. * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  11156. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  11157. *
  11158. * ```js
  11159. * click: function(e) {
  11160. * console.log(
  11161. * Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
  11162. * e.yAxis[0].value
  11163. * )
  11164. * }
  11165. * ```
  11166. *
  11167. * @sample {highcharts} highcharts/chart/events-click/
  11168. * Alert coordinates on click
  11169. * @sample {highcharts} highcharts/chart/events-container/
  11170. * Alternatively, attach event to container
  11171. * @sample {highstock} stock/chart/events-click/
  11172. * Alert coordinates on click
  11173. * @sample {highstock} highcharts/chart/events-container/
  11174. * Alternatively, attach event to container
  11175. * @sample {highmaps} maps/chart/events-click/
  11176. * Record coordinates on click
  11177. * @sample {highmaps} highcharts/chart/events-container/
  11178. * Alternatively, attach event to container
  11179. *
  11180. * @type {Highcharts.ChartClickCallbackFunction}
  11181. * @since 1.2.0
  11182. * @context Highcharts.Chart
  11183. * @apioption chart.events.click
  11184. */
  11185. /**
  11186. * Fires when the chart is finished loading. Since v4.2.2, it also waits
  11187. * for images to be loaded, for example from point markers. One
  11188. * parameter, `event`, is passed to the function, containing common
  11189. * event information.
  11190. *
  11191. * There is also a second parameter to the chart constructor where a
  11192. * callback function can be passed to be executed on chart.load.
  11193. *
  11194. * @sample {highcharts} highcharts/chart/events-load/
  11195. * Alert on chart load
  11196. * @sample {highstock} stock/chart/events-load/
  11197. * Alert on chart load
  11198. * @sample {highmaps} maps/chart/events-load/
  11199. * Add series on chart load
  11200. *
  11201. * @type {Highcharts.ChartLoadCallbackFunction}
  11202. * @context Highcharts.Chart
  11203. * @apioption chart.events.load
  11204. */
  11205. /**
  11206. * Fires when the chart is redrawn, either after a call to
  11207. * `chart.redraw()` or after an axis, series or point is modified with
  11208. * the `redraw` option set to `true`. One parameter, `event`, is passed
  11209. * to the function, containing common event information.
  11210. *
  11211. * @sample {highcharts} highcharts/chart/events-redraw/
  11212. * Alert on chart redraw
  11213. * @sample {highstock} stock/chart/events-redraw/
  11214. * Alert on chart redraw when adding a series or moving the
  11215. * zoomed range
  11216. * @sample {highmaps} maps/chart/events-redraw/
  11217. * Set subtitle on chart redraw
  11218. *
  11219. * @type {Highcharts.ChartRedrawCallbackFunction}
  11220. * @since 1.2.0
  11221. * @context Highcharts.Chart
  11222. * @apioption chart.events.redraw
  11223. */
  11224. /**
  11225. * Fires after initial load of the chart (directly after the `load`
  11226. * event), and after each redraw (directly after the `redraw` event).
  11227. *
  11228. * @type {Highcharts.ChartRenderCallbackFunction}
  11229. * @since 5.0.7
  11230. * @context Highcharts.Chart
  11231. * @apioption chart.events.render
  11232. */
  11233. /**
  11234. * Fires when an area of the chart has been selected. Selection is
  11235. * enabled by setting the chart's zoomType. One parameter, `event`, is
  11236. * passed to the function, containing common event information. The
  11237. * default action for the selection event is to zoom the chart to the
  11238. * selected area. It can be prevented by calling
  11239. * `event.preventDefault()` or return false.
  11240. *
  11241. * Information on the selected area can be found through `event.xAxis`
  11242. * and `event.yAxis`, which are arrays containing the axes of each
  11243. * dimension and each axis' min and max values. The primary axes are
  11244. * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  11245. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  11246. *
  11247. * ```js
  11248. * selection: function(event) {
  11249. * // log the min and max of the primary, datetime x-axis
  11250. * console.log(
  11251. * Highcharts.dateFormat(
  11252. * '%Y-%m-%d %H:%M:%S',
  11253. * event.xAxis[0].min
  11254. * ),
  11255. * Highcharts.dateFormat(
  11256. * '%Y-%m-%d %H:%M:%S',
  11257. * event.xAxis[0].max
  11258. * )
  11259. * );
  11260. * // log the min and max of the y axis
  11261. * console.log(event.yAxis[0].min, event.yAxis[0].max);
  11262. * }
  11263. * ```
  11264. *
  11265. * @sample {highcharts} highcharts/chart/events-selection/
  11266. * Report on selection and reset
  11267. * @sample {highcharts} highcharts/chart/events-selection-points/
  11268. * Select a range of points through a drag selection
  11269. * @sample {highstock} stock/chart/events-selection/
  11270. * Report on selection and reset
  11271. * @sample {highstock} highcharts/chart/events-selection-points/
  11272. * Select a range of points through a drag selection
  11273. * (Highcharts)
  11274. *
  11275. * @type {Highcharts.ChartSelectionCallbackFunction}
  11276. * @apioption chart.events.selection
  11277. */
  11278. /**
  11279. * The margin between the outer edge of the chart and the plot area.
  11280. * The numbers in the array designate top, right, bottom and left
  11281. * respectively. Use the options `marginTop`, `marginRight`,
  11282. * `marginBottom` and `marginLeft` for shorthand setting of one option.
  11283. *
  11284. * By default there is no margin. The actual space is dynamically
  11285. * calculated from the offset of axis labels, axis title, title,
  11286. * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
  11287. * `spacingBottom` and `spacingLeft` options.
  11288. *
  11289. * @sample {highcharts} highcharts/chart/margins-zero/
  11290. * Zero margins
  11291. * @sample {highstock} stock/chart/margin-zero/
  11292. * Zero margins
  11293. *
  11294. * @type {number|Array<number>}
  11295. * @apioption chart.margin
  11296. */
  11297. /**
  11298. * The margin between the bottom outer edge of the chart and the plot
  11299. * area. Use this to set a fixed pixel value for the margin as opposed
  11300. * to the default dynamic margin. See also `spacingBottom`.
  11301. *
  11302. * @sample {highcharts} highcharts/chart/marginbottom/
  11303. * 100px bottom margin
  11304. * @sample {highstock} stock/chart/marginbottom/
  11305. * 100px bottom margin
  11306. * @sample {highmaps} maps/chart/margin/
  11307. * 100px margins
  11308. *
  11309. * @type {number}
  11310. * @since 2.0
  11311. * @apioption chart.marginBottom
  11312. */
  11313. /**
  11314. * The margin between the left outer edge of the chart and the plot
  11315. * area. Use this to set a fixed pixel value for the margin as opposed
  11316. * to the default dynamic margin. See also `spacingLeft`.
  11317. *
  11318. * @sample {highcharts} highcharts/chart/marginleft/
  11319. * 150px left margin
  11320. * @sample {highstock} stock/chart/marginleft/
  11321. * 150px left margin
  11322. * @sample {highmaps} maps/chart/margin/
  11323. * 100px margins
  11324. *
  11325. * @type {number}
  11326. * @since 2.0
  11327. * @apioption chart.marginLeft
  11328. */
  11329. /**
  11330. * The margin between the right outer edge of the chart and the plot
  11331. * area. Use this to set a fixed pixel value for the margin as opposed
  11332. * to the default dynamic margin. See also `spacingRight`.
  11333. *
  11334. * @sample {highcharts} highcharts/chart/marginright/
  11335. * 100px right margin
  11336. * @sample {highstock} stock/chart/marginright/
  11337. * 100px right margin
  11338. * @sample {highmaps} maps/chart/margin/
  11339. * 100px margins
  11340. *
  11341. * @type {number}
  11342. * @since 2.0
  11343. * @apioption chart.marginRight
  11344. */
  11345. /**
  11346. * The margin between the top outer edge of the chart and the plot area.
  11347. * Use this to set a fixed pixel value for the margin as opposed to
  11348. * the default dynamic margin. See also `spacingTop`.
  11349. *
  11350. * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
  11351. * @sample {highstock} stock/chart/margintop/
  11352. * 100px top margin
  11353. * @sample {highmaps} maps/chart/margin/
  11354. * 100px margins
  11355. *
  11356. * @type {number}
  11357. * @since 2.0
  11358. * @apioption chart.marginTop
  11359. */
  11360. /**
  11361. * Callback function to override the default function that formats all
  11362. * the numbers in the chart. Returns a string with the formatted number.
  11363. *
  11364. * @sample highcharts/members/highcharts-numberformat
  11365. * Arabic digits in Highcharts
  11366. * @type {Highcharts.NumberFormatterCallbackFunction}
  11367. * @since 8.0.0
  11368. * @apioption chart.numberFormatter
  11369. */
  11370. /**
  11371. * Allows setting a key to switch between zooming and panning. Can be
  11372. * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
  11373. * key on Windows) or `shift`. The keys are mapped directly to the key
  11374. * properties of the click event argument (`event.altKey`,
  11375. * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
  11376. *
  11377. * @type {string}
  11378. * @since 4.0.3
  11379. * @product highcharts gantt
  11380. * @validvalue ["alt", "ctrl", "meta", "shift"]
  11381. * @apioption chart.panKey
  11382. */
  11383. /**
  11384. * Allow panning in a chart. Best used with [panKey](#chart.panKey)
  11385. * to combine zooming and panning.
  11386. *
  11387. * On touch devices, when the [tooltip.followTouchMove](
  11388. * #tooltip.followTouchMove) option is `true` (default), panning
  11389. * requires two fingers. To allow panning with one finger, set
  11390. * `followTouchMove` to `false`.
  11391. *
  11392. * @sample {highcharts} highcharts/chart/pankey/ Zooming and panning
  11393. * @sample {highstock} stock/chart/panning/ Zooming and xy panning
  11394. *
  11395. * @product highcharts highstock gantt
  11396. * @apioption chart.panning
  11397. */
  11398. /**
  11399. * Enable or disable chart panning.
  11400. *
  11401. * @type {boolean}
  11402. * @default {highcharts} false
  11403. * @default {highstock} true
  11404. * @apioption chart.panning.enabled
  11405. */
  11406. /**
  11407. * Decides in what dimensions the user can pan the chart. Can be
  11408. * one of `x`, `y`, or `xy`.
  11409. *
  11410. * @sample {highcharts} highcharts/chart/panning-type
  11411. * Zooming and xy panning
  11412. *
  11413. * @type {string}
  11414. * @validvalue ["x", "y", "xy"]
  11415. * @default x
  11416. * @apioption chart.panning.type
  11417. */
  11418. /**
  11419. * Equivalent to [zoomType](#chart.zoomType), but for multitouch
  11420. * gestures only. By default, the `pinchType` is the same as the
  11421. * `zoomType` setting. However, pinching can be enabled separately in
  11422. * some cases, for example in stock charts where a mouse drag pans the
  11423. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  11424. * #tooltip.followTouchMove) is true, pinchType only applies to
  11425. * two-finger touches.
  11426. *
  11427. * @type {string}
  11428. * @default {highcharts} undefined
  11429. * @default {highstock} x
  11430. * @since 3.0
  11431. * @product highcharts highstock gantt
  11432. * @validvalue ["x", "y", "xy"]
  11433. * @apioption chart.pinchType
  11434. */
  11435. /**
  11436. * Whether to apply styled mode. When in styled mode, no presentational
  11437. * attributes or CSS are applied to the chart SVG. Instead, CSS rules
  11438. * are required to style the chart. The default style sheet is
  11439. * available from `https://code.highcharts.com/css/highcharts.css`.
  11440. *
  11441. * @type {boolean}
  11442. * @default false
  11443. * @since 7.0
  11444. * @apioption chart.styledMode
  11445. */
  11446. styledMode: false,
  11447. /**
  11448. * The corner radius of the outer chart border.
  11449. *
  11450. * @sample {highcharts} highcharts/chart/borderradius/
  11451. * 20px radius
  11452. * @sample {highstock} stock/chart/border/
  11453. * 10px radius
  11454. * @sample {highmaps} maps/chart/border/
  11455. * Border options
  11456. *
  11457. */
  11458. borderRadius: 0,
  11459. /**
  11460. * In styled mode, this sets how many colors the class names
  11461. * should rotate between. With ten colors, series (or points) are
  11462. * given class names like `highcharts-color-0`, `highcharts-color-0`
  11463. * [...] `highcharts-color-9`. The equivalent in non-styled mode
  11464. * is to set colors using the [colors](#colors) setting.
  11465. *
  11466. * @since 5.0.0
  11467. */
  11468. colorCount: 10,
  11469. /**
  11470. * Alias of `type`.
  11471. *
  11472. * @sample {highcharts} highcharts/chart/defaultseriestype/
  11473. * Bar
  11474. *
  11475. * @deprecated
  11476. *
  11477. * @product highcharts
  11478. */
  11479. defaultSeriesType: 'line',
  11480. /**
  11481. * If true, the axes will scale to the remaining visible series once
  11482. * one series is hidden. If false, hiding and showing a series will
  11483. * not affect the axes or the other series. For stacks, once one series
  11484. * within the stack is hidden, the rest of the stack will close in
  11485. * around it even if the axis is not affected.
  11486. *
  11487. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
  11488. * True by default
  11489. * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
  11490. * False
  11491. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
  11492. * True with stack
  11493. * @sample {highstock} stock/chart/ignorehiddenseries-true/
  11494. * True by default
  11495. * @sample {highstock} stock/chart/ignorehiddenseries-false/
  11496. * False
  11497. *
  11498. * @since 1.2.0
  11499. * @product highcharts highstock gantt
  11500. */
  11501. ignoreHiddenSeries: true,
  11502. /**
  11503. * Whether to invert the axes so that the x axis is vertical and y axis
  11504. * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
  11505. * by default.
  11506. *
  11507. * @productdesc {highcharts}
  11508. * If a bar series is present in the chart, it will be inverted
  11509. * automatically. Inverting the chart doesn't have an effect if there
  11510. * are no cartesian series in the chart, or if the chart is
  11511. * [polar](#chart.polar).
  11512. *
  11513. * @sample {highcharts} highcharts/chart/inverted/
  11514. * Inverted line
  11515. * @sample {highstock} stock/navigator/inverted/
  11516. * Inverted stock chart
  11517. *
  11518. * @type {boolean}
  11519. * @default false
  11520. * @product highcharts highstock gantt
  11521. * @apioption chart.inverted
  11522. */
  11523. /**
  11524. * The distance between the outer edge of the chart and the content,
  11525. * like title or legend, or axis title and labels if present. The
  11526. * numbers in the array designate top, right, bottom and left
  11527. * respectively. Use the options spacingTop, spacingRight, spacingBottom
  11528. * and spacingLeft options for shorthand setting of one option.
  11529. *
  11530. * @type {Array<number>}
  11531. * @see [chart.margin](#chart.margin)
  11532. * @default [10, 10, 15, 10]
  11533. * @since 3.0.6
  11534. */
  11535. spacing: [10, 10, 15, 10],
  11536. /**
  11537. * The button that appears after a selection zoom, allowing the user
  11538. * to reset zoom.
  11539. */
  11540. resetZoomButton: {
  11541. /**
  11542. * What frame the button placement should be related to. Can be
  11543. * either `plotBox` or `spacingBox`.
  11544. *
  11545. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  11546. * Relative to the chart
  11547. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  11548. * Relative to the chart
  11549. *
  11550. * @type {Highcharts.ButtonRelativeToValue}
  11551. * @default plot
  11552. * @since 2.2
  11553. * @apioption chart.resetZoomButton.relativeTo
  11554. */
  11555. /**
  11556. * A collection of attributes for the button. The object takes SVG
  11557. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  11558. * border radius. The theme also supports `style`, a collection of
  11559. * CSS properties for the text. Equivalent attributes for the hover
  11560. * state are given in `theme.states.hover`.
  11561. *
  11562. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  11563. * Theming the button
  11564. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  11565. * Theming the button
  11566. *
  11567. * @type {Highcharts.SVGAttributes}
  11568. * @since 2.2
  11569. */
  11570. theme: {
  11571. /** @internal */
  11572. zIndex: 6
  11573. },
  11574. /**
  11575. * The position of the button.
  11576. *
  11577. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  11578. * Above the plot area
  11579. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  11580. * Above the plot area
  11581. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  11582. * Above the plot area
  11583. *
  11584. * @type {Highcharts.AlignObject}
  11585. * @since 2.2
  11586. */
  11587. position: {
  11588. /**
  11589. * The horizontal alignment of the button.
  11590. */
  11591. align: 'right',
  11592. /**
  11593. * The horizontal offset of the button.
  11594. */
  11595. x: -10,
  11596. /**
  11597. * The vertical alignment of the button.
  11598. *
  11599. * @type {Highcharts.VerticalAlignValue}
  11600. * @default top
  11601. * @apioption chart.resetZoomButton.position.verticalAlign
  11602. */
  11603. /**
  11604. * The vertical offset of the button.
  11605. */
  11606. y: 10
  11607. }
  11608. },
  11609. /**
  11610. * The pixel width of the plot area border.
  11611. *
  11612. * @sample {highcharts} highcharts/chart/plotborderwidth/
  11613. * 1px border
  11614. * @sample {highstock} stock/chart/plotborder/
  11615. * 2px border
  11616. * @sample {highmaps} maps/chart/plotborder/
  11617. * Plot border options
  11618. *
  11619. * @type {number}
  11620. * @default 0
  11621. * @apioption chart.plotBorderWidth
  11622. */
  11623. /**
  11624. * Whether to apply a drop shadow to the plot area. Requires that
  11625. * plotBackgroundColor be set. The shadow can be an object configuration
  11626. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  11627. *
  11628. * @sample {highcharts} highcharts/chart/plotshadow/
  11629. * Plot shadow
  11630. * @sample {highstock} stock/chart/plotshadow/
  11631. * Plot shadow
  11632. * @sample {highmaps} maps/chart/plotborder/
  11633. * Plot border options
  11634. *
  11635. * @type {boolean|Highcharts.CSSObject}
  11636. * @default false
  11637. * @apioption chart.plotShadow
  11638. */
  11639. /**
  11640. * When true, cartesian charts like line, spline, area and column are
  11641. * transformed into the polar coordinate system. This produces _polar
  11642. * charts_, also known as _radar charts_.
  11643. *
  11644. * @sample {highcharts} highcharts/demo/polar/
  11645. * Polar chart
  11646. * @sample {highcharts} highcharts/demo/polar-wind-rose/
  11647. * Wind rose, stacked polar column chart
  11648. * @sample {highcharts} highcharts/demo/polar-spider/
  11649. * Spider web chart
  11650. * @sample {highcharts} highcharts/parallel-coordinates/polar/
  11651. * Star plot, multivariate data in a polar chart
  11652. *
  11653. * @type {boolean}
  11654. * @default false
  11655. * @since 2.3.0
  11656. * @product highcharts
  11657. * @requires highcharts-more
  11658. * @apioption chart.polar
  11659. */
  11660. /**
  11661. * Whether to reflow the chart to fit the width of the container div
  11662. * on resizing the window.
  11663. *
  11664. * @sample {highcharts} highcharts/chart/reflow-true/
  11665. * True by default
  11666. * @sample {highcharts} highcharts/chart/reflow-false/
  11667. * False
  11668. * @sample {highstock} stock/chart/reflow-true/
  11669. * True by default
  11670. * @sample {highstock} stock/chart/reflow-false/
  11671. * False
  11672. * @sample {highmaps} maps/chart/reflow-true/
  11673. * True by default
  11674. * @sample {highmaps} maps/chart/reflow-false/
  11675. * False
  11676. *
  11677. * @type {boolean}
  11678. * @default true
  11679. * @since 2.1
  11680. * @apioption chart.reflow
  11681. */
  11682. /**
  11683. * The HTML element where the chart will be rendered. If it is a string,
  11684. * the element by that id is used. The HTML element can also be passed
  11685. * by direct reference, or as the first argument of the chart
  11686. * constructor, in which case the option is not needed.
  11687. *
  11688. * @sample {highcharts} highcharts/chart/reflow-true/
  11689. * String
  11690. * @sample {highcharts} highcharts/chart/renderto-object/
  11691. * Object reference
  11692. * @sample {highcharts} highcharts/chart/renderto-jquery/
  11693. * Object reference through jQuery
  11694. * @sample {highstock} stock/chart/renderto-string/
  11695. * String
  11696. * @sample {highstock} stock/chart/renderto-object/
  11697. * Object reference
  11698. * @sample {highstock} stock/chart/renderto-jquery/
  11699. * Object reference through jQuery
  11700. *
  11701. * @type {string|Highcharts.HTMLDOMElement}
  11702. * @apioption chart.renderTo
  11703. */
  11704. /**
  11705. * The background color of the marker square when selecting (zooming
  11706. * in on) an area of the chart.
  11707. *
  11708. * @see In styled mode, the selection marker fill is set with the
  11709. * `.highcharts-selection-marker` class.
  11710. *
  11711. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11712. * @default rgba(51,92,173,0.25)
  11713. * @since 2.1.7
  11714. * @apioption chart.selectionMarkerFill
  11715. */
  11716. /**
  11717. * Whether to apply a drop shadow to the outer chart area. Requires
  11718. * that backgroundColor be set. The shadow can be an object
  11719. * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
  11720. * `width`.
  11721. *
  11722. * @sample {highcharts} highcharts/chart/shadow/
  11723. * Shadow
  11724. * @sample {highstock} stock/chart/shadow/
  11725. * Shadow
  11726. * @sample {highmaps} maps/chart/border/
  11727. * Chart border and shadow
  11728. *
  11729. * @type {boolean|Highcharts.CSSObject}
  11730. * @default false
  11731. * @apioption chart.shadow
  11732. */
  11733. /**
  11734. * Whether to show the axes initially. This only applies to empty charts
  11735. * where series are added dynamically, as axes are automatically added
  11736. * to cartesian series.
  11737. *
  11738. * @sample {highcharts} highcharts/chart/showaxes-false/
  11739. * False by default
  11740. * @sample {highcharts} highcharts/chart/showaxes-true/
  11741. * True
  11742. *
  11743. * @type {boolean}
  11744. * @since 1.2.5
  11745. * @product highcharts gantt
  11746. * @apioption chart.showAxes
  11747. */
  11748. /**
  11749. * The space between the bottom edge of the chart and the content (plot
  11750. * area, axis title and labels, title, subtitle or legend in top
  11751. * position).
  11752. *
  11753. * @sample {highcharts} highcharts/chart/spacingbottom/
  11754. * Spacing bottom set to 100
  11755. * @sample {highstock} stock/chart/spacingbottom/
  11756. * Spacing bottom set to 100
  11757. * @sample {highmaps} maps/chart/spacing/
  11758. * Spacing 100 all around
  11759. *
  11760. * @type {number}
  11761. * @default 15
  11762. * @since 2.1
  11763. * @apioption chart.spacingBottom
  11764. */
  11765. /**
  11766. * The space between the left edge of the chart and the content (plot
  11767. * area, axis title and labels, title, subtitle or legend in top
  11768. * position).
  11769. *
  11770. * @sample {highcharts} highcharts/chart/spacingleft/
  11771. * Spacing left set to 100
  11772. * @sample {highstock} stock/chart/spacingleft/
  11773. * Spacing left set to 100
  11774. * @sample {highmaps} maps/chart/spacing/
  11775. * Spacing 100 all around
  11776. *
  11777. * @type {number}
  11778. * @default 10
  11779. * @since 2.1
  11780. * @apioption chart.spacingLeft
  11781. */
  11782. /**
  11783. * The space between the right edge of the chart and the content (plot
  11784. * area, axis title and labels, title, subtitle or legend in top
  11785. * position).
  11786. *
  11787. * @sample {highcharts} highcharts/chart/spacingright-100/
  11788. * Spacing set to 100
  11789. * @sample {highcharts} highcharts/chart/spacingright-legend/
  11790. * Legend in right position with default spacing
  11791. * @sample {highstock} stock/chart/spacingright/
  11792. * Spacing set to 100
  11793. * @sample {highmaps} maps/chart/spacing/
  11794. * Spacing 100 all around
  11795. *
  11796. * @type {number}
  11797. * @default 10
  11798. * @since 2.1
  11799. * @apioption chart.spacingRight
  11800. */
  11801. /**
  11802. * The space between the top edge of the chart and the content (plot
  11803. * area, axis title and labels, title, subtitle or legend in top
  11804. * position).
  11805. *
  11806. * @sample {highcharts} highcharts/chart/spacingtop-100/
  11807. * A top spacing of 100
  11808. * @sample {highcharts} highcharts/chart/spacingtop-10/
  11809. * Floating chart title makes the plot area align to the default
  11810. * spacingTop of 10.
  11811. * @sample {highstock} stock/chart/spacingtop/
  11812. * A top spacing of 100
  11813. * @sample {highmaps} maps/chart/spacing/
  11814. * Spacing 100 all around
  11815. *
  11816. * @type {number}
  11817. * @default 10
  11818. * @since 2.1
  11819. * @apioption chart.spacingTop
  11820. */
  11821. /**
  11822. * Additional CSS styles to apply inline to the container `div`. Note
  11823. * that since the default font styles are applied in the renderer, it
  11824. * is ignorant of the individual chart options and must be set globally.
  11825. *
  11826. * @see In styled mode, general chart styles can be set with the
  11827. * `.highcharts-root` class.
  11828. * @sample {highcharts} highcharts/chart/style-serif-font/
  11829. * Using a serif type font
  11830. * @sample {highcharts} highcharts/css/em/
  11831. * Styled mode with relative font sizes
  11832. * @sample {highstock} stock/chart/style/
  11833. * Using a serif type font
  11834. * @sample {highmaps} maps/chart/style-serif-font/
  11835. * Using a serif type font
  11836. *
  11837. * @type {Highcharts.CSSObject}
  11838. * @default {"fontFamily": "\"Lucida Grande\", \"Lucida Sans Unicode\", Verdana, Arial, Helvetica, sans-serif","fontSize":"12px"}
  11839. * @apioption chart.style
  11840. */
  11841. /**
  11842. * The default series type for the chart. Can be any of the chart types
  11843. * listed under [plotOptions](#plotOptions) and [series](#series) or can
  11844. * be a series provided by an additional module.
  11845. *
  11846. * In TypeScript this option has no effect in sense of typing and
  11847. * instead the `type` option must always be set in the series.
  11848. *
  11849. * @sample {highcharts} highcharts/chart/type-bar/
  11850. * Bar
  11851. * @sample {highstock} stock/chart/type/
  11852. * Areaspline
  11853. * @sample {highmaps} maps/chart/type-mapline/
  11854. * Mapline
  11855. *
  11856. * @type {string}
  11857. * @default {highcharts} line
  11858. * @default {highstock} line
  11859. * @default {highmaps} map
  11860. * @since 2.1.0
  11861. * @apioption chart.type
  11862. */
  11863. /**
  11864. * Decides in what dimensions the user can zoom by dragging the mouse.
  11865. * Can be one of `x`, `y` or `xy`.
  11866. *
  11867. * @see [panKey](#chart.panKey)
  11868. *
  11869. * @sample {highcharts} highcharts/chart/zoomtype-none/
  11870. * None by default
  11871. * @sample {highcharts} highcharts/chart/zoomtype-x/
  11872. * X
  11873. * @sample {highcharts} highcharts/chart/zoomtype-y/
  11874. * Y
  11875. * @sample {highcharts} highcharts/chart/zoomtype-xy/
  11876. * Xy
  11877. * @sample {highstock} stock/demo/basic-line/
  11878. * None by default
  11879. * @sample {highstock} stock/chart/zoomtype-x/
  11880. * X
  11881. * @sample {highstock} stock/chart/zoomtype-y/
  11882. * Y
  11883. * @sample {highstock} stock/chart/zoomtype-xy/
  11884. * Xy
  11885. *
  11886. * @type {string}
  11887. * @product highcharts highstock gantt
  11888. * @validvalue ["x", "y", "xy"]
  11889. * @apioption chart.zoomType
  11890. */
  11891. /**
  11892. * An explicit width for the chart. By default (when `null`) the width
  11893. * is calculated from the offset width of the containing element.
  11894. *
  11895. * @sample {highcharts} highcharts/chart/width/
  11896. * 800px wide
  11897. * @sample {highstock} stock/chart/width/
  11898. * 800px wide
  11899. * @sample {highmaps} maps/chart/size/
  11900. * Chart with explicit size
  11901. *
  11902. * @type {null|number|string}
  11903. */
  11904. width: null,
  11905. /**
  11906. * An explicit height for the chart. If a _number_, the height is
  11907. * given in pixels. If given a _percentage string_ (for example
  11908. * `'56%'`), the height is given as the percentage of the actual chart
  11909. * width. This allows for preserving the aspect ratio across responsive
  11910. * sizes.
  11911. *
  11912. * By default (when `null`) the height is calculated from the offset
  11913. * height of the containing element, or 400 pixels if the containing
  11914. * element's height is 0.
  11915. *
  11916. * @sample {highcharts} highcharts/chart/height/
  11917. * 500px height
  11918. * @sample {highstock} stock/chart/height/
  11919. * 300px height
  11920. * @sample {highmaps} maps/chart/size/
  11921. * Chart with explicit size
  11922. * @sample highcharts/chart/height-percent/
  11923. * Highcharts with percentage height
  11924. *
  11925. * @type {null|number|string}
  11926. */
  11927. height: null,
  11928. /**
  11929. * The color of the outer chart border.
  11930. *
  11931. * @see In styled mode, the stroke is set with the
  11932. * `.highcharts-background` class.
  11933. *
  11934. * @sample {highcharts} highcharts/chart/bordercolor/
  11935. * Brown border
  11936. * @sample {highstock} stock/chart/border/
  11937. * Brown border
  11938. * @sample {highmaps} maps/chart/border/
  11939. * Border options
  11940. *
  11941. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11942. */
  11943. borderColor: '#335cad',
  11944. /**
  11945. * The pixel width of the outer chart border.
  11946. *
  11947. * @see In styled mode, the stroke is set with the
  11948. * `.highcharts-background` class.
  11949. *
  11950. * @sample {highcharts} highcharts/chart/borderwidth/
  11951. * 5px border
  11952. * @sample {highstock} stock/chart/border/
  11953. * 2px border
  11954. * @sample {highmaps} maps/chart/border/
  11955. * Border options
  11956. *
  11957. * @type {number}
  11958. * @default 0
  11959. * @apioption chart.borderWidth
  11960. */
  11961. /**
  11962. * The background color or gradient for the outer chart area.
  11963. *
  11964. * @see In styled mode, the background is set with the
  11965. * `.highcharts-background` class.
  11966. *
  11967. * @sample {highcharts} highcharts/chart/backgroundcolor-color/
  11968. * Color
  11969. * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
  11970. * Gradient
  11971. * @sample {highstock} stock/chart/backgroundcolor-color/
  11972. * Color
  11973. * @sample {highstock} stock/chart/backgroundcolor-gradient/
  11974. * Gradient
  11975. * @sample {highmaps} maps/chart/backgroundcolor-color/
  11976. * Color
  11977. * @sample {highmaps} maps/chart/backgroundcolor-gradient/
  11978. * Gradient
  11979. *
  11980. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11981. */
  11982. backgroundColor: '#ffffff',
  11983. /**
  11984. * The background color or gradient for the plot area.
  11985. *
  11986. * @see In styled mode, the plot background is set with the
  11987. * `.highcharts-plot-background` class.
  11988. *
  11989. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
  11990. * Color
  11991. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
  11992. * Gradient
  11993. * @sample {highstock} stock/chart/plotbackgroundcolor-color/
  11994. * Color
  11995. * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
  11996. * Gradient
  11997. * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
  11998. * Color
  11999. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  12000. * Gradient
  12001. *
  12002. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12003. * @apioption chart.plotBackgroundColor
  12004. */
  12005. /**
  12006. * The URL for an image to use as the plot background. To set an image
  12007. * as the background for the entire chart, set a CSS background image
  12008. * to the container element. Note that for the image to be applied to
  12009. * exported charts, its URL needs to be accessible by the export server.
  12010. *
  12011. * @see In styled mode, a plot background image can be set with the
  12012. * `.highcharts-plot-background` class and a [custom pattern](
  12013. * https://www.highcharts.com/docs/chart-design-and-style/
  12014. * gradients-shadows-and-patterns).
  12015. *
  12016. * @sample {highcharts} highcharts/chart/plotbackgroundimage/
  12017. * Skies
  12018. * @sample {highstock} stock/chart/plotbackgroundimage/
  12019. * Skies
  12020. *
  12021. * @type {string}
  12022. * @apioption chart.plotBackgroundImage
  12023. */
  12024. /**
  12025. * The color of the inner chart or plot area border.
  12026. *
  12027. * @see In styled mode, a plot border stroke can be set with the
  12028. * `.highcharts-plot-border` class.
  12029. *
  12030. * @sample {highcharts} highcharts/chart/plotbordercolor/
  12031. * Blue border
  12032. * @sample {highstock} stock/chart/plotborder/
  12033. * Blue border
  12034. * @sample {highmaps} maps/chart/plotborder/
  12035. * Plot border options
  12036. *
  12037. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12038. */
  12039. plotBorderColor: '#cccccc'
  12040. },
  12041. /**
  12042. * The chart's main title.
  12043. *
  12044. * @sample {highmaps} maps/title/title/
  12045. * Title options demonstrated
  12046. */
  12047. title: {
  12048. /**
  12049. * When the title is floating, the plot area will not move to make space
  12050. * for it.
  12051. *
  12052. * @sample {highcharts} highcharts/chart/zoomtype-none/
  12053. * False by default
  12054. * @sample {highcharts} highcharts/title/floating/
  12055. * True - title on top of the plot area
  12056. * @sample {highstock} stock/chart/title-floating/
  12057. * True - title on top of the plot area
  12058. *
  12059. * @type {boolean}
  12060. * @default false
  12061. * @since 2.1
  12062. * @apioption title.floating
  12063. */
  12064. /**
  12065. * CSS styles for the title. Use this for font styling, but use `align`,
  12066. * `x` and `y` for text alignment.
  12067. *
  12068. * In styled mode, the title style is given in the `.highcharts-title`
  12069. * class.
  12070. *
  12071. * @sample {highcharts} highcharts/title/style/
  12072. * Custom color and weight
  12073. * @sample {highstock} stock/chart/title-style/
  12074. * Custom color and weight
  12075. * @sample highcharts/css/titles/
  12076. * Styled mode
  12077. *
  12078. * @type {Highcharts.CSSObject}
  12079. * @default {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
  12080. * @default {highstock} { "color": "#333333", "fontSize": "16px" }
  12081. * @apioption title.style
  12082. */
  12083. /**
  12084. * Whether to
  12085. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  12086. * to render the text.
  12087. *
  12088. * @type {boolean}
  12089. * @default false
  12090. * @apioption title.useHTML
  12091. */
  12092. /**
  12093. * The vertical alignment of the title. Can be one of `"top"`,
  12094. * `"middle"` and `"bottom"`. When a value is given, the title behaves
  12095. * as if [floating](#title.floating) were `true`.
  12096. *
  12097. * @sample {highcharts} highcharts/title/verticalalign/
  12098. * Chart title in bottom right corner
  12099. * @sample {highstock} stock/chart/title-verticalalign/
  12100. * Chart title in bottom right corner
  12101. *
  12102. * @type {Highcharts.VerticalAlignValue}
  12103. * @since 2.1
  12104. * @apioption title.verticalAlign
  12105. */
  12106. /**
  12107. * The x position of the title relative to the alignment within
  12108. * `chart.spacingLeft` and `chart.spacingRight`.
  12109. *
  12110. * @sample {highcharts} highcharts/title/align/
  12111. * Aligned to the plot area (x = 70px = margin left - spacing
  12112. * left)
  12113. * @sample {highstock} stock/chart/title-align/
  12114. * Aligned to the plot area (x = 50px = margin left - spacing
  12115. * left)
  12116. *
  12117. * @type {number}
  12118. * @default 0
  12119. * @since 2.0
  12120. * @apioption title.x
  12121. */
  12122. /**
  12123. * The y position of the title relative to the alignment within
  12124. * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
  12125. * #chart.spacingBottom). By default it depends on the font size.
  12126. *
  12127. * @sample {highcharts} highcharts/title/y/
  12128. * Title inside the plot area
  12129. * @sample {highstock} stock/chart/title-verticalalign/
  12130. * Chart title in bottom right corner
  12131. *
  12132. * @type {number}
  12133. * @since 2.0
  12134. * @apioption title.y
  12135. */
  12136. /**
  12137. * The title of the chart. To disable the title, set the `text` to
  12138. * `undefined`.
  12139. *
  12140. * @sample {highcharts} highcharts/title/text/
  12141. * Custom title
  12142. * @sample {highstock} stock/chart/title-text/
  12143. * Custom title
  12144. *
  12145. * @default {highcharts|highmaps} Chart title
  12146. * @default {highstock} undefined
  12147. */
  12148. text: 'Chart title',
  12149. /**
  12150. * The horizontal alignment of the title. Can be one of "left", "center"
  12151. * and "right".
  12152. *
  12153. * @sample {highcharts} highcharts/title/align/
  12154. * Aligned to the plot area (x = 70px = margin left - spacing
  12155. * left)
  12156. * @sample {highstock} stock/chart/title-align/
  12157. * Aligned to the plot area (x = 50px = margin left - spacing
  12158. * left)
  12159. *
  12160. * @type {Highcharts.AlignValue}
  12161. * @since 2.0
  12162. */
  12163. align: 'center',
  12164. /**
  12165. * The margin between the title and the plot area, or if a subtitle
  12166. * is present, the margin between the subtitle and the plot area.
  12167. *
  12168. * @sample {highcharts} highcharts/title/margin-50/
  12169. * A chart title margin of 50
  12170. * @sample {highcharts} highcharts/title/margin-subtitle/
  12171. * The same margin applied with a subtitle
  12172. * @sample {highstock} stock/chart/title-margin/
  12173. * A chart title margin of 50
  12174. *
  12175. * @since 2.1
  12176. */
  12177. margin: 15,
  12178. /**
  12179. * Adjustment made to the title width, normally to reserve space for
  12180. * the exporting burger menu.
  12181. *
  12182. * @sample highcharts/title/widthadjust/
  12183. * Wider menu, greater padding
  12184. *
  12185. * @since 4.2.5
  12186. */
  12187. widthAdjust: -44
  12188. },
  12189. /**
  12190. * The chart's subtitle. This can be used both to display a subtitle below
  12191. * the main title, and to display random text anywhere in the chart. The
  12192. * subtitle can be updated after chart initialization through the
  12193. * `Chart.setTitle` method.
  12194. *
  12195. * @sample {highmaps} maps/title/subtitle/
  12196. * Subtitle options demonstrated
  12197. */
  12198. subtitle: {
  12199. /**
  12200. * When the subtitle is floating, the plot area will not move to make
  12201. * space for it.
  12202. *
  12203. * @sample {highcharts} highcharts/subtitle/floating/
  12204. * Floating title and subtitle
  12205. * @sample {highstock} stock/chart/subtitle-footnote
  12206. * Footnote floating at bottom right of plot area
  12207. *
  12208. * @type {boolean}
  12209. * @default false
  12210. * @since 2.1
  12211. * @apioption subtitle.floating
  12212. */
  12213. /**
  12214. * CSS styles for the title.
  12215. *
  12216. * In styled mode, the subtitle style is given in the
  12217. * `.highcharts-subtitle` class.
  12218. *
  12219. * @sample {highcharts} highcharts/subtitle/style/
  12220. * Custom color and weight
  12221. * @sample {highcharts} highcharts/css/titles/
  12222. * Styled mode
  12223. * @sample {highstock} stock/chart/subtitle-style
  12224. * Custom color and weight
  12225. * @sample {highstock} highcharts/css/titles/
  12226. * Styled mode
  12227. * @sample {highmaps} highcharts/css/titles/
  12228. * Styled mode
  12229. *
  12230. * @type {Highcharts.CSSObject}
  12231. * @default {"color": "#666666"}
  12232. * @apioption subtitle.style
  12233. */
  12234. /**
  12235. * Whether to
  12236. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  12237. * to render the text.
  12238. *
  12239. * @type {boolean}
  12240. * @default false
  12241. * @apioption subtitle.useHTML
  12242. */
  12243. /**
  12244. * The vertical alignment of the title. Can be one of `"top"`,
  12245. * `"middle"` and `"bottom"`. When middle, the subtitle behaves as
  12246. * floating.
  12247. *
  12248. * @sample {highcharts} highcharts/subtitle/verticalalign/
  12249. * Footnote at the bottom right of plot area
  12250. * @sample {highstock} stock/chart/subtitle-footnote
  12251. * Footnote at the bottom right of plot area
  12252. *
  12253. * @type {Highcharts.VerticalAlignValue}
  12254. * @since 2.1
  12255. * @apioption subtitle.verticalAlign
  12256. */
  12257. /**
  12258. * The x position of the subtitle relative to the alignment within
  12259. * `chart.spacingLeft` and `chart.spacingRight`.
  12260. *
  12261. * @sample {highcharts} highcharts/subtitle/align/
  12262. * Footnote at right of plot area
  12263. * @sample {highstock} stock/chart/subtitle-footnote
  12264. * Footnote at the bottom right of plot area
  12265. *
  12266. * @type {number}
  12267. * @default 0
  12268. * @since 2.0
  12269. * @apioption subtitle.x
  12270. */
  12271. /**
  12272. * The y position of the subtitle relative to the alignment within
  12273. * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
  12274. * is laid out below the title unless the title is floating.
  12275. *
  12276. * @sample {highcharts} highcharts/subtitle/verticalalign/
  12277. * Footnote at the bottom right of plot area
  12278. * @sample {highstock} stock/chart/subtitle-footnote
  12279. * Footnote at the bottom right of plot area
  12280. *
  12281. * @type {number}
  12282. * @since 2.0
  12283. * @apioption subtitle.y
  12284. */
  12285. /**
  12286. * The subtitle of the chart.
  12287. *
  12288. * @sample {highcharts|highstock} highcharts/subtitle/text/
  12289. * Custom subtitle
  12290. * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
  12291. * Formatted and linked text.
  12292. */
  12293. text: '',
  12294. /**
  12295. * The horizontal alignment of the subtitle. Can be one of "left",
  12296. * "center" and "right".
  12297. *
  12298. * @sample {highcharts} highcharts/subtitle/align/
  12299. * Footnote at right of plot area
  12300. * @sample {highstock} stock/chart/subtitle-footnote
  12301. * Footnote at bottom right of plot area
  12302. *
  12303. * @type {Highcharts.AlignValue}
  12304. * @since 2.0
  12305. */
  12306. align: 'center',
  12307. /**
  12308. * Adjustment made to the subtitle width, normally to reserve space
  12309. * for the exporting burger menu.
  12310. *
  12311. * @see [title.widthAdjust](#title.widthAdjust)
  12312. *
  12313. * @sample highcharts/title/widthadjust/
  12314. * Wider menu, greater padding
  12315. *
  12316. * @since 4.2.5
  12317. */
  12318. widthAdjust: -44
  12319. },
  12320. /**
  12321. * The chart's caption, which will render below the chart and will be part
  12322. * of exported charts. The caption can be updated after chart initialization
  12323. * through the `Chart.update` or `Chart.caption.update` methods.
  12324. *
  12325. * @sample highcharts/caption/text/
  12326. * A chart with a caption
  12327. * @since 7.2.0
  12328. */
  12329. caption: {
  12330. /**
  12331. * When the caption is floating, the plot area will not move to make
  12332. * space for it.
  12333. *
  12334. * @type {boolean}
  12335. * @default false
  12336. * @apioption caption.floating
  12337. */
  12338. /**
  12339. * The margin between the caption and the plot area.
  12340. */
  12341. margin: 15,
  12342. /**
  12343. * CSS styles for the caption.
  12344. *
  12345. * In styled mode, the caption style is given in the
  12346. * `.highcharts-caption` class.
  12347. *
  12348. * @sample {highcharts} highcharts/css/titles/
  12349. * Styled mode
  12350. *
  12351. * @type {Highcharts.CSSObject}
  12352. * @default {"color": "#666666"}
  12353. * @apioption caption.style
  12354. */
  12355. /**
  12356. * Whether to
  12357. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  12358. * to render the text.
  12359. *
  12360. * @type {boolean}
  12361. * @default false
  12362. * @apioption caption.useHTML
  12363. */
  12364. /**
  12365. * The x position of the caption relative to the alignment within
  12366. * `chart.spacingLeft` and `chart.spacingRight`.
  12367. *
  12368. * @type {number}
  12369. * @default 0
  12370. * @apioption caption.x
  12371. */
  12372. /**
  12373. * The y position of the caption relative to the alignment within
  12374. * `chart.spacingTop` and `chart.spacingBottom`.
  12375. *
  12376. * @type {number}
  12377. * @apioption caption.y
  12378. */
  12379. /**
  12380. * The caption text of the chart.
  12381. *
  12382. * @sample {highcharts} highcharts/caption/text/
  12383. * Custom caption
  12384. */
  12385. text: '',
  12386. /**
  12387. * The horizontal alignment of the caption. Can be one of "left",
  12388. * "center" and "right".
  12389. *
  12390. * @type {Highcharts.AlignValue}
  12391. */
  12392. align: 'left',
  12393. /**
  12394. * The vertical alignment of the caption. Can be one of `"top"`,
  12395. * `"middle"` and `"bottom"`. When middle, the caption behaves as
  12396. * floating.
  12397. *
  12398. * @type {Highcharts.VerticalAlignValue}
  12399. */
  12400. verticalAlign: 'bottom'
  12401. },
  12402. /**
  12403. * The plotOptions is a wrapper object for config objects for each series
  12404. * type. The config objects for each series can also be overridden for
  12405. * each series item as given in the series array.
  12406. *
  12407. * Configuration options for the series are given in three levels. Options
  12408. * for all series in a chart are given in the [plotOptions.series](
  12409. * #plotOptions.series) object. Then options for all series of a specific
  12410. * type are given in the plotOptions of that type, for example
  12411. * `plotOptions.line`. Next, options for one single series are given in
  12412. * [the series array](#series).
  12413. */
  12414. plotOptions: {},
  12415. /**
  12416. * HTML labels that can be positioned anywhere in the chart area.
  12417. *
  12418. * This option is deprecated since v7.1.2. Instead, use
  12419. * [annotations](#annotations) that support labels.
  12420. *
  12421. * @deprecated
  12422. * @product highcharts highstock
  12423. */
  12424. labels: {
  12425. /**
  12426. * An HTML label that can be positioned anywhere in the chart area.
  12427. *
  12428. * @deprecated
  12429. * @type {Array<*>}
  12430. * @apioption labels.items
  12431. */
  12432. /**
  12433. * Inner HTML or text for the label.
  12434. *
  12435. * @deprecated
  12436. * @type {string}
  12437. * @apioption labels.items.html
  12438. */
  12439. /**
  12440. * CSS styles for each label. To position the label, use left and top
  12441. * like this:
  12442. * ```js
  12443. * style: {
  12444. * left: '100px',
  12445. * top: '100px'
  12446. * }
  12447. * ```
  12448. *
  12449. * @deprecated
  12450. * @type {Highcharts.CSSObject}
  12451. * @apioption labels.items.style
  12452. */
  12453. /**
  12454. * Shared CSS styles for all labels.
  12455. *
  12456. * @deprecated
  12457. * @type {Highcharts.CSSObject}
  12458. * @default {"color": "#333333", "position": "absolute"}
  12459. */
  12460. style: {
  12461. /**
  12462. * @ignore-option
  12463. */
  12464. position: 'absolute',
  12465. /**
  12466. * @ignore-option
  12467. */
  12468. color: '#333333'
  12469. }
  12470. },
  12471. /**
  12472. * The legend is a box containing a symbol and name for each series
  12473. * item or point item in the chart. Each series (or points in case
  12474. * of pie charts) is represented by a symbol and its name in the legend.
  12475. *
  12476. * It is possible to override the symbol creator function and create
  12477. * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
  12478. *
  12479. * @productdesc {highmaps}
  12480. * A Highmaps legend by default contains one legend item per series, but if
  12481. * a `colorAxis` is defined, the axis will be displayed in the legend.
  12482. * Either as a gradient, or as multiple legend items for `dataClasses`.
  12483. */
  12484. legend: {
  12485. /**
  12486. * The background color of the legend.
  12487. *
  12488. * @see In styled mode, the legend background fill can be applied with
  12489. * the `.highcharts-legend-box` class.
  12490. *
  12491. * @sample {highcharts} highcharts/legend/backgroundcolor/
  12492. * Yellowish background
  12493. * @sample {highstock} stock/legend/align/
  12494. * Various legend options
  12495. * @sample {highmaps} maps/legend/border-background/
  12496. * Border and background options
  12497. *
  12498. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12499. * @apioption legend.backgroundColor
  12500. */
  12501. /**
  12502. * The width of the drawn border around the legend.
  12503. *
  12504. * @see In styled mode, the legend border stroke width can be applied
  12505. * with the `.highcharts-legend-box` class.
  12506. *
  12507. * @sample {highcharts} highcharts/legend/borderwidth/
  12508. * 2px border width
  12509. * @sample {highstock} stock/legend/align/
  12510. * Various legend options
  12511. * @sample {highmaps} maps/legend/border-background/
  12512. * Border and background options
  12513. *
  12514. * @type {number}
  12515. * @default 0
  12516. * @apioption legend.borderWidth
  12517. */
  12518. /**
  12519. * Enable or disable the legend. There is also a series-specific option,
  12520. * [showInLegend](#plotOptions.series.showInLegend), that can hide the
  12521. * series from the legend. In some series types this is `false` by
  12522. * default, so it must set to `true` in order to show the legend for the
  12523. * series.
  12524. *
  12525. * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
  12526. * @sample {highstock} stock/legend/align/ Various legend options
  12527. * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
  12528. *
  12529. * @default {highstock} false
  12530. * @default {highmaps} true
  12531. * @default {gantt} false
  12532. */
  12533. enabled: true,
  12534. /**
  12535. * The horizontal alignment of the legend box within the chart area.
  12536. * Valid values are `left`, `center` and `right`.
  12537. *
  12538. * In the case that the legend is aligned in a corner position, the
  12539. * `layout` option will determine whether to place it above/below
  12540. * or on the side of the plot area.
  12541. *
  12542. * @sample {highcharts} highcharts/legend/align/
  12543. * Legend at the right of the chart
  12544. * @sample {highstock} stock/legend/align/
  12545. * Various legend options
  12546. * @sample {highmaps} maps/legend/alignment/
  12547. * Legend alignment
  12548. *
  12549. * @type {Highcharts.AlignValue}
  12550. * @since 2.0
  12551. */
  12552. align: 'center',
  12553. /**
  12554. * If the [layout](legend.layout) is `horizontal` and the legend items
  12555. * span over two lines or more, whether to align the items into vertical
  12556. * columns. Setting this to `false` makes room for more items, but will
  12557. * look more messy.
  12558. *
  12559. * @since 6.1.0
  12560. */
  12561. alignColumns: true,
  12562. /**
  12563. * When the legend is floating, the plot area ignores it and is allowed
  12564. * to be placed below it.
  12565. *
  12566. * @sample {highcharts} highcharts/legend/floating-false/
  12567. * False by default
  12568. * @sample {highcharts} highcharts/legend/floating-true/
  12569. * True
  12570. * @sample {highmaps} maps/legend/alignment/
  12571. * Floating legend
  12572. *
  12573. * @type {boolean}
  12574. * @default false
  12575. * @since 2.1
  12576. * @apioption legend.floating
  12577. */
  12578. /**
  12579. * The layout of the legend items. Can be one of `horizontal` or
  12580. * `vertical` or `proximate`. When `proximate`, the legend items will be
  12581. * placed as close as possible to the graphs they're representing,
  12582. * except in inverted charts or when the legend position doesn't allow
  12583. * it.
  12584. *
  12585. * @sample {highcharts} highcharts/legend/layout-horizontal/
  12586. * Horizontal by default
  12587. * @sample {highcharts} highcharts/legend/layout-vertical/
  12588. * Vertical
  12589. * @sample highcharts/legend/layout-proximate
  12590. * Labels proximate to the data
  12591. * @sample {highstock} stock/legend/layout-horizontal/
  12592. * Horizontal by default
  12593. * @sample {highmaps} maps/legend/padding-itemmargin/
  12594. * Vertical with data classes
  12595. * @sample {highmaps} maps/legend/layout-vertical/
  12596. * Vertical with color axis gradient
  12597. *
  12598. * @validvalue ["horizontal", "vertical", "proximate"]
  12599. */
  12600. layout: 'horizontal',
  12601. /**
  12602. * In a legend with horizontal layout, the itemDistance defines the
  12603. * pixel distance between each item.
  12604. *
  12605. * @sample {highcharts} highcharts/legend/layout-horizontal/
  12606. * 50px item distance
  12607. * @sample {highstock} highcharts/legend/layout-horizontal/
  12608. * 50px item distance
  12609. *
  12610. * @type {number}
  12611. * @default {highcharts} 20
  12612. * @default {highstock} 20
  12613. * @default {highmaps} 8
  12614. * @since 3.0.3
  12615. * @apioption legend.itemDistance
  12616. */
  12617. /**
  12618. * The pixel bottom margin for each legend item.
  12619. *
  12620. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  12621. * Padding and item margins demonstrated
  12622. * @sample {highmaps} maps/legend/padding-itemmargin/
  12623. * Padding and item margins demonstrated
  12624. *
  12625. * @type {number}
  12626. * @default 0
  12627. * @since 2.2.0
  12628. * @apioption legend.itemMarginBottom
  12629. */
  12630. /**
  12631. * The pixel top margin for each legend item.
  12632. *
  12633. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  12634. * Padding and item margins demonstrated
  12635. * @sample {highmaps} maps/legend/padding-itemmargin/
  12636. * Padding and item margins demonstrated
  12637. *
  12638. * @type {number}
  12639. * @default 0
  12640. * @since 2.2.0
  12641. * @apioption legend.itemMarginTop
  12642. */
  12643. /**
  12644. * The width for each legend item. By default the items are laid out
  12645. * successively. In a [horizontal layout](legend.layout), if the items
  12646. * are laid out across two rows or more, they will be vertically aligned
  12647. * depending on the [legend.alignColumns](legend.alignColumns) option.
  12648. *
  12649. * @sample {highcharts} highcharts/legend/itemwidth-default/
  12650. * Undefined by default
  12651. * @sample {highcharts} highcharts/legend/itemwidth-80/
  12652. * 80 for aligned legend items
  12653. *
  12654. * @type {number}
  12655. * @since 2.0
  12656. * @apioption legend.itemWidth
  12657. */
  12658. /**
  12659. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  12660. * for each legend label. Available variables relates to properties on
  12661. * the series, or the point in case of pies.
  12662. *
  12663. * @type {string}
  12664. * @default {name}
  12665. * @since 1.3
  12666. * @apioption legend.labelFormat
  12667. */
  12668. /* eslint-disable valid-jsdoc */
  12669. /**
  12670. * Callback function to format each of the series' labels. The `this`
  12671. * keyword refers to the series object, or the point object in case of
  12672. * pie charts. By default the series or point name is printed.
  12673. *
  12674. * @productdesc {highmaps}
  12675. * In Highmaps the context can also be a data class in case of a
  12676. * `colorAxis`.
  12677. *
  12678. * @sample {highcharts} highcharts/legend/labelformatter/
  12679. * Add text
  12680. * @sample {highmaps} maps/legend/labelformatter/
  12681. * Data classes with label formatter
  12682. *
  12683. * @type {Highcharts.FormatterCallbackFunction<Point|Series>}
  12684. */
  12685. labelFormatter: function () {
  12686. /** eslint-enable valid-jsdoc */
  12687. return this.name;
  12688. },
  12689. /**
  12690. * Line height for the legend items. Deprecated as of 2.1\. Instead,
  12691. * the line height for each item can be set using
  12692. * `itemStyle.lineHeight`, and the padding between items using
  12693. * `itemMarginTop` and `itemMarginBottom`.
  12694. *
  12695. * @sample {highcharts} highcharts/legend/lineheight/
  12696. * Setting padding
  12697. *
  12698. * @deprecated
  12699. *
  12700. * @type {number}
  12701. * @default 16
  12702. * @since 2.0
  12703. * @product highcharts gantt
  12704. * @apioption legend.lineHeight
  12705. */
  12706. /**
  12707. * If the plot area sized is calculated automatically and the legend is
  12708. * not floating, the legend margin is the space between the legend and
  12709. * the axis labels or plot area.
  12710. *
  12711. * @sample {highcharts} highcharts/legend/margin-default/
  12712. * 12 pixels by default
  12713. * @sample {highcharts} highcharts/legend/margin-30/
  12714. * 30 pixels
  12715. *
  12716. * @type {number}
  12717. * @default 12
  12718. * @since 2.1
  12719. * @apioption legend.margin
  12720. */
  12721. /**
  12722. * Maximum pixel height for the legend. When the maximum height is
  12723. * extended, navigation will show.
  12724. *
  12725. * @type {number}
  12726. * @since 2.3.0
  12727. * @apioption legend.maxHeight
  12728. */
  12729. /**
  12730. * The color of the drawn border around the legend.
  12731. *
  12732. * @see In styled mode, the legend border stroke can be applied with the
  12733. * `.highcharts-legend-box` class.
  12734. *
  12735. * @sample {highcharts} highcharts/legend/bordercolor/
  12736. * Brown border
  12737. * @sample {highstock} stock/legend/align/
  12738. * Various legend options
  12739. * @sample {highmaps} maps/legend/border-background/
  12740. * Border and background options
  12741. *
  12742. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12743. */
  12744. borderColor: '#999999',
  12745. /**
  12746. * The border corner radius of the legend.
  12747. *
  12748. * @sample {highcharts} highcharts/legend/borderradius-default/
  12749. * Square by default
  12750. * @sample {highcharts} highcharts/legend/borderradius-round/
  12751. * 5px rounded
  12752. * @sample {highmaps} maps/legend/border-background/
  12753. * Border and background options
  12754. */
  12755. borderRadius: 0,
  12756. /**
  12757. * Options for the paging or navigation appearing when the legend is
  12758. * overflown. Navigation works well on screen, but not in static
  12759. * exported images. One way of working around that is to
  12760. * [increase the chart height in
  12761. * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
  12762. */
  12763. navigation: {
  12764. /**
  12765. * How to animate the pages when navigating up or down. A value of
  12766. * `true` applies the default navigation given in the
  12767. * `chart.animation` option. Additional options can be given as an
  12768. * object containing values for easing and duration.
  12769. *
  12770. * @sample {highcharts} highcharts/legend/navigation/
  12771. * Legend page navigation demonstrated
  12772. * @sample {highstock} highcharts/legend/navigation/
  12773. * Legend page navigation demonstrated
  12774. *
  12775. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  12776. * @default true
  12777. * @since 2.2.4
  12778. * @apioption legend.navigation.animation
  12779. */
  12780. /**
  12781. * The pixel size of the up and down arrows in the legend paging
  12782. * navigation.
  12783. *
  12784. * @sample {highcharts} highcharts/legend/navigation/
  12785. * Legend page navigation demonstrated
  12786. * @sample {highstock} highcharts/legend/navigation/
  12787. * Legend page navigation demonstrated
  12788. *
  12789. * @type {number}
  12790. * @default 12
  12791. * @since 2.2.4
  12792. * @apioption legend.navigation.arrowSize
  12793. */
  12794. /**
  12795. * Whether to enable the legend navigation. In most cases, disabling
  12796. * the navigation results in an unwanted overflow.
  12797. *
  12798. * See also the [adapt chart to legend](
  12799. * https://www.highcharts.com/products/plugin-registry/single/8/Adapt-Chart-To-Legend)
  12800. * plugin for a solution to extend the chart height to make room for
  12801. * the legend, optionally in exported charts only.
  12802. *
  12803. * @type {boolean}
  12804. * @default true
  12805. * @since 4.2.4
  12806. * @apioption legend.navigation.enabled
  12807. */
  12808. /**
  12809. * Text styles for the legend page navigation.
  12810. *
  12811. * @see In styled mode, the navigation items are styled with the
  12812. * `.highcharts-legend-navigation` class.
  12813. *
  12814. * @sample {highcharts} highcharts/legend/navigation/
  12815. * Legend page navigation demonstrated
  12816. * @sample {highstock} highcharts/legend/navigation/
  12817. * Legend page navigation demonstrated
  12818. *
  12819. * @type {Highcharts.CSSObject}
  12820. * @since 2.2.4
  12821. * @apioption legend.navigation.style
  12822. */
  12823. /**
  12824. * The color for the active up or down arrow in the legend page
  12825. * navigation.
  12826. *
  12827. * @see In styled mode, the active arrow be styled with the
  12828. * `.highcharts-legend-nav-active` class.
  12829. *
  12830. * @sample {highcharts} highcharts/legend/navigation/
  12831. * Legend page navigation demonstrated
  12832. * @sample {highstock} highcharts/legend/navigation/
  12833. * Legend page navigation demonstrated
  12834. *
  12835. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12836. * @since 2.2.4
  12837. */
  12838. activeColor: '#003399',
  12839. /**
  12840. * The color of the inactive up or down arrow in the legend page
  12841. * navigation. .
  12842. *
  12843. * @see In styled mode, the inactive arrow be styled with the
  12844. * `.highcharts-legend-nav-inactive` class.
  12845. *
  12846. * @sample {highcharts} highcharts/legend/navigation/
  12847. * Legend page navigation demonstrated
  12848. * @sample {highstock} highcharts/legend/navigation/
  12849. * Legend page navigation demonstrated
  12850. *
  12851. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12852. * @since 2.2.4
  12853. */
  12854. inactiveColor: '#cccccc'
  12855. },
  12856. /**
  12857. * The inner padding of the legend box.
  12858. *
  12859. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  12860. * Padding and item margins demonstrated
  12861. * @sample {highmaps} maps/legend/padding-itemmargin/
  12862. * Padding and item margins demonstrated
  12863. *
  12864. * @type {number}
  12865. * @default 8
  12866. * @since 2.2.0
  12867. * @apioption legend.padding
  12868. */
  12869. /**
  12870. * Whether to reverse the order of the legend items compared to the
  12871. * order of the series or points as defined in the configuration object.
  12872. *
  12873. * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
  12874. * [series.legendIndex](#series.legendIndex)
  12875. *
  12876. * @sample {highcharts} highcharts/legend/reversed/
  12877. * Stacked bar with reversed legend
  12878. *
  12879. * @type {boolean}
  12880. * @default false
  12881. * @since 1.2.5
  12882. * @apioption legend.reversed
  12883. */
  12884. /**
  12885. * Whether to show the symbol on the right side of the text rather than
  12886. * the left side. This is common in Arabic and Hebraic.
  12887. *
  12888. * @sample {highcharts} highcharts/legend/rtl/
  12889. * Symbol to the right
  12890. *
  12891. * @type {boolean}
  12892. * @default false
  12893. * @since 2.2
  12894. * @apioption legend.rtl
  12895. */
  12896. /**
  12897. * CSS styles for the legend area. In the 1.x versions the position
  12898. * of the legend area was determined by CSS. In 2.x, the position is
  12899. * determined by properties like `align`, `verticalAlign`, `x` and `y`,
  12900. * but the styles are still parsed for backwards compatibility.
  12901. *
  12902. * @deprecated
  12903. *
  12904. * @type {Highcharts.CSSObject}
  12905. * @product highcharts highstock
  12906. * @apioption legend.style
  12907. */
  12908. /**
  12909. * CSS styles for each legend item. Only a subset of CSS is supported,
  12910. * notably those options related to text. The default `textOverflow`
  12911. * property makes long texts truncate. Set it to `undefined` to wrap
  12912. * text instead. A `width` property can be added to control the text
  12913. * width.
  12914. *
  12915. * @see In styled mode, the legend items can be styled with the
  12916. * `.highcharts-legend-item` class.
  12917. *
  12918. * @sample {highcharts} highcharts/legend/itemstyle/
  12919. * Bold black text
  12920. * @sample {highmaps} maps/legend/itemstyle/
  12921. * Item text styles
  12922. *
  12923. * @type {Highcharts.CSSObject}
  12924. * @default {"color": "#333333", "cursor": "pointer", "fontSize": "12px", "fontWeight": "bold", "textOverflow": "ellipsis"}
  12925. */
  12926. itemStyle: {
  12927. /**
  12928. * @ignore
  12929. */
  12930. color: '#333333',
  12931. /**
  12932. * @ignore
  12933. */
  12934. cursor: 'pointer',
  12935. /**
  12936. * @ignore
  12937. */
  12938. fontSize: '12px',
  12939. /**
  12940. * @ignore
  12941. */
  12942. fontWeight: 'bold',
  12943. /**
  12944. * @ignore
  12945. */
  12946. textOverflow: 'ellipsis'
  12947. },
  12948. /**
  12949. * CSS styles for each legend item in hover mode. Only a subset of
  12950. * CSS is supported, notably those options related to text. Properties
  12951. * are inherited from `style` unless overridden here.
  12952. *
  12953. * @see In styled mode, the hovered legend items can be styled with
  12954. * the `.highcharts-legend-item:hover` pesudo-class.
  12955. *
  12956. * @sample {highcharts} highcharts/legend/itemhoverstyle/
  12957. * Red on hover
  12958. * @sample {highmaps} maps/legend/itemstyle/
  12959. * Item text styles
  12960. *
  12961. * @type {Highcharts.CSSObject}
  12962. * @default {"color": "#000000"}
  12963. */
  12964. itemHoverStyle: {
  12965. /**
  12966. * @ignore
  12967. */
  12968. color: '#000000'
  12969. },
  12970. /**
  12971. * CSS styles for each legend item when the corresponding series or
  12972. * point is hidden. Only a subset of CSS is supported, notably those
  12973. * options related to text. Properties are inherited from `style`
  12974. * unless overridden here.
  12975. *
  12976. * @see In styled mode, the hidden legend items can be styled with
  12977. * the `.highcharts-legend-item-hidden` class.
  12978. *
  12979. * @sample {highcharts} highcharts/legend/itemhiddenstyle/
  12980. * Darker gray color
  12981. *
  12982. * @type {Highcharts.CSSObject}
  12983. * @default {"color": "#cccccc"}
  12984. */
  12985. itemHiddenStyle: {
  12986. /**
  12987. * @ignore
  12988. */
  12989. color: '#cccccc'
  12990. },
  12991. /**
  12992. * Whether to apply a drop shadow to the legend. A `backgroundColor`
  12993. * also needs to be applied for this to take effect. The shadow can be
  12994. * an object configuration containing `color`, `offsetX`, `offsetY`,
  12995. * `opacity` and `width`.
  12996. *
  12997. * @sample {highcharts} highcharts/legend/shadow/
  12998. * White background and drop shadow
  12999. * @sample {highstock} stock/legend/align/
  13000. * Various legend options
  13001. * @sample {highmaps} maps/legend/border-background/
  13002. * Border and background options
  13003. *
  13004. * @type {boolean|Highcharts.CSSObject}
  13005. */
  13006. shadow: false,
  13007. /**
  13008. * Default styling for the checkbox next to a legend item when
  13009. * `showCheckbox` is true.
  13010. *
  13011. * @type {Highcharts.CSSObject}
  13012. * @default {"width": "13px", "height": "13px", "position":"absolute"}
  13013. */
  13014. itemCheckboxStyle: {
  13015. /**
  13016. * @ignore
  13017. */
  13018. position: 'absolute',
  13019. /**
  13020. * @ignore
  13021. */
  13022. width: '13px',
  13023. /**
  13024. * @ignore
  13025. */
  13026. height: '13px'
  13027. },
  13028. // itemWidth: undefined,
  13029. /**
  13030. * When this is true, the legend symbol width will be the same as
  13031. * the symbol height, which in turn defaults to the font size of the
  13032. * legend items.
  13033. *
  13034. * @since 5.0.0
  13035. */
  13036. squareSymbol: true,
  13037. /**
  13038. * The pixel height of the symbol for series types that use a rectangle
  13039. * in the legend. Defaults to the font size of legend items.
  13040. *
  13041. * @productdesc {highmaps}
  13042. * In Highmaps, when the symbol is the gradient of a vertical color
  13043. * axis, the height defaults to 200.
  13044. *
  13045. * @sample {highmaps} maps/legend/layout-vertical-sized/
  13046. * Sized vertical gradient
  13047. * @sample {highmaps} maps/legend/padding-itemmargin/
  13048. * No distance between data classes
  13049. *
  13050. * @type {number}
  13051. * @since 3.0.8
  13052. * @apioption legend.symbolHeight
  13053. */
  13054. /**
  13055. * The border radius of the symbol for series types that use a rectangle
  13056. * in the legend. Defaults to half the `symbolHeight`.
  13057. *
  13058. * @sample {highcharts} highcharts/legend/symbolradius/
  13059. * Round symbols
  13060. * @sample {highstock} highcharts/legend/symbolradius/
  13061. * Round symbols
  13062. * @sample {highmaps} highcharts/legend/symbolradius/
  13063. * Round symbols
  13064. *
  13065. * @type {number}
  13066. * @since 3.0.8
  13067. * @apioption legend.symbolRadius
  13068. */
  13069. /**
  13070. * The pixel width of the legend item symbol. When the `squareSymbol`
  13071. * option is set, this defaults to the `symbolHeight`, otherwise 16.
  13072. *
  13073. * @productdesc {highmaps}
  13074. * In Highmaps, when the symbol is the gradient of a horizontal color
  13075. * axis, the width defaults to 200.
  13076. *
  13077. * @sample {highcharts} highcharts/legend/symbolwidth/
  13078. * Greater symbol width and padding
  13079. * @sample {highmaps} maps/legend/padding-itemmargin/
  13080. * Padding and item margins demonstrated
  13081. * @sample {highmaps} maps/legend/layout-vertical-sized/
  13082. * Sized vertical gradient
  13083. *
  13084. * @type {number}
  13085. * @apioption legend.symbolWidth
  13086. */
  13087. /**
  13088. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  13089. * to render the legend item texts.
  13090. *
  13091. * Prior to 4.1.7, when using HTML, [legend.navigation](
  13092. * #legend.navigation) was disabled.
  13093. *
  13094. * @type {boolean}
  13095. * @default false
  13096. * @apioption legend.useHTML
  13097. */
  13098. /**
  13099. * The width of the legend box. If a number is set, it translates to
  13100. * pixels. Since v7.0.2 it allows setting a percent string of the full
  13101. * chart width, for example `40%`.
  13102. *
  13103. * Defaults to the full chart width for legends below or above the
  13104. * chart, half the chart width for legends to the left and right.
  13105. *
  13106. * @sample {highcharts} highcharts/legend/width/
  13107. * Aligned to the plot area
  13108. * @sample {highcharts} highcharts/legend/width-percent/
  13109. * A percent of the chart width
  13110. *
  13111. * @type {number|string}
  13112. * @since 2.0
  13113. * @apioption legend.width
  13114. */
  13115. /**
  13116. * The pixel padding between the legend item symbol and the legend
  13117. * item text.
  13118. *
  13119. * @sample {highcharts} highcharts/legend/symbolpadding/
  13120. * Greater symbol width and padding
  13121. */
  13122. symbolPadding: 5,
  13123. /**
  13124. * The vertical alignment of the legend box. Can be one of `top`,
  13125. * `middle` or `bottom`. Vertical position can be further determined
  13126. * by the `y` option.
  13127. *
  13128. * In the case that the legend is aligned in a corner position, the
  13129. * `layout` option will determine whether to place it above/below
  13130. * or on the side of the plot area.
  13131. *
  13132. * When the [layout](#legend.layout) option is `proximate`, the
  13133. * `verticalAlign` option doesn't apply.
  13134. *
  13135. * @sample {highcharts} highcharts/legend/verticalalign/
  13136. * Legend 100px from the top of the chart
  13137. * @sample {highstock} stock/legend/align/
  13138. * Various legend options
  13139. * @sample {highmaps} maps/legend/alignment/
  13140. * Legend alignment
  13141. *
  13142. * @type {Highcharts.VerticalAlignValue}
  13143. * @since 2.0
  13144. */
  13145. verticalAlign: 'bottom',
  13146. // width: undefined,
  13147. /**
  13148. * The x offset of the legend relative to its horizontal alignment
  13149. * `align` within chart.spacingLeft and chart.spacingRight. Negative
  13150. * x moves it to the left, positive x moves it to the right.
  13151. *
  13152. * @sample {highcharts} highcharts/legend/width/
  13153. * Aligned to the plot area
  13154. *
  13155. * @since 2.0
  13156. */
  13157. x: 0,
  13158. /**
  13159. * The vertical offset of the legend relative to it's vertical alignment
  13160. * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
  13161. * Negative y moves it up, positive y moves it down.
  13162. *
  13163. * @sample {highcharts} highcharts/legend/verticalalign/
  13164. * Legend 100px from the top of the chart
  13165. * @sample {highstock} stock/legend/align/
  13166. * Various legend options
  13167. * @sample {highmaps} maps/legend/alignment/
  13168. * Legend alignment
  13169. *
  13170. * @since 2.0
  13171. */
  13172. y: 0,
  13173. /**
  13174. * A title to be added on top of the legend.
  13175. *
  13176. * @sample {highcharts} highcharts/legend/title/
  13177. * Legend title
  13178. * @sample {highmaps} maps/legend/alignment/
  13179. * Legend with title
  13180. *
  13181. * @since 3.0
  13182. */
  13183. title: {
  13184. /**
  13185. * A text or HTML string for the title.
  13186. *
  13187. * @type {string}
  13188. * @since 3.0
  13189. * @apioption legend.title.text
  13190. */
  13191. /**
  13192. * Generic CSS styles for the legend title.
  13193. *
  13194. * @see In styled mode, the legend title is styled with the
  13195. * `.highcharts-legend-title` class.
  13196. *
  13197. * @type {Highcharts.CSSObject}
  13198. * @default {"fontWeight": "bold"}
  13199. * @since 3.0
  13200. */
  13201. style: {
  13202. /**
  13203. * @ignore
  13204. */
  13205. fontWeight: 'bold'
  13206. }
  13207. }
  13208. },
  13209. /**
  13210. * The loading options control the appearance of the loading screen
  13211. * that covers the plot area on chart operations. This screen only
  13212. * appears after an explicit call to `chart.showLoading()`. It is a
  13213. * utility for developers to communicate to the end user that something
  13214. * is going on, for example while retrieving new data via an XHR connection.
  13215. * The "Loading..." text itself is not part of this configuration
  13216. * object, but part of the `lang` object.
  13217. */
  13218. loading: {
  13219. /**
  13220. * The duration in milliseconds of the fade out effect.
  13221. *
  13222. * @sample highcharts/loading/hideduration/
  13223. * Fade in and out over a second
  13224. *
  13225. * @type {number}
  13226. * @default 100
  13227. * @since 1.2.0
  13228. * @apioption loading.hideDuration
  13229. */
  13230. /**
  13231. * The duration in milliseconds of the fade in effect.
  13232. *
  13233. * @sample highcharts/loading/hideduration/
  13234. * Fade in and out over a second
  13235. *
  13236. * @type {number}
  13237. * @default 100
  13238. * @since 1.2.0
  13239. * @apioption loading.showDuration
  13240. */
  13241. /**
  13242. * CSS styles for the loading label `span`.
  13243. *
  13244. * @see In styled mode, the loading label is styled with the
  13245. * `.highcharts-loading-inner` class.
  13246. *
  13247. * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
  13248. * Vertically centered
  13249. * @sample {highstock} stock/loading/general/
  13250. * Label styles
  13251. *
  13252. * @type {Highcharts.CSSObject}
  13253. * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
  13254. * @since 1.2.0
  13255. */
  13256. labelStyle: {
  13257. /**
  13258. * @ignore
  13259. */
  13260. fontWeight: 'bold',
  13261. /**
  13262. * @ignore
  13263. */
  13264. position: 'relative',
  13265. /**
  13266. * @ignore
  13267. */
  13268. top: '45%'
  13269. },
  13270. /**
  13271. * CSS styles for the loading screen that covers the plot area.
  13272. *
  13273. * In styled mode, the loading label is styled with the
  13274. * `.highcharts-loading` class.
  13275. *
  13276. * @sample {highcharts|highmaps} highcharts/loading/style/
  13277. * Gray plot area, white text
  13278. * @sample {highstock} stock/loading/general/
  13279. * Gray plot area, white text
  13280. *
  13281. * @type {Highcharts.CSSObject}
  13282. * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
  13283. * @since 1.2.0
  13284. */
  13285. style: {
  13286. /**
  13287. * @ignore
  13288. */
  13289. position: 'absolute',
  13290. /**
  13291. * @ignore
  13292. */
  13293. backgroundColor: '#ffffff',
  13294. /**
  13295. * @ignore
  13296. */
  13297. opacity: 0.5,
  13298. /**
  13299. * @ignore
  13300. */
  13301. textAlign: 'center'
  13302. }
  13303. },
  13304. /**
  13305. * Options for the tooltip that appears when the user hovers over a
  13306. * series or point.
  13307. *
  13308. * @declare Highcharts.TooltipOptions
  13309. */
  13310. tooltip: {
  13311. /**
  13312. * The color of the tooltip border. When `undefined`, the border takes
  13313. * the color of the corresponding series or point.
  13314. *
  13315. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13316. * Follow series by default
  13317. * @sample {highcharts} highcharts/tooltip/bordercolor-black/
  13318. * Black border
  13319. * @sample {highstock} stock/tooltip/general/
  13320. * Styled tooltip
  13321. * @sample {highmaps} maps/tooltip/background-border/
  13322. * Background and border demo
  13323. *
  13324. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13325. * @apioption tooltip.borderColor
  13326. */
  13327. /**
  13328. * A CSS class name to apply to the tooltip's container div,
  13329. * allowing unique CSS styling for each chart.
  13330. *
  13331. * @type {string}
  13332. * @apioption tooltip.className
  13333. */
  13334. /**
  13335. * Since 4.1, the crosshair definitions are moved to the Axis object
  13336. * in order for a better separation from the tooltip. See
  13337. * [xAxis.crosshair](#xAxis.crosshair).
  13338. *
  13339. * @sample {highcharts} highcharts/tooltip/crosshairs-x/
  13340. * Enable a crosshair for the x value
  13341. *
  13342. * @deprecated
  13343. *
  13344. * @type {*}
  13345. * @default true
  13346. * @apioption tooltip.crosshairs
  13347. */
  13348. /**
  13349. * Distance from point to tooltip in pixels.
  13350. *
  13351. * @type {number}
  13352. * @default 16
  13353. * @apioption tooltip.distance
  13354. */
  13355. /**
  13356. * Whether the tooltip should follow the mouse as it moves across
  13357. * columns, pie slices and other point types with an extent.
  13358. * By default it behaves this way for pie, polygon, map, sankey
  13359. * and wordcloud series by override in the `plotOptions`
  13360. * for those series types.
  13361. *
  13362. * For touch moves to behave the same way, [followTouchMove](
  13363. * #tooltip.followTouchMove) must be `true` also.
  13364. *
  13365. * @type {boolean}
  13366. * @default {highcharts} false
  13367. * @default {highstock} false
  13368. * @default {highmaps} true
  13369. * @since 3.0
  13370. * @apioption tooltip.followPointer
  13371. */
  13372. /**
  13373. * Whether the tooltip should update as the finger moves on a touch
  13374. * device. If this is `true` and [chart.panning](#chart.panning) is
  13375. * set,`followTouchMove` will take over one-finger touches, so the user
  13376. * needs to use two fingers for zooming and panning.
  13377. *
  13378. * Note the difference to [followPointer](#tooltip.followPointer) that
  13379. * only defines the _position_ of the tooltip. If `followPointer` is
  13380. * false in for example a column series, the tooltip will show above or
  13381. * below the column, but as `followTouchMove` is true, the tooltip will
  13382. * jump from column to column as the user swipes across the plot area.
  13383. *
  13384. * @type {boolean}
  13385. * @default {highcharts} true
  13386. * @default {highstock} true
  13387. * @default {highmaps} false
  13388. * @since 3.0.1
  13389. * @apioption tooltip.followTouchMove
  13390. */
  13391. /**
  13392. * Callback function to format the text of the tooltip from scratch. In
  13393. * case of single or [shared](#tooltip.shared) tooltips, a string should
  13394. * be returned. In case of [split](#tooltip.split) tooltips, it should
  13395. * return an array where the first item is the header, and subsequent
  13396. * items are mapped to the points. Return `false` to disable tooltip for
  13397. * a specific point on series.
  13398. *
  13399. * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
  13400. * the tooltip is parsed and converted to SVG, therefore this isn't a
  13401. * complete HTML renderer. The following HTML tags are supported: `b`,
  13402. * `br`, `em`, `i`, `span`, `strong`. Spans can be styled with a `style`
  13403. * attribute, but only text-related CSS, that is shared with SVG, is
  13404. * handled.
  13405. *
  13406. * The available data in the formatter differ a bit depending on whether
  13407. * the tooltip is shared or split, or belongs to a single point. In a
  13408. * shared/split tooltip, all properties except `x`, which is common for
  13409. * all points, are kept in an array, `this.points`.
  13410. *
  13411. * Available data are:
  13412. *
  13413. * - **this.percentage (not shared) /**
  13414. * **this.points[i].percentage (shared)**:
  13415. * Stacked series and pies only. The point's percentage of the total.
  13416. *
  13417. * - **this.point (not shared) / this.points[i].point (shared)**:
  13418. * The point object. The point name, if defined, is available through
  13419. * `this.point.name`.
  13420. *
  13421. * - **this.points**:
  13422. * In a shared tooltip, this is an array containing all other
  13423. * properties for each point.
  13424. *
  13425. * - **this.series (not shared) / this.points[i].series (shared)**:
  13426. * The series object. The series name is available through
  13427. * `this.series.name`.
  13428. *
  13429. * - **this.total (not shared) / this.points[i].total (shared)**:
  13430. * Stacked series only. The total value at this point's x value.
  13431. *
  13432. * - **this.x**:
  13433. * The x value. This property is the same regardless of the tooltip
  13434. * being shared or not.
  13435. *
  13436. * - **this.y (not shared) / this.points[i].y (shared)**:
  13437. * The y value.
  13438. *
  13439. * @sample {highcharts} highcharts/tooltip/formatter-simple/
  13440. * Simple string formatting
  13441. * @sample {highcharts} highcharts/tooltip/formatter-shared/
  13442. * Formatting with shared tooltip
  13443. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  13444. * Formatting with split tooltip
  13445. * @sample highcharts/tooltip/formatter-conditional-default/
  13446. * Extending default formatter
  13447. * @sample {highstock} stock/tooltip/formatter/
  13448. * Formatting with shared tooltip
  13449. * @sample {highmaps} maps/tooltip/formatter/
  13450. * String formatting
  13451. *
  13452. * @type {Highcharts.TooltipFormatterCallbackFunction}
  13453. * @apioption tooltip.formatter
  13454. */
  13455. /**
  13456. * Callback function to format the text of the tooltip for
  13457. * visible null points.
  13458. * Works analogously to [formatter](#tooltip.formatter).
  13459. *
  13460. * @sample highcharts/plotoptions/series-nullformat
  13461. * Format data label and tooltip for null point.
  13462. *
  13463. * @type {Highcharts.TooltipFormatterCallbackFunction}
  13464. * @apioption tooltip.nullFormatter
  13465. */
  13466. /**
  13467. * The number of milliseconds to wait until the tooltip is hidden when
  13468. * mouse out from a point or chart.
  13469. *
  13470. * @type {number}
  13471. * @default 500
  13472. * @since 3.0
  13473. * @apioption tooltip.hideDelay
  13474. */
  13475. /**
  13476. * Whether to allow the tooltip to render outside the chart's SVG
  13477. * element box. By default (`false`), the tooltip is rendered within the
  13478. * chart's SVG element, which results in the tooltip being aligned
  13479. * inside the chart area. For small charts, this may result in clipping
  13480. * or overlapping. When `true`, a separate SVG element is created and
  13481. * overlaid on the page, allowing the tooltip to be aligned inside the
  13482. * page itself.
  13483. *
  13484. * Defaults to `true` if `chart.scrollablePlotArea` is activated,
  13485. * otherwise `false`.
  13486. *
  13487. * @sample highcharts/tooltip/outside
  13488. * Small charts with tooltips outside
  13489. *
  13490. * @type {boolean|undefined}
  13491. * @default undefined
  13492. * @since 6.1.1
  13493. * @apioption tooltip.outside
  13494. */
  13495. /**
  13496. * A callback function for formatting the HTML output for a single point
  13497. * in the tooltip. Like the `pointFormat` string, but with more
  13498. * flexibility.
  13499. *
  13500. * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
  13501. * @since 4.1.0
  13502. * @context Highcharts.Point
  13503. * @apioption tooltip.pointFormatter
  13504. */
  13505. /**
  13506. * A callback function to place the tooltip in a default position. The
  13507. * callback receives three parameters: `labelWidth`, `labelHeight` and
  13508. * `point`, where point contains values for `plotX` and `plotY` telling
  13509. * where the reference point is in the plot area. Add `chart.plotLeft`
  13510. * and `chart.plotTop` to get the full coordinates.
  13511. *
  13512. * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
  13513. * positioner is called for each of the boxes separately, including
  13514. * xAxis header. xAxis header is not a point, instead `point` argument
  13515. * contains info:
  13516. * `{ plotX: Number, plotY: Number, isHeader: Boolean }`
  13517. *
  13518. *
  13519. * The return should be an object containing x and y values, for example
  13520. * `{ x: 100, y: 100 }`.
  13521. *
  13522. * @sample {highcharts} highcharts/tooltip/positioner/
  13523. * A fixed tooltip position
  13524. * @sample {highstock} stock/tooltip/positioner/
  13525. * A fixed tooltip position on top of the chart
  13526. * @sample {highmaps} maps/tooltip/positioner/
  13527. * A fixed tooltip position
  13528. * @sample {highstock} stock/tooltip/split-positioner/
  13529. * Split tooltip with fixed positions
  13530. * @sample {highstock} stock/tooltip/positioner-scrollable-plotarea/
  13531. * Scrollable plot area combined with tooltip positioner
  13532. *
  13533. * @type {Highcharts.TooltipPositionerCallbackFunction}
  13534. * @since 2.2.4
  13535. * @apioption tooltip.positioner
  13536. */
  13537. /**
  13538. * The name of a symbol to use for the border around the tooltip. Can
  13539. * be one of: `"callout"`, `"circle"`, or `"square"`. When
  13540. * [tooltip.split](#tooltip.split)
  13541. * option is enabled, shape is applied to all boxes except header, which
  13542. * is controlled by
  13543. * [tooltip.headerShape](#tooltip.headerShape).
  13544. *
  13545. * Custom callbacks for symbol path generation can also be added to
  13546. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  13547. * [series.marker.symbol](plotOptions.line.marker.symbol).
  13548. *
  13549. * @type {Highcharts.TooltipShapeValue}
  13550. * @default callout
  13551. * @since 4.0
  13552. * @apioption tooltip.shape
  13553. */
  13554. /**
  13555. * The name of a symbol to use for the border around the tooltip
  13556. * header. Applies only when [tooltip.split](#tooltip.split) is
  13557. * enabled.
  13558. *
  13559. * Custom callbacks for symbol path generation can also be added to
  13560. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  13561. * [series.marker.symbol](plotOptions.line.marker.symbol).
  13562. *
  13563. * @see [tooltip.shape](#tooltip.shape)
  13564. *
  13565. * @sample {highstock} stock/tooltip/split-positioner/
  13566. * Different shapes for header and split boxes
  13567. *
  13568. * @type {Highcharts.TooltipShapeValue}
  13569. * @default callout
  13570. * @validvalue ["callout", "square"]
  13571. * @since 7.0
  13572. * @apioption tooltip.headerShape
  13573. */
  13574. /**
  13575. * When the tooltip is shared, the entire plot area will capture mouse
  13576. * movement or touch events. Tooltip texts for series types with ordered
  13577. * data (not pie, scatter, flags etc) will be shown in a single bubble.
  13578. * This is recommended for single series charts and for tablet/mobile
  13579. * optimized charts.
  13580. *
  13581. * See also [tooltip.split](#tooltip.split), that is better suited for
  13582. * charts with many series, especially line-type series. The
  13583. * `tooltip.split` option takes precedence over `tooltip.shared`.
  13584. *
  13585. * @sample {highcharts} highcharts/tooltip/shared-false/
  13586. * False by default
  13587. * @sample {highcharts} highcharts/tooltip/shared-true/
  13588. * True
  13589. * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
  13590. * True with x axis crosshair
  13591. * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
  13592. * True with mixed series types
  13593. *
  13594. * @type {boolean}
  13595. * @default false
  13596. * @since 2.1
  13597. * @product highcharts highstock
  13598. * @apioption tooltip.shared
  13599. */
  13600. /**
  13601. * Split the tooltip into one label per series, with the header close
  13602. * to the axis. This is recommended over [shared](#tooltip.shared)
  13603. * tooltips for charts with multiple line series, generally making them
  13604. * easier to read. This option takes precedence over `tooltip.shared`.
  13605. *
  13606. * @productdesc {highstock} In Highstock, tooltips are split by default
  13607. * since v6.0.0. Stock charts typically contain multi-dimension points
  13608. * and multiple panes, making split tooltips the preferred layout over
  13609. * the previous `shared` tooltip.
  13610. *
  13611. * @sample highcharts/tooltip/split/
  13612. * Split tooltip
  13613. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  13614. * Split tooltip and custom formatter callback
  13615. *
  13616. * @type {boolean}
  13617. * @default {highcharts} false
  13618. * @default {highstock} true
  13619. * @since 5.0.0
  13620. * @product highcharts highstock
  13621. * @apioption tooltip.split
  13622. */
  13623. /**
  13624. * Prevents the tooltip from switching or closing, when touched or
  13625. * pointed.
  13626. *
  13627. * @sample highcharts/tooltip/stickoncontact/
  13628. * Tooltip sticks on pointer contact
  13629. *
  13630. * @type {boolean}
  13631. * @since 8.0.1
  13632. * @apioption tooltip.stickOnContact
  13633. */
  13634. /**
  13635. * Use HTML to render the contents of the tooltip instead of SVG. Using
  13636. * HTML allows advanced formatting like tables and images in the
  13637. * tooltip. It is also recommended for rtl languages as it works around
  13638. * rtl bugs in early Firefox.
  13639. *
  13640. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  13641. * A table for value alignment
  13642. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  13643. * Full HTML tooltip
  13644. * @sample {highmaps} maps/tooltip/usehtml/
  13645. * Pure HTML tooltip
  13646. *
  13647. * @type {boolean}
  13648. * @default false
  13649. * @since 2.2
  13650. * @apioption tooltip.useHTML
  13651. */
  13652. /**
  13653. * How many decimals to show in each series' y value. This is
  13654. * overridable in each series' tooltip options object. The default is to
  13655. * preserve all decimals.
  13656. *
  13657. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  13658. * Set decimals, prefix and suffix for the value
  13659. * @sample {highmaps} maps/tooltip/valuedecimals/
  13660. * Set decimals, prefix and suffix for the value
  13661. *
  13662. * @type {number}
  13663. * @since 2.2
  13664. * @apioption tooltip.valueDecimals
  13665. */
  13666. /**
  13667. * A string to prepend to each series' y value. Overridable in each
  13668. * series' tooltip options object.
  13669. *
  13670. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  13671. * Set decimals, prefix and suffix for the value
  13672. * @sample {highmaps} maps/tooltip/valuedecimals/
  13673. * Set decimals, prefix and suffix for the value
  13674. *
  13675. * @type {string}
  13676. * @since 2.2
  13677. * @apioption tooltip.valuePrefix
  13678. */
  13679. /**
  13680. * A string to append to each series' y value. Overridable in each
  13681. * series' tooltip options object.
  13682. *
  13683. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  13684. * Set decimals, prefix and suffix for the value
  13685. * @sample {highmaps} maps/tooltip/valuedecimals/
  13686. * Set decimals, prefix and suffix for the value
  13687. *
  13688. * @type {string}
  13689. * @since 2.2
  13690. * @apioption tooltip.valueSuffix
  13691. */
  13692. /**
  13693. * The format for the date in the tooltip header if the X axis is a
  13694. * datetime axis. The default is a best guess based on the smallest
  13695. * distance between points in the chart.
  13696. *
  13697. * @sample {highcharts} highcharts/tooltip/xdateformat/
  13698. * A different format
  13699. *
  13700. * @type {string}
  13701. * @product highcharts highstock gantt
  13702. * @apioption tooltip.xDateFormat
  13703. */
  13704. /**
  13705. * How many decimals to show for the `point.change` value when the
  13706. * `series.compare` option is set. This is overridable in each series'
  13707. * tooltip options object. The default is to preserve all decimals.
  13708. *
  13709. * @type {number}
  13710. * @since 1.0.1
  13711. * @product highstock
  13712. * @apioption tooltip.changeDecimals
  13713. */
  13714. /**
  13715. * Enable or disable the tooltip.
  13716. *
  13717. * @sample {highcharts} highcharts/tooltip/enabled/
  13718. * Disabled
  13719. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  13720. * Disable tooltip and show values on chart instead
  13721. */
  13722. enabled: true,
  13723. /**
  13724. * Enable or disable animation of the tooltip.
  13725. *
  13726. * @type {boolean}
  13727. * @default true
  13728. * @since 2.3.0
  13729. */
  13730. animation: svg,
  13731. /**
  13732. * The radius of the rounded border corners.
  13733. *
  13734. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13735. * 5px by default
  13736. * @sample {highcharts} highcharts/tooltip/borderradius-0/
  13737. * Square borders
  13738. * @sample {highmaps} maps/tooltip/background-border/
  13739. * Background and border demo
  13740. */
  13741. borderRadius: 3,
  13742. /**
  13743. * For series on a datetime axes, the date format in the tooltip's
  13744. * header will by default be guessed based on the closest data points.
  13745. * This member gives the default string representations used for
  13746. * each unit. For an overview of the replacement codes, see
  13747. * [dateFormat](/class-reference/Highcharts#dateFormat).
  13748. *
  13749. * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
  13750. *
  13751. * @type {Highcharts.Dictionary<string>}
  13752. * @product highcharts highstock gantt
  13753. */
  13754. dateTimeLabelFormats: {
  13755. /** @internal */
  13756. millisecond: '%A, %b %e, %H:%M:%S.%L',
  13757. /** @internal */
  13758. second: '%A, %b %e, %H:%M:%S',
  13759. /** @internal */
  13760. minute: '%A, %b %e, %H:%M',
  13761. /** @internal */
  13762. hour: '%A, %b %e, %H:%M',
  13763. /** @internal */
  13764. day: '%A, %b %e, %Y',
  13765. /** @internal */
  13766. week: 'Week from %A, %b %e, %Y',
  13767. /** @internal */
  13768. month: '%B %Y',
  13769. /** @internal */
  13770. year: '%Y'
  13771. },
  13772. /**
  13773. * A string to append to the tooltip format.
  13774. *
  13775. * @sample {highcharts} highcharts/tooltip/footerformat/
  13776. * A table for value alignment
  13777. * @sample {highmaps} maps/tooltip/format/
  13778. * Format demo
  13779. *
  13780. * @since 2.2
  13781. */
  13782. footerFormat: '',
  13783. /**
  13784. * Padding inside the tooltip, in pixels.
  13785. *
  13786. * @since 5.0.0
  13787. */
  13788. padding: 8,
  13789. /**
  13790. * Proximity snap for graphs or single points. It defaults to 10 for
  13791. * mouse-powered devices and 25 for touch devices.
  13792. *
  13793. * Note that in most cases the whole plot area captures the mouse
  13794. * movement, and in these cases `tooltip.snap` doesn't make sense. This
  13795. * applies when [stickyTracking](#plotOptions.series.stickyTracking)
  13796. * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
  13797. * or [split](#tooltip.split).
  13798. *
  13799. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13800. * 10 px by default
  13801. * @sample {highcharts} highcharts/tooltip/snap-50/
  13802. * 50 px on graph
  13803. *
  13804. * @type {number}
  13805. * @default 10/25
  13806. * @since 1.2.0
  13807. * @product highcharts highstock
  13808. */
  13809. snap: isTouchDevice ? 25 : 10,
  13810. /**
  13811. * The HTML of the tooltip header line. Variables are enclosed by
  13812. * curly brackets. Available variables are `point.key`, `series.name`,
  13813. * `series.color` and other members from the `point` and `series`
  13814. * objects. The `point.key` variable contains the category name, x
  13815. * value or datetime string depending on the type of axis. For datetime
  13816. * axes, the `point.key` date format can be set using
  13817. * `tooltip.xDateFormat`.
  13818. *
  13819. * @sample {highcharts} highcharts/tooltip/footerformat/
  13820. * An HTML table in the tooltip
  13821. * @sample {highstock} highcharts/tooltip/footerformat/
  13822. * An HTML table in the tooltip
  13823. * @sample {highmaps} maps/tooltip/format/
  13824. * Format demo
  13825. *
  13826. * @type {string}
  13827. * @apioption tooltip.headerFormat
  13828. */
  13829. headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
  13830. /**
  13831. * The HTML of the null point's line in the tooltip. Works analogously
  13832. * to [pointFormat](#tooltip.pointFormat).
  13833. *
  13834. * @sample {highcharts} highcharts/plotoptions/series-nullformat
  13835. * Format data label and tooltip for null point.
  13836. *
  13837. * @type {string}
  13838. * @apioption tooltip.nullFormat
  13839. */
  13840. /**
  13841. * The HTML of the point's line in the tooltip. Variables are enclosed
  13842. * by curly brackets. Available variables are point.x, point.y, series.
  13843. * name and series.color and other properties on the same form.
  13844. * Furthermore, `point.y` can be extended by the `tooltip.valuePrefix`
  13845. * and `tooltip.valueSuffix` variables. This can also be overridden for
  13846. * each series, which makes it a good hook for displaying units.
  13847. *
  13848. * In styled mode, the dot is colored by a class name rather
  13849. * than the point color.
  13850. *
  13851. * @sample {highcharts} highcharts/tooltip/pointformat/
  13852. * A different point format with value suffix
  13853. * @sample {highmaps} maps/tooltip/format/
  13854. * Format demo
  13855. *
  13856. * @type {string}
  13857. * @since 2.2
  13858. * @apioption tooltip.pointFormat
  13859. */
  13860. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
  13861. /**
  13862. * The background color or gradient for the tooltip.
  13863. *
  13864. * In styled mode, the stroke width is set in the
  13865. * `.highcharts-tooltip-box` class.
  13866. *
  13867. * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
  13868. * Yellowish background
  13869. * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
  13870. * Gradient
  13871. * @sample {highcharts} highcharts/css/tooltip-border-background/
  13872. * Tooltip in styled mode
  13873. * @sample {highstock} stock/tooltip/general/
  13874. * Custom tooltip
  13875. * @sample {highstock} highcharts/css/tooltip-border-background/
  13876. * Tooltip in styled mode
  13877. * @sample {highmaps} maps/tooltip/background-border/
  13878. * Background and border demo
  13879. * @sample {highmaps} highcharts/css/tooltip-border-background/
  13880. * Tooltip in styled mode
  13881. *
  13882. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13883. */
  13884. backgroundColor: color('#f7f7f7')
  13885. .setOpacity(0.85).get(),
  13886. /**
  13887. * The pixel width of the tooltip border.
  13888. *
  13889. * In styled mode, the stroke width is set in the
  13890. * `.highcharts-tooltip-box` class.
  13891. *
  13892. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13893. * 2px by default
  13894. * @sample {highcharts} highcharts/tooltip/borderwidth/
  13895. * No border (shadow only)
  13896. * @sample {highcharts} highcharts/css/tooltip-border-background/
  13897. * Tooltip in styled mode
  13898. * @sample {highstock} stock/tooltip/general/
  13899. * Custom tooltip
  13900. * @sample {highstock} highcharts/css/tooltip-border-background/
  13901. * Tooltip in styled mode
  13902. * @sample {highmaps} maps/tooltip/background-border/
  13903. * Background and border demo
  13904. * @sample {highmaps} highcharts/css/tooltip-border-background/
  13905. * Tooltip in styled mode
  13906. */
  13907. borderWidth: 1,
  13908. /**
  13909. * Whether to apply a drop shadow to the tooltip.
  13910. *
  13911. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13912. * True by default
  13913. * @sample {highcharts} highcharts/tooltip/shadow/
  13914. * False
  13915. * @sample {highmaps} maps/tooltip/positioner/
  13916. * Fixed tooltip position, border and shadow disabled
  13917. *
  13918. * @type {boolean|Highcharts.ShadowOptionsObject}
  13919. */
  13920. shadow: true,
  13921. /**
  13922. * CSS styles for the tooltip. The tooltip can also be styled through
  13923. * the CSS class `.highcharts-tooltip`.
  13924. *
  13925. * Note that the default `pointerEvents` style makes the tooltip ignore
  13926. * mouse events, so in order to use clickable tooltips, this value must
  13927. * be set to `auto`.
  13928. *
  13929. * @sample {highcharts} highcharts/tooltip/style/
  13930. * Greater padding, bold text
  13931. *
  13932. * @type {Highcharts.CSSObject}
  13933. */
  13934. style: {
  13935. /** @internal */
  13936. color: '#333333',
  13937. /** @internal */
  13938. cursor: 'default',
  13939. /** @internal */
  13940. fontSize: '12px',
  13941. /** @internal */
  13942. whiteSpace: 'nowrap'
  13943. }
  13944. },
  13945. /**
  13946. * Highchart by default puts a credits label in the lower right corner
  13947. * of the chart. This can be changed using these options.
  13948. */
  13949. credits: {
  13950. /**
  13951. * Credits for map source to be concatenated with conventional credit
  13952. * text. By default this is a format string that collects copyright
  13953. * information from the map if available.
  13954. *
  13955. * @see [mapTextFull](#credits.mapTextFull)
  13956. * @see [text](#credits.text)
  13957. *
  13958. * @type {string}
  13959. * @default \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
  13960. * @since 4.2.2
  13961. * @product highmaps
  13962. * @apioption credits.mapText
  13963. */
  13964. /**
  13965. * Detailed credits for map source to be displayed on hover of credits
  13966. * text. By default this is a format string that collects copyright
  13967. * information from the map if available.
  13968. *
  13969. * @see [mapText](#credits.mapText)
  13970. * @see [text](#credits.text)
  13971. *
  13972. * @type {string}
  13973. * @default {geojson.copyright}
  13974. * @since 4.2.2
  13975. * @product highmaps
  13976. * @apioption credits.mapTextFull
  13977. */
  13978. /**
  13979. * Whether to show the credits text.
  13980. *
  13981. * @sample {highcharts} highcharts/credits/enabled-false/
  13982. * Credits disabled
  13983. * @sample {highstock} stock/credits/enabled/
  13984. * Credits disabled
  13985. * @sample {highmaps} maps/credits/enabled-false/
  13986. * Credits disabled
  13987. */
  13988. enabled: true,
  13989. /**
  13990. * The URL for the credits label.
  13991. *
  13992. * @sample {highcharts} highcharts/credits/href/
  13993. * Custom URL and text
  13994. * @sample {highmaps} maps/credits/customized/
  13995. * Custom URL and text
  13996. */
  13997. href: 'https://www.highcharts.com?credits',
  13998. /**
  13999. * Position configuration for the credits label.
  14000. *
  14001. * @sample {highcharts} highcharts/credits/position-left/
  14002. * Left aligned
  14003. * @sample {highcharts} highcharts/credits/position-left/
  14004. * Left aligned
  14005. * @sample {highmaps} maps/credits/customized/
  14006. * Left aligned
  14007. * @sample {highmaps} maps/credits/customized/
  14008. * Left aligned
  14009. *
  14010. * @type {Highcharts.AlignObject}
  14011. * @since 2.1
  14012. */
  14013. position: {
  14014. /** @internal */
  14015. align: 'right',
  14016. /** @internal */
  14017. x: -10,
  14018. /** @internal */
  14019. verticalAlign: 'bottom',
  14020. /** @internal */
  14021. y: -5
  14022. },
  14023. /**
  14024. * CSS styles for the credits label.
  14025. *
  14026. * @see In styled mode, credits styles can be set with the
  14027. * `.highcharts-credits` class.
  14028. *
  14029. * @type {Highcharts.CSSObject}
  14030. */
  14031. style: {
  14032. /** @internal */
  14033. cursor: 'pointer',
  14034. /** @internal */
  14035. color: '#999999',
  14036. /** @internal */
  14037. fontSize: '9px'
  14038. },
  14039. /**
  14040. * The text for the credits label.
  14041. *
  14042. * @productdesc {highmaps}
  14043. * If a map is loaded as GeoJSON, the text defaults to
  14044. * `Highcharts @ {map-credits}`. Otherwise, it defaults to
  14045. * `Highcharts.com`.
  14046. *
  14047. * @sample {highcharts} highcharts/credits/href/
  14048. * Custom URL and text
  14049. * @sample {highmaps} maps/credits/customized/
  14050. * Custom URL and text
  14051. */
  14052. text: 'Highcharts.com'
  14053. }
  14054. };
  14055. /* eslint-disable spaced-comment */
  14056. '';
  14057. /**
  14058. * Global `Time` object with default options. Since v6.0.5, time settings can be
  14059. * applied individually for each chart. If no individual settings apply, this
  14060. * `Time` object is shared by all instances.
  14061. *
  14062. * @name Highcharts.time
  14063. * @type {Highcharts.Time}
  14064. */
  14065. H.time = new Time(merge(H.defaultOptions.global, H.defaultOptions.time));
  14066. /**
  14067. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
  14068. * human readable date string. The format is a subset of the formats for PHP's
  14069. * [strftime](https://www.php.net/manual/en/function.strftime.php) function.
  14070. * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
  14071. *
  14072. * Since v6.0.5, all internal dates are formatted through the
  14073. * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
  14074. * The `Highcharts.dateFormat` function only reflects global time settings set
  14075. * with `setOptions`.
  14076. *
  14077. * Supported format keys:
  14078. * - `%a`: Short weekday, like 'Mon'
  14079. * - `%A`: Long weekday, like 'Monday'
  14080. * - `%d`: Two digit day of the month, 01 to 31
  14081. * - `%e`: Day of the month, 1 through 31
  14082. * - `%w`: Day of the week, 0 through 6
  14083. * - `%b`: Short month, like 'Jan'
  14084. * - `%B`: Long month, like 'January'
  14085. * - `%m`: Two digit month number, 01 through 12
  14086. * - `%y`: Two digits year, like 09 for 2009
  14087. * - `%Y`: Four digits year, like 2009
  14088. * - `%H`: Two digits hours in 24h format, 00 through 23
  14089. * - `%k`: Hours in 24h format, 0 through 23
  14090. * - `%I`: Two digits hours in 12h format, 00 through 11
  14091. * - `%l`: Hours in 12h format, 1 through 12
  14092. * - `%M`: Two digits minutes, 00 through 59
  14093. * - `%p`: Upper case AM or PM
  14094. * - `%P`: Lower case AM or PM
  14095. * - `%S`: Two digits seconds, 00 through 59
  14096. * - `%L`: Milliseconds (naming from Ruby)
  14097. *
  14098. * @function Highcharts.dateFormat
  14099. *
  14100. * @param {string} format
  14101. * The desired format where various time representations are prefixed
  14102. * with `%`.
  14103. *
  14104. * @param {number} timestamp
  14105. * The JavaScript timestamp.
  14106. *
  14107. * @param {boolean} [capitalize=false]
  14108. * Upper case first letter in the return.
  14109. *
  14110. * @return {string}
  14111. * The formatted date.
  14112. */
  14113. H.dateFormat = function (format, timestamp, capitalize) {
  14114. return H.time.dateFormat(format, timestamp, capitalize);
  14115. };
  14116. var optionsModule = {
  14117. dateFormat: H.dateFormat,
  14118. defaultOptions: H.defaultOptions,
  14119. time: H.time
  14120. };
  14121. return optionsModule;
  14122. });
  14123. _registerModule(_modules, 'Core/Axis/Axis.js', [_modules['Core/Color.js'], _modules['Core/Globals.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Utilities.js'], _modules['Core/Options.js']], function (Color, H, Tick, U, O) {
  14124. /* *
  14125. *
  14126. * (c) 2010-2020 Torstein Honsi
  14127. *
  14128. * License: www.highcharts.com/license
  14129. *
  14130. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  14131. *
  14132. * */
  14133. var addEvent = U.addEvent,
  14134. animObject = U.animObject,
  14135. arrayMax = U.arrayMax,
  14136. arrayMin = U.arrayMin,
  14137. clamp = U.clamp,
  14138. correctFloat = U.correctFloat,
  14139. defined = U.defined,
  14140. destroyObjectProperties = U.destroyObjectProperties,
  14141. error = U.error,
  14142. extend = U.extend,
  14143. fireEvent = U.fireEvent,
  14144. format = U.format,
  14145. getMagnitude = U.getMagnitude,
  14146. isArray = U.isArray,
  14147. isFunction = U.isFunction,
  14148. isNumber = U.isNumber,
  14149. isString = U.isString,
  14150. merge = U.merge,
  14151. normalizeTickInterval = U.normalizeTickInterval,
  14152. objectEach = U.objectEach,
  14153. pick = U.pick,
  14154. relativeLength = U.relativeLength,
  14155. removeEvent = U.removeEvent,
  14156. splat = U.splat,
  14157. syncTimeout = U.syncTimeout;
  14158. /**
  14159. * Options for the path on the Axis to be calculated.
  14160. * @interface Highcharts.AxisPlotLinePathOptionsObject
  14161. */ /**
  14162. * Axis value.
  14163. * @name Highcharts.AxisPlotLinePathOptionsObject#value
  14164. * @type {number|undefined}
  14165. */ /**
  14166. * Line width used for calculation crisp line coordinates. Defaults to 1.
  14167. * @name Highcharts.AxisPlotLinePathOptionsObject#lineWidth
  14168. * @type {number|undefined}
  14169. */ /**
  14170. * If `false`, the function will return null when it falls outside the axis
  14171. * bounds. If `true`, the function will return a path aligned to the plot area
  14172. * sides if it falls outside. If `pass`, it will return a path outside.
  14173. * @name Highcharts.AxisPlotLinePathOptionsObject#force
  14174. * @type {string|boolean|undefined}
  14175. */ /**
  14176. * Used in Highstock. When `true`, plot paths (crosshair, plotLines, gridLines)
  14177. * will be rendered on all axes when defined on the first axis.
  14178. * @name Highcharts.AxisPlotLinePathOptionsObject#acrossPanes
  14179. * @type {boolean|undefined}
  14180. */ /**
  14181. * Use old coordinates (for resizing and rescaling).
  14182. * If not set, defaults to `false`.
  14183. * @name Highcharts.AxisPlotLinePathOptionsObject#old
  14184. * @type {boolean|undefined}
  14185. */ /**
  14186. * If given, return the plot line path of a pixel position on the axis.
  14187. * @name Highcharts.AxisPlotLinePathOptionsObject#translatedValue
  14188. * @type {number|undefined}
  14189. */ /**
  14190. * Used in Polar axes. Reverse the positions for concatenation of polygonal
  14191. * plot bands
  14192. * @name Highcharts.AxisPlotLinePathOptionsObject#reverse
  14193. * @type {boolean|undefined}
  14194. */
  14195. /**
  14196. * Options for crosshairs on axes.
  14197. *
  14198. * @product highstock
  14199. *
  14200. * @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
  14201. */
  14202. /**
  14203. * @typedef {"navigator"|"pan"|"rangeSelectorButton"|"rangeSelectorInput"|"scrollbar"|"traverseUpButton"|"zoom"} Highcharts.AxisExtremesTriggerValue
  14204. */
  14205. /**
  14206. * @callback Highcharts.AxisEventCallbackFunction
  14207. *
  14208. * @param {Highcharts.Axis} this
  14209. */
  14210. /**
  14211. * @callback Highcharts.AxisLabelsFormatterCallbackFunction
  14212. *
  14213. * @param {Highcharts.AxisLabelsFormatterContextObject<number>} this
  14214. *
  14215. * @param {Highcharts.AxisLabelsFormatterContextObject<string>} that
  14216. *
  14217. * @return {string}
  14218. */
  14219. /**
  14220. * @interface Highcharts.AxisLabelsFormatterContextObject<T>
  14221. */ /**
  14222. * @name Highcharts.AxisLabelsFormatterContextObject<T>#axis
  14223. * @type {Highcharts.Axis}
  14224. */ /**
  14225. * @name Highcharts.AxisLabelsFormatterContextObject<T>#chart
  14226. * @type {Highcharts.Chart}
  14227. */ /**
  14228. * @name Highcharts.AxisLabelsFormatterContextObject<T>#isFirst
  14229. * @type {boolean}
  14230. */ /**
  14231. * @name Highcharts.AxisLabelsFormatterContextObject<T>#isLast
  14232. * @type {boolean}
  14233. */ /**
  14234. * @name Highcharts.AxisLabelsFormatterContextObject<T>#pos
  14235. * @type {number}
  14236. */ /**
  14237. * This can be either a numeric value or a category string.
  14238. * @name Highcharts.AxisLabelsFormatterContextObject<T>#value
  14239. * @type {T}
  14240. */
  14241. /**
  14242. * Options for axes.
  14243. *
  14244. * @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
  14245. */
  14246. /**
  14247. * @callback Highcharts.AxisPointBreakEventCallbackFunction
  14248. *
  14249. * @param {Highcharts.Axis} this
  14250. *
  14251. * @param {Highcharts.AxisPointBreakEventObject} evt
  14252. */
  14253. /**
  14254. * @interface Highcharts.AxisPointBreakEventObject
  14255. */ /**
  14256. * @name Highcharts.AxisPointBreakEventObject#brk
  14257. * @type {Highcharts.Dictionary<number>}
  14258. */ /**
  14259. * @name Highcharts.AxisPointBreakEventObject#point
  14260. * @type {Highcharts.Point}
  14261. */ /**
  14262. * @name Highcharts.AxisPointBreakEventObject#preventDefault
  14263. * @type {Function}
  14264. */ /**
  14265. * @name Highcharts.AxisPointBreakEventObject#target
  14266. * @type {Highcharts.SVGElement}
  14267. */ /**
  14268. * @name Highcharts.AxisPointBreakEventObject#type
  14269. * @type {"pointBreak"|"pointInBreak"}
  14270. */
  14271. /**
  14272. * @callback Highcharts.AxisSetExtremesEventCallbackFunction
  14273. *
  14274. * @param {Highcharts.Axis} this
  14275. *
  14276. * @param {Highcharts.AxisSetExtremesEventObject} evt
  14277. */
  14278. /**
  14279. * @interface Highcharts.AxisSetExtremesEventObject
  14280. * @extends Highcharts.ExtremesObject
  14281. */ /**
  14282. * @name Highcharts.AxisSetExtremesEventObject#preventDefault
  14283. * @type {Function}
  14284. */ /**
  14285. * @name Highcharts.AxisSetExtremesEventObject#target
  14286. * @type {Highcharts.SVGElement}
  14287. */ /**
  14288. * @name Highcharts.AxisSetExtremesEventObject#trigger
  14289. * @type {Highcharts.AxisExtremesTriggerValue|string}
  14290. */ /**
  14291. * @name Highcharts.AxisSetExtremesEventObject#type
  14292. * @type {"setExtremes"}
  14293. */
  14294. /**
  14295. * @callback Highcharts.AxisTickPositionerCallbackFunction
  14296. *
  14297. * @param {Highcharts.Axis} this
  14298. *
  14299. * @return {Highcharts.AxisTickPositionsArray}
  14300. */
  14301. /**
  14302. * @interface Highcharts.AxisTickPositionsArray
  14303. * @augments Array<number>
  14304. */
  14305. /**
  14306. * @typedef {"high"|"low"|"middle"} Highcharts.AxisTitleAlignValue
  14307. */
  14308. /**
  14309. * @typedef {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} Highcharts.AxisTitleOptions
  14310. */
  14311. /**
  14312. * @typedef {"linear"|"logarithmic"|"datetime"|"category"|"treegrid"} Highcharts.AxisTypeValue
  14313. */
  14314. /**
  14315. * The returned object literal from the {@link Highcharts.Axis#getExtremes}
  14316. * function.
  14317. *
  14318. * @interface Highcharts.ExtremesObject
  14319. */ /**
  14320. * The maximum value of the axis' associated series.
  14321. * @name Highcharts.ExtremesObject#dataMax
  14322. * @type {number}
  14323. */ /**
  14324. * The minimum value of the axis' associated series.
  14325. * @name Highcharts.ExtremesObject#dataMin
  14326. * @type {number}
  14327. */ /**
  14328. * The maximum axis value, either automatic or set manually. If the `max` option
  14329. * is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
  14330. * the same as `dataMax`.
  14331. * @name Highcharts.ExtremesObject#max
  14332. * @type {number}
  14333. */ /**
  14334. * The minimum axis value, either automatic or set manually. If the `min` option
  14335. * is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
  14336. * the same as `dataMin`.
  14337. * @name Highcharts.ExtremesObject#min
  14338. * @type {number}
  14339. */ /**
  14340. * The user defined maximum, either from the `max` option or from a zoom or
  14341. * `setExtremes` action.
  14342. * @name Highcharts.ExtremesObject#userMax
  14343. * @type {number}
  14344. */ /**
  14345. * The user defined minimum, either from the `min` option or from a zoom or
  14346. * `setExtremes` action.
  14347. * @name Highcharts.ExtremesObject#userMin
  14348. * @type {number}
  14349. */
  14350. /**
  14351. * Formatter function for the text of a crosshair label.
  14352. *
  14353. * @callback Highcharts.XAxisCrosshairLabelFormatterCallbackFunction
  14354. *
  14355. * @param {Highcharts.Axis} this
  14356. * Axis context
  14357. *
  14358. * @param {number} value
  14359. * Y value of the data point
  14360. *
  14361. * @return {string}
  14362. */
  14363. var defaultOptions = O.defaultOptions;
  14364. var deg2rad = H.deg2rad;
  14365. /**
  14366. * Create a new axis object. Called internally when instanciating a new chart or
  14367. * adding axes by {@link Highcharts.Chart#addAxis}.
  14368. *
  14369. * A chart can have from 0 axes (pie chart) to multiples. In a normal, single
  14370. * series cartesian chart, there is one X axis and one Y axis.
  14371. *
  14372. * The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
  14373. * an array of Axis objects. If there is only one axis, it can be referenced
  14374. * through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
  14375. * pattern goes for Y axes.
  14376. *
  14377. * If you need to get the axes from a series object, use the `series.xAxis` and
  14378. * `series.yAxis` properties. These are not arrays, as one series can only be
  14379. * associated to one X and one Y axis.
  14380. *
  14381. * A third way to reference the axis programmatically is by `id`. Add an `id` in
  14382. * the axis configuration options, and get the axis by
  14383. * {@link Highcharts.Chart#get}.
  14384. *
  14385. * Configuration options for the axes are given in options.xAxis and
  14386. * options.yAxis.
  14387. *
  14388. * @class
  14389. * @name Highcharts.Axis
  14390. *
  14391. * @param {Highcharts.Chart} chart
  14392. * The Chart instance to apply the axis on.
  14393. *
  14394. * @param {Highcharts.AxisOptions} userOptions
  14395. * Axis options.
  14396. */
  14397. var Axis = /** @class */ (function () {
  14398. /* *
  14399. *
  14400. * Constructors
  14401. *
  14402. * */
  14403. function Axis(chart, userOptions) {
  14404. this.alternateBands = void 0;
  14405. this.bottom = void 0;
  14406. this.categories = void 0;
  14407. this.chart = void 0;
  14408. this.closestPointRange = void 0;
  14409. this.coll = void 0;
  14410. this.hasNames = void 0;
  14411. this.hasVisibleSeries = void 0;
  14412. this.height = void 0;
  14413. this.isLinked = void 0;
  14414. this.labelEdge = void 0; // @todo
  14415. this.labelFormatter = void 0;
  14416. this.left = void 0;
  14417. this.len = void 0;
  14418. this.max = void 0;
  14419. this.maxLabelLength = void 0;
  14420. this.min = void 0;
  14421. this.minorTickInterval = void 0;
  14422. this.minorTicks = void 0;
  14423. this.minPixelPadding = void 0;
  14424. this.names = void 0;
  14425. this.offset = void 0;
  14426. this.oldMax = void 0;
  14427. this.oldMin = void 0;
  14428. this.options = void 0;
  14429. this.overlap = void 0;
  14430. this.paddedTicks = void 0;
  14431. this.plotLinesAndBands = void 0;
  14432. this.plotLinesAndBandsGroups = void 0;
  14433. this.pointRange = void 0;
  14434. this.pointRangePadding = void 0;
  14435. this.pos = void 0;
  14436. this.positiveValuesOnly = void 0;
  14437. this.right = void 0;
  14438. this.series = void 0;
  14439. this.side = void 0;
  14440. this.tickAmount = void 0;
  14441. this.tickInterval = void 0;
  14442. this.tickmarkOffset = void 0;
  14443. this.tickPositions = void 0;
  14444. this.tickRotCorr = void 0;
  14445. this.ticks = void 0;
  14446. this.top = void 0;
  14447. this.transA = void 0;
  14448. this.transB = void 0;
  14449. this.translationSlope = void 0;
  14450. this.userOptions = void 0;
  14451. this.visible = void 0;
  14452. this.width = void 0;
  14453. this.zoomEnabled = void 0;
  14454. this.init(chart, userOptions);
  14455. }
  14456. /* *
  14457. *
  14458. * Functions
  14459. *
  14460. * */
  14461. /**
  14462. * Overrideable function to initialize the axis.
  14463. *
  14464. * @see {@link Axis}
  14465. *
  14466. * @function Highcharts.Axis#init
  14467. *
  14468. * @param {Highcharts.Chart} chart
  14469. * The Chart instance to apply the axis on.
  14470. *
  14471. * @param {Highcharts.AxisOptions} userOptions
  14472. * Axis options.
  14473. *
  14474. * @fires Highcharts.Axis#event:afterInit
  14475. * @fires Highcharts.Axis#event:init
  14476. */
  14477. Axis.prototype.init = function (chart, userOptions) {
  14478. var isXAxis = userOptions.isX,
  14479. axis = this;
  14480. /**
  14481. * The Chart that the axis belongs to.
  14482. *
  14483. * @name Highcharts.Axis#chart
  14484. * @type {Highcharts.Chart}
  14485. */
  14486. axis.chart = chart;
  14487. /**
  14488. * Whether the axis is horizontal.
  14489. *
  14490. * @name Highcharts.Axis#horiz
  14491. * @type {boolean|undefined}
  14492. */
  14493. axis.horiz = chart.inverted && !axis.isZAxis ? !isXAxis : isXAxis;
  14494. /**
  14495. * Whether the axis is the x-axis.
  14496. *
  14497. * @name Highcharts.Axis#isXAxis
  14498. * @type {boolean|undefined}
  14499. */
  14500. axis.isXAxis = isXAxis;
  14501. /**
  14502. * The collection where the axis belongs, for example `xAxis`, `yAxis`
  14503. * or `colorAxis`. Corresponds to properties on Chart, for example
  14504. * {@link Chart.xAxis}.
  14505. *
  14506. * @name Highcharts.Axis#coll
  14507. * @type {string}
  14508. */
  14509. axis.coll = axis.coll || (isXAxis ? 'xAxis' : 'yAxis');
  14510. fireEvent(this, 'init', { userOptions: userOptions });
  14511. axis.opposite = userOptions.opposite; // needed in setOptions
  14512. /**
  14513. * The side on which the axis is rendered. 0 is top, 1 is right, 2
  14514. * is bottom and 3 is left.
  14515. *
  14516. * @name Highcharts.Axis#side
  14517. * @type {number}
  14518. */
  14519. axis.side = userOptions.side || (axis.horiz ?
  14520. (axis.opposite ? 0 : 2) : // top : bottom
  14521. (axis.opposite ? 1 : 3)); // right : left
  14522. /**
  14523. * Current options for the axis after merge of defaults and user's
  14524. * options.
  14525. *
  14526. * @name Highcharts.Axis#options
  14527. * @type {Highcharts.AxisOptions}
  14528. */
  14529. axis.setOptions(userOptions);
  14530. var options = this.options,
  14531. type = options.type;
  14532. axis.labelFormatter = (options.labels.formatter ||
  14533. // can be overwritten by dynamic format
  14534. axis.defaultLabelFormatter);
  14535. /**
  14536. * User's options for this axis without defaults.
  14537. *
  14538. * @name Highcharts.Axis#userOptions
  14539. * @type {Highcharts.AxisOptions}
  14540. */
  14541. axis.userOptions = userOptions;
  14542. axis.minPixelPadding = 0;
  14543. /**
  14544. * Whether the axis is reversed. Based on the `axis.reversed`,
  14545. * option, but inverted charts have reversed xAxis by default.
  14546. *
  14547. * @name Highcharts.Axis#reversed
  14548. * @type {boolean}
  14549. */
  14550. axis.reversed = options.reversed;
  14551. axis.visible = options.visible !== false;
  14552. axis.zoomEnabled = options.zoomEnabled !== false;
  14553. // Initial categories
  14554. axis.hasNames =
  14555. type === 'category' || options.categories === true;
  14556. /**
  14557. * If categories are present for the axis, names are used instead of
  14558. * numbers for that axis.
  14559. *
  14560. * Since Highcharts 3.0, categories can also be extracted by giving each
  14561. * point a name and setting axis type to `category`. However, if you
  14562. * have multiple series, best practice remains defining the `categories`
  14563. * array.
  14564. *
  14565. * @see [xAxis.categories](/highcharts/xAxis.categories)
  14566. *
  14567. * @name Highcharts.Axis#categories
  14568. * @type {Array<string>}
  14569. * @readonly
  14570. */
  14571. axis.categories = options.categories || axis.hasNames;
  14572. if (!axis.names) { // Preserve on update (#3830)
  14573. axis.names = [];
  14574. axis.names.keys = {};
  14575. }
  14576. // Placeholder for plotlines and plotbands groups
  14577. axis.plotLinesAndBandsGroups = {};
  14578. // Shorthand types
  14579. axis.positiveValuesOnly = !!axis.logarithmic;
  14580. // Flag, if axis is linked to another axis
  14581. axis.isLinked = defined(options.linkedTo);
  14582. /**
  14583. * List of major ticks mapped by postition on axis.
  14584. *
  14585. * @see {@link Highcharts.Tick}
  14586. *
  14587. * @name Highcharts.Axis#ticks
  14588. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  14589. */
  14590. axis.ticks = {};
  14591. axis.labelEdge = [];
  14592. /**
  14593. * List of minor ticks mapped by position on the axis.
  14594. *
  14595. * @see {@link Highcharts.Tick}
  14596. *
  14597. * @name Highcharts.Axis#minorTicks
  14598. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  14599. */
  14600. axis.minorTicks = {};
  14601. // List of plotLines/Bands
  14602. axis.plotLinesAndBands = [];
  14603. // Alternate bands
  14604. axis.alternateBands = {};
  14605. // Axis metrics
  14606. axis.len = 0;
  14607. axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
  14608. axis.range = options.range;
  14609. axis.offset = options.offset || 0;
  14610. /**
  14611. * The maximum value of the axis. In a logarithmic axis, this is the
  14612. * logarithm of the real value, and the real value can be obtained from
  14613. * {@link Axis#getExtremes}.
  14614. *
  14615. * @name Highcharts.Axis#max
  14616. * @type {number|null}
  14617. */
  14618. axis.max = null;
  14619. /**
  14620. * The minimum value of the axis. In a logarithmic axis, this is the
  14621. * logarithm of the real value, and the real value can be obtained from
  14622. * {@link Axis#getExtremes}.
  14623. *
  14624. * @name Highcharts.Axis#min
  14625. * @type {number|null}
  14626. */
  14627. axis.min = null;
  14628. /**
  14629. * The processed crosshair options.
  14630. *
  14631. * @name Highcharts.Axis#crosshair
  14632. * @type {boolean|Highcharts.AxisCrosshairOptions}
  14633. */
  14634. axis.crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1], false);
  14635. var events = axis.options.events;
  14636. // Register. Don't add it again on Axis.update().
  14637. if (chart.axes.indexOf(axis) === -1) { //
  14638. if (isXAxis) { // #2713
  14639. chart.axes.splice(chart.xAxis.length, 0, axis);
  14640. }
  14641. else {
  14642. chart.axes.push(axis);
  14643. }
  14644. chart[axis.coll].push(axis);
  14645. }
  14646. /**
  14647. * All series associated to the axis.
  14648. *
  14649. * @name Highcharts.Axis#series
  14650. * @type {Array<Highcharts.Series>}
  14651. */
  14652. axis.series = axis.series || []; // populated by Series
  14653. // Reversed axis
  14654. if (chart.inverted &&
  14655. !axis.isZAxis &&
  14656. isXAxis &&
  14657. typeof axis.reversed === 'undefined') {
  14658. axis.reversed = true;
  14659. }
  14660. axis.labelRotation = axis.options.labels.rotation;
  14661. // register event listeners
  14662. objectEach(events, function (event, eventType) {
  14663. if (isFunction(event)) {
  14664. addEvent(axis, eventType, event);
  14665. }
  14666. });
  14667. fireEvent(this, 'afterInit');
  14668. };
  14669. /**
  14670. * Merge and set options.
  14671. *
  14672. * @private
  14673. * @function Highcharts.Axis#setOptions
  14674. *
  14675. * @param {Highcharts.AxisOptions} userOptions
  14676. * Axis options.
  14677. *
  14678. * @fires Highcharts.Axis#event:afterSetOptions
  14679. */
  14680. Axis.prototype.setOptions = function (userOptions) {
  14681. this.options = merge(Axis.defaultOptions, (this.coll === 'yAxis') && Axis.defaultYAxisOptions, [
  14682. Axis.defaultTopAxisOptions,
  14683. Axis.defaultRightAxisOptions,
  14684. Axis.defaultBottomAxisOptions,
  14685. Axis.defaultLeftAxisOptions
  14686. ][this.side], merge(
  14687. // if set in setOptions (#1053):
  14688. defaultOptions[this.coll], userOptions));
  14689. fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
  14690. };
  14691. /**
  14692. * The default label formatter. The context is a special config object for
  14693. * the label. In apps, use the
  14694. * [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
  14695. * instead, except when a modification is needed.
  14696. *
  14697. * @function Highcharts.Axis#defaultLabelFormatter
  14698. *
  14699. * @param {Highcharts.AxisLabelsFormatterContextObject<number>|Highcharts.AxisLabelsFormatterContextObject<string>} this
  14700. * Formatter context of axis label.
  14701. *
  14702. * @return {string}
  14703. * The formatted label content.
  14704. */
  14705. Axis.prototype.defaultLabelFormatter = function () {
  14706. var axis = this.axis,
  14707. value = isNumber(this.value) ? this.value : NaN,
  14708. time = axis.chart.time,
  14709. categories = axis.categories,
  14710. dateTimeLabelFormat = this.dateTimeLabelFormat,
  14711. lang = defaultOptions.lang,
  14712. numericSymbols = lang.numericSymbols,
  14713. numSymMagnitude = lang.numericSymbolMagnitude || 1000,
  14714. i = numericSymbols && numericSymbols.length,
  14715. multi,
  14716. ret,
  14717. formatOption = axis.options.labels.format,
  14718. // make sure the same symbol is added for all labels on a linear
  14719. // axis
  14720. numericSymbolDetector = axis.logarithmic ?
  14721. Math.abs(value) :
  14722. axis.tickInterval;
  14723. var chart = this.chart;
  14724. var numberFormatter = chart.numberFormatter;
  14725. if (formatOption) {
  14726. ret = format(formatOption, this, chart);
  14727. }
  14728. else if (categories) {
  14729. ret = "" + this.value;
  14730. }
  14731. else if (dateTimeLabelFormat) { // datetime axis
  14732. ret = time.dateFormat(dateTimeLabelFormat, value);
  14733. }
  14734. else if (i && numericSymbolDetector >= 1000) {
  14735. // Decide whether we should add a numeric symbol like k (thousands)
  14736. // or M (millions). If we are to enable this in tooltip or other
  14737. // places as well, we can move this logic to the numberFormatter and
  14738. // enable it by a parameter.
  14739. while (i-- && typeof ret === 'undefined') {
  14740. multi = Math.pow(numSymMagnitude, i + 1);
  14741. if (
  14742. // Only accept a numeric symbol when the distance is more
  14743. // than a full unit. So for example if the symbol is k, we
  14744. // don't accept numbers like 0.5k.
  14745. numericSymbolDetector >= multi &&
  14746. // Accept one decimal before the symbol. Accepts 0.5k but
  14747. // not 0.25k. How does this work with the previous?
  14748. (value * 10) % multi === 0 &&
  14749. numericSymbols[i] !== null &&
  14750. value !== 0) { // #5480
  14751. ret = numberFormatter(value / multi, -1) + numericSymbols[i];
  14752. }
  14753. }
  14754. }
  14755. if (typeof ret === 'undefined') {
  14756. if (Math.abs(value) >= 10000) { // add thousands separators
  14757. ret = numberFormatter(value, -1);
  14758. }
  14759. else { // small numbers
  14760. ret = numberFormatter(value, -1, void 0, ''); // #2466
  14761. }
  14762. }
  14763. return ret;
  14764. };
  14765. /**
  14766. * Get the minimum and maximum for the series of each axis. The function
  14767. * analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
  14768. *
  14769. * @private
  14770. * @function Highcharts.Axis#getSeriesExtremes
  14771. *
  14772. * @fires Highcharts.Axis#event:afterGetSeriesExtremes
  14773. * @fires Highcharts.Axis#event:getSeriesExtremes
  14774. */
  14775. Axis.prototype.getSeriesExtremes = function () {
  14776. var axis = this,
  14777. chart = axis.chart,
  14778. xExtremes;
  14779. fireEvent(this, 'getSeriesExtremes', null, function () {
  14780. axis.hasVisibleSeries = false;
  14781. // Reset properties in case we're redrawing (#3353)
  14782. axis.dataMin = axis.dataMax = axis.threshold = null;
  14783. axis.softThreshold = !axis.isXAxis;
  14784. if (axis.stacking) {
  14785. axis.stacking.buildStacks();
  14786. }
  14787. // loop through this axis' series
  14788. axis.series.forEach(function (series) {
  14789. if (series.visible ||
  14790. !chart.options.chart.ignoreHiddenSeries) {
  14791. var seriesOptions = series.options,
  14792. xData,
  14793. threshold = seriesOptions.threshold,
  14794. seriesDataMin,
  14795. seriesDataMax;
  14796. axis.hasVisibleSeries = true;
  14797. // Validate threshold in logarithmic axes
  14798. if (axis.positiveValuesOnly && threshold <= 0) {
  14799. threshold = null;
  14800. }
  14801. // Get dataMin and dataMax for X axes
  14802. if (axis.isXAxis) {
  14803. xData = series.xData;
  14804. if (xData.length) {
  14805. var isPositive = function (number) { return number > 0; };
  14806. xData = axis.logarithmic ?
  14807. xData.filter(axis.validatePositiveValue) :
  14808. xData;
  14809. xExtremes = series.getXExtremes(xData);
  14810. // If xData contains values which is not numbers,
  14811. // then filter them out. To prevent performance hit,
  14812. // we only do this after we have already found
  14813. // seriesDataMin because in most cases all data is
  14814. // valid. #5234.
  14815. seriesDataMin = xExtremes.min;
  14816. seriesDataMax = xExtremes.max;
  14817. if (!isNumber(seriesDataMin) &&
  14818. // #5010:
  14819. !(seriesDataMin instanceof Date)) {
  14820. xData = xData.filter(isNumber);
  14821. xExtremes = series.getXExtremes(xData);
  14822. // Do it again with valid data
  14823. seriesDataMin = xExtremes.min;
  14824. seriesDataMax = xExtremes.max;
  14825. }
  14826. if (xData.length) {
  14827. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  14828. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  14829. }
  14830. }
  14831. // Get dataMin and dataMax for Y axes, as well as handle
  14832. // stacking and processed data
  14833. }
  14834. else {
  14835. // Get this particular series extremes
  14836. var dataExtremes = series.applyExtremes();
  14837. // Get the dataMin and dataMax so far. If percentage is
  14838. // used, the min and max are always 0 and 100. If
  14839. // seriesDataMin and seriesDataMax is null, then series
  14840. // doesn't have active y data, we continue with nulls
  14841. if (isNumber(dataExtremes.dataMin)) {
  14842. seriesDataMin = dataExtremes.dataMin;
  14843. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  14844. }
  14845. if (isNumber(dataExtremes.dataMax)) {
  14846. seriesDataMax = dataExtremes.dataMax;
  14847. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  14848. }
  14849. // Adjust to threshold
  14850. if (defined(threshold)) {
  14851. axis.threshold = threshold;
  14852. }
  14853. // If any series has a hard threshold, it takes
  14854. // precedence
  14855. if (!seriesOptions.softThreshold ||
  14856. axis.positiveValuesOnly) {
  14857. axis.softThreshold = false;
  14858. }
  14859. }
  14860. }
  14861. });
  14862. });
  14863. fireEvent(this, 'afterGetSeriesExtremes');
  14864. };
  14865. /**
  14866. * Translate from axis value to pixel position on the chart, or back. Use
  14867. * the `toPixels` and `toValue` functions in applications.
  14868. *
  14869. * @private
  14870. * @function Highcharts.Axis#translate
  14871. *
  14872. * @param {number} val
  14873. * TO-DO: parameter description
  14874. *
  14875. * @param {boolean|null} [backwards]
  14876. * TO-DO: parameter description
  14877. *
  14878. * @param {boolean|null} [cvsCoord]
  14879. * TO-DO: parameter description
  14880. *
  14881. * @param {boolean|null} [old]
  14882. * TO-DO: parameter description
  14883. *
  14884. * @param {boolean} [handleLog]
  14885. * TO-DO: parameter description
  14886. *
  14887. * @param {number} [pointPlacement]
  14888. * TO-DO: parameter description
  14889. *
  14890. * @return {number|undefined}
  14891. */
  14892. Axis.prototype.translate = function (val, backwards, cvsCoord, old, handleLog, pointPlacement) {
  14893. var axis = this.linkedParent || this, // #1417
  14894. sign = 1,
  14895. cvsOffset = 0,
  14896. localA = old ? axis.oldTransA : axis.transA,
  14897. localMin = old ? axis.oldMin : axis.min,
  14898. returnValue = 0,
  14899. minPixelPadding = axis.minPixelPadding,
  14900. doPostTranslate = (axis.isOrdinal ||
  14901. axis.brokenAxis && axis.brokenAxis.hasBreaks ||
  14902. (axis.logarithmic && handleLog)) && axis.lin2val;
  14903. if (!localA) {
  14904. localA = axis.transA;
  14905. }
  14906. // In vertical axes, the canvas coordinates start from 0 at the top like
  14907. // in SVG.
  14908. if (cvsCoord) {
  14909. sign *= -1; // canvas coordinates inverts the value
  14910. cvsOffset = axis.len;
  14911. }
  14912. // Handle reversed axis
  14913. if (axis.reversed) {
  14914. sign *= -1;
  14915. cvsOffset -= sign * (axis.sector || axis.len);
  14916. }
  14917. // From pixels to value
  14918. if (backwards) { // reverse translation
  14919. val = val * sign + cvsOffset;
  14920. val -= minPixelPadding;
  14921. // from chart pixel to value:
  14922. returnValue = val / localA + localMin;
  14923. if (doPostTranslate) { // log and ordinal axes
  14924. returnValue = axis.lin2val(returnValue);
  14925. }
  14926. // From value to pixels
  14927. }
  14928. else {
  14929. if (doPostTranslate) { // log and ordinal axes
  14930. val = axis.val2lin(val);
  14931. }
  14932. returnValue = isNumber(localMin) ?
  14933. (sign * (val - localMin) * localA +
  14934. cvsOffset +
  14935. (sign * minPixelPadding) +
  14936. (isNumber(pointPlacement) ?
  14937. localA * pointPlacement :
  14938. 0)) :
  14939. void 0;
  14940. }
  14941. return returnValue;
  14942. };
  14943. /**
  14944. * Translate a value in terms of axis units into pixels within the chart.
  14945. *
  14946. * @function Highcharts.Axis#toPixels
  14947. *
  14948. * @param {number} value
  14949. * A value in terms of axis units.
  14950. *
  14951. * @param {boolean} paneCoordinates
  14952. * Whether to return the pixel coordinate relative to the chart or just the
  14953. * axis/pane itself.
  14954. *
  14955. * @return {number}
  14956. * Pixel position of the value on the chart or axis.
  14957. */
  14958. Axis.prototype.toPixels = function (value, paneCoordinates) {
  14959. return this.translate(value, false, !this.horiz, null, true) +
  14960. (paneCoordinates ? 0 : this.pos);
  14961. };
  14962. /**
  14963. * Translate a pixel position along the axis to a value in terms of axis
  14964. * units.
  14965. *
  14966. * @function Highcharts.Axis#toValue
  14967. *
  14968. * @param {number} pixel
  14969. * The pixel value coordinate.
  14970. *
  14971. * @param {boolean} [paneCoordinates=false]
  14972. * Whether the input pixel is relative to the chart or just the axis/pane
  14973. * itself.
  14974. *
  14975. * @return {number}
  14976. * The axis value.
  14977. */
  14978. Axis.prototype.toValue = function (pixel, paneCoordinates) {
  14979. return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true);
  14980. };
  14981. /**
  14982. * Create the path for a plot line that goes from the given value on
  14983. * this axis, across the plot to the opposite side. Also used internally for
  14984. * grid lines and crosshairs.
  14985. *
  14986. * @function Highcharts.Axis#getPlotLinePath
  14987. *
  14988. * @param {Highcharts.AxisPlotLinePathOptionsObject} options
  14989. * Options for the path.
  14990. *
  14991. * @return {Highcharts.SVGPathArray|null}
  14992. * The SVG path definition for the plot line.
  14993. */
  14994. Axis.prototype.getPlotLinePath = function (options) {
  14995. var axis = this,
  14996. chart = axis.chart,
  14997. axisLeft = axis.left,
  14998. axisTop = axis.top,
  14999. old = options.old,
  15000. value = options.value,
  15001. translatedValue = options.translatedValue,
  15002. lineWidth = options.lineWidth,
  15003. force = options.force,
  15004. x1,
  15005. y1,
  15006. x2,
  15007. y2,
  15008. cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
  15009. cWidth = (old && chart.oldChartWidth) || chart.chartWidth,
  15010. skip,
  15011. transB = axis.transB,
  15012. evt;
  15013. // eslint-disable-next-line valid-jsdoc
  15014. /**
  15015. * Check if x is between a and b. If not, either move to a/b
  15016. * or skip, depending on the force parameter.
  15017. * @private
  15018. */
  15019. function between(x, a, b) {
  15020. if (force !== 'pass' && x < a || x > b) {
  15021. if (force) {
  15022. x = clamp(x, a, b);
  15023. }
  15024. else {
  15025. skip = true;
  15026. }
  15027. }
  15028. return x;
  15029. }
  15030. evt = {
  15031. value: value,
  15032. lineWidth: lineWidth,
  15033. old: old,
  15034. force: force,
  15035. acrossPanes: options.acrossPanes,
  15036. translatedValue: translatedValue
  15037. };
  15038. fireEvent(this, 'getPlotLinePath', evt, function (e) {
  15039. translatedValue = pick(translatedValue, axis.translate(value, null, null, old));
  15040. // Keep the translated value within sane bounds, and avoid Infinity
  15041. // to fail the isNumber test (#7709).
  15042. translatedValue = clamp(translatedValue, -1e5, 1e5);
  15043. x1 = x2 = Math.round(translatedValue + transB);
  15044. y1 = y2 = Math.round(cHeight - translatedValue - transB);
  15045. if (!isNumber(translatedValue)) { // no min or max
  15046. skip = true;
  15047. force = false; // #7175, don't force it when path is invalid
  15048. }
  15049. else if (axis.horiz) {
  15050. y1 = axisTop;
  15051. y2 = cHeight - axis.bottom;
  15052. x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
  15053. }
  15054. else {
  15055. x1 = axisLeft;
  15056. x2 = cWidth - axis.right;
  15057. y1 = y2 = between(y1, axisTop, axisTop + axis.height);
  15058. }
  15059. e.path = skip && !force ?
  15060. null :
  15061. chart.renderer.crispLine([['M', x1, y1], ['L', x2, y2]], lineWidth || 1);
  15062. });
  15063. return evt.path;
  15064. };
  15065. /**
  15066. * Internal function to et the tick positions of a linear axis to round
  15067. * values like whole tens or every five.
  15068. *
  15069. * @function Highcharts.Axis#getLinearTickPositions
  15070. *
  15071. * @param {number} tickInterval
  15072. * The normalized tick interval.
  15073. *
  15074. * @param {number} min
  15075. * Axis minimum.
  15076. *
  15077. * @param {number} max
  15078. * Axis maximum.
  15079. *
  15080. * @return {Array<number>}
  15081. * An array of axis values where ticks should be placed.
  15082. */
  15083. Axis.prototype.getLinearTickPositions = function (tickInterval, min, max) {
  15084. var pos,
  15085. lastPos,
  15086. roundedMin = correctFloat(Math.floor(min / tickInterval) * tickInterval),
  15087. roundedMax = correctFloat(Math.ceil(max / tickInterval) * tickInterval),
  15088. tickPositions = [],
  15089. precision;
  15090. // When the precision is higher than what we filter out in
  15091. // correctFloat, skip it (#6183).
  15092. if (correctFloat(roundedMin + tickInterval) === roundedMin) {
  15093. precision = 20;
  15094. }
  15095. // For single points, add a tick regardless of the relative position
  15096. // (#2662, #6274)
  15097. if (this.single) {
  15098. return [min];
  15099. }
  15100. // Populate the intermediate values
  15101. pos = roundedMin;
  15102. while (pos <= roundedMax) {
  15103. // Place the tick on the rounded value
  15104. tickPositions.push(pos);
  15105. // Always add the raw tickInterval, not the corrected one.
  15106. pos = correctFloat(pos + tickInterval, precision);
  15107. // If the interval is not big enough in the current min - max range
  15108. // to actually increase the loop variable, we need to break out to
  15109. // prevent endless loop. Issue #619
  15110. if (pos === lastPos) {
  15111. break;
  15112. }
  15113. // Record the last value
  15114. lastPos = pos;
  15115. }
  15116. return tickPositions;
  15117. };
  15118. /**
  15119. * Resolve the new minorTicks/minorTickInterval options into the legacy
  15120. * loosely typed minorTickInterval option.
  15121. *
  15122. * @function Highcharts.Axis#getMinorTickInterval
  15123. *
  15124. * @return {number|"auto"|null}
  15125. */
  15126. Axis.prototype.getMinorTickInterval = function () {
  15127. var options = this.options;
  15128. if (options.minorTicks === true) {
  15129. return pick(options.minorTickInterval, 'auto');
  15130. }
  15131. if (options.minorTicks === false) {
  15132. return null;
  15133. }
  15134. return options.minorTickInterval;
  15135. };
  15136. /**
  15137. * Internal function to return the minor tick positions. For logarithmic
  15138. * axes, the same logic as for major ticks is reused.
  15139. *
  15140. * @function Highcharts.Axis#getMinorTickPositions
  15141. *
  15142. * @return {Array<number>}
  15143. * An array of axis values where ticks should be placed.
  15144. */
  15145. Axis.prototype.getMinorTickPositions = function () {
  15146. var axis = this,
  15147. options = axis.options,
  15148. tickPositions = axis.tickPositions,
  15149. minorTickInterval = axis.minorTickInterval,
  15150. minorTickPositions = [],
  15151. pos,
  15152. pointRangePadding = axis.pointRangePadding || 0,
  15153. min = axis.min - pointRangePadding, // #1498
  15154. max = axis.max + pointRangePadding, // #1498
  15155. range = max - min;
  15156. // If minor ticks get too dense, they are hard to read, and may cause
  15157. // long running script. So we don't draw them.
  15158. if (range && range / minorTickInterval < axis.len / 3) { // #3875
  15159. var logarithmic_1 = axis.logarithmic;
  15160. if (logarithmic_1) {
  15161. // For each interval in the major ticks, compute the minor ticks
  15162. // separately.
  15163. this.paddedTicks.forEach(function (_pos, i, paddedTicks) {
  15164. if (i) {
  15165. minorTickPositions.push.apply(minorTickPositions, logarithmic_1.getLogTickPositions(minorTickInterval, paddedTicks[i - 1], paddedTicks[i], true));
  15166. }
  15167. });
  15168. }
  15169. else if (axis.dateTime &&
  15170. this.getMinorTickInterval() === 'auto') { // #1314
  15171. minorTickPositions = minorTickPositions.concat(axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(minorTickInterval), min, max, options.startOfWeek));
  15172. }
  15173. else {
  15174. for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) {
  15175. // Very, very, tight grid lines (#5771)
  15176. if (pos === minorTickPositions[0]) {
  15177. break;
  15178. }
  15179. minorTickPositions.push(pos);
  15180. }
  15181. }
  15182. }
  15183. if (minorTickPositions.length !== 0) {
  15184. axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
  15185. }
  15186. return minorTickPositions;
  15187. };
  15188. /**
  15189. * Adjust the min and max for the minimum range. Keep in mind that the
  15190. * series data is not yet processed, so we don't have information on data
  15191. * cropping and grouping, or updated `axis.pointRange` or
  15192. * `series.pointRange`. The data can't be processed until we have finally
  15193. * established min and max.
  15194. *
  15195. * @private
  15196. * @function Highcharts.Axis#adjustForMinRange
  15197. */
  15198. Axis.prototype.adjustForMinRange = function () {
  15199. var axis = this,
  15200. options = axis.options,
  15201. min = axis.min,
  15202. max = axis.max,
  15203. log = axis.logarithmic,
  15204. zoomOffset,
  15205. spaceAvailable,
  15206. closestDataRange,
  15207. i,
  15208. distance,
  15209. xData,
  15210. loopLength,
  15211. minArgs,
  15212. maxArgs,
  15213. minRange;
  15214. // Set the automatic minimum range based on the closest point distance
  15215. if (axis.isXAxis &&
  15216. typeof axis.minRange === 'undefined' &&
  15217. !log) {
  15218. if (defined(options.min) || defined(options.max)) {
  15219. axis.minRange = null; // don't do this again
  15220. }
  15221. else {
  15222. // Find the closest distance between raw data points, as opposed
  15223. // to closestPointRange that applies to processed points
  15224. // (cropped and grouped)
  15225. axis.series.forEach(function (series) {
  15226. xData = series.xData;
  15227. loopLength = series.xIncrement ? 1 : xData.length - 1;
  15228. for (i = loopLength; i > 0; i--) {
  15229. distance = xData[i] - xData[i - 1];
  15230. if (typeof closestDataRange === 'undefined' ||
  15231. distance < closestDataRange) {
  15232. closestDataRange = distance;
  15233. }
  15234. }
  15235. });
  15236. axis.minRange = Math.min(closestDataRange * 5, axis.dataMax - axis.dataMin);
  15237. }
  15238. }
  15239. // if minRange is exceeded, adjust
  15240. if (max - min < axis.minRange) {
  15241. spaceAvailable =
  15242. axis.dataMax - axis.dataMin >=
  15243. axis.minRange;
  15244. minRange = axis.minRange;
  15245. zoomOffset = (minRange - max + min) / 2;
  15246. // if min and max options have been set, don't go beyond it
  15247. minArgs = [
  15248. min - zoomOffset,
  15249. pick(options.min, min - zoomOffset)
  15250. ];
  15251. // If space is available, stay within the data range
  15252. if (spaceAvailable) {
  15253. minArgs[2] = axis.logarithmic ?
  15254. axis.logarithmic.log2lin(axis.dataMin) :
  15255. axis.dataMin;
  15256. }
  15257. min = arrayMax(minArgs);
  15258. maxArgs = [
  15259. min + minRange,
  15260. pick(options.max, min + minRange)
  15261. ];
  15262. // If space is availabe, stay within the data range
  15263. if (spaceAvailable) {
  15264. maxArgs[2] = log ?
  15265. log.log2lin(axis.dataMax) :
  15266. axis.dataMax;
  15267. }
  15268. max = arrayMin(maxArgs);
  15269. // now if the max is adjusted, adjust the min back
  15270. if (max - min < minRange) {
  15271. minArgs[0] = max - minRange;
  15272. minArgs[1] = pick(options.min, max - minRange);
  15273. min = arrayMax(minArgs);
  15274. }
  15275. }
  15276. // Record modified extremes
  15277. axis.min = min;
  15278. axis.max = max;
  15279. };
  15280. // eslint-disable-next-line valid-jsdoc
  15281. /**
  15282. * Find the closestPointRange across all series.
  15283. *
  15284. * @private
  15285. * @function Highcharts.Axis#getClosest
  15286. */
  15287. Axis.prototype.getClosest = function () {
  15288. var ret;
  15289. if (this.categories) {
  15290. ret = 1;
  15291. }
  15292. else {
  15293. this.series.forEach(function (series) {
  15294. var seriesClosest = series.closestPointRange,
  15295. visible = series.visible ||
  15296. !series.chart.options.chart.ignoreHiddenSeries;
  15297. if (!series.noSharedTooltip &&
  15298. defined(seriesClosest) &&
  15299. visible) {
  15300. ret = defined(ret) ?
  15301. Math.min(ret, seriesClosest) :
  15302. seriesClosest;
  15303. }
  15304. });
  15305. }
  15306. return ret;
  15307. };
  15308. /**
  15309. * When a point name is given and no x, search for the name in the existing
  15310. * categories, or if categories aren't provided, search names or create a
  15311. * new category (#2522).
  15312. * @private
  15313. * @function Highcharts.Axis#nameToX
  15314. *
  15315. * @param {Highcharts.Point} point
  15316. * The point to inspect.
  15317. *
  15318. * @return {number}
  15319. * The X value that the point is given.
  15320. */
  15321. Axis.prototype.nameToX = function (point) {
  15322. var explicitCategories = isArray(this.categories),
  15323. names = explicitCategories ? this.categories : this.names,
  15324. nameX = point.options.x,
  15325. x;
  15326. point.series.requireSorting = false;
  15327. if (!defined(nameX)) {
  15328. nameX = this.options.uniqueNames === false ?
  15329. point.series.autoIncrement() :
  15330. (explicitCategories ?
  15331. names.indexOf(point.name) :
  15332. pick(names.keys[point.name], -1));
  15333. }
  15334. if (nameX === -1) { // Not found in currenct categories
  15335. if (!explicitCategories) {
  15336. x = names.length;
  15337. }
  15338. }
  15339. else {
  15340. x = nameX;
  15341. }
  15342. // Write the last point's name to the names array
  15343. if (typeof x !== 'undefined') {
  15344. this.names[x] = point.name;
  15345. // Backwards mapping is much faster than array searching (#7725)
  15346. this.names.keys[point.name] = x;
  15347. }
  15348. return x;
  15349. };
  15350. /**
  15351. * When changes have been done to series data, update the axis.names.
  15352. *
  15353. * @private
  15354. * @function Highcharts.Axis#updateNames
  15355. */
  15356. Axis.prototype.updateNames = function () {
  15357. var axis = this,
  15358. names = this.names,
  15359. i = names.length;
  15360. if (i > 0) {
  15361. Object.keys(names.keys).forEach(function (key) {
  15362. delete (names.keys)[key];
  15363. });
  15364. names.length = 0;
  15365. this.minRange = this.userMinRange; // Reset
  15366. (this.series || []).forEach(function (series) {
  15367. // Reset incrementer (#5928)
  15368. series.xIncrement = null;
  15369. // When adding a series, points are not yet generated
  15370. if (!series.points || series.isDirtyData) {
  15371. // When we're updating the series with data that is longer
  15372. // than it was, and cropThreshold is passed, we need to make
  15373. // sure that the axis.max is increased _before_ running the
  15374. // premature processData. Otherwise this early iteration of
  15375. // processData will crop the points to axis.max, and the
  15376. // names array will be too short (#5857).
  15377. axis.max = Math.max(axis.max, series.xData.length - 1);
  15378. series.processData();
  15379. series.generatePoints();
  15380. }
  15381. series.data.forEach(function (point, i) {
  15382. var x;
  15383. if (point &&
  15384. point.options &&
  15385. typeof point.name !== 'undefined' // #9562
  15386. ) {
  15387. x = axis.nameToX(point);
  15388. if (typeof x !== 'undefined' && x !== point.x) {
  15389. point.x = x;
  15390. series.xData[i] = x;
  15391. }
  15392. }
  15393. });
  15394. });
  15395. }
  15396. };
  15397. /**
  15398. * Update translation information.
  15399. *
  15400. * @private
  15401. * @function Highcharts.Axis#setAxisTranslation
  15402. *
  15403. * @param {boolean} [saveOld]
  15404. * TO-DO: parameter description
  15405. *
  15406. * @fires Highcharts.Axis#event:afterSetAxisTranslation
  15407. */
  15408. Axis.prototype.setAxisTranslation = function (saveOld) {
  15409. var axis = this,
  15410. range = axis.max - axis.min,
  15411. pointRange = axis.axisPointRange || 0,
  15412. closestPointRange,
  15413. minPointOffset = 0,
  15414. pointRangePadding = 0,
  15415. linkedParent = axis.linkedParent,
  15416. ordinalCorrection,
  15417. hasCategories = !!axis.categories,
  15418. transA = axis.transA,
  15419. isXAxis = axis.isXAxis;
  15420. // Adjust translation for padding. Y axis with categories need to go
  15421. // through the same (#1784).
  15422. if (isXAxis || hasCategories || pointRange) {
  15423. // Get the closest points
  15424. closestPointRange = axis.getClosest();
  15425. if (linkedParent) {
  15426. minPointOffset = linkedParent.minPointOffset;
  15427. pointRangePadding = linkedParent.pointRangePadding;
  15428. }
  15429. else {
  15430. axis.series.forEach(function (series) {
  15431. var seriesPointRange = hasCategories ?
  15432. 1 :
  15433. (isXAxis ?
  15434. pick(series.options.pointRange,
  15435. closestPointRange, 0) :
  15436. (axis.axisPointRange || 0)), // #2806
  15437. pointPlacement = series.options.pointPlacement;
  15438. pointRange = Math.max(pointRange, seriesPointRange);
  15439. if (!axis.single || hasCategories) {
  15440. // TODO: series should internally set x- and y-
  15441. // pointPlacement to simplify this logic.
  15442. var isPointPlacementAxis = series.is('xrange') ? !isXAxis : isXAxis;
  15443. // minPointOffset is the value padding to the left of
  15444. // the axis in order to make room for points with a
  15445. // pointRange, typically columns. When the
  15446. // pointPlacement option is 'between' or 'on', this
  15447. // padding does not apply.
  15448. minPointOffset = Math.max(minPointOffset, isPointPlacementAxis && isString(pointPlacement) ?
  15449. 0 :
  15450. seriesPointRange / 2);
  15451. // Determine the total padding needed to the length of
  15452. // the axis to make room for the pointRange. If the
  15453. // series' pointPlacement is 'on', no padding is added.
  15454. pointRangePadding = Math.max(pointRangePadding, isPointPlacementAxis && pointPlacement === 'on' ?
  15455. 0 :
  15456. seriesPointRange);
  15457. }
  15458. });
  15459. }
  15460. // Record minPointOffset and pointRangePadding
  15461. ordinalCorrection = axis.ordinal && axis.ordinal.slope && closestPointRange ?
  15462. axis.ordinal.slope / closestPointRange :
  15463. 1; // #988, #1853
  15464. axis.minPointOffset = minPointOffset =
  15465. minPointOffset * ordinalCorrection;
  15466. axis.pointRangePadding =
  15467. pointRangePadding = pointRangePadding * ordinalCorrection;
  15468. // pointRange means the width reserved for each point, like in a
  15469. // column chart
  15470. axis.pointRange = Math.min(pointRange, axis.single && hasCategories ? 1 : range);
  15471. // closestPointRange means the closest distance between points. In
  15472. // columns it is mostly equal to pointRange, but in lines pointRange
  15473. // is 0 while closestPointRange is some other value
  15474. if (isXAxis) {
  15475. axis.closestPointRange = closestPointRange;
  15476. }
  15477. }
  15478. // Secondary values
  15479. if (saveOld) {
  15480. axis.oldTransA = transA;
  15481. }
  15482. axis.translationSlope = axis.transA = transA =
  15483. axis.staticScale ||
  15484. axis.len / ((range + pointRangePadding) || 1);
  15485. // Translation addend
  15486. axis.transB = axis.horiz ? axis.left : axis.bottom;
  15487. axis.minPixelPadding = transA * minPointOffset;
  15488. fireEvent(this, 'afterSetAxisTranslation');
  15489. };
  15490. /**
  15491. * @private
  15492. * @function Highcharts.Axis#minFromRange
  15493. *
  15494. * @return {number}
  15495. */
  15496. Axis.prototype.minFromRange = function () {
  15497. var axis = this;
  15498. return axis.max - axis.range;
  15499. };
  15500. /**
  15501. * Set the tick positions to round values and optionally extend the extremes
  15502. * to the nearest tick.
  15503. *
  15504. * @private
  15505. * @function Highcharts.Axis#setTickInterval
  15506. *
  15507. * @param {boolean} secondPass
  15508. * TO-DO: parameter description
  15509. *
  15510. * @fires Highcharts.Axis#event:foundExtremes
  15511. */
  15512. Axis.prototype.setTickInterval = function (secondPass) {
  15513. var axis = this,
  15514. chart = axis.chart,
  15515. log = axis.logarithmic,
  15516. options = axis.options,
  15517. isXAxis = axis.isXAxis,
  15518. isLinked = axis.isLinked,
  15519. maxPadding = options.maxPadding,
  15520. minPadding = options.minPadding,
  15521. length,
  15522. linkedParentExtremes,
  15523. tickIntervalOption = options.tickInterval,
  15524. minTickInterval,
  15525. tickPixelIntervalOption = options.tickPixelInterval,
  15526. categories = axis.categories,
  15527. threshold = isNumber(axis.threshold) ? axis.threshold : null,
  15528. softThreshold = axis.softThreshold,
  15529. thresholdMin,
  15530. thresholdMax,
  15531. hardMin,
  15532. hardMax;
  15533. if (!axis.dateTime && !categories && !isLinked) {
  15534. this.getTickAmount();
  15535. }
  15536. // Min or max set either by zooming/setExtremes or initial options
  15537. hardMin = pick(axis.userMin, options.min);
  15538. hardMax = pick(axis.userMax, options.max);
  15539. // Linked axis gets the extremes from the parent axis
  15540. if (isLinked) {
  15541. axis.linkedParent = chart[axis.coll][options.linkedTo];
  15542. linkedParentExtremes = axis.linkedParent.getExtremes();
  15543. axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
  15544. axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
  15545. if (options.type !== axis.linkedParent.options.type) {
  15546. // Can't link axes of different type
  15547. error(11, 1, chart);
  15548. }
  15549. // Initial min and max from the extreme data values
  15550. }
  15551. else {
  15552. // Adjust to hard threshold
  15553. if (softThreshold && defined(threshold)) {
  15554. if (axis.dataMin >= threshold) {
  15555. thresholdMin = threshold;
  15556. minPadding = 0;
  15557. }
  15558. else if (axis.dataMax <= threshold) {
  15559. thresholdMax = threshold;
  15560. maxPadding = 0;
  15561. }
  15562. }
  15563. axis.min = pick(hardMin, thresholdMin, axis.dataMin);
  15564. axis.max = pick(hardMax, thresholdMax, axis.dataMax);
  15565. }
  15566. if (log) {
  15567. if (axis.positiveValuesOnly &&
  15568. !secondPass &&
  15569. Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
  15570. // Can't plot negative values on log axis
  15571. error(10, 1, chart);
  15572. }
  15573. // The correctFloat cures #934, float errors on full tens. But it
  15574. // was too aggressive for #4360 because of conversion back to lin,
  15575. // therefore use precision 15.
  15576. axis.min = correctFloat(log.log2lin(axis.min), 16);
  15577. axis.max = correctFloat(log.log2lin(axis.max), 16);
  15578. }
  15579. // handle zoomed range
  15580. if (axis.range && defined(axis.max)) {
  15581. // #618, #6773:
  15582. axis.userMin = axis.min = hardMin =
  15583. Math.max(axis.dataMin, axis.minFromRange());
  15584. axis.userMax = hardMax = axis.max;
  15585. axis.range = null; // don't use it when running setExtremes
  15586. }
  15587. // Hook for Highstock Scroller. Consider combining with beforePadding.
  15588. fireEvent(axis, 'foundExtremes');
  15589. // Hook for adjusting this.min and this.max. Used by bubble series.
  15590. if (axis.beforePadding) {
  15591. axis.beforePadding();
  15592. }
  15593. // adjust min and max for the minimum range
  15594. axis.adjustForMinRange();
  15595. // Pad the values to get clear of the chart's edges. To avoid
  15596. // tickInterval taking the padding into account, we do this after
  15597. // computing tick interval (#1337).
  15598. if (!categories &&
  15599. !axis.axisPointRange &&
  15600. !(axis.stacking && axis.stacking.usePercentage) &&
  15601. !isLinked &&
  15602. defined(axis.min) &&
  15603. defined(axis.max)) {
  15604. length = axis.max - axis.min;
  15605. if (length) {
  15606. if (!defined(hardMin) && minPadding) {
  15607. axis.min -= length * minPadding;
  15608. }
  15609. if (!defined(hardMax) && maxPadding) {
  15610. axis.max += length * maxPadding;
  15611. }
  15612. }
  15613. }
  15614. // Handle options for floor, ceiling, softMin and softMax (#6359)
  15615. if (!isNumber(axis.userMin)) {
  15616. if (isNumber(options.softMin) && options.softMin < axis.min) {
  15617. axis.min = hardMin = options.softMin; // #6894
  15618. }
  15619. if (isNumber(options.floor)) {
  15620. axis.min = Math.max(axis.min, options.floor);
  15621. }
  15622. }
  15623. if (!isNumber(axis.userMax)) {
  15624. if (isNumber(options.softMax) && options.softMax > axis.max) {
  15625. axis.max = hardMax = options.softMax; // #6894
  15626. }
  15627. if (isNumber(options.ceiling)) {
  15628. axis.max = Math.min(axis.max, options.ceiling);
  15629. }
  15630. }
  15631. // When the threshold is soft, adjust the extreme value only if the data
  15632. // extreme and the padded extreme land on either side of the threshold.
  15633. // For example, a series of [0, 1, 2, 3] would make the yAxis add a tick
  15634. // for -1 because of the default minPadding and startOnTick options.
  15635. // This is prevented by the softThreshold option.
  15636. if (softThreshold && defined(axis.dataMin)) {
  15637. threshold = threshold || 0;
  15638. if (!defined(hardMin) &&
  15639. axis.min < threshold &&
  15640. axis.dataMin >= threshold) {
  15641. axis.min = axis.options.minRange ?
  15642. Math.min(threshold, axis.max -
  15643. axis.minRange) :
  15644. threshold;
  15645. }
  15646. else if (!defined(hardMax) &&
  15647. axis.max > threshold &&
  15648. axis.dataMax <= threshold) {
  15649. axis.max = axis.options.minRange ?
  15650. Math.max(threshold, axis.min +
  15651. axis.minRange) :
  15652. threshold;
  15653. }
  15654. }
  15655. // get tickInterval
  15656. if (axis.min === axis.max ||
  15657. typeof axis.min === 'undefined' ||
  15658. typeof axis.max === 'undefined') {
  15659. axis.tickInterval = 1;
  15660. }
  15661. else if (isLinked &&
  15662. !tickIntervalOption &&
  15663. tickPixelIntervalOption ===
  15664. axis.linkedParent.options.tickPixelInterval) {
  15665. axis.tickInterval = tickIntervalOption =
  15666. axis.linkedParent.tickInterval;
  15667. }
  15668. else {
  15669. axis.tickInterval = pick(tickIntervalOption, this.tickAmount ?
  15670. ((axis.max - axis.min) /
  15671. Math.max(this.tickAmount - 1, 1)) :
  15672. void 0,
  15673. // For categoried axis, 1 is default, for linear axis use
  15674. // tickPix
  15675. categories ?
  15676. 1 :
  15677. // don't let it be more than the data range
  15678. (axis.max - axis.min) *
  15679. tickPixelIntervalOption /
  15680. Math.max(axis.len, tickPixelIntervalOption));
  15681. }
  15682. // Now we're finished detecting min and max, crop and group series data.
  15683. // This is in turn needed in order to find tick positions in ordinal
  15684. // axes.
  15685. if (isXAxis && !secondPass) {
  15686. axis.series.forEach(function (series) {
  15687. series.processData(axis.min !== axis.oldMin || axis.max !== axis.oldMax);
  15688. });
  15689. }
  15690. // set the translation factor used in translate function
  15691. axis.setAxisTranslation(true);
  15692. // hook for ordinal axes and radial axes
  15693. fireEvent(this, 'initialAxisTranslation');
  15694. // In column-like charts, don't cramp in more ticks than there are
  15695. // points (#1943, #4184)
  15696. if (axis.pointRange && !tickIntervalOption) {
  15697. axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
  15698. }
  15699. // Before normalizing the tick interval, handle minimum tick interval.
  15700. // This applies only if tickInterval is not defined.
  15701. minTickInterval = pick(options.minTickInterval,
  15702. // In datetime axes, don't go below the data interval, except when
  15703. // there are scatter-like series involved (#13369).
  15704. axis.dateTime &&
  15705. !axis.series.some(function (s) { return s.noSharedTooltip; }) ?
  15706. axis.closestPointRange : 0);
  15707. if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
  15708. axis.tickInterval = minTickInterval;
  15709. }
  15710. // for linear axes, get magnitude and normalize the interval
  15711. if (!axis.dateTime && !axis.logarithmic && !tickIntervalOption) {
  15712. axis.tickInterval = normalizeTickInterval(axis.tickInterval, void 0, getMagnitude(axis.tickInterval), pick(options.allowDecimals,
  15713. // If the tick interval is greather than 0.5, avoid
  15714. // decimals, as linear axes are often used to render
  15715. // discrete values. #3363. If a tick amount is set, allow
  15716. // decimals by default, as it increases the chances for a
  15717. // good fit.
  15718. axis.tickInterval < 0.5 || this.tickAmount !== void 0), !!this.tickAmount);
  15719. }
  15720. // Prevent ticks from getting so close that we can't draw the labels
  15721. if (!this.tickAmount) {
  15722. axis.tickInterval = axis.unsquish();
  15723. }
  15724. this.setTickPositions();
  15725. };
  15726. /**
  15727. * Now we have computed the normalized tickInterval, get the tick positions.
  15728. *
  15729. * @private
  15730. * @function Highcharts.Axis#setTickPositions
  15731. *
  15732. * @fires Highcharts.Axis#event:afterSetTickPositions
  15733. */
  15734. Axis.prototype.setTickPositions = function () {
  15735. var axis = this,
  15736. options = this.options,
  15737. tickPositions,
  15738. tickPositionsOption = options.tickPositions,
  15739. minorTickIntervalOption = this.getMinorTickInterval(),
  15740. tickPositioner = options.tickPositioner,
  15741. hasVerticalPanning = this.hasVerticalPanning(),
  15742. isColorAxis = this.coll === 'colorAxis',
  15743. startOnTick = (isColorAxis || !hasVerticalPanning) && options.startOnTick,
  15744. endOnTick = (isColorAxis || !hasVerticalPanning) && options.endOnTick;
  15745. // Set the tickmarkOffset
  15746. this.tickmarkOffset = (this.categories &&
  15747. options.tickmarkPlacement === 'between' &&
  15748. this.tickInterval === 1) ? 0.5 : 0; // #3202
  15749. // get minorTickInterval
  15750. this.minorTickInterval =
  15751. minorTickIntervalOption === 'auto' &&
  15752. this.tickInterval ?
  15753. this.tickInterval / 5 :
  15754. minorTickIntervalOption;
  15755. // When there is only one point, or all points have the same value on
  15756. // this axis, then min and max are equal and tickPositions.length is 0
  15757. // or 1. In this case, add some padding in order to center the point,
  15758. // but leave it with one tick. #1337.
  15759. this.single =
  15760. this.min === this.max &&
  15761. defined(this.min) &&
  15762. !this.tickAmount &&
  15763. (
  15764. // Data is on integer (#6563)
  15765. parseInt(this.min, 10) === this.min ||
  15766. // Between integers and decimals are not allowed (#6274)
  15767. options.allowDecimals !== false);
  15768. /**
  15769. * Contains the current positions that are laid out on the axis. The
  15770. * positions are numbers in terms of axis values. In a category axis
  15771. * they are integers, in a datetime axis they are also integers, but
  15772. * designating milliseconds.
  15773. *
  15774. * This property is read only - for modifying the tick positions, use
  15775. * the `tickPositioner` callback or [axis.tickPositions(
  15776. * https://api.highcharts.com/highcharts/xAxis.tickPositions) option
  15777. * instead.
  15778. *
  15779. * @name Highcharts.Axis#tickPositions
  15780. * @type {Highcharts.AxisTickPositionsArray|undefined}
  15781. */
  15782. this.tickPositions =
  15783. // Find the tick positions. Work on a copy (#1565)
  15784. tickPositions =
  15785. (tickPositionsOption && tickPositionsOption.slice());
  15786. if (!tickPositions) {
  15787. // Too many ticks (#6405). Create a friendly warning and provide two
  15788. // ticks so at least we can show the data series.
  15789. if ((!axis.ordinal || !axis.ordinal.positions) &&
  15790. ((this.max - this.min) /
  15791. this.tickInterval >
  15792. Math.max(2 * this.len, 200))) {
  15793. tickPositions = [this.min, this.max];
  15794. error(19, false, this.chart);
  15795. }
  15796. else if (axis.dateTime) {
  15797. tickPositions = axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(this.tickInterval, options.units), this.min, this.max, options.startOfWeek, axis.ordinal && axis.ordinal.positions, this.closestPointRange, true);
  15798. }
  15799. else if (axis.logarithmic) {
  15800. tickPositions = axis.logarithmic.getLogTickPositions(this.tickInterval, this.min, this.max);
  15801. }
  15802. else {
  15803. tickPositions = this.getLinearTickPositions(this.tickInterval, this.min, this.max);
  15804. }
  15805. // Too dense ticks, keep only the first and last (#4477)
  15806. if (tickPositions.length > this.len) {
  15807. tickPositions = [tickPositions[0], tickPositions.pop()];
  15808. // Reduce doubled value (#7339)
  15809. if (tickPositions[0] === tickPositions[1]) {
  15810. tickPositions.length = 1;
  15811. }
  15812. }
  15813. this.tickPositions = tickPositions;
  15814. // Run the tick positioner callback, that allows modifying auto tick
  15815. // positions.
  15816. if (tickPositioner) {
  15817. tickPositioner = tickPositioner.apply(axis, [this.min, this.max]);
  15818. if (tickPositioner) {
  15819. this.tickPositions = tickPositions = tickPositioner;
  15820. }
  15821. }
  15822. }
  15823. // Reset min/max or remove extremes based on start/end on tick
  15824. this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
  15825. this.trimTicks(tickPositions, startOnTick, endOnTick);
  15826. if (!this.isLinked) {
  15827. // Substract half a unit (#2619, #2846, #2515, #3390),
  15828. // but not in case of multiple ticks (#6897)
  15829. if (this.single &&
  15830. tickPositions.length < 2 &&
  15831. !this.categories &&
  15832. !this.series.some(function (s) {
  15833. return (s.is('heatmap') && s.options.pointPlacement === 'between');
  15834. })) {
  15835. this.min -= 0.5;
  15836. this.max += 0.5;
  15837. }
  15838. if (!tickPositionsOption && !tickPositioner) {
  15839. this.adjustTickAmount();
  15840. }
  15841. }
  15842. fireEvent(this, 'afterSetTickPositions');
  15843. };
  15844. /**
  15845. * Handle startOnTick and endOnTick by either adapting to padding min/max or
  15846. * rounded min/max. Also handle single data points.
  15847. *
  15848. * @private
  15849. * @function Highcharts.Axis#trimTicks
  15850. *
  15851. * @param {Array<number>} tickPositions
  15852. * TO-DO: parameter description
  15853. *
  15854. * @param {boolean} [startOnTick]
  15855. * TO-DO: parameter description
  15856. *
  15857. * @param {boolean} [endOnTick]
  15858. * TO-DO: parameter description
  15859. */
  15860. Axis.prototype.trimTicks = function (tickPositions, startOnTick, endOnTick) {
  15861. var roundedMin = tickPositions[0],
  15862. roundedMax = tickPositions[tickPositions.length - 1],
  15863. minPointOffset = (!this.isOrdinal && this.minPointOffset) || 0; // (#12716)
  15864. fireEvent(this, 'trimTicks');
  15865. if (!this.isLinked) {
  15866. if (startOnTick && roundedMin !== -Infinity) { // #6502
  15867. this.min = roundedMin;
  15868. }
  15869. else {
  15870. while (this.min - minPointOffset > tickPositions[0]) {
  15871. tickPositions.shift();
  15872. }
  15873. }
  15874. if (endOnTick) {
  15875. this.max = roundedMax;
  15876. }
  15877. else {
  15878. while (this.max + minPointOffset <
  15879. tickPositions[tickPositions.length - 1]) {
  15880. tickPositions.pop();
  15881. }
  15882. }
  15883. // If no tick are left, set one tick in the middle (#3195)
  15884. if (tickPositions.length === 0 &&
  15885. defined(roundedMin) &&
  15886. !this.options.tickPositions) {
  15887. tickPositions.push((roundedMax + roundedMin) / 2);
  15888. }
  15889. }
  15890. };
  15891. /**
  15892. * Check if there are multiple axes in the same pane.
  15893. *
  15894. * @private
  15895. * @function Highcharts.Axis#alignToOthers
  15896. *
  15897. * @return {boolean|undefined}
  15898. * True if there are other axes.
  15899. */
  15900. Axis.prototype.alignToOthers = function () {
  15901. var axis = this,
  15902. others = // Whether there is another axis to pair with this one
  15903. {},
  15904. hasOther,
  15905. options = axis.options;
  15906. if (
  15907. // Only if alignTicks is true
  15908. this.chart.options.chart.alignTicks !== false &&
  15909. options.alignTicks !== false &&
  15910. // Disabled when startOnTick or endOnTick are false (#7604)
  15911. options.startOnTick !== false &&
  15912. options.endOnTick !== false &&
  15913. // Don't try to align ticks on a log axis, they are not evenly
  15914. // spaced (#6021)
  15915. !axis.logarithmic) {
  15916. this.chart[this.coll].forEach(function (axis) {
  15917. var otherOptions = axis.options, horiz = axis.horiz, key = [
  15918. horiz ? otherOptions.left : otherOptions.top,
  15919. otherOptions.width,
  15920. otherOptions.height,
  15921. otherOptions.pane
  15922. ].join(',');
  15923. if (axis.series.length) { // #4442
  15924. if (others[key]) {
  15925. hasOther = true; // #4201
  15926. }
  15927. else {
  15928. others[key] = 1;
  15929. }
  15930. }
  15931. });
  15932. }
  15933. return hasOther;
  15934. };
  15935. /**
  15936. * Find the max ticks of either the x and y axis collection, and record it
  15937. * in `this.tickAmount`.
  15938. *
  15939. * @private
  15940. * @function Highcharts.Axis#getTickAmount
  15941. */
  15942. Axis.prototype.getTickAmount = function () {
  15943. var axis = this,
  15944. options = this.options,
  15945. tickAmount = options.tickAmount,
  15946. tickPixelInterval = options.tickPixelInterval;
  15947. if (!defined(options.tickInterval) &&
  15948. !tickAmount && this.len < tickPixelInterval &&
  15949. !this.isRadial &&
  15950. !axis.logarithmic &&
  15951. options.startOnTick &&
  15952. options.endOnTick) {
  15953. tickAmount = 2;
  15954. }
  15955. if (!tickAmount && this.alignToOthers()) {
  15956. // Add 1 because 4 tick intervals require 5 ticks (including first
  15957. // and last)
  15958. tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
  15959. }
  15960. // For tick amounts of 2 and 3, compute five ticks and remove the
  15961. // intermediate ones. This prevents the axis from adding ticks that are
  15962. // too far away from the data extremes.
  15963. if (tickAmount < 4) {
  15964. this.finalTickAmt = tickAmount;
  15965. tickAmount = 5;
  15966. }
  15967. this.tickAmount = tickAmount;
  15968. };
  15969. /**
  15970. * When using multiple axes, adjust the number of ticks to match the highest
  15971. * number of ticks in that group.
  15972. *
  15973. * @private
  15974. * @function Highcharts.Axis#adjustTickAmount
  15975. */
  15976. Axis.prototype.adjustTickAmount = function () {
  15977. var axis = this,
  15978. axisOptions = axis.options,
  15979. tickInterval = axis.tickInterval,
  15980. tickPositions = axis.tickPositions,
  15981. tickAmount = axis.tickAmount,
  15982. finalTickAmt = axis.finalTickAmt,
  15983. currentTickAmount = tickPositions && tickPositions.length,
  15984. threshold = pick(axis.threshold,
  15985. axis.softThreshold ? 0 : null),
  15986. min,
  15987. len,
  15988. i;
  15989. if (axis.hasData()) {
  15990. if (currentTickAmount < tickAmount) {
  15991. min = axis.min;
  15992. while (tickPositions.length < tickAmount) {
  15993. // Extend evenly for both sides unless we're on the
  15994. // threshold (#3965)
  15995. if (tickPositions.length % 2 ||
  15996. min === threshold) {
  15997. // to the end
  15998. tickPositions.push(correctFloat(tickPositions[tickPositions.length - 1] +
  15999. tickInterval));
  16000. }
  16001. else {
  16002. // to the start
  16003. tickPositions.unshift(correctFloat(tickPositions[0] - tickInterval));
  16004. }
  16005. }
  16006. axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
  16007. // Do not crop when ticks are not extremes (#9841)
  16008. axis.min = axisOptions.startOnTick ?
  16009. tickPositions[0] :
  16010. Math.min(axis.min, tickPositions[0]);
  16011. axis.max = axisOptions.endOnTick ?
  16012. tickPositions[tickPositions.length - 1] :
  16013. Math.max(axis.max, tickPositions[tickPositions.length - 1]);
  16014. // We have too many ticks, run second pass to try to reduce ticks
  16015. }
  16016. else if (currentTickAmount > tickAmount) {
  16017. axis.tickInterval *= 2;
  16018. axis.setTickPositions();
  16019. }
  16020. // The finalTickAmt property is set in getTickAmount
  16021. if (defined(finalTickAmt)) {
  16022. i = len = tickPositions.length;
  16023. while (i--) {
  16024. if (
  16025. // Remove every other tick
  16026. (finalTickAmt === 3 && i % 2 === 1) ||
  16027. // Remove all but first and last
  16028. (finalTickAmt <= 2 && i > 0 && i < len - 1)) {
  16029. tickPositions.splice(i, 1);
  16030. }
  16031. }
  16032. axis.finalTickAmt = void 0;
  16033. }
  16034. }
  16035. };
  16036. /**
  16037. * Set the scale based on data min and max, user set min and max or options.
  16038. *
  16039. * @private
  16040. * @function Highcharts.Axis#setScale
  16041. *
  16042. * @fires Highcharts.Axis#event:afterSetScale
  16043. */
  16044. Axis.prototype.setScale = function () {
  16045. var axis = this,
  16046. isDirtyAxisLength,
  16047. isDirtyData = false,
  16048. isXAxisDirty = false;
  16049. axis.series.forEach(function (series) {
  16050. var _a;
  16051. isDirtyData = isDirtyData || series.isDirtyData || series.isDirty;
  16052. // When x axis is dirty, we need new data extremes for y as
  16053. // well:
  16054. isXAxisDirty = isXAxisDirty || ((_a = series.xAxis) === null || _a === void 0 ? void 0 : _a.isDirty) || false;
  16055. });
  16056. axis.oldMin = axis.min;
  16057. axis.oldMax = axis.max;
  16058. axis.oldAxisLength = axis.len;
  16059. // set the new axisLength
  16060. axis.setAxisSize();
  16061. isDirtyAxisLength = axis.len !== axis.oldAxisLength;
  16062. // do we really need to go through all this?
  16063. if (isDirtyAxisLength ||
  16064. isDirtyData ||
  16065. isXAxisDirty ||
  16066. axis.isLinked ||
  16067. axis.forceRedraw ||
  16068. axis.userMin !== axis.oldUserMin ||
  16069. axis.userMax !== axis.oldUserMax ||
  16070. axis.alignToOthers()) {
  16071. if (axis.stacking) {
  16072. axis.stacking.resetStacks();
  16073. }
  16074. axis.forceRedraw = false;
  16075. // get data extremes if needed
  16076. axis.getSeriesExtremes();
  16077. // get fixed positions based on tickInterval
  16078. axis.setTickInterval();
  16079. // record old values to decide whether a rescale is necessary later
  16080. // on (#540)
  16081. axis.oldUserMin = axis.userMin;
  16082. axis.oldUserMax = axis.userMax;
  16083. // Mark as dirty if it is not already set to dirty and extremes have
  16084. // changed. #595.
  16085. if (!axis.isDirty) {
  16086. axis.isDirty =
  16087. isDirtyAxisLength ||
  16088. axis.min !== axis.oldMin ||
  16089. axis.max !== axis.oldMax;
  16090. }
  16091. }
  16092. else if (axis.stacking) {
  16093. axis.stacking.cleanStacks();
  16094. }
  16095. // Recalculate panning state object, when the data
  16096. // has changed. It is required when vertical panning is enabled.
  16097. if (isDirtyData && axis.panningState) {
  16098. axis.panningState.isDirty = true;
  16099. }
  16100. fireEvent(this, 'afterSetScale');
  16101. };
  16102. /**
  16103. * Set the minimum and maximum of the axes after render time. If the
  16104. * `startOnTick` and `endOnTick` options are true, the minimum and maximum
  16105. * values are rounded off to the nearest tick. To prevent this, these
  16106. * options can be set to false before calling setExtremes. Also, setExtremes
  16107. * will not allow a range lower than the `minRange` option, which by default
  16108. * is the range of five points.
  16109. *
  16110. * @sample highcharts/members/axis-setextremes/
  16111. * Set extremes from a button
  16112. * @sample highcharts/members/axis-setextremes-datetime/
  16113. * Set extremes on a datetime axis
  16114. * @sample highcharts/members/axis-setextremes-off-ticks/
  16115. * Set extremes off ticks
  16116. * @sample stock/members/axis-setextremes/
  16117. * Set extremes in Highstock
  16118. * @sample maps/members/axis-setextremes/
  16119. * Set extremes in Highmaps
  16120. *
  16121. * @function Highcharts.Axis#setExtremes
  16122. *
  16123. * @param {number} [newMin]
  16124. * The new minimum value.
  16125. *
  16126. * @param {number} [newMax]
  16127. * The new maximum value.
  16128. *
  16129. * @param {boolean} [redraw=true]
  16130. * Whether to redraw the chart or wait for an explicit call to
  16131. * {@link Highcharts.Chart#redraw}
  16132. *
  16133. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  16134. * Enable or modify animations.
  16135. *
  16136. * @param {*} [eventArguments]
  16137. * Arguments to be accessed in event handler.
  16138. *
  16139. * @fires Highcharts.Axis#event:setExtremes
  16140. */
  16141. Axis.prototype.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  16142. var axis = this,
  16143. chart = axis.chart;
  16144. redraw = pick(redraw, true); // defaults to true
  16145. axis.series.forEach(function (serie) {
  16146. delete serie.kdTree;
  16147. });
  16148. // Extend the arguments with min and max
  16149. eventArguments = extend(eventArguments, {
  16150. min: newMin,
  16151. max: newMax
  16152. });
  16153. // Fire the event
  16154. fireEvent(axis, 'setExtremes', eventArguments, function () {
  16155. axis.userMin = newMin;
  16156. axis.userMax = newMax;
  16157. axis.eventArgs = eventArguments;
  16158. if (redraw) {
  16159. chart.redraw(animation);
  16160. }
  16161. });
  16162. };
  16163. /**
  16164. * Overridable method for zooming chart. Pulled out in a separate method to
  16165. * allow overriding in stock charts.
  16166. * @private
  16167. * @function Highcharts.Axis#zoom
  16168. *
  16169. * @param {number} newMin
  16170. * TO-DO: parameter description
  16171. *
  16172. * @param {number} newMax
  16173. * TO-DO: parameter description
  16174. *
  16175. * @return {boolean}
  16176. */
  16177. Axis.prototype.zoom = function (newMin, newMax) {
  16178. var axis = this,
  16179. dataMin = this.dataMin,
  16180. dataMax = this.dataMax,
  16181. options = this.options,
  16182. min = Math.min(dataMin,
  16183. pick(options.min,
  16184. dataMin)),
  16185. max = Math.max(dataMax,
  16186. pick(options.max,
  16187. dataMax)),
  16188. evt = {
  16189. newMin: newMin,
  16190. newMax: newMax
  16191. };
  16192. fireEvent(this, 'zoom', evt, function (e) {
  16193. // Use e.newMin and e.newMax - event handlers may have altered them
  16194. var newMin = e.newMin,
  16195. newMax = e.newMax;
  16196. if (newMin !== axis.min || newMax !== axis.max) { // #5790
  16197. // Prevent pinch zooming out of range. Check for defined is for
  16198. // #1946. #1734.
  16199. if (!axis.allowZoomOutside) {
  16200. // #6014, sometimes newMax will be smaller than min (or
  16201. // newMin will be larger than max).
  16202. if (defined(dataMin)) {
  16203. if (newMin < min) {
  16204. newMin = min;
  16205. }
  16206. if (newMin > max) {
  16207. newMin = max;
  16208. }
  16209. }
  16210. if (defined(dataMax)) {
  16211. if (newMax < min) {
  16212. newMax = min;
  16213. }
  16214. if (newMax > max) {
  16215. newMax = max;
  16216. }
  16217. }
  16218. }
  16219. // In full view, displaying the reset zoom button is not
  16220. // required
  16221. axis.displayBtn = (typeof newMin !== 'undefined' ||
  16222. typeof newMax !== 'undefined');
  16223. // Do it
  16224. axis.setExtremes(newMin, newMax, false, void 0, { trigger: 'zoom' });
  16225. }
  16226. e.zoomed = true;
  16227. });
  16228. return evt.zoomed;
  16229. };
  16230. /**
  16231. * Update the axis metrics.
  16232. *
  16233. * @private
  16234. * @function Highcharts.Axis#setAxisSize
  16235. */
  16236. Axis.prototype.setAxisSize = function () {
  16237. var chart = this.chart,
  16238. options = this.options,
  16239. // [top, right, bottom, left]
  16240. offsets = options.offsets || [0, 0, 0, 0],
  16241. horiz = this.horiz,
  16242. // Check for percentage based input values. Rounding fixes problems
  16243. // with column overflow and plot line filtering (#4898, #4899)
  16244. width = this.width = Math.round(relativeLength(pick(options.width,
  16245. chart.plotWidth - offsets[3] + offsets[1]),
  16246. chart.plotWidth)),
  16247. height = this.height = Math.round(relativeLength(pick(options.height,
  16248. chart.plotHeight - offsets[0] + offsets[2]),
  16249. chart.plotHeight)),
  16250. top = this.top = Math.round(relativeLength(pick(options.top,
  16251. chart.plotTop + offsets[0]),
  16252. chart.plotHeight,
  16253. chart.plotTop)),
  16254. left = this.left = Math.round(relativeLength(pick(options.left,
  16255. chart.plotLeft + offsets[3]),
  16256. chart.plotWidth,
  16257. chart.plotLeft));
  16258. // Expose basic values to use in Series object and navigator
  16259. this.bottom = chart.chartHeight - height - top;
  16260. this.right = chart.chartWidth - width - left;
  16261. // Direction agnostic properties
  16262. this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
  16263. this.pos = horiz ? left : top; // distance from SVG origin
  16264. };
  16265. /**
  16266. * Get the current extremes for the axis.
  16267. *
  16268. * @sample highcharts/members/axis-getextremes/
  16269. * Report extremes by click on a button
  16270. * @sample maps/members/axis-getextremes/
  16271. * Get extremes in Highmaps
  16272. *
  16273. * @function Highcharts.Axis#getExtremes
  16274. *
  16275. * @return {Highcharts.ExtremesObject}
  16276. * An object containing extremes information.
  16277. */
  16278. Axis.prototype.getExtremes = function () {
  16279. var axis = this;
  16280. var log = axis.logarithmic;
  16281. return {
  16282. min: log ?
  16283. correctFloat(log.lin2log(axis.min)) :
  16284. axis.min,
  16285. max: log ?
  16286. correctFloat(log.lin2log(axis.max)) :
  16287. axis.max,
  16288. dataMin: axis.dataMin,
  16289. dataMax: axis.dataMax,
  16290. userMin: axis.userMin,
  16291. userMax: axis.userMax
  16292. };
  16293. };
  16294. /**
  16295. * Get the zero plane either based on zero or on the min or max value.
  16296. * Used in bar and area plots.
  16297. *
  16298. * @function Highcharts.Axis#getThreshold
  16299. *
  16300. * @param {number} threshold
  16301. * The threshold in axis values.
  16302. *
  16303. * @return {number|undefined}
  16304. * The translated threshold position in terms of pixels, and corrected to
  16305. * stay within the axis bounds.
  16306. */
  16307. Axis.prototype.getThreshold = function (threshold) {
  16308. var axis = this,
  16309. log = axis.logarithmic,
  16310. realMin = log ? log.lin2log(axis.min) : axis.min,
  16311. realMax = log ? log.lin2log(axis.max) : axis.max;
  16312. if (threshold === null || threshold === -Infinity) {
  16313. threshold = realMin;
  16314. }
  16315. else if (threshold === Infinity) {
  16316. threshold = realMax;
  16317. }
  16318. else if (realMin > threshold) {
  16319. threshold = realMin;
  16320. }
  16321. else if (realMax < threshold) {
  16322. threshold = realMax;
  16323. }
  16324. return axis.translate(threshold, 0, 1, 0, 1);
  16325. };
  16326. /**
  16327. * Compute auto alignment for the axis label based on which side the axis is
  16328. * on and the given rotation for the label.
  16329. *
  16330. * @private
  16331. * @function Highcharts.Axis#autoLabelAlign
  16332. *
  16333. * @param {number} rotation
  16334. * The rotation in degrees as set by either the `rotation` or `autoRotation`
  16335. * options.
  16336. *
  16337. * @return {Highcharts.AlignValue}
  16338. * Can be `"center"`, `"left"` or `"right"`.
  16339. */
  16340. Axis.prototype.autoLabelAlign = function (rotation) {
  16341. var angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360,
  16342. evt = { align: 'center' };
  16343. fireEvent(this, 'autoLabelAlign', evt, function (e) {
  16344. if (angle > 15 && angle < 165) {
  16345. e.align = 'right';
  16346. }
  16347. else if (angle > 195 && angle < 345) {
  16348. e.align = 'left';
  16349. }
  16350. });
  16351. return evt.align;
  16352. };
  16353. /**
  16354. * Get the tick length and width for the axis based on axis options.
  16355. * @private
  16356. * @function Highcharts.Axis#tickSize
  16357. *
  16358. * @param {string} [prefix]
  16359. * 'tick' or 'minorTick'
  16360. *
  16361. * @return {Array<number,number>|undefined}
  16362. * An array of tickLength and tickWidth
  16363. */
  16364. Axis.prototype.tickSize = function (prefix) {
  16365. var options = this.options, tickLength = options[prefix === 'tick' ? 'tickLength' : 'minorTickLength'], tickWidth = pick(options[prefix === 'tick' ? 'tickWidth' : 'minorTickWidth'],
  16366. // Default to 1 on linear and datetime X axes
  16367. prefix === 'tick' && this.isXAxis && !this.categories ? 1 : 0), e, tickSize;
  16368. if (tickWidth && tickLength) {
  16369. // Negate the length
  16370. if (options[prefix + 'Position'] === 'inside') {
  16371. tickLength = -tickLength;
  16372. }
  16373. tickSize = [tickLength, tickWidth];
  16374. }
  16375. e = { tickSize: tickSize };
  16376. fireEvent(this, 'afterTickSize', e);
  16377. return e.tickSize;
  16378. };
  16379. /**
  16380. * Return the size of the labels.
  16381. *
  16382. * @private
  16383. * @function Highcharts.Axis#labelMetrics
  16384. *
  16385. * @return {Highcharts.FontMetricsObject}
  16386. */
  16387. Axis.prototype.labelMetrics = function () {
  16388. var index = this.tickPositions && this.tickPositions[0] || 0;
  16389. return this.chart.renderer.fontMetrics(this.options.labels.style &&
  16390. this.options.labels.style.fontSize, this.ticks[index] && this.ticks[index].label);
  16391. };
  16392. /**
  16393. * Prevent the ticks from getting so close we can't draw the labels. On a
  16394. * horizontal axis, this is handled by rotating the labels, removing ticks
  16395. * and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
  16396. *
  16397. * @private
  16398. * @function Highcharts.Axis#unsquish
  16399. *
  16400. * @return {number}
  16401. */
  16402. Axis.prototype.unsquish = function () {
  16403. var labelOptions = this.options.labels,
  16404. horiz = this.horiz,
  16405. tickInterval = this.tickInterval,
  16406. newTickInterval = tickInterval,
  16407. slotSize = this.len / (((this.categories ? 1 : 0) +
  16408. this.max -
  16409. this.min) /
  16410. tickInterval),
  16411. rotation,
  16412. rotationOption = labelOptions.rotation,
  16413. labelMetrics = this.labelMetrics(),
  16414. step,
  16415. bestScore = Number.MAX_VALUE,
  16416. autoRotation,
  16417. range = this.max - this.min,
  16418. // Return the multiple of tickInterval that is needed to avoid
  16419. // collision
  16420. getStep = function (spaceNeeded) {
  16421. var step = spaceNeeded / (slotSize || 1);
  16422. step = step > 1 ? Math.ceil(step) : 1;
  16423. // Guard for very small or negative angles (#9835)
  16424. if (step * tickInterval > range &&
  16425. spaceNeeded !== Infinity &&
  16426. slotSize !== Infinity &&
  16427. range) {
  16428. step = Math.ceil(range / tickInterval);
  16429. }
  16430. return correctFloat(step * tickInterval);
  16431. };
  16432. if (horiz) {
  16433. autoRotation = !labelOptions.staggerLines &&
  16434. !labelOptions.step &&
  16435. ( // #3971
  16436. defined(rotationOption) ?
  16437. [rotationOption] :
  16438. slotSize < pick(labelOptions.autoRotationLimit, 80) && labelOptions.autoRotation);
  16439. if (autoRotation) {
  16440. // Loop over the given autoRotation options, and determine
  16441. // which gives the best score. The best score is that with
  16442. // the lowest number of steps and a rotation closest
  16443. // to horizontal.
  16444. autoRotation.forEach(function (rot) {
  16445. var score;
  16446. if (rot === rotationOption ||
  16447. (rot && rot >= -90 && rot <= 90)) { // #3891
  16448. step = getStep(Math.abs(labelMetrics.h / Math.sin(deg2rad * rot)));
  16449. score = step + Math.abs(rot / 360);
  16450. if (score < bestScore) {
  16451. bestScore = score;
  16452. rotation = rot;
  16453. newTickInterval = step;
  16454. }
  16455. }
  16456. });
  16457. }
  16458. }
  16459. else if (!labelOptions.step) { // #4411
  16460. newTickInterval = getStep(labelMetrics.h);
  16461. }
  16462. this.autoRotation = autoRotation;
  16463. this.labelRotation = pick(rotation, rotationOption);
  16464. return newTickInterval;
  16465. };
  16466. /**
  16467. * Get the general slot width for labels/categories on this axis. This may
  16468. * change between the pre-render (from Axis.getOffset) and the final tick
  16469. * rendering and placement.
  16470. *
  16471. * @private
  16472. * @function Highcharts.Axis#getSlotWidth
  16473. *
  16474. * @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
  16475. * basing on tick label. It is used in highcharts-3d module, where the slots
  16476. * has different widths depending on perspective angles.
  16477. *
  16478. * @return {number}
  16479. * The pixel width allocated to each axis label.
  16480. */
  16481. Axis.prototype.getSlotWidth = function (tick) {
  16482. var _a;
  16483. // #5086, #1580, #1931
  16484. var chart = this.chart,
  16485. horiz = this.horiz,
  16486. labelOptions = this.options.labels,
  16487. slotCount = Math.max(this.tickPositions.length - (this.categories ? 0 : 1), 1),
  16488. marginLeft = chart.margin[3];
  16489. // Used by grid axis
  16490. if (tick && isNumber(tick.slotWidth)) { // #13221, can be 0
  16491. return tick.slotWidth;
  16492. }
  16493. if (horiz &&
  16494. labelOptions &&
  16495. (labelOptions.step || 0) < 2) {
  16496. if (labelOptions.rotation) { // #4415
  16497. return 0;
  16498. }
  16499. return ((this.staggerLines || 1) * this.len) / slotCount;
  16500. }
  16501. if (!horiz) {
  16502. // #7028
  16503. var cssWidth = (_a = labelOptions === null || labelOptions === void 0 ? void 0 : labelOptions.style) === null || _a === void 0 ? void 0 : _a.width;
  16504. if (cssWidth !== void 0) {
  16505. return parseInt(cssWidth, 10);
  16506. }
  16507. if (marginLeft) {
  16508. return marginLeft - chart.spacing[3];
  16509. }
  16510. }
  16511. // Last resort, a fraction of the available size
  16512. return chart.chartWidth * 0.33;
  16513. };
  16514. /**
  16515. * Render the axis labels and determine whether ellipsis or rotation need to
  16516. * be applied.
  16517. *
  16518. * @private
  16519. * @function Highcharts.Axis#renderUnsquish
  16520. */
  16521. Axis.prototype.renderUnsquish = function () {
  16522. var chart = this.chart,
  16523. renderer = chart.renderer,
  16524. tickPositions = this.tickPositions,
  16525. ticks = this.ticks,
  16526. labelOptions = this.options.labels,
  16527. labelStyleOptions = (labelOptions && labelOptions.style || {}),
  16528. horiz = this.horiz,
  16529. slotWidth = this.getSlotWidth(),
  16530. innerWidth = Math.max(1,
  16531. Math.round(slotWidth - 2 * (labelOptions.padding || 5))),
  16532. attr = {},
  16533. labelMetrics = this.labelMetrics(),
  16534. textOverflowOption = (labelOptions.style &&
  16535. labelOptions.style.textOverflow),
  16536. commonWidth,
  16537. commonTextOverflow,
  16538. maxLabelLength = 0,
  16539. label,
  16540. i,
  16541. pos;
  16542. // Set rotation option unless it is "auto", like in gauges
  16543. if (!isString(labelOptions.rotation)) {
  16544. // #4443:
  16545. attr.rotation = labelOptions.rotation || 0;
  16546. }
  16547. // Get the longest label length
  16548. tickPositions.forEach(function (tick) {
  16549. tick = ticks[tick];
  16550. // Replace label - sorting animation
  16551. if (tick.movedLabel) {
  16552. tick.replaceMovedLabel();
  16553. }
  16554. if (tick &&
  16555. tick.label &&
  16556. tick.label.textPxLength > maxLabelLength) {
  16557. maxLabelLength = tick.label.textPxLength;
  16558. }
  16559. });
  16560. this.maxLabelLength = maxLabelLength;
  16561. // Handle auto rotation on horizontal axis
  16562. if (this.autoRotation) {
  16563. // Apply rotation only if the label is too wide for the slot, and
  16564. // the label is wider than its height.
  16565. if (maxLabelLength > innerWidth &&
  16566. maxLabelLength > labelMetrics.h) {
  16567. attr.rotation = this.labelRotation;
  16568. }
  16569. else {
  16570. this.labelRotation = 0;
  16571. }
  16572. // Handle word-wrap or ellipsis on vertical axis
  16573. }
  16574. else if (slotWidth) {
  16575. // For word-wrap or ellipsis
  16576. commonWidth = innerWidth;
  16577. if (!textOverflowOption) {
  16578. commonTextOverflow = 'clip';
  16579. // On vertical axis, only allow word wrap if there is room
  16580. // for more lines.
  16581. i = tickPositions.length;
  16582. while (!horiz && i--) {
  16583. pos = tickPositions[i];
  16584. label = ticks[pos].label;
  16585. if (label) {
  16586. // Reset ellipsis in order to get the correct
  16587. // bounding box (#4070)
  16588. if (label.styles &&
  16589. label.styles.textOverflow === 'ellipsis') {
  16590. label.css({ textOverflow: 'clip' });
  16591. // Set the correct width in order to read
  16592. // the bounding box height (#4678, #5034)
  16593. }
  16594. else if (label.textPxLength > slotWidth) {
  16595. label.css({ width: slotWidth + 'px' });
  16596. }
  16597. if (label.getBBox().height > (this.len / tickPositions.length -
  16598. (labelMetrics.h - labelMetrics.f))) {
  16599. label.specificTextOverflow = 'ellipsis';
  16600. }
  16601. }
  16602. }
  16603. }
  16604. }
  16605. // Add ellipsis if the label length is significantly longer than ideal
  16606. if (attr.rotation) {
  16607. commonWidth = (maxLabelLength > chart.chartHeight * 0.5 ?
  16608. chart.chartHeight * 0.33 :
  16609. maxLabelLength);
  16610. if (!textOverflowOption) {
  16611. commonTextOverflow = 'ellipsis';
  16612. }
  16613. }
  16614. // Set the explicit or automatic label alignment
  16615. this.labelAlign = labelOptions.align ||
  16616. this.autoLabelAlign(this.labelRotation);
  16617. if (this.labelAlign) {
  16618. attr.align = this.labelAlign;
  16619. }
  16620. // Apply general and specific CSS
  16621. tickPositions.forEach(function (pos) {
  16622. var tick = ticks[pos],
  16623. label = tick && tick.label,
  16624. widthOption = labelStyleOptions.width,
  16625. css = {};
  16626. if (label) {
  16627. // This needs to go before the CSS in old IE (#4502)
  16628. label.attr(attr);
  16629. if (tick.shortenLabel) {
  16630. tick.shortenLabel();
  16631. }
  16632. else if (commonWidth &&
  16633. !widthOption &&
  16634. // Setting width in this case messes with the bounding box
  16635. // (#7975)
  16636. labelStyleOptions.whiteSpace !== 'nowrap' &&
  16637. (
  16638. // Speed optimizing, #7656
  16639. commonWidth < label.textPxLength ||
  16640. // Resetting CSS, #4928
  16641. label.element.tagName === 'SPAN')) {
  16642. css.width = commonWidth + 'px';
  16643. if (!textOverflowOption) {
  16644. css.textOverflow = (label.specificTextOverflow ||
  16645. commonTextOverflow);
  16646. }
  16647. label.css(css);
  16648. // Reset previously shortened label (#8210)
  16649. }
  16650. else if (label.styles &&
  16651. label.styles.width &&
  16652. !css.width &&
  16653. !widthOption) {
  16654. label.css({ width: null });
  16655. }
  16656. delete label.specificTextOverflow;
  16657. tick.rotation = attr.rotation;
  16658. }
  16659. }, this);
  16660. // Note: Why is this not part of getLabelPosition?
  16661. this.tickRotCorr = renderer.rotCorr(labelMetrics.b, this.labelRotation || 0, this.side !== 0);
  16662. };
  16663. /**
  16664. * Return true if the axis has associated data.
  16665. *
  16666. * @function Highcharts.Axis#hasData
  16667. *
  16668. * @return {boolean}
  16669. * True if the axis has associated visible series and those series have
  16670. * either valid data points or explicit `min` and `max` settings.
  16671. */
  16672. Axis.prototype.hasData = function () {
  16673. return this.series.some(function (s) {
  16674. return s.hasData();
  16675. }) ||
  16676. (this.options.showEmpty &&
  16677. defined(this.min) &&
  16678. defined(this.max));
  16679. };
  16680. /**
  16681. * Adds the title defined in axis.options.title.
  16682. *
  16683. * @function Highcharts.Axis#addTitle
  16684. *
  16685. * @param {boolean} [display]
  16686. * Whether or not to display the title.
  16687. */
  16688. Axis.prototype.addTitle = function (display) {
  16689. var axis = this,
  16690. renderer = axis.chart.renderer,
  16691. horiz = axis.horiz,
  16692. opposite = axis.opposite,
  16693. options = axis.options,
  16694. axisTitleOptions = options.title,
  16695. textAlign,
  16696. styledMode = axis.chart.styledMode;
  16697. if (!axis.axisTitle) {
  16698. textAlign = axisTitleOptions.textAlign;
  16699. if (!textAlign) {
  16700. textAlign = (horiz ? {
  16701. low: 'left',
  16702. middle: 'center',
  16703. high: 'right'
  16704. } : {
  16705. low: opposite ? 'right' : 'left',
  16706. middle: 'center',
  16707. high: opposite ? 'left' : 'right'
  16708. })[axisTitleOptions.align];
  16709. }
  16710. axis.axisTitle = renderer
  16711. .text(axisTitleOptions.text, 0, 0, axisTitleOptions.useHTML)
  16712. .attr({
  16713. zIndex: 7,
  16714. rotation: axisTitleOptions.rotation || 0,
  16715. align: textAlign
  16716. })
  16717. .addClass('highcharts-axis-title');
  16718. // #7814, don't mutate style option
  16719. if (!styledMode) {
  16720. axis.axisTitle.css(merge(axisTitleOptions.style));
  16721. }
  16722. axis.axisTitle.add(axis.axisGroup);
  16723. axis.axisTitle.isNew = true;
  16724. }
  16725. // Max width defaults to the length of the axis
  16726. if (!styledMode &&
  16727. !axisTitleOptions.style.width &&
  16728. !axis.isRadial) {
  16729. axis.axisTitle.css({
  16730. width: axis.len + 'px'
  16731. });
  16732. }
  16733. // hide or show the title depending on whether showEmpty is set
  16734. axis.axisTitle[display ? 'show' : 'hide'](display);
  16735. };
  16736. /**
  16737. * Generates a tick for initial positioning.
  16738. *
  16739. * @private
  16740. * @function Highcharts.Axis#generateTick
  16741. *
  16742. * @param {number} pos
  16743. * The tick position in axis values.
  16744. *
  16745. * @param {number} [i]
  16746. * The index of the tick in {@link Axis.tickPositions}.
  16747. */
  16748. Axis.prototype.generateTick = function (pos) {
  16749. var axis = this;
  16750. var ticks = axis.ticks;
  16751. if (!ticks[pos]) {
  16752. ticks[pos] = new Tick(axis, pos);
  16753. }
  16754. else {
  16755. ticks[pos].addLabel(); // update labels depending on tick interval
  16756. }
  16757. };
  16758. /**
  16759. * Render the tick labels to a preliminary position to get their sizes
  16760. *
  16761. * @private
  16762. * @function Highcharts.Axis#getOffset
  16763. *
  16764. * @fires Highcharts.Axis#event:afterGetOffset
  16765. */
  16766. Axis.prototype.getOffset = function () {
  16767. var axis = this,
  16768. chart = axis.chart,
  16769. renderer = chart.renderer,
  16770. options = axis.options,
  16771. tickPositions = axis.tickPositions,
  16772. ticks = axis.ticks,
  16773. horiz = axis.horiz,
  16774. side = axis.side,
  16775. invertedSide = chart.inverted &&
  16776. !axis.isZAxis ? [1, 0, 3, 2][side] : side,
  16777. hasData,
  16778. showAxis,
  16779. titleOffset = 0,
  16780. titleOffsetOption,
  16781. titleMargin = 0,
  16782. axisTitleOptions = options.title,
  16783. labelOptions = options.labels,
  16784. labelOffset = 0, // reset
  16785. labelOffsetPadded,
  16786. axisOffset = chart.axisOffset,
  16787. clipOffset = chart.clipOffset,
  16788. clip,
  16789. directionFactor = [-1, 1, 1, -1][side],
  16790. className = options.className,
  16791. axisParent = axis.axisParent, // Used in color axis
  16792. lineHeightCorrection;
  16793. // For reuse in Axis.render
  16794. hasData = axis.hasData();
  16795. axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
  16796. // Set/reset staggerLines
  16797. axis.staggerLines = axis.horiz && labelOptions.staggerLines;
  16798. // Create the axisGroup and gridGroup elements on first iteration
  16799. if (!axis.axisGroup) {
  16800. axis.gridGroup = renderer.g('grid')
  16801. .attr({ zIndex: options.gridZIndex || 1 })
  16802. .addClass('highcharts-' + this.coll.toLowerCase() + '-grid ' +
  16803. (className || ''))
  16804. .add(axisParent);
  16805. axis.axisGroup = renderer.g('axis')
  16806. .attr({ zIndex: options.zIndex || 2 })
  16807. .addClass('highcharts-' + this.coll.toLowerCase() + ' ' +
  16808. (className || ''))
  16809. .add(axisParent);
  16810. axis.labelGroup = renderer.g('axis-labels')
  16811. .attr({ zIndex: labelOptions.zIndex || 7 })
  16812. .addClass('highcharts-' + axis.coll.toLowerCase() + '-labels ' +
  16813. (className || ''))
  16814. .add(axisParent);
  16815. }
  16816. if (hasData || axis.isLinked) {
  16817. // Generate ticks
  16818. tickPositions.forEach(function (pos, i) {
  16819. // i is not used here, but may be used in overrides
  16820. axis.generateTick(pos, i);
  16821. });
  16822. axis.renderUnsquish();
  16823. // Left side must be align: right and right side must
  16824. // have align: left for labels
  16825. axis.reserveSpaceDefault = (side === 0 ||
  16826. side === 2 ||
  16827. { 1: 'left', 3: 'right' }[side] === axis.labelAlign);
  16828. if (pick(labelOptions.reserveSpace, axis.labelAlign === 'center' ? true : null, axis.reserveSpaceDefault)) {
  16829. tickPositions.forEach(function (pos) {
  16830. // get the highest offset
  16831. labelOffset = Math.max(ticks[pos].getLabelSize(), labelOffset);
  16832. });
  16833. }
  16834. if (axis.staggerLines) {
  16835. labelOffset *= axis.staggerLines;
  16836. }
  16837. axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
  16838. }
  16839. else { // doesn't have data
  16840. objectEach(ticks, function (tick, n) {
  16841. tick.destroy();
  16842. delete ticks[n];
  16843. });
  16844. }
  16845. if (axisTitleOptions &&
  16846. axisTitleOptions.text &&
  16847. axisTitleOptions.enabled !== false) {
  16848. axis.addTitle(showAxis);
  16849. if (showAxis && axisTitleOptions.reserveSpace !== false) {
  16850. axis.titleOffset = titleOffset =
  16851. axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
  16852. titleOffsetOption = axisTitleOptions.offset;
  16853. titleMargin = defined(titleOffsetOption) ?
  16854. 0 :
  16855. pick(axisTitleOptions.margin, horiz ? 5 : 10);
  16856. }
  16857. }
  16858. // Render the axis line
  16859. axis.renderLine();
  16860. // handle automatic or user set offset
  16861. axis.offset = directionFactor * pick(options.offset, axisOffset[side] ? axisOffset[side] + (options.margin || 0) : 0);
  16862. axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
  16863. if (side === 0) {
  16864. lineHeightCorrection = -axis.labelMetrics().h;
  16865. }
  16866. else if (side === 2) {
  16867. lineHeightCorrection = axis.tickRotCorr.y;
  16868. }
  16869. else {
  16870. lineHeightCorrection = 0;
  16871. }
  16872. // Find the padded label offset
  16873. labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
  16874. if (labelOffset) {
  16875. labelOffsetPadded -= lineHeightCorrection;
  16876. labelOffsetPadded += directionFactor * (horiz ?
  16877. pick(labelOptions.y, axis.tickRotCorr.y + directionFactor * 8) :
  16878. labelOptions.x);
  16879. }
  16880. axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
  16881. if (axis.getMaxLabelDimensions) {
  16882. axis.maxLabelDimensions = axis.getMaxLabelDimensions(ticks, tickPositions);
  16883. }
  16884. // Due to GridAxis.tickSize, tickSize should be calculated after ticks
  16885. // has rendered.
  16886. var tickSize = this.tickSize('tick');
  16887. axisOffset[side] = Math.max(axisOffset[side], axis.axisTitleMargin + titleOffset +
  16888. directionFactor * axis.offset, labelOffsetPadded, // #3027
  16889. tickPositions && tickPositions.length && tickSize ?
  16890. tickSize[0] + directionFactor * axis.offset :
  16891. 0 // #4866
  16892. );
  16893. // Decide the clipping needed to keep the graph inside
  16894. // the plot area and axis lines
  16895. clip = options.offset ?
  16896. 0 :
  16897. // #4308, #4371:
  16898. Math.floor(axis.axisLine.strokeWidth() / 2) * 2;
  16899. clipOffset[invertedSide] =
  16900. Math.max(clipOffset[invertedSide], clip);
  16901. fireEvent(this, 'afterGetOffset');
  16902. };
  16903. /**
  16904. * Internal function to get the path for the axis line. Extended for polar
  16905. * charts.
  16906. *
  16907. * @function Highcharts.Axis#getLinePath
  16908. *
  16909. * @param {number} lineWidth
  16910. * The line width in pixels.
  16911. *
  16912. * @return {Highcharts.SVGPathArray}
  16913. * The SVG path definition in array form.
  16914. */
  16915. Axis.prototype.getLinePath = function (lineWidth) {
  16916. var chart = this.chart,
  16917. opposite = this.opposite,
  16918. offset = this.offset,
  16919. horiz = this.horiz,
  16920. lineLeft = this.left + (opposite ? this.width : 0) + offset,
  16921. lineTop = chart.chartHeight - this.bottom -
  16922. (opposite ? this.height : 0) + offset;
  16923. if (opposite) {
  16924. lineWidth *= -1; // crispify the other way - #1480, #1687
  16925. }
  16926. return chart.renderer
  16927. .crispLine([
  16928. [
  16929. 'M',
  16930. horiz ?
  16931. this.left :
  16932. lineLeft,
  16933. horiz ?
  16934. lineTop :
  16935. this.top
  16936. ],
  16937. [
  16938. 'L',
  16939. horiz ?
  16940. chart.chartWidth - this.right :
  16941. lineLeft,
  16942. horiz ?
  16943. lineTop :
  16944. chart.chartHeight - this.bottom
  16945. ]
  16946. ], lineWidth);
  16947. };
  16948. /**
  16949. * Render the axis line. Called internally when rendering and redrawing the
  16950. * axis.
  16951. *
  16952. * @function Highcharts.Axis#renderLine
  16953. */
  16954. Axis.prototype.renderLine = function () {
  16955. if (!this.axisLine) {
  16956. this.axisLine = this.chart.renderer.path()
  16957. .addClass('highcharts-axis-line')
  16958. .add(this.axisGroup);
  16959. if (!this.chart.styledMode) {
  16960. this.axisLine.attr({
  16961. stroke: this.options.lineColor,
  16962. 'stroke-width': this.options.lineWidth,
  16963. zIndex: 7
  16964. });
  16965. }
  16966. }
  16967. };
  16968. /**
  16969. * Position the axis title.
  16970. *
  16971. * @private
  16972. * @function Highcharts.Axis#getTitlePosition
  16973. *
  16974. * @return {Highcharts.PositionObject}
  16975. * X and Y positions for the title.
  16976. */
  16977. Axis.prototype.getTitlePosition = function () {
  16978. // compute anchor points for each of the title align options
  16979. var horiz = this.horiz,
  16980. axisLeft = this.left,
  16981. axisTop = this.top,
  16982. axisLength = this.len,
  16983. axisTitleOptions = this.options.title,
  16984. margin = horiz ? axisLeft : axisTop,
  16985. opposite = this.opposite,
  16986. offset = this.offset,
  16987. xOption = axisTitleOptions.x || 0,
  16988. yOption = axisTitleOptions.y || 0,
  16989. axisTitle = this.axisTitle,
  16990. fontMetrics = this.chart.renderer.fontMetrics(axisTitleOptions.style &&
  16991. axisTitleOptions.style.fontSize,
  16992. axisTitle),
  16993. // The part of a multiline text that is below the baseline of the
  16994. // first line. Subtract 1 to preserve pixel-perfectness from the
  16995. // old behaviour (v5.0.12), where only one line was allowed.
  16996. textHeightOvershoot = Math.max(axisTitle.getBBox(null, 0).height - fontMetrics.h - 1, 0),
  16997. // the position in the length direction of the axis
  16998. alongAxis = {
  16999. low: margin + (horiz ? 0 : axisLength),
  17000. middle: margin + axisLength / 2,
  17001. high: margin + (horiz ? axisLength : 0)
  17002. }[axisTitleOptions.align],
  17003. // the position in the perpendicular direction of the axis
  17004. offAxis = (horiz ? axisTop + this.height : axisLeft) +
  17005. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  17006. (opposite ? -1 : 1) * // so does opposite axes
  17007. this.axisTitleMargin +
  17008. [
  17009. -textHeightOvershoot,
  17010. textHeightOvershoot,
  17011. fontMetrics.f,
  17012. -textHeightOvershoot // left
  17013. ][this.side],
  17014. titlePosition = {
  17015. x: horiz ?
  17016. alongAxis + xOption :
  17017. offAxis + (opposite ? this.width : 0) + offset + xOption,
  17018. y: horiz ?
  17019. offAxis + yOption - (opposite ? this.height : 0) + offset :
  17020. alongAxis + yOption
  17021. };
  17022. fireEvent(this, 'afterGetTitlePosition', { titlePosition: titlePosition });
  17023. return titlePosition;
  17024. };
  17025. /**
  17026. * Render a minor tick into the given position. If a minor tick already
  17027. * exists in this position, move it.
  17028. *
  17029. * @function Highcharts.Axis#renderMinorTick
  17030. *
  17031. * @param {number} pos
  17032. * The position in axis values.
  17033. */
  17034. Axis.prototype.renderMinorTick = function (pos) {
  17035. var axis = this;
  17036. var slideInTicks = axis.chart.hasRendered && isNumber(axis.oldMin);
  17037. var minorTicks = axis.minorTicks;
  17038. if (!minorTicks[pos]) {
  17039. minorTicks[pos] = new Tick(axis, pos, 'minor');
  17040. }
  17041. // Render new ticks in old position
  17042. if (slideInTicks && minorTicks[pos].isNew) {
  17043. minorTicks[pos].render(null, true);
  17044. }
  17045. minorTicks[pos].render(null, false, 1);
  17046. };
  17047. /**
  17048. * Render a major tick into the given position. If a tick already exists
  17049. * in this position, move it.
  17050. *
  17051. * @function Highcharts.Axis#renderTick
  17052. *
  17053. * @param {number} pos
  17054. * The position in axis values.
  17055. *
  17056. * @param {number} i
  17057. * The tick index.
  17058. */
  17059. Axis.prototype.renderTick = function (pos, i) {
  17060. var axis = this;
  17061. var isLinked = axis.isLinked;
  17062. var ticks = axis.ticks;
  17063. var slideInTicks = axis.chart.hasRendered && isNumber(axis.oldMin);
  17064. // Linked axes need an extra check to find out if
  17065. if (!isLinked ||
  17066. (pos >= axis.min && pos <= axis.max)) {
  17067. if (!ticks[pos]) {
  17068. ticks[pos] = new Tick(axis, pos);
  17069. }
  17070. // NOTE this seems like overkill. Could be handled in tick.render by
  17071. // setting old position in attr, then set new position in animate.
  17072. // render new ticks in old position
  17073. if (slideInTicks && ticks[pos].isNew) {
  17074. // Start with negative opacity so that it is visible from
  17075. // halfway into the animation
  17076. ticks[pos].render(i, true, -1);
  17077. }
  17078. ticks[pos].render(i);
  17079. }
  17080. };
  17081. /**
  17082. * Render the axis.
  17083. *
  17084. * @private
  17085. * @function Highcharts.Axis#render
  17086. *
  17087. * @fires Highcharts.Axis#event:afterRender
  17088. */
  17089. Axis.prototype.render = function () {
  17090. var axis = this,
  17091. chart = axis.chart,
  17092. log = axis.logarithmic,
  17093. renderer = chart.renderer,
  17094. options = axis.options,
  17095. isLinked = axis.isLinked,
  17096. tickPositions = axis.tickPositions,
  17097. axisTitle = axis.axisTitle,
  17098. ticks = axis.ticks,
  17099. minorTicks = axis.minorTicks,
  17100. alternateBands = axis.alternateBands,
  17101. stackLabelOptions = options.stackLabels,
  17102. alternateGridColor = options.alternateGridColor,
  17103. tickmarkOffset = axis.tickmarkOffset,
  17104. axisLine = axis.axisLine,
  17105. showAxis = axis.showAxis,
  17106. animation = animObject(renderer.globalAnimation),
  17107. from,
  17108. to;
  17109. // Reset
  17110. axis.labelEdge.length = 0;
  17111. axis.overlap = false;
  17112. // Mark all elements inActive before we go over and mark the active ones
  17113. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  17114. objectEach(coll, function (tick) {
  17115. tick.isActive = false;
  17116. });
  17117. });
  17118. // If the series has data draw the ticks. Else only the line and title
  17119. if (axis.hasData() || isLinked) {
  17120. // minor ticks
  17121. if (axis.minorTickInterval && !axis.categories) {
  17122. axis.getMinorTickPositions().forEach(function (pos) {
  17123. axis.renderMinorTick(pos);
  17124. });
  17125. }
  17126. // Major ticks. Pull out the first item and render it last so that
  17127. // we can get the position of the neighbour label. #808.
  17128. if (tickPositions.length) { // #1300
  17129. tickPositions.forEach(function (pos, i) {
  17130. axis.renderTick(pos, i);
  17131. });
  17132. // In a categorized axis, the tick marks are displayed
  17133. // between labels. So we need to add a tick mark and
  17134. // grid line at the left edge of the X axis.
  17135. if (tickmarkOffset && (axis.min === 0 || axis.single)) {
  17136. if (!ticks[-1]) {
  17137. ticks[-1] = new Tick(axis, -1, null, true);
  17138. }
  17139. ticks[-1].render(-1);
  17140. }
  17141. }
  17142. // alternate grid color
  17143. if (alternateGridColor) {
  17144. tickPositions.forEach(function (pos, i) {
  17145. to = typeof tickPositions[i + 1] !== 'undefined' ?
  17146. tickPositions[i + 1] + tickmarkOffset :
  17147. axis.max - tickmarkOffset;
  17148. if (i % 2 === 0 &&
  17149. pos < axis.max &&
  17150. to <= axis.max + (chart.polar ?
  17151. -tickmarkOffset :
  17152. tickmarkOffset)) { // #2248, #4660
  17153. if (!alternateBands[pos]) {
  17154. // Should be imported from PlotLineOrBand.js, but
  17155. // the dependency cycle with axis is a problem
  17156. alternateBands[pos] = new H.PlotLineOrBand(axis);
  17157. }
  17158. from = pos + tickmarkOffset; // #949
  17159. alternateBands[pos].options = {
  17160. from: log ? log.lin2log(from) : from,
  17161. to: log ? log.lin2log(to) : to,
  17162. color: alternateGridColor,
  17163. className: 'highcharts-alternate-grid'
  17164. };
  17165. alternateBands[pos].render();
  17166. alternateBands[pos].isActive = true;
  17167. }
  17168. });
  17169. }
  17170. // custom plot lines and bands
  17171. if (!axis._addedPlotLB) { // only first time
  17172. (options.plotLines || [])
  17173. .concat(options.plotBands || [])
  17174. .forEach(function (plotLineOptions) {
  17175. axis.addPlotBandOrLine(plotLineOptions);
  17176. });
  17177. axis._addedPlotLB = true;
  17178. }
  17179. } // end if hasData
  17180. // Remove inactive ticks
  17181. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  17182. var i,
  17183. forDestruction = [],
  17184. delay = animation.duration,
  17185. destroyInactiveItems = function () {
  17186. i = forDestruction.length;
  17187. while (i--) {
  17188. // When resizing rapidly, the same items
  17189. // may be destroyed in different timeouts,
  17190. // or the may be reactivated
  17191. if (coll[forDestruction[i]] &&
  17192. !coll[forDestruction[i]].isActive) {
  17193. coll[forDestruction[i]].destroy();
  17194. delete coll[forDestruction[i]];
  17195. }
  17196. }
  17197. };
  17198. objectEach(coll, function (tick, pos) {
  17199. if (!tick.isActive) {
  17200. // Render to zero opacity
  17201. tick.render(pos, false, 0);
  17202. tick.isActive = false;
  17203. forDestruction.push(pos);
  17204. }
  17205. });
  17206. // When the objects are finished fading out, destroy them
  17207. syncTimeout(destroyInactiveItems, coll === alternateBands ||
  17208. !chart.hasRendered ||
  17209. !delay ?
  17210. 0 :
  17211. delay);
  17212. });
  17213. // Set the axis line path
  17214. if (axisLine) {
  17215. axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
  17216. d: this.getLinePath(axisLine.strokeWidth())
  17217. });
  17218. axisLine.isPlaced = true;
  17219. // Show or hide the line depending on options.showEmpty
  17220. axisLine[showAxis ? 'show' : 'hide'](showAxis);
  17221. }
  17222. if (axisTitle && showAxis) {
  17223. var titleXy = axis.getTitlePosition();
  17224. if (isNumber(titleXy.y)) {
  17225. axisTitle[axisTitle.isNew ? 'attr' : 'animate'](titleXy);
  17226. axisTitle.isNew = false;
  17227. }
  17228. else {
  17229. axisTitle.attr('y', -9999);
  17230. axisTitle.isNew = true;
  17231. }
  17232. }
  17233. // Stacked totals:
  17234. if (stackLabelOptions && stackLabelOptions.enabled && axis.stacking) {
  17235. axis.stacking.renderStackTotals();
  17236. }
  17237. // End stacked totals
  17238. axis.isDirty = false;
  17239. fireEvent(this, 'afterRender');
  17240. };
  17241. /**
  17242. * Redraw the axis to reflect changes in the data or axis extremes. Called
  17243. * internally from Highcharts.Chart#redraw.
  17244. *
  17245. * @private
  17246. * @function Highcharts.Axis#redraw
  17247. */
  17248. Axis.prototype.redraw = function () {
  17249. if (this.visible) {
  17250. // render the axis
  17251. this.render();
  17252. // move plot lines and bands
  17253. this.plotLinesAndBands.forEach(function (plotLine) {
  17254. plotLine.render();
  17255. });
  17256. }
  17257. // mark associated series as dirty and ready for redraw
  17258. this.series.forEach(function (series) {
  17259. series.isDirty = true;
  17260. });
  17261. };
  17262. /**
  17263. * Returns an array of axis properties, that should be untouched during
  17264. * reinitialization.
  17265. *
  17266. * @private
  17267. * @function Highcharts.Axis#getKeepProps
  17268. *
  17269. * @return {Array<string>}
  17270. */
  17271. Axis.prototype.getKeepProps = function () {
  17272. return (this.keepProps || Axis.keepProps);
  17273. };
  17274. /**
  17275. * Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
  17276. * to fully remove the axis.
  17277. *
  17278. * @private
  17279. * @function Highcharts.Axis#destroy
  17280. *
  17281. * @param {boolean} [keepEvents]
  17282. * Whether to preserve events, used internally in Axis.update.
  17283. */
  17284. Axis.prototype.destroy = function (keepEvents) {
  17285. var axis = this,
  17286. plotLinesAndBands = axis.plotLinesAndBands,
  17287. plotGroup,
  17288. i;
  17289. fireEvent(this, 'destroy', { keepEvents: keepEvents });
  17290. // Remove the events
  17291. if (!keepEvents) {
  17292. removeEvent(axis);
  17293. }
  17294. // Destroy collections
  17295. [axis.ticks, axis.minorTicks, axis.alternateBands].forEach(function (coll) {
  17296. destroyObjectProperties(coll);
  17297. });
  17298. if (plotLinesAndBands) {
  17299. i = plotLinesAndBands.length;
  17300. while (i--) { // #1975
  17301. plotLinesAndBands[i].destroy();
  17302. }
  17303. }
  17304. // Destroy elements
  17305. ['axisLine', 'axisTitle', 'axisGroup',
  17306. 'gridGroup', 'labelGroup', 'cross', 'scrollbar'].forEach(function (prop) {
  17307. if (axis[prop]) {
  17308. axis[prop] = axis[prop].destroy();
  17309. }
  17310. });
  17311. // Destroy each generated group for plotlines and plotbands
  17312. for (plotGroup in axis.plotLinesAndBandsGroups) { // eslint-disable-line guard-for-in
  17313. axis.plotLinesAndBandsGroups[plotGroup] =
  17314. axis.plotLinesAndBandsGroups[plotGroup].destroy();
  17315. }
  17316. // Delete all properties and fall back to the prototype.
  17317. objectEach(axis, function (val, key) {
  17318. if (axis.getKeepProps().indexOf(key) === -1) {
  17319. delete axis[key];
  17320. }
  17321. });
  17322. };
  17323. /**
  17324. * Internal function to draw a crosshair.
  17325. *
  17326. * @function Highcharts.Axis#drawCrosshair
  17327. *
  17328. * @param {Highcharts.PointerEventObject} [e]
  17329. * The event arguments from the modified pointer event, extended with
  17330. * `chartX` and `chartY`
  17331. *
  17332. * @param {Highcharts.Point} [point]
  17333. * The Point object if the crosshair snaps to points.
  17334. *
  17335. * @fires Highcharts.Axis#event:afterDrawCrosshair
  17336. * @fires Highcharts.Axis#event:drawCrosshair
  17337. */
  17338. Axis.prototype.drawCrosshair = function (e, point) {
  17339. var path,
  17340. options = this.crosshair,
  17341. snap = pick(options.snap,
  17342. true),
  17343. pos,
  17344. categorized,
  17345. graphic = this.cross,
  17346. crossOptions,
  17347. chart = this.chart;
  17348. fireEvent(this, 'drawCrosshair', { e: e, point: point });
  17349. // Use last available event when updating non-snapped crosshairs without
  17350. // mouse interaction (#5287)
  17351. if (!e) {
  17352. e = this.cross && this.cross.e;
  17353. }
  17354. if (
  17355. // Disabled in options
  17356. !this.crosshair ||
  17357. // Snap
  17358. ((defined(point) || !snap) === false)) {
  17359. this.hideCrosshair();
  17360. }
  17361. else {
  17362. // Get the path
  17363. if (!snap) {
  17364. pos = e &&
  17365. (this.horiz ?
  17366. e.chartX - this.pos :
  17367. this.len - e.chartY + this.pos);
  17368. }
  17369. else if (defined(point)) {
  17370. // #3834
  17371. pos = pick(this.coll !== 'colorAxis' ?
  17372. point.crosshairPos : // 3D axis extension
  17373. null, this.isXAxis ?
  17374. point.plotX :
  17375. this.len - point.plotY);
  17376. }
  17377. if (defined(pos)) {
  17378. crossOptions = {
  17379. // value, only used on radial
  17380. value: point && (this.isXAxis ?
  17381. point.x :
  17382. pick(point.stackY, point.y)),
  17383. translatedValue: pos
  17384. };
  17385. if (chart.polar) {
  17386. // Additional information required for crosshairs in
  17387. // polar chart
  17388. extend(crossOptions, {
  17389. isCrosshair: true,
  17390. chartX: e && e.chartX,
  17391. chartY: e && e.chartY,
  17392. point: point
  17393. });
  17394. }
  17395. path = this.getPlotLinePath(crossOptions) ||
  17396. null; // #3189
  17397. }
  17398. if (!defined(path)) {
  17399. this.hideCrosshair();
  17400. return;
  17401. }
  17402. categorized = this.categories && !this.isRadial;
  17403. // Draw the cross
  17404. if (!graphic) {
  17405. this.cross = graphic = chart.renderer
  17406. .path()
  17407. .addClass('highcharts-crosshair highcharts-crosshair-' +
  17408. (categorized ? 'category ' : 'thin ') +
  17409. options.className)
  17410. .attr({
  17411. zIndex: pick(options.zIndex, 2)
  17412. })
  17413. .add();
  17414. // Presentational attributes
  17415. if (!chart.styledMode) {
  17416. graphic.attr({
  17417. stroke: options.color ||
  17418. (categorized ?
  17419. Color
  17420. .parse('#ccd6eb')
  17421. .setOpacity(0.25)
  17422. .get() :
  17423. '#cccccc'),
  17424. 'stroke-width': pick(options.width, 1)
  17425. }).css({
  17426. 'pointer-events': 'none'
  17427. });
  17428. if (options.dashStyle) {
  17429. graphic.attr({
  17430. dashstyle: options.dashStyle
  17431. });
  17432. }
  17433. }
  17434. }
  17435. graphic.show().attr({
  17436. d: path
  17437. });
  17438. if (categorized && !options.width) {
  17439. graphic.attr({
  17440. 'stroke-width': this.transA
  17441. });
  17442. }
  17443. this.cross.e = e;
  17444. }
  17445. fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
  17446. };
  17447. /**
  17448. * Hide the crosshair if visible.
  17449. *
  17450. * @function Highcharts.Axis#hideCrosshair
  17451. */
  17452. Axis.prototype.hideCrosshair = function () {
  17453. if (this.cross) {
  17454. this.cross.hide();
  17455. }
  17456. fireEvent(this, 'afterHideCrosshair');
  17457. };
  17458. /**
  17459. * Check whether the chart has vertical panning ('y' or 'xy' type).
  17460. *
  17461. * @private
  17462. * @function Highcharts.Axis#hasVerticalPanning
  17463. * @return {boolean}
  17464. *
  17465. */
  17466. Axis.prototype.hasVerticalPanning = function () {
  17467. var _a,
  17468. _b;
  17469. return /y/.test(((_b = (_a = this.chart.options.chart) === null || _a === void 0 ? void 0 : _a.panning) === null || _b === void 0 ? void 0 : _b.type) || '');
  17470. };
  17471. /**
  17472. * Check whether the given value is a positive valid axis value.
  17473. *
  17474. * @private
  17475. * @function Highcharts.Axis#validatePositiveValue
  17476. *
  17477. * @param {unknown} value
  17478. * The axis value
  17479. * @return {boolean}
  17480. *
  17481. */
  17482. Axis.prototype.validatePositiveValue = function (value) {
  17483. return isNumber(value) && value > 0;
  17484. };
  17485. /* *
  17486. *
  17487. * Static Properties
  17488. *
  17489. * */
  17490. /**
  17491. * The X axis or category axis. Normally this is the horizontal axis,
  17492. * though if the chart is inverted this is the vertical axis. In case of
  17493. * multiple axes, the xAxis node is an array of configuration objects.
  17494. *
  17495. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  17496. * access to the axis.
  17497. *
  17498. * @productdesc {highmaps}
  17499. * In Highmaps, the axis is hidden, but it is used behind the scenes to
  17500. * control features like zooming and panning. Zooming is in effect the same
  17501. * as setting the extremes of one of the exes.
  17502. *
  17503. * @type {*|Array<*>}
  17504. * @optionparent xAxis
  17505. *
  17506. * @private
  17507. */
  17508. Axis.defaultOptions = {
  17509. /**
  17510. * When using multiple axis, the ticks of two or more opposite axes
  17511. * will automatically be aligned by adding ticks to the axis or axes
  17512. * with the least ticks, as if `tickAmount` were specified.
  17513. *
  17514. * This can be prevented by setting `alignTicks` to false. If the grid
  17515. * lines look messy, it's a good idea to hide them for the secondary
  17516. * axis by setting `gridLineWidth` to 0.
  17517. *
  17518. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  17519. * then the `alignTicks ` will be disabled for the Axis.
  17520. *
  17521. * Disabled for logarithmic axes.
  17522. *
  17523. * @type {boolean}
  17524. * @default true
  17525. * @product highcharts highstock gantt
  17526. * @apioption xAxis.alignTicks
  17527. */
  17528. /**
  17529. * Whether to allow decimals in this axis' ticks. When counting
  17530. * integers, like persons or hits on a web page, decimals should
  17531. * be avoided in the labels.
  17532. *
  17533. * @see [minTickInterval](#xAxis.minTickInterval)
  17534. *
  17535. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
  17536. * True by default
  17537. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
  17538. * False
  17539. *
  17540. * @type {boolean}
  17541. * @default true
  17542. * @since 2.0
  17543. * @apioption xAxis.allowDecimals
  17544. */
  17545. /**
  17546. * When using an alternate grid color, a band is painted across the
  17547. * plot area between every other grid line.
  17548. *
  17549. * @sample {highcharts} highcharts/yaxis/alternategridcolor/
  17550. * Alternate grid color on the Y axis
  17551. * @sample {highstock} stock/xaxis/alternategridcolor/
  17552. * Alternate grid color on the Y axis
  17553. *
  17554. * @type {Highcharts.ColorType}
  17555. * @apioption xAxis.alternateGridColor
  17556. */
  17557. /**
  17558. * An array defining breaks in the axis, the sections defined will be
  17559. * left out and all the points shifted closer to each other.
  17560. *
  17561. * @productdesc {highcharts}
  17562. * Requires that the broken-axis.js module is loaded.
  17563. *
  17564. * @sample {highcharts} highcharts/axisbreak/break-simple/
  17565. * Simple break
  17566. * @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
  17567. * Advanced with callback
  17568. * @sample {highstock} stock/demo/intraday-breaks/
  17569. * Break on nights and weekends
  17570. *
  17571. * @type {Array<*>}
  17572. * @since 4.1.0
  17573. * @product highcharts highstock gantt
  17574. * @apioption xAxis.breaks
  17575. */
  17576. /**
  17577. * A number indicating how much space should be left between the start
  17578. * and the end of the break. The break size is given in axis units,
  17579. * so for instance on a `datetime` axis, a break size of 3600000 would
  17580. * indicate the equivalent of an hour.
  17581. *
  17582. * @type {number}
  17583. * @default 0
  17584. * @since 4.1.0
  17585. * @product highcharts highstock gantt
  17586. * @apioption xAxis.breaks.breakSize
  17587. */
  17588. /**
  17589. * The point where the break starts.
  17590. *
  17591. * @type {number}
  17592. * @since 4.1.0
  17593. * @product highcharts highstock gantt
  17594. * @apioption xAxis.breaks.from
  17595. */
  17596. /**
  17597. * Defines an interval after which the break appears again. By default
  17598. * the breaks do not repeat.
  17599. *
  17600. * @type {number}
  17601. * @default 0
  17602. * @since 4.1.0
  17603. * @product highcharts highstock gantt
  17604. * @apioption xAxis.breaks.repeat
  17605. */
  17606. /**
  17607. * The point where the break ends.
  17608. *
  17609. * @type {number}
  17610. * @since 4.1.0
  17611. * @product highcharts highstock gantt
  17612. * @apioption xAxis.breaks.to
  17613. */
  17614. /**
  17615. * If categories are present for the xAxis, names are used instead of
  17616. * numbers for that axis.
  17617. *
  17618. * Since Highcharts 3.0, categories can also
  17619. * be extracted by giving each point a [name](#series.data) and setting
  17620. * axis [type](#xAxis.type) to `category`. However, if you have multiple
  17621. * series, best practice remains defining the `categories` array.
  17622. *
  17623. * Example: `categories: ['Apples', 'Bananas', 'Oranges']`
  17624. *
  17625. * @sample {highcharts} highcharts/demo/line-labels/
  17626. * With
  17627. * @sample {highcharts} highcharts/xaxis/categories/
  17628. * Without
  17629. *
  17630. * @type {Array<string>}
  17631. * @product highcharts gantt
  17632. * @apioption xAxis.categories
  17633. */
  17634. /**
  17635. * The highest allowed value for automatically computed axis extremes.
  17636. *
  17637. * @see [floor](#xAxis.floor)
  17638. *
  17639. * @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
  17640. * Floor and ceiling
  17641. *
  17642. * @type {number}
  17643. * @since 4.0
  17644. * @product highcharts highstock gantt
  17645. * @apioption xAxis.ceiling
  17646. */
  17647. /**
  17648. * A class name that opens for styling the axis by CSS, especially in
  17649. * Highcharts styled mode. The class name is applied to group elements
  17650. * for the grid, axis elements and labels.
  17651. *
  17652. * @sample {highcharts|highstock|highmaps} highcharts/css/axis/
  17653. * Multiple axes with separate styling
  17654. *
  17655. * @type {string}
  17656. * @since 5.0.0
  17657. * @apioption xAxis.className
  17658. */
  17659. /**
  17660. * Configure a crosshair that follows either the mouse pointer or the
  17661. * hovered point.
  17662. *
  17663. * In styled mode, the crosshairs are styled in the
  17664. * `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
  17665. * `.highcharts-xaxis-category` classes.
  17666. *
  17667. * @productdesc {highstock}
  17668. * In Highstock, by default, the crosshair is enabled on the X axis and
  17669. * disabled on the Y axis.
  17670. *
  17671. * @sample {highcharts} highcharts/xaxis/crosshair-both/
  17672. * Crosshair on both axes
  17673. * @sample {highstock} stock/xaxis/crosshairs-xy/
  17674. * Crosshair on both axes
  17675. * @sample {highmaps} highcharts/xaxis/crosshair-both/
  17676. * Crosshair on both axes
  17677. *
  17678. * @declare Highcharts.AxisCrosshairOptions
  17679. * @type {boolean|*}
  17680. * @default false
  17681. * @since 4.1
  17682. * @apioption xAxis.crosshair
  17683. */
  17684. /**
  17685. * A class name for the crosshair, especially as a hook for styling.
  17686. *
  17687. * @type {string}
  17688. * @since 5.0.0
  17689. * @apioption xAxis.crosshair.className
  17690. */
  17691. /**
  17692. * The color of the crosshair. Defaults to `#cccccc` for numeric and
  17693. * datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
  17694. * the crosshair by default highlights the whole category.
  17695. *
  17696. * @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
  17697. * Customized crosshairs
  17698. *
  17699. * @type {Highcharts.ColorType}
  17700. * @default #cccccc
  17701. * @since 4.1
  17702. * @apioption xAxis.crosshair.color
  17703. */
  17704. /**
  17705. * The dash style for the crosshair. See
  17706. * [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  17707. * for possible values.
  17708. *
  17709. * @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
  17710. * Dotted crosshair
  17711. * @sample {highstock} stock/xaxis/crosshair-dashed/
  17712. * Dashed X axis crosshair
  17713. *
  17714. * @type {Highcharts.DashStyleValue}
  17715. * @default Solid
  17716. * @since 4.1
  17717. * @apioption xAxis.crosshair.dashStyle
  17718. */
  17719. /**
  17720. * A label on the axis next to the crosshair.
  17721. *
  17722. * In styled mode, the label is styled with the
  17723. * `.highcharts-crosshair-label` class.
  17724. *
  17725. * @sample {highstock} stock/xaxis/crosshair-label/
  17726. * Crosshair labels
  17727. * @sample {highstock} highcharts/css/crosshair-label/
  17728. * Style mode
  17729. *
  17730. * @declare Highcharts.AxisCrosshairLabelOptions
  17731. * @since 2.1
  17732. * @product highstock
  17733. * @apioption xAxis.crosshair.label
  17734. */
  17735. /**
  17736. * Alignment of the label compared to the axis. Defaults to `"left"` for
  17737. * right-side axes, `"right"` for left-side axes and `"center"` for
  17738. * horizontal axes.
  17739. *
  17740. * @type {Highcharts.AlignValue}
  17741. * @since 2.1
  17742. * @product highstock
  17743. * @apioption xAxis.crosshair.label.align
  17744. */
  17745. /**
  17746. * The background color for the label. Defaults to the related series
  17747. * color, or `#666666` if that is not available.
  17748. *
  17749. * @type {Highcharts.ColorType}
  17750. * @since 2.1
  17751. * @product highstock
  17752. * @apioption xAxis.crosshair.label.backgroundColor
  17753. */
  17754. /**
  17755. * The border color for the crosshair label
  17756. *
  17757. * @type {Highcharts.ColorType}
  17758. * @since 2.1
  17759. * @product highstock
  17760. * @apioption xAxis.crosshair.label.borderColor
  17761. */
  17762. /**
  17763. * The border corner radius of the crosshair label.
  17764. *
  17765. * @type {number}
  17766. * @default 3
  17767. * @since 2.1.10
  17768. * @product highstock
  17769. * @apioption xAxis.crosshair.label.borderRadius
  17770. */
  17771. /**
  17772. * The border width for the crosshair label.
  17773. *
  17774. * @type {number}
  17775. * @default 0
  17776. * @since 2.1
  17777. * @product highstock
  17778. * @apioption xAxis.crosshair.label.borderWidth
  17779. */
  17780. /**
  17781. * Flag to enable crosshair's label.
  17782. *
  17783. * @sample {highstock} stock/xaxis/crosshairs-xy/
  17784. * Enabled label for yAxis' crosshair
  17785. *
  17786. * @type {boolean}
  17787. * @default false
  17788. * @since 2.1
  17789. * @product highstock
  17790. * @apioption xAxis.crosshair.label.enabled
  17791. */
  17792. /**
  17793. * A format string for the crosshair label. Defaults to `{value}` for
  17794. * numeric axes and `{value:%b %d, %Y}` for datetime axes.
  17795. *
  17796. * @type {string}
  17797. * @since 2.1
  17798. * @product highstock
  17799. * @apioption xAxis.crosshair.label.format
  17800. */
  17801. /**
  17802. * Formatter function for the label text.
  17803. *
  17804. * @type {Highcharts.XAxisCrosshairLabelFormatterCallbackFunction}
  17805. * @since 2.1
  17806. * @product highstock
  17807. * @apioption xAxis.crosshair.label.formatter
  17808. */
  17809. /**
  17810. * Padding inside the crosshair label.
  17811. *
  17812. * @type {number}
  17813. * @default 8
  17814. * @since 2.1
  17815. * @product highstock
  17816. * @apioption xAxis.crosshair.label.padding
  17817. */
  17818. /**
  17819. * The shape to use for the label box.
  17820. *
  17821. * @type {string}
  17822. * @default callout
  17823. * @since 2.1
  17824. * @product highstock
  17825. * @apioption xAxis.crosshair.label.shape
  17826. */
  17827. /**
  17828. * Text styles for the crosshair label.
  17829. *
  17830. * @type {Highcharts.CSSObject}
  17831. * @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
  17832. * @since 2.1
  17833. * @product highstock
  17834. * @apioption xAxis.crosshair.label.style
  17835. */
  17836. /**
  17837. * Whether the crosshair should snap to the point or follow the pointer
  17838. * independent of points.
  17839. *
  17840. * @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
  17841. * True by default
  17842. * @sample {highmaps} maps/demo/latlon-advanced/
  17843. * Snap is false
  17844. *
  17845. * @type {boolean}
  17846. * @default true
  17847. * @since 4.1
  17848. * @apioption xAxis.crosshair.snap
  17849. */
  17850. /**
  17851. * The pixel width of the crosshair. Defaults to 1 for numeric or
  17852. * datetime axes, and for one category width for category axes.
  17853. *
  17854. * @sample {highcharts} highcharts/xaxis/crosshair-customized/
  17855. * Customized crosshairs
  17856. * @sample {highstock} highcharts/xaxis/crosshair-customized/
  17857. * Customized crosshairs
  17858. * @sample {highmaps} highcharts/xaxis/crosshair-customized/
  17859. * Customized crosshairs
  17860. *
  17861. * @type {number}
  17862. * @default 1
  17863. * @since 4.1
  17864. * @apioption xAxis.crosshair.width
  17865. */
  17866. /**
  17867. * The Z index of the crosshair. Higher Z indices allow drawing the
  17868. * crosshair on top of the series or behind the grid lines.
  17869. *
  17870. * @type {number}
  17871. * @default 2
  17872. * @since 4.1
  17873. * @apioption xAxis.crosshair.zIndex
  17874. */
  17875. /**
  17876. * Whether to zoom axis. If `chart.zoomType` is set, the option allows
  17877. * to disable zooming on an individual axis.
  17878. *
  17879. * @sample {highcharts} highcharts/xaxis/zoomenabled/
  17880. * Zoom enabled is false
  17881. *
  17882. *
  17883. * @type {boolean}
  17884. * @default enabled
  17885. * @apioption xAxis.zoomEnabled
  17886. */
  17887. /**
  17888. * For a datetime axis, the scale will automatically adjust to the
  17889. * appropriate unit. This member gives the default string
  17890. * representations used for each unit. For intermediate values,
  17891. * different units may be used, for example the `day` unit can be used
  17892. * on midnight and `hour` unit be used for intermediate values on the
  17893. * same axis. For an overview of the replacement codes, see
  17894. * [dateFormat](/class-reference/Highcharts#dateFormat).
  17895. *
  17896. * Defaults to:
  17897. * ```js
  17898. * {
  17899. * millisecond: '%H:%M:%S.%L',
  17900. * second: '%H:%M:%S',
  17901. * minute: '%H:%M',
  17902. * hour: '%H:%M',
  17903. * day: '%e. %b',
  17904. * week: '%e. %b',
  17905. * month: '%b \'%y',
  17906. * year: '%Y'
  17907. * }
  17908. * ```
  17909. *
  17910. * @sample {highcharts} highcharts/xaxis/datetimelabelformats/
  17911. * Different day format on X axis
  17912. * @sample {highstock} stock/xaxis/datetimelabelformats/
  17913. * More information in x axis labels
  17914. *
  17915. * @declare Highcharts.AxisDateTimeLabelFormatsOptions
  17916. * @product highcharts highstock
  17917. */
  17918. dateTimeLabelFormats: {
  17919. /**
  17920. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17921. * @type {string|*}
  17922. */
  17923. millisecond: {
  17924. main: '%H:%M:%S.%L',
  17925. range: false
  17926. },
  17927. /**
  17928. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17929. * @type {string|*}
  17930. */
  17931. second: {
  17932. main: '%H:%M:%S',
  17933. range: false
  17934. },
  17935. /**
  17936. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17937. * @type {string|*}
  17938. */
  17939. minute: {
  17940. main: '%H:%M',
  17941. range: false
  17942. },
  17943. /**
  17944. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17945. * @type {string|*}
  17946. */
  17947. hour: {
  17948. main: '%H:%M',
  17949. range: false
  17950. },
  17951. /**
  17952. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17953. * @type {string|*}
  17954. */
  17955. day: {
  17956. main: '%e. %b'
  17957. },
  17958. /**
  17959. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17960. * @type {string|*}
  17961. */
  17962. week: {
  17963. main: '%e. %b'
  17964. },
  17965. /**
  17966. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17967. * @type {string|*}
  17968. */
  17969. month: {
  17970. main: '%b \'%y'
  17971. },
  17972. /**
  17973. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17974. * @type {string|*}
  17975. */
  17976. year: {
  17977. main: '%Y'
  17978. }
  17979. },
  17980. /**
  17981. * Whether to force the axis to end on a tick. Use this option with
  17982. * the `maxPadding` option to control the axis end.
  17983. *
  17984. * @productdesc {highstock}
  17985. * In Highstock, `endOnTick` is always `false` when the navigator
  17986. * is enabled, to prevent jumpy scrolling.
  17987. *
  17988. * @sample {highcharts} highcharts/chart/reflow-true/
  17989. * True by default
  17990. * @sample {highcharts} highcharts/yaxis/endontick/
  17991. * False
  17992. * @sample {highstock} stock/demo/basic-line/
  17993. * True by default
  17994. * @sample {highstock} stock/xaxis/endontick/
  17995. * False
  17996. *
  17997. * @since 1.2.0
  17998. */
  17999. endOnTick: false,
  18000. /**
  18001. * Event handlers for the axis.
  18002. *
  18003. * @type {*}
  18004. * @apioption xAxis.events
  18005. */
  18006. /**
  18007. * An event fired after the breaks have rendered.
  18008. *
  18009. * @see [breaks](#xAxis.breaks)
  18010. *
  18011. * @sample {highcharts} highcharts/axisbreak/break-event/
  18012. * AfterBreak Event
  18013. *
  18014. * @type {Highcharts.AxisEventCallbackFunction}
  18015. * @since 4.1.0
  18016. * @product highcharts gantt
  18017. * @apioption xAxis.events.afterBreaks
  18018. */
  18019. /**
  18020. * As opposed to the `setExtremes` event, this event fires after the
  18021. * final min and max values are computed and corrected for `minRange`.
  18022. *
  18023. * Fires when the minimum and maximum is set for the axis, either by
  18024. * calling the `.setExtremes()` method or by selecting an area in the
  18025. * chart. One parameter, `event`, is passed to the function, containing
  18026. * common event information.
  18027. *
  18028. * The new user set minimum and maximum values can be found by
  18029. * `event.min` and `event.max`. These reflect the axis minimum and
  18030. * maximum in axis values. The actual data extremes are found in
  18031. * `event.dataMin` and `event.dataMax`.
  18032. *
  18033. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  18034. * @since 2.3
  18035. * @context Highcharts.Axis
  18036. * @apioption xAxis.events.afterSetExtremes
  18037. */
  18038. /**
  18039. * An event fired when a break from this axis occurs on a point.
  18040. *
  18041. * @see [breaks](#xAxis.breaks)
  18042. *
  18043. * @sample {highcharts} highcharts/axisbreak/break-visualized/
  18044. * Visualization of a Break
  18045. *
  18046. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  18047. * @since 4.1.0
  18048. * @product highcharts gantt
  18049. * @context Highcharts.Axis
  18050. * @apioption xAxis.events.pointBreak
  18051. */
  18052. /**
  18053. * An event fired when a point falls inside a break from this axis.
  18054. *
  18055. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  18056. * @product highcharts highstock gantt
  18057. * @context Highcharts.Axis
  18058. * @apioption xAxis.events.pointInBreak
  18059. */
  18060. /**
  18061. * Fires when the minimum and maximum is set for the axis, either by
  18062. * calling the `.setExtremes()` method or by selecting an area in the
  18063. * chart. One parameter, `event`, is passed to the function,
  18064. * containing common event information.
  18065. *
  18066. * The new user set minimum and maximum values can be found by
  18067. * `event.min` and `event.max`. These reflect the axis minimum and
  18068. * maximum in data values. When an axis is zoomed all the way out from
  18069. * the "Reset zoom" button, `event.min` and `event.max` are null, and
  18070. * the new extremes are set based on `this.dataMin` and `this.dataMax`.
  18071. *
  18072. * @sample {highstock} stock/xaxis/events-setextremes/
  18073. * Log new extremes on x axis
  18074. *
  18075. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  18076. * @since 1.2.0
  18077. * @context Highcharts.Axis
  18078. * @apioption xAxis.events.setExtremes
  18079. */
  18080. /**
  18081. * The lowest allowed value for automatically computed axis extremes.
  18082. *
  18083. * @see [ceiling](#yAxis.ceiling)
  18084. *
  18085. * @sample {highcharts} highcharts/yaxis/floor-ceiling/
  18086. * Floor and ceiling
  18087. * @sample {highstock} stock/demo/lazy-loading/
  18088. * Prevent negative stock price on Y axis
  18089. *
  18090. * @type {number}
  18091. * @since 4.0
  18092. * @product highcharts highstock gantt
  18093. * @apioption xAxis.floor
  18094. */
  18095. /**
  18096. * The dash or dot style of the grid lines. For possible values, see
  18097. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  18098. *
  18099. * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
  18100. * Long dashes
  18101. * @sample {highstock} stock/xaxis/gridlinedashstyle/
  18102. * Long dashes
  18103. *
  18104. * @type {Highcharts.DashStyleValue}
  18105. * @default Solid
  18106. * @since 1.2
  18107. * @apioption xAxis.gridLineDashStyle
  18108. */
  18109. /**
  18110. * The Z index of the grid lines.
  18111. *
  18112. * @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
  18113. * A Z index of 4 renders the grid above the graph
  18114. *
  18115. * @type {number}
  18116. * @default 1
  18117. * @product highcharts highstock gantt
  18118. * @apioption xAxis.gridZIndex
  18119. */
  18120. /**
  18121. * An id for the axis. This can be used after render time to get
  18122. * a pointer to the axis object through `chart.get()`.
  18123. *
  18124. * @sample {highcharts} highcharts/xaxis/id/
  18125. * Get the object
  18126. * @sample {highstock} stock/xaxis/id/
  18127. * Get the object
  18128. *
  18129. * @type {string}
  18130. * @since 1.2.0
  18131. * @apioption xAxis.id
  18132. */
  18133. /**
  18134. * The axis labels show the number or category for each tick.
  18135. *
  18136. * Since v8.0.0: Labels are animated in categorized x-axis with
  18137. * updating data if `tickInterval` and `step` is set to 1.
  18138. *
  18139. * @productdesc {highmaps}
  18140. * X and Y axis labels are by default disabled in Highmaps, but the
  18141. * functionality is inherited from Highcharts and used on `colorAxis`,
  18142. * and can be enabled on X and Y axes too.
  18143. */
  18144. labels: {
  18145. /**
  18146. * What part of the string the given position is anchored to.
  18147. * If `left`, the left side of the string is at the axis position.
  18148. * Can be one of `"left"`, `"center"` or `"right"`. Defaults to
  18149. * an intelligent guess based on which side of the chart the axis
  18150. * is on and the rotation of the label.
  18151. *
  18152. * @see [reserveSpace](#xAxis.labels.reserveSpace)
  18153. *
  18154. * @sample {highcharts} highcharts/xaxis/labels-align-left/
  18155. * Left
  18156. * @sample {highcharts} highcharts/xaxis/labels-align-right/
  18157. * Right
  18158. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  18159. * Left-aligned labels on a vertical category axis
  18160. *
  18161. * @type {Highcharts.AlignValue}
  18162. * @apioption xAxis.labels.align
  18163. */
  18164. /**
  18165. * For horizontal axes, the allowed degrees of label rotation
  18166. * to prevent overlapping labels. If there is enough space,
  18167. * labels are not rotated. As the chart gets narrower, it
  18168. * will start rotating the labels -45 degrees, then remove
  18169. * every second label and try again with rotations 0 and -45 etc.
  18170. * Set it to `false` to disable rotation, which will
  18171. * cause the labels to word-wrap if possible.
  18172. *
  18173. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
  18174. * Default auto rotation of 0 or -45
  18175. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
  18176. * Custom graded auto rotation
  18177. *
  18178. * @type {Array<number>|false}
  18179. * @default [-45]
  18180. * @since 4.1.0
  18181. * @product highcharts highstock gantt
  18182. * @apioption xAxis.labels.autoRotation
  18183. */
  18184. /**
  18185. * When each category width is more than this many pixels, we don't
  18186. * apply auto rotation. Instead, we lay out the axis label with word
  18187. * wrap. A lower limit makes sense when the label contains multiple
  18188. * short words that don't extend the available horizontal space for
  18189. * each label.
  18190. *
  18191. * @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
  18192. * Lower limit
  18193. *
  18194. * @type {number}
  18195. * @default 80
  18196. * @since 4.1.5
  18197. * @product highcharts gantt
  18198. * @apioption xAxis.labels.autoRotationLimit
  18199. */
  18200. /**
  18201. * Polar charts only. The label's pixel distance from the perimeter
  18202. * of the plot area.
  18203. *
  18204. * @type {number}
  18205. * @default 15
  18206. * @product highcharts gantt
  18207. * @apioption xAxis.labels.distance
  18208. */
  18209. /**
  18210. * Enable or disable the axis labels.
  18211. *
  18212. * @sample {highcharts} highcharts/xaxis/labels-enabled/
  18213. * X axis labels disabled
  18214. * @sample {highstock} stock/xaxis/labels-enabled/
  18215. * X axis labels disabled
  18216. *
  18217. * @default {highcharts|highstock|gantt} true
  18218. * @default {highmaps} false
  18219. */
  18220. enabled: true,
  18221. /**
  18222. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  18223. * for the axis label.
  18224. *
  18225. * @sample {highcharts|highstock} highcharts/yaxis/labels-format/
  18226. * Add units to Y axis label
  18227. *
  18228. * @type {string}
  18229. * @default {value}
  18230. * @since 3.0
  18231. * @apioption xAxis.labels.format
  18232. */
  18233. /**
  18234. * Callback JavaScript function to format the label. The value
  18235. * is given by `this.value`. Additional properties for `this` are
  18236. * `axis`, `chart`, `isFirst` and `isLast`. The value of the default
  18237. * label formatter can be retrieved by calling
  18238. * `this.axis.defaultLabelFormatter.call(this)` within the function.
  18239. *
  18240. * Defaults to:
  18241. * ```js
  18242. * function() {
  18243. * return this.value;
  18244. * }
  18245. * ```
  18246. *
  18247. * @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
  18248. * Linked category names
  18249. * @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
  18250. * Modified numeric labels
  18251. * @sample {highstock} stock/xaxis/labels-formatter/
  18252. * Added units on Y axis
  18253. *
  18254. * @type {Highcharts.AxisLabelsFormatterCallbackFunction}
  18255. * @apioption xAxis.labels.formatter
  18256. */
  18257. /**
  18258. * The number of pixels to indent the labels per level in a treegrid
  18259. * axis.
  18260. *
  18261. * @sample gantt/treegrid-axis/demo
  18262. * Indentation 10px by default.
  18263. * @sample gantt/treegrid-axis/indentation-0px
  18264. * Indentation set to 0px.
  18265. *
  18266. * @product gantt
  18267. */
  18268. indentation: 10,
  18269. /**
  18270. * Horizontal axis only. When `staggerLines` is not set,
  18271. * `maxStaggerLines` defines how many lines the axis is allowed to
  18272. * add to automatically avoid overlapping X labels. Set to `1` to
  18273. * disable overlap detection.
  18274. *
  18275. * @deprecated
  18276. * @type {number}
  18277. * @default 5
  18278. * @since 1.3.3
  18279. * @apioption xAxis.labels.maxStaggerLines
  18280. */
  18281. /**
  18282. * How to handle overflowing labels on horizontal axis. If set to
  18283. * `"allow"`, it will not be aligned at all. By default it
  18284. * `"justify"` labels inside the chart area. If there is room to
  18285. * move it, it will be aligned to the edge, else it will be removed.
  18286. *
  18287. * @type {string}
  18288. * @default justify
  18289. * @since 2.2.5
  18290. * @validvalue ["allow", "justify"]
  18291. * @apioption xAxis.labels.overflow
  18292. */
  18293. /**
  18294. * The pixel padding for axis labels, to ensure white space between
  18295. * them.
  18296. *
  18297. * @type {number}
  18298. * @default 5
  18299. * @product highcharts gantt
  18300. * @apioption xAxis.labels.padding
  18301. */
  18302. /**
  18303. * Whether to reserve space for the labels. By default, space is
  18304. * reserved for the labels in these cases:
  18305. *
  18306. * * On all horizontal axes.
  18307. * * On vertical axes if `label.align` is `right` on a left-side
  18308. * axis or `left` on a right-side axis.
  18309. * * On vertical axes if `label.align` is `center`.
  18310. *
  18311. * This can be turned off when for example the labels are rendered
  18312. * inside the plot area instead of outside.
  18313. *
  18314. * @see [labels.align](#xAxis.labels.align)
  18315. *
  18316. * @sample {highcharts} highcharts/xaxis/labels-reservespace/
  18317. * No reserved space, labels inside plot
  18318. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  18319. * Left-aligned labels on a vertical category axis
  18320. *
  18321. * @type {boolean}
  18322. * @since 4.1.10
  18323. * @product highcharts gantt
  18324. * @apioption xAxis.labels.reserveSpace
  18325. */
  18326. /**
  18327. * Rotation of the labels in degrees.
  18328. *
  18329. * @sample {highcharts} highcharts/xaxis/labels-rotation/
  18330. * X axis labels rotated 90°
  18331. *
  18332. * @type {number}
  18333. * @default 0
  18334. * @apioption xAxis.labels.rotation
  18335. */
  18336. /**
  18337. * Horizontal axes only. The number of lines to spread the labels
  18338. * over to make room or tighter labels.
  18339. *
  18340. * @sample {highcharts} highcharts/xaxis/labels-staggerlines/
  18341. * Show labels over two lines
  18342. * @sample {highstock} stock/xaxis/labels-staggerlines/
  18343. * Show labels over two lines
  18344. *
  18345. * @type {number}
  18346. * @since 2.1
  18347. * @apioption xAxis.labels.staggerLines
  18348. */
  18349. /**
  18350. * To show only every _n_'th label on the axis, set the step to _n_.
  18351. * Setting the step to 2 shows every other label.
  18352. *
  18353. * By default, the step is calculated automatically to avoid
  18354. * overlap. To prevent this, set it to 1\. This usually only
  18355. * happens on a category axis, and is often a sign that you have
  18356. * chosen the wrong axis type.
  18357. *
  18358. * Read more at
  18359. * [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
  18360. * => What axis should I use?
  18361. *
  18362. * @sample {highcharts} highcharts/xaxis/labels-step/
  18363. * Showing only every other axis label on a categorized
  18364. * x-axis
  18365. * @sample {highcharts} highcharts/xaxis/labels-step-auto/
  18366. * Auto steps on a category axis
  18367. *
  18368. * @type {number}
  18369. * @since 2.1
  18370. * @apioption xAxis.labels.step
  18371. */
  18372. /**
  18373. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  18374. * to render the labels.
  18375. *
  18376. * @type {boolean}
  18377. * @default false
  18378. * @apioption xAxis.labels.useHTML
  18379. */
  18380. /**
  18381. * The x position offset of all labels relative to the tick
  18382. * positions on the axis.
  18383. *
  18384. * @sample {highcharts} highcharts/xaxis/labels-x/
  18385. * Y axis labels placed on grid lines
  18386. */
  18387. x: 0,
  18388. /**
  18389. * The y position offset of all labels relative to the tick
  18390. * positions on the axis. The default makes it adapt to the font
  18391. * size of the bottom axis.
  18392. *
  18393. * @sample {highcharts} highcharts/xaxis/labels-x/
  18394. * Y axis labels placed on grid lines
  18395. *
  18396. * @type {number}
  18397. * @apioption xAxis.labels.y
  18398. */
  18399. /**
  18400. * The Z index for the axis labels.
  18401. *
  18402. * @type {number}
  18403. * @default 7
  18404. * @apioption xAxis.labels.zIndex
  18405. */
  18406. /**
  18407. * CSS styles for the label. Use `whiteSpace: 'nowrap'` to prevent
  18408. * wrapping of category labels. Use `textOverflow: 'none'` to
  18409. * prevent ellipsis (dots).
  18410. *
  18411. * In styled mode, the labels are styled with the
  18412. * `.highcharts-axis-labels` class.
  18413. *
  18414. * @sample {highcharts} highcharts/xaxis/labels-style/
  18415. * Red X axis labels
  18416. *
  18417. * @type {Highcharts.CSSObject}
  18418. */
  18419. style: {
  18420. /** @internal */
  18421. color: '#666666',
  18422. /** @internal */
  18423. cursor: 'default',
  18424. /** @internal */
  18425. fontSize: '11px'
  18426. }
  18427. },
  18428. /**
  18429. * The left position as the horizontal axis. If it's a number, it is
  18430. * interpreted as pixel position relative to the chart.
  18431. *
  18432. * Since Highcharts v5.0.13: If it's a percentage string, it is
  18433. * interpreted as percentages of the plot width, offset from plot area
  18434. * left.
  18435. *
  18436. * @type {number|string}
  18437. * @product highcharts highstock
  18438. * @apioption xAxis.left
  18439. */
  18440. /**
  18441. * The top position as the vertical axis. If it's a number, it is
  18442. * interpreted as pixel position relative to the chart.
  18443. *
  18444. * Since Highcharts 2: If it's a percentage string, it is interpreted
  18445. * as percentages of the plot height, offset from plot area top.
  18446. *
  18447. * @type {number|string}
  18448. * @product highcharts highstock
  18449. * @apioption xAxis.top
  18450. */
  18451. /**
  18452. * Index of another axis that this axis is linked to. When an axis is
  18453. * linked to a master axis, it will take the same extremes as
  18454. * the master, but as assigned by min or max or by setExtremes.
  18455. * It can be used to show additional info, or to ease reading the
  18456. * chart by duplicating the scales.
  18457. *
  18458. * @sample {highcharts} highcharts/xaxis/linkedto/
  18459. * Different string formats of the same date
  18460. * @sample {highcharts} highcharts/yaxis/linkedto/
  18461. * Y values on both sides
  18462. *
  18463. * @type {number}
  18464. * @since 2.0.2
  18465. * @product highcharts highstock gantt
  18466. * @apioption xAxis.linkedTo
  18467. */
  18468. /**
  18469. * The maximum value of the axis. If `null`, the max value is
  18470. * automatically calculated.
  18471. *
  18472. * If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
  18473. * might be rounded up.
  18474. *
  18475. * If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
  18476. * beyond the set max in order to reach the given number of ticks. The
  18477. * same may happen in a chart with multiple axes, determined by [chart.
  18478. * alignTicks](#chart), where a `tickAmount` is applied internally.
  18479. *
  18480. * @sample {highcharts} highcharts/yaxis/max-200/
  18481. * Y axis max of 200
  18482. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  18483. * Y axis max on logarithmic axis
  18484. * @sample {highstock} stock/xaxis/min-max/
  18485. * Fixed min and max on X axis
  18486. * @sample {highmaps} maps/axis/min-max/
  18487. * Pre-zoomed to a specific area
  18488. *
  18489. * @type {number|null}
  18490. * @apioption xAxis.max
  18491. */
  18492. /**
  18493. * Padding of the max value relative to the length of the axis. A
  18494. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  18495. * when you don't want the highest data value to appear on the edge
  18496. * of the plot area. When the axis' `max` option is set or a max extreme
  18497. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  18498. *
  18499. * @sample {highcharts} highcharts/yaxis/maxpadding/
  18500. * Max padding of 0.25 on y axis
  18501. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  18502. * Greater min- and maxPadding
  18503. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  18504. * Add some padding
  18505. *
  18506. * @default {highcharts} 0.01
  18507. * @default {highstock|highmaps} 0
  18508. * @since 1.2.0
  18509. */
  18510. maxPadding: 0.01,
  18511. /**
  18512. * Deprecated. Use `minRange` instead.
  18513. *
  18514. * @deprecated
  18515. * @type {number}
  18516. * @product highcharts highstock
  18517. * @apioption xAxis.maxZoom
  18518. */
  18519. /**
  18520. * The minimum value of the axis. If `null` the min value is
  18521. * automatically calculated.
  18522. *
  18523. * If the [startOnTick](#yAxis.startOnTick) option is true (default),
  18524. * the `min` value might be rounded down.
  18525. *
  18526. * The automatically calculated minimum value is also affected by
  18527. * [floor](#yAxis.floor), [softMin](#yAxis.softMin),
  18528. * [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
  18529. * as well as [series.threshold](#plotOptions.series.threshold)
  18530. * and [series.softThreshold](#plotOptions.series.softThreshold).
  18531. *
  18532. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  18533. * -50 with startOnTick to false
  18534. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  18535. * -50 with startOnTick true by default
  18536. * @sample {highstock} stock/xaxis/min-max/
  18537. * Set min and max on X axis
  18538. * @sample {highmaps} maps/axis/min-max/
  18539. * Pre-zoomed to a specific area
  18540. *
  18541. * @type {number|null}
  18542. * @apioption xAxis.min
  18543. */
  18544. /**
  18545. * The dash or dot style of the minor grid lines. For possible values,
  18546. * see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  18547. *
  18548. * @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
  18549. * Long dashes on minor grid lines
  18550. * @sample {highstock} stock/xaxis/minorgridlinedashstyle/
  18551. * Long dashes on minor grid lines
  18552. *
  18553. * @type {Highcharts.DashStyleValue}
  18554. * @default Solid
  18555. * @since 1.2
  18556. * @apioption xAxis.minorGridLineDashStyle
  18557. */
  18558. /**
  18559. * Specific tick interval in axis units for the minor ticks. On a linear
  18560. * axis, if `"auto"`, the minor tick interval is calculated as a fifth
  18561. * of the tickInterval. If `null` or `undefined`, minor ticks are not
  18562. * shown.
  18563. *
  18564. * On logarithmic axes, the unit is the power of the value. For example,
  18565. * setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
  18566. * 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
  18567. * between 1 and 10, 10 and 100 etc.
  18568. *
  18569. * If user settings dictate minor ticks to become too dense, they don't
  18570. * make sense, and will be ignored to prevent performance problems.
  18571. *
  18572. * @sample {highcharts} highcharts/yaxis/minortickinterval-null/
  18573. * Null by default
  18574. * @sample {highcharts} highcharts/yaxis/minortickinterval-5/
  18575. * 5 units
  18576. * @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
  18577. * "auto"
  18578. * @sample {highcharts} highcharts/yaxis/minortickinterval-log/
  18579. * 0.1
  18580. * @sample {highstock} stock/demo/basic-line/
  18581. * Null by default
  18582. * @sample {highstock} stock/xaxis/minortickinterval-auto/
  18583. * "auto"
  18584. *
  18585. * @type {number|string|null}
  18586. * @apioption xAxis.minorTickInterval
  18587. */
  18588. /**
  18589. * The pixel length of the minor tick marks.
  18590. *
  18591. * @sample {highcharts} highcharts/yaxis/minorticklength/
  18592. * 10px on Y axis
  18593. * @sample {highstock} stock/xaxis/minorticks/
  18594. * 10px on Y axis
  18595. */
  18596. minorTickLength: 2,
  18597. /**
  18598. * The position of the minor tick marks relative to the axis line.
  18599. * Can be one of `inside` and `outside`.
  18600. *
  18601. * @sample {highcharts} highcharts/yaxis/minortickposition-outside/
  18602. * Outside by default
  18603. * @sample {highcharts} highcharts/yaxis/minortickposition-inside/
  18604. * Inside
  18605. * @sample {highstock} stock/xaxis/minorticks/
  18606. * Inside
  18607. *
  18608. * @validvalue ["inside", "outside"]
  18609. */
  18610. minorTickPosition: 'outside',
  18611. /**
  18612. * Enable or disable minor ticks. Unless
  18613. * [minorTickInterval](#xAxis.minorTickInterval) is set, the tick
  18614. * interval is calculated as a fifth of the `tickInterval`.
  18615. *
  18616. * On a logarithmic axis, minor ticks are laid out based on a best
  18617. * guess, attempting to enter approximately 5 minor ticks between
  18618. * each major tick.
  18619. *
  18620. * Prior to v6.0.0, ticks were unabled in auto layout by setting
  18621. * `minorTickInterval` to `"auto"`.
  18622. *
  18623. * @productdesc {highcharts}
  18624. * On axes using [categories](#xAxis.categories), minor ticks are not
  18625. * supported.
  18626. *
  18627. * @sample {highcharts} highcharts/yaxis/minorticks-true/
  18628. * Enabled on linear Y axis
  18629. *
  18630. * @type {boolean}
  18631. * @default false
  18632. * @since 6.0.0
  18633. * @apioption xAxis.minorTicks
  18634. */
  18635. /**
  18636. * The pixel width of the minor tick mark.
  18637. *
  18638. * @sample {highcharts} highcharts/yaxis/minortickwidth/
  18639. * 3px width
  18640. * @sample {highstock} stock/xaxis/minorticks/
  18641. * 1px width
  18642. *
  18643. * @type {number}
  18644. * @default 0
  18645. * @apioption xAxis.minorTickWidth
  18646. */
  18647. /**
  18648. * Padding of the min value relative to the length of the axis. A
  18649. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  18650. * when you don't want the lowest data value to appear on the edge
  18651. * of the plot area. When the axis' `min` option is set or a min extreme
  18652. * is set using `axis.setExtremes()`, the minPadding will be ignored.
  18653. *
  18654. * @sample {highcharts} highcharts/yaxis/minpadding/
  18655. * Min padding of 0.2
  18656. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  18657. * Greater min- and maxPadding
  18658. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  18659. * Add some padding
  18660. *
  18661. * @default {highcharts} 0.01
  18662. * @default {highstock|highmaps} 0
  18663. * @since 1.2.0
  18664. * @product highcharts highstock gantt
  18665. */
  18666. minPadding: 0.01,
  18667. /**
  18668. * The minimum range to display on this axis. The entire axis will not
  18669. * be allowed to span over a smaller interval than this. For example,
  18670. * for a datetime axis the main unit is milliseconds. If minRange is
  18671. * set to 3600000, you can't zoom in more than to one hour.
  18672. *
  18673. * The default minRange for the x axis is five times the smallest
  18674. * interval between any of the data points.
  18675. *
  18676. * On a logarithmic axis, the unit for the minimum range is the power.
  18677. * So a minRange of 1 means that the axis can be zoomed to 10-100,
  18678. * 100-1000, 1000-10000 etc.
  18679. *
  18680. * **Note**: The `minPadding`, `maxPadding`, `startOnTick` and
  18681. * `endOnTick` settings also affect how the extremes of the axis
  18682. * are computed.
  18683. *
  18684. * @sample {highcharts} highcharts/xaxis/minrange/
  18685. * Minimum range of 5
  18686. * @sample {highstock} stock/xaxis/minrange/
  18687. * Max zoom of 6 months overrides user selections
  18688. * @sample {highmaps} maps/axis/minrange/
  18689. * Minimum range of 1000
  18690. *
  18691. * @type {number}
  18692. * @apioption xAxis.minRange
  18693. */
  18694. /**
  18695. * The minimum tick interval allowed in axis values. For example on
  18696. * zooming in on an axis with daily data, this can be used to prevent
  18697. * the axis from showing hours. Defaults to the closest distance between
  18698. * two points on the axis.
  18699. *
  18700. * @type {number}
  18701. * @since 2.3.0
  18702. * @apioption xAxis.minTickInterval
  18703. */
  18704. /**
  18705. * The distance in pixels from the plot area to the axis line.
  18706. * A positive offset moves the axis with it's line, labels and ticks
  18707. * away from the plot area. This is typically used when two or more
  18708. * axes are displayed on the same side of the plot. With multiple
  18709. * axes the offset is dynamically adjusted to avoid collision, this
  18710. * can be overridden by setting offset explicitly.
  18711. *
  18712. * @sample {highcharts} highcharts/yaxis/offset/
  18713. * Y axis offset of 70
  18714. * @sample {highcharts} highcharts/yaxis/offset-centered/
  18715. * Axes positioned in the center of the plot
  18716. * @sample {highstock} stock/xaxis/offset/
  18717. * Y axis offset by 70 px
  18718. *
  18719. * @type {number}
  18720. * @default 0
  18721. * @apioption xAxis.offset
  18722. */
  18723. /**
  18724. * Whether to display the axis on the opposite side of the normal. The
  18725. * normal is on the left side for vertical axes and bottom for
  18726. * horizontal, so the opposite sides will be right and top respectively.
  18727. * This is typically used with dual or multiple axes.
  18728. *
  18729. * @sample {highcharts} highcharts/yaxis/opposite/
  18730. * Secondary Y axis opposite
  18731. * @sample {highstock} stock/xaxis/opposite/
  18732. * Y axis on left side
  18733. *
  18734. * @type {boolean}
  18735. * @default false
  18736. * @apioption xAxis.opposite
  18737. */
  18738. /**
  18739. * In an ordinal axis, the points are equally spaced in the chart
  18740. * regardless of the actual time or x distance between them. This means
  18741. * that missing data periods (e.g. nights or weekends for a stock chart)
  18742. * will not take up space in the chart.
  18743. * Having `ordinal: false` will show any gaps created by the `gapSize`
  18744. * setting proportionate to their duration.
  18745. *
  18746. * In stock charts the X axis is ordinal by default, unless
  18747. * the boost module is used and at least one of the series' data length
  18748. * exceeds the [boostThreshold](#series.line.boostThreshold).
  18749. *
  18750. * @sample {highstock} stock/xaxis/ordinal-true/
  18751. * True by default
  18752. * @sample {highstock} stock/xaxis/ordinal-false/
  18753. * False
  18754. *
  18755. * @type {boolean}
  18756. * @default true
  18757. * @since 1.1
  18758. * @product highstock
  18759. * @apioption xAxis.ordinal
  18760. */
  18761. /**
  18762. * Additional range on the right side of the xAxis. Works similar to
  18763. * `xAxis.maxPadding`, but value is set in milliseconds. Can be set for
  18764. * both main `xAxis` and the navigator's `xAxis`.
  18765. *
  18766. * @sample {highstock} stock/xaxis/overscroll/
  18767. * One minute overscroll with live data
  18768. *
  18769. * @type {number}
  18770. * @default 0
  18771. * @since 6.0.0
  18772. * @product highstock
  18773. * @apioption xAxis.overscroll
  18774. */
  18775. /**
  18776. * Refers to the index in the [panes](#panes) array. Used for circular
  18777. * gauges and polar charts. When the option is not set then first pane
  18778. * will be used.
  18779. *
  18780. * @sample highcharts/demo/gauge-vu-meter
  18781. * Two gauges with different center
  18782. *
  18783. * @type {number}
  18784. * @product highcharts
  18785. * @apioption xAxis.pane
  18786. */
  18787. /**
  18788. * The zoomed range to display when only defining one or none of `min`
  18789. * or `max`. For example, to show the latest month, a range of one month
  18790. * can be set.
  18791. *
  18792. * @sample {highstock} stock/xaxis/range/
  18793. * Setting a zoomed range when the rangeSelector is disabled
  18794. *
  18795. * @type {number}
  18796. * @product highstock
  18797. * @apioption xAxis.range
  18798. */
  18799. /**
  18800. * Whether to reverse the axis so that the highest number is closest
  18801. * to the origin. If the chart is inverted, the x axis is reversed by
  18802. * default.
  18803. *
  18804. * @sample {highcharts} highcharts/yaxis/reversed/
  18805. * Reversed Y axis
  18806. * @sample {highstock} stock/xaxis/reversed/
  18807. * Reversed Y axis
  18808. *
  18809. * @type {boolean}
  18810. * @default false
  18811. * @apioption xAxis.reversed
  18812. */
  18813. // reversed: false,
  18814. /**
  18815. * This option determines how stacks should be ordered within a group.
  18816. * For example reversed xAxis also reverses stacks, so first series
  18817. * comes last in a group. To keep order like for non-reversed xAxis
  18818. * enable this option.
  18819. *
  18820. * @sample {highcharts} highcharts/xaxis/reversedstacks/
  18821. * Reversed stacks comparison
  18822. * @sample {highstock} highcharts/xaxis/reversedstacks/
  18823. * Reversed stacks comparison
  18824. *
  18825. * @type {boolean}
  18826. * @default false
  18827. * @since 6.1.1
  18828. * @product highcharts highstock
  18829. * @apioption xAxis.reversedStacks
  18830. */
  18831. /**
  18832. * An optional scrollbar to display on the X axis in response to
  18833. * limiting the minimum and maximum of the axis values.
  18834. *
  18835. * In styled mode, all the presentational options for the scrollbar are
  18836. * replaced by the classes `.highcharts-scrollbar-thumb`,
  18837. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  18838. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  18839. *
  18840. * @sample {highstock} stock/yaxis/heatmap-scrollbars/
  18841. * Heatmap with both scrollbars
  18842. *
  18843. * @extends scrollbar
  18844. * @since 4.2.6
  18845. * @product highstock
  18846. * @apioption xAxis.scrollbar
  18847. */
  18848. /**
  18849. * Whether to show the axis line and title when the axis has no data.
  18850. *
  18851. * @sample {highcharts} highcharts/yaxis/showempty/
  18852. * When clicking the legend to hide series, one axis preserves
  18853. * line and title, the other doesn't
  18854. * @sample {highstock} highcharts/yaxis/showempty/
  18855. * When clicking the legend to hide series, one axis preserves
  18856. * line and title, the other doesn't
  18857. *
  18858. * @since 1.1
  18859. */
  18860. showEmpty: true,
  18861. /**
  18862. * Whether to show the first tick label.
  18863. *
  18864. * @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
  18865. * Set to false on X axis
  18866. * @sample {highstock} stock/xaxis/showfirstlabel/
  18867. * Labels below plot lines on Y axis
  18868. *
  18869. * @type {boolean}
  18870. * @default true
  18871. * @apioption xAxis.showFirstLabel
  18872. */
  18873. /**
  18874. * Whether to show the last tick label. Defaults to `true` on cartesian
  18875. * charts, and `false` on polar charts.
  18876. *
  18877. * @sample {highcharts} highcharts/xaxis/showlastlabel-true/
  18878. * Set to true on X axis
  18879. * @sample {highstock} stock/xaxis/showfirstlabel/
  18880. * Labels below plot lines on Y axis
  18881. *
  18882. * @type {boolean}
  18883. * @default true
  18884. * @product highcharts highstock gantt
  18885. * @apioption xAxis.showLastLabel
  18886. */
  18887. /**
  18888. * A soft maximum for the axis. If the series data maximum is less than
  18889. * this, the axis will stay at this maximum, but if the series data
  18890. * maximum is higher, the axis will flex to show all data.
  18891. *
  18892. * @sample highcharts/yaxis/softmin-softmax/
  18893. * Soft min and max
  18894. *
  18895. * @type {number}
  18896. * @since 5.0.1
  18897. * @product highcharts highstock gantt
  18898. * @apioption xAxis.softMax
  18899. */
  18900. /**
  18901. * A soft minimum for the axis. If the series data minimum is greater
  18902. * than this, the axis will stay at this minimum, but if the series
  18903. * data minimum is lower, the axis will flex to show all data.
  18904. *
  18905. * @sample highcharts/yaxis/softmin-softmax/
  18906. * Soft min and max
  18907. *
  18908. * @type {number}
  18909. * @since 5.0.1
  18910. * @product highcharts highstock gantt
  18911. * @apioption xAxis.softMin
  18912. */
  18913. /**
  18914. * For datetime axes, this decides where to put the tick between weeks.
  18915. * 0 = Sunday, 1 = Monday.
  18916. *
  18917. * @sample {highcharts} highcharts/xaxis/startofweek-monday/
  18918. * Monday by default
  18919. * @sample {highcharts} highcharts/xaxis/startofweek-sunday/
  18920. * Sunday
  18921. * @sample {highstock} stock/xaxis/startofweek-1
  18922. * Monday by default
  18923. * @sample {highstock} stock/xaxis/startofweek-0
  18924. * Sunday
  18925. *
  18926. * @product highcharts highstock gantt
  18927. */
  18928. startOfWeek: 1,
  18929. /**
  18930. * Whether to force the axis to start on a tick. Use this option with
  18931. * the `minPadding` option to control the axis start.
  18932. *
  18933. * @productdesc {highstock}
  18934. * In Highstock, `startOnTick` is always `false` when the navigator
  18935. * is enabled, to prevent jumpy scrolling.
  18936. *
  18937. * @sample {highcharts} highcharts/xaxis/startontick-false/
  18938. * False by default
  18939. * @sample {highcharts} highcharts/xaxis/startontick-true/
  18940. * True
  18941. *
  18942. * @since 1.2.0
  18943. */
  18944. startOnTick: false,
  18945. /**
  18946. * The amount of ticks to draw on the axis. This opens up for aligning
  18947. * the ticks of multiple charts or panes within a chart. This option
  18948. * overrides the `tickPixelInterval` option.
  18949. *
  18950. * This option only has an effect on linear axes. Datetime, logarithmic
  18951. * or category axes are not affected.
  18952. *
  18953. * @sample {highcharts} highcharts/yaxis/tickamount/
  18954. * 8 ticks on Y axis
  18955. * @sample {highstock} highcharts/yaxis/tickamount/
  18956. * 8 ticks on Y axis
  18957. *
  18958. * @type {number}
  18959. * @since 4.1.0
  18960. * @product highcharts highstock gantt
  18961. * @apioption xAxis.tickAmount
  18962. */
  18963. /**
  18964. * The interval of the tick marks in axis units. When `undefined`, the
  18965. * tick interval is computed to approximately follow the
  18966. * [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
  18967. * axes. On categorized axes, a `undefined` tickInterval will default to
  18968. * 1, one category. Note that datetime axes are based on milliseconds,
  18969. * so for example an interval of one day is expressed as
  18970. * `24 * 3600 * 1000`.
  18971. *
  18972. * On logarithmic axes, the tickInterval is based on powers, so a
  18973. * tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
  18974. * tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
  18975. * of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
  18976. * 40 etc.
  18977. *
  18978. *
  18979. * If the tickInterval is too dense for labels to be drawn, Highcharts
  18980. * may remove ticks.
  18981. *
  18982. * If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
  18983. * option may interfere with the `tickInterval` setting.
  18984. *
  18985. * @see [tickPixelInterval](#xAxis.tickPixelInterval)
  18986. * @see [tickPositions](#xAxis.tickPositions)
  18987. * @see [tickPositioner](#xAxis.tickPositioner)
  18988. *
  18989. * @sample {highcharts} highcharts/xaxis/tickinterval-5/
  18990. * Tick interval of 5 on a linear axis
  18991. * @sample {highstock} stock/xaxis/tickinterval/
  18992. * Tick interval of 0.01 on Y axis
  18993. *
  18994. * @type {number}
  18995. * @apioption xAxis.tickInterval
  18996. */
  18997. /**
  18998. * The pixel length of the main tick marks.
  18999. *
  19000. * @sample {highcharts} highcharts/xaxis/ticklength/
  19001. * 20 px tick length on the X axis
  19002. * @sample {highstock} stock/xaxis/ticks/
  19003. * Formatted ticks on X axis
  19004. */
  19005. tickLength: 10,
  19006. /**
  19007. * If tickInterval is `null` this option sets the approximate pixel
  19008. * interval of the tick marks. Not applicable to categorized axis.
  19009. *
  19010. * The tick interval is also influenced by the [minTickInterval](
  19011. * #xAxis.minTickInterval) option, that, by default prevents ticks from
  19012. * being denser than the data points.
  19013. *
  19014. * @see [tickInterval](#xAxis.tickInterval)
  19015. * @see [tickPositioner](#xAxis.tickPositioner)
  19016. * @see [tickPositions](#xAxis.tickPositions)
  19017. *
  19018. * @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
  19019. * 50 px on X axis
  19020. * @sample {highstock} stock/xaxis/tickpixelinterval/
  19021. * 200 px on X axis
  19022. */
  19023. tickPixelInterval: 100,
  19024. /**
  19025. * For categorized axes only. If `on` the tick mark is placed in the
  19026. * center of the category, if `between` the tick mark is placed between
  19027. * categories. The default is `between` if the `tickInterval` is 1, else
  19028. * `on`.
  19029. *
  19030. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
  19031. * "between" by default
  19032. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
  19033. * "on"
  19034. *
  19035. * @product highcharts gantt
  19036. * @validvalue ["on", "between"]
  19037. */
  19038. tickmarkPlacement: 'between',
  19039. /**
  19040. * The position of the major tick marks relative to the axis line.
  19041. * Can be one of `inside` and `outside`.
  19042. *
  19043. * @sample {highcharts} highcharts/xaxis/tickposition-outside/
  19044. * "outside" by default
  19045. * @sample {highcharts} highcharts/xaxis/tickposition-inside/
  19046. * "inside"
  19047. * @sample {highstock} stock/xaxis/ticks/
  19048. * Formatted ticks on X axis
  19049. *
  19050. * @validvalue ["inside", "outside"]
  19051. */
  19052. tickPosition: 'outside',
  19053. /**
  19054. * A callback function returning array defining where the ticks are
  19055. * laid out on the axis. This overrides the default behaviour of
  19056. * [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
  19057. * #xAxis.tickInterval). The automatic tick positions are accessible
  19058. * through `this.tickPositions` and can be modified by the callback.
  19059. *
  19060. * @see [tickPositions](#xAxis.tickPositions)
  19061. *
  19062. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  19063. * Demo of tickPositions and tickPositioner
  19064. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  19065. * Demo of tickPositions and tickPositioner
  19066. *
  19067. * @type {Highcharts.AxisTickPositionerCallbackFunction}
  19068. * @apioption xAxis.tickPositioner
  19069. */
  19070. /**
  19071. * An array defining where the ticks are laid out on the axis. This
  19072. * overrides the default behaviour of [tickPixelInterval](
  19073. * #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
  19074. *
  19075. * @see [tickPositioner](#xAxis.tickPositioner)
  19076. *
  19077. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  19078. * Demo of tickPositions and tickPositioner
  19079. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  19080. * Demo of tickPositions and tickPositioner
  19081. *
  19082. * @type {Array<number>}
  19083. * @apioption xAxis.tickPositions
  19084. */
  19085. /**
  19086. * The pixel width of the major tick marks. Defaults to 0 on category
  19087. * axes, otherwise 1.
  19088. *
  19089. * In styled mode, the stroke width is given in the `.highcharts-tick`
  19090. * class, but in order for the element to be generated on category axes,
  19091. * the option must be explicitly set to 1.
  19092. *
  19093. * @sample {highcharts} highcharts/xaxis/tickwidth/
  19094. * 10 px width
  19095. * @sample {highcharts} highcharts/css/axis-grid/
  19096. * Styled mode
  19097. * @sample {highstock} stock/xaxis/ticks/
  19098. * Formatted ticks on X axis
  19099. * @sample {highstock} highcharts/css/axis-grid/
  19100. * Styled mode
  19101. *
  19102. * @type {undefined|number}
  19103. * @default {highstock} 1
  19104. * @default {highmaps} 0
  19105. * @apioption xAxis.tickWidth
  19106. */
  19107. /**
  19108. * The axis title, showing next to the axis line.
  19109. *
  19110. * @productdesc {highmaps}
  19111. * In Highmaps, the axis is hidden by default, but adding an axis title
  19112. * is still possible. X axis and Y axis titles will appear at the bottom
  19113. * and left by default.
  19114. */
  19115. title: {
  19116. /**
  19117. * Deprecated. Set the `text` to `null` to disable the title.
  19118. *
  19119. * @deprecated
  19120. * @type {boolean}
  19121. * @product highcharts
  19122. * @apioption xAxis.title.enabled
  19123. */
  19124. /**
  19125. * The pixel distance between the axis labels or line and the title.
  19126. * Defaults to 0 for horizontal axes, 10 for vertical
  19127. *
  19128. * @sample {highcharts} highcharts/xaxis/title-margin/
  19129. * Y axis title margin of 60
  19130. *
  19131. * @type {number}
  19132. * @apioption xAxis.title.margin
  19133. */
  19134. /**
  19135. * The distance of the axis title from the axis line. By default,
  19136. * this distance is computed from the offset width of the labels,
  19137. * the labels' distance from the axis and the title's margin.
  19138. * However when the offset option is set, it overrides all this.
  19139. *
  19140. * @sample {highcharts} highcharts/yaxis/title-offset/
  19141. * Place the axis title on top of the axis
  19142. * @sample {highstock} highcharts/yaxis/title-offset/
  19143. * Place the axis title on top of the Y axis
  19144. *
  19145. * @type {number}
  19146. * @since 2.2.0
  19147. * @apioption xAxis.title.offset
  19148. */
  19149. /**
  19150. * Whether to reserve space for the title when laying out the axis.
  19151. *
  19152. * @type {boolean}
  19153. * @default true
  19154. * @since 5.0.11
  19155. * @product highcharts highstock gantt
  19156. * @apioption xAxis.title.reserveSpace
  19157. */
  19158. /**
  19159. * The rotation of the text in degrees. 0 is horizontal, 270 is
  19160. * vertical reading from bottom to top.
  19161. *
  19162. * @sample {highcharts} highcharts/yaxis/title-offset/
  19163. * Horizontal
  19164. *
  19165. * @type {number}
  19166. * @default 0
  19167. * @apioption xAxis.title.rotation
  19168. */
  19169. /**
  19170. * The actual text of the axis title. It can contain basic HTML tags
  19171. * like `b`, `i` and `span` with style.
  19172. *
  19173. * @sample {highcharts} highcharts/xaxis/title-text/
  19174. * Custom HTML
  19175. * @sample {highstock} stock/xaxis/title-text/
  19176. * Titles for both axes
  19177. *
  19178. * @type {string|null}
  19179. * @apioption xAxis.title.text
  19180. */
  19181. /**
  19182. * Alignment of the text, can be `"left"`, `"right"` or `"center"`.
  19183. * Default alignment depends on the
  19184. * [title.align](xAxis.title.align):
  19185. *
  19186. * Horizontal axes:
  19187. * - for `align` = `"low"`, `textAlign` is set to `left`
  19188. * - for `align` = `"middle"`, `textAlign` is set to `center`
  19189. * - for `align` = `"high"`, `textAlign` is set to `right`
  19190. *
  19191. * Vertical axes:
  19192. * - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
  19193. * set to `right`
  19194. * - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
  19195. * set to `left`
  19196. * - for `align` = `"middle"`, `textAlign` is set to `center`
  19197. * - for `align` = `"high"` and `opposite` = `true` `textAlign` is
  19198. * set to `left`
  19199. * - for `align` = `"high"` and `opposite` = `false` `textAlign` is
  19200. * set to `right`
  19201. *
  19202. * @type {Highcharts.AlignValue}
  19203. * @apioption xAxis.title.textAlign
  19204. */
  19205. /**
  19206. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  19207. * to render the axis title.
  19208. *
  19209. * @type {boolean}
  19210. * @default false
  19211. * @product highcharts highstock gantt
  19212. * @apioption xAxis.title.useHTML
  19213. */
  19214. /**
  19215. * Horizontal pixel offset of the title position.
  19216. *
  19217. * @type {number}
  19218. * @default 0
  19219. * @since 4.1.6
  19220. * @product highcharts highstock gantt
  19221. * @apioption xAxis.title.x
  19222. */
  19223. /**
  19224. * Vertical pixel offset of the title position.
  19225. *
  19226. * @type {number}
  19227. * @product highcharts highstock gantt
  19228. * @apioption xAxis.title.y
  19229. */
  19230. /**
  19231. * Alignment of the title relative to the axis values. Possible
  19232. * values are "low", "middle" or "high".
  19233. *
  19234. * @sample {highcharts} highcharts/xaxis/title-align-low/
  19235. * "low"
  19236. * @sample {highcharts} highcharts/xaxis/title-align-center/
  19237. * "middle" by default
  19238. * @sample {highcharts} highcharts/xaxis/title-align-high/
  19239. * "high"
  19240. * @sample {highcharts} highcharts/yaxis/title-offset/
  19241. * Place the Y axis title on top of the axis
  19242. * @sample {highstock} stock/xaxis/title-align/
  19243. * Aligned to "high" value
  19244. *
  19245. * @type {Highcharts.AxisTitleAlignValue}
  19246. */
  19247. align: 'middle',
  19248. /**
  19249. * CSS styles for the title. If the title text is longer than the
  19250. * axis length, it will wrap to multiple lines by default. This can
  19251. * be customized by setting `textOverflow: 'ellipsis'`, by
  19252. * setting a specific `width` or by setting `whiteSpace: 'nowrap'`.
  19253. *
  19254. * In styled mode, the stroke width is given in the
  19255. * `.highcharts-axis-title` class.
  19256. *
  19257. * @sample {highcharts} highcharts/xaxis/title-style/
  19258. * Red
  19259. * @sample {highcharts} highcharts/css/axis/
  19260. * Styled mode
  19261. *
  19262. * @type {Highcharts.CSSObject}
  19263. */
  19264. style: {
  19265. /** @internal */
  19266. color: '#666666'
  19267. }
  19268. },
  19269. /**
  19270. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
  19271. * or `category`. In a datetime axis, the numbers are given in
  19272. * milliseconds, and tick marks are placed on appropriate values like
  19273. * full hours or days. In a category axis, the
  19274. * [point names](#series.line.data.name) of the chart's series are used
  19275. * for categories, if not a [categories](#xAxis.categories) array is
  19276. * defined.
  19277. *
  19278. * @sample {highcharts} highcharts/xaxis/type-linear/
  19279. * Linear
  19280. * @sample {highcharts} highcharts/yaxis/type-log/
  19281. * Logarithmic
  19282. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  19283. * Logarithmic with minor grid lines
  19284. * @sample {highcharts} highcharts/xaxis/type-log-both/
  19285. * Logarithmic on two axes
  19286. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  19287. * Logarithmic with extension to emulate negative values
  19288. *
  19289. * @type {Highcharts.AxisTypeValue}
  19290. * @product highcharts gantt
  19291. */
  19292. type: 'linear',
  19293. /**
  19294. * If there are multiple axes on the same side of the chart, the pixel
  19295. * margin between the axes. Defaults to 0 on vertical axes, 15 on
  19296. * horizontal axes.
  19297. *
  19298. * @type {number}
  19299. * @since 7.0.3
  19300. * @apioption xAxis.margin
  19301. */
  19302. /**
  19303. * Applies only when the axis `type` is `category`. When `uniqueNames`
  19304. * is true, points are placed on the X axis according to their names.
  19305. * If the same point name is repeated in the same or another series,
  19306. * the point is placed on the same X position as other points of the
  19307. * same name. When `uniqueNames` is false, the points are laid out in
  19308. * increasing X positions regardless of their names, and the X axis
  19309. * category will take the name of the last point in each position.
  19310. *
  19311. * @sample {highcharts} highcharts/xaxis/uniquenames-true/
  19312. * True by default
  19313. * @sample {highcharts} highcharts/xaxis/uniquenames-false/
  19314. * False
  19315. *
  19316. * @type {boolean}
  19317. * @default true
  19318. * @since 4.2.7
  19319. * @product highcharts gantt
  19320. * @apioption xAxis.uniqueNames
  19321. */
  19322. /**
  19323. * Datetime axis only. An array determining what time intervals the
  19324. * ticks are allowed to fall on. Each array item is an array where the
  19325. * first value is the time unit and the second value another array of
  19326. * allowed multiples.
  19327. *
  19328. * Defaults to:
  19329. * ```js
  19330. * units: [[
  19331. * 'millisecond', // unit name
  19332. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  19333. * ], [
  19334. * 'second',
  19335. * [1, 2, 5, 10, 15, 30]
  19336. * ], [
  19337. * 'minute',
  19338. * [1, 2, 5, 10, 15, 30]
  19339. * ], [
  19340. * 'hour',
  19341. * [1, 2, 3, 4, 6, 8, 12]
  19342. * ], [
  19343. * 'day',
  19344. * [1]
  19345. * ], [
  19346. * 'week',
  19347. * [1]
  19348. * ], [
  19349. * 'month',
  19350. * [1, 3, 6]
  19351. * ], [
  19352. * 'year',
  19353. * null
  19354. * ]]
  19355. * ```
  19356. *
  19357. * @type {Array<Array<string,(Array<number>|null)>>}
  19358. * @product highcharts highstock gantt
  19359. * @apioption xAxis.units
  19360. */
  19361. /**
  19362. * Whether axis, including axis title, line, ticks and labels, should
  19363. * be visible.
  19364. *
  19365. * @type {boolean}
  19366. * @default true
  19367. * @since 4.1.9
  19368. * @product highcharts highstock gantt
  19369. * @apioption xAxis.visible
  19370. */
  19371. /**
  19372. * Color of the minor, secondary grid lines.
  19373. *
  19374. * In styled mode, the stroke width is given in the
  19375. * `.highcharts-minor-grid-line` class.
  19376. *
  19377. * @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
  19378. * Bright grey lines from Y axis
  19379. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  19380. * Styled mode
  19381. * @sample {highstock} stock/xaxis/minorgridlinecolor/
  19382. * Bright grey lines from Y axis
  19383. *
  19384. * @type {Highcharts.ColorType}
  19385. * @default #f2f2f2
  19386. */
  19387. minorGridLineColor: '#f2f2f2',
  19388. /**
  19389. * Width of the minor, secondary grid lines.
  19390. *
  19391. * In styled mode, the stroke width is given in the
  19392. * `.highcharts-grid-line` class.
  19393. *
  19394. * @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
  19395. * 2px lines from Y axis
  19396. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  19397. * Styled mode
  19398. * @sample {highstock} stock/xaxis/minorgridlinewidth/
  19399. * 2px lines from Y axis
  19400. */
  19401. minorGridLineWidth: 1,
  19402. /**
  19403. * Color for the minor tick marks.
  19404. *
  19405. * @sample {highcharts} highcharts/yaxis/minortickcolor/
  19406. * Black tick marks on Y axis
  19407. * @sample {highstock} stock/xaxis/minorticks/
  19408. * Black tick marks on Y axis
  19409. *
  19410. * @type {Highcharts.ColorType}
  19411. * @default #999999
  19412. */
  19413. minorTickColor: '#999999',
  19414. /**
  19415. * The color of the line marking the axis itself.
  19416. *
  19417. * In styled mode, the line stroke is given in the
  19418. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  19419. *
  19420. * @productdesc {highmaps}
  19421. * In Highmaps, the axis line is hidden by default, because the axis is
  19422. * not visible by default.
  19423. *
  19424. * @sample {highcharts} highcharts/yaxis/linecolor/
  19425. * A red line on Y axis
  19426. * @sample {highcharts|highstock} highcharts/css/axis/
  19427. * Axes in styled mode
  19428. * @sample {highstock} stock/xaxis/linecolor/
  19429. * A red line on X axis
  19430. *
  19431. * @type {Highcharts.ColorType}
  19432. * @default #ccd6eb
  19433. */
  19434. lineColor: '#ccd6eb',
  19435. /**
  19436. * The width of the line marking the axis itself.
  19437. *
  19438. * In styled mode, the stroke width is given in the
  19439. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  19440. *
  19441. * @sample {highcharts} highcharts/yaxis/linecolor/
  19442. * A 1px line on Y axis
  19443. * @sample {highcharts|highstock} highcharts/css/axis/
  19444. * Axes in styled mode
  19445. * @sample {highstock} stock/xaxis/linewidth/
  19446. * A 2px line on X axis
  19447. *
  19448. * @default {highcharts|highstock} 1
  19449. * @default {highmaps} 0
  19450. */
  19451. lineWidth: 1,
  19452. /**
  19453. * Color of the grid lines extending the ticks across the plot area.
  19454. *
  19455. * In styled mode, the stroke is given in the `.highcharts-grid-line`
  19456. * class.
  19457. *
  19458. * @productdesc {highmaps}
  19459. * In Highmaps, the grid lines are hidden by default.
  19460. *
  19461. * @sample {highcharts} highcharts/yaxis/gridlinecolor/
  19462. * Green lines
  19463. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  19464. * Styled mode
  19465. * @sample {highstock} stock/xaxis/gridlinecolor/
  19466. * Green lines
  19467. *
  19468. * @type {Highcharts.ColorType}
  19469. * @default #e6e6e6
  19470. */
  19471. gridLineColor: '#e6e6e6',
  19472. // gridLineDashStyle: 'solid',
  19473. /**
  19474. * The width of the grid lines extending the ticks across the plot area.
  19475. *
  19476. * In styled mode, the stroke width is given in the
  19477. * `.highcharts-grid-line` class.
  19478. *
  19479. * @sample {highcharts} highcharts/yaxis/gridlinewidth/
  19480. * 2px lines
  19481. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  19482. * Styled mode
  19483. * @sample {highstock} stock/xaxis/gridlinewidth/
  19484. * 2px lines
  19485. *
  19486. * @type {number}
  19487. * @default 0
  19488. * @apioption xAxis.gridLineWidth
  19489. */
  19490. // gridLineWidth: 0,
  19491. /**
  19492. * The height as the vertical axis. If it's a number, it is
  19493. * interpreted as pixels.
  19494. *
  19495. * Since Highcharts 2: If it's a percentage string, it is interpreted
  19496. * as percentages of the total plot height.
  19497. *
  19498. * @type {number|string}
  19499. * @product highcharts highstock
  19500. * @apioption xAxis.height
  19501. */
  19502. /**
  19503. * The width as the horizontal axis. If it's a number, it is interpreted
  19504. * as pixels.
  19505. *
  19506. * Since Highcharts v5.0.13: If it's a percentage string, it is
  19507. * interpreted as percentages of the total plot width.
  19508. *
  19509. * @type {number|string}
  19510. * @product highcharts highstock
  19511. * @apioption xAxis.width
  19512. */
  19513. /**
  19514. * Color for the main tick marks.
  19515. *
  19516. * In styled mode, the stroke is given in the `.highcharts-tick`
  19517. * class.
  19518. *
  19519. * @sample {highcharts} highcharts/xaxis/tickcolor/
  19520. * Red ticks on X axis
  19521. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  19522. * Styled mode
  19523. * @sample {highstock} stock/xaxis/ticks/
  19524. * Formatted ticks on X axis
  19525. *
  19526. * @type {Highcharts.ColorType}
  19527. * @default #ccd6eb
  19528. */
  19529. tickColor: '#ccd6eb'
  19530. // tickWidth: 1
  19531. };
  19532. /**
  19533. * The Y axis or value axis. Normally this is the vertical axis,
  19534. * though if the chart is inverted this is the horizontal axis.
  19535. * In case of multiple axes, the yAxis node is an array of
  19536. * configuration objects.
  19537. *
  19538. * See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
  19539. * access to the axis.
  19540. *
  19541. * @type {*|Array<*>}
  19542. * @extends xAxis
  19543. * @excluding currentDateIndicator,ordinal,overscroll
  19544. * @optionparent yAxis
  19545. *
  19546. * @private
  19547. */
  19548. Axis.defaultYAxisOptions = {
  19549. /**
  19550. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
  19551. * `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
  19552. * `linear` for other chart types.
  19553. *
  19554. * In a datetime axis, the numbers are given in milliseconds, and tick
  19555. * marks are placed on appropriate values, like full hours or days. In a
  19556. * category or treegrid axis, the [point names](#series.line.data.name)
  19557. * of the chart's series are used for categories, if a
  19558. * [categories](#xAxis.categories) array is not defined.
  19559. *
  19560. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  19561. * Logarithmic with minor grid lines
  19562. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  19563. * Logarithmic with extension to emulate negative values
  19564. * @sample {gantt} gantt/treegrid-axis/demo
  19565. * Treegrid axis
  19566. *
  19567. * @type {Highcharts.AxisTypeValue}
  19568. * @default {highcharts} linear
  19569. * @default {gantt} treegrid
  19570. * @product highcharts gantt
  19571. * @apioption yAxis.type
  19572. */
  19573. /**
  19574. * The height of the Y axis. If it's a number, it is interpreted as
  19575. * pixels.
  19576. *
  19577. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  19578. * percentages of the total plot height.
  19579. *
  19580. * @see [yAxis.top](#yAxis.top)
  19581. *
  19582. * @sample {highstock} stock/demo/candlestick-and-volume/
  19583. * Percentage height panes
  19584. *
  19585. * @type {number|string}
  19586. * @product highcharts highstock
  19587. * @apioption yAxis.height
  19588. */
  19589. /**
  19590. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  19591. * to represent the maximum value of the Y axis.
  19592. *
  19593. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  19594. * Min and max colors
  19595. *
  19596. * @type {Highcharts.ColorType}
  19597. * @default #003399
  19598. * @since 4.0
  19599. * @product highcharts
  19600. * @apioption yAxis.maxColor
  19601. */
  19602. /**
  19603. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  19604. * to represent the minimum value of the Y axis.
  19605. *
  19606. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  19607. * Min and max color
  19608. *
  19609. * @type {Highcharts.ColorType}
  19610. * @default #e6ebf5
  19611. * @since 4.0
  19612. * @product highcharts
  19613. * @apioption yAxis.minColor
  19614. */
  19615. /**
  19616. * Whether to reverse the axis so that the highest number is closest
  19617. * to the origin.
  19618. *
  19619. * @sample {highcharts} highcharts/yaxis/reversed/
  19620. * Reversed Y axis
  19621. * @sample {highstock} stock/xaxis/reversed/
  19622. * Reversed Y axis
  19623. *
  19624. * @type {boolean}
  19625. * @default {highcharts} false
  19626. * @default {highstock} false
  19627. * @default {highmaps} true
  19628. * @default {gantt} true
  19629. * @apioption yAxis.reversed
  19630. */
  19631. /**
  19632. * If `true`, the first series in a stack will be drawn on top in a
  19633. * positive, non-reversed Y axis. If `false`, the first series is in
  19634. * the base of the stack.
  19635. *
  19636. * @sample {highcharts} highcharts/yaxis/reversedstacks-false/
  19637. * Non-reversed stacks
  19638. * @sample {highstock} highcharts/yaxis/reversedstacks-false/
  19639. * Non-reversed stacks
  19640. *
  19641. * @type {boolean}
  19642. * @default true
  19643. * @since 3.0.10
  19644. * @product highcharts highstock
  19645. * @apioption yAxis.reversedStacks
  19646. */
  19647. /**
  19648. * Solid gauge series only. Color stops for the solid gauge. Use this
  19649. * in cases where a linear gradient between a `minColor` and `maxColor`
  19650. * is not sufficient. The stops is an array of tuples, where the first
  19651. * item is a float between 0 and 1 assigning the relative position in
  19652. * the gradient, and the second item is the color.
  19653. *
  19654. * For solid gauges, the Y axis also inherits the concept of
  19655. * [data classes](https://api.highcharts.com/highmaps#colorAxis.dataClasses)
  19656. * from the Highmaps color axis.
  19657. *
  19658. * @see [minColor](#yAxis.minColor)
  19659. * @see [maxColor](#yAxis.maxColor)
  19660. *
  19661. * @sample {highcharts} highcharts/demo/gauge-solid/
  19662. * True by default
  19663. *
  19664. * @type {Array<Array<number,Highcharts.ColorType>>}
  19665. * @since 4.0
  19666. * @product highcharts
  19667. * @apioption yAxis.stops
  19668. */
  19669. /**
  19670. * The pixel width of the major tick marks.
  19671. *
  19672. * @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
  19673. * @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
  19674. *
  19675. * @type {number}
  19676. * @default 0
  19677. * @product highcharts highstock gantt
  19678. * @apioption yAxis.tickWidth
  19679. */
  19680. /**
  19681. * Whether to force the axis to end on a tick. Use this option with
  19682. * the `maxPadding` option to control the axis end.
  19683. *
  19684. * This option is always disabled, when panning type is
  19685. * either `y` or `xy`.
  19686. *
  19687. * @see [type](#chart.panning.type)
  19688. *
  19689. *
  19690. * @sample {highcharts} highcharts/chart/reflow-true/
  19691. * True by default
  19692. * @sample {highcharts} highcharts/yaxis/endontick/
  19693. * False
  19694. * @sample {highstock} stock/demo/basic-line/
  19695. * True by default
  19696. * @sample {highstock} stock/xaxis/endontick/
  19697. * False for Y axis
  19698. *
  19699. * @since 1.2.0
  19700. */
  19701. endOnTick: true,
  19702. /**
  19703. * Padding of the max value relative to the length of the axis. A
  19704. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  19705. * when you don't want the highest data value to appear on the edge
  19706. * of the plot area. When the axis' `max` option is set or a max extreme
  19707. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  19708. *
  19709. * Also the `softThreshold` option takes precedence over `maxPadding`,
  19710. * so if the data is tangent to the threshold, `maxPadding` may not
  19711. * apply unless `softThreshold` is set to false.
  19712. *
  19713. * @sample {highcharts} highcharts/yaxis/maxpadding-02/
  19714. * Max padding of 0.2
  19715. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  19716. * Greater min- and maxPadding
  19717. *
  19718. * @since 1.2.0
  19719. * @product highcharts highstock gantt
  19720. */
  19721. maxPadding: 0.05,
  19722. /**
  19723. * Padding of the min value relative to the length of the axis. A
  19724. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  19725. * when you don't want the lowest data value to appear on the edge
  19726. * of the plot area. When the axis' `min` option is set or a max extreme
  19727. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  19728. *
  19729. * Also the `softThreshold` option takes precedence over `minPadding`,
  19730. * so if the data is tangent to the threshold, `minPadding` may not
  19731. * apply unless `softThreshold` is set to false.
  19732. *
  19733. * @sample {highcharts} highcharts/yaxis/minpadding/
  19734. * Min padding of 0.2
  19735. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  19736. * Greater min- and maxPadding
  19737. *
  19738. * @since 1.2.0
  19739. * @product highcharts highstock gantt
  19740. */
  19741. minPadding: 0.05,
  19742. /**
  19743. * @productdesc {highstock}
  19744. * In Highstock 1.x, the Y axis was placed on the left side by default.
  19745. *
  19746. * @sample {highcharts} highcharts/yaxis/opposite/
  19747. * Secondary Y axis opposite
  19748. * @sample {highstock} stock/xaxis/opposite/
  19749. * Y axis on left side
  19750. *
  19751. * @type {boolean}
  19752. * @default {highstock} true
  19753. * @default {highcharts} false
  19754. * @product highstock highcharts gantt
  19755. * @apioption yAxis.opposite
  19756. */
  19757. /**
  19758. * @see [tickInterval](#xAxis.tickInterval)
  19759. * @see [tickPositioner](#xAxis.tickPositioner)
  19760. * @see [tickPositions](#xAxis.tickPositions)
  19761. */
  19762. tickPixelInterval: 72,
  19763. showLastLabel: true,
  19764. /**
  19765. * @extends xAxis.labels
  19766. */
  19767. labels: {
  19768. /**
  19769. * Angular gauges and solid gauges only.
  19770. * The label's pixel distance from the perimeter of the plot area.
  19771. *
  19772. * Since v7.1.2: If it's a percentage string, it is interpreted the
  19773. * same as [series.radius](#plotOptions.gauge.radius), so label can be
  19774. * aligned under the gauge's shape.
  19775. *
  19776. * @sample {highcharts} highcharts/yaxis/labels-distance/
  19777. * Labels centered under the arc
  19778. *
  19779. * @type {number|string}
  19780. * @default -25
  19781. * @product highcharts
  19782. * @apioption yAxis.labels.distance
  19783. */
  19784. /**
  19785. * The y position offset of all labels relative to the tick
  19786. * positions on the axis. For polar and radial axis consider the use
  19787. * of the [distance](#yAxis.labels.distance) option.
  19788. *
  19789. * @sample {highcharts} highcharts/xaxis/labels-x/
  19790. * Y axis labels placed on grid lines
  19791. *
  19792. * @type {number}
  19793. * @default {highcharts} 3
  19794. * @default {highstock} -2
  19795. * @default {highmaps} 3
  19796. * @apioption yAxis.labels.y
  19797. */
  19798. /**
  19799. * What part of the string the given position is anchored to. Can
  19800. * be one of `"left"`, `"center"` or `"right"`. The exact position
  19801. * also depends on the `labels.x` setting.
  19802. *
  19803. * Angular gauges and solid gauges defaults to `"center"`.
  19804. * Solid gauges with two labels have additional option `"auto"`
  19805. * for automatic horizontal and vertical alignment.
  19806. *
  19807. * @see [yAxis.labels.distance](#yAxis.labels.distance)
  19808. *
  19809. * @sample {highcharts} highcharts/yaxis/labels-align-left/
  19810. * Left
  19811. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  19812. * Solid gauge labels auto aligned
  19813. *
  19814. * @type {Highcharts.AlignValue}
  19815. * @default {highcharts|highmaps} right
  19816. * @default {highstock} left
  19817. * @apioption yAxis.labels.align
  19818. */
  19819. /**
  19820. * The x position offset of all labels relative to the tick
  19821. * positions on the axis. Defaults to -15 for left axis, 15 for
  19822. * right axis.
  19823. *
  19824. * @sample {highcharts} highcharts/xaxis/labels-x/
  19825. * Y axis labels placed on grid lines
  19826. */
  19827. x: -8
  19828. },
  19829. /**
  19830. * @productdesc {highmaps}
  19831. * In Highmaps, the axis line is hidden by default, because the axis is
  19832. * not visible by default.
  19833. *
  19834. * @type {Highcharts.ColorType}
  19835. * @apioption yAxis.lineColor
  19836. */
  19837. /**
  19838. * @sample {highcharts} highcharts/yaxis/max-200/
  19839. * Y axis max of 200
  19840. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  19841. * Y axis max on logarithmic axis
  19842. * @sample {highstock} stock/yaxis/min-max/
  19843. * Fixed min and max on Y axis
  19844. * @sample {highmaps} maps/axis/min-max/
  19845. * Pre-zoomed to a specific area
  19846. *
  19847. * @apioption yAxis.max
  19848. */
  19849. /**
  19850. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  19851. * -50 with startOnTick to false
  19852. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  19853. * -50 with startOnTick true by default
  19854. * @sample {highstock} stock/yaxis/min-max/
  19855. * Fixed min and max on Y axis
  19856. * @sample {highmaps} maps/axis/min-max/
  19857. * Pre-zoomed to a specific area
  19858. *
  19859. * @apioption yAxis.min
  19860. */
  19861. /**
  19862. * An optional scrollbar to display on the Y axis in response to
  19863. * limiting the minimum an maximum of the axis values.
  19864. *
  19865. * In styled mode, all the presentational options for the scrollbar
  19866. * are replaced by the classes `.highcharts-scrollbar-thumb`,
  19867. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  19868. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  19869. *
  19870. * @sample {highstock} stock/yaxis/scrollbar/
  19871. * Scrollbar on the Y axis
  19872. *
  19873. * @extends scrollbar
  19874. * @since 4.2.6
  19875. * @product highstock
  19876. * @excluding height
  19877. * @apioption yAxis.scrollbar
  19878. */
  19879. /**
  19880. * Enable the scrollbar on the Y axis.
  19881. *
  19882. * @sample {highstock} stock/yaxis/scrollbar/
  19883. * Enabled on Y axis
  19884. *
  19885. * @type {boolean}
  19886. * @default false
  19887. * @since 4.2.6
  19888. * @product highstock
  19889. * @apioption yAxis.scrollbar.enabled
  19890. */
  19891. /**
  19892. * Pixel margin between the scrollbar and the axis elements.
  19893. *
  19894. * @type {number}
  19895. * @default 10
  19896. * @since 4.2.6
  19897. * @product highstock
  19898. * @apioption yAxis.scrollbar.margin
  19899. */
  19900. /**
  19901. * Whether to show the scrollbar when it is fully zoomed out at max
  19902. * range. Setting it to `false` on the Y axis makes the scrollbar stay
  19903. * hidden until the user zooms in, like common in browsers.
  19904. *
  19905. * @type {boolean}
  19906. * @default true
  19907. * @since 4.2.6
  19908. * @product highstock
  19909. * @apioption yAxis.scrollbar.showFull
  19910. */
  19911. /**
  19912. * The width of a vertical scrollbar or height of a horizontal
  19913. * scrollbar. Defaults to 20 on touch devices.
  19914. *
  19915. * @type {number}
  19916. * @default 14
  19917. * @since 4.2.6
  19918. * @product highstock
  19919. * @apioption yAxis.scrollbar.size
  19920. */
  19921. /**
  19922. * Z index of the scrollbar elements.
  19923. *
  19924. * @type {number}
  19925. * @default 3
  19926. * @since 4.2.6
  19927. * @product highstock
  19928. * @apioption yAxis.scrollbar.zIndex
  19929. */
  19930. /**
  19931. * A soft maximum for the axis. If the series data maximum is less
  19932. * than this, the axis will stay at this maximum, but if the series
  19933. * data maximum is higher, the axis will flex to show all data.
  19934. *
  19935. * **Note**: The [series.softThreshold](
  19936. * #plotOptions.series.softThreshold) option takes precedence over this
  19937. * option.
  19938. *
  19939. * @sample highcharts/yaxis/softmin-softmax/
  19940. * Soft min and max
  19941. *
  19942. * @type {number}
  19943. * @since 5.0.1
  19944. * @product highcharts highstock gantt
  19945. * @apioption yAxis.softMax
  19946. */
  19947. /**
  19948. * A soft minimum for the axis. If the series data minimum is greater
  19949. * than this, the axis will stay at this minimum, but if the series
  19950. * data minimum is lower, the axis will flex to show all data.
  19951. *
  19952. * **Note**: The [series.softThreshold](
  19953. * #plotOptions.series.softThreshold) option takes precedence over this
  19954. * option.
  19955. *
  19956. * @sample highcharts/yaxis/softmin-softmax/
  19957. * Soft min and max
  19958. *
  19959. * @type {number}
  19960. * @since 5.0.1
  19961. * @product highcharts highstock gantt
  19962. * @apioption yAxis.softMin
  19963. */
  19964. /**
  19965. * Defines the horizontal alignment of the stack total label. Can be one
  19966. * of `"left"`, `"center"` or `"right"`. The default value is calculated
  19967. * at runtime and depends on orientation and whether the stack is
  19968. * positive or negative.
  19969. *
  19970. * @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
  19971. * Aligned to the left
  19972. * @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
  19973. * Aligned in center
  19974. * @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
  19975. * Aligned to the right
  19976. *
  19977. * @type {Highcharts.AlignValue}
  19978. * @since 2.1.5
  19979. * @product highcharts
  19980. * @apioption yAxis.stackLabels.align
  19981. */
  19982. /**
  19983. * A format string for the data label. Available variables are the same
  19984. * as for `formatter`.
  19985. *
  19986. * @type {string}
  19987. * @default {total}
  19988. * @since 3.0.2
  19989. * @product highcharts highstock
  19990. * @apioption yAxis.stackLabels.format
  19991. */
  19992. /**
  19993. * Rotation of the labels in degrees.
  19994. *
  19995. * @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
  19996. * Labels rotated 45°
  19997. *
  19998. * @type {number}
  19999. * @default 0
  20000. * @since 2.1.5
  20001. * @product highcharts
  20002. * @apioption yAxis.stackLabels.rotation
  20003. */
  20004. /**
  20005. * The text alignment for the label. While `align` determines where the
  20006. * texts anchor point is placed with regards to the stack, `textAlign`
  20007. * determines how the text is aligned against its anchor point. Possible
  20008. * values are `"left"`, `"center"` and `"right"`. The default value is
  20009. * calculated at runtime and depends on orientation and whether the
  20010. * stack is positive or negative.
  20011. *
  20012. * @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
  20013. * Label in center position but text-aligned left
  20014. *
  20015. * @type {Highcharts.AlignValue}
  20016. * @since 2.1.5
  20017. * @product highcharts
  20018. * @apioption yAxis.stackLabels.textAlign
  20019. */
  20020. /**
  20021. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  20022. * to render the labels.
  20023. *
  20024. * @type {boolean}
  20025. * @default false
  20026. * @since 3.0
  20027. * @product highcharts highstock
  20028. * @apioption yAxis.stackLabels.useHTML
  20029. */
  20030. /**
  20031. * Defines the vertical alignment of the stack total label. Can be one
  20032. * of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
  20033. * at runtime and depends on orientation and whether the stack is
  20034. * positive or negative.
  20035. *
  20036. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
  20037. * Vertically aligned top
  20038. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
  20039. * Vertically aligned middle
  20040. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
  20041. * Vertically aligned bottom
  20042. *
  20043. * @type {Highcharts.VerticalAlignValue}
  20044. * @since 2.1.5
  20045. * @product highcharts
  20046. * @apioption yAxis.stackLabels.verticalAlign
  20047. */
  20048. /**
  20049. * The x position offset of the label relative to the left of the
  20050. * stacked bar. The default value is calculated at runtime and depends
  20051. * on orientation and whether the stack is positive or negative.
  20052. *
  20053. * @sample {highcharts} highcharts/yaxis/stacklabels-x/
  20054. * Stack total labels with x offset
  20055. *
  20056. * @type {number}
  20057. * @since 2.1.5
  20058. * @product highcharts
  20059. * @apioption yAxis.stackLabels.x
  20060. */
  20061. /**
  20062. * The y position offset of the label relative to the tick position
  20063. * on the axis. The default value is calculated at runtime and depends
  20064. * on orientation and whether the stack is positive or negative.
  20065. *
  20066. * @sample {highcharts} highcharts/yaxis/stacklabels-y/
  20067. * Stack total labels with y offset
  20068. *
  20069. * @type {number}
  20070. * @since 2.1.5
  20071. * @product highcharts
  20072. * @apioption yAxis.stackLabels.y
  20073. */
  20074. /**
  20075. * Whether to force the axis to start on a tick. Use this option with
  20076. * the `maxPadding` option to control the axis start.
  20077. *
  20078. * This option is always disabled, when panning type is
  20079. * either `y` or `xy`.
  20080. *
  20081. * @see [type](#chart.panning.type)
  20082. *
  20083. * @sample {highcharts} highcharts/xaxis/startontick-false/
  20084. * False by default
  20085. * @sample {highcharts} highcharts/xaxis/startontick-true/
  20086. * True
  20087. * @sample {highstock} stock/xaxis/endontick/
  20088. * False for Y axis
  20089. *
  20090. * @since 1.2.0
  20091. * @product highcharts highstock gantt
  20092. */
  20093. startOnTick: true,
  20094. title: {
  20095. /**
  20096. * The pixel distance between the axis labels and the title.
  20097. * Positive values are outside the axis line, negative are inside.
  20098. *
  20099. * @sample {highcharts} highcharts/xaxis/title-margin/
  20100. * Y axis title margin of 60
  20101. *
  20102. * @type {number}
  20103. * @default 40
  20104. * @apioption yAxis.title.margin
  20105. */
  20106. /**
  20107. * The rotation of the text in degrees. 0 is horizontal, 270 is
  20108. * vertical reading from bottom to top.
  20109. *
  20110. * @sample {highcharts} highcharts/yaxis/title-offset/
  20111. * Horizontal
  20112. */
  20113. rotation: 270,
  20114. /**
  20115. * The actual text of the axis title. Horizontal texts can contain
  20116. * HTML, but rotated texts are painted using vector techniques and
  20117. * must be clean text. The Y axis title is disabled by setting the
  20118. * `text` option to `undefined`.
  20119. *
  20120. * @sample {highcharts} highcharts/xaxis/title-text/
  20121. * Custom HTML
  20122. *
  20123. * @type {string|null}
  20124. * @default {highcharts} Values
  20125. * @default {highstock} undefined
  20126. * @product highcharts highstock gantt
  20127. */
  20128. text: 'Values'
  20129. },
  20130. /**
  20131. * The top position of the Y axis. If it's a number, it is interpreted
  20132. * as pixel position relative to the chart.
  20133. *
  20134. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  20135. * percentages of the plot height, offset from plot area top.
  20136. *
  20137. * @see [yAxis.height](#yAxis.height)
  20138. *
  20139. * @sample {highstock} stock/demo/candlestick-and-volume/
  20140. * Percentage height panes
  20141. *
  20142. * @type {number|string}
  20143. * @product highcharts highstock
  20144. * @apioption yAxis.top
  20145. */
  20146. /**
  20147. * The stack labels show the total value for each bar in a stacked
  20148. * column or bar chart. The label will be placed on top of positive
  20149. * columns and below negative columns. In case of an inverted column
  20150. * chart or a bar chart the label is placed to the right of positive
  20151. * bars and to the left of negative bars.
  20152. *
  20153. * @product highcharts
  20154. */
  20155. stackLabels: {
  20156. /**
  20157. * Enable or disable the initial animation when a series is
  20158. * displayed for the `stackLabels`. The animation can also be set as
  20159. * a configuration object. Please note that this option only
  20160. * applies to the initial animation.
  20161. * For other animations, see [chart.animation](#chart.animation)
  20162. * and the animation parameter under the API methods.
  20163. * The following properties are supported:
  20164. *
  20165. * - `defer`: The animation delay time in milliseconds.
  20166. *
  20167. * @sample {highcharts} highcharts/plotoptions/animation-defer/
  20168. * Animation defer settings
  20169. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  20170. * @since 8.2.0
  20171. * @apioption yAxis.stackLabels.animation
  20172. */
  20173. animation: {},
  20174. /**
  20175. * The animation delay time in milliseconds.
  20176. * Set to `0` renders stackLabel immediately.
  20177. * As `undefined` inherits defer time from the [series.animation.defer](#plotOptions.series.animation.defer).
  20178. *
  20179. * @type {number}
  20180. * @since 8.2.0
  20181. * @apioption yAxis.stackLabels.animation.defer
  20182. */
  20183. /**
  20184. * Allow the stack labels to overlap.
  20185. *
  20186. * @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
  20187. * Default false
  20188. *
  20189. * @since 5.0.13
  20190. * @product highcharts
  20191. */
  20192. allowOverlap: false,
  20193. /**
  20194. * The background color or gradient for the stack label.
  20195. *
  20196. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  20197. * Stack labels box options
  20198. * @type {Highcharts.ColorType}
  20199. * @since 8.1.0
  20200. * @apioption yAxis.stackLabels.backgroundColor
  20201. */
  20202. /**
  20203. * The border color for the stack label. Defaults to `undefined`.
  20204. *
  20205. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  20206. * Stack labels box options
  20207. * @type {Highcharts.ColorType}
  20208. * @since 8.1.0
  20209. * @apioption yAxis.stackLabels.borderColor
  20210. */
  20211. /**
  20212. * The border radius in pixels for the stack label.
  20213. *
  20214. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  20215. * Stack labels box options
  20216. * @type {number}
  20217. * @default 0
  20218. * @since 8.1.0
  20219. * @apioption yAxis.stackLabels.borderRadius
  20220. */
  20221. /**
  20222. * The border width in pixels for the stack label.
  20223. *
  20224. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  20225. * Stack labels box options
  20226. * @type {number}
  20227. * @default 0
  20228. * @since 8.1.0
  20229. * @apioption yAxis.stackLabels.borderWidth
  20230. */
  20231. /**
  20232. * Enable or disable the stack total labels.
  20233. *
  20234. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
  20235. * Enabled stack total labels
  20236. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled-waterfall/
  20237. * Enabled stack labels in waterfall chart
  20238. *
  20239. * @since 2.1.5
  20240. * @product highcharts
  20241. */
  20242. enabled: false,
  20243. /**
  20244. * Whether to hide stack labels that are outside the plot area.
  20245. * By default, the stack label is moved
  20246. * inside the plot area according to the
  20247. * [overflow](/highcharts/#yAxis/stackLabels/overflow)
  20248. * option.
  20249. *
  20250. * @type {boolean}
  20251. * @since 7.1.3
  20252. */
  20253. crop: true,
  20254. /**
  20255. * How to handle stack total labels that flow outside the plot area.
  20256. * The default is set to `"justify"`,
  20257. * which aligns them inside the plot area.
  20258. * For columns and bars, this means it will be moved inside the bar.
  20259. * To display stack labels outside the plot area,
  20260. * set `crop` to `false` and `overflow` to `"allow"`.
  20261. *
  20262. * @sample highcharts/yaxis/stacklabels-overflow/
  20263. * Stack labels flows outside the plot area.
  20264. *
  20265. * @type {Highcharts.DataLabelsOverflowValue}
  20266. * @since 7.1.3
  20267. */
  20268. overflow: 'justify',
  20269. /* eslint-disable valid-jsdoc */
  20270. /**
  20271. * Callback JavaScript function to format the label. The value is
  20272. * given by `this.total`.
  20273. *
  20274. * @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
  20275. * Added units to stack total value
  20276. *
  20277. * @type {Highcharts.FormatterCallbackFunction<Highcharts.StackItemObject>}
  20278. * @since 2.1.5
  20279. * @product highcharts
  20280. */
  20281. formatter: function () {
  20282. var numberFormatter = this.axis.chart.numberFormatter;
  20283. /* eslint-enable valid-jsdoc */
  20284. return numberFormatter(this.total, -1);
  20285. },
  20286. /**
  20287. * CSS styles for the label.
  20288. *
  20289. * In styled mode, the styles are set in the
  20290. * `.highcharts-stack-label` class.
  20291. *
  20292. * @sample {highcharts} highcharts/yaxis/stacklabels-style/
  20293. * Red stack total labels
  20294. *
  20295. * @type {Highcharts.CSSObject}
  20296. * @since 2.1.5
  20297. * @product highcharts
  20298. */
  20299. style: {
  20300. /** @internal */
  20301. color: '#000000',
  20302. /** @internal */
  20303. fontSize: '11px',
  20304. /** @internal */
  20305. fontWeight: 'bold',
  20306. /** @internal */
  20307. textOutline: '1px contrast'
  20308. }
  20309. },
  20310. gridLineWidth: 1,
  20311. lineWidth: 0
  20312. // tickWidth: 0
  20313. };
  20314. /**
  20315. * The Z axis or depth axis for 3D plots.
  20316. *
  20317. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  20318. * access to the axis.
  20319. *
  20320. * @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
  20321. * Z-Axis with Categories
  20322. * @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
  20323. * Z-Axis with styling
  20324. *
  20325. * @type {*|Array<*>}
  20326. * @extends xAxis
  20327. * @since 5.0.0
  20328. * @product highcharts
  20329. * @excluding breaks, crosshair, height, left, lineColor, lineWidth,
  20330. * nameToX, showEmpty, top, width
  20331. * @apioption zAxis
  20332. *
  20333. * @private
  20334. */
  20335. // This variable extends the defaultOptions for left axes.
  20336. Axis.defaultLeftAxisOptions = {
  20337. labels: {
  20338. x: -15
  20339. },
  20340. title: {
  20341. rotation: 270
  20342. }
  20343. };
  20344. // This variable extends the defaultOptions for right axes.
  20345. Axis.defaultRightAxisOptions = {
  20346. labels: {
  20347. x: 15
  20348. },
  20349. title: {
  20350. rotation: 90
  20351. }
  20352. };
  20353. // This variable extends the defaultOptions for bottom axes.
  20354. Axis.defaultBottomAxisOptions = {
  20355. labels: {
  20356. autoRotation: [-45],
  20357. x: 0
  20358. // overflow: undefined,
  20359. // staggerLines: null
  20360. },
  20361. margin: 15,
  20362. title: {
  20363. rotation: 0
  20364. }
  20365. };
  20366. // This variable extends the defaultOptions for top axes.
  20367. Axis.defaultTopAxisOptions = {
  20368. labels: {
  20369. autoRotation: [-45],
  20370. x: 0
  20371. // overflow: undefined
  20372. // staggerLines: null
  20373. },
  20374. margin: 15,
  20375. title: {
  20376. rotation: 0
  20377. }
  20378. };
  20379. // Properties to survive after destroy, needed for Axis.update (#4317,
  20380. // #5773, #5881).
  20381. Axis.keepProps = ['extKey', 'hcEvents', 'names', 'series', 'userMax', 'userMin'];
  20382. return Axis;
  20383. }());
  20384. H.Axis = Axis;
  20385. return H.Axis;
  20386. });
  20387. _registerModule(_modules, 'Core/Axis/DateTimeAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Utilities.js']], function (Axis, U) {
  20388. /* *
  20389. *
  20390. * (c) 2010-2020 Torstein Honsi
  20391. *
  20392. * License: www.highcharts.com/license
  20393. *
  20394. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  20395. *
  20396. * */
  20397. var addEvent = U.addEvent,
  20398. getMagnitude = U.getMagnitude,
  20399. normalizeTickInterval = U.normalizeTickInterval,
  20400. timeUnits = U.timeUnits;
  20401. /* eslint-disable valid-jsdoc */
  20402. var DateTimeAxisAdditions = /** @class */ (function () {
  20403. /* *
  20404. *
  20405. * Constructors
  20406. *
  20407. * */
  20408. function DateTimeAxisAdditions(axis) {
  20409. this.axis = axis;
  20410. }
  20411. /* *
  20412. *
  20413. * Functions
  20414. *
  20415. * */
  20416. /**
  20417. * Get a normalized tick interval for dates. Returns a configuration object
  20418. * with unit range (interval), count and name. Used to prepare data for
  20419. * `getTimeTicks`. Previously this logic was part of getTimeTicks, but as
  20420. * `getTimeTicks` now runs of segments in stock charts, the normalizing
  20421. * logic was extracted in order to prevent it for running over again for
  20422. * each segment having the same interval. #662, #697.
  20423. * @private
  20424. */
  20425. /**
  20426. * Get a normalized tick interval for dates. Returns a configuration object
  20427. * with unit range (interval), count and name. Used to prepare data for
  20428. * `getTimeTicks`. Previously this logic was part of getTimeTicks, but as
  20429. * `getTimeTicks` now runs of segments in stock charts, the normalizing
  20430. * logic was extracted in order to prevent it for running over again for
  20431. * each segment having the same interval. #662, #697.
  20432. * @private
  20433. */
  20434. DateTimeAxisAdditions.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption) {
  20435. var units = unitsOption || [[
  20436. 'millisecond',
  20437. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  20438. ],
  20439. [
  20440. 'second',
  20441. [1, 2, 5, 10, 15, 30]
  20442. ],
  20443. [
  20444. 'minute',
  20445. [1, 2, 5, 10, 15, 30]
  20446. ],
  20447. [
  20448. 'hour',
  20449. [1, 2, 3, 4, 6, 8, 12]
  20450. ],
  20451. [
  20452. 'day',
  20453. [1, 2]
  20454. ],
  20455. [
  20456. 'week',
  20457. [1, 2]
  20458. ],
  20459. [
  20460. 'month',
  20461. [1, 2, 3, 4, 6]
  20462. ],
  20463. [
  20464. 'year',
  20465. null
  20466. ]],
  20467. unit = units[units.length - 1], // default unit is years
  20468. interval = timeUnits[unit[0]],
  20469. multiples = unit[1],
  20470. count,
  20471. i;
  20472. // loop through the units to find the one that best fits the
  20473. // tickInterval
  20474. for (i = 0; i < units.length; i++) {
  20475. unit = units[i];
  20476. interval = timeUnits[unit[0]];
  20477. multiples = unit[1];
  20478. if (units[i + 1]) {
  20479. // lessThan is in the middle between the highest multiple and
  20480. // the next unit.
  20481. var lessThan = (interval *
  20482. multiples[multiples.length - 1] +
  20483. timeUnits[units[i + 1][0]]) / 2;
  20484. // break and keep the current unit
  20485. if (tickInterval <= lessThan) {
  20486. break;
  20487. }
  20488. }
  20489. }
  20490. // prevent 2.5 years intervals, though 25, 250 etc. are allowed
  20491. if (interval === timeUnits.year && tickInterval < 5 * interval) {
  20492. multiples = [1, 2, 5];
  20493. }
  20494. // get the count
  20495. count = normalizeTickInterval(tickInterval / interval, multiples, unit[0] === 'year' ? // #1913, #2360
  20496. Math.max(getMagnitude(tickInterval / interval), 1) :
  20497. 1);
  20498. return {
  20499. unitRange: interval,
  20500. count: count,
  20501. unitName: unit[0]
  20502. };
  20503. };
  20504. return DateTimeAxisAdditions;
  20505. }());
  20506. /**
  20507. * Date and time support for axes.
  20508. *
  20509. * @private
  20510. * @class
  20511. */
  20512. var DateTimeAxis = /** @class */ (function () {
  20513. function DateTimeAxis() {
  20514. }
  20515. /* *
  20516. *
  20517. * Static Functions
  20518. *
  20519. * */
  20520. /**
  20521. * Extends axis class with date and time support.
  20522. * @private
  20523. */
  20524. DateTimeAxis.compose = function (AxisClass) {
  20525. AxisClass.keepProps.push('dateTime');
  20526. var axisProto = AxisClass.prototype;
  20527. /**
  20528. * Set the tick positions to a time unit that makes sense, for example
  20529. * on the first of each month or on every Monday. Return an array with
  20530. * the time positions. Used in datetime axes as well as for grouping
  20531. * data on a datetime axis.
  20532. *
  20533. * @private
  20534. * @function Highcharts.Axis#getTimeTicks
  20535. *
  20536. * @param {Highcharts.TimeNormalizeObject} normalizedInterval
  20537. * The interval in axis values (ms) and thecount.
  20538. *
  20539. * @param {number} min
  20540. * The minimum in axis values.
  20541. *
  20542. * @param {number} max
  20543. * The maximum in axis values.
  20544. *
  20545. * @param {number} startOfWeek
  20546. *
  20547. * @return {Highcharts.AxisTickPositionsArray}
  20548. */
  20549. axisProto.getTimeTicks = function () {
  20550. return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
  20551. };
  20552. /* eslint-disable no-invalid-this */
  20553. addEvent(AxisClass, 'init', function (e) {
  20554. var axis = this;
  20555. var options = e.userOptions;
  20556. if (options.type !== 'datetime') {
  20557. axis.dateTime = void 0;
  20558. return;
  20559. }
  20560. if (!axis.dateTime) {
  20561. axis.dateTime = new DateTimeAxisAdditions(axis);
  20562. }
  20563. });
  20564. /* eslint-enable no-invalid-this */
  20565. };
  20566. /* *
  20567. *
  20568. * Static Properties
  20569. *
  20570. * */
  20571. DateTimeAxis.AdditionsClass = DateTimeAxisAdditions;
  20572. return DateTimeAxis;
  20573. }());
  20574. DateTimeAxis.compose(Axis);
  20575. return DateTimeAxis;
  20576. });
  20577. _registerModule(_modules, 'Core/Axis/LogarithmicAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Utilities.js']], function (Axis, U) {
  20578. /* *
  20579. *
  20580. * (c) 2010-2020 Torstein Honsi
  20581. *
  20582. * License: www.highcharts.com/license
  20583. *
  20584. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  20585. *
  20586. * */
  20587. var addEvent = U.addEvent,
  20588. getMagnitude = U.getMagnitude,
  20589. normalizeTickInterval = U.normalizeTickInterval,
  20590. pick = U.pick;
  20591. /* eslint-disable valid-jsdoc */
  20592. /**
  20593. * Provides logarithmic support for axes.
  20594. *
  20595. * @private
  20596. * @class
  20597. */
  20598. var LogarithmicAxisAdditions = /** @class */ (function () {
  20599. /* *
  20600. *
  20601. * Constructors
  20602. *
  20603. * */
  20604. function LogarithmicAxisAdditions(axis) {
  20605. this.axis = axis;
  20606. }
  20607. /* *
  20608. *
  20609. * Functions
  20610. *
  20611. * */
  20612. /**
  20613. * Set the tick positions of a logarithmic axis.
  20614. */
  20615. LogarithmicAxisAdditions.prototype.getLogTickPositions = function (interval, min, max, minor) {
  20616. var log = this;
  20617. var axis = log.axis;
  20618. var axisLength = axis.len;
  20619. var options = axis.options;
  20620. // Since we use this method for both major and minor ticks,
  20621. // use a local variable and return the result
  20622. var positions = [];
  20623. // Reset
  20624. if (!minor) {
  20625. log.minorAutoInterval = void 0;
  20626. }
  20627. // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
  20628. if (interval >= 0.5) {
  20629. interval = Math.round(interval);
  20630. positions = axis.getLinearTickPositions(interval, min, max);
  20631. // Second case: We need intermediary ticks. For example
  20632. // 1, 2, 4, 6, 8, 10, 20, 40 etc.
  20633. }
  20634. else if (interval >= 0.08) {
  20635. var roundedMin = Math.floor(min),
  20636. intermediate,
  20637. i,
  20638. j,
  20639. len,
  20640. pos,
  20641. lastPos,
  20642. break2;
  20643. if (interval > 0.3) {
  20644. intermediate = [1, 2, 4];
  20645. // 0.2 equals five minor ticks per 1, 10, 100 etc
  20646. }
  20647. else if (interval > 0.15) {
  20648. intermediate = [1, 2, 4, 6, 8];
  20649. }
  20650. else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
  20651. intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  20652. }
  20653. for (i = roundedMin; i < max + 1 && !break2; i++) {
  20654. len = intermediate.length;
  20655. for (j = 0; j < len && !break2; j++) {
  20656. pos = log.log2lin(log.lin2log(i) * intermediate[j]);
  20657. // #1670, lastPos is #3113
  20658. if (pos > min &&
  20659. (!minor || lastPos <= max) &&
  20660. typeof lastPos !== 'undefined') {
  20661. positions.push(lastPos);
  20662. }
  20663. if (lastPos > max) {
  20664. break2 = true;
  20665. }
  20666. lastPos = pos;
  20667. }
  20668. }
  20669. // Third case: We are so deep in between whole logarithmic values that
  20670. // we might as well handle the tick positions like a linear axis. For
  20671. // example 1.01, 1.02, 1.03, 1.04.
  20672. }
  20673. else {
  20674. var realMin = log.lin2log(min),
  20675. realMax = log.lin2log(max),
  20676. tickIntervalOption = minor ?
  20677. axis.getMinorTickInterval() :
  20678. options.tickInterval,
  20679. filteredTickIntervalOption = tickIntervalOption === 'auto' ?
  20680. null :
  20681. tickIntervalOption,
  20682. tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1),
  20683. totalPixelLength = minor ?
  20684. axisLength / axis.tickPositions.length :
  20685. axisLength;
  20686. interval = pick(filteredTickIntervalOption, log.minorAutoInterval, (realMax - realMin) *
  20687. tickPixelIntervalOption / (totalPixelLength || 1));
  20688. interval = normalizeTickInterval(interval, void 0, getMagnitude(interval));
  20689. positions = axis.getLinearTickPositions(interval, realMin, realMax).map(log.log2lin);
  20690. if (!minor) {
  20691. log.minorAutoInterval = interval / 5;
  20692. }
  20693. }
  20694. // Set the axis-level tickInterval variable
  20695. if (!minor) {
  20696. axis.tickInterval = interval;
  20697. }
  20698. return positions;
  20699. };
  20700. LogarithmicAxisAdditions.prototype.lin2log = function (num) {
  20701. return Math.pow(10, num);
  20702. };
  20703. LogarithmicAxisAdditions.prototype.log2lin = function (num) {
  20704. return Math.log(num) / Math.LN10;
  20705. };
  20706. return LogarithmicAxisAdditions;
  20707. }());
  20708. var LogarithmicAxis = /** @class */ (function () {
  20709. function LogarithmicAxis() {
  20710. }
  20711. /**
  20712. * Provides logarithmic support for axes.
  20713. *
  20714. * @private
  20715. */
  20716. LogarithmicAxis.compose = function (AxisClass) {
  20717. AxisClass.keepProps.push('logarithmic');
  20718. // HC <= 8 backwards compatibility, allow wrapping
  20719. // Axis.prototype.lin2log and log2lin
  20720. // @todo Remove this in next major
  20721. var axisProto = AxisClass.prototype;
  20722. var logAxisProto = LogarithmicAxisAdditions.prototype;
  20723. axisProto.log2lin = logAxisProto.log2lin;
  20724. axisProto.lin2log = logAxisProto.lin2log;
  20725. /* eslint-disable no-invalid-this */
  20726. addEvent(AxisClass, 'init', function (e) {
  20727. var axis = this;
  20728. var options = e.userOptions;
  20729. var logarithmic = axis.logarithmic;
  20730. if (options.type !== 'logarithmic') {
  20731. axis.logarithmic = void 0;
  20732. }
  20733. else {
  20734. if (!logarithmic) {
  20735. logarithmic = axis.logarithmic = new LogarithmicAxisAdditions(axis);
  20736. }
  20737. // HC <= 8 backwards compatibility, allow wrapping
  20738. // Axis.prototype.lin2log and log2lin
  20739. // @todo Remove this in next major
  20740. if (axis.log2lin !== logarithmic.log2lin) {
  20741. logarithmic.log2lin = axis.log2lin.bind(axis);
  20742. }
  20743. if (axis.lin2log !== logarithmic.lin2log) {
  20744. logarithmic.lin2log = axis.lin2log.bind(axis);
  20745. }
  20746. }
  20747. });
  20748. addEvent(AxisClass, 'afterInit', function () {
  20749. var axis = this;
  20750. var log = axis.logarithmic;
  20751. // extend logarithmic axis
  20752. if (log) {
  20753. axis.lin2val = function (num) {
  20754. return log.lin2log(num);
  20755. };
  20756. axis.val2lin = function (num) {
  20757. return log.log2lin(num);
  20758. };
  20759. }
  20760. });
  20761. };
  20762. return LogarithmicAxis;
  20763. }());
  20764. LogarithmicAxis.compose(Axis); // @todo move to factory functions
  20765. return LogarithmicAxis;
  20766. });
  20767. _registerModule(_modules, 'Core/Axis/PlotLineOrBand.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Axis, H, U) {
  20768. /* *
  20769. *
  20770. * (c) 2010-2020 Torstein Honsi
  20771. *
  20772. * License: www.highcharts.com/license
  20773. *
  20774. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  20775. *
  20776. * */
  20777. /**
  20778. * Options for plot bands on axes.
  20779. *
  20780. * @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
  20781. */
  20782. /**
  20783. * Options for plot band labels on axes.
  20784. *
  20785. * @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
  20786. */
  20787. /**
  20788. * Options for plot lines on axes.
  20789. *
  20790. * @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
  20791. */
  20792. /**
  20793. * Options for plot line labels on axes.
  20794. *
  20795. * @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
  20796. */
  20797. var arrayMax = U.arrayMax,
  20798. arrayMin = U.arrayMin,
  20799. defined = U.defined,
  20800. destroyObjectProperties = U.destroyObjectProperties,
  20801. erase = U.erase,
  20802. extend = U.extend,
  20803. merge = U.merge,
  20804. objectEach = U.objectEach,
  20805. pick = U.pick;
  20806. /* eslint-disable no-invalid-this, valid-jsdoc */
  20807. /**
  20808. * The object wrapper for plot lines and plot bands
  20809. *
  20810. * @class
  20811. * @name Highcharts.PlotLineOrBand
  20812. *
  20813. * @param {Highcharts.Axis} axis
  20814. *
  20815. * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} [options]
  20816. */
  20817. var PlotLineOrBand = /** @class */ (function () {
  20818. function PlotLineOrBand(axis, options) {
  20819. this.axis = axis;
  20820. if (options) {
  20821. this.options = options;
  20822. this.id = options.id;
  20823. }
  20824. }
  20825. /**
  20826. * Render the plot line or plot band. If it is already existing,
  20827. * move it.
  20828. *
  20829. * @private
  20830. * @function Highcharts.PlotLineOrBand#render
  20831. * @return {Highcharts.PlotLineOrBand|undefined}
  20832. */
  20833. PlotLineOrBand.prototype.render = function () {
  20834. H.fireEvent(this, 'render');
  20835. var plotLine = this,
  20836. axis = plotLine.axis,
  20837. horiz = axis.horiz,
  20838. log = axis.logarithmic,
  20839. options = plotLine.options,
  20840. optionsLabel = options.label,
  20841. label = plotLine.label,
  20842. to = options.to,
  20843. from = options.from,
  20844. value = options.value,
  20845. isBand = defined(from) && defined(to),
  20846. isLine = defined(value),
  20847. svgElem = plotLine.svgElem,
  20848. isNew = !svgElem,
  20849. path = [],
  20850. color = options.color,
  20851. zIndex = pick(options.zIndex, 0),
  20852. events = options.events,
  20853. attribs = {
  20854. 'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
  20855. (options.className || '')
  20856. },
  20857. groupAttribs = {},
  20858. renderer = axis.chart.renderer,
  20859. groupName = isBand ? 'bands' : 'lines',
  20860. group;
  20861. // logarithmic conversion
  20862. if (log) {
  20863. from = log.log2lin(from);
  20864. to = log.log2lin(to);
  20865. value = log.log2lin(value);
  20866. }
  20867. // Set the presentational attributes
  20868. if (!axis.chart.styledMode) {
  20869. if (isLine) {
  20870. attribs.stroke = color || '#999999';
  20871. attribs['stroke-width'] = pick(options.width, 1);
  20872. if (options.dashStyle) {
  20873. attribs.dashstyle =
  20874. options.dashStyle;
  20875. }
  20876. }
  20877. else if (isBand) { // plot band
  20878. attribs.fill = color || '#e6ebf5';
  20879. if (options.borderWidth) {
  20880. attribs.stroke = options.borderColor;
  20881. attribs['stroke-width'] = options.borderWidth;
  20882. }
  20883. }
  20884. }
  20885. // Grouping and zIndex
  20886. groupAttribs.zIndex = zIndex;
  20887. groupName += '-' + zIndex;
  20888. group = axis.plotLinesAndBandsGroups[groupName];
  20889. if (!group) {
  20890. axis.plotLinesAndBandsGroups[groupName] = group =
  20891. renderer.g('plot-' + groupName)
  20892. .attr(groupAttribs).add();
  20893. }
  20894. // Create the path
  20895. if (isNew) {
  20896. /**
  20897. * SVG element of the plot line or band.
  20898. *
  20899. * @name Highcharts.PlotLineOrBand#svgElement
  20900. * @type {Highcharts.SVGElement}
  20901. */
  20902. plotLine.svgElem = svgElem = renderer
  20903. .path()
  20904. .attr(attribs)
  20905. .add(group);
  20906. }
  20907. // Set the path or return
  20908. if (isLine) {
  20909. path = axis.getPlotLinePath({
  20910. value: value,
  20911. lineWidth: svgElem.strokeWidth(),
  20912. acrossPanes: options.acrossPanes
  20913. });
  20914. }
  20915. else if (isBand) { // plot band
  20916. path = axis.getPlotBandPath(from, to, options);
  20917. }
  20918. else {
  20919. return;
  20920. }
  20921. // common for lines and bands
  20922. // Add events only if they were not added before.
  20923. if (!plotLine.eventsAdded && events) {
  20924. objectEach(events, function (event, eventType) {
  20925. svgElem.on(eventType, function (e) {
  20926. events[eventType].apply(plotLine, [e]);
  20927. });
  20928. });
  20929. plotLine.eventsAdded = true;
  20930. }
  20931. if ((isNew || !svgElem.d) && path && path.length) {
  20932. svgElem.attr({ d: path });
  20933. }
  20934. else if (svgElem) {
  20935. if (path) {
  20936. svgElem.show(true);
  20937. svgElem.animate({ d: path });
  20938. }
  20939. else if (svgElem.d) {
  20940. svgElem.hide();
  20941. if (label) {
  20942. plotLine.label = label = label.destroy();
  20943. }
  20944. }
  20945. }
  20946. // the plot band/line label
  20947. if (optionsLabel &&
  20948. (defined(optionsLabel.text) || defined(optionsLabel.formatter)) &&
  20949. path &&
  20950. path.length &&
  20951. axis.width > 0 &&
  20952. axis.height > 0 &&
  20953. !path.isFlat) {
  20954. // apply defaults
  20955. optionsLabel = merge({
  20956. align: horiz && isBand && 'center',
  20957. x: horiz ? !isBand && 4 : 10,
  20958. verticalAlign: !horiz && isBand && 'middle',
  20959. y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
  20960. rotation: horiz && !isBand && 90
  20961. }, optionsLabel);
  20962. this.renderLabel(optionsLabel, path, isBand, zIndex);
  20963. }
  20964. else if (label) { // move out of sight
  20965. label.hide();
  20966. }
  20967. // chainable
  20968. return plotLine;
  20969. };
  20970. /**
  20971. * Render and align label for plot line or band.
  20972. *
  20973. * @private
  20974. * @function Highcharts.PlotLineOrBand#renderLabel
  20975. * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
  20976. * @param {Highcharts.SVGPathArray} path
  20977. * @param {boolean} [isBand]
  20978. * @param {number} [zIndex]
  20979. * @return {void}
  20980. */
  20981. PlotLineOrBand.prototype.renderLabel = function (optionsLabel, path, isBand, zIndex) {
  20982. var plotLine = this,
  20983. label = plotLine.label,
  20984. renderer = plotLine.axis.chart.renderer,
  20985. attribs,
  20986. xBounds,
  20987. yBounds,
  20988. x,
  20989. y,
  20990. labelText;
  20991. // add the SVG element
  20992. if (!label) {
  20993. attribs = {
  20994. align: optionsLabel.textAlign || optionsLabel.align,
  20995. rotation: optionsLabel.rotation,
  20996. 'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
  20997. '-label ' + (optionsLabel.className || '')
  20998. };
  20999. attribs.zIndex = zIndex;
  21000. labelText = this.getLabelText(optionsLabel);
  21001. /**
  21002. * SVG element of the label.
  21003. *
  21004. * @name Highcharts.PlotLineOrBand#label
  21005. * @type {Highcharts.SVGElement}
  21006. */
  21007. plotLine.label = label = renderer
  21008. .text(labelText, 0, 0, optionsLabel.useHTML)
  21009. .attr(attribs)
  21010. .add();
  21011. if (!this.axis.chart.styledMode) {
  21012. label.css(optionsLabel.style);
  21013. }
  21014. }
  21015. // get the bounding box and align the label
  21016. // #3000 changed to better handle choice between plotband or plotline
  21017. xBounds = path.xBounds ||
  21018. [path[0][1], path[1][1], (isBand ? path[2][1] : path[0][1])];
  21019. yBounds = path.yBounds ||
  21020. [path[0][2], path[1][2], (isBand ? path[2][2] : path[0][2])];
  21021. x = arrayMin(xBounds);
  21022. y = arrayMin(yBounds);
  21023. label.align(optionsLabel, false, {
  21024. x: x,
  21025. y: y,
  21026. width: arrayMax(xBounds) - x,
  21027. height: arrayMax(yBounds) - y
  21028. });
  21029. label.show(true);
  21030. };
  21031. /**
  21032. * Get label's text content.
  21033. *
  21034. * @private
  21035. * @function Highcharts.PlotLineOrBand#getLabelText
  21036. * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
  21037. * @return {string}
  21038. */
  21039. PlotLineOrBand.prototype.getLabelText = function (optionsLabel) {
  21040. return defined(optionsLabel.formatter) ?
  21041. optionsLabel.formatter
  21042. .call(this) :
  21043. optionsLabel.text;
  21044. };
  21045. /**
  21046. * Remove the plot line or band.
  21047. *
  21048. * @function Highcharts.PlotLineOrBand#destroy
  21049. * @return {void}
  21050. */
  21051. PlotLineOrBand.prototype.destroy = function () {
  21052. // remove it from the lookup
  21053. erase(this.axis.plotLinesAndBands, this);
  21054. delete this.axis;
  21055. destroyObjectProperties(this);
  21056. };
  21057. return PlotLineOrBand;
  21058. }());
  21059. /* eslint-enable no-invalid-this, valid-jsdoc */
  21060. // Object with members for extending the Axis prototype
  21061. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  21062. /**
  21063. * An array of colored bands stretching across the plot area marking an
  21064. * interval on the axis.
  21065. *
  21066. * In styled mode, the plot bands are styled by the `.highcharts-plot-band`
  21067. * class in addition to the `className` option.
  21068. *
  21069. * @productdesc {highcharts}
  21070. * In a gauge, a plot band on the Y axis (value axis) will stretch along the
  21071. * perimeter of the gauge.
  21072. *
  21073. * @type {Array<*>}
  21074. * @product highcharts highstock gantt
  21075. * @apioption xAxis.plotBands
  21076. */
  21077. /**
  21078. * Flag to decide if plotBand should be rendered across all panes.
  21079. *
  21080. * @since 7.1.2
  21081. * @product highstock
  21082. * @type {boolean}
  21083. * @default true
  21084. * @apioption xAxis.plotBands.acrossPanes
  21085. */
  21086. /**
  21087. * Border color for the plot band. Also requires `borderWidth` to be set.
  21088. *
  21089. * @type {Highcharts.ColorString}
  21090. * @apioption xAxis.plotBands.borderColor
  21091. */
  21092. /**
  21093. * Border width for the plot band. Also requires `borderColor` to be set.
  21094. *
  21095. * @type {number}
  21096. * @default 0
  21097. * @apioption xAxis.plotBands.borderWidth
  21098. */
  21099. /**
  21100. * A custom class name, in addition to the default `highcharts-plot-band`,
  21101. * to apply to each individual band.
  21102. *
  21103. * @type {string}
  21104. * @since 5.0.0
  21105. * @apioption xAxis.plotBands.className
  21106. */
  21107. /**
  21108. * The color of the plot band.
  21109. *
  21110. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  21111. * Color band
  21112. * @sample {highstock} stock/xaxis/plotbands/
  21113. * Plot band on Y axis
  21114. *
  21115. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  21116. * @default #e6ebf5
  21117. * @apioption xAxis.plotBands.color
  21118. */
  21119. /**
  21120. * An object defining mouse events for the plot band. Supported properties
  21121. * are `click`, `mouseover`, `mouseout`, `mousemove`.
  21122. *
  21123. * @sample {highcharts} highcharts/xaxis/plotbands-events/
  21124. * Mouse events demonstrated
  21125. *
  21126. * @since 1.2
  21127. * @apioption xAxis.plotBands.events
  21128. */
  21129. /**
  21130. * Click event on a plot band.
  21131. *
  21132. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21133. * @apioption xAxis.plotBands.events.click
  21134. */
  21135. /**
  21136. * Mouse move event on a plot band.
  21137. *
  21138. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21139. * @apioption xAxis.plotBands.events.mousemove
  21140. */
  21141. /**
  21142. * Mouse out event on the corner of a plot band.
  21143. *
  21144. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21145. * @apioption xAxis.plotBands.events.mouseout
  21146. */
  21147. /**
  21148. * Mouse over event on a plot band.
  21149. *
  21150. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21151. * @apioption xAxis.plotBands.events.mouseover
  21152. */
  21153. /**
  21154. * The start position of the plot band in axis units.
  21155. *
  21156. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  21157. * Datetime axis
  21158. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  21159. * Categorized axis
  21160. * @sample {highstock} stock/xaxis/plotbands/
  21161. * Plot band on Y axis
  21162. *
  21163. * @type {number}
  21164. * @apioption xAxis.plotBands.from
  21165. */
  21166. /**
  21167. * An id used for identifying the plot band in Axis.removePlotBand.
  21168. *
  21169. * @sample {highcharts} highcharts/xaxis/plotbands-id/
  21170. * Remove plot band by id
  21171. * @sample {highstock} highcharts/xaxis/plotbands-id/
  21172. * Remove plot band by id
  21173. *
  21174. * @type {string}
  21175. * @apioption xAxis.plotBands.id
  21176. */
  21177. /**
  21178. * The end position of the plot band in axis units.
  21179. *
  21180. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  21181. * Datetime axis
  21182. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  21183. * Categorized axis
  21184. * @sample {highstock} stock/xaxis/plotbands/
  21185. * Plot band on Y axis
  21186. *
  21187. * @type {number}
  21188. * @apioption xAxis.plotBands.to
  21189. */
  21190. /**
  21191. * The z index of the plot band within the chart, relative to other
  21192. * elements. Using the same z index as another element may give
  21193. * unpredictable results, as the last rendered element will be on top.
  21194. * Values from 0 to 20 make sense.
  21195. *
  21196. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  21197. * Behind plot lines by default
  21198. * @sample {highcharts} highcharts/xaxis/plotbands-zindex/
  21199. * Above plot lines
  21200. * @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
  21201. * Above plot lines and series
  21202. *
  21203. * @type {number}
  21204. * @since 1.2
  21205. * @apioption xAxis.plotBands.zIndex
  21206. */
  21207. /**
  21208. * Text labels for the plot bands
  21209. *
  21210. * @product highcharts highstock gantt
  21211. * @apioption xAxis.plotBands.label
  21212. */
  21213. /**
  21214. * Horizontal alignment of the label. Can be one of "left", "center" or
  21215. * "right".
  21216. *
  21217. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  21218. * Aligned to the right
  21219. * @sample {highstock} stock/xaxis/plotbands-label/
  21220. * Plot band with labels
  21221. *
  21222. * @type {Highcharts.AlignValue}
  21223. * @default center
  21224. * @since 2.1
  21225. * @apioption xAxis.plotBands.label.align
  21226. */
  21227. /**
  21228. * Rotation of the text label in degrees .
  21229. *
  21230. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  21231. * Vertical text
  21232. *
  21233. * @type {number}
  21234. * @default 0
  21235. * @since 2.1
  21236. * @apioption xAxis.plotBands.label.rotation
  21237. */
  21238. /**
  21239. * CSS styles for the text label.
  21240. *
  21241. * In styled mode, the labels are styled by the
  21242. * `.highcharts-plot-band-label` class.
  21243. *
  21244. * @sample {highcharts} highcharts/xaxis/plotbands-label-style/
  21245. * Blue and bold label
  21246. *
  21247. * @type {Highcharts.CSSObject}
  21248. * @since 2.1
  21249. * @apioption xAxis.plotBands.label.style
  21250. */
  21251. /**
  21252. * The string text itself. A subset of HTML is supported.
  21253. *
  21254. * @type {string}
  21255. * @since 2.1
  21256. * @apioption xAxis.plotBands.label.text
  21257. */
  21258. /**
  21259. * The text alignment for the label. While `align` determines where the
  21260. * texts anchor point is placed within the plot band, `textAlign` determines
  21261. * how the text is aligned against its anchor point. Possible values are
  21262. * "left", "center" and "right". Defaults to the same as the `align` option.
  21263. *
  21264. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  21265. * Vertical text in center position but text-aligned left
  21266. *
  21267. * @type {Highcharts.AlignValue}
  21268. * @since 2.1
  21269. * @apioption xAxis.plotBands.label.textAlign
  21270. */
  21271. /**
  21272. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  21273. * to render the labels.
  21274. *
  21275. * @type {boolean}
  21276. * @default false
  21277. * @since 3.0.3
  21278. * @apioption xAxis.plotBands.label.useHTML
  21279. */
  21280. /**
  21281. * Vertical alignment of the label relative to the plot band. Can be one of
  21282. * "top", "middle" or "bottom".
  21283. *
  21284. * @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
  21285. * Vertically centered label
  21286. * @sample {highstock} stock/xaxis/plotbands-label/
  21287. * Plot band with labels
  21288. *
  21289. * @type {Highcharts.VerticalAlignValue}
  21290. * @default top
  21291. * @since 2.1
  21292. * @apioption xAxis.plotBands.label.verticalAlign
  21293. */
  21294. /**
  21295. * Horizontal position relative the alignment. Default varies by
  21296. * orientation.
  21297. *
  21298. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  21299. * Aligned 10px from the right edge
  21300. * @sample {highstock} stock/xaxis/plotbands-label/
  21301. * Plot band with labels
  21302. *
  21303. * @type {number}
  21304. * @since 2.1
  21305. * @apioption xAxis.plotBands.label.x
  21306. */
  21307. /**
  21308. * Vertical position of the text baseline relative to the alignment. Default
  21309. * varies by orientation.
  21310. *
  21311. * @sample {highcharts} highcharts/xaxis/plotbands-label-y/
  21312. * Label on x axis
  21313. * @sample {highstock} stock/xaxis/plotbands-label/
  21314. * Plot band with labels
  21315. *
  21316. * @type {number}
  21317. * @since 2.1
  21318. * @apioption xAxis.plotBands.label.y
  21319. */
  21320. /**
  21321. * An array of lines stretching across the plot area, marking a specific
  21322. * value on one of the axes.
  21323. *
  21324. * In styled mode, the plot lines are styled by the
  21325. * `.highcharts-plot-line` class in addition to the `className` option.
  21326. *
  21327. * @type {Array<*>}
  21328. * @product highcharts highstock gantt
  21329. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  21330. * Basic plot line
  21331. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  21332. * Solid gauge plot line
  21333. * @apioption xAxis.plotLines
  21334. */
  21335. /**
  21336. * Flag to decide if plotLine should be rendered across all panes.
  21337. *
  21338. * @sample {highstock} stock/xaxis/plotlines-acrosspanes/
  21339. * Plot lines on different panes
  21340. *
  21341. * @since 7.1.2
  21342. * @product highstock
  21343. * @type {boolean}
  21344. * @default true
  21345. * @apioption xAxis.plotLines.acrossPanes
  21346. */
  21347. /**
  21348. * A custom class name, in addition to the default `highcharts-plot-line`,
  21349. * to apply to each individual line.
  21350. *
  21351. * @type {string}
  21352. * @since 5.0.0
  21353. * @apioption xAxis.plotLines.className
  21354. */
  21355. /**
  21356. * The color of the line.
  21357. *
  21358. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  21359. * A red line from X axis
  21360. * @sample {highstock} stock/xaxis/plotlines/
  21361. * Plot line on Y axis
  21362. *
  21363. * @type {Highcharts.ColorString}
  21364. * @default #999999
  21365. * @apioption xAxis.plotLines.color
  21366. */
  21367. /**
  21368. * The dashing or dot style for the plot line. For possible values see
  21369. * [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  21370. *
  21371. * @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
  21372. * Dash and dot pattern
  21373. * @sample {highstock} stock/xaxis/plotlines/
  21374. * Plot line on Y axis
  21375. *
  21376. * @type {Highcharts.DashStyleValue}
  21377. * @default Solid
  21378. * @since 1.2
  21379. * @apioption xAxis.plotLines.dashStyle
  21380. */
  21381. /**
  21382. * An object defining mouse events for the plot line. Supported
  21383. * properties are `click`, `mouseover`, `mouseout`, `mousemove`.
  21384. *
  21385. * @sample {highcharts} highcharts/xaxis/plotlines-events/
  21386. * Mouse events demonstrated
  21387. *
  21388. * @since 1.2
  21389. * @apioption xAxis.plotLines.events
  21390. */
  21391. /**
  21392. * Click event on a plot band.
  21393. *
  21394. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21395. * @apioption xAxis.plotLines.events.click
  21396. */
  21397. /**
  21398. * Mouse move event on a plot band.
  21399. *
  21400. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21401. * @apioption xAxis.plotLines.events.mousemove
  21402. */
  21403. /**
  21404. * Mouse out event on the corner of a plot band.
  21405. *
  21406. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21407. * @apioption xAxis.plotLines.events.mouseout
  21408. */
  21409. /**
  21410. * Mouse over event on a plot band.
  21411. *
  21412. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  21413. * @apioption xAxis.plotLines.events.mouseover
  21414. */
  21415. /**
  21416. * An id used for identifying the plot line in Axis.removePlotLine.
  21417. *
  21418. * @sample {highcharts} highcharts/xaxis/plotlines-id/
  21419. * Remove plot line by id
  21420. *
  21421. * @type {string}
  21422. * @apioption xAxis.plotLines.id
  21423. */
  21424. /**
  21425. * The position of the line in axis units.
  21426. *
  21427. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  21428. * Between two categories on X axis
  21429. * @sample {highstock} stock/xaxis/plotlines/
  21430. * Plot line on Y axis
  21431. *
  21432. * @type {number}
  21433. * @apioption xAxis.plotLines.value
  21434. */
  21435. /**
  21436. * The width or thickness of the plot line.
  21437. *
  21438. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  21439. * 2px wide line from X axis
  21440. * @sample {highstock} stock/xaxis/plotlines/
  21441. * Plot line on Y axis
  21442. *
  21443. * @type {number}
  21444. * @default 2
  21445. * @apioption xAxis.plotLines.width
  21446. */
  21447. /**
  21448. * The z index of the plot line within the chart.
  21449. *
  21450. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
  21451. * Behind plot lines by default
  21452. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
  21453. * Above plot lines
  21454. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
  21455. * Above plot lines and series
  21456. *
  21457. * @type {number}
  21458. * @since 1.2
  21459. * @apioption xAxis.plotLines.zIndex
  21460. */
  21461. /**
  21462. * Text labels for the plot bands
  21463. *
  21464. * @apioption xAxis.plotLines.label
  21465. */
  21466. /**
  21467. * Horizontal alignment of the label. Can be one of "left", "center" or
  21468. * "right".
  21469. *
  21470. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  21471. * Aligned to the right
  21472. * @sample {highstock} stock/xaxis/plotlines/
  21473. * Plot line on Y axis
  21474. *
  21475. * @type {Highcharts.AlignValue}
  21476. * @default left
  21477. * @since 2.1
  21478. * @apioption xAxis.plotLines.label.align
  21479. */
  21480. /**
  21481. * Callback JavaScript function to format the label. Useful properties like
  21482. * the value of plot line or the range of plot band (`from` & `to`
  21483. * properties) can be found in `this.options` object.
  21484. *
  21485. * @sample {highcharts} highcharts/xaxis/plotlines-plotbands-label-formatter
  21486. * Label formatters for plot line and plot band.
  21487. * @type {Highcharts.FormatterCallbackFunction<Highcharts.PlotLineOrBand>}
  21488. * @apioption xAxis.plotLines.label.formatter
  21489. */
  21490. /**
  21491. * Rotation of the text label in degrees. Defaults to 0 for horizontal plot
  21492. * lines and 90 for vertical lines.
  21493. *
  21494. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  21495. * Slanted text
  21496. *
  21497. * @type {number}
  21498. * @since 2.1
  21499. * @apioption xAxis.plotLines.label.rotation
  21500. */
  21501. /**
  21502. * CSS styles for the text label.
  21503. *
  21504. * In styled mode, the labels are styled by the
  21505. * `.highcharts-plot-line-label` class.
  21506. *
  21507. * @sample {highcharts} highcharts/xaxis/plotlines-label-style/
  21508. * Blue and bold label
  21509. *
  21510. * @type {Highcharts.CSSObject}
  21511. * @since 2.1
  21512. * @apioption xAxis.plotLines.label.style
  21513. */
  21514. /**
  21515. * The text itself. A subset of HTML is supported.
  21516. *
  21517. * @type {string}
  21518. * @since 2.1
  21519. * @apioption xAxis.plotLines.label.text
  21520. */
  21521. /**
  21522. * The text alignment for the label. While `align` determines where the
  21523. * texts anchor point is placed within the plot band, `textAlign` determines
  21524. * how the text is aligned against its anchor point. Possible values are
  21525. * "left", "center" and "right". Defaults to the same as the `align` option.
  21526. *
  21527. * @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
  21528. * Text label in bottom position
  21529. *
  21530. * @type {Highcharts.AlignValue}
  21531. * @since 2.1
  21532. * @apioption xAxis.plotLines.label.textAlign
  21533. */
  21534. /**
  21535. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  21536. * to render the labels.
  21537. *
  21538. * @type {boolean}
  21539. * @default false
  21540. * @since 3.0.3
  21541. * @apioption xAxis.plotLines.label.useHTML
  21542. */
  21543. /**
  21544. * Vertical alignment of the label relative to the plot line. Can be
  21545. * one of "top", "middle" or "bottom".
  21546. *
  21547. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  21548. * Vertically centered label
  21549. *
  21550. * @type {Highcharts.VerticalAlignValue}
  21551. * @default {highcharts} top
  21552. * @default {highstock} top
  21553. * @since 2.1
  21554. * @apioption xAxis.plotLines.label.verticalAlign
  21555. */
  21556. /**
  21557. * Horizontal position relative the alignment. Default varies by
  21558. * orientation.
  21559. *
  21560. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  21561. * Aligned 10px from the right edge
  21562. * @sample {highstock} stock/xaxis/plotlines/
  21563. * Plot line on Y axis
  21564. *
  21565. * @type {number}
  21566. * @since 2.1
  21567. * @apioption xAxis.plotLines.label.x
  21568. */
  21569. /**
  21570. * Vertical position of the text baseline relative to the alignment. Default
  21571. * varies by orientation.
  21572. *
  21573. * @sample {highcharts} highcharts/xaxis/plotlines-label-y/
  21574. * Label below the plot line
  21575. * @sample {highstock} stock/xaxis/plotlines/
  21576. * Plot line on Y axis
  21577. *
  21578. * @type {number}
  21579. * @since 2.1
  21580. * @apioption xAxis.plotLines.label.y
  21581. */
  21582. /**
  21583. *
  21584. * @type {Array<*>}
  21585. * @extends xAxis.plotBands
  21586. * @apioption yAxis.plotBands
  21587. */
  21588. /**
  21589. * In a gauge chart, this option determines the inner radius of the
  21590. * plot band that stretches along the perimeter. It can be given as
  21591. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  21592. * By default, the inner radius is controlled by the [thickness](
  21593. * #yAxis.plotBands.thickness) option.
  21594. *
  21595. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  21596. * Gauge plot band
  21597. *
  21598. * @type {number|string}
  21599. * @since 2.3
  21600. * @product highcharts
  21601. * @apioption yAxis.plotBands.innerRadius
  21602. */
  21603. /**
  21604. * In a gauge chart, this option determines the outer radius of the
  21605. * plot band that stretches along the perimeter. It can be given as
  21606. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  21607. *
  21608. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  21609. * Gauge plot band
  21610. *
  21611. * @type {number|string}
  21612. * @default 100%
  21613. * @since 2.3
  21614. * @product highcharts
  21615. * @apioption yAxis.plotBands.outerRadius
  21616. */
  21617. /**
  21618. * In a gauge chart, this option sets the width of the plot band
  21619. * stretching along the perimeter. It can be given as a percentage
  21620. * string, like `"10%"`, or as a pixel number, like `10`. The default
  21621. * value 10 is the same as the default [tickLength](#yAxis.tickLength),
  21622. * thus making the plot band act as a background for the tick markers.
  21623. *
  21624. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  21625. * Gauge plot band
  21626. *
  21627. * @type {number|string}
  21628. * @default 10
  21629. * @since 2.3
  21630. * @product highcharts
  21631. * @apioption yAxis.plotBands.thickness
  21632. */
  21633. /**
  21634. * @type {Array<*>}
  21635. * @extends xAxis.plotLines
  21636. * @apioption yAxis.plotLines
  21637. */
  21638. /* eslint-disable no-invalid-this, valid-jsdoc */
  21639. /**
  21640. * Internal function to create the SVG path definition for a plot band.
  21641. *
  21642. * @function Highcharts.Axis#getPlotBandPath
  21643. *
  21644. * @param {number} from
  21645. * The axis value to start from.
  21646. *
  21647. * @param {number} to
  21648. * The axis value to end on.
  21649. *
  21650. * @return {Highcharts.SVGPathArray}
  21651. * The SVG path definition in array form.
  21652. */
  21653. getPlotBandPath: function (from, to) {
  21654. var toPath = this.getPlotLinePath({
  21655. value: to,
  21656. force: true,
  21657. acrossPanes: this.options.acrossPanes
  21658. }),
  21659. path = this.getPlotLinePath({
  21660. value: from,
  21661. force: true,
  21662. acrossPanes: this.options.acrossPanes
  21663. }),
  21664. result = [],
  21665. i,
  21666. // #4964 check if chart is inverted or plotband is on yAxis
  21667. horiz = this.horiz,
  21668. plus = 1,
  21669. isFlat,
  21670. outside = (from < this.min && to < this.min) ||
  21671. (from > this.max && to > this.max);
  21672. if (path && toPath) {
  21673. // Flat paths don't need labels (#3836)
  21674. if (outside) {
  21675. isFlat = path.toString() === toPath.toString();
  21676. plus = 0;
  21677. }
  21678. // Go over each subpath - for panes in Highstock
  21679. for (i = 0; i < path.length; i += 2) {
  21680. var pathStart = path[i],
  21681. pathEnd = path[i + 1],
  21682. toPathStart = toPath[i],
  21683. toPathEnd = toPath[i + 1];
  21684. // Type checking all affected path segments. Consider something
  21685. // smarter.
  21686. if ((pathStart[0] === 'M' || pathStart[0] === 'L') &&
  21687. (pathEnd[0] === 'M' || pathEnd[0] === 'L') &&
  21688. (toPathStart[0] === 'M' || toPathStart[0] === 'L') &&
  21689. (toPathEnd[0] === 'M' || toPathEnd[0] === 'L')) {
  21690. // Add 1 pixel when coordinates are the same
  21691. if (horiz && toPathStart[1] === pathStart[1]) {
  21692. toPathStart[1] += plus;
  21693. toPathEnd[1] += plus;
  21694. }
  21695. else if (!horiz && toPathStart[2] === pathStart[2]) {
  21696. toPathStart[2] += plus;
  21697. toPathEnd[2] += plus;
  21698. }
  21699. result.push(['M', pathStart[1], pathStart[2]], ['L', pathEnd[1], pathEnd[2]], ['L', toPathEnd[1], toPathEnd[2]], ['L', toPathStart[1], toPathStart[2]], ['Z']);
  21700. }
  21701. result.isFlat = isFlat;
  21702. }
  21703. }
  21704. else { // outside the axis area
  21705. path = null;
  21706. }
  21707. return result;
  21708. },
  21709. /**
  21710. * Add a plot band after render time.
  21711. *
  21712. * @sample highcharts/members/axis-addplotband/
  21713. * Toggle the plot band from a button
  21714. *
  21715. * @function Highcharts.Axis#addPlotBand
  21716. *
  21717. * @param {Highcharts.AxisPlotBandsOptions} options
  21718. * A configuration object for the plot band, as defined in
  21719. * [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
  21720. *
  21721. * @return {Highcharts.PlotLineOrBand|undefined}
  21722. * The added plot band.
  21723. */
  21724. addPlotBand: function (options) {
  21725. return this.addPlotBandOrLine(options, 'plotBands');
  21726. },
  21727. /**
  21728. * Add a plot line after render time.
  21729. *
  21730. * @sample highcharts/members/axis-addplotline/
  21731. * Toggle the plot line from a button
  21732. *
  21733. * @function Highcharts.Axis#addPlotLine
  21734. *
  21735. * @param {Highcharts.AxisPlotLinesOptions} options
  21736. * A configuration object for the plot line, as defined in
  21737. * [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
  21738. *
  21739. * @return {Highcharts.PlotLineOrBand|undefined}
  21740. * The added plot line.
  21741. */
  21742. addPlotLine: function (options) {
  21743. return this.addPlotBandOrLine(options, 'plotLines');
  21744. },
  21745. /**
  21746. * Add a plot band or plot line after render time. Called from addPlotBand
  21747. * and addPlotLine internally.
  21748. *
  21749. * @private
  21750. * @function Highcharts.Axis#addPlotBandOrLine
  21751. *
  21752. * @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
  21753. * The plotBand or plotLine configuration object.
  21754. *
  21755. * @param {"plotBands"|"plotLines"} [coll]
  21756. *
  21757. * @return {Highcharts.PlotLineOrBand|undefined}
  21758. */
  21759. addPlotBandOrLine: function (options, coll) {
  21760. var obj = new PlotLineOrBand(this,
  21761. options).render(),
  21762. userOptions = this.userOptions;
  21763. if (obj) { // #2189
  21764. // Add it to the user options for exporting and Axis.update
  21765. if (coll) {
  21766. // Workaround Microsoft/TypeScript issue #32693
  21767. var updatedOptions = (userOptions[coll] || []);
  21768. updatedOptions.push(options);
  21769. userOptions[coll] = updatedOptions;
  21770. }
  21771. this.plotLinesAndBands.push(obj);
  21772. this._addedPlotLB = true;
  21773. }
  21774. return obj;
  21775. },
  21776. /**
  21777. * Remove a plot band or plot line from the chart by id. Called internally
  21778. * from `removePlotBand` and `removePlotLine`.
  21779. *
  21780. * @private
  21781. * @function Highcharts.Axis#removePlotBandOrLine
  21782. * @param {string} id
  21783. * @return {void}
  21784. */
  21785. removePlotBandOrLine: function (id) {
  21786. var plotLinesAndBands = this.plotLinesAndBands,
  21787. options = this.options,
  21788. userOptions = this.userOptions,
  21789. i = plotLinesAndBands.length;
  21790. while (i--) {
  21791. if (plotLinesAndBands[i].id === id) {
  21792. plotLinesAndBands[i].destroy();
  21793. }
  21794. }
  21795. ([
  21796. options.plotLines || [],
  21797. userOptions.plotLines || [],
  21798. options.plotBands || [],
  21799. userOptions.plotBands || []
  21800. ]).forEach(function (arr) {
  21801. i = arr.length;
  21802. while (i--) {
  21803. if ((arr[i] || {}).id === id) {
  21804. erase(arr, arr[i]);
  21805. }
  21806. }
  21807. });
  21808. },
  21809. /**
  21810. * Remove a plot band by its id.
  21811. *
  21812. * @sample highcharts/members/axis-removeplotband/
  21813. * Remove plot band by id
  21814. * @sample highcharts/members/axis-addplotband/
  21815. * Toggle the plot band from a button
  21816. *
  21817. * @function Highcharts.Axis#removePlotBand
  21818. *
  21819. * @param {string} id
  21820. * The plot band's `id` as given in the original configuration
  21821. * object or in the `addPlotBand` option.
  21822. *
  21823. * @return {void}
  21824. */
  21825. removePlotBand: function (id) {
  21826. this.removePlotBandOrLine(id);
  21827. },
  21828. /**
  21829. * Remove a plot line by its id.
  21830. *
  21831. * @sample highcharts/xaxis/plotlines-id/
  21832. * Remove plot line by id
  21833. * @sample highcharts/members/axis-addplotline/
  21834. * Toggle the plot line from a button
  21835. *
  21836. * @function Highcharts.Axis#removePlotLine
  21837. *
  21838. * @param {string} id
  21839. * The plot line's `id` as given in the original configuration
  21840. * object or in the `addPlotLine` option.
  21841. */
  21842. removePlotLine: function (id) {
  21843. this.removePlotBandOrLine(id);
  21844. }
  21845. });
  21846. H.PlotLineOrBand = PlotLineOrBand;
  21847. return H.PlotLineOrBand;
  21848. });
  21849. _registerModule(_modules, 'Core/Tooltip.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  21850. /* *
  21851. *
  21852. * (c) 2010-2020 Torstein Honsi
  21853. *
  21854. * License: www.highcharts.com/license
  21855. *
  21856. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  21857. *
  21858. * */
  21859. var doc = H.doc;
  21860. var clamp = U.clamp,
  21861. css = U.css,
  21862. defined = U.defined,
  21863. discardElement = U.discardElement,
  21864. extend = U.extend,
  21865. fireEvent = U.fireEvent,
  21866. format = U.format,
  21867. isNumber = U.isNumber,
  21868. isString = U.isString,
  21869. merge = U.merge,
  21870. pick = U.pick,
  21871. splat = U.splat,
  21872. syncTimeout = U.syncTimeout,
  21873. timeUnits = U.timeUnits;
  21874. /**
  21875. * Callback function to format the text of the tooltip from scratch.
  21876. *
  21877. * In case of single or shared tooltips, a string should be be returned. In case
  21878. * of splitted tooltips, it should return an array where the first item is the
  21879. * header, and subsequent items are mapped to the points. Return `false` to
  21880. * disable tooltip for a specific point on series.
  21881. *
  21882. * @callback Highcharts.TooltipFormatterCallbackFunction
  21883. *
  21884. * @param {Highcharts.TooltipFormatterContextObject} this
  21885. * Context to format
  21886. *
  21887. * @param {Highcharts.Tooltip} tooltip
  21888. * The tooltip instance
  21889. *
  21890. * @return {false|string|Array<(string|null|undefined)>|null|undefined}
  21891. * Formatted text or false
  21892. */
  21893. /**
  21894. * @interface Highcharts.TooltipFormatterContextObject
  21895. */ /**
  21896. * @name Highcharts.TooltipFormatterContextObject#color
  21897. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  21898. */ /**
  21899. * @name Highcharts.TooltipFormatterContextObject#colorIndex
  21900. * @type {number|undefined}
  21901. */ /**
  21902. * @name Highcharts.TooltipFormatterContextObject#key
  21903. * @type {number}
  21904. */ /**
  21905. * @name Highcharts.TooltipFormatterContextObject#percentage
  21906. * @type {number|undefined}
  21907. */ /**
  21908. * @name Highcharts.TooltipFormatterContextObject#point
  21909. * @type {Highcharts.Point}
  21910. */ /**
  21911. * @name Highcharts.TooltipFormatterContextObject#points
  21912. * @type {Array<Highcharts.TooltipFormatterContextObject>|undefined}
  21913. */ /**
  21914. * @name Highcharts.TooltipFormatterContextObject#series
  21915. * @type {Highcharts.Series}
  21916. */ /**
  21917. * @name Highcharts.TooltipFormatterContextObject#total
  21918. * @type {number|undefined}
  21919. */ /**
  21920. * @name Highcharts.TooltipFormatterContextObject#x
  21921. * @type {number}
  21922. */ /**
  21923. * @name Highcharts.TooltipFormatterContextObject#y
  21924. * @type {number}
  21925. */
  21926. /**
  21927. * A callback function to place the tooltip in a specific position.
  21928. *
  21929. * @callback Highcharts.TooltipPositionerCallbackFunction
  21930. *
  21931. * @param {Highcharts.Tooltip} this
  21932. * Tooltip context of the callback.
  21933. *
  21934. * @param {number} labelWidth
  21935. * Width of the tooltip.
  21936. *
  21937. * @param {number} labelHeight
  21938. * Height of the tooltip.
  21939. *
  21940. * @param {Highcharts.Point|Highcharts.TooltipPositionerPointObject} point
  21941. * Point information for positioning a tooltip.
  21942. *
  21943. * @return {Highcharts.PositionObject}
  21944. * New position for the tooltip.
  21945. */
  21946. /**
  21947. * Point information for positioning a tooltip.
  21948. *
  21949. * @interface Highcharts.TooltipPositionerPointObject
  21950. */ /**
  21951. * If `tooltip.split` option is enabled and positioner is called for each of the
  21952. * boxes separately, this property indicates the call on the xAxis header, which
  21953. * is not a point itself.
  21954. * @name Highcharts.TooltipPositionerPointObject#isHeader
  21955. * @type {boolean}
  21956. */ /**
  21957. * The reference point relative to the plot area. Add chart.plotLeft to get the
  21958. * full coordinates.
  21959. * @name Highcharts.TooltipPositionerPointObject#plotX
  21960. * @type {number}
  21961. */ /**
  21962. * The reference point relative to the plot area. Add chart.plotTop to get the
  21963. * full coordinates.
  21964. * @name Highcharts.TooltipPositionerPointObject#plotY
  21965. * @type {number}
  21966. */
  21967. /**
  21968. * @typedef {"callout"|"circle"|"square"} Highcharts.TooltipShapeValue
  21969. */
  21970. ''; // separates doclets above from variables below
  21971. /* eslint-disable no-invalid-this, valid-jsdoc */
  21972. /**
  21973. * Tooltip of a chart.
  21974. *
  21975. * @class
  21976. * @name Highcharts.Tooltip
  21977. *
  21978. * @param {Highcharts.Chart} chart
  21979. * The chart instance.
  21980. *
  21981. * @param {Highcharts.TooltipOptions} options
  21982. * Tooltip options.
  21983. */
  21984. var Tooltip = /** @class */ (function () {
  21985. /* *
  21986. *
  21987. * Constructors
  21988. *
  21989. * */
  21990. function Tooltip(chart, options) {
  21991. this.container = void 0;
  21992. this.crosshairs = [];
  21993. this.distance = 0;
  21994. this.isHidden = true;
  21995. this.isSticky = false;
  21996. this.now = {};
  21997. this.options = {};
  21998. this.outside = false;
  21999. this.chart = chart;
  22000. this.init(chart, options);
  22001. }
  22002. /* *
  22003. *
  22004. * Functions
  22005. *
  22006. * */
  22007. /**
  22008. * In styled mode, apply the default filter for the tooltip drop-shadow. It
  22009. * needs to have an id specific to the chart, otherwise there will be issues
  22010. * when one tooltip adopts the filter of a different chart, specifically one
  22011. * where the container is hidden.
  22012. *
  22013. * @private
  22014. * @function Highcharts.Tooltip#applyFilter
  22015. */
  22016. Tooltip.prototype.applyFilter = function () {
  22017. var chart = this.chart;
  22018. chart.renderer.definition({
  22019. tagName: 'filter',
  22020. id: 'drop-shadow-' + chart.index,
  22021. opacity: 0.5,
  22022. children: [{
  22023. tagName: 'feGaussianBlur',
  22024. 'in': 'SourceAlpha',
  22025. stdDeviation: 1
  22026. }, {
  22027. tagName: 'feOffset',
  22028. dx: 1,
  22029. dy: 1
  22030. }, {
  22031. tagName: 'feComponentTransfer',
  22032. children: [{
  22033. tagName: 'feFuncA',
  22034. type: 'linear',
  22035. slope: 0.3
  22036. }]
  22037. }, {
  22038. tagName: 'feMerge',
  22039. children: [{
  22040. tagName: 'feMergeNode'
  22041. }, {
  22042. tagName: 'feMergeNode',
  22043. 'in': 'SourceGraphic'
  22044. }]
  22045. }]
  22046. });
  22047. chart.renderer.definition({
  22048. tagName: 'style',
  22049. textContent: '.highcharts-tooltip-' + chart.index + '{' +
  22050. 'filter:url(#drop-shadow-' + chart.index + ')' +
  22051. '}'
  22052. });
  22053. };
  22054. /**
  22055. * Build the body (lines) of the tooltip by iterating over the items and
  22056. * returning one entry for each item, abstracting this functionality allows
  22057. * to easily overwrite and extend it.
  22058. *
  22059. * @private
  22060. * @function Highcharts.Tooltip#bodyFormatter
  22061. * @param {Array<(Highcharts.Point|Highcharts.Series)>} items
  22062. * @return {Array<string>}
  22063. */
  22064. Tooltip.prototype.bodyFormatter = function (items) {
  22065. return items.map(function (item) {
  22066. var tooltipOptions = item.series.tooltipOptions;
  22067. return (tooltipOptions[(item.point.formatPrefix || 'point') + 'Formatter'] ||
  22068. item.point.tooltipFormatter).call(item.point, tooltipOptions[(item.point.formatPrefix || 'point') + 'Format'] || '');
  22069. });
  22070. };
  22071. /**
  22072. * Destroy the single tooltips in a split tooltip.
  22073. * If the tooltip is active then it is not destroyed, unless forced to.
  22074. *
  22075. * @private
  22076. * @function Highcharts.Tooltip#cleanSplit
  22077. *
  22078. * @param {boolean} [force]
  22079. * Force destroy all tooltips.
  22080. */
  22081. Tooltip.prototype.cleanSplit = function (force) {
  22082. this.chart.series.forEach(function (series) {
  22083. var tt = series && series.tt;
  22084. if (tt) {
  22085. if (!tt.isActive || force) {
  22086. series.tt = tt.destroy();
  22087. }
  22088. else {
  22089. tt.isActive = false;
  22090. }
  22091. }
  22092. });
  22093. };
  22094. /**
  22095. * In case no user defined formatter is given, this will be used. Note that
  22096. * the context here is an object holding point, series, x, y etc.
  22097. *
  22098. * @function Highcharts.Tooltip#defaultFormatter
  22099. *
  22100. * @param {Highcharts.Tooltip} tooltip
  22101. *
  22102. * @return {Array<string>}
  22103. */
  22104. Tooltip.prototype.defaultFormatter = function (tooltip) {
  22105. var items = this.points || splat(this),
  22106. s;
  22107. // Build the header
  22108. s = [tooltip.tooltipFooterHeaderFormatter(items[0])];
  22109. // build the values
  22110. s = s.concat(tooltip.bodyFormatter(items));
  22111. // footer
  22112. s.push(tooltip.tooltipFooterHeaderFormatter(items[0], true));
  22113. return s;
  22114. };
  22115. /**
  22116. * Removes and destroys the tooltip and its elements.
  22117. *
  22118. * @function Highcharts.Tooltip#destroy
  22119. */
  22120. Tooltip.prototype.destroy = function () {
  22121. // Destroy and clear local variables
  22122. if (this.label) {
  22123. this.label = this.label.destroy();
  22124. }
  22125. if (this.split && this.tt) {
  22126. this.cleanSplit(this.chart, true);
  22127. this.tt = this.tt.destroy();
  22128. }
  22129. if (this.renderer) {
  22130. this.renderer = this.renderer.destroy();
  22131. discardElement(this.container);
  22132. }
  22133. U.clearTimeout(this.hideTimer);
  22134. U.clearTimeout(this.tooltipTimeout);
  22135. };
  22136. /**
  22137. * Extendable method to get the anchor position of the tooltip
  22138. * from a point or set of points
  22139. *
  22140. * @private
  22141. * @function Highcharts.Tooltip#getAnchor
  22142. *
  22143. * @param {Highcharts.Point|Array<Highcharts.Point>} points
  22144. *
  22145. * @param {Highcharts.PointerEventObject} [mouseEvent]
  22146. *
  22147. * @return {Array<number>}
  22148. */
  22149. Tooltip.prototype.getAnchor = function (points, mouseEvent) {
  22150. var ret,
  22151. chart = this.chart,
  22152. pointer = chart.pointer,
  22153. inverted = chart.inverted,
  22154. plotTop = chart.plotTop,
  22155. plotLeft = chart.plotLeft,
  22156. plotX = 0,
  22157. plotY = 0,
  22158. yAxis,
  22159. xAxis;
  22160. points = splat(points);
  22161. // When tooltip follows mouse, relate the position to the mouse
  22162. if (this.followPointer && mouseEvent) {
  22163. if (typeof mouseEvent.chartX === 'undefined') {
  22164. mouseEvent = pointer.normalize(mouseEvent);
  22165. }
  22166. ret = [
  22167. mouseEvent.chartX - plotLeft,
  22168. mouseEvent.chartY - plotTop
  22169. ];
  22170. // Some series types use a specificly calculated tooltip position for
  22171. // each point
  22172. }
  22173. else if (points[0].tooltipPos) {
  22174. ret = points[0].tooltipPos;
  22175. // When shared, use the average position
  22176. }
  22177. else {
  22178. points.forEach(function (point) {
  22179. yAxis = point.series.yAxis;
  22180. xAxis = point.series.xAxis;
  22181. plotX += point.plotX +
  22182. (!inverted && xAxis ? xAxis.left - plotLeft : 0);
  22183. plotY += (point.plotLow ?
  22184. (point.plotLow + point.plotHigh) / 2 :
  22185. point.plotY) + (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
  22186. });
  22187. plotX /= points.length;
  22188. plotY /= points.length;
  22189. ret = [
  22190. inverted ? chart.plotWidth - plotY : plotX,
  22191. this.shared && !inverted && points.length > 1 && mouseEvent ?
  22192. // place shared tooltip next to the mouse (#424)
  22193. mouseEvent.chartY - plotTop :
  22194. inverted ? chart.plotHeight - plotX : plotY
  22195. ];
  22196. }
  22197. return ret.map(Math.round);
  22198. };
  22199. /**
  22200. * Get the optimal date format for a point, based on a range.
  22201. *
  22202. * @private
  22203. * @function Highcharts.Tooltip#getDateFormat
  22204. *
  22205. * @param {number} range
  22206. * The time range
  22207. *
  22208. * @param {number} date
  22209. * The date of the point in question
  22210. *
  22211. * @param {number} startOfWeek
  22212. * An integer representing the first day of the week, where 0 is
  22213. * Sunday.
  22214. *
  22215. * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
  22216. * A map of time units to formats.
  22217. *
  22218. * @return {string}
  22219. * The optimal date format for a point.
  22220. */
  22221. Tooltip.prototype.getDateFormat = function (range, date, startOfWeek, dateTimeLabelFormats) {
  22222. var time = this.chart.time, dateStr = time.dateFormat('%m-%d %H:%M:%S.%L', date), format, n, blank = '01-01 00:00:00.000', strpos = {
  22223. millisecond: 15,
  22224. second: 12,
  22225. minute: 9,
  22226. hour: 6,
  22227. day: 3
  22228. }, lastN = 'millisecond'; // for sub-millisecond data, #4223
  22229. for (n in timeUnits) { // eslint-disable-line guard-for-in
  22230. // If the range is exactly one week and we're looking at a
  22231. // Sunday/Monday, go for the week format
  22232. if (range === timeUnits.week &&
  22233. +time.dateFormat('%w', date) === startOfWeek &&
  22234. dateStr.substr(6) === blank.substr(6)) {
  22235. n = 'week';
  22236. break;
  22237. }
  22238. // The first format that is too great for the range
  22239. if (timeUnits[n] > range) {
  22240. n = lastN;
  22241. break;
  22242. }
  22243. // If the point is placed every day at 23:59, we need to show
  22244. // the minutes as well. #2637.
  22245. if (strpos[n] &&
  22246. dateStr.substr(strpos[n]) !== blank.substr(strpos[n])) {
  22247. break;
  22248. }
  22249. // Weeks are outside the hierarchy, only apply them on
  22250. // Mondays/Sundays like in the first condition
  22251. if (n !== 'week') {
  22252. lastN = n;
  22253. }
  22254. }
  22255. if (n) {
  22256. format = time.resolveDTLFormat(dateTimeLabelFormats[n]).main;
  22257. }
  22258. return format;
  22259. };
  22260. /**
  22261. * Creates the Tooltip label element if it does not exist, then returns it.
  22262. *
  22263. * @function Highcharts.Tooltip#getLabel
  22264. * @return {Highcharts.SVGElement}
  22265. */
  22266. Tooltip.prototype.getLabel = function () {
  22267. var _a,
  22268. _b;
  22269. var tooltip = this,
  22270. renderer = this.chart.renderer,
  22271. styledMode = this.chart.styledMode,
  22272. options = this.options,
  22273. className = ('tooltip' + (defined(options.className) ?
  22274. ' ' + options.className :
  22275. '')),
  22276. pointerEvents = (((_a = options.style) === null || _a === void 0 ? void 0 : _a.pointerEvents) ||
  22277. (!this.followPointer && options.stickOnContact ? 'auto' : 'none')),
  22278. container,
  22279. set,
  22280. onMouseEnter = function () {
  22281. tooltip.inContact = true;
  22282. }, onMouseLeave = function () {
  22283. var series = tooltip.chart.hoverSeries;
  22284. tooltip.inContact = false;
  22285. if (series &&
  22286. series.onMouseOut) {
  22287. series.onMouseOut();
  22288. }
  22289. };
  22290. if (!this.label) {
  22291. if (this.outside) {
  22292. /**
  22293. * Reference to the tooltip's container, when
  22294. * [Highcharts.Tooltip#outside] is set to true, otherwise
  22295. * it's undefined.
  22296. *
  22297. * @name Highcharts.Tooltip#container
  22298. * @type {Highcharts.HTMLDOMElement|undefined}
  22299. */
  22300. this.container = container = H.doc.createElement('div');
  22301. container.className = 'highcharts-tooltip-container';
  22302. css(container, {
  22303. position: 'absolute',
  22304. top: '1px',
  22305. pointerEvents: pointerEvents,
  22306. zIndex: 3
  22307. });
  22308. H.doc.body.appendChild(container);
  22309. /**
  22310. * Reference to the tooltip's renderer, when
  22311. * [Highcharts.Tooltip#outside] is set to true, otherwise
  22312. * it's undefined.
  22313. *
  22314. * @name Highcharts.Tooltip#renderer
  22315. * @type {Highcharts.SVGRenderer|undefined}
  22316. */
  22317. this.renderer = renderer = new H.Renderer(container, 0, 0, (_b = this.chart.options.chart) === null || _b === void 0 ? void 0 : _b.style, void 0, void 0, renderer.styledMode);
  22318. }
  22319. // Create the label
  22320. if (this.split) {
  22321. this.label = renderer.g(className);
  22322. }
  22323. else {
  22324. this.label = renderer
  22325. .label('', 0, 0, options.shape || 'callout', null, null, options.useHTML, null, className)
  22326. .attr({
  22327. padding: options.padding,
  22328. r: options.borderRadius
  22329. });
  22330. if (!styledMode) {
  22331. this.label
  22332. .attr({
  22333. fill: options.backgroundColor,
  22334. 'stroke-width': options.borderWidth
  22335. })
  22336. // #2301, #2657
  22337. .css(options.style)
  22338. .css({ pointerEvents: pointerEvents })
  22339. .shadow(options.shadow);
  22340. }
  22341. }
  22342. if (styledMode) {
  22343. // Apply the drop-shadow filter
  22344. this.applyFilter();
  22345. this.label.addClass('highcharts-tooltip-' + this.chart.index);
  22346. }
  22347. // Split tooltip use updateTooltipContainer to position the tooltip
  22348. // container.
  22349. if (tooltip.outside && !tooltip.split) {
  22350. var label_1 = this.label;
  22351. var xSetter_1 = label_1.xSetter,
  22352. ySetter_1 = label_1.ySetter;
  22353. label_1.xSetter = function (value) {
  22354. xSetter_1.call(label_1, tooltip.distance);
  22355. container.style.left = value + 'px';
  22356. };
  22357. label_1.ySetter = function (value) {
  22358. ySetter_1.call(label_1, tooltip.distance);
  22359. container.style.top = value + 'px';
  22360. };
  22361. }
  22362. this.label
  22363. .on('mouseenter', onMouseEnter)
  22364. .on('mouseleave', onMouseLeave)
  22365. .attr({ zIndex: 8 })
  22366. .add();
  22367. }
  22368. return this.label;
  22369. };
  22370. /**
  22371. * Place the tooltip in a chart without spilling over
  22372. * and not covering the point it self.
  22373. *
  22374. * @private
  22375. * @function Highcharts.Tooltip#getPosition
  22376. *
  22377. * @param {number} boxWidth
  22378. *
  22379. * @param {number} boxHeight
  22380. *
  22381. * @param {Highcharts.Point} point
  22382. *
  22383. * @return {Highcharts.PositionObject}
  22384. */
  22385. Tooltip.prototype.getPosition = function (boxWidth, boxHeight, point) {
  22386. var chart = this.chart,
  22387. distance = this.distance,
  22388. ret = {},
  22389. // Don't use h if chart isn't inverted (#7242) ???
  22390. h = (chart.inverted && point.h) || 0, // #4117 ???
  22391. swapped,
  22392. outside = this.outside,
  22393. outerWidth = outside ?
  22394. // substract distance to prevent scrollbars
  22395. doc.documentElement.clientWidth - 2 * distance :
  22396. chart.chartWidth,
  22397. outerHeight = outside ?
  22398. Math.max(doc.body.scrollHeight,
  22399. doc.documentElement.scrollHeight,
  22400. doc.body.offsetHeight,
  22401. doc.documentElement.offsetHeight,
  22402. doc.documentElement.clientHeight) :
  22403. chart.chartHeight,
  22404. chartPosition = chart.pointer.getChartPosition(),
  22405. containerScaling = chart.containerScaling,
  22406. scaleX = function (val) { return ( // eslint-disable-line no-confusing-arrow
  22407. containerScaling ? val * containerScaling.scaleX : val); },
  22408. scaleY = function (val) { return ( // eslint-disable-line no-confusing-arrow
  22409. containerScaling ? val * containerScaling.scaleY : val); },
  22410. // Build parameter arrays for firstDimension()/secondDimension()
  22411. buildDimensionArray = function (dim) {
  22412. var isX = dim === 'x';
  22413. return [
  22414. dim,
  22415. isX ? outerWidth : outerHeight,
  22416. isX ? boxWidth : boxHeight
  22417. ].concat(outside ? [
  22418. // If we are using tooltip.outside, we need to scale the
  22419. // position to match scaling of the container in case there
  22420. // is a transform/zoom on the container. #11329
  22421. isX ? scaleX(boxWidth) : scaleY(boxHeight),
  22422. isX ? chartPosition.left - distance +
  22423. scaleX(point.plotX + chart.plotLeft) :
  22424. chartPosition.top - distance +
  22425. scaleY(point.plotY + chart.plotTop),
  22426. 0,
  22427. isX ? outerWidth : outerHeight
  22428. ] : [
  22429. // Not outside, no scaling is needed
  22430. isX ? boxWidth : boxHeight,
  22431. isX ? point.plotX + chart.plotLeft :
  22432. point.plotY + chart.plotTop,
  22433. isX ? chart.plotLeft : chart.plotTop,
  22434. isX ? chart.plotLeft + chart.plotWidth :
  22435. chart.plotTop + chart.plotHeight
  22436. ]);
  22437. }, first = buildDimensionArray('y'), second = buildDimensionArray('x'),
  22438. // The far side is right or bottom
  22439. preferFarSide = !this.followPointer && pick(point.ttBelow, !chart.inverted === !!point.negative), // #4984
  22440. /*
  22441. * Handle the preferred dimension. When the preferred dimension is
  22442. * tooltip on top or bottom of the point, it will look for space
  22443. * there.
  22444. *
  22445. * @private
  22446. */
  22447. firstDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  22448. point, min, max) {
  22449. var scaledDist = dim === 'y' ?
  22450. scaleY(distance) : scaleX(distance),
  22451. scaleDiff = (innerSize - scaledInnerSize) / 2,
  22452. roomLeft = scaledInnerSize < point - distance,
  22453. roomRight = point + distance + scaledInnerSize < outerSize,
  22454. alignedLeft = point - scaledDist - innerSize + scaleDiff,
  22455. alignedRight = point + scaledDist - scaleDiff;
  22456. if (preferFarSide && roomRight) {
  22457. ret[dim] = alignedRight;
  22458. }
  22459. else if (!preferFarSide && roomLeft) {
  22460. ret[dim] = alignedLeft;
  22461. }
  22462. else if (roomLeft) {
  22463. ret[dim] = Math.min(max - scaledInnerSize, alignedLeft - h < 0 ? alignedLeft : alignedLeft - h);
  22464. }
  22465. else if (roomRight) {
  22466. ret[dim] = Math.max(min, alignedRight + h + innerSize > outerSize ?
  22467. alignedRight :
  22468. alignedRight + h);
  22469. }
  22470. else {
  22471. return false;
  22472. }
  22473. },
  22474. /*
  22475. * Handle the secondary dimension. If the preferred dimension is
  22476. * tooltip on top or bottom of the point, the second dimension is to
  22477. * align the tooltip above the point, trying to align center but
  22478. * allowing left or right align within the chart box.
  22479. *
  22480. * @private
  22481. */
  22482. secondDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  22483. point) {
  22484. var retVal;
  22485. // Too close to the edge, return false and swap dimensions
  22486. if (point < distance || point > outerSize - distance) {
  22487. retVal = false;
  22488. // Align left/top
  22489. }
  22490. else if (point < innerSize / 2) {
  22491. ret[dim] = 1;
  22492. // Align right/bottom
  22493. }
  22494. else if (point > outerSize - scaledInnerSize / 2) {
  22495. ret[dim] = outerSize - scaledInnerSize - 2;
  22496. // Align center
  22497. }
  22498. else {
  22499. ret[dim] = point - innerSize / 2;
  22500. }
  22501. return retVal;
  22502. },
  22503. /*
  22504. * Swap the dimensions
  22505. */
  22506. swap = function (count) {
  22507. var temp = first;
  22508. first = second;
  22509. second = temp;
  22510. swapped = count;
  22511. }, run = function () {
  22512. if (firstDimension.apply(0, first) !== false) {
  22513. if (secondDimension.apply(0, second) === false &&
  22514. !swapped) {
  22515. swap(true);
  22516. run();
  22517. }
  22518. }
  22519. else if (!swapped) {
  22520. swap(true);
  22521. run();
  22522. }
  22523. else {
  22524. ret.x = ret.y = 0;
  22525. }
  22526. };
  22527. // Under these conditions, prefer the tooltip on the side of the point
  22528. if (chart.inverted || this.len > 1) {
  22529. swap();
  22530. }
  22531. run();
  22532. return ret;
  22533. };
  22534. /**
  22535. * Get the best X date format based on the closest point range on the axis.
  22536. *
  22537. * @private
  22538. * @function Highcharts.Tooltip#getXDateFormat
  22539. *
  22540. * @param {Highcharts.Point} point
  22541. *
  22542. * @param {Highcharts.TooltipOptions} options
  22543. *
  22544. * @param {Highcharts.Axis} xAxis
  22545. *
  22546. * @return {string}
  22547. */
  22548. Tooltip.prototype.getXDateFormat = function (point, options, xAxis) {
  22549. var xDateFormat,
  22550. dateTimeLabelFormats = options.dateTimeLabelFormats,
  22551. closestPointRange = xAxis && xAxis.closestPointRange;
  22552. if (closestPointRange) {
  22553. xDateFormat = this.getDateFormat(closestPointRange, point.x, xAxis.options.startOfWeek, dateTimeLabelFormats);
  22554. }
  22555. else {
  22556. xDateFormat = dateTimeLabelFormats.day;
  22557. }
  22558. return xDateFormat || dateTimeLabelFormats.year; // #2546, 2581
  22559. };
  22560. /**
  22561. * Hides the tooltip with a fade out animation.
  22562. *
  22563. * @function Highcharts.Tooltip#hide
  22564. *
  22565. * @param {number} [delay]
  22566. * The fade out in milliseconds. If no value is provided the value
  22567. * of the tooltip.hideDelay option is used. A value of 0 disables
  22568. * the fade out animation.
  22569. */
  22570. Tooltip.prototype.hide = function (delay) {
  22571. var tooltip = this;
  22572. // disallow duplicate timers (#1728, #1766)
  22573. U.clearTimeout(this.hideTimer);
  22574. delay = pick(delay, this.options.hideDelay, 500);
  22575. if (!this.isHidden) {
  22576. this.hideTimer = syncTimeout(function () {
  22577. // If there is a delay, do fadeOut with the default duration. If
  22578. // the hideDelay is 0, we assume no animation is wanted, so we
  22579. // pass 0 duration. #12994.
  22580. tooltip.getLabel().fadeOut(delay ? void 0 : delay);
  22581. tooltip.isHidden = true;
  22582. }, delay);
  22583. }
  22584. };
  22585. /**
  22586. * @private
  22587. * @function Highcharts.Tooltip#init
  22588. *
  22589. * @param {Highcharts.Chart} chart
  22590. * The chart instance.
  22591. *
  22592. * @param {Highcharts.TooltipOptions} options
  22593. * Tooltip options.
  22594. */
  22595. Tooltip.prototype.init = function (chart, options) {
  22596. /**
  22597. * Chart of the tooltip.
  22598. *
  22599. * @readonly
  22600. * @name Highcharts.Tooltip#chart
  22601. * @type {Highcharts.Chart}
  22602. */
  22603. this.chart = chart;
  22604. /**
  22605. * Used tooltip options.
  22606. *
  22607. * @readonly
  22608. * @name Highcharts.Tooltip#options
  22609. * @type {Highcharts.TooltipOptions}
  22610. */
  22611. this.options = options;
  22612. /**
  22613. * List of crosshairs.
  22614. *
  22615. * @private
  22616. * @readonly
  22617. * @name Highcharts.Tooltip#crosshairs
  22618. * @type {Array<null>}
  22619. */
  22620. this.crosshairs = [];
  22621. /**
  22622. * Current values of x and y when animating.
  22623. *
  22624. * @private
  22625. * @readonly
  22626. * @name Highcharts.Tooltip#now
  22627. * @type {Highcharts.PositionObject}
  22628. */
  22629. this.now = { x: 0, y: 0 };
  22630. /**
  22631. * Tooltips are initially hidden.
  22632. *
  22633. * @private
  22634. * @readonly
  22635. * @name Highcharts.Tooltip#isHidden
  22636. * @type {boolean}
  22637. */
  22638. this.isHidden = true;
  22639. /**
  22640. * True, if the tooltip is split into one label per series, with the
  22641. * header close to the axis.
  22642. *
  22643. * @readonly
  22644. * @name Highcharts.Tooltip#split
  22645. * @type {boolean|undefined}
  22646. */
  22647. this.split = options.split && !chart.inverted && !chart.polar;
  22648. /**
  22649. * When the tooltip is shared, the entire plot area will capture mouse
  22650. * movement or touch events.
  22651. *
  22652. * @readonly
  22653. * @name Highcharts.Tooltip#shared
  22654. * @type {boolean|undefined}
  22655. */
  22656. this.shared = options.shared || this.split;
  22657. /**
  22658. * Whether to allow the tooltip to render outside the chart's SVG
  22659. * element box. By default (false), the tooltip is rendered within the
  22660. * chart's SVG element, which results in the tooltip being aligned
  22661. * inside the chart area.
  22662. *
  22663. * @readonly
  22664. * @name Highcharts.Tooltip#outside
  22665. * @type {boolean}
  22666. *
  22667. * @todo
  22668. * Split tooltip does not support outside in the first iteration. Should
  22669. * not be too complicated to implement.
  22670. */
  22671. this.outside = pick(options.outside, Boolean(chart.scrollablePixelsX || chart.scrollablePixelsY));
  22672. };
  22673. /**
  22674. * Returns true, if the pointer is in contact with the tooltip tracker.
  22675. */
  22676. Tooltip.prototype.isStickyOnContact = function () {
  22677. return !!(!this.followPointer &&
  22678. this.options.stickOnContact &&
  22679. this.inContact);
  22680. };
  22681. /**
  22682. * Moves the tooltip with a soft animation to a new position.
  22683. *
  22684. * @private
  22685. * @function Highcharts.Tooltip#move
  22686. *
  22687. * @param {number} x
  22688. *
  22689. * @param {number} y
  22690. *
  22691. * @param {number} anchorX
  22692. *
  22693. * @param {number} anchorY
  22694. */
  22695. Tooltip.prototype.move = function (x, y, anchorX, anchorY) {
  22696. var tooltip = this,
  22697. now = tooltip.now,
  22698. animate = tooltip.options.animation !== false &&
  22699. !tooltip.isHidden &&
  22700. // When we get close to the target position, abort animation and
  22701. // land on the right place (#3056)
  22702. (Math.abs(x - now.x) > 1 || Math.abs(y - now.y) > 1),
  22703. skipAnchor = tooltip.followPointer || tooltip.len > 1;
  22704. // Get intermediate values for animation
  22705. extend(now, {
  22706. x: animate ? (2 * now.x + x) / 3 : x,
  22707. y: animate ? (now.y + y) / 2 : y,
  22708. anchorX: skipAnchor ?
  22709. void 0 :
  22710. animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
  22711. anchorY: skipAnchor ?
  22712. void 0 :
  22713. animate ? (now.anchorY + anchorY) / 2 : anchorY
  22714. });
  22715. // Move to the intermediate value
  22716. tooltip.getLabel().attr(now);
  22717. tooltip.drawTracker();
  22718. // Run on next tick of the mouse tracker
  22719. if (animate) {
  22720. // Never allow two timeouts
  22721. U.clearTimeout(this.tooltipTimeout);
  22722. // Set the fixed interval ticking for the smooth tooltip
  22723. this.tooltipTimeout = setTimeout(function () {
  22724. // The interval function may still be running during destroy,
  22725. // so check that the chart is really there before calling.
  22726. if (tooltip) {
  22727. tooltip.move(x, y, anchorX, anchorY);
  22728. }
  22729. }, 32);
  22730. }
  22731. };
  22732. /**
  22733. * Refresh the tooltip's text and position.
  22734. *
  22735. * @function Highcharts.Tooltip#refresh
  22736. *
  22737. * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
  22738. * Either a point or an array of points.
  22739. *
  22740. * @param {Highcharts.PointerEventObject} [mouseEvent]
  22741. * Mouse event, that is responsible for the refresh and should be
  22742. * used for the tooltip update.
  22743. */
  22744. Tooltip.prototype.refresh = function (pointOrPoints, mouseEvent) {
  22745. var tooltip = this,
  22746. chart = this.chart,
  22747. options = tooltip.options,
  22748. x,
  22749. y,
  22750. point = pointOrPoints,
  22751. anchor,
  22752. textConfig = {},
  22753. text,
  22754. pointConfig = [],
  22755. formatter = options.formatter || tooltip.defaultFormatter,
  22756. shared = tooltip.shared,
  22757. currentSeries,
  22758. styledMode = chart.styledMode;
  22759. if (!options.enabled) {
  22760. return;
  22761. }
  22762. U.clearTimeout(this.hideTimer);
  22763. // get the reference point coordinates (pie charts use tooltipPos)
  22764. tooltip.followPointer = splat(point)[0].series.tooltipOptions
  22765. .followPointer;
  22766. anchor = tooltip.getAnchor(point, mouseEvent);
  22767. x = anchor[0];
  22768. y = anchor[1];
  22769. // shared tooltip, array is sent over
  22770. if (shared &&
  22771. !(point.series &&
  22772. point.series.noSharedTooltip)) {
  22773. chart.pointer.applyInactiveState(point);
  22774. // Now set hover state for the choosen ones:
  22775. point.forEach(function (item) {
  22776. item.setState('hover');
  22777. pointConfig.push(item.getLabelConfig());
  22778. });
  22779. textConfig = {
  22780. x: point[0].category,
  22781. y: point[0].y
  22782. };
  22783. textConfig.points = pointConfig;
  22784. point = point[0];
  22785. // single point tooltip
  22786. }
  22787. else {
  22788. textConfig = point.getLabelConfig();
  22789. }
  22790. this.len = pointConfig.length; // #6128
  22791. text = formatter.call(textConfig, tooltip);
  22792. // register the current series
  22793. currentSeries = point.series;
  22794. this.distance = pick(currentSeries.tooltipOptions.distance, 16);
  22795. // update the inner HTML
  22796. if (text === false) {
  22797. this.hide();
  22798. }
  22799. else {
  22800. // update text
  22801. if (tooltip.split) {
  22802. this.renderSplit(text, splat(pointOrPoints));
  22803. }
  22804. else {
  22805. var label = tooltip.getLabel();
  22806. // Prevent the tooltip from flowing over the chart box (#6659)
  22807. if (!options.style.width || styledMode) {
  22808. label.css({
  22809. width: this.chart.spacingBox.width + 'px'
  22810. });
  22811. }
  22812. label.attr({
  22813. text: text && text.join ?
  22814. text.join('') :
  22815. text
  22816. });
  22817. // Set the stroke color of the box to reflect the point
  22818. label.removeClass(/highcharts-color-[\d]+/g)
  22819. .addClass('highcharts-color-' +
  22820. pick(point.colorIndex, currentSeries.colorIndex));
  22821. if (!styledMode) {
  22822. label.attr({
  22823. stroke: (options.borderColor ||
  22824. point.color ||
  22825. currentSeries.color ||
  22826. '#666666')
  22827. });
  22828. }
  22829. tooltip.updatePosition({
  22830. plotX: x,
  22831. plotY: y,
  22832. negative: point.negative,
  22833. ttBelow: point.ttBelow,
  22834. h: anchor[2] || 0
  22835. });
  22836. }
  22837. // show it
  22838. if (tooltip.isHidden && tooltip.label) {
  22839. tooltip.label.attr({
  22840. opacity: 1
  22841. }).show();
  22842. }
  22843. tooltip.isHidden = false;
  22844. }
  22845. fireEvent(this, 'refresh');
  22846. };
  22847. /**
  22848. * Render the split tooltip. Loops over each point's text and adds
  22849. * a label next to the point, then uses the distribute function to
  22850. * find best non-overlapping positions.
  22851. *
  22852. * @private
  22853. * @function Highcharts.Tooltip#renderSplit
  22854. *
  22855. * @param {string|Array<(boolean|string)>} labels
  22856. *
  22857. * @param {Array<Highcharts.Point>} points
  22858. */
  22859. Tooltip.prototype.renderSplit = function (labels, points) {
  22860. var tooltip = this;
  22861. var chart = tooltip.chart,
  22862. _a = tooltip.chart,
  22863. chartWidth = _a.chartWidth,
  22864. chartHeight = _a.chartHeight,
  22865. plotHeight = _a.plotHeight,
  22866. plotLeft = _a.plotLeft,
  22867. plotTop = _a.plotTop,
  22868. pointer = _a.pointer,
  22869. ren = _a.renderer,
  22870. _b = _a.scrollablePixelsY,
  22871. scrollablePixelsY = _b === void 0 ? 0 : _b,
  22872. _c = _a.scrollingContainer,
  22873. _d = _c === void 0 ? { scrollLeft: 0,
  22874. scrollTop: 0 } : _c,
  22875. scrollLeft = _d.scrollLeft,
  22876. scrollTop = _d.scrollTop,
  22877. styledMode = _a.styledMode,
  22878. distance = tooltip.distance,
  22879. options = tooltip.options,
  22880. positioner = tooltip.options.positioner;
  22881. // The area which the tooltip should be limited to. Limit to scrollable
  22882. // plot area if enabled, otherwise limit to the chart container.
  22883. var bounds = {
  22884. left: scrollLeft,
  22885. right: scrollLeft + chartWidth,
  22886. top: scrollTop,
  22887. bottom: scrollTop + chartHeight
  22888. };
  22889. var tooltipLabel = tooltip.getLabel();
  22890. var headerTop = Boolean(chart.xAxis[0] && chart.xAxis[0].opposite);
  22891. var distributionBoxTop = plotTop + scrollTop;
  22892. var headerHeight = 0;
  22893. var adjustedPlotHeight = plotHeight - scrollablePixelsY;
  22894. /**
  22895. * Calculates the anchor position for the partial tooltip
  22896. *
  22897. * @private
  22898. * @param {Highcharts.Point} point The point related to the tooltip
  22899. * @return {object} Returns an object with anchorX and anchorY
  22900. */
  22901. function getAnchor(point) {
  22902. var isHeader = point.isHeader,
  22903. _a = point.plotX,
  22904. plotX = _a === void 0 ? 0 : _a,
  22905. _b = point.plotY,
  22906. plotY = _b === void 0 ? 0 : _b,
  22907. series = point.series;
  22908. var anchorX;
  22909. var anchorY;
  22910. if (isHeader) {
  22911. // Set anchorX to plotX
  22912. anchorX = plotLeft + plotX;
  22913. // Set anchorY to center of visible plot area.
  22914. anchorY = plotTop + plotHeight / 2;
  22915. }
  22916. else {
  22917. var xAxis = series.xAxis,
  22918. yAxis = series.yAxis;
  22919. // Set anchorX to plotX. Limit to within xAxis.
  22920. anchorX = xAxis.pos + clamp(plotX, -distance, xAxis.len + distance);
  22921. // Set anchorY, limit to the scrollable plot area
  22922. if (yAxis.pos + plotY >= scrollTop + plotTop &&
  22923. yAxis.pos + plotY <= scrollTop + plotTop + plotHeight - scrollablePixelsY) {
  22924. anchorY = yAxis.pos + plotY;
  22925. }
  22926. }
  22927. // Limit values to plot area
  22928. anchorX = clamp(anchorX, bounds.left - distance, bounds.right + distance);
  22929. return { anchorX: anchorX, anchorY: anchorY };
  22930. }
  22931. /**
  22932. * Calculates the position of the partial tooltip
  22933. *
  22934. * @private
  22935. * @param {number} anchorX The partial tooltip anchor x position
  22936. * @param {number} anchorY The partial tooltip anchor y position
  22937. * @param {boolean} isHeader Whether the partial tooltip is a header
  22938. * @param {number} boxWidth Width of the partial tooltip
  22939. * @return {Highcharts.PositionObject} Returns the partial tooltip x and
  22940. * y position
  22941. */
  22942. function defaultPositioner(anchorX, anchorY, isHeader, boxWidth, alignedLeft) {
  22943. if (alignedLeft === void 0) { alignedLeft = true; }
  22944. var y;
  22945. var x;
  22946. if (isHeader) {
  22947. y = headerTop ? 0 : adjustedPlotHeight;
  22948. x = clamp(anchorX - (boxWidth / 2), bounds.left, bounds.right - boxWidth);
  22949. }
  22950. else {
  22951. y = anchorY - distributionBoxTop;
  22952. x = alignedLeft ?
  22953. anchorX - boxWidth - distance :
  22954. anchorX + distance;
  22955. x = clamp(x, alignedLeft ? x : bounds.left, bounds.right);
  22956. }
  22957. // NOTE: y is relative to distributionBoxTop
  22958. return { x: x, y: y };
  22959. }
  22960. /**
  22961. * Updates the attributes and styling of the partial tooltip. Creates a
  22962. * new partial tooltip if it does not exists.
  22963. *
  22964. * @private
  22965. * @param {Highcharts.SVGElement|undefined} partialTooltip
  22966. * The partial tooltip to update
  22967. * @param {Highcharts.Point} point
  22968. * The point related to the partial tooltip
  22969. * @param {boolean|string} str The text for the partial tooltip
  22970. * @return {Highcharts.SVGElement} Returns the updated partial tooltip
  22971. */
  22972. function updatePartialTooltip(partialTooltip, point, str) {
  22973. var tt = partialTooltip;
  22974. var isHeader = point.isHeader,
  22975. series = point.series;
  22976. var colorClass = 'highcharts-color-' + pick(point.colorIndex, series.colorIndex, 'none');
  22977. if (!tt) {
  22978. var attribs = {
  22979. padding: options.padding,
  22980. r: options.borderRadius
  22981. };
  22982. if (!styledMode) {
  22983. attribs.fill = options.backgroundColor;
  22984. attribs['stroke-width'] = options.borderWidth;
  22985. }
  22986. tt = ren
  22987. .label('', 0, 0, (options[isHeader ? 'headerShape' : 'shape']) ||
  22988. 'callout', void 0, void 0, options.useHTML)
  22989. .addClass((isHeader ? 'highcharts-tooltip-header ' : '') +
  22990. 'highcharts-tooltip-box ' +
  22991. colorClass)
  22992. .attr(attribs)
  22993. .add(tooltipLabel);
  22994. }
  22995. tt.isActive = true;
  22996. tt.attr({
  22997. text: str
  22998. });
  22999. if (!styledMode) {
  23000. tt.css(options.style)
  23001. .shadow(options.shadow)
  23002. .attr({
  23003. stroke: (options.borderColor ||
  23004. point.color ||
  23005. series.color ||
  23006. '#333333')
  23007. });
  23008. }
  23009. return tt;
  23010. }
  23011. // Graceful degradation for legacy formatters
  23012. if (isString(labels)) {
  23013. labels = [false, labels];
  23014. }
  23015. // Create the individual labels for header and points, ignore footer
  23016. var boxes = labels.slice(0,
  23017. points.length + 1).reduce(function (boxes,
  23018. str,
  23019. i) {
  23020. if (str !== false && str !== '') {
  23021. var point = (points[i - 1] ||
  23022. {
  23023. // Item 0 is the header. Instead of this, we could also
  23024. // use the crosshair label
  23025. isHeader: true,
  23026. plotX: points[0].plotX,
  23027. plotY: plotHeight,
  23028. series: {}
  23029. });
  23030. var isHeader = point.isHeader;
  23031. // Store the tooltip label referance on the series
  23032. var owner = isHeader ? tooltip : point.series;
  23033. var tt = owner.tt = updatePartialTooltip(owner.tt,
  23034. point,
  23035. str);
  23036. // Get X position now, so we can move all to the other side in
  23037. // case of overflow
  23038. var bBox = tt.getBBox();
  23039. var boxWidth = bBox.width + tt.strokeWidth();
  23040. if (isHeader) {
  23041. headerHeight = bBox.height;
  23042. adjustedPlotHeight += headerHeight;
  23043. if (headerTop) {
  23044. distributionBoxTop -= headerHeight;
  23045. }
  23046. }
  23047. var _a = getAnchor(point),
  23048. anchorX = _a.anchorX,
  23049. anchorY = _a.anchorY;
  23050. if (typeof anchorY === 'number') {
  23051. var size = bBox.height + 1;
  23052. var boxPosition = (positioner ?
  23053. positioner.call(tooltip,
  23054. boxWidth,
  23055. size,
  23056. point) :
  23057. defaultPositioner(anchorX,
  23058. anchorY,
  23059. isHeader,
  23060. boxWidth));
  23061. boxes.push({
  23062. // 0-align to the top, 1-align to the bottom
  23063. align: positioner ? 0 : void 0,
  23064. anchorX: anchorX,
  23065. anchorY: anchorY,
  23066. boxWidth: boxWidth,
  23067. point: point,
  23068. rank: pick(boxPosition.rank, isHeader ? 1 : 0),
  23069. size: size,
  23070. target: boxPosition.y,
  23071. tt: tt,
  23072. x: boxPosition.x
  23073. });
  23074. }
  23075. else {
  23076. // Hide tooltips which anchorY is outside the visible plot
  23077. // area
  23078. tt.isActive = false;
  23079. }
  23080. }
  23081. return boxes;
  23082. }, []);
  23083. // If overflow left then align all labels to the right
  23084. if (!positioner && boxes.some(function (box) { return box.x < bounds.left; })) {
  23085. boxes = boxes.map(function (box) {
  23086. var _a = defaultPositioner(box.anchorX,
  23087. box.anchorY,
  23088. box.point.isHeader,
  23089. box.boxWidth,
  23090. false),
  23091. x = _a.x,
  23092. y = _a.y;
  23093. return extend(box, {
  23094. target: y,
  23095. x: x
  23096. });
  23097. });
  23098. }
  23099. // Clean previous run (for missing points)
  23100. tooltip.cleanSplit();
  23101. // Distribute and put in place
  23102. H.distribute(boxes, adjustedPlotHeight);
  23103. boxes.forEach(function (box) {
  23104. var anchorX = box.anchorX,
  23105. anchorY = box.anchorY,
  23106. pos = box.pos,
  23107. x = box.x;
  23108. // Put the label in place
  23109. box.tt.attr({
  23110. visibility: typeof pos === 'undefined' ? 'hidden' : 'inherit',
  23111. x: x,
  23112. /* NOTE: y should equal pos to be consistent with !split
  23113. * tooltip, but is currently relative to plotTop. Is left as is
  23114. * to avoid breaking change. Remove distributionBoxTop to make
  23115. * it consistent.
  23116. */
  23117. y: pos + distributionBoxTop,
  23118. anchorX: anchorX,
  23119. anchorY: anchorY
  23120. });
  23121. });
  23122. /* If we have a seperate tooltip container, then update the necessary
  23123. * container properties.
  23124. * Test that tooltip has its own container and renderer before executing
  23125. * the operation.
  23126. */
  23127. var container = tooltip.container,
  23128. outside = tooltip.outside,
  23129. renderer = tooltip.renderer;
  23130. if (outside && container && renderer) {
  23131. // Set container size to fit the tooltip
  23132. var _e = tooltipLabel.getBBox(),
  23133. width = _e.width,
  23134. height = _e.height,
  23135. x = _e.x,
  23136. y = _e.y;
  23137. renderer.setSize(width + x, height + y, false);
  23138. // Position the tooltip container to the chart container
  23139. var chartPosition = pointer.getChartPosition();
  23140. container.style.left = chartPosition.left + 'px';
  23141. container.style.top = chartPosition.top + 'px';
  23142. }
  23143. };
  23144. /**
  23145. * If the `stickOnContact` option is active, this will add a tracker shape.
  23146. *
  23147. * @private
  23148. * @function Highcharts.Tooltip#drawTracker
  23149. */
  23150. Tooltip.prototype.drawTracker = function () {
  23151. var tooltip = this;
  23152. if (tooltip.followPointer ||
  23153. !tooltip.options.stickOnContact) {
  23154. if (tooltip.tracker) {
  23155. tooltip.tracker.destroy();
  23156. }
  23157. return;
  23158. }
  23159. var chart = tooltip.chart;
  23160. var label = tooltip.label;
  23161. var point = chart.hoverPoint;
  23162. if (!label || !point) {
  23163. return;
  23164. }
  23165. var box = {
  23166. x: 0,
  23167. y: 0,
  23168. width: 0,
  23169. height: 0
  23170. };
  23171. // Combine anchor and tooltip
  23172. var anchorPos = this.getAnchor(point);
  23173. var labelBBox = label.getBBox();
  23174. anchorPos[0] += chart.plotLeft - label.translateX;
  23175. anchorPos[1] += chart.plotTop - label.translateY;
  23176. // When the mouse pointer is between the anchor point and the label,
  23177. // the label should stick.
  23178. box.x = Math.min(0, anchorPos[0]);
  23179. box.y = Math.min(0, anchorPos[1]);
  23180. box.width = (anchorPos[0] < 0 ?
  23181. Math.max(Math.abs(anchorPos[0]), (labelBBox.width - anchorPos[0])) :
  23182. Math.max(Math.abs(anchorPos[0]), labelBBox.width));
  23183. box.height = (anchorPos[1] < 0 ?
  23184. Math.max(Math.abs(anchorPos[1]), (labelBBox.height - Math.abs(anchorPos[1]))) :
  23185. Math.max(Math.abs(anchorPos[1]), labelBBox.height));
  23186. if (tooltip.tracker) {
  23187. tooltip.tracker.attr(box);
  23188. }
  23189. else {
  23190. tooltip.tracker = label.renderer
  23191. .rect(box)
  23192. .addClass('highcharts-tracker')
  23193. .add(label);
  23194. if (!chart.styledMode) {
  23195. tooltip.tracker.attr({
  23196. fill: 'rgba(0,0,0,0)'
  23197. });
  23198. }
  23199. }
  23200. };
  23201. /**
  23202. * @private
  23203. */
  23204. Tooltip.prototype.styledModeFormat = function (formatString) {
  23205. return formatString
  23206. .replace('style="font-size: 10px"', 'class="highcharts-header"')
  23207. .replace(/style="color:{(point|series)\.color}"/g, 'class="highcharts-color-{$1.colorIndex}"');
  23208. };
  23209. /**
  23210. * Format the footer/header of the tooltip
  23211. * #3397: abstraction to enable formatting of footer and header
  23212. *
  23213. * @private
  23214. * @function Highcharts.Tooltip#tooltipFooterHeaderFormatter
  23215. * @param {Highcharts.PointLabelObject} labelConfig
  23216. * @param {boolean} [isFooter]
  23217. * @return {string}
  23218. */
  23219. Tooltip.prototype.tooltipFooterHeaderFormatter = function (labelConfig, isFooter) {
  23220. var footOrHead = isFooter ? 'footer' : 'header',
  23221. series = labelConfig.series,
  23222. tooltipOptions = series.tooltipOptions,
  23223. xDateFormat = tooltipOptions.xDateFormat,
  23224. xAxis = series.xAxis,
  23225. isDateTime = (xAxis &&
  23226. xAxis.options.type === 'datetime' &&
  23227. isNumber(labelConfig.key)),
  23228. formatString = tooltipOptions[footOrHead + 'Format'],
  23229. e = {
  23230. isFooter: isFooter,
  23231. labelConfig: labelConfig
  23232. };
  23233. fireEvent(this, 'headerFormatter', e, function (e) {
  23234. // Guess the best date format based on the closest point distance
  23235. // (#568, #3418)
  23236. if (isDateTime && !xDateFormat) {
  23237. xDateFormat = this.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  23238. }
  23239. // Insert the footer date format if any
  23240. if (isDateTime && xDateFormat) {
  23241. ((labelConfig.point && labelConfig.point.tooltipDateKeys) ||
  23242. ['key']).forEach(function (key) {
  23243. formatString = formatString.replace('{point.' + key + '}', '{point.' + key + ':' + xDateFormat + '}');
  23244. });
  23245. }
  23246. // Replace default header style with class name
  23247. if (series.chart.styledMode) {
  23248. formatString = this.styledModeFormat(formatString);
  23249. }
  23250. e.text = format(formatString, {
  23251. point: labelConfig,
  23252. series: series
  23253. }, this.chart);
  23254. });
  23255. return e.text;
  23256. };
  23257. /**
  23258. * Updates the tooltip with the provided tooltip options.
  23259. *
  23260. * @function Highcharts.Tooltip#update
  23261. *
  23262. * @param {Highcharts.TooltipOptions} options
  23263. * The tooltip options to update.
  23264. */
  23265. Tooltip.prototype.update = function (options) {
  23266. this.destroy();
  23267. // Update user options (#6218)
  23268. merge(true, this.chart.options.tooltip.userOptions, options);
  23269. this.init(this.chart, merge(true, this.options, options));
  23270. };
  23271. /**
  23272. * Find the new position and perform the move
  23273. *
  23274. * @private
  23275. * @function Highcharts.Tooltip#updatePosition
  23276. *
  23277. * @param {Highcharts.Point} point
  23278. */
  23279. Tooltip.prototype.updatePosition = function (point) {
  23280. var chart = this.chart,
  23281. pointer = chart.pointer,
  23282. label = this.getLabel(),
  23283. pos,
  23284. anchorX = point.plotX + chart.plotLeft,
  23285. anchorY = point.plotY + chart.plotTop,
  23286. pad;
  23287. // Needed for outside: true (#11688)
  23288. var chartPosition = pointer.getChartPosition();
  23289. pos = (this.options.positioner || this.getPosition).call(this, label.width, label.height, point);
  23290. // Set the renderer size dynamically to prevent document size to change
  23291. if (this.outside) {
  23292. pad = (this.options.borderWidth || 0) + 2 * this.distance;
  23293. this.renderer.setSize(label.width + pad, label.height + pad, false);
  23294. // Anchor and tooltip container need scaling if chart container has
  23295. // scale transform/css zoom. #11329.
  23296. var containerScaling = chart.containerScaling;
  23297. if (containerScaling) {
  23298. css(this.container, {
  23299. transform: "scale(" + containerScaling.scaleX + ", " + containerScaling.scaleY + ")"
  23300. });
  23301. anchorX *= containerScaling.scaleX;
  23302. anchorY *= containerScaling.scaleY;
  23303. }
  23304. anchorX += chartPosition.left - pos.x;
  23305. anchorY += chartPosition.top - pos.y;
  23306. }
  23307. // do the move
  23308. this.move(Math.round(pos.x), Math.round(pos.y || 0), // can be undefined (#3977)
  23309. anchorX, anchorY);
  23310. };
  23311. return Tooltip;
  23312. }());
  23313. H.Tooltip = Tooltip;
  23314. return H.Tooltip;
  23315. });
  23316. _registerModule(_modules, 'Core/Pointer.js', [_modules['Core/Color.js'], _modules['Core/Globals.js'], _modules['Core/Tooltip.js'], _modules['Core/Utilities.js']], function (Color, H, Tooltip, U) {
  23317. /* *
  23318. *
  23319. * (c) 2010-2020 Torstein Honsi
  23320. *
  23321. * License: www.highcharts.com/license
  23322. *
  23323. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  23324. *
  23325. * */
  23326. var color = Color.parse;
  23327. var charts = H.charts,
  23328. noop = H.noop;
  23329. var addEvent = U.addEvent,
  23330. attr = U.attr,
  23331. css = U.css,
  23332. defined = U.defined,
  23333. extend = U.extend,
  23334. find = U.find,
  23335. fireEvent = U.fireEvent,
  23336. isNumber = U.isNumber,
  23337. isObject = U.isObject,
  23338. objectEach = U.objectEach,
  23339. offset = U.offset,
  23340. pick = U.pick,
  23341. splat = U.splat;
  23342. /**
  23343. * One position in relation to an axis.
  23344. *
  23345. * @interface Highcharts.PointerAxisCoordinateObject
  23346. */ /**
  23347. * Related axis.
  23348. *
  23349. * @name Highcharts.PointerAxisCoordinateObject#axis
  23350. * @type {Highcharts.Axis}
  23351. */ /**
  23352. * Axis value.
  23353. *
  23354. * @name Highcharts.PointerAxisCoordinateObject#value
  23355. * @type {number}
  23356. */
  23357. /**
  23358. * Positions in terms of axis values.
  23359. *
  23360. * @interface Highcharts.PointerAxisCoordinatesObject
  23361. */ /**
  23362. * Positions on the x-axis.
  23363. * @name Highcharts.PointerAxisCoordinatesObject#xAxis
  23364. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  23365. */ /**
  23366. * Positions on the y-axis.
  23367. * @name Highcharts.PointerAxisCoordinatesObject#yAxis
  23368. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  23369. */
  23370. /**
  23371. * Pointer coordinates.
  23372. *
  23373. * @interface Highcharts.PointerCoordinatesObject
  23374. */ /**
  23375. * @name Highcharts.PointerCoordinatesObject#chartX
  23376. * @type {number}
  23377. */ /**
  23378. * @name Highcharts.PointerCoordinatesObject#chartY
  23379. * @type {number}
  23380. */
  23381. /**
  23382. * A native browser mouse or touch event, extended with position information
  23383. * relative to the {@link Chart.container}.
  23384. *
  23385. * @interface Highcharts.PointerEventObject
  23386. * @extends global.PointerEvent
  23387. */ /**
  23388. * The X coordinate of the pointer interaction relative to the chart.
  23389. *
  23390. * @name Highcharts.PointerEventObject#chartX
  23391. * @type {number}
  23392. */ /**
  23393. * The Y coordinate of the pointer interaction relative to the chart.
  23394. *
  23395. * @name Highcharts.PointerEventObject#chartY
  23396. * @type {number}
  23397. */
  23398. /**
  23399. * Axis-specific data of a selection.
  23400. *
  23401. * @interface Highcharts.SelectDataObject
  23402. */ /**
  23403. * @name Highcharts.SelectDataObject#axis
  23404. * @type {Highcharts.Axis}
  23405. */ /**
  23406. * @name Highcharts.SelectDataObject#max
  23407. * @type {number}
  23408. */ /**
  23409. * @name Highcharts.SelectDataObject#min
  23410. * @type {number}
  23411. */
  23412. /**
  23413. * Object for select events.
  23414. *
  23415. * @interface Highcharts.SelectEventObject
  23416. */ /**
  23417. * @name Highcharts.SelectEventObject#originalEvent
  23418. * @type {global.Event}
  23419. */ /**
  23420. * @name Highcharts.SelectEventObject#xAxis
  23421. * @type {Array<Highcharts.SelectDataObject>}
  23422. */ /**
  23423. * @name Highcharts.SelectEventObject#yAxis
  23424. * @type {Array<Highcharts.SelectDataObject>}
  23425. */
  23426. ''; // detach doclets above
  23427. /* eslint-disable no-invalid-this, valid-jsdoc */
  23428. /**
  23429. * The mouse and touch tracker object. Each {@link Chart} item has one
  23430. * assosiated Pointer item that can be accessed from the {@link Chart.pointer}
  23431. * property.
  23432. *
  23433. * @class
  23434. * @name Highcharts.Pointer
  23435. *
  23436. * @param {Highcharts.Chart} chart
  23437. * The chart instance.
  23438. *
  23439. * @param {Highcharts.Options} options
  23440. * The root options object. The pointer uses options from the chart and
  23441. * tooltip structures.
  23442. */
  23443. var Pointer = /** @class */ (function () {
  23444. /* *
  23445. *
  23446. * Constructors
  23447. *
  23448. * */
  23449. function Pointer(chart, options) {
  23450. this.lastValidTouch = {};
  23451. this.pinchDown = [];
  23452. this.runChartClick = false;
  23453. this.chart = chart;
  23454. this.hasDragged = false;
  23455. this.options = options;
  23456. this.unbindContainerMouseLeave = function () { };
  23457. this.unbindContainerMouseEnter = function () { };
  23458. this.init(chart, options);
  23459. }
  23460. /* *
  23461. *
  23462. * Functions
  23463. *
  23464. * */
  23465. /**
  23466. * Set inactive state to all series that are not currently hovered,
  23467. * or, if `inactiveOtherPoints` is set to true, set inactive state to
  23468. * all points within that series.
  23469. *
  23470. * @private
  23471. * @function Highcharts.Pointer#applyInactiveState
  23472. * @param {Array<Highcharts.Point>} points
  23473. * Currently hovered points
  23474. */
  23475. Pointer.prototype.applyInactiveState = function (points) {
  23476. var activeSeries = [],
  23477. series;
  23478. // Get all active series from the hovered points
  23479. (points || []).forEach(function (item) {
  23480. series = item.series;
  23481. // Include itself
  23482. activeSeries.push(series);
  23483. // Include parent series
  23484. if (series.linkedParent) {
  23485. activeSeries.push(series.linkedParent);
  23486. }
  23487. // Include all child series
  23488. if (series.linkedSeries) {
  23489. activeSeries = activeSeries.concat(series.linkedSeries);
  23490. }
  23491. // Include navigator series
  23492. if (series.navigatorSeries) {
  23493. activeSeries.push(series.navigatorSeries);
  23494. }
  23495. });
  23496. // Now loop over all series, filtering out active series
  23497. this.chart.series.forEach(function (inactiveSeries) {
  23498. if (activeSeries.indexOf(inactiveSeries) === -1) {
  23499. // Inactive series
  23500. inactiveSeries.setState('inactive', true);
  23501. }
  23502. else if (inactiveSeries.options.inactiveOtherPoints) {
  23503. // Active series, but other points should be inactivated
  23504. inactiveSeries.setAllPointsToState('inactive');
  23505. }
  23506. });
  23507. };
  23508. /**
  23509. * Destroys the Pointer object and disconnects DOM events.
  23510. *
  23511. * @function Highcharts.Pointer#destroy
  23512. */
  23513. Pointer.prototype.destroy = function () {
  23514. var pointer = this;
  23515. if (typeof pointer.unDocMouseMove !== 'undefined') {
  23516. pointer.unDocMouseMove();
  23517. }
  23518. this.unbindContainerMouseLeave();
  23519. if (!H.chartCount) {
  23520. if (H.unbindDocumentMouseUp) {
  23521. H.unbindDocumentMouseUp = H.unbindDocumentMouseUp();
  23522. }
  23523. if (H.unbindDocumentTouchEnd) {
  23524. H.unbindDocumentTouchEnd = H.unbindDocumentTouchEnd();
  23525. }
  23526. }
  23527. // memory and CPU leak
  23528. clearInterval(pointer.tooltipTimeout);
  23529. objectEach(pointer, function (_val, prop) {
  23530. pointer[prop] = void 0;
  23531. });
  23532. };
  23533. /**
  23534. * Perform a drag operation in response to a mousemove event while the mouse
  23535. * is down.
  23536. *
  23537. * @private
  23538. * @function Highcharts.Pointer#drag
  23539. *
  23540. * @param {Highcharts.PointerEventObject} e
  23541. *
  23542. * @return {void}
  23543. */
  23544. Pointer.prototype.drag = function (e) {
  23545. var chart = this.chart,
  23546. chartOptions = chart.options.chart,
  23547. chartX = e.chartX,
  23548. chartY = e.chartY,
  23549. zoomHor = this.zoomHor,
  23550. zoomVert = this.zoomVert,
  23551. plotLeft = chart.plotLeft,
  23552. plotTop = chart.plotTop,
  23553. plotWidth = chart.plotWidth,
  23554. plotHeight = chart.plotHeight,
  23555. clickedInside,
  23556. size,
  23557. selectionMarker = this.selectionMarker,
  23558. mouseDownX = (this.mouseDownX || 0),
  23559. mouseDownY = (this.mouseDownY || 0),
  23560. panningEnabled = isObject(chartOptions.panning) ?
  23561. chartOptions.panning && chartOptions.panning.enabled :
  23562. chartOptions.panning,
  23563. panKey = (chartOptions.panKey && e[chartOptions.panKey + 'Key']);
  23564. // If the device supports both touch and mouse (like IE11), and we are
  23565. // touch-dragging inside the plot area, don't handle the mouse event.
  23566. // #4339.
  23567. if (selectionMarker && selectionMarker.touch) {
  23568. return;
  23569. }
  23570. // If the mouse is outside the plot area, adjust to cooordinates
  23571. // inside to prevent the selection marker from going outside
  23572. if (chartX < plotLeft) {
  23573. chartX = plotLeft;
  23574. }
  23575. else if (chartX > plotLeft + plotWidth) {
  23576. chartX = plotLeft + plotWidth;
  23577. }
  23578. if (chartY < plotTop) {
  23579. chartY = plotTop;
  23580. }
  23581. else if (chartY > plotTop + plotHeight) {
  23582. chartY = plotTop + plotHeight;
  23583. }
  23584. // determine if the mouse has moved more than 10px
  23585. this.hasDragged = Math.sqrt(Math.pow(mouseDownX - chartX, 2) +
  23586. Math.pow(mouseDownY - chartY, 2));
  23587. if (this.hasDragged > 10) {
  23588. clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop);
  23589. // make a selection
  23590. if (chart.hasCartesianSeries &&
  23591. (this.zoomX || this.zoomY) &&
  23592. clickedInside &&
  23593. !panKey) {
  23594. if (!selectionMarker) {
  23595. this.selectionMarker = selectionMarker =
  23596. chart.renderer.rect(plotLeft, plotTop, zoomHor ? 1 : plotWidth, zoomVert ? 1 : plotHeight, 0)
  23597. .attr({
  23598. 'class': 'highcharts-selection-marker',
  23599. zIndex: 7
  23600. })
  23601. .add();
  23602. if (!chart.styledMode) {
  23603. selectionMarker.attr({
  23604. fill: (chartOptions.selectionMarkerFill ||
  23605. color('#335cad')
  23606. .setOpacity(0.25).get())
  23607. });
  23608. }
  23609. }
  23610. }
  23611. // adjust the width of the selection marker
  23612. if (selectionMarker && zoomHor) {
  23613. size = chartX - mouseDownX;
  23614. selectionMarker.attr({
  23615. width: Math.abs(size),
  23616. x: (size > 0 ? 0 : size) + mouseDownX
  23617. });
  23618. }
  23619. // adjust the height of the selection marker
  23620. if (selectionMarker && zoomVert) {
  23621. size = chartY - mouseDownY;
  23622. selectionMarker.attr({
  23623. height: Math.abs(size),
  23624. y: (size > 0 ? 0 : size) + mouseDownY
  23625. });
  23626. }
  23627. // panning
  23628. if (clickedInside &&
  23629. !selectionMarker &&
  23630. panningEnabled) {
  23631. chart.pan(e, chartOptions.panning);
  23632. }
  23633. }
  23634. };
  23635. /**
  23636. * Start a drag operation.
  23637. *
  23638. * @private
  23639. * @function Highcharts.Pointer#dragStart
  23640. *
  23641. * @param {Highcharts.PointerEventObject} e
  23642. *
  23643. * @return {void}
  23644. */
  23645. Pointer.prototype.dragStart = function (e) {
  23646. var chart = this.chart;
  23647. // Record the start position
  23648. chart.mouseIsDown = e.type;
  23649. chart.cancelClick = false;
  23650. chart.mouseDownX = this.mouseDownX = e.chartX;
  23651. chart.mouseDownY = this.mouseDownY = e.chartY;
  23652. };
  23653. /**
  23654. * On mouse up or touch end across the entire document, drop the selection.
  23655. *
  23656. * @private
  23657. * @function Highcharts.Pointer#drop
  23658. *
  23659. * @param {global.Event} e
  23660. */
  23661. Pointer.prototype.drop = function (e) {
  23662. var pointer = this,
  23663. chart = this.chart,
  23664. hasPinched = this.hasPinched;
  23665. if (this.selectionMarker) {
  23666. var selectionData = {
  23667. originalEvent: e,
  23668. xAxis: [],
  23669. yAxis: []
  23670. },
  23671. selectionBox = this.selectionMarker,
  23672. selectionLeft = selectionBox.attr ?
  23673. selectionBox.attr('x') :
  23674. selectionBox.x,
  23675. selectionTop = selectionBox.attr ?
  23676. selectionBox.attr('y') :
  23677. selectionBox.y,
  23678. selectionWidth = selectionBox.attr ?
  23679. selectionBox.attr('width') :
  23680. selectionBox.width,
  23681. selectionHeight = selectionBox.attr ?
  23682. selectionBox.attr('height') :
  23683. selectionBox.height,
  23684. runZoom;
  23685. // a selection has been made
  23686. if (this.hasDragged || hasPinched) {
  23687. // record each axis' min and max
  23688. chart.axes.forEach(function (axis) {
  23689. if (axis.zoomEnabled &&
  23690. defined(axis.min) &&
  23691. (hasPinched ||
  23692. pointer[{
  23693. xAxis: 'zoomX',
  23694. yAxis: 'zoomY'
  23695. }[axis.coll]]) &&
  23696. isNumber(selectionLeft) &&
  23697. isNumber(selectionTop)) { // #859, #3569
  23698. var horiz = axis.horiz,
  23699. minPixelPadding = e.type === 'touchend' ?
  23700. axis.minPixelPadding :
  23701. 0, // #1207, #3075
  23702. selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop) +
  23703. minPixelPadding),
  23704. selectionMax = axis.toValue((horiz ?
  23705. selectionLeft + selectionWidth :
  23706. selectionTop + selectionHeight) - minPixelPadding);
  23707. selectionData[axis.coll].push({
  23708. axis: axis,
  23709. // Min/max for reversed axes
  23710. min: Math.min(selectionMin, selectionMax),
  23711. max: Math.max(selectionMin, selectionMax)
  23712. });
  23713. runZoom = true;
  23714. }
  23715. });
  23716. if (runZoom) {
  23717. fireEvent(chart, 'selection', selectionData, function (args) {
  23718. chart.zoom(extend(args, hasPinched ?
  23719. { animation: false } :
  23720. null));
  23721. });
  23722. }
  23723. }
  23724. if (isNumber(chart.index)) {
  23725. this.selectionMarker = this.selectionMarker.destroy();
  23726. }
  23727. // Reset scaling preview
  23728. if (hasPinched) {
  23729. this.scaleGroups();
  23730. }
  23731. }
  23732. // Reset all. Check isNumber because it may be destroyed on mouse up
  23733. // (#877)
  23734. if (chart && isNumber(chart.index)) {
  23735. css(chart.container, { cursor: chart._cursor });
  23736. chart.cancelClick = this.hasDragged > 10; // #370
  23737. chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
  23738. this.pinchDown = [];
  23739. }
  23740. };
  23741. /**
  23742. * Finds the closest point to a set of coordinates, using the k-d-tree
  23743. * algorithm.
  23744. *
  23745. * @function Highcharts.Pointer#findNearestKDPoint
  23746. *
  23747. * @param {Array<Highcharts.Series>} series
  23748. * All the series to search in.
  23749. *
  23750. * @param {boolean|undefined} shared
  23751. * Whether it is a shared tooltip or not.
  23752. *
  23753. * @param {Highcharts.PointerEventObject} e
  23754. * The pointer event object, containing chart coordinates of the
  23755. * pointer.
  23756. *
  23757. * @return {Highcharts.Point|undefined}
  23758. * The point closest to given coordinates.
  23759. */
  23760. Pointer.prototype.findNearestKDPoint = function (series, shared, e) {
  23761. var chart = this.chart;
  23762. var hoverPoint = chart.hoverPoint;
  23763. var tooltip = chart.tooltip;
  23764. if (hoverPoint &&
  23765. tooltip &&
  23766. tooltip.isStickyOnContact()) {
  23767. return hoverPoint;
  23768. }
  23769. var closest;
  23770. /** @private */
  23771. function sort(p1, p2) {
  23772. var isCloserX = p1.distX - p2.distX,
  23773. isCloser = p1.dist - p2.dist,
  23774. isAbove = (p2.series.group && p2.series.group.zIndex) -
  23775. (p1.series.group && p1.series.group.zIndex),
  23776. result;
  23777. // We have two points which are not in the same place on xAxis
  23778. // and shared tooltip:
  23779. if (isCloserX !== 0 && shared) { // #5721
  23780. result = isCloserX;
  23781. // Points are not exactly in the same place on x/yAxis:
  23782. }
  23783. else if (isCloser !== 0) {
  23784. result = isCloser;
  23785. // The same xAxis and yAxis position, sort by z-index:
  23786. }
  23787. else if (isAbove !== 0) {
  23788. result = isAbove;
  23789. // The same zIndex, sort by array index:
  23790. }
  23791. else {
  23792. result =
  23793. p1.series.index > p2.series.index ?
  23794. -1 :
  23795. 1;
  23796. }
  23797. return result;
  23798. }
  23799. series.forEach(function (s) {
  23800. var noSharedTooltip = s.noSharedTooltip && shared,
  23801. compareX = (!noSharedTooltip &&
  23802. s.options.findNearestPointBy.indexOf('y') < 0),
  23803. point = s.searchPoint(e,
  23804. compareX);
  23805. if ( // Check that we actually found a point on the series.
  23806. isObject(point, true) &&
  23807. // Use the new point if it is closer.
  23808. (!isObject(closest, true) ||
  23809. (sort(closest, point) > 0))) {
  23810. closest = point;
  23811. }
  23812. });
  23813. return closest;
  23814. };
  23815. /**
  23816. * @private
  23817. * @function Highcharts.Pointer#getChartCoordinatesFromPoint
  23818. * @param {Highcharts.Point} point
  23819. * @param {boolean} [inverted]
  23820. * @return {Highcharts.PointerCoordinatesObject|undefined}
  23821. */
  23822. Pointer.prototype.getChartCoordinatesFromPoint = function (point, inverted) {
  23823. var series = point.series,
  23824. xAxis = series.xAxis,
  23825. yAxis = series.yAxis,
  23826. plotX = pick(point.clientX,
  23827. point.plotX),
  23828. shapeArgs = point.shapeArgs;
  23829. if (xAxis && yAxis) {
  23830. return inverted ? {
  23831. chartX: xAxis.len + xAxis.pos - plotX,
  23832. chartY: yAxis.len + yAxis.pos - point.plotY
  23833. } : {
  23834. chartX: plotX + xAxis.pos,
  23835. chartY: point.plotY + yAxis.pos
  23836. };
  23837. }
  23838. if (shapeArgs && shapeArgs.x && shapeArgs.y) {
  23839. // E.g. pies do not have axes
  23840. return {
  23841. chartX: shapeArgs.x,
  23842. chartY: shapeArgs.y
  23843. };
  23844. }
  23845. };
  23846. /**
  23847. * Return the cached chartPosition if it is available on the Pointer,
  23848. * otherwise find it. Running offset is quite expensive, so it should be
  23849. * avoided when we know the chart hasn't moved.
  23850. *
  23851. * @function Highcharts.Pointer#getChartPosition
  23852. *
  23853. * @return {Highcharts.OffsetObject}
  23854. * The offset of the chart container within the page
  23855. */
  23856. Pointer.prototype.getChartPosition = function () {
  23857. return (this.chartPosition ||
  23858. (this.chartPosition = offset(this.chart.container)));
  23859. };
  23860. /**
  23861. * Get the click position in terms of axis values.
  23862. *
  23863. * @function Highcharts.Pointer#getCoordinates
  23864. *
  23865. * @param {Highcharts.PointerEventObject} e
  23866. * Pointer event, extended with `chartX` and `chartY` properties.
  23867. *
  23868. * @return {Highcharts.PointerAxisCoordinatesObject}
  23869. */
  23870. Pointer.prototype.getCoordinates = function (e) {
  23871. var coordinates = {
  23872. xAxis: [],
  23873. yAxis: []
  23874. };
  23875. this.chart.axes.forEach(function (axis) {
  23876. coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
  23877. axis: axis,
  23878. value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
  23879. });
  23880. });
  23881. return coordinates;
  23882. };
  23883. /**
  23884. * Calculates what is the current hovered point/points and series.
  23885. *
  23886. * @private
  23887. * @function Highcharts.Pointer#getHoverData
  23888. *
  23889. * @param {Highcharts.Point|undefined} existingHoverPoint
  23890. * The point currrently beeing hovered.
  23891. *
  23892. * @param {Highcharts.Series|undefined} existingHoverSeries
  23893. * The series currently beeing hovered.
  23894. *
  23895. * @param {Array<Highcharts.Series>} series
  23896. * All the series in the chart.
  23897. *
  23898. * @param {boolean} isDirectTouch
  23899. * Is the pointer directly hovering the point.
  23900. *
  23901. * @param {boolean|undefined} shared
  23902. * Whether it is a shared tooltip or not.
  23903. *
  23904. * @param {Highcharts.PointerEventObject} [e]
  23905. * The triggering event, containing chart coordinates of the pointer.
  23906. *
  23907. * @return {object}
  23908. * Object containing resulting hover data: hoverPoint, hoverSeries,
  23909. * and hoverPoints.
  23910. */
  23911. Pointer.prototype.getHoverData = function (existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
  23912. var hoverPoint,
  23913. hoverPoints = [],
  23914. hoverSeries = existingHoverSeries,
  23915. useExisting = !!(isDirectTouch && existingHoverPoint),
  23916. notSticky = hoverSeries && !hoverSeries.stickyTracking,
  23917. // Which series to look in for the hover point
  23918. searchSeries,
  23919. // Parameters needed for beforeGetHoverData event.
  23920. eventArgs = {
  23921. chartX: e ? e.chartX : void 0,
  23922. chartY: e ? e.chartY : void 0,
  23923. shared: shared
  23924. },
  23925. filter = function (s) {
  23926. return (s.visible &&
  23927. !(!shared && s.directTouch) && // #3821
  23928. pick(s.options.enableMouseTracking,
  23929. true));
  23930. };
  23931. // Find chart.hoverPane and update filter method in polar.
  23932. fireEvent(this, 'beforeGetHoverData', eventArgs);
  23933. searchSeries = notSticky ?
  23934. // Only search on hovered series if it has stickyTracking false
  23935. [hoverSeries] :
  23936. // Filter what series to look in.
  23937. series.filter(function (s) {
  23938. return eventArgs.filter ? eventArgs.filter(s) : filter(s) &&
  23939. s.stickyTracking;
  23940. });
  23941. // Use existing hovered point or find the one closest to coordinates.
  23942. hoverPoint = useExisting || !e ?
  23943. existingHoverPoint :
  23944. this.findNearestKDPoint(searchSeries, shared, e);
  23945. // Assign hover series
  23946. hoverSeries = hoverPoint && hoverPoint.series;
  23947. // If we have a hoverPoint, assign hoverPoints.
  23948. if (hoverPoint) {
  23949. // When tooltip is shared, it displays more than one point
  23950. if (shared && !hoverSeries.noSharedTooltip) {
  23951. searchSeries = series.filter(function (s) {
  23952. return eventArgs.filter ?
  23953. eventArgs.filter(s) : filter(s) && !s.noSharedTooltip;
  23954. });
  23955. // Get all points with the same x value as the hoverPoint
  23956. searchSeries.forEach(function (s) {
  23957. var point = find(s.points,
  23958. function (p) {
  23959. return p.x === hoverPoint.x && !p.isNull;
  23960. });
  23961. if (isObject(point)) {
  23962. /*
  23963. * Boost returns a minimal point. Convert it to a usable
  23964. * point for tooltip and states.
  23965. */
  23966. if (s.chart.isBoosting) {
  23967. point = s.getPoint(point);
  23968. }
  23969. hoverPoints.push(point);
  23970. }
  23971. });
  23972. }
  23973. else {
  23974. hoverPoints.push(hoverPoint);
  23975. }
  23976. }
  23977. // Check whether the hoverPoint is inside pane we are hovering over.
  23978. eventArgs = { hoverPoint: hoverPoint };
  23979. fireEvent(this, 'afterGetHoverData', eventArgs);
  23980. return {
  23981. hoverPoint: eventArgs.hoverPoint,
  23982. hoverSeries: hoverSeries,
  23983. hoverPoints: hoverPoints
  23984. };
  23985. };
  23986. /**
  23987. * @private
  23988. * @function Highcharts.Pointer#getPointFromEvent
  23989. *
  23990. * @param {global.Event} e
  23991. *
  23992. * @return {Highcharts.Point|undefined}
  23993. */
  23994. Pointer.prototype.getPointFromEvent = function (e) {
  23995. var target = e.target,
  23996. point;
  23997. while (target && !point) {
  23998. point = target.point;
  23999. target = target.parentNode;
  24000. }
  24001. return point;
  24002. };
  24003. /**
  24004. * @private
  24005. * @function Highcharts.Pointer#onTrackerMouseOut
  24006. *
  24007. * @param {Highcharts.PointerEventObject} e
  24008. *
  24009. * @return {void}
  24010. */
  24011. Pointer.prototype.onTrackerMouseOut = function (e) {
  24012. var chart = this.chart;
  24013. var relatedTarget = e.relatedTarget || e.toElement;
  24014. var series = chart.hoverSeries;
  24015. this.isDirectTouch = false;
  24016. if (series &&
  24017. relatedTarget &&
  24018. !series.stickyTracking &&
  24019. !this.inClass(relatedTarget, 'highcharts-tooltip') &&
  24020. (!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465, #5553
  24021. !this.inClass(relatedTarget, 'highcharts-tracker'))) {
  24022. series.onMouseOut();
  24023. }
  24024. };
  24025. /**
  24026. * Utility to detect whether an element has, or has a parent with, a
  24027. * specificclass name. Used on detection of tracker objects and on deciding
  24028. * whether hovering the tooltip should cause the active series to mouse out.
  24029. *
  24030. * @function Highcharts.Pointer#inClass
  24031. *
  24032. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  24033. * The element to investigate.
  24034. *
  24035. * @param {string} className
  24036. * The class name to look for.
  24037. *
  24038. * @return {boolean|undefined}
  24039. * True if either the element or one of its parents has the given
  24040. * class name.
  24041. */
  24042. Pointer.prototype.inClass = function (element, className) {
  24043. var elemClassName;
  24044. while (element) {
  24045. elemClassName = attr(element, 'class');
  24046. if (elemClassName) {
  24047. if (elemClassName.indexOf(className) !== -1) {
  24048. return true;
  24049. }
  24050. if (elemClassName.indexOf('highcharts-container') !== -1) {
  24051. return false;
  24052. }
  24053. }
  24054. element = element.parentNode;
  24055. }
  24056. };
  24057. /**
  24058. * Initialize the Pointer.
  24059. *
  24060. * @private
  24061. * @function Highcharts.Pointer#init
  24062. *
  24063. * @param {Highcharts.Chart} chart
  24064. * The Chart instance.
  24065. *
  24066. * @param {Highcharts.Options} options
  24067. * The root options object. The pointer uses options from the chart
  24068. * and tooltip structures.
  24069. *
  24070. * @return {void}
  24071. */
  24072. Pointer.prototype.init = function (chart, options) {
  24073. // Store references
  24074. this.options = options;
  24075. this.chart = chart;
  24076. // Do we need to handle click on a touch device?
  24077. this.runChartClick =
  24078. options.chart.events &&
  24079. !!options.chart.events.click;
  24080. this.pinchDown = [];
  24081. this.lastValidTouch = {};
  24082. if (Tooltip) {
  24083. /**
  24084. * Tooltip object for points of series.
  24085. *
  24086. * @name Highcharts.Chart#tooltip
  24087. * @type {Highcharts.Tooltip}
  24088. */
  24089. chart.tooltip = new Tooltip(chart, options.tooltip);
  24090. this.followTouchMove = pick(options.tooltip.followTouchMove, true);
  24091. }
  24092. this.setDOMEvents();
  24093. };
  24094. /**
  24095. * Takes a browser event object and extends it with custom Highcharts
  24096. * properties `chartX` and `chartY` in order to work on the internal
  24097. * coordinate system.
  24098. *
  24099. * @function Highcharts.Pointer#normalize
  24100. *
  24101. * @param {global.MouseEvent|global.PointerEvent|global.TouchEvent} e
  24102. * Event object in standard browsers.
  24103. *
  24104. * @param {Highcharts.OffsetObject} [chartPosition]
  24105. * Additional chart offset.
  24106. *
  24107. * @return {Highcharts.PointerEventObject}
  24108. * A browser event with extended properties `chartX` and `chartY`.
  24109. */
  24110. Pointer.prototype.normalize = function (e, chartPosition) {
  24111. var touches = e.touches;
  24112. // iOS (#2757)
  24113. var ePos = (touches ?
  24114. touches.length ?
  24115. touches.item(0) :
  24116. (pick(// #13534
  24117. touches.changedTouches,
  24118. e.changedTouches))[0] :
  24119. e);
  24120. // Get mouse position
  24121. if (!chartPosition) {
  24122. chartPosition = this.getChartPosition();
  24123. }
  24124. var chartX = ePos.pageX - chartPosition.left,
  24125. chartY = ePos.pageY - chartPosition.top;
  24126. // #11329 - when there is scaling on a parent element, we need to take
  24127. // this into account
  24128. var containerScaling = this.chart.containerScaling;
  24129. if (containerScaling) {
  24130. chartX /= containerScaling.scaleX;
  24131. chartY /= containerScaling.scaleY;
  24132. }
  24133. return extend(e, {
  24134. chartX: Math.round(chartX),
  24135. chartY: Math.round(chartY)
  24136. });
  24137. };
  24138. /**
  24139. * @private
  24140. * @function Highcharts.Pointer#onContainerClick
  24141. */
  24142. Pointer.prototype.onContainerClick = function (e) {
  24143. var chart = this.chart;
  24144. var hoverPoint = chart.hoverPoint;
  24145. var pEvt = this.normalize(e);
  24146. var plotLeft = chart.plotLeft;
  24147. var plotTop = chart.plotTop;
  24148. if (!chart.cancelClick) {
  24149. // On tracker click, fire the series and point events. #783, #1583
  24150. if (hoverPoint &&
  24151. this.inClass(pEvt.target, 'highcharts-tracker')) {
  24152. // the series click event
  24153. fireEvent(hoverPoint.series, 'click', extend(pEvt, {
  24154. point: hoverPoint
  24155. }));
  24156. // the point click event
  24157. if (chart.hoverPoint) { // it may be destroyed (#1844)
  24158. hoverPoint.firePointEvent('click', pEvt);
  24159. }
  24160. // When clicking outside a tracker, fire a chart event
  24161. }
  24162. else {
  24163. extend(pEvt, this.getCoordinates(pEvt));
  24164. // fire a click event in the chart
  24165. if (chart.isInsidePlot((pEvt.chartX - plotLeft), (pEvt.chartY - plotTop))) {
  24166. fireEvent(chart, 'click', pEvt);
  24167. }
  24168. }
  24169. }
  24170. };
  24171. /**
  24172. * @private
  24173. * @function Highcharts.Pointer#onContainerMouseDown
  24174. *
  24175. * @param {global.MouseEvent} e
  24176. */
  24177. Pointer.prototype.onContainerMouseDown = function (e) {
  24178. var isPrimaryButton = ((e.buttons || e.button) & 1) === 1;
  24179. // Normalize before the 'if' for the legacy IE (#7850)
  24180. e = this.normalize(e);
  24181. // #11635, Firefox does not reliable fire move event after click scroll
  24182. if (H.isFirefox &&
  24183. e.button !== 0) {
  24184. this.onContainerMouseMove(e);
  24185. }
  24186. // #11635, limiting to primary button (incl. IE 8 support)
  24187. if (typeof e.button === 'undefined' ||
  24188. isPrimaryButton) {
  24189. this.zoomOption(e);
  24190. // #295, #13737 solve conflict between container drag and chart zoom
  24191. if (isPrimaryButton &&
  24192. e.preventDefault) {
  24193. e.preventDefault();
  24194. }
  24195. this.dragStart(e);
  24196. }
  24197. };
  24198. /**
  24199. * When mouse leaves the container, hide the tooltip.
  24200. *
  24201. * @private
  24202. * @function Highcharts.Pointer#onContainerMouseLeave
  24203. *
  24204. * @param {global.MouseEvent} e
  24205. *
  24206. * @return {void}
  24207. */
  24208. Pointer.prototype.onContainerMouseLeave = function (e) {
  24209. var chart = charts[pick(H.hoverChartIndex, -1)];
  24210. var tooltip = this.chart.tooltip;
  24211. e = this.normalize(e);
  24212. // #4886, MS Touch end fires mouseleave but with no related target
  24213. if (chart &&
  24214. (e.relatedTarget || e.toElement)) {
  24215. chart.pointer.reset();
  24216. // Also reset the chart position, used in #149 fix
  24217. chart.pointer.chartPosition = void 0;
  24218. }
  24219. if ( // #11635, Firefox wheel scroll does not fire out events consistently
  24220. tooltip &&
  24221. !tooltip.isHidden) {
  24222. this.reset();
  24223. }
  24224. };
  24225. /**
  24226. * When mouse enters the container, delete pointer's chartPosition.
  24227. *
  24228. * @private
  24229. * @function Highcharts.Pointer#onContainerMouseEnter
  24230. *
  24231. * @param {global.MouseEvent} e
  24232. *
  24233. * @return {void}
  24234. */
  24235. Pointer.prototype.onContainerMouseEnter = function (e) {
  24236. delete this.chartPosition;
  24237. };
  24238. /**
  24239. * The mousemove, touchmove and touchstart event handler
  24240. *
  24241. * @private
  24242. * @function Highcharts.Pointer#onContainerMouseMove
  24243. *
  24244. * @param {global.MouseEvent} e
  24245. *
  24246. * @return {void}
  24247. */
  24248. Pointer.prototype.onContainerMouseMove = function (e) {
  24249. var chart = this.chart;
  24250. var pEvt = this.normalize(e);
  24251. this.setHoverChartIndex();
  24252. // In IE8 we apparently need this returnValue set to false in order to
  24253. // avoid text being selected. But in Chrome, e.returnValue is prevented,
  24254. // plus we don't need to run e.preventDefault to prevent selected text
  24255. // in modern browsers. So we set it conditionally. Remove it when IE8 is
  24256. // no longer needed. #2251, #3224.
  24257. if (!pEvt.preventDefault) {
  24258. pEvt.returnValue = false;
  24259. }
  24260. if (chart.mouseIsDown === 'mousedown') {
  24261. this.drag(pEvt);
  24262. }
  24263. // Show the tooltip and run mouse over events (#977)
  24264. if (!chart.openMenu &&
  24265. (this.inClass(pEvt.target, 'highcharts-tracker') ||
  24266. chart.isInsidePlot((pEvt.chartX - chart.plotLeft), (pEvt.chartY - chart.plotTop)))) {
  24267. this.runPointActions(pEvt);
  24268. }
  24269. };
  24270. /**
  24271. * @private
  24272. * @function Highcharts.Pointer#onDocumentTouchEnd
  24273. *
  24274. * @param {Highcharts.PointerEventObject} e
  24275. *
  24276. * @return {void}
  24277. */
  24278. Pointer.prototype.onDocumentTouchEnd = function (e) {
  24279. if (charts[H.hoverChartIndex]) {
  24280. charts[H.hoverChartIndex].pointer.drop(e);
  24281. }
  24282. };
  24283. /**
  24284. * @private
  24285. * @function Highcharts.Pointer#onContainerTouchMove
  24286. *
  24287. * @param {Highcharts.PointerEventObject} e
  24288. *
  24289. * @return {void}
  24290. */
  24291. Pointer.prototype.onContainerTouchMove = function (e) {
  24292. this.touch(e);
  24293. };
  24294. /**
  24295. * @private
  24296. * @function Highcharts.Pointer#onContainerTouchStart
  24297. *
  24298. * @param {Highcharts.PointerEventObject} e
  24299. *
  24300. * @return {void}
  24301. */
  24302. Pointer.prototype.onContainerTouchStart = function (e) {
  24303. this.zoomOption(e);
  24304. this.touch(e, true);
  24305. };
  24306. /**
  24307. * Special handler for mouse move that will hide the tooltip when the mouse
  24308. * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
  24309. * always fire.
  24310. *
  24311. * @private
  24312. * @function Highcharts.Pointer#onDocumentMouseMove
  24313. *
  24314. * @param {global.MouseEvent} e
  24315. *
  24316. * @return {void}
  24317. */
  24318. Pointer.prototype.onDocumentMouseMove = function (e) {
  24319. var chart = this.chart;
  24320. var chartPosition = this.chartPosition;
  24321. var pEvt = this.normalize(e,
  24322. chartPosition);
  24323. var tooltip = chart.tooltip;
  24324. // If we're outside, hide the tooltip
  24325. if (chartPosition &&
  24326. (!tooltip ||
  24327. !tooltip.isStickyOnContact()) &&
  24328. !chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop) &&
  24329. !this.inClass(pEvt.target, 'highcharts-tracker')) {
  24330. this.reset();
  24331. }
  24332. };
  24333. /**
  24334. * @private
  24335. * @function Highcharts.Pointer#onDocumentMouseUp
  24336. *
  24337. * @param {global.MouseEvent} e
  24338. *
  24339. * @return {void}
  24340. */
  24341. Pointer.prototype.onDocumentMouseUp = function (e) {
  24342. var chart = charts[pick(H.hoverChartIndex, -1)];
  24343. if (chart) {
  24344. chart.pointer.drop(e);
  24345. }
  24346. };
  24347. /**
  24348. * Handle touch events with two touches
  24349. *
  24350. * @private
  24351. * @function Highcharts.Pointer#pinch
  24352. *
  24353. * @param {Highcharts.PointerEventObject} e
  24354. *
  24355. * @return {void}
  24356. */
  24357. Pointer.prototype.pinch = function (e) {
  24358. var self = this,
  24359. chart = self.chart,
  24360. pinchDown = self.pinchDown,
  24361. touches = (e.touches || []),
  24362. touchesLength = touches.length,
  24363. lastValidTouch = self.lastValidTouch,
  24364. hasZoom = self.hasZoom,
  24365. selectionMarker = self.selectionMarker,
  24366. transform = {},
  24367. fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, 'highcharts-tracker') &&
  24368. chart.runTrackerClick) ||
  24369. self.runChartClick),
  24370. clip = {};
  24371. // Don't initiate panning until the user has pinched. This prevents us
  24372. // from blocking page scrolling as users scroll down a long page
  24373. // (#4210).
  24374. if (touchesLength > 1) {
  24375. self.initiated = true;
  24376. }
  24377. // On touch devices, only proceed to trigger click if a handler is
  24378. // defined
  24379. if (hasZoom && self.initiated && !fireClickEvent) {
  24380. e.preventDefault();
  24381. }
  24382. // Normalize each touch
  24383. [].map.call(touches, function (e) {
  24384. return self.normalize(e);
  24385. });
  24386. // Register the touch start position
  24387. if (e.type === 'touchstart') {
  24388. [].forEach.call(touches, function (e, i) {
  24389. pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
  24390. });
  24391. lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
  24392. pinchDown[1].chartX];
  24393. lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
  24394. pinchDown[1].chartY];
  24395. // Identify the data bounds in pixels
  24396. chart.axes.forEach(function (axis) {
  24397. if (axis.zoomEnabled) {
  24398. var bounds = chart.bounds[axis.horiz ? 'h' : 'v'],
  24399. minPixelPadding = axis.minPixelPadding,
  24400. min = axis.toPixels(Math.min(pick(axis.options.min,
  24401. axis.dataMin),
  24402. axis.dataMin)),
  24403. max = axis.toPixels(Math.max(pick(axis.options.max,
  24404. axis.dataMax),
  24405. axis.dataMax)),
  24406. absMin = Math.min(min,
  24407. max),
  24408. absMax = Math.max(min,
  24409. max);
  24410. // Store the bounds for use in the touchmove handler
  24411. bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
  24412. bounds.max = Math.max(axis.pos + axis.len, absMax + minPixelPadding);
  24413. }
  24414. });
  24415. self.res = true; // reset on next move
  24416. // Optionally move the tooltip on touchmove
  24417. }
  24418. else if (self.followTouchMove && touchesLength === 1) {
  24419. this.runPointActions(self.normalize(e));
  24420. // Event type is touchmove, handle panning and pinching
  24421. }
  24422. else if (pinchDown.length) { // can be 0 when releasing, if touchend
  24423. // fires first
  24424. // Set the marker
  24425. if (!selectionMarker) {
  24426. self.selectionMarker = selectionMarker = extend({
  24427. destroy: noop,
  24428. touch: true
  24429. }, chart.plotBox);
  24430. }
  24431. self.pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  24432. self.hasPinched = hasZoom;
  24433. // Scale and translate the groups to provide visual feedback during
  24434. // pinching
  24435. self.scaleGroups(transform, clip);
  24436. if (self.res) {
  24437. self.res = false;
  24438. this.reset(false, 0);
  24439. }
  24440. }
  24441. };
  24442. /**
  24443. * Run translation operations
  24444. *
  24445. * @private
  24446. * @function Highcharts.Pointer#pinchTranslate
  24447. *
  24448. * @param {Array<*>} pinchDown
  24449. *
  24450. * @param {Array<Highcharts.PointerEventObject>} touches
  24451. *
  24452. * @param {*} transform
  24453. *
  24454. * @param {*} selectionMarker
  24455. *
  24456. * @param {*} clip
  24457. *
  24458. * @param {*} lastValidTouch
  24459. *
  24460. * @return {void}
  24461. */
  24462. Pointer.prototype.pinchTranslate = function (pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
  24463. if (this.zoomHor) {
  24464. this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  24465. }
  24466. if (this.zoomVert) {
  24467. this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  24468. }
  24469. };
  24470. /**
  24471. * Run translation operations for each direction (horizontal and vertical)
  24472. * independently.
  24473. *
  24474. * @private
  24475. * @function Highcharts.Pointer#pinchTranslateDirection
  24476. *
  24477. * @param {boolean} horiz
  24478. *
  24479. * @param {Array<*>} pinchDown
  24480. *
  24481. * @param {Array<Highcharts.PointerEventObject>} touches
  24482. *
  24483. * @param {*} transform
  24484. *
  24485. * @param {*} selectionMarker
  24486. *
  24487. * @param {*} clip
  24488. *
  24489. * @param {*} lastValidTouch
  24490. *
  24491. * @param {number|undefined} [forcedScale=1]
  24492. *
  24493. * @return {void}
  24494. */
  24495. Pointer.prototype.pinchTranslateDirection = function (horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) {
  24496. var chart = this.chart, xy = horiz ? 'x' : 'y', XY = horiz ? 'X' : 'Y', sChartXY = ('chart' + XY), wh = horiz ? 'width' : 'height', plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')], selectionWH, selectionXY, clipXY, scale = forcedScale || 1, inverted = chart.inverted, bounds = chart.bounds[horiz ? 'h' : 'v'], singleTouch = pinchDown.length === 1, touch0Start = pinchDown[0][sChartXY], touch0Now = touches[0][sChartXY], touch1Start = !singleTouch && pinchDown[1][sChartXY], touch1Now = !singleTouch && touches[1][sChartXY], outOfBounds, transformScale, scaleKey, setScale = function () {
  24497. // Don't zoom if fingers are too close on this axis
  24498. if (typeof touch1Now === 'number' &&
  24499. Math.abs(touch0Start - touch1Start) > 20) {
  24500. scale = forcedScale ||
  24501. Math.abs(touch0Now - touch1Now) /
  24502. Math.abs(touch0Start - touch1Start);
  24503. }
  24504. clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
  24505. selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
  24506. };
  24507. // Set the scale, first pass
  24508. setScale();
  24509. // The clip position (x or y) is altered if out of bounds, the selection
  24510. // position is not
  24511. selectionXY = clipXY;
  24512. // Out of bounds
  24513. if (selectionXY < bounds.min) {
  24514. selectionXY = bounds.min;
  24515. outOfBounds = true;
  24516. }
  24517. else if (selectionXY + selectionWH > bounds.max) {
  24518. selectionXY = bounds.max - selectionWH;
  24519. outOfBounds = true;
  24520. }
  24521. // Is the chart dragged off its bounds, determined by dataMin and
  24522. // dataMax?
  24523. if (outOfBounds) {
  24524. // Modify the touchNow position in order to create an elastic drag
  24525. // movement. This indicates to the user that the chart is responsive
  24526. // but can't be dragged further.
  24527. touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
  24528. if (typeof touch1Now === 'number') {
  24529. touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
  24530. }
  24531. // Set the scale, second pass to adapt to the modified touchNow
  24532. // positions
  24533. setScale();
  24534. }
  24535. else {
  24536. lastValidTouch[xy] = [touch0Now, touch1Now];
  24537. }
  24538. // Set geometry for clipping, selection and transformation
  24539. if (!inverted) {
  24540. clip[xy] = clipXY - plotLeftTop;
  24541. clip[wh] = selectionWH;
  24542. }
  24543. scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
  24544. transformScale = inverted ? 1 / scale : scale;
  24545. selectionMarker[wh] = selectionWH;
  24546. selectionMarker[xy] = selectionXY;
  24547. transform[scaleKey] = scale;
  24548. transform['translate' + XY] = (transformScale * plotLeftTop) +
  24549. (touch0Now - (transformScale * touch0Start));
  24550. };
  24551. /**
  24552. * Reset the tracking by hiding the tooltip, the hover series state and the
  24553. * hover point
  24554. *
  24555. * @function Highcharts.Pointer#reset
  24556. *
  24557. * @param {boolean} [allowMove]
  24558. * Instead of destroying the tooltip altogether, allow moving it if
  24559. * possible.
  24560. *
  24561. * @param {number} [delay]
  24562. *
  24563. * @return {void}
  24564. */
  24565. Pointer.prototype.reset = function (allowMove, delay) {
  24566. var pointer = this,
  24567. chart = pointer.chart,
  24568. hoverSeries = chart.hoverSeries,
  24569. hoverPoint = chart.hoverPoint,
  24570. hoverPoints = chart.hoverPoints,
  24571. tooltip = chart.tooltip,
  24572. tooltipPoints = tooltip && tooltip.shared ?
  24573. hoverPoints :
  24574. hoverPoint;
  24575. // Check if the points have moved outside the plot area (#1003, #4736,
  24576. // #5101)
  24577. if (allowMove && tooltipPoints) {
  24578. splat(tooltipPoints).forEach(function (point) {
  24579. if (point.series.isCartesian &&
  24580. typeof point.plotX === 'undefined') {
  24581. allowMove = false;
  24582. }
  24583. });
  24584. }
  24585. // Just move the tooltip, #349
  24586. if (allowMove) {
  24587. if (tooltip && tooltipPoints && splat(tooltipPoints).length) {
  24588. tooltip.refresh(tooltipPoints);
  24589. if (tooltip.shared && hoverPoints) { // #8284
  24590. hoverPoints.forEach(function (point) {
  24591. point.setState(point.state, true);
  24592. if (point.series.isCartesian) {
  24593. if (point.series.xAxis.crosshair) {
  24594. point.series.xAxis
  24595. .drawCrosshair(null, point);
  24596. }
  24597. if (point.series.yAxis.crosshair) {
  24598. point.series.yAxis
  24599. .drawCrosshair(null, point);
  24600. }
  24601. }
  24602. });
  24603. }
  24604. else if (hoverPoint) { // #2500
  24605. hoverPoint.setState(hoverPoint.state, true);
  24606. chart.axes.forEach(function (axis) {
  24607. if (axis.crosshair &&
  24608. hoverPoint.series[axis.coll] === axis) {
  24609. axis.drawCrosshair(null, hoverPoint);
  24610. }
  24611. });
  24612. }
  24613. }
  24614. // Full reset
  24615. }
  24616. else {
  24617. if (hoverPoint) {
  24618. hoverPoint.onMouseOut();
  24619. }
  24620. if (hoverPoints) {
  24621. hoverPoints.forEach(function (point) {
  24622. point.setState();
  24623. });
  24624. }
  24625. if (hoverSeries) {
  24626. hoverSeries.onMouseOut();
  24627. }
  24628. if (tooltip) {
  24629. tooltip.hide(delay);
  24630. }
  24631. if (pointer.unDocMouseMove) {
  24632. pointer.unDocMouseMove = pointer.unDocMouseMove();
  24633. }
  24634. // Remove crosshairs
  24635. chart.axes.forEach(function (axis) {
  24636. axis.hideCrosshair();
  24637. });
  24638. pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
  24639. }
  24640. };
  24641. /**
  24642. * With line type charts with a single tracker, get the point closest to the
  24643. * mouse. Run Point.onMouseOver and display tooltip for the point or points.
  24644. *
  24645. * @private
  24646. * @function Highcharts.Pointer#runPointActions
  24647. *
  24648. * @param {global.Event} e
  24649. *
  24650. * @param {Highcharts.PointerEventObject} [p]
  24651. *
  24652. * @return {void}
  24653. *
  24654. * @fires Highcharts.Point#event:mouseOut
  24655. * @fires Highcharts.Point#event:mouseOver
  24656. */
  24657. Pointer.prototype.runPointActions = function (e, p) {
  24658. var pointer = this,
  24659. chart = pointer.chart,
  24660. series = chart.series,
  24661. tooltip = (chart.tooltip && chart.tooltip.options.enabled ?
  24662. chart.tooltip :
  24663. void 0),
  24664. shared = (tooltip ?
  24665. tooltip.shared :
  24666. false),
  24667. hoverPoint = p || chart.hoverPoint,
  24668. hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries,
  24669. // onMouseOver or already hovering a series with directTouch
  24670. isDirectTouch = (!e || e.type !== 'touchmove') && (!!p || ((hoverSeries && hoverSeries.directTouch) &&
  24671. pointer.isDirectTouch)),
  24672. hoverData = this.getHoverData(hoverPoint,
  24673. hoverSeries,
  24674. series,
  24675. isDirectTouch,
  24676. shared,
  24677. e),
  24678. useSharedTooltip,
  24679. followPointer,
  24680. anchor,
  24681. points;
  24682. // Update variables from hoverData.
  24683. hoverPoint = hoverData.hoverPoint;
  24684. points = hoverData.hoverPoints;
  24685. hoverSeries = hoverData.hoverSeries;
  24686. followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
  24687. useSharedTooltip = (shared &&
  24688. hoverSeries &&
  24689. !hoverSeries.noSharedTooltip);
  24690. // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
  24691. // #3926, #4200
  24692. if (hoverPoint &&
  24693. // !(hoverSeries && hoverSeries.directTouch) &&
  24694. (hoverPoint !== chart.hoverPoint || (tooltip && tooltip.isHidden))) {
  24695. (chart.hoverPoints || []).forEach(function (p) {
  24696. if (points.indexOf(p) === -1) {
  24697. p.setState();
  24698. }
  24699. });
  24700. // Set normal state to previous series
  24701. if (chart.hoverSeries !== hoverSeries) {
  24702. hoverSeries.onMouseOver();
  24703. }
  24704. pointer.applyInactiveState(points);
  24705. // Do mouseover on all points (#3919, #3985, #4410, #5622)
  24706. (points || []).forEach(function (p) {
  24707. p.setState('hover');
  24708. });
  24709. // If tracking is on series in stead of on each point,
  24710. // fire mouseOver on hover point. // #4448
  24711. if (chart.hoverPoint) {
  24712. chart.hoverPoint.firePointEvent('mouseOut');
  24713. }
  24714. // Hover point may have been destroyed in the event handlers (#7127)
  24715. if (!hoverPoint.series) {
  24716. return;
  24717. }
  24718. /**
  24719. * Contains all hovered points.
  24720. *
  24721. * @name Highcharts.Chart#hoverPoints
  24722. * @type {Array<Highcharts.Point>|null}
  24723. */
  24724. chart.hoverPoints = points;
  24725. /**
  24726. * Contains the original hovered point.
  24727. *
  24728. * @name Highcharts.Chart#hoverPoint
  24729. * @type {Highcharts.Point|null}
  24730. */
  24731. chart.hoverPoint = hoverPoint;
  24732. /**
  24733. * Hover state should not be lost when axis is updated (#12569)
  24734. * Axis.update runs pointer.reset which uses chart.hoverPoint.state
  24735. * to apply state which does not exist in hoverPoint yet.
  24736. * The mouseOver event should be triggered when hoverPoint
  24737. * is correct.
  24738. */
  24739. hoverPoint.firePointEvent('mouseOver');
  24740. // Draw tooltip if necessary
  24741. if (tooltip) {
  24742. tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
  24743. }
  24744. // Update positions (regardless of kdpoint or hoverPoint)
  24745. }
  24746. else if (followPointer && tooltip && !tooltip.isHidden) {
  24747. anchor = tooltip.getAnchor([{}], e);
  24748. tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
  24749. }
  24750. // Start the event listener to pick up the tooltip and crosshairs
  24751. if (!pointer.unDocMouseMove) {
  24752. pointer.unDocMouseMove = addEvent(chart.container.ownerDocument, 'mousemove', function (e) {
  24753. var chart = charts[H.hoverChartIndex];
  24754. if (chart) {
  24755. chart.pointer.onDocumentMouseMove(e);
  24756. }
  24757. });
  24758. }
  24759. // Issues related to crosshair #4927, #5269 #5066, #5658
  24760. chart.axes.forEach(function drawAxisCrosshair(axis) {
  24761. var snap = pick((axis.crosshair || {}).snap,
  24762. true);
  24763. var point;
  24764. if (snap) {
  24765. point = chart.hoverPoint; // #13002
  24766. if (!point || point.series[axis.coll] !== axis) {
  24767. point = find(points, function (p) {
  24768. return p.series[axis.coll] === axis;
  24769. });
  24770. }
  24771. }
  24772. // Axis has snapping crosshairs, and one of the hover points belongs
  24773. // to axis. Always call drawCrosshair when it is not snap.
  24774. if (point || !snap) {
  24775. axis.drawCrosshair(e, point);
  24776. // Axis has snapping crosshairs, but no hover point belongs to axis
  24777. }
  24778. else {
  24779. axis.hideCrosshair();
  24780. }
  24781. });
  24782. };
  24783. /**
  24784. * Scale series groups to a certain scale and translation.
  24785. *
  24786. * @private
  24787. * @function Highcharts.Pointer#scaleGroups
  24788. *
  24789. * @param {Highcharts.SeriesPlotBoxObject} [attribs]
  24790. *
  24791. * @param {boolean} [clip]
  24792. *
  24793. * @return {void}
  24794. */
  24795. Pointer.prototype.scaleGroups = function (attribs, clip) {
  24796. var chart = this.chart,
  24797. seriesAttribs;
  24798. // Scale each series
  24799. chart.series.forEach(function (series) {
  24800. seriesAttribs = attribs || series.getPlotBox(); // #1701
  24801. if (series.xAxis && series.xAxis.zoomEnabled && series.group) {
  24802. series.group.attr(seriesAttribs);
  24803. if (series.markerGroup) {
  24804. series.markerGroup.attr(seriesAttribs);
  24805. series.markerGroup.clip(clip ? chart.clipRect : null);
  24806. }
  24807. if (series.dataLabelsGroup) {
  24808. series.dataLabelsGroup.attr(seriesAttribs);
  24809. }
  24810. }
  24811. });
  24812. // Clip
  24813. chart.clipRect.attr(clip || chart.clipBox);
  24814. };
  24815. /**
  24816. * Set the JS DOM events on the container and document. This method should
  24817. * contain a one-to-one assignment between methods and their handlers. Any
  24818. * advanced logic should be moved to the handler reflecting the event's
  24819. * name.
  24820. *
  24821. * @private
  24822. * @function Highcharts.Pointer#setDOMEvents
  24823. *
  24824. * @return {void}
  24825. */
  24826. Pointer.prototype.setDOMEvents = function () {
  24827. var container = this.chart.container,
  24828. ownerDoc = container.ownerDocument;
  24829. container.onmousedown = this.onContainerMouseDown.bind(this);
  24830. container.onmousemove = this.onContainerMouseMove.bind(this);
  24831. container.onclick = this.onContainerClick.bind(this);
  24832. this.unbindContainerMouseEnter = addEvent(container, 'mouseenter', this.onContainerMouseEnter.bind(this));
  24833. this.unbindContainerMouseLeave = addEvent(container, 'mouseleave', this.onContainerMouseLeave.bind(this));
  24834. if (!H.unbindDocumentMouseUp) {
  24835. H.unbindDocumentMouseUp = addEvent(ownerDoc, 'mouseup', this.onDocumentMouseUp.bind(this));
  24836. }
  24837. if (H.hasTouch) {
  24838. addEvent(container, 'touchstart', this.onContainerTouchStart.bind(this));
  24839. addEvent(container, 'touchmove', this.onContainerTouchMove.bind(this));
  24840. if (!H.unbindDocumentTouchEnd) {
  24841. H.unbindDocumentTouchEnd = addEvent(ownerDoc, 'touchend', this.onDocumentTouchEnd.bind(this));
  24842. }
  24843. }
  24844. };
  24845. /**
  24846. * Sets the index of the hovered chart and leaves the previous hovered
  24847. * chart, to reset states like tooltip.
  24848. *
  24849. * @private
  24850. * @function Highcharts.Pointer#setHoverChartIndex
  24851. */
  24852. Pointer.prototype.setHoverChartIndex = function () {
  24853. var chart = this.chart;
  24854. var hoverChart = H.charts[pick(H.hoverChartIndex, -1)];
  24855. if (hoverChart &&
  24856. hoverChart !== chart) {
  24857. hoverChart.pointer.onContainerMouseLeave({ relatedTarget: true });
  24858. }
  24859. if (!hoverChart ||
  24860. !hoverChart.mouseIsDown) {
  24861. H.hoverChartIndex = chart.index;
  24862. }
  24863. };
  24864. /**
  24865. * General touch handler shared by touchstart and touchmove.
  24866. *
  24867. * @private
  24868. * @function Highcharts.Pointer#touch
  24869. *
  24870. * @param {Highcharts.PointerEventObject} e
  24871. *
  24872. * @param {boolean} [start]
  24873. *
  24874. * @return {void}
  24875. */
  24876. Pointer.prototype.touch = function (e, start) {
  24877. var chart = this.chart,
  24878. hasMoved,
  24879. pinchDown,
  24880. isInside;
  24881. this.setHoverChartIndex();
  24882. if (e.touches.length === 1) {
  24883. e = this.normalize(e);
  24884. isInside = chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop);
  24885. if (isInside && !chart.openMenu) {
  24886. // Run mouse events and display tooltip etc
  24887. if (start) {
  24888. this.runPointActions(e);
  24889. }
  24890. // Android fires touchmove events after the touchstart even if
  24891. // the finger hasn't moved, or moved only a pixel or two. In iOS
  24892. // however, the touchmove doesn't fire unless the finger moves
  24893. // more than ~4px. So we emulate this behaviour in Android by
  24894. // checking how much it moved, and cancelling on small
  24895. // distances. #3450.
  24896. if (e.type === 'touchmove') {
  24897. pinchDown = this.pinchDown;
  24898. hasMoved = pinchDown[0] ? Math.sqrt(// #5266
  24899. Math.pow(pinchDown[0].chartX - e.chartX, 2) +
  24900. Math.pow(pinchDown[0].chartY - e.chartY, 2)) >= 4 : false;
  24901. }
  24902. if (pick(hasMoved, true)) {
  24903. this.pinch(e);
  24904. }
  24905. }
  24906. else if (start) {
  24907. // Hide the tooltip on touching outside the plot area (#1203)
  24908. this.reset();
  24909. }
  24910. }
  24911. else if (e.touches.length === 2) {
  24912. this.pinch(e);
  24913. }
  24914. };
  24915. /**
  24916. * Resolve the zoomType option, this is reset on all touch start and mouse
  24917. * down events.
  24918. *
  24919. * @private
  24920. * @function Highcharts.Pointer#zoomOption
  24921. *
  24922. * @param {global.Event} e
  24923. * Event object.
  24924. *
  24925. * @param {void}
  24926. */
  24927. Pointer.prototype.zoomOption = function (e) {
  24928. var chart = this.chart,
  24929. options = chart.options.chart,
  24930. zoomType = options.zoomType || '',
  24931. inverted = chart.inverted,
  24932. zoomX,
  24933. zoomY;
  24934. // Look for the pinchType option
  24935. if (/touch/.test(e.type)) {
  24936. zoomType = pick(options.pinchType, zoomType);
  24937. }
  24938. this.zoomX = zoomX = /x/.test(zoomType);
  24939. this.zoomY = zoomY = /y/.test(zoomType);
  24940. this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
  24941. this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
  24942. this.hasZoom = zoomX || zoomY;
  24943. };
  24944. return Pointer;
  24945. }());
  24946. H.Pointer = Pointer;
  24947. return Pointer;
  24948. });
  24949. _registerModule(_modules, 'Core/MSPointer.js', [_modules['Core/Globals.js'], _modules['Core/Pointer.js'], _modules['Core/Utilities.js']], function (H, Pointer, U) {
  24950. /* *
  24951. *
  24952. * (c) 2010-2020 Torstein Honsi
  24953. *
  24954. * License: www.highcharts.com/license
  24955. *
  24956. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  24957. *
  24958. * */
  24959. var __extends = (this && this.__extends) || (function () {
  24960. var extendStatics = function (d,
  24961. b) {
  24962. extendStatics = Object.setPrototypeOf ||
  24963. ({ __proto__: [] } instanceof Array && function (d,
  24964. b) { d.__proto__ = b; }) ||
  24965. function (d,
  24966. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  24967. return extendStatics(d, b);
  24968. };
  24969. return function (d, b) {
  24970. extendStatics(d, b);
  24971. function __() { this.constructor = d; }
  24972. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  24973. };
  24974. })();
  24975. var charts = H.charts,
  24976. doc = H.doc,
  24977. noop = H.noop,
  24978. win = H.win;
  24979. var addEvent = U.addEvent,
  24980. css = U.css,
  24981. objectEach = U.objectEach,
  24982. removeEvent = U.removeEvent;
  24983. /* globals MSPointerEvent, PointerEvent */
  24984. // The touches object keeps track of the points being touched at all times
  24985. var touches = {};
  24986. var hasPointerEvent = !!win.PointerEvent;
  24987. /* eslint-disable valid-jsdoc */
  24988. /** @private */
  24989. function getWebkitTouches() {
  24990. var fake = [];
  24991. fake.item = function (i) {
  24992. return this[i];
  24993. };
  24994. objectEach(touches, function (touch) {
  24995. fake.push({
  24996. pageX: touch.pageX,
  24997. pageY: touch.pageY,
  24998. target: touch.target
  24999. });
  25000. });
  25001. return fake;
  25002. }
  25003. /** @private */
  25004. function translateMSPointer(e, method, wktype, func) {
  25005. var p;
  25006. if ((e.pointerType === 'touch' ||
  25007. e.pointerType === e.MSPOINTER_TYPE_TOUCH) && charts[H.hoverChartIndex]) {
  25008. func(e);
  25009. p = charts[H.hoverChartIndex].pointer;
  25010. p[method]({
  25011. type: wktype,
  25012. target: e.currentTarget,
  25013. preventDefault: noop,
  25014. touches: getWebkitTouches()
  25015. });
  25016. }
  25017. }
  25018. /** @private */
  25019. var MSPointer = /** @class */ (function (_super) {
  25020. __extends(MSPointer, _super);
  25021. function MSPointer() {
  25022. return _super !== null && _super.apply(this, arguments) || this;
  25023. }
  25024. /* *
  25025. *
  25026. * Functions
  25027. *
  25028. * */
  25029. /**
  25030. * Add or remove the MS Pointer specific events
  25031. *
  25032. * @private
  25033. * @function Highcharts.Pointer#batchMSEvents
  25034. *
  25035. * @param {Function} fn
  25036. *
  25037. * @return {void}
  25038. */
  25039. MSPointer.prototype.batchMSEvents = function (fn) {
  25040. fn(this.chart.container, hasPointerEvent ? 'pointerdown' : 'MSPointerDown', this.onContainerPointerDown);
  25041. fn(this.chart.container, hasPointerEvent ? 'pointermove' : 'MSPointerMove', this.onContainerPointerMove);
  25042. fn(doc, hasPointerEvent ? 'pointerup' : 'MSPointerUp', this.onDocumentPointerUp);
  25043. };
  25044. // Destroy MS events also
  25045. MSPointer.prototype.destroy = function () {
  25046. this.batchMSEvents(removeEvent);
  25047. _super.prototype.destroy.call(this);
  25048. };
  25049. // Disable default IE actions for pinch and such on chart element
  25050. MSPointer.prototype.init = function (chart, options) {
  25051. _super.prototype.init.call(this, chart, options);
  25052. if (this.hasZoom) { // #4014
  25053. css(chart.container, {
  25054. '-ms-touch-action': 'none',
  25055. 'touch-action': 'none'
  25056. });
  25057. }
  25058. };
  25059. /**
  25060. * @private
  25061. * @function Highcharts.Pointer#onContainerPointerDown
  25062. *
  25063. * @param {Highcharts.PointerEventObject} e
  25064. *
  25065. * @return {void}
  25066. */
  25067. MSPointer.prototype.onContainerPointerDown = function (e) {
  25068. translateMSPointer(e, 'onContainerTouchStart', 'touchstart', function (e) {
  25069. touches[e.pointerId] = {
  25070. pageX: e.pageX,
  25071. pageY: e.pageY,
  25072. target: e.currentTarget
  25073. };
  25074. });
  25075. };
  25076. /**
  25077. * @private
  25078. * @function Highcharts.Pointer#onContainerPointerMove
  25079. *
  25080. * @param {Highcharts.PointerEventObject} e
  25081. *
  25082. * @return {void}
  25083. */
  25084. MSPointer.prototype.onContainerPointerMove = function (e) {
  25085. translateMSPointer(e, 'onContainerTouchMove', 'touchmove', function (e) {
  25086. touches[e.pointerId] = ({ pageX: e.pageX, pageY: e.pageY });
  25087. if (!touches[e.pointerId].target) {
  25088. touches[e.pointerId].target = e.currentTarget;
  25089. }
  25090. });
  25091. };
  25092. /**
  25093. * @private
  25094. * @function Highcharts.Pointer#onDocumentPointerUp
  25095. *
  25096. * @param {Highcharts.PointerEventObject} e
  25097. *
  25098. * @return {void}
  25099. */
  25100. MSPointer.prototype.onDocumentPointerUp = function (e) {
  25101. translateMSPointer(e, 'onDocumentTouchEnd', 'touchend', function (e) {
  25102. delete touches[e.pointerId];
  25103. });
  25104. };
  25105. // Add IE specific touch events to chart
  25106. MSPointer.prototype.setDOMEvents = function () {
  25107. _super.prototype.setDOMEvents.call(this);
  25108. if (this.hasZoom || this.followTouchMove) {
  25109. this.batchMSEvents(addEvent);
  25110. }
  25111. };
  25112. return MSPointer;
  25113. }(Pointer));
  25114. return MSPointer;
  25115. });
  25116. _registerModule(_modules, 'Core/Legend.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  25117. /* *
  25118. *
  25119. * (c) 2010-2020 Torstein Honsi
  25120. *
  25121. * License: www.highcharts.com/license
  25122. *
  25123. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  25124. *
  25125. * */
  25126. /**
  25127. * Gets fired when the legend item belonging to a point is clicked. The default
  25128. * action is to toggle the visibility of the point. This can be prevented by
  25129. * returning `false` or calling `event.preventDefault()`.
  25130. *
  25131. * @callback Highcharts.PointLegendItemClickCallbackFunction
  25132. *
  25133. * @param {Highcharts.Point} this
  25134. * The point on which the event occured.
  25135. *
  25136. * @param {Highcharts.PointLegendItemClickEventObject} event
  25137. * The event that occured.
  25138. */
  25139. /**
  25140. * Information about the legend click event.
  25141. *
  25142. * @interface Highcharts.PointLegendItemClickEventObject
  25143. */ /**
  25144. * Related browser event.
  25145. * @name Highcharts.PointLegendItemClickEventObject#browserEvent
  25146. * @type {Highcharts.PointerEvent}
  25147. */ /**
  25148. * Prevent the default action of toggle the visibility of the point.
  25149. * @name Highcharts.PointLegendItemClickEventObject#preventDefault
  25150. * @type {Function}
  25151. */ /**
  25152. * Related point.
  25153. * @name Highcharts.PointLegendItemClickEventObject#target
  25154. * @type {Highcharts.Point}
  25155. */ /**
  25156. * Event type.
  25157. * @name Highcharts.PointLegendItemClickEventObject#type
  25158. * @type {"legendItemClick"}
  25159. */
  25160. /**
  25161. * Gets fired when the legend item belonging to a series is clicked. The default
  25162. * action is to toggle the visibility of the series. This can be prevented by
  25163. * returning `false` or calling `event.preventDefault()`.
  25164. *
  25165. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  25166. *
  25167. * @param {Highcharts.Series} this
  25168. * The series where the event occured.
  25169. *
  25170. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  25171. * The event that occured.
  25172. */
  25173. /**
  25174. * Information about the legend click event.
  25175. *
  25176. * @interface Highcharts.SeriesLegendItemClickEventObject
  25177. */ /**
  25178. * Related browser event.
  25179. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  25180. * @type {Highcharts.PointerEvent}
  25181. */ /**
  25182. * Prevent the default action of toggle the visibility of the series.
  25183. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  25184. * @type {Function}
  25185. */ /**
  25186. * Related series.
  25187. * @name Highcharts.SeriesLegendItemClickEventObject#target
  25188. * @type {Highcharts.Series}
  25189. */ /**
  25190. * Event type.
  25191. * @name Highcharts.SeriesLegendItemClickEventObject#type
  25192. * @type {"legendItemClick"}
  25193. */
  25194. var addEvent = U.addEvent,
  25195. animObject = U.animObject,
  25196. css = U.css,
  25197. defined = U.defined,
  25198. discardElement = U.discardElement,
  25199. find = U.find,
  25200. fireEvent = U.fireEvent,
  25201. format = U.format,
  25202. isNumber = U.isNumber,
  25203. merge = U.merge,
  25204. pick = U.pick,
  25205. relativeLength = U.relativeLength,
  25206. setAnimation = U.setAnimation,
  25207. stableSort = U.stableSort,
  25208. syncTimeout = U.syncTimeout,
  25209. wrap = U.wrap;
  25210. var isFirefox = H.isFirefox,
  25211. marginNames = H.marginNames,
  25212. win = H.win;
  25213. /* eslint-disable no-invalid-this, valid-jsdoc */
  25214. /**
  25215. * The overview of the chart's series. The legend object is instanciated
  25216. * internally in the chart constructor, and is available from the `chart.legend`
  25217. * property. Each chart has only one legend.
  25218. *
  25219. * @class
  25220. * @name Highcharts.Legend
  25221. *
  25222. * @param {Highcharts.Chart} chart
  25223. * The chart instance.
  25224. *
  25225. * @param {Highcharts.LegendOptions} options
  25226. * Legend options.
  25227. */
  25228. var Legend = /** @class */ (function () {
  25229. /* *
  25230. *
  25231. * Constructors
  25232. *
  25233. * */
  25234. function Legend(chart, options) {
  25235. /* *
  25236. *
  25237. * Properties
  25238. *
  25239. * */
  25240. this.allItems = [];
  25241. this.box = void 0;
  25242. this.contentGroup = void 0;
  25243. this.display = false;
  25244. this.group = void 0;
  25245. this.initialItemY = 0;
  25246. this.itemHeight = 0;
  25247. this.itemMarginBottom = 0;
  25248. this.itemMarginTop = 0;
  25249. this.itemX = 0;
  25250. this.itemY = 0;
  25251. this.lastItemY = 0;
  25252. this.lastLineHeight = 0;
  25253. this.legendHeight = 0;
  25254. this.legendWidth = 0;
  25255. this.maxItemWidth = 0;
  25256. this.maxLegendWidth = 0;
  25257. this.offsetWidth = 0;
  25258. this.options = {};
  25259. this.padding = 0;
  25260. this.pages = [];
  25261. this.proximate = false;
  25262. this.scrollGroup = void 0;
  25263. this.symbolHeight = 0;
  25264. this.symbolWidth = 0;
  25265. this.titleHeight = 0;
  25266. this.totalItemWidth = 0;
  25267. this.widthOption = 0;
  25268. this.chart = chart;
  25269. this.init(chart, options);
  25270. }
  25271. /* *
  25272. *
  25273. * Functions
  25274. *
  25275. * */
  25276. /**
  25277. * Initialize the legend.
  25278. *
  25279. * @private
  25280. * @function Highcharts.Legend#init
  25281. *
  25282. * @param {Highcharts.Chart} chart
  25283. * The chart instance.
  25284. *
  25285. * @param {Highcharts.LegendOptions} options
  25286. * Legend options.
  25287. */
  25288. Legend.prototype.init = function (chart, options) {
  25289. /**
  25290. * Chart of this legend.
  25291. *
  25292. * @readonly
  25293. * @name Highcharts.Legend#chart
  25294. * @type {Highcharts.Chart}
  25295. */
  25296. this.chart = chart;
  25297. this.setOptions(options);
  25298. if (options.enabled) {
  25299. // Render it
  25300. this.render();
  25301. // move checkboxes
  25302. addEvent(this.chart, 'endResize', function () {
  25303. this.legend.positionCheckboxes();
  25304. });
  25305. if (this.proximate) {
  25306. this.unchartrender = addEvent(this.chart, 'render', function () {
  25307. this.legend.proximatePositions();
  25308. this.legend.positionItems();
  25309. });
  25310. }
  25311. else if (this.unchartrender) {
  25312. this.unchartrender();
  25313. }
  25314. }
  25315. };
  25316. /**
  25317. * @private
  25318. * @function Highcharts.Legend#setOptions
  25319. * @param {Highcharts.LegendOptions} options
  25320. */
  25321. Legend.prototype.setOptions = function (options) {
  25322. var padding = pick(options.padding, 8);
  25323. /**
  25324. * Legend options.
  25325. *
  25326. * @readonly
  25327. * @name Highcharts.Legend#options
  25328. * @type {Highcharts.LegendOptions}
  25329. */
  25330. this.options = options;
  25331. if (!this.chart.styledMode) {
  25332. this.itemStyle = options.itemStyle;
  25333. this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle);
  25334. }
  25335. this.itemMarginTop = options.itemMarginTop || 0;
  25336. this.itemMarginBottom = options.itemMarginBottom || 0;
  25337. this.padding = padding;
  25338. this.initialItemY = padding - 5; // 5 is pixels above the text
  25339. this.symbolWidth = pick(options.symbolWidth, 16);
  25340. this.pages = [];
  25341. this.proximate = options.layout === 'proximate' && !this.chart.inverted;
  25342. this.baseline = void 0; // #12705: baseline has to be reset on every update
  25343. };
  25344. /**
  25345. * Update the legend with new options. Equivalent to running `chart.update`
  25346. * with a legend configuration option.
  25347. *
  25348. * @sample highcharts/legend/legend-update/
  25349. * Legend update
  25350. *
  25351. * @function Highcharts.Legend#update
  25352. *
  25353. * @param {Highcharts.LegendOptions} options
  25354. * Legend options.
  25355. *
  25356. * @param {boolean} [redraw=true]
  25357. * Whether to redraw the chart after the axis is altered. If doing more
  25358. * operations on the chart, it is a good idea to set redraw to false and
  25359. * call {@link Chart#redraw} after. Whether to redraw the chart.
  25360. *
  25361. * @fires Highcharts.Legends#event:afterUpdate
  25362. */
  25363. Legend.prototype.update = function (options, redraw) {
  25364. var chart = this.chart;
  25365. this.setOptions(merge(true, this.options, options));
  25366. this.destroy();
  25367. chart.isDirtyLegend = chart.isDirtyBox = true;
  25368. if (pick(redraw, true)) {
  25369. chart.redraw();
  25370. }
  25371. fireEvent(this, 'afterUpdate');
  25372. };
  25373. /**
  25374. * Set the colors for the legend item.
  25375. *
  25376. * @private
  25377. * @function Highcharts.Legend#colorizeItem
  25378. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  25379. * A Series or Point instance
  25380. * @param {boolean} [visible=false]
  25381. * Dimmed or colored
  25382. *
  25383. * @todo
  25384. * Make events official: Fires the event `afterColorizeItem`.
  25385. */
  25386. Legend.prototype.colorizeItem = function (item, visible) {
  25387. item.legendGroup[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
  25388. if (!this.chart.styledMode) {
  25389. var legend = this,
  25390. options = legend.options,
  25391. legendItem = item.legendItem,
  25392. legendLine = item.legendLine,
  25393. legendSymbol = item.legendSymbol,
  25394. hiddenColor = legend.itemHiddenStyle.color,
  25395. textColor = visible ?
  25396. options.itemStyle.color :
  25397. hiddenColor,
  25398. symbolColor = visible ?
  25399. (item.color || hiddenColor) :
  25400. hiddenColor,
  25401. markerOptions = item.options && item.options.marker,
  25402. symbolAttr = { fill: symbolColor };
  25403. if (legendItem) {
  25404. legendItem.css({
  25405. fill: textColor,
  25406. color: textColor // #1553, oldIE
  25407. });
  25408. }
  25409. if (legendLine) {
  25410. legendLine.attr({ stroke: symbolColor });
  25411. }
  25412. if (legendSymbol) {
  25413. // Apply marker options
  25414. if (markerOptions && legendSymbol.isMarker) { // #585
  25415. symbolAttr = item.pointAttribs();
  25416. if (!visible) {
  25417. // #6769
  25418. symbolAttr.stroke = symbolAttr.fill = hiddenColor;
  25419. }
  25420. }
  25421. legendSymbol.attr(symbolAttr);
  25422. }
  25423. }
  25424. fireEvent(this, 'afterColorizeItem', { item: item, visible: visible });
  25425. };
  25426. /**
  25427. * @private
  25428. * @function Highcharts.Legend#positionItems
  25429. */
  25430. Legend.prototype.positionItems = function () {
  25431. // Now that the legend width and height are established, put the items
  25432. // in the final position
  25433. this.allItems.forEach(this.positionItem, this);
  25434. if (!this.chart.isResizing) {
  25435. this.positionCheckboxes();
  25436. }
  25437. };
  25438. /**
  25439. * Position the legend item.
  25440. *
  25441. * @private
  25442. * @function Highcharts.Legend#positionItem
  25443. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  25444. * The item to position
  25445. */
  25446. Legend.prototype.positionItem = function (item) {
  25447. var _this = this;
  25448. var legend = this,
  25449. options = legend.options,
  25450. symbolPadding = options.symbolPadding,
  25451. ltr = !options.rtl,
  25452. legendItemPos = item._legendItemPos,
  25453. itemX = legendItemPos[0],
  25454. itemY = legendItemPos[1],
  25455. checkbox = item.checkbox,
  25456. legendGroup = item.legendGroup;
  25457. if (legendGroup && legendGroup.element) {
  25458. var attribs = {
  25459. translateX: ltr ?
  25460. itemX :
  25461. legend.legendWidth - itemX - 2 * symbolPadding - 4,
  25462. translateY: itemY
  25463. };
  25464. var complete = function () {
  25465. fireEvent(_this, 'afterPositionItem', { item: item });
  25466. };
  25467. if (defined(legendGroup.translateY)) {
  25468. legendGroup.animate(attribs, void 0, complete);
  25469. }
  25470. else {
  25471. legendGroup.attr(attribs);
  25472. complete();
  25473. }
  25474. }
  25475. if (checkbox) {
  25476. checkbox.x = itemX;
  25477. checkbox.y = itemY;
  25478. }
  25479. };
  25480. /**
  25481. * Destroy a single legend item, used internally on removing series items.
  25482. *
  25483. * @private
  25484. * @function Highcharts.Legend#destroyItem
  25485. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  25486. * The item to remove
  25487. */
  25488. Legend.prototype.destroyItem = function (item) {
  25489. var checkbox = item.checkbox;
  25490. // destroy SVG elements
  25491. ['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'].forEach(function (key) {
  25492. if (item[key]) {
  25493. item[key] = item[key].destroy();
  25494. }
  25495. });
  25496. if (checkbox) {
  25497. discardElement(item.checkbox);
  25498. }
  25499. };
  25500. /**
  25501. * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
  25502. * must be called after destruction.
  25503. *
  25504. * @private
  25505. * @function Highcharts.Legend#destroy
  25506. */
  25507. Legend.prototype.destroy = function () {
  25508. /**
  25509. * @private
  25510. * @param {string} key
  25511. * @return {void}
  25512. */
  25513. function destroyItems(key) {
  25514. if (this[key]) {
  25515. this[key] = this[key].destroy();
  25516. }
  25517. }
  25518. // Destroy items
  25519. this.getAllItems().forEach(function (item) {
  25520. ['legendItem', 'legendGroup'].forEach(destroyItems, item);
  25521. });
  25522. // Destroy legend elements
  25523. [
  25524. 'clipRect',
  25525. 'up',
  25526. 'down',
  25527. 'pager',
  25528. 'nav',
  25529. 'box',
  25530. 'title',
  25531. 'group'
  25532. ].forEach(destroyItems, this);
  25533. this.display = null; // Reset in .render on update.
  25534. };
  25535. /**
  25536. * Position the checkboxes after the width is determined.
  25537. *
  25538. * @private
  25539. * @function Highcharts.Legend#positionCheckboxes
  25540. */
  25541. Legend.prototype.positionCheckboxes = function () {
  25542. var alignAttr = this.group && this.group.alignAttr,
  25543. translateY,
  25544. clipHeight = this.clipHeight || this.legendHeight,
  25545. titleHeight = this.titleHeight;
  25546. if (alignAttr) {
  25547. translateY = alignAttr.translateY;
  25548. this.allItems.forEach(function (item) {
  25549. var checkbox = item.checkbox,
  25550. top;
  25551. if (checkbox) {
  25552. top = translateY + titleHeight + checkbox.y +
  25553. (this.scrollOffset || 0) + 3;
  25554. css(checkbox, {
  25555. left: (alignAttr.translateX + item.checkboxOffset +
  25556. checkbox.x - 20) + 'px',
  25557. top: top + 'px',
  25558. display: this.proximate || (top > translateY - 6 &&
  25559. top < translateY + clipHeight - 6) ?
  25560. '' :
  25561. 'none'
  25562. });
  25563. }
  25564. }, this);
  25565. }
  25566. };
  25567. /**
  25568. * Render the legend title on top of the legend.
  25569. *
  25570. * @private
  25571. * @function Highcharts.Legend#renderTitle
  25572. */
  25573. Legend.prototype.renderTitle = function () {
  25574. var options = this.options,
  25575. padding = this.padding,
  25576. titleOptions = options.title,
  25577. titleHeight = 0,
  25578. bBox;
  25579. if (titleOptions.text) {
  25580. if (!this.title) {
  25581. /**
  25582. * SVG element of the legend title.
  25583. *
  25584. * @readonly
  25585. * @name Highcharts.Legend#title
  25586. * @type {Highcharts.SVGElement}
  25587. */
  25588. this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, options.useHTML, null, 'legend-title')
  25589. .attr({ zIndex: 1 });
  25590. if (!this.chart.styledMode) {
  25591. this.title.css(titleOptions.style);
  25592. }
  25593. this.title.add(this.group);
  25594. }
  25595. // Set the max title width (#7253)
  25596. if (!titleOptions.width) {
  25597. this.title.css({
  25598. width: this.maxLegendWidth + 'px'
  25599. });
  25600. }
  25601. bBox = this.title.getBBox();
  25602. titleHeight = bBox.height;
  25603. this.offsetWidth = bBox.width; // #1717
  25604. this.contentGroup.attr({ translateY: titleHeight });
  25605. }
  25606. this.titleHeight = titleHeight;
  25607. };
  25608. /**
  25609. * Set the legend item text.
  25610. *
  25611. * @function Highcharts.Legend#setText
  25612. * @param {Highcharts.Point|Highcharts.Series} item
  25613. * The item for which to update the text in the legend.
  25614. */
  25615. Legend.prototype.setText = function (item) {
  25616. var options = this.options;
  25617. item.legendItem.attr({
  25618. text: options.labelFormat ?
  25619. format(options.labelFormat, item, this.chart) :
  25620. options.labelFormatter.call(item)
  25621. });
  25622. };
  25623. /**
  25624. * Render a single specific legend item. Called internally from the `render`
  25625. * function.
  25626. *
  25627. * @private
  25628. * @function Highcharts.Legend#renderItem
  25629. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  25630. * The item to render.
  25631. */
  25632. Legend.prototype.renderItem = function (item) {
  25633. var legend = this,
  25634. chart = legend.chart,
  25635. renderer = chart.renderer,
  25636. options = legend.options,
  25637. horizontal = options.layout === 'horizontal',
  25638. symbolWidth = legend.symbolWidth,
  25639. symbolPadding = options.symbolPadding,
  25640. itemStyle = legend.itemStyle,
  25641. itemHiddenStyle = legend.itemHiddenStyle,
  25642. itemDistance = horizontal ? pick(options.itemDistance, 20) : 0,
  25643. ltr = !options.rtl,
  25644. bBox,
  25645. li = item.legendItem,
  25646. isSeries = !item.series,
  25647. series = !isSeries && item.series.drawLegendSymbol ?
  25648. item.series :
  25649. item,
  25650. seriesOptions = series.options,
  25651. showCheckbox = legend.createCheckboxForItem &&
  25652. seriesOptions &&
  25653. seriesOptions.showCheckbox,
  25654. // full width minus text width
  25655. itemExtraWidth = symbolWidth + symbolPadding +
  25656. itemDistance + (showCheckbox ? 20 : 0),
  25657. useHTML = options.useHTML,
  25658. itemClassName = item.options.className;
  25659. if (!li) { // generate it once, later move it
  25660. // Generate the group box, a group to hold the symbol and text. Text
  25661. // is to be appended in Legend class.
  25662. item.legendGroup = renderer
  25663. .g('legend-item')
  25664. .addClass('highcharts-' + series.type + '-series ' +
  25665. 'highcharts-color-' + item.colorIndex +
  25666. (itemClassName ? ' ' + itemClassName : '') +
  25667. (isSeries ?
  25668. ' highcharts-series-' + item.index :
  25669. ''))
  25670. .attr({ zIndex: 1 })
  25671. .add(legend.scrollGroup);
  25672. // Generate the list item text and add it to the group
  25673. item.legendItem = li = renderer.text('', ltr ?
  25674. symbolWidth + symbolPadding :
  25675. -symbolPadding, legend.baseline || 0, useHTML);
  25676. if (!chart.styledMode) {
  25677. // merge to prevent modifying original (#1021)
  25678. li.css(merge(item.visible ?
  25679. itemStyle :
  25680. itemHiddenStyle));
  25681. }
  25682. li
  25683. .attr({
  25684. align: ltr ? 'left' : 'right',
  25685. zIndex: 2
  25686. })
  25687. .add(item.legendGroup);
  25688. // Get the baseline for the first item - the font size is equal for
  25689. // all
  25690. if (!legend.baseline) {
  25691. legend.fontMetrics = renderer.fontMetrics(chart.styledMode ? 12 : itemStyle.fontSize, li);
  25692. legend.baseline =
  25693. legend.fontMetrics.f + 3 + legend.itemMarginTop;
  25694. li.attr('y', legend.baseline);
  25695. }
  25696. // Draw the legend symbol inside the group box
  25697. legend.symbolHeight =
  25698. options.symbolHeight || legend.fontMetrics.f;
  25699. series.drawLegendSymbol(legend, item);
  25700. if (legend.setItemEvents) {
  25701. legend.setItemEvents(item, li, useHTML);
  25702. }
  25703. }
  25704. // Add the HTML checkbox on top
  25705. if (showCheckbox && !item.checkbox && legend.createCheckboxForItem) {
  25706. legend.createCheckboxForItem(item);
  25707. }
  25708. // Colorize the items
  25709. legend.colorizeItem(item, item.visible);
  25710. // Take care of max width and text overflow (#6659)
  25711. if (chart.styledMode || !itemStyle.width) {
  25712. li.css({
  25713. width: ((options.itemWidth ||
  25714. legend.widthOption ||
  25715. chart.spacingBox.width) - itemExtraWidth) + 'px'
  25716. });
  25717. }
  25718. // Always update the text
  25719. legend.setText(item);
  25720. // calculate the positions for the next line
  25721. bBox = li.getBBox();
  25722. item.itemWidth = item.checkboxOffset =
  25723. options.itemWidth ||
  25724. item.legendItemWidth ||
  25725. bBox.width + itemExtraWidth;
  25726. legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
  25727. legend.totalItemWidth += item.itemWidth;
  25728. legend.itemHeight = item.itemHeight = Math.round(item.legendItemHeight || bBox.height || legend.symbolHeight);
  25729. };
  25730. /**
  25731. * Get the position of the item in the layout. We now know the
  25732. * maxItemWidth from the previous loop.
  25733. *
  25734. * @private
  25735. * @function Highcharts.Legend#layoutItem
  25736. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  25737. */
  25738. Legend.prototype.layoutItem = function (item) {
  25739. var options = this.options,
  25740. padding = this.padding,
  25741. horizontal = options.layout === 'horizontal',
  25742. itemHeight = item.itemHeight,
  25743. itemMarginBottom = this.itemMarginBottom,
  25744. itemMarginTop = this.itemMarginTop,
  25745. itemDistance = horizontal ? pick(options.itemDistance, 20) : 0,
  25746. maxLegendWidth = this.maxLegendWidth,
  25747. itemWidth = (options.alignColumns &&
  25748. this.totalItemWidth > maxLegendWidth) ?
  25749. this.maxItemWidth :
  25750. item.itemWidth;
  25751. // If the item exceeds the width, start a new line
  25752. if (horizontal &&
  25753. this.itemX - padding + itemWidth > maxLegendWidth) {
  25754. this.itemX = padding;
  25755. if (this.lastLineHeight) { // Not for the first line (#10167)
  25756. this.itemY += (itemMarginTop +
  25757. this.lastLineHeight +
  25758. itemMarginBottom);
  25759. }
  25760. this.lastLineHeight = 0; // reset for next line (#915, #3976)
  25761. }
  25762. // Set the edge positions
  25763. this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
  25764. this.lastLineHeight = Math.max(// #915
  25765. itemHeight, this.lastLineHeight);
  25766. // cache the position of the newly generated or reordered items
  25767. item._legendItemPos = [this.itemX, this.itemY];
  25768. // advance
  25769. if (horizontal) {
  25770. this.itemX += itemWidth;
  25771. }
  25772. else {
  25773. this.itemY +=
  25774. itemMarginTop + itemHeight + itemMarginBottom;
  25775. this.lastLineHeight = itemHeight;
  25776. }
  25777. // the width of the widest item
  25778. this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
  25779. // decrease by itemDistance only when no checkbox #4853
  25780. 0 :
  25781. itemDistance) : itemWidth) + padding, this.offsetWidth);
  25782. };
  25783. /**
  25784. * Get all items, which is one item per series for most series and one
  25785. * item per point for pie series and its derivatives. Fires the event
  25786. * `afterGetAllItems`.
  25787. *
  25788. * @private
  25789. * @function Highcharts.Legend#getAllItems
  25790. * @return {Array<(Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series)>}
  25791. * The current items in the legend.
  25792. * @fires Highcharts.Legend#event:afterGetAllItems
  25793. */
  25794. Legend.prototype.getAllItems = function () {
  25795. var allItems = [];
  25796. this.chart.series.forEach(function (series) {
  25797. var seriesOptions = series && series.options;
  25798. // Handle showInLegend. If the series is linked to another series,
  25799. // defaults to false.
  25800. if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
  25801. // Use points or series for the legend item depending on
  25802. // legendType
  25803. allItems = allItems.concat(series.legendItems ||
  25804. (seriesOptions.legendType === 'point' ?
  25805. series.data :
  25806. series));
  25807. }
  25808. });
  25809. fireEvent(this, 'afterGetAllItems', { allItems: allItems });
  25810. return allItems;
  25811. };
  25812. /**
  25813. * Get a short, three letter string reflecting the alignment and layout.
  25814. *
  25815. * @private
  25816. * @function Highcharts.Legend#getAlignment
  25817. * @return {string}
  25818. * The alignment, empty string if floating
  25819. */
  25820. Legend.prototype.getAlignment = function () {
  25821. var options = this.options;
  25822. // Use the first letter of each alignment option in order to detect
  25823. // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
  25824. if (this.proximate) {
  25825. return options.align.charAt(0) + 'tv';
  25826. }
  25827. return options.floating ? '' : (options.align.charAt(0) +
  25828. options.verticalAlign.charAt(0) +
  25829. options.layout.charAt(0));
  25830. };
  25831. /**
  25832. * Adjust the chart margins by reserving space for the legend on only one
  25833. * side of the chart. If the position is set to a corner, top or bottom is
  25834. * reserved for horizontal legends and left or right for vertical ones.
  25835. *
  25836. * @private
  25837. * @function Highcharts.Legend#adjustMargins
  25838. * @param {Array<number>} margin
  25839. * @param {Array<number>} spacing
  25840. */
  25841. Legend.prototype.adjustMargins = function (margin, spacing) {
  25842. var chart = this.chart,
  25843. options = this.options,
  25844. alignment = this.getAlignment();
  25845. if (alignment) {
  25846. ([
  25847. /(lth|ct|rth)/,
  25848. /(rtv|rm|rbv)/,
  25849. /(rbh|cb|lbh)/,
  25850. /(lbv|lm|ltv)/
  25851. ]).forEach(function (alignments, side) {
  25852. if (alignments.test(alignment) && !defined(margin[side])) {
  25853. // Now we have detected on which side of the chart we should
  25854. // reserve space for the legend
  25855. chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
  25856. [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
  25857. pick(options.margin, 12) +
  25858. spacing[side] +
  25859. (chart.titleOffset[side] || 0)));
  25860. }
  25861. });
  25862. }
  25863. };
  25864. /**
  25865. * @private
  25866. * @function Highcharts.Legend#proximatePositions
  25867. */
  25868. Legend.prototype.proximatePositions = function () {
  25869. var chart = this.chart,
  25870. boxes = [],
  25871. alignLeft = this.options.align === 'left';
  25872. this.allItems.forEach(function (item) {
  25873. var lastPoint,
  25874. height,
  25875. useFirstPoint = alignLeft,
  25876. target,
  25877. top;
  25878. if (item.yAxis) {
  25879. if (item.xAxis.options.reversed) {
  25880. useFirstPoint = !useFirstPoint;
  25881. }
  25882. if (item.points) {
  25883. lastPoint = find(useFirstPoint ?
  25884. item.points :
  25885. item.points.slice(0).reverse(), function (item) {
  25886. return isNumber(item.plotY);
  25887. });
  25888. }
  25889. height = this.itemMarginTop +
  25890. item.legendItem.getBBox().height +
  25891. this.itemMarginBottom;
  25892. top = item.yAxis.top - chart.plotTop;
  25893. if (item.visible) {
  25894. target = lastPoint ?
  25895. lastPoint.plotY :
  25896. item.yAxis.height;
  25897. target += top - 0.3 * height;
  25898. }
  25899. else {
  25900. target = top + item.yAxis.height;
  25901. }
  25902. boxes.push({
  25903. target: target,
  25904. size: height,
  25905. item: item
  25906. });
  25907. }
  25908. }, this);
  25909. H.distribute(boxes, chart.plotHeight);
  25910. boxes.forEach(function (box) {
  25911. box.item._legendItemPos[1] =
  25912. chart.plotTop - chart.spacing[0] + box.pos;
  25913. });
  25914. };
  25915. /**
  25916. * Render the legend. This method can be called both before and after
  25917. * `chart.render`. If called after, it will only rearrange items instead
  25918. * of creating new ones. Called internally on initial render and after
  25919. * redraws.
  25920. *
  25921. * @private
  25922. * @function Highcharts.Legend#render
  25923. */
  25924. Legend.prototype.render = function () {
  25925. var legend = this,
  25926. chart = legend.chart,
  25927. renderer = chart.renderer,
  25928. legendGroup = legend.group,
  25929. allItems,
  25930. display,
  25931. legendWidth,
  25932. legendHeight,
  25933. box = legend.box,
  25934. options = legend.options,
  25935. padding = legend.padding,
  25936. allowedWidth;
  25937. legend.itemX = padding;
  25938. legend.itemY = legend.initialItemY;
  25939. legend.offsetWidth = 0;
  25940. legend.lastItemY = 0;
  25941. legend.widthOption = relativeLength(options.width, chart.spacingBox.width - padding);
  25942. // Compute how wide the legend is allowed to be
  25943. allowedWidth =
  25944. chart.spacingBox.width - 2 * padding - options.x;
  25945. if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
  25946. allowedWidth /= 2;
  25947. }
  25948. legend.maxLegendWidth = legend.widthOption || allowedWidth;
  25949. if (!legendGroup) {
  25950. /**
  25951. * SVG group of the legend.
  25952. *
  25953. * @readonly
  25954. * @name Highcharts.Legend#group
  25955. * @type {Highcharts.SVGElement}
  25956. */
  25957. legend.group = legendGroup = renderer.g('legend')
  25958. .attr({ zIndex: 7 })
  25959. .add();
  25960. legend.contentGroup = renderer.g()
  25961. .attr({ zIndex: 1 }) // above background
  25962. .add(legendGroup);
  25963. legend.scrollGroup = renderer.g()
  25964. .add(legend.contentGroup);
  25965. }
  25966. legend.renderTitle();
  25967. // add each series or point
  25968. allItems = legend.getAllItems();
  25969. // sort by legendIndex
  25970. stableSort(allItems, function (a, b) {
  25971. return ((a.options && a.options.legendIndex) || 0) -
  25972. ((b.options && b.options.legendIndex) || 0);
  25973. });
  25974. // reversed legend
  25975. if (options.reversed) {
  25976. allItems.reverse();
  25977. }
  25978. /**
  25979. * All items for the legend, which is an array of series for most series
  25980. * and an array of points for pie series and its derivatives.
  25981. *
  25982. * @readonly
  25983. * @name Highcharts.Legend#allItems
  25984. * @type {Array<(Highcharts.Point|Highcharts.Series)>}
  25985. */
  25986. legend.allItems = allItems;
  25987. legend.display = display = !!allItems.length;
  25988. // Render the items. First we run a loop to set the text and properties
  25989. // and read all the bounding boxes. The next loop computes the item
  25990. // positions based on the bounding boxes.
  25991. legend.lastLineHeight = 0;
  25992. legend.maxItemWidth = 0;
  25993. legend.totalItemWidth = 0;
  25994. legend.itemHeight = 0;
  25995. allItems.forEach(legend.renderItem, legend);
  25996. allItems.forEach(legend.layoutItem, legend);
  25997. // Get the box
  25998. legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
  25999. legendHeight = legend.lastItemY + legend.lastLineHeight +
  26000. legend.titleHeight;
  26001. legendHeight = legend.handleOverflow(legendHeight);
  26002. legendHeight += padding;
  26003. // Draw the border and/or background
  26004. if (!box) {
  26005. /**
  26006. * SVG element of the legend box.
  26007. *
  26008. * @readonly
  26009. * @name Highcharts.Legend#box
  26010. * @type {Highcharts.SVGElement}
  26011. */
  26012. legend.box = box = renderer.rect()
  26013. .addClass('highcharts-legend-box')
  26014. .attr({
  26015. r: options.borderRadius
  26016. })
  26017. .add(legendGroup);
  26018. box.isNew = true;
  26019. }
  26020. // Presentational
  26021. if (!chart.styledMode) {
  26022. box
  26023. .attr({
  26024. stroke: options.borderColor,
  26025. 'stroke-width': options.borderWidth || 0,
  26026. fill: options.backgroundColor || 'none'
  26027. })
  26028. .shadow(options.shadow);
  26029. }
  26030. if (legendWidth > 0 && legendHeight > 0) {
  26031. box[box.isNew ? 'attr' : 'animate'](box.crisp.call({}, {
  26032. x: 0,
  26033. y: 0,
  26034. width: legendWidth,
  26035. height: legendHeight
  26036. }, box.strokeWidth()));
  26037. box.isNew = false;
  26038. }
  26039. // hide the border if no items
  26040. box[display ? 'show' : 'hide']();
  26041. // Open for responsiveness
  26042. if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
  26043. legendWidth = legendHeight = 0;
  26044. }
  26045. legend.legendWidth = legendWidth;
  26046. legend.legendHeight = legendHeight;
  26047. if (display) {
  26048. legend.align();
  26049. }
  26050. if (!this.proximate) {
  26051. this.positionItems();
  26052. }
  26053. fireEvent(this, 'afterRender');
  26054. };
  26055. /**
  26056. * Align the legend to chart's box.
  26057. *
  26058. * @private
  26059. * @function Highcharts.align
  26060. * @param {Highcharts.BBoxObject} alignTo
  26061. * @return {void}
  26062. */
  26063. Legend.prototype.align = function (alignTo) {
  26064. if (alignTo === void 0) { alignTo = this.chart.spacingBox; }
  26065. var chart = this.chart,
  26066. options = this.options;
  26067. // If aligning to the top and the layout is horizontal, adjust for
  26068. // the title (#7428)
  26069. var y = alignTo.y;
  26070. if (/(lth|ct|rth)/.test(this.getAlignment()) &&
  26071. chart.titleOffset[0] > 0) {
  26072. y += chart.titleOffset[0];
  26073. }
  26074. else if (/(lbh|cb|rbh)/.test(this.getAlignment()) &&
  26075. chart.titleOffset[2] > 0) {
  26076. y -= chart.titleOffset[2];
  26077. }
  26078. if (y !== alignTo.y) {
  26079. alignTo = merge(alignTo, { y: y });
  26080. }
  26081. this.group.align(merge(options, {
  26082. width: this.legendWidth,
  26083. height: this.legendHeight,
  26084. verticalAlign: this.proximate ? 'top' : options.verticalAlign
  26085. }), true, alignTo);
  26086. };
  26087. /**
  26088. * Set up the overflow handling by adding navigation with up and down arrows
  26089. * below the legend.
  26090. *
  26091. * @private
  26092. * @function Highcharts.Legend#handleOverflow
  26093. * @param {number} legendHeight
  26094. * @return {number}
  26095. */
  26096. Legend.prototype.handleOverflow = function (legendHeight) {
  26097. var legend = this,
  26098. chart = this.chart,
  26099. renderer = chart.renderer,
  26100. options = this.options,
  26101. optionsY = options.y,
  26102. alignTop = options.verticalAlign === 'top',
  26103. padding = this.padding,
  26104. spaceHeight = (chart.spacingBox.height +
  26105. (alignTop ? -optionsY : optionsY) - padding),
  26106. maxHeight = options.maxHeight,
  26107. clipHeight,
  26108. clipRect = this.clipRect,
  26109. navOptions = options.navigation,
  26110. animation = pick(navOptions.animation,
  26111. true),
  26112. arrowSize = navOptions.arrowSize || 12,
  26113. nav = this.nav,
  26114. pages = this.pages,
  26115. lastY,
  26116. allItems = this.allItems,
  26117. clipToHeight = function (height) {
  26118. if (typeof height === 'number') {
  26119. clipRect.attr({
  26120. height: height
  26121. });
  26122. }
  26123. else if (clipRect) { // Reset (#5912)
  26124. legend.clipRect = clipRect.destroy();
  26125. legend.contentGroup.clip();
  26126. }
  26127. // useHTML
  26128. if (legend.contentGroup.div) {
  26129. legend.contentGroup.div.style.clip = height ?
  26130. 'rect(' + padding + 'px,9999px,' +
  26131. (padding + height) + 'px,0)' :
  26132. 'auto';
  26133. }
  26134. }, addTracker = function (key) {
  26135. legend[key] = renderer
  26136. .circle(0, 0, arrowSize * 1.3)
  26137. .translate(arrowSize / 2, arrowSize / 2)
  26138. .add(nav);
  26139. if (!chart.styledMode) {
  26140. legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
  26141. }
  26142. return legend[key];
  26143. };
  26144. // Adjust the height
  26145. if (options.layout === 'horizontal' &&
  26146. options.verticalAlign !== 'middle' &&
  26147. !options.floating) {
  26148. spaceHeight /= 2;
  26149. }
  26150. if (maxHeight) {
  26151. spaceHeight = Math.min(spaceHeight, maxHeight);
  26152. }
  26153. // Reset the legend height and adjust the clipping rectangle
  26154. pages.length = 0;
  26155. if (legendHeight > spaceHeight &&
  26156. navOptions.enabled !== false) {
  26157. this.clipHeight = clipHeight =
  26158. Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
  26159. this.currentPage = pick(this.currentPage, 1);
  26160. this.fullHeight = legendHeight;
  26161. // Fill pages with Y positions so that the top of each a legend item
  26162. // defines the scroll top for each page (#2098)
  26163. allItems.forEach(function (item, i) {
  26164. var y = item._legendItemPos[1],
  26165. h = Math.round(item.legendItem.getBBox().height),
  26166. len = pages.length;
  26167. if (!len || (y - pages[len - 1] > clipHeight &&
  26168. (lastY || y) !== pages[len - 1])) {
  26169. pages.push(lastY || y);
  26170. len++;
  26171. }
  26172. // Keep track of which page each item is on
  26173. item.pageIx = len - 1;
  26174. if (lastY) {
  26175. allItems[i - 1].pageIx = len - 1;
  26176. }
  26177. if (i === allItems.length - 1 &&
  26178. y + h - pages[len - 1] > clipHeight &&
  26179. y !== lastY // #2617
  26180. ) {
  26181. pages.push(y);
  26182. item.pageIx = len;
  26183. }
  26184. if (y !== lastY) {
  26185. lastY = y;
  26186. }
  26187. });
  26188. // Only apply clipping if needed. Clipping causes blurred legend in
  26189. // PDF export (#1787)
  26190. if (!clipRect) {
  26191. clipRect = legend.clipRect =
  26192. renderer.clipRect(0, padding, 9999, 0);
  26193. legend.contentGroup.clip(clipRect);
  26194. }
  26195. clipToHeight(clipHeight);
  26196. // Add navigation elements
  26197. if (!nav) {
  26198. this.nav = nav = renderer.g()
  26199. .attr({ zIndex: 1 })
  26200. .add(this.group);
  26201. this.up = renderer
  26202. .symbol('triangle', 0, 0, arrowSize, arrowSize)
  26203. .add(nav);
  26204. addTracker('upTracker')
  26205. .on('click', function () {
  26206. legend.scroll(-1, animation);
  26207. });
  26208. this.pager = renderer.text('', 15, 10)
  26209. .addClass('highcharts-legend-navigation');
  26210. if (!chart.styledMode) {
  26211. this.pager.css(navOptions.style);
  26212. }
  26213. this.pager.add(nav);
  26214. this.down = renderer
  26215. .symbol('triangle-down', 0, 0, arrowSize, arrowSize)
  26216. .add(nav);
  26217. addTracker('downTracker')
  26218. .on('click', function () {
  26219. legend.scroll(1, animation);
  26220. });
  26221. }
  26222. // Set initial position
  26223. legend.scroll(0);
  26224. legendHeight = spaceHeight;
  26225. // Reset
  26226. }
  26227. else if (nav) {
  26228. clipToHeight();
  26229. this.nav = nav.destroy(); // #6322
  26230. this.scrollGroup.attr({
  26231. translateY: 1
  26232. });
  26233. this.clipHeight = 0; // #1379
  26234. }
  26235. return legendHeight;
  26236. };
  26237. /**
  26238. * Scroll the legend by a number of pages.
  26239. *
  26240. * @private
  26241. * @function Highcharts.Legend#scroll
  26242. *
  26243. * @param {number} scrollBy
  26244. * The number of pages to scroll.
  26245. *
  26246. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  26247. * Whether and how to apply animation.
  26248. *
  26249. * @return {void}
  26250. */
  26251. Legend.prototype.scroll = function (scrollBy, animation) {
  26252. var _this = this;
  26253. var chart = this.chart,
  26254. pages = this.pages,
  26255. pageCount = pages.length,
  26256. currentPage = this.currentPage + scrollBy,
  26257. clipHeight = this.clipHeight,
  26258. navOptions = this.options.navigation,
  26259. pager = this.pager,
  26260. padding = this.padding;
  26261. // When resizing while looking at the last page
  26262. if (currentPage > pageCount) {
  26263. currentPage = pageCount;
  26264. }
  26265. if (currentPage > 0) {
  26266. if (typeof animation !== 'undefined') {
  26267. setAnimation(animation, chart);
  26268. }
  26269. this.nav.attr({
  26270. translateX: padding,
  26271. translateY: clipHeight + this.padding + 7 + this.titleHeight,
  26272. visibility: 'visible'
  26273. });
  26274. [this.up, this.upTracker].forEach(function (elem) {
  26275. elem.attr({
  26276. 'class': currentPage === 1 ?
  26277. 'highcharts-legend-nav-inactive' :
  26278. 'highcharts-legend-nav-active'
  26279. });
  26280. });
  26281. pager.attr({
  26282. text: currentPage + '/' + pageCount
  26283. });
  26284. [this.down, this.downTracker].forEach(function (elem) {
  26285. elem.attr({
  26286. // adjust to text width
  26287. x: 18 + this.pager.getBBox().width,
  26288. 'class': currentPage === pageCount ?
  26289. 'highcharts-legend-nav-inactive' :
  26290. 'highcharts-legend-nav-active'
  26291. });
  26292. }, this);
  26293. if (!chart.styledMode) {
  26294. this.up
  26295. .attr({
  26296. fill: currentPage === 1 ?
  26297. navOptions.inactiveColor :
  26298. navOptions.activeColor
  26299. });
  26300. this.upTracker
  26301. .css({
  26302. cursor: currentPage === 1 ? 'default' : 'pointer'
  26303. });
  26304. this.down
  26305. .attr({
  26306. fill: currentPage === pageCount ?
  26307. navOptions.inactiveColor :
  26308. navOptions.activeColor
  26309. });
  26310. this.downTracker
  26311. .css({
  26312. cursor: currentPage === pageCount ?
  26313. 'default' :
  26314. 'pointer'
  26315. });
  26316. }
  26317. this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
  26318. this.scrollGroup.animate({
  26319. translateY: this.scrollOffset
  26320. });
  26321. this.currentPage = currentPage;
  26322. this.positionCheckboxes();
  26323. // Fire event after scroll animation is complete
  26324. var animOptions = animObject(pick(animation,
  26325. chart.renderer.globalAnimation,
  26326. true));
  26327. syncTimeout(function () {
  26328. fireEvent(_this, 'afterScroll', { currentPage: currentPage });
  26329. }, animOptions.duration);
  26330. }
  26331. };
  26332. return Legend;
  26333. }());
  26334. // Workaround for #2030, horizontal legend items not displaying in IE11 Preview,
  26335. // and for #2580, a similar drawing flaw in Firefox 26.
  26336. // Explore if there's a general cause for this. The problem may be related
  26337. // to nested group elements, as the legend item texts are within 4 group
  26338. // elements.
  26339. if (/Trident\/7\.0/.test(win.navigator && win.navigator.userAgent) ||
  26340. isFirefox) {
  26341. wrap(Legend.prototype, 'positionItem', function (proceed, item) {
  26342. var legend = this,
  26343. // If chart destroyed in sync, this is undefined (#2030)
  26344. runPositionItem = function () {
  26345. if (item._legendItemPos) {
  26346. proceed.call(legend,
  26347. item);
  26348. }
  26349. };
  26350. // Do it now, for export and to get checkbox placement
  26351. runPositionItem();
  26352. // Do it after to work around the core issue
  26353. if (!legend.bubbleLegend) {
  26354. setTimeout(runPositionItem);
  26355. }
  26356. });
  26357. }
  26358. H.Legend = Legend;
  26359. return H.Legend;
  26360. });
  26361. _registerModule(_modules, 'Core/Chart/Chart.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Legend.js'], _modules['Core/MSPointer.js'], _modules['Core/Options.js'], _modules['Core/Pointer.js'], _modules['Core/Time.js'], _modules['Core/Utilities.js']], function (Axis, H, Legend, MSPointer, O, Pointer, Time, U) {
  26362. /* *
  26363. *
  26364. * (c) 2010-2020 Torstein Honsi
  26365. *
  26366. * License: www.highcharts.com/license
  26367. *
  26368. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  26369. *
  26370. * */
  26371. var charts = H.charts,
  26372. doc = H.doc,
  26373. seriesTypes = H.seriesTypes,
  26374. win = H.win;
  26375. var defaultOptions = O.defaultOptions;
  26376. var addEvent = U.addEvent,
  26377. animate = U.animate,
  26378. animObject = U.animObject,
  26379. attr = U.attr,
  26380. createElement = U.createElement,
  26381. css = U.css,
  26382. defined = U.defined,
  26383. discardElement = U.discardElement,
  26384. erase = U.erase,
  26385. error = U.error,
  26386. extend = U.extend,
  26387. find = U.find,
  26388. fireEvent = U.fireEvent,
  26389. getStyle = U.getStyle,
  26390. isArray = U.isArray,
  26391. isFunction = U.isFunction,
  26392. isNumber = U.isNumber,
  26393. isObject = U.isObject,
  26394. isString = U.isString,
  26395. merge = U.merge,
  26396. numberFormat = U.numberFormat,
  26397. objectEach = U.objectEach,
  26398. pick = U.pick,
  26399. pInt = U.pInt,
  26400. relativeLength = U.relativeLength,
  26401. removeEvent = U.removeEvent,
  26402. setAnimation = U.setAnimation,
  26403. splat = U.splat,
  26404. syncTimeout = U.syncTimeout,
  26405. uniqueKey = U.uniqueKey;
  26406. /**
  26407. * Callback for chart constructors.
  26408. *
  26409. * @callback Highcharts.ChartCallbackFunction
  26410. *
  26411. * @param {Highcharts.Chart} chart
  26412. * Created chart.
  26413. */
  26414. /**
  26415. * Format a number and return a string based on input settings.
  26416. *
  26417. * @callback Highcharts.NumberFormatterCallbackFunction
  26418. *
  26419. * @param {number} number
  26420. * The input number to format.
  26421. *
  26422. * @param {number} decimals
  26423. * The amount of decimals. A value of -1 preserves the amount in the
  26424. * input number.
  26425. *
  26426. * @param {string} [decimalPoint]
  26427. * The decimal point, defaults to the one given in the lang options, or
  26428. * a dot.
  26429. *
  26430. * @param {string} [thousandsSep]
  26431. * The thousands separator, defaults to the one given in the lang
  26432. * options, or a space character.
  26433. *
  26434. * @return {string} The formatted number.
  26435. */
  26436. /**
  26437. * The chart title. The title has an `update` method that allows modifying the
  26438. * options directly or indirectly via `chart.update`.
  26439. *
  26440. * @interface Highcharts.TitleObject
  26441. * @extends Highcharts.SVGElement
  26442. */ /**
  26443. * Modify options for the title.
  26444. *
  26445. * @function Highcharts.TitleObject#update
  26446. *
  26447. * @param {Highcharts.TitleOptions} titleOptions
  26448. * Options to modify.
  26449. *
  26450. * @param {boolean} [redraw=true]
  26451. * Whether to redraw the chart after the title is altered. If doing more
  26452. * operations on the chart, it is a good idea to set redraw to false and
  26453. * call {@link Chart#redraw} after.
  26454. */
  26455. /**
  26456. * The chart subtitle. The subtitle has an `update` method that
  26457. * allows modifying the options directly or indirectly via
  26458. * `chart.update`.
  26459. *
  26460. * @interface Highcharts.SubtitleObject
  26461. * @extends Highcharts.SVGElement
  26462. */ /**
  26463. * Modify options for the subtitle.
  26464. *
  26465. * @function Highcharts.SubtitleObject#update
  26466. *
  26467. * @param {Highcharts.SubtitleOptions} subtitleOptions
  26468. * Options to modify.
  26469. *
  26470. * @param {boolean} [redraw=true]
  26471. * Whether to redraw the chart after the subtitle is altered. If doing
  26472. * more operations on the chart, it is a good idea to set redraw to false
  26473. * and call {@link Chart#redraw} after.
  26474. */
  26475. /**
  26476. * The chart caption. The caption has an `update` method that
  26477. * allows modifying the options directly or indirectly via
  26478. * `chart.update`.
  26479. *
  26480. * @interface Highcharts.CaptionObject
  26481. * @extends Highcharts.SVGElement
  26482. */ /**
  26483. * Modify options for the caption.
  26484. *
  26485. * @function Highcharts.CaptionObject#update
  26486. *
  26487. * @param {Highcharts.CaptionOptions} captionOptions
  26488. * Options to modify.
  26489. *
  26490. * @param {boolean} [redraw=true]
  26491. * Whether to redraw the chart after the caption is altered. If doing
  26492. * more operations on the chart, it is a good idea to set redraw to false
  26493. * and call {@link Chart#redraw} after.
  26494. */
  26495. var marginNames = H.marginNames;
  26496. /* eslint-disable no-invalid-this, valid-jsdoc */
  26497. /**
  26498. * The Chart class. The recommended constructor is {@link Highcharts#chart}.
  26499. *
  26500. * @example
  26501. * var chart = Highcharts.chart('container', {
  26502. * title: {
  26503. * text: 'My chart'
  26504. * },
  26505. * series: [{
  26506. * data: [1, 3, 2, 4]
  26507. * }]
  26508. * })
  26509. *
  26510. * @class
  26511. * @name Highcharts.Chart
  26512. *
  26513. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  26514. * The DOM element to render to, or its id.
  26515. *
  26516. * @param {Highcharts.Options} options
  26517. * The chart options structure.
  26518. *
  26519. * @param {Highcharts.ChartCallbackFunction} [callback]
  26520. * Function to run when the chart has loaded and and all external images
  26521. * are loaded. Defining a
  26522. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  26523. * handler is equivalent.
  26524. */
  26525. var Chart = /** @class */ (function () {
  26526. function Chart(a, b, c) {
  26527. this.axes = void 0;
  26528. this.axisOffset = void 0;
  26529. this.bounds = void 0;
  26530. this.chartHeight = void 0;
  26531. this.chartWidth = void 0;
  26532. this.clipBox = void 0;
  26533. this.colorCounter = void 0;
  26534. this.container = void 0;
  26535. this.index = void 0;
  26536. this.isResizing = void 0;
  26537. this.labelCollectors = void 0;
  26538. this.legend = void 0;
  26539. this.margin = void 0;
  26540. this.numberFormatter = void 0;
  26541. this.options = void 0;
  26542. this.plotBox = void 0;
  26543. this.plotHeight = void 0;
  26544. this.plotLeft = void 0;
  26545. this.plotTop = void 0;
  26546. this.plotWidth = void 0;
  26547. this.pointCount = void 0;
  26548. this.pointer = void 0;
  26549. this.renderer = void 0;
  26550. this.renderTo = void 0;
  26551. this.series = void 0;
  26552. this.spacing = void 0;
  26553. this.spacingBox = void 0;
  26554. this.symbolCounter = void 0;
  26555. this.time = void 0;
  26556. this.titleOffset = void 0;
  26557. this.userOptions = void 0;
  26558. this.xAxis = void 0;
  26559. this.yAxis = void 0;
  26560. this.getArgs(a, b, c);
  26561. }
  26562. /* *
  26563. *
  26564. * Functions
  26565. *
  26566. * */
  26567. /**
  26568. * Handle the arguments passed to the constructor.
  26569. *
  26570. * @private
  26571. * @function Highcharts.Chart#getArgs
  26572. *
  26573. * @param {...Array<*>} arguments
  26574. * All arguments for the constructor.
  26575. *
  26576. * @fires Highcharts.Chart#event:init
  26577. * @fires Highcharts.Chart#event:afterInit
  26578. */
  26579. Chart.prototype.getArgs = function (a, b, c) {
  26580. // Remove the optional first argument, renderTo, and
  26581. // set it on this.
  26582. if (isString(a) || a.nodeName) {
  26583. this.renderTo = a;
  26584. this.init(b, c);
  26585. }
  26586. else {
  26587. this.init(a, b);
  26588. }
  26589. };
  26590. /**
  26591. * Overridable function that initializes the chart. The constructor's
  26592. * arguments are passed on directly.
  26593. *
  26594. * @function Highcharts.Chart#init
  26595. *
  26596. * @param {Highcharts.Options} userOptions
  26597. * Custom options.
  26598. *
  26599. * @param {Function} [callback]
  26600. * Function to run when the chart has loaded and and all external
  26601. * images are loaded.
  26602. *
  26603. * @return {void}
  26604. *
  26605. * @fires Highcharts.Chart#event:init
  26606. * @fires Highcharts.Chart#event:afterInit
  26607. */
  26608. Chart.prototype.init = function (userOptions, callback) {
  26609. // Handle regular options
  26610. var options,
  26611. // skip merging data points to increase performance
  26612. seriesOptions = userOptions.series,
  26613. userPlotOptions = userOptions.plotOptions || {};
  26614. // Fire the event with a default function
  26615. fireEvent(this, 'init', { args: arguments }, function () {
  26616. userOptions.series = null;
  26617. options = merge(defaultOptions, userOptions); // do the merge
  26618. var optionsChart = options.chart || {};
  26619. // Override (by copy of user options) or clear tooltip options
  26620. // in chart.options.plotOptions (#6218)
  26621. objectEach(options.plotOptions, function (typeOptions, type) {
  26622. if (isObject(typeOptions)) { // #8766
  26623. typeOptions.tooltip = (userPlotOptions[type] && // override by copy:
  26624. merge(userPlotOptions[type].tooltip)) || void 0; // or clear
  26625. }
  26626. });
  26627. // User options have higher priority than default options
  26628. // (#6218). In case of exporting: path is changed
  26629. options.tooltip.userOptions = (userOptions.chart &&
  26630. userOptions.chart.forExport &&
  26631. userOptions.tooltip.userOptions) || userOptions.tooltip;
  26632. // set back the series data
  26633. options.series = userOptions.series = seriesOptions;
  26634. /**
  26635. * The original options given to the constructor or a chart factory
  26636. * like {@link Highcharts.chart} and {@link Highcharts.stockChart}.
  26637. *
  26638. * @name Highcharts.Chart#userOptions
  26639. * @type {Highcharts.Options}
  26640. */
  26641. this.userOptions = userOptions;
  26642. var chartEvents = optionsChart.events;
  26643. this.margin = [];
  26644. this.spacing = [];
  26645. // Pixel data bounds for touch zoom
  26646. this.bounds = { h: {}, v: {} };
  26647. // An array of functions that returns labels that should be
  26648. // considered for anti-collision
  26649. this.labelCollectors = [];
  26650. this.callback = callback;
  26651. this.isResizing = 0;
  26652. /**
  26653. * The options structure for the chart after merging
  26654. * {@link #defaultOptions} and {@link #userOptions}. It contains
  26655. * members for the sub elements like series, legend, tooltip etc.
  26656. *
  26657. * @name Highcharts.Chart#options
  26658. * @type {Highcharts.Options}
  26659. */
  26660. this.options = options;
  26661. /**
  26662. * All the axes in the chart.
  26663. *
  26664. * @see Highcharts.Chart.xAxis
  26665. * @see Highcharts.Chart.yAxis
  26666. *
  26667. * @name Highcharts.Chart#axes
  26668. * @type {Array<Highcharts.Axis>}
  26669. */
  26670. this.axes = [];
  26671. /**
  26672. * All the current series in the chart.
  26673. *
  26674. * @name Highcharts.Chart#series
  26675. * @type {Array<Highcharts.Series>}
  26676. */
  26677. this.series = [];
  26678. /**
  26679. * The `Time` object associated with the chart. Since v6.0.5,
  26680. * time settings can be applied individually for each chart. If
  26681. * no individual settings apply, the `Time` object is shared by
  26682. * all instances.
  26683. *
  26684. * @name Highcharts.Chart#time
  26685. * @type {Highcharts.Time}
  26686. */
  26687. this.time =
  26688. userOptions.time && Object.keys(userOptions.time).length ?
  26689. new Time(userOptions.time) :
  26690. H.time;
  26691. /**
  26692. * Callback function to override the default function that formats
  26693. * all the numbers in the chart. Returns a string with the formatted
  26694. * number.
  26695. *
  26696. * @name Highcharts.Chart#numberFormatter
  26697. * @type {Highcharts.NumberFormatterCallbackFunction}
  26698. */
  26699. this.numberFormatter = optionsChart.numberFormatter || numberFormat;
  26700. /**
  26701. * Whether the chart is in styled mode, meaning all presentatinoal
  26702. * attributes are avoided.
  26703. *
  26704. * @name Highcharts.Chart#styledMode
  26705. * @type {boolean}
  26706. */
  26707. this.styledMode = optionsChart.styledMode;
  26708. this.hasCartesianSeries = optionsChart.showAxes;
  26709. var chart = this;
  26710. /**
  26711. * Index position of the chart in the {@link Highcharts#charts}
  26712. * property.
  26713. *
  26714. * @name Highcharts.Chart#index
  26715. * @type {number}
  26716. * @readonly
  26717. */
  26718. chart.index = charts.length; // Add the chart to the global lookup
  26719. charts.push(chart);
  26720. H.chartCount++;
  26721. // Chart event handlers
  26722. if (chartEvents) {
  26723. objectEach(chartEvents, function (event, eventType) {
  26724. if (isFunction(event)) {
  26725. addEvent(chart, eventType, event);
  26726. }
  26727. });
  26728. }
  26729. /**
  26730. * A collection of the X axes in the chart.
  26731. *
  26732. * @name Highcharts.Chart#xAxis
  26733. * @type {Array<Highcharts.Axis>}
  26734. */
  26735. chart.xAxis = [];
  26736. /**
  26737. * A collection of the Y axes in the chart.
  26738. *
  26739. * @name Highcharts.Chart#yAxis
  26740. * @type {Array<Highcharts.Axis>}
  26741. *
  26742. * @todo
  26743. * Make events official: Fire the event `afterInit`.
  26744. */
  26745. chart.yAxis = [];
  26746. chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
  26747. // Fire after init but before first render, before axes and series
  26748. // have been initialized.
  26749. fireEvent(chart, 'afterInit');
  26750. chart.firstRender();
  26751. });
  26752. };
  26753. /**
  26754. * Internal function to unitialize an individual series.
  26755. *
  26756. * @private
  26757. * @function Highcharts.Chart#initSeries
  26758. */
  26759. Chart.prototype.initSeries = function (options) {
  26760. var chart = this,
  26761. optionsChart = chart.options.chart,
  26762. type = (options.type ||
  26763. optionsChart.type ||
  26764. optionsChart.defaultSeriesType),
  26765. series,
  26766. Constr = seriesTypes[type];
  26767. // No such series type
  26768. if (!Constr) {
  26769. error(17, true, chart, { missingModuleFor: type });
  26770. }
  26771. series = new Constr();
  26772. series.init(this, options);
  26773. return series;
  26774. };
  26775. /**
  26776. * Internal function to set data for all series with enabled sorting.
  26777. *
  26778. * @private
  26779. * @function Highcharts.Chart#setSeriesData
  26780. */
  26781. Chart.prototype.setSeriesData = function () {
  26782. this.getSeriesOrderByLinks().forEach(function (series) {
  26783. // We need to set data for series with sorting after series init
  26784. if (!series.points && !series.data && series.enabledDataSorting) {
  26785. series.setData(series.options.data, false);
  26786. }
  26787. });
  26788. };
  26789. /**
  26790. * Sort and return chart series in order depending on the number of linked
  26791. * series.
  26792. *
  26793. * @private
  26794. * @function Highcharts.Series#getSeriesOrderByLinks
  26795. * @return {Array<Highcharts.Series>}
  26796. */
  26797. Chart.prototype.getSeriesOrderByLinks = function () {
  26798. return this.series.concat().sort(function (a, b) {
  26799. if (a.linkedSeries.length || b.linkedSeries.length) {
  26800. return b.linkedSeries.length - a.linkedSeries.length;
  26801. }
  26802. return 0;
  26803. });
  26804. };
  26805. /**
  26806. * Order all series above a given index. When series are added and ordered
  26807. * by configuration, only the last series is handled (#248, #1123, #2456,
  26808. * #6112). This function is called on series initialization and destroy.
  26809. *
  26810. * @private
  26811. * @function Highcharts.Series#orderSeries
  26812. * @param {number} [fromIndex]
  26813. * If this is given, only the series above this index are handled.
  26814. */
  26815. Chart.prototype.orderSeries = function (fromIndex) {
  26816. var series = this.series,
  26817. i = fromIndex || 0;
  26818. for (; i < series.length; i++) {
  26819. if (series[i]) {
  26820. /**
  26821. * Contains the series' index in the `Chart.series` array.
  26822. *
  26823. * @name Highcharts.Series#index
  26824. * @type {number}
  26825. * @readonly
  26826. */
  26827. series[i].index = i;
  26828. series[i].name = series[i].getName();
  26829. }
  26830. }
  26831. };
  26832. /**
  26833. * Check whether a given point is within the plot area.
  26834. *
  26835. * @function Highcharts.Chart#isInsidePlot
  26836. *
  26837. * @param {number} plotX
  26838. * Pixel x relative to the plot area.
  26839. *
  26840. * @param {number} plotY
  26841. * Pixel y relative to the plot area.
  26842. *
  26843. * @param {boolean} [inverted]
  26844. * Whether the chart is inverted.
  26845. *
  26846. * @return {boolean}
  26847. * Returns true if the given point is inside the plot area.
  26848. */
  26849. Chart.prototype.isInsidePlot = function (plotX, plotY, inverted) {
  26850. var x = inverted ? plotY : plotX,
  26851. y = inverted ? plotX : plotY,
  26852. e = {
  26853. x: x,
  26854. y: y,
  26855. isInsidePlot: x >= 0 &&
  26856. x <= this.plotWidth &&
  26857. y >= 0 &&
  26858. y <= this.plotHeight
  26859. };
  26860. fireEvent(this, 'afterIsInsidePlot', e);
  26861. return e.isInsidePlot;
  26862. };
  26863. /**
  26864. * Redraw the chart after changes have been done to the data, axis extremes
  26865. * chart size or chart elements. All methods for updating axes, series or
  26866. * points have a parameter for redrawing the chart. This is `true` by
  26867. * default. But in many cases you want to do more than one operation on the
  26868. * chart before redrawing, for example add a number of points. In those
  26869. * cases it is a waste of resources to redraw the chart for each new point
  26870. * added. So you add the points and call `chart.redraw()` after.
  26871. *
  26872. * @function Highcharts.Chart#redraw
  26873. *
  26874. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  26875. * If or how to apply animation to the redraw.
  26876. *
  26877. * @fires Highcharts.Chart#event:afterSetExtremes
  26878. * @fires Highcharts.Chart#event:beforeRedraw
  26879. * @fires Highcharts.Chart#event:predraw
  26880. * @fires Highcharts.Chart#event:redraw
  26881. * @fires Highcharts.Chart#event:render
  26882. * @fires Highcharts.Chart#event:updatedData
  26883. */
  26884. Chart.prototype.redraw = function (animation) {
  26885. fireEvent(this, 'beforeRedraw');
  26886. var chart = this,
  26887. axes = chart.axes,
  26888. series = chart.series,
  26889. pointer = chart.pointer,
  26890. legend = chart.legend,
  26891. legendUserOptions = chart.userOptions.legend,
  26892. redrawLegend = chart.isDirtyLegend,
  26893. hasStackedSeries,
  26894. hasDirtyStacks,
  26895. hasCartesianSeries = chart.hasCartesianSeries,
  26896. isDirtyBox = chart.isDirtyBox,
  26897. i,
  26898. serie,
  26899. renderer = chart.renderer,
  26900. isHiddenChart = renderer.isHidden(),
  26901. afterRedraw = [];
  26902. // Handle responsive rules, not only on resize (#6130)
  26903. if (chart.setResponsive) {
  26904. chart.setResponsive(false);
  26905. }
  26906. // Set the global animation. When chart.hasRendered is not true, the
  26907. // redraw call comes from a responsive rule and animation should not
  26908. // occur.
  26909. setAnimation(chart.hasRendered ? animation : false, chart);
  26910. if (isHiddenChart) {
  26911. chart.temporaryDisplay();
  26912. }
  26913. // Adjust title layout (reflow multiline text)
  26914. chart.layOutTitles();
  26915. // link stacked series
  26916. i = series.length;
  26917. while (i--) {
  26918. serie = series[i];
  26919. if (serie.options.stacking) {
  26920. hasStackedSeries = true;
  26921. if (serie.isDirty) {
  26922. hasDirtyStacks = true;
  26923. break;
  26924. }
  26925. }
  26926. }
  26927. if (hasDirtyStacks) { // mark others as dirty
  26928. i = series.length;
  26929. while (i--) {
  26930. serie = series[i];
  26931. if (serie.options.stacking) {
  26932. serie.isDirty = true;
  26933. }
  26934. }
  26935. }
  26936. // Handle updated data in the series
  26937. series.forEach(function (serie) {
  26938. if (serie.isDirty) {
  26939. if (serie.options.legendType === 'point') {
  26940. if (serie.updateTotals) {
  26941. serie.updateTotals();
  26942. }
  26943. redrawLegend = true;
  26944. }
  26945. else if (legendUserOptions &&
  26946. (legendUserOptions.labelFormatter ||
  26947. legendUserOptions.labelFormat)) {
  26948. redrawLegend = true; // #2165
  26949. }
  26950. }
  26951. if (serie.isDirtyData) {
  26952. fireEvent(serie, 'updatedData');
  26953. }
  26954. });
  26955. // handle added or removed series
  26956. if (redrawLegend && legend && legend.options.enabled) {
  26957. // draw legend graphics
  26958. legend.render();
  26959. chart.isDirtyLegend = false;
  26960. }
  26961. // reset stacks
  26962. if (hasStackedSeries) {
  26963. chart.getStacks();
  26964. }
  26965. if (hasCartesianSeries) {
  26966. // set axes scales
  26967. axes.forEach(function (axis) {
  26968. // Don't do setScale again if we're only resizing. Regression
  26969. // #13507. But we need it after chart.update (responsive), as
  26970. // axis is initialized again (#12137).
  26971. if (!chart.isResizing || !isNumber(axis.min)) {
  26972. axis.updateNames();
  26973. axis.setScale();
  26974. }
  26975. });
  26976. }
  26977. chart.getMargins(); // #3098
  26978. if (hasCartesianSeries) {
  26979. // If one axis is dirty, all axes must be redrawn (#792, #2169)
  26980. axes.forEach(function (axis) {
  26981. if (axis.isDirty) {
  26982. isDirtyBox = true;
  26983. }
  26984. });
  26985. // redraw axes
  26986. axes.forEach(function (axis) {
  26987. // Fire 'afterSetExtremes' only if extremes are set
  26988. var key = axis.min + ',' + axis.max;
  26989. if (axis.extKey !== key) { // #821, #4452
  26990. axis.extKey = key;
  26991. // prevent a recursive call to chart.redraw() (#1119)
  26992. afterRedraw.push(function () {
  26993. fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751
  26994. delete axis.eventArgs;
  26995. });
  26996. }
  26997. if (isDirtyBox || hasStackedSeries) {
  26998. axis.redraw();
  26999. }
  27000. });
  27001. }
  27002. // the plot areas size has changed
  27003. if (isDirtyBox) {
  27004. chart.drawChartBox();
  27005. }
  27006. // Fire an event before redrawing series, used by the boost module to
  27007. // clear previous series renderings.
  27008. fireEvent(chart, 'predraw');
  27009. // redraw affected series
  27010. series.forEach(function (serie) {
  27011. if ((isDirtyBox || serie.isDirty) && serie.visible) {
  27012. serie.redraw();
  27013. }
  27014. // Set it here, otherwise we will have unlimited 'updatedData' calls
  27015. // for a hidden series after setData(). Fixes #6012
  27016. serie.isDirtyData = false;
  27017. });
  27018. // move tooltip or reset
  27019. if (pointer) {
  27020. pointer.reset(true);
  27021. }
  27022. // redraw if canvas
  27023. renderer.draw();
  27024. // Fire the events
  27025. fireEvent(chart, 'redraw');
  27026. fireEvent(chart, 'render');
  27027. if (isHiddenChart) {
  27028. chart.temporaryDisplay(true);
  27029. }
  27030. // Fire callbacks that are put on hold until after the redraw
  27031. afterRedraw.forEach(function (callback) {
  27032. callback.call();
  27033. });
  27034. };
  27035. /**
  27036. * Get an axis, series or point object by `id` as given in the configuration
  27037. * options. Returns `undefined` if no item is found.
  27038. *
  27039. * @sample highcharts/plotoptions/series-id/
  27040. * Get series by id
  27041. *
  27042. * @function Highcharts.Chart#get
  27043. *
  27044. * @param {string} id
  27045. * The id as given in the configuration options.
  27046. *
  27047. * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
  27048. * The retrieved item.
  27049. */
  27050. Chart.prototype.get = function (id) {
  27051. var ret,
  27052. series = this.series,
  27053. i;
  27054. /**
  27055. * @private
  27056. * @param {Highcharts.Axis|Highcharts.Series} item
  27057. * @return {boolean}
  27058. */
  27059. function itemById(item) {
  27060. return (item.id === id ||
  27061. (item.options && item.options.id === id));
  27062. }
  27063. ret =
  27064. // Search axes
  27065. find(this.axes, itemById) ||
  27066. // Search series
  27067. find(this.series, itemById);
  27068. // Search points
  27069. for (i = 0; !ret && i < series.length; i++) {
  27070. ret = find(series[i].points || [], itemById);
  27071. }
  27072. return ret;
  27073. };
  27074. /**
  27075. * Create the Axis instances based on the config options.
  27076. *
  27077. * @private
  27078. * @function Highcharts.Chart#getAxes
  27079. * @fires Highcharts.Chart#event:afterGetAxes
  27080. * @fires Highcharts.Chart#event:getAxes
  27081. */
  27082. Chart.prototype.getAxes = function () {
  27083. var chart = this,
  27084. options = this.options,
  27085. xAxisOptions = options.xAxis = splat(options.xAxis || {}),
  27086. yAxisOptions = options.yAxis = splat(options.yAxis || {}),
  27087. optionsArray;
  27088. fireEvent(this, 'getAxes');
  27089. // make sure the options are arrays and add some members
  27090. xAxisOptions.forEach(function (axis, i) {
  27091. axis.index = i;
  27092. axis.isX = true;
  27093. });
  27094. yAxisOptions.forEach(function (axis, i) {
  27095. axis.index = i;
  27096. });
  27097. // concatenate all axis options into one array
  27098. optionsArray = xAxisOptions.concat(yAxisOptions);
  27099. optionsArray.forEach(function (axisOptions) {
  27100. new Axis(chart, axisOptions); // eslint-disable-line no-new
  27101. });
  27102. fireEvent(this, 'afterGetAxes');
  27103. };
  27104. /**
  27105. * Returns an array of all currently selected points in the chart. Points
  27106. * can be selected by clicking or programmatically by the
  27107. * {@link Highcharts.Point#select}
  27108. * function.
  27109. *
  27110. * @sample highcharts/plotoptions/series-allowpointselect-line/
  27111. * Get selected points
  27112. *
  27113. * @function Highcharts.Chart#getSelectedPoints
  27114. *
  27115. * @return {Array<Highcharts.Point>}
  27116. * The currently selected points.
  27117. */
  27118. Chart.prototype.getSelectedPoints = function () {
  27119. var points = [];
  27120. this.series.forEach(function (serie) {
  27121. // For one-to-one points inspect series.data in order to retrieve
  27122. // points outside the visible range (#6445). For grouped data,
  27123. // inspect the generated series.points.
  27124. points = points.concat(serie.getPointsCollection().filter(function (point) {
  27125. return pick(point.selectedStaging, point.selected);
  27126. }));
  27127. });
  27128. return points;
  27129. };
  27130. /**
  27131. * Returns an array of all currently selected series in the chart. Series
  27132. * can be selected either programmatically by the
  27133. * {@link Highcharts.Series#select}
  27134. * function or by checking the checkbox next to the legend item if
  27135. * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
  27136. * is true.
  27137. *
  27138. * @sample highcharts/members/chart-getselectedseries/
  27139. * Get selected series
  27140. *
  27141. * @function Highcharts.Chart#getSelectedSeries
  27142. *
  27143. * @return {Array<Highcharts.Series>}
  27144. * The currently selected series.
  27145. */
  27146. Chart.prototype.getSelectedSeries = function () {
  27147. return this.series.filter(function (serie) {
  27148. return serie.selected;
  27149. });
  27150. };
  27151. /**
  27152. * Set a new title or subtitle for the chart.
  27153. *
  27154. * @sample highcharts/members/chart-settitle/
  27155. * Set title text and styles
  27156. *
  27157. * @function Highcharts.Chart#setTitle
  27158. *
  27159. * @param {Highcharts.TitleOptions} [titleOptions]
  27160. * New title options. The title text itself is set by the
  27161. * `titleOptions.text` property.
  27162. *
  27163. * @param {Highcharts.SubtitleOptions} [subtitleOptions]
  27164. * New subtitle options. The subtitle text itself is set by the
  27165. * `subtitleOptions.text` property.
  27166. *
  27167. * @param {boolean} [redraw]
  27168. * Whether to redraw the chart or wait for a later call to
  27169. * `chart.redraw()`.
  27170. */
  27171. Chart.prototype.setTitle = function (titleOptions, subtitleOptions, redraw) {
  27172. this.applyDescription('title', titleOptions);
  27173. this.applyDescription('subtitle', subtitleOptions);
  27174. // The initial call also adds the caption. On update, chart.update will
  27175. // relay to Chart.setCaption.
  27176. this.applyDescription('caption', void 0);
  27177. this.layOutTitles(redraw);
  27178. };
  27179. /**
  27180. * Apply a title, subtitle or caption for the chart
  27181. *
  27182. * @private
  27183. * @function Highcharts.Chart#applyDescription
  27184. * @param name {string}
  27185. * Either title, subtitle or caption
  27186. * @param {Highcharts.TitleOptions|Highcharts.SubtitleOptions|Highcharts.CaptionOptions|undefined} explicitOptions
  27187. * The options to set, will be merged with default options.
  27188. */
  27189. Chart.prototype.applyDescription = function (name, explicitOptions) {
  27190. var chart = this;
  27191. // Default style
  27192. var style = name === 'title' ? {
  27193. color: '#333333',
  27194. fontSize: this.options.isStock ? '16px' : '18px' // #2944
  27195. } : {
  27196. color: '#666666'
  27197. };
  27198. // Merge default options with explicit options
  27199. var options = this.options[name] = merge(
  27200. // Default styles
  27201. (!this.styledMode && { style: style }),
  27202. this.options[name],
  27203. explicitOptions);
  27204. var elem = this[name];
  27205. if (elem && explicitOptions) {
  27206. this[name] = elem = elem.destroy(); // remove old
  27207. }
  27208. if (options && !elem) {
  27209. elem = this.renderer.text(options.text, 0, 0, options.useHTML)
  27210. .attr({
  27211. align: options.align,
  27212. 'class': 'highcharts-' + name,
  27213. zIndex: options.zIndex || 4
  27214. })
  27215. .add();
  27216. // Update methods, shortcut to Chart.setTitle, Chart.setSubtitle and
  27217. // Chart.setCaption
  27218. elem.update = function (updateOptions) {
  27219. var fn = {
  27220. title: 'setTitle',
  27221. subtitle: 'setSubtitle',
  27222. caption: 'setCaption'
  27223. }[name];
  27224. chart[fn](updateOptions);
  27225. };
  27226. // Presentational
  27227. if (!this.styledMode) {
  27228. elem.css(options.style);
  27229. }
  27230. /**
  27231. * The chart title. The title has an `update` method that allows
  27232. * modifying the options directly or indirectly via
  27233. * `chart.update`.
  27234. *
  27235. * @sample highcharts/members/title-update/
  27236. * Updating titles
  27237. *
  27238. * @name Highcharts.Chart#title
  27239. * @type {Highcharts.TitleObject}
  27240. */
  27241. /**
  27242. * The chart subtitle. The subtitle has an `update` method that
  27243. * allows modifying the options directly or indirectly via
  27244. * `chart.update`.
  27245. *
  27246. * @name Highcharts.Chart#subtitle
  27247. * @type {Highcharts.SubtitleObject}
  27248. */
  27249. this[name] = elem;
  27250. }
  27251. };
  27252. /**
  27253. * Internal function to lay out the chart title, subtitle and caption, and
  27254. * cache the full offset height for use in `getMargins`. The result is
  27255. * stored in `this.titleOffset`.
  27256. *
  27257. * @private
  27258. * @function Highcharts.Chart#layOutTitles
  27259. *
  27260. * @param {boolean} [redraw=true]
  27261. * @fires Highcharts.Chart#event:afterLayOutTitles
  27262. */
  27263. Chart.prototype.layOutTitles = function (redraw) {
  27264. var titleOffset = [0, 0, 0],
  27265. requiresDirtyBox,
  27266. renderer = this.renderer,
  27267. spacingBox = this.spacingBox;
  27268. // Lay out the title and the subtitle respectively
  27269. ['title', 'subtitle', 'caption'].forEach(function (key) {
  27270. var title = this[key], titleOptions = this.options[key], verticalAlign = titleOptions.verticalAlign || 'top', offset = key === 'title' ? -3 :
  27271. // Floating subtitle (#6574)
  27272. verticalAlign === 'top' ? titleOffset[0] + 2 : 0, titleSize, height;
  27273. if (title) {
  27274. if (!this.styledMode) {
  27275. titleSize = titleOptions.style.fontSize;
  27276. }
  27277. titleSize = renderer.fontMetrics(titleSize, title).b;
  27278. title
  27279. .css({
  27280. width: (titleOptions.width ||
  27281. spacingBox.width + (titleOptions.widthAdjust || 0)) + 'px'
  27282. });
  27283. // Skip the cache for HTML (#3481, #11666)
  27284. height = Math.round(title.getBBox(titleOptions.useHTML).height);
  27285. title.align(extend({
  27286. y: verticalAlign === 'bottom' ?
  27287. titleSize :
  27288. offset + titleSize,
  27289. height: height
  27290. }, titleOptions), false, 'spacingBox');
  27291. if (!titleOptions.floating) {
  27292. if (verticalAlign === 'top') {
  27293. titleOffset[0] = Math.ceil(titleOffset[0] +
  27294. height);
  27295. }
  27296. else if (verticalAlign === 'bottom') {
  27297. titleOffset[2] = Math.ceil(titleOffset[2] +
  27298. height);
  27299. }
  27300. }
  27301. }
  27302. }, this);
  27303. // Handle title.margin and caption.margin
  27304. if (titleOffset[0] &&
  27305. (this.options.title.verticalAlign || 'top') === 'top') {
  27306. titleOffset[0] += this.options.title.margin;
  27307. }
  27308. if (titleOffset[2] &&
  27309. this.options.caption.verticalAlign === 'bottom') {
  27310. titleOffset[2] += this.options.caption.margin;
  27311. }
  27312. requiresDirtyBox = (!this.titleOffset ||
  27313. this.titleOffset.join(',') !== titleOffset.join(','));
  27314. // Used in getMargins
  27315. this.titleOffset = titleOffset;
  27316. fireEvent(this, 'afterLayOutTitles');
  27317. if (!this.isDirtyBox && requiresDirtyBox) {
  27318. this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
  27319. // Redraw if necessary (#2719, #2744)
  27320. if (this.hasRendered && pick(redraw, true) && this.isDirtyBox) {
  27321. this.redraw();
  27322. }
  27323. }
  27324. };
  27325. /**
  27326. * Internal function to get the chart width and height according to options
  27327. * and container size. Sets {@link Chart.chartWidth} and
  27328. * {@link Chart.chartHeight}.
  27329. *
  27330. * @private
  27331. * @function Highcharts.Chart#getChartSize
  27332. */
  27333. Chart.prototype.getChartSize = function () {
  27334. var chart = this,
  27335. optionsChart = chart.options.chart,
  27336. widthOption = optionsChart.width,
  27337. heightOption = optionsChart.height,
  27338. renderTo = chart.renderTo;
  27339. // Get inner width and height
  27340. if (!defined(widthOption)) {
  27341. chart.containerWidth = getStyle(renderTo, 'width');
  27342. }
  27343. if (!defined(heightOption)) {
  27344. chart.containerHeight = getStyle(renderTo, 'height');
  27345. }
  27346. /**
  27347. * The current pixel width of the chart.
  27348. *
  27349. * @name Highcharts.Chart#chartWidth
  27350. * @type {number}
  27351. */
  27352. chart.chartWidth = Math.max(// #1393
  27353. 0, widthOption || chart.containerWidth || 600 // #1460
  27354. );
  27355. /**
  27356. * The current pixel height of the chart.
  27357. *
  27358. * @name Highcharts.Chart#chartHeight
  27359. * @type {number}
  27360. */
  27361. chart.chartHeight = Math.max(0, relativeLength(heightOption, chart.chartWidth) ||
  27362. (chart.containerHeight > 1 ?
  27363. chart.containerHeight :
  27364. 400));
  27365. };
  27366. /**
  27367. * If the renderTo element has no offsetWidth, most likely one or more of
  27368. * its parents are hidden. Loop up the DOM tree to temporarily display the
  27369. * parents, then save the original display properties, and when the true
  27370. * size is retrieved, reset them. Used on first render and on redraws.
  27371. *
  27372. * @private
  27373. * @function Highcharts.Chart#temporaryDisplay
  27374. *
  27375. * @param {boolean} [revert]
  27376. * Revert to the saved original styles.
  27377. */
  27378. Chart.prototype.temporaryDisplay = function (revert) {
  27379. var node = this.renderTo,
  27380. tempStyle;
  27381. if (!revert) {
  27382. while (node && node.style) {
  27383. // When rendering to a detached node, it needs to be temporarily
  27384. // attached in order to read styling and bounding boxes (#5783,
  27385. // #7024).
  27386. if (!doc.body.contains(node) && !node.parentNode) {
  27387. node.hcOrigDetached = true;
  27388. doc.body.appendChild(node);
  27389. }
  27390. if (getStyle(node, 'display', false) === 'none' ||
  27391. node.hcOricDetached) {
  27392. node.hcOrigStyle = {
  27393. display: node.style.display,
  27394. height: node.style.height,
  27395. overflow: node.style.overflow
  27396. };
  27397. tempStyle = {
  27398. display: 'block',
  27399. overflow: 'hidden'
  27400. };
  27401. if (node !== this.renderTo) {
  27402. tempStyle.height = 0;
  27403. }
  27404. css(node, tempStyle);
  27405. // If it still doesn't have an offset width after setting
  27406. // display to block, it probably has an !important priority
  27407. // #2631, 6803
  27408. if (!node.offsetWidth) {
  27409. node.style.setProperty('display', 'block', 'important');
  27410. }
  27411. }
  27412. node = node.parentNode;
  27413. if (node === doc.body) {
  27414. break;
  27415. }
  27416. }
  27417. }
  27418. else {
  27419. while (node && node.style) {
  27420. if (node.hcOrigStyle) {
  27421. css(node, node.hcOrigStyle);
  27422. delete node.hcOrigStyle;
  27423. }
  27424. if (node.hcOrigDetached) {
  27425. doc.body.removeChild(node);
  27426. node.hcOrigDetached = false;
  27427. }
  27428. node = node.parentNode;
  27429. }
  27430. }
  27431. };
  27432. /**
  27433. * Set the {@link Chart.container|chart container's} class name, in
  27434. * addition to `highcharts-container`.
  27435. *
  27436. * @function Highcharts.Chart#setClassName
  27437. *
  27438. * @param {string} [className]
  27439. * The additional class name.
  27440. */
  27441. Chart.prototype.setClassName = function (className) {
  27442. this.container.className = 'highcharts-container ' + (className || '');
  27443. };
  27444. /**
  27445. * Get the containing element, determine the size and create the inner
  27446. * container div to hold the chart.
  27447. *
  27448. * @private
  27449. * @function Highcharts.Chart#afterGetContainer
  27450. * @fires Highcharts.Chart#event:afterGetContainer
  27451. */
  27452. Chart.prototype.getContainer = function () {
  27453. var chart = this,
  27454. container,
  27455. options = chart.options,
  27456. optionsChart = options.chart,
  27457. chartWidth,
  27458. chartHeight,
  27459. renderTo = chart.renderTo,
  27460. indexAttrName = 'data-highcharts-chart',
  27461. oldChartIndex,
  27462. Ren,
  27463. containerId = uniqueKey(),
  27464. containerStyle,
  27465. key;
  27466. if (!renderTo) {
  27467. chart.renderTo = renderTo =
  27468. optionsChart.renderTo;
  27469. }
  27470. if (isString(renderTo)) {
  27471. chart.renderTo = renderTo =
  27472. doc.getElementById(renderTo);
  27473. }
  27474. // Display an error if the renderTo is wrong
  27475. if (!renderTo) {
  27476. error(13, true, chart);
  27477. }
  27478. // If the container already holds a chart, destroy it. The check for
  27479. // hasRendered is there because web pages that are saved to disk from
  27480. // the browser, will preserve the data-highcharts-chart attribute and
  27481. // the SVG contents, but not an interactive chart. So in this case,
  27482. // charts[oldChartIndex] will point to the wrong chart if any (#2609).
  27483. oldChartIndex = pInt(attr(renderTo, indexAttrName));
  27484. if (isNumber(oldChartIndex) &&
  27485. charts[oldChartIndex] &&
  27486. charts[oldChartIndex].hasRendered) {
  27487. charts[oldChartIndex].destroy();
  27488. }
  27489. // Make a reference to the chart from the div
  27490. attr(renderTo, indexAttrName, chart.index);
  27491. // remove previous chart
  27492. renderTo.innerHTML = '';
  27493. // If the container doesn't have an offsetWidth, it has or is a child of
  27494. // a node that has display:none. We need to temporarily move it out to a
  27495. // visible state to determine the size, else the legend and tooltips
  27496. // won't render properly. The skipClone option is used in sparklines as
  27497. // a micro optimization, saving about 1-2 ms each chart.
  27498. if (!optionsChart.skipClone && !renderTo.offsetWidth) {
  27499. chart.temporaryDisplay();
  27500. }
  27501. // get the width and height
  27502. chart.getChartSize();
  27503. chartWidth = chart.chartWidth;
  27504. chartHeight = chart.chartHeight;
  27505. // Allow table cells and flex-boxes to shrink without the chart blocking
  27506. // them out (#6427)
  27507. css(renderTo, { overflow: 'hidden' });
  27508. // Create the inner container
  27509. if (!chart.styledMode) {
  27510. containerStyle = extend({
  27511. position: 'relative',
  27512. // needed for context menu (avoidscrollbars) and content
  27513. // overflow in IE
  27514. overflow: 'hidden',
  27515. width: chartWidth + 'px',
  27516. height: chartHeight + 'px',
  27517. textAlign: 'left',
  27518. lineHeight: 'normal',
  27519. zIndex: 0,
  27520. '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
  27521. userSelect: 'none' // #13503
  27522. }, optionsChart.style);
  27523. }
  27524. /**
  27525. * The containing HTML element of the chart. The container is
  27526. * dynamically inserted into the element given as the `renderTo`
  27527. * parameter in the {@link Highcharts#chart} constructor.
  27528. *
  27529. * @name Highcharts.Chart#container
  27530. * @type {Highcharts.HTMLDOMElement}
  27531. */
  27532. container = createElement('div', {
  27533. id: containerId
  27534. }, containerStyle, renderTo);
  27535. chart.container = container;
  27536. // cache the cursor (#1650)
  27537. chart._cursor = container.style.cursor;
  27538. // Initialize the renderer
  27539. Ren = H[optionsChart.renderer] || H.Renderer;
  27540. /**
  27541. * The renderer instance of the chart. Each chart instance has only one
  27542. * associated renderer.
  27543. *
  27544. * @name Highcharts.Chart#renderer
  27545. * @type {Highcharts.SVGRenderer}
  27546. */
  27547. chart.renderer = new Ren(container, chartWidth, chartHeight, null, optionsChart.forExport, options.exporting && options.exporting.allowHTML, chart.styledMode);
  27548. // Set the initial animation from the options
  27549. setAnimation(void 0, chart);
  27550. chart.setClassName(optionsChart.className);
  27551. if (!chart.styledMode) {
  27552. chart.renderer.setStyle(optionsChart.style);
  27553. }
  27554. else {
  27555. // Initialize definitions
  27556. for (key in options.defs) { // eslint-disable-line guard-for-in
  27557. this.renderer.definition(options.defs[key]);
  27558. }
  27559. }
  27560. // Add a reference to the charts index
  27561. chart.renderer.chartIndex = chart.index;
  27562. fireEvent(this, 'afterGetContainer');
  27563. };
  27564. /**
  27565. * Calculate margins by rendering axis labels in a preliminary position.
  27566. * Title, subtitle and legend have already been rendered at this stage, but
  27567. * will be moved into their final positions.
  27568. *
  27569. * @private
  27570. * @function Highcharts.Chart#getMargins
  27571. * @fires Highcharts.Chart#event:getMargins
  27572. */
  27573. Chart.prototype.getMargins = function (skipAxes) {
  27574. var _a = this,
  27575. spacing = _a.spacing,
  27576. margin = _a.margin,
  27577. titleOffset = _a.titleOffset;
  27578. this.resetMargins();
  27579. // Adjust for title and subtitle
  27580. if (titleOffset[0] && !defined(margin[0])) {
  27581. this.plotTop = Math.max(this.plotTop, titleOffset[0] + spacing[0]);
  27582. }
  27583. if (titleOffset[2] && !defined(margin[2])) {
  27584. this.marginBottom = Math.max(this.marginBottom, titleOffset[2] + spacing[2]);
  27585. }
  27586. // Adjust for legend
  27587. if (this.legend && this.legend.display) {
  27588. this.legend.adjustMargins(margin, spacing);
  27589. }
  27590. fireEvent(this, 'getMargins');
  27591. if (!skipAxes) {
  27592. this.getAxisMargins();
  27593. }
  27594. };
  27595. /**
  27596. * @private
  27597. * @function Highcharts.Chart#getAxisMargins
  27598. */
  27599. Chart.prototype.getAxisMargins = function () {
  27600. var chart = this,
  27601. // [top, right, bottom, left]
  27602. axisOffset = chart.axisOffset = [0, 0, 0, 0],
  27603. colorAxis = chart.colorAxis,
  27604. margin = chart.margin,
  27605. getOffset = function (axes) {
  27606. axes.forEach(function (axis) {
  27607. if (axis.visible) {
  27608. axis.getOffset();
  27609. }
  27610. });
  27611. };
  27612. // pre-render axes to get labels offset width
  27613. if (chart.hasCartesianSeries) {
  27614. getOffset(chart.axes);
  27615. }
  27616. else if (colorAxis && colorAxis.length) {
  27617. getOffset(colorAxis);
  27618. }
  27619. // Add the axis offsets
  27620. marginNames.forEach(function (m, side) {
  27621. if (!defined(margin[side])) {
  27622. chart[m] += axisOffset[side];
  27623. }
  27624. });
  27625. chart.setChartSize();
  27626. };
  27627. /**
  27628. * Reflows the chart to its container. By default, the chart reflows
  27629. * automatically to its container following a `window.resize` event, as per
  27630. * the [chart.reflow](https://api.highcharts.com/highcharts/chart.reflow)
  27631. * option. However, there are no reliable events for div resize, so if the
  27632. * container is resized without a window resize event, this must be called
  27633. * explicitly.
  27634. *
  27635. * @sample highcharts/members/chart-reflow/
  27636. * Resize div and reflow
  27637. * @sample highcharts/chart/events-container/
  27638. * Pop up and reflow
  27639. *
  27640. * @function Highcharts.Chart#reflow
  27641. *
  27642. * @param {global.Event} [e]
  27643. * Event arguments. Used primarily when the function is called
  27644. * internally as a response to window resize.
  27645. */
  27646. Chart.prototype.reflow = function (e) {
  27647. var chart = this, optionsChart = chart.options.chart, renderTo = chart.renderTo, hasUserSize = (defined(optionsChart.width) &&
  27648. defined(optionsChart.height)), width = optionsChart.width || getStyle(renderTo, 'width'), height = optionsChart.height || getStyle(renderTo, 'height'), target = e ? e.target : win;
  27649. // Width and height checks for display:none. Target is doc in IE8 and
  27650. // Opera, win in Firefox, Chrome and IE9.
  27651. if (!hasUserSize &&
  27652. !chart.isPrinting &&
  27653. width &&
  27654. height &&
  27655. (target === win || target === doc)) {
  27656. if (width !== chart.containerWidth ||
  27657. height !== chart.containerHeight) {
  27658. U.clearTimeout(chart.reflowTimeout);
  27659. // When called from window.resize, e is set, else it's called
  27660. // directly (#2224)
  27661. chart.reflowTimeout = syncTimeout(function () {
  27662. // Set size, it may have been destroyed in the meantime
  27663. // (#1257)
  27664. if (chart.container) {
  27665. chart.setSize(void 0, void 0, false);
  27666. }
  27667. }, e ? 100 : 0);
  27668. }
  27669. chart.containerWidth = width;
  27670. chart.containerHeight = height;
  27671. }
  27672. };
  27673. /**
  27674. * Toggle the event handlers necessary for auto resizing, depending on the
  27675. * `chart.reflow` option.
  27676. *
  27677. * @private
  27678. * @function Highcharts.Chart#setReflow
  27679. */
  27680. Chart.prototype.setReflow = function (reflow) {
  27681. var chart = this;
  27682. if (reflow !== false && !this.unbindReflow) {
  27683. this.unbindReflow = addEvent(win, 'resize', function (e) {
  27684. // a removed event listener still runs in Edge and IE if the
  27685. // listener was removed while the event runs, so check if the
  27686. // chart is not destroyed (#11609)
  27687. if (chart.options) {
  27688. chart.reflow(e);
  27689. }
  27690. });
  27691. addEvent(this, 'destroy', this.unbindReflow);
  27692. }
  27693. else if (reflow === false && this.unbindReflow) {
  27694. // Unbind and unset
  27695. this.unbindReflow = this.unbindReflow();
  27696. }
  27697. // The following will add listeners to re-fit the chart before and after
  27698. // printing (#2284). However it only works in WebKit. Should have worked
  27699. // in Firefox, but not supported in IE.
  27700. /*
  27701. if (win.matchMedia) {
  27702. win.matchMedia('print').addListener(function reflow() {
  27703. chart.reflow();
  27704. });
  27705. }
  27706. //*/
  27707. };
  27708. /**
  27709. * Resize the chart to a given width and height. In order to set the width
  27710. * only, the height argument may be skipped. To set the height only, pass
  27711. * `undefined` for the width.
  27712. *
  27713. * @sample highcharts/members/chart-setsize-button/
  27714. * Test resizing from buttons
  27715. * @sample highcharts/members/chart-setsize-jquery-resizable/
  27716. * Add a jQuery UI resizable
  27717. * @sample stock/members/chart-setsize/
  27718. * Highstock with UI resizable
  27719. *
  27720. * @function Highcharts.Chart#setSize
  27721. *
  27722. * @param {number|null} [width]
  27723. * The new pixel width of the chart. Since v4.2.6, the argument can
  27724. * be `undefined` in order to preserve the current value (when
  27725. * setting height only), or `null` to adapt to the width of the
  27726. * containing element.
  27727. *
  27728. * @param {number|null} [height]
  27729. * The new pixel height of the chart. Since v4.2.6, the argument can
  27730. * be `undefined` in order to preserve the current value, or `null`
  27731. * in order to adapt to the height of the containing element.
  27732. *
  27733. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  27734. * Whether and how to apply animation.
  27735. *
  27736. * @return {void}
  27737. *
  27738. * @fires Highcharts.Chart#event:endResize
  27739. * @fires Highcharts.Chart#event:resize
  27740. */
  27741. Chart.prototype.setSize = function (width, height, animation) {
  27742. var chart = this,
  27743. renderer = chart.renderer,
  27744. globalAnimation;
  27745. // Handle the isResizing counter
  27746. chart.isResizing += 1;
  27747. // set the animation for the current process
  27748. setAnimation(animation, chart);
  27749. globalAnimation = renderer.globalAnimation;
  27750. chart.oldChartHeight = chart.chartHeight;
  27751. chart.oldChartWidth = chart.chartWidth;
  27752. if (typeof width !== 'undefined') {
  27753. chart.options.chart.width = width;
  27754. }
  27755. if (typeof height !== 'undefined') {
  27756. chart.options.chart.height = height;
  27757. }
  27758. chart.getChartSize();
  27759. // Resize the container with the global animation applied if enabled
  27760. // (#2503)
  27761. if (!chart.styledMode) {
  27762. (globalAnimation ? animate : css)(chart.container, {
  27763. width: chart.chartWidth + 'px',
  27764. height: chart.chartHeight + 'px'
  27765. }, globalAnimation);
  27766. }
  27767. chart.setChartSize(true);
  27768. renderer.setSize(chart.chartWidth, chart.chartHeight, globalAnimation);
  27769. // handle axes
  27770. chart.axes.forEach(function (axis) {
  27771. axis.isDirty = true;
  27772. axis.setScale();
  27773. });
  27774. chart.isDirtyLegend = true; // force legend redraw
  27775. chart.isDirtyBox = true; // force redraw of plot and chart border
  27776. chart.layOutTitles(); // #2857
  27777. chart.getMargins();
  27778. chart.redraw(globalAnimation);
  27779. chart.oldChartHeight = null;
  27780. fireEvent(chart, 'resize');
  27781. // Fire endResize and set isResizing back. If animation is disabled,
  27782. // fire without delay
  27783. syncTimeout(function () {
  27784. if (chart) {
  27785. fireEvent(chart, 'endResize', null, function () {
  27786. chart.isResizing -= 1;
  27787. });
  27788. }
  27789. }, animObject(globalAnimation).duration);
  27790. };
  27791. /**
  27792. * Set the public chart properties. This is done before and after the
  27793. * pre-render to determine margin sizes.
  27794. *
  27795. * @private
  27796. * @function Highcharts.Chart#setChartSize
  27797. * @fires Highcharts.Chart#event:afterSetChartSize
  27798. */
  27799. Chart.prototype.setChartSize = function (skipAxes) {
  27800. var chart = this,
  27801. inverted = chart.inverted,
  27802. renderer = chart.renderer,
  27803. chartWidth = chart.chartWidth,
  27804. chartHeight = chart.chartHeight,
  27805. optionsChart = chart.options.chart,
  27806. spacing = chart.spacing,
  27807. clipOffset = chart.clipOffset,
  27808. clipX,
  27809. clipY,
  27810. plotLeft,
  27811. plotTop,
  27812. plotWidth,
  27813. plotHeight,
  27814. plotBorderWidth;
  27815. /**
  27816. * The current left position of the plot area in pixels.
  27817. *
  27818. * @name Highcharts.Chart#plotLeft
  27819. * @type {number}
  27820. */
  27821. chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
  27822. /**
  27823. * The current top position of the plot area in pixels.
  27824. *
  27825. * @name Highcharts.Chart#plotTop
  27826. * @type {number}
  27827. */
  27828. chart.plotTop = plotTop = Math.round(chart.plotTop);
  27829. /**
  27830. * The current width of the plot area in pixels.
  27831. *
  27832. * @name Highcharts.Chart#plotWidth
  27833. * @type {number}
  27834. */
  27835. chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
  27836. /**
  27837. * The current height of the plot area in pixels.
  27838. *
  27839. * @name Highcharts.Chart#plotHeight
  27840. * @type {number}
  27841. */
  27842. chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
  27843. chart.plotSizeX = inverted ? plotHeight : plotWidth;
  27844. chart.plotSizeY = inverted ? plotWidth : plotHeight;
  27845. chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
  27846. // Set boxes used for alignment
  27847. chart.spacingBox = renderer.spacingBox = {
  27848. x: spacing[3],
  27849. y: spacing[0],
  27850. width: chartWidth - spacing[3] - spacing[1],
  27851. height: chartHeight - spacing[0] - spacing[2]
  27852. };
  27853. chart.plotBox = renderer.plotBox = {
  27854. x: plotLeft,
  27855. y: plotTop,
  27856. width: plotWidth,
  27857. height: plotHeight
  27858. };
  27859. plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2);
  27860. clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2);
  27861. clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
  27862. chart.clipBox = {
  27863. x: clipX,
  27864. y: clipY,
  27865. width: Math.floor(chart.plotSizeX -
  27866. Math.max(plotBorderWidth, clipOffset[1]) / 2 -
  27867. clipX),
  27868. height: Math.max(0, Math.floor(chart.plotSizeY -
  27869. Math.max(plotBorderWidth, clipOffset[2]) / 2 -
  27870. clipY))
  27871. };
  27872. if (!skipAxes) {
  27873. chart.axes.forEach(function (axis) {
  27874. axis.setAxisSize();
  27875. axis.setAxisTranslation();
  27876. });
  27877. }
  27878. fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
  27879. };
  27880. /**
  27881. * Initial margins before auto size margins are applied.
  27882. *
  27883. * @private
  27884. * @function Highcharts.Chart#resetMargins
  27885. */
  27886. Chart.prototype.resetMargins = function () {
  27887. fireEvent(this, 'resetMargins');
  27888. var chart = this,
  27889. chartOptions = chart.options.chart;
  27890. // Create margin and spacing array
  27891. ['margin', 'spacing'].forEach(function splashArrays(target) {
  27892. var value = chartOptions[target],
  27893. values = isObject(value) ? value : [value,
  27894. value,
  27895. value,
  27896. value];
  27897. [
  27898. 'Top',
  27899. 'Right',
  27900. 'Bottom',
  27901. 'Left'
  27902. ].forEach(function (sideName, side) {
  27903. chart[target][side] = pick(chartOptions[target + sideName], values[side]);
  27904. });
  27905. });
  27906. // Set margin names like chart.plotTop, chart.plotLeft,
  27907. // chart.marginRight, chart.marginBottom.
  27908. marginNames.forEach(function (m, side) {
  27909. chart[m] = pick(chart.margin[side], chart.spacing[side]);
  27910. });
  27911. chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
  27912. chart.clipOffset = [0, 0, 0, 0];
  27913. };
  27914. /**
  27915. * Internal function to draw or redraw the borders and backgrounds for chart
  27916. * and plot area.
  27917. *
  27918. * @private
  27919. * @function Highcharts.Chart#drawChartBox
  27920. * @fires Highcharts.Chart#event:afterDrawChartBox
  27921. */
  27922. Chart.prototype.drawChartBox = function () {
  27923. var chart = this,
  27924. optionsChart = chart.options.chart,
  27925. renderer = chart.renderer,
  27926. chartWidth = chart.chartWidth,
  27927. chartHeight = chart.chartHeight,
  27928. chartBackground = chart.chartBackground,
  27929. plotBackground = chart.plotBackground,
  27930. plotBorder = chart.plotBorder,
  27931. chartBorderWidth,
  27932. styledMode = chart.styledMode,
  27933. plotBGImage = chart.plotBGImage,
  27934. chartBackgroundColor = optionsChart.backgroundColor,
  27935. plotBackgroundColor = optionsChart.plotBackgroundColor,
  27936. plotBackgroundImage = optionsChart.plotBackgroundImage,
  27937. mgn,
  27938. bgAttr,
  27939. plotLeft = chart.plotLeft,
  27940. plotTop = chart.plotTop,
  27941. plotWidth = chart.plotWidth,
  27942. plotHeight = chart.plotHeight,
  27943. plotBox = chart.plotBox,
  27944. clipRect = chart.clipRect,
  27945. clipBox = chart.clipBox,
  27946. verb = 'animate';
  27947. // Chart area
  27948. if (!chartBackground) {
  27949. chart.chartBackground = chartBackground = renderer.rect()
  27950. .addClass('highcharts-background')
  27951. .add();
  27952. verb = 'attr';
  27953. }
  27954. if (!styledMode) {
  27955. // Presentational
  27956. chartBorderWidth = optionsChart.borderWidth || 0;
  27957. mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
  27958. bgAttr = {
  27959. fill: chartBackgroundColor || 'none'
  27960. };
  27961. if (chartBorderWidth || chartBackground['stroke-width']) { // #980
  27962. bgAttr.stroke = optionsChart.borderColor;
  27963. bgAttr['stroke-width'] = chartBorderWidth;
  27964. }
  27965. chartBackground
  27966. .attr(bgAttr)
  27967. .shadow(optionsChart.shadow);
  27968. }
  27969. else {
  27970. chartBorderWidth = mgn = chartBackground.strokeWidth();
  27971. }
  27972. chartBackground[verb]({
  27973. x: mgn / 2,
  27974. y: mgn / 2,
  27975. width: chartWidth - mgn - chartBorderWidth % 2,
  27976. height: chartHeight - mgn - chartBorderWidth % 2,
  27977. r: optionsChart.borderRadius
  27978. });
  27979. // Plot background
  27980. verb = 'animate';
  27981. if (!plotBackground) {
  27982. verb = 'attr';
  27983. chart.plotBackground = plotBackground = renderer.rect()
  27984. .addClass('highcharts-plot-background')
  27985. .add();
  27986. }
  27987. plotBackground[verb](plotBox);
  27988. if (!styledMode) {
  27989. // Presentational attributes for the background
  27990. plotBackground
  27991. .attr({
  27992. fill: plotBackgroundColor || 'none'
  27993. })
  27994. .shadow(optionsChart.plotShadow);
  27995. // Create the background image
  27996. if (plotBackgroundImage) {
  27997. if (!plotBGImage) {
  27998. chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight).add();
  27999. }
  28000. else {
  28001. if (plotBackgroundImage !== plotBGImage.attr('href')) {
  28002. plotBGImage.attr('href', plotBackgroundImage);
  28003. }
  28004. plotBGImage.animate(plotBox);
  28005. }
  28006. }
  28007. }
  28008. // Plot clip
  28009. if (!clipRect) {
  28010. chart.clipRect = renderer.clipRect(clipBox);
  28011. }
  28012. else {
  28013. clipRect.animate({
  28014. width: clipBox.width,
  28015. height: clipBox.height
  28016. });
  28017. }
  28018. // Plot area border
  28019. verb = 'animate';
  28020. if (!plotBorder) {
  28021. verb = 'attr';
  28022. chart.plotBorder = plotBorder = renderer.rect()
  28023. .addClass('highcharts-plot-border')
  28024. .attr({
  28025. zIndex: 1 // Above the grid
  28026. })
  28027. .add();
  28028. }
  28029. if (!styledMode) {
  28030. // Presentational
  28031. plotBorder.attr({
  28032. stroke: optionsChart.plotBorderColor,
  28033. 'stroke-width': optionsChart.plotBorderWidth || 0,
  28034. fill: 'none'
  28035. });
  28036. }
  28037. plotBorder[verb](plotBorder.crisp({
  28038. x: plotLeft,
  28039. y: plotTop,
  28040. width: plotWidth,
  28041. height: plotHeight
  28042. }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
  28043. // reset
  28044. chart.isDirtyBox = false;
  28045. fireEvent(this, 'afterDrawChartBox');
  28046. };
  28047. /**
  28048. * Detect whether a certain chart property is needed based on inspecting its
  28049. * options and series. This mainly applies to the chart.inverted property,
  28050. * and in extensions to the chart.angular and chart.polar properties.
  28051. *
  28052. * @private
  28053. * @function Highcharts.Chart#propFromSeries
  28054. * @return {void}
  28055. */
  28056. Chart.prototype.propFromSeries = function () {
  28057. var chart = this,
  28058. optionsChart = chart.options.chart,
  28059. klass,
  28060. seriesOptions = chart.options.series,
  28061. i,
  28062. value;
  28063. /**
  28064. * The flag is set to `true` if a series of the chart is inverted.
  28065. *
  28066. * @name Highcharts.Chart#inverted
  28067. * @type {boolean|undefined}
  28068. */
  28069. ['inverted', 'angular', 'polar'].forEach(function (key) {
  28070. // The default series type's class
  28071. klass = seriesTypes[(optionsChart.type ||
  28072. optionsChart.defaultSeriesType)];
  28073. // Get the value from available chart-wide properties
  28074. value =
  28075. // It is set in the options:
  28076. optionsChart[key] ||
  28077. // The default series class:
  28078. (klass && klass.prototype[key]);
  28079. // requires it
  28080. // 4. Check if any the chart's series require it
  28081. i = seriesOptions && seriesOptions.length;
  28082. while (!value && i--) {
  28083. klass = seriesTypes[seriesOptions[i].type];
  28084. if (klass && klass.prototype[key]) {
  28085. value = true;
  28086. }
  28087. }
  28088. // Set the chart property
  28089. chart[key] = value;
  28090. });
  28091. };
  28092. /**
  28093. * Internal function to link two or more series together, based on the
  28094. * `linkedTo` option. This is done from `Chart.render`, and after
  28095. * `Chart.addSeries` and `Series.remove`.
  28096. *
  28097. * @private
  28098. * @function Highcharts.Chart#linkSeries
  28099. * @fires Highcharts.Chart#event:afterLinkSeries
  28100. */
  28101. Chart.prototype.linkSeries = function () {
  28102. var chart = this,
  28103. chartSeries = chart.series;
  28104. // Reset links
  28105. chartSeries.forEach(function (series) {
  28106. series.linkedSeries.length = 0;
  28107. });
  28108. // Apply new links
  28109. chartSeries.forEach(function (series) {
  28110. var linkedTo = series.options.linkedTo;
  28111. if (isString(linkedTo)) {
  28112. if (linkedTo === ':previous') {
  28113. linkedTo = chart.series[series.index - 1];
  28114. }
  28115. else {
  28116. linkedTo = chart.get(linkedTo);
  28117. }
  28118. // #3341 avoid mutual linking
  28119. if (linkedTo && linkedTo.linkedParent !== series) {
  28120. linkedTo.linkedSeries.push(series);
  28121. series.linkedParent = linkedTo;
  28122. if (linkedTo.enabledDataSorting) {
  28123. series.setDataSortingOptions();
  28124. }
  28125. series.visible = pick(series.options.visible, linkedTo.options.visible, series.visible); // #3879
  28126. }
  28127. }
  28128. });
  28129. fireEvent(this, 'afterLinkSeries');
  28130. };
  28131. /**
  28132. * Render series for the chart.
  28133. *
  28134. * @private
  28135. * @function Highcharts.Chart#renderSeries
  28136. */
  28137. Chart.prototype.renderSeries = function () {
  28138. this.series.forEach(function (serie) {
  28139. serie.translate();
  28140. serie.render();
  28141. });
  28142. };
  28143. /**
  28144. * Render labels for the chart.
  28145. *
  28146. * @private
  28147. * @function Highcharts.Chart#renderLabels
  28148. */
  28149. Chart.prototype.renderLabels = function () {
  28150. var chart = this,
  28151. labels = chart.options.labels;
  28152. if (labels.items) {
  28153. labels.items.forEach(function (label) {
  28154. var style = extend(labels.style,
  28155. label.style),
  28156. x = pInt(style.left) + chart.plotLeft,
  28157. y = pInt(style.top) + chart.plotTop + 12;
  28158. // delete to prevent rewriting in IE
  28159. delete style.left;
  28160. delete style.top;
  28161. chart.renderer.text(label.html, x, y)
  28162. .attr({ zIndex: 2 })
  28163. .css(style)
  28164. .add();
  28165. });
  28166. }
  28167. };
  28168. /**
  28169. * Render all graphics for the chart. Runs internally on initialization.
  28170. *
  28171. * @private
  28172. * @function Highcharts.Chart#render
  28173. */
  28174. Chart.prototype.render = function () {
  28175. var chart = this,
  28176. axes = chart.axes,
  28177. colorAxis = chart.colorAxis,
  28178. renderer = chart.renderer,
  28179. options = chart.options,
  28180. correction = 0, // correction for X axis labels
  28181. tempWidth,
  28182. tempHeight,
  28183. redoHorizontal,
  28184. redoVertical,
  28185. renderAxes = function (axes) {
  28186. axes.forEach(function (axis) {
  28187. if (axis.visible) {
  28188. axis.render();
  28189. }
  28190. });
  28191. };
  28192. // Title
  28193. chart.setTitle();
  28194. /**
  28195. * The overview of the chart's series.
  28196. *
  28197. * @name Highcharts.Chart#legend
  28198. * @type {Highcharts.Legend}
  28199. */
  28200. chart.legend = new Legend(chart, options.legend);
  28201. // Get stacks
  28202. if (chart.getStacks) {
  28203. chart.getStacks();
  28204. }
  28205. // Get chart margins
  28206. chart.getMargins(true);
  28207. chart.setChartSize();
  28208. // Record preliminary dimensions for later comparison
  28209. tempWidth = chart.plotWidth;
  28210. axes.some(function (axis) {
  28211. if (axis.horiz &&
  28212. axis.visible &&
  28213. axis.options.labels.enabled &&
  28214. axis.series.length) {
  28215. // 21 is the most common correction for X axis labels
  28216. correction = 21;
  28217. return true;
  28218. }
  28219. });
  28220. // use Math.max to prevent negative plotHeight
  28221. chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
  28222. tempHeight = chart.plotHeight;
  28223. // Get margins by pre-rendering axes
  28224. axes.forEach(function (axis) {
  28225. axis.setScale();
  28226. });
  28227. chart.getAxisMargins();
  28228. // If the plot area size has changed significantly, calculate tick
  28229. // positions again
  28230. redoHorizontal = tempWidth / chart.plotWidth > 1.1;
  28231. // Height is more sensitive, use lower threshold
  28232. redoVertical = tempHeight / chart.plotHeight > 1.05;
  28233. if (redoHorizontal || redoVertical) {
  28234. axes.forEach(function (axis) {
  28235. if ((axis.horiz && redoHorizontal) ||
  28236. (!axis.horiz && redoVertical)) {
  28237. // update to reflect the new margins
  28238. axis.setTickInterval(true);
  28239. }
  28240. });
  28241. chart.getMargins(); // second pass to check for new labels
  28242. }
  28243. // Draw the borders and backgrounds
  28244. chart.drawChartBox();
  28245. // Axes
  28246. if (chart.hasCartesianSeries) {
  28247. renderAxes(axes);
  28248. }
  28249. else if (colorAxis && colorAxis.length) {
  28250. renderAxes(colorAxis);
  28251. }
  28252. // The series
  28253. if (!chart.seriesGroup) {
  28254. chart.seriesGroup = renderer.g('series-group')
  28255. .attr({ zIndex: 3 })
  28256. .add();
  28257. }
  28258. chart.renderSeries();
  28259. // Labels
  28260. chart.renderLabels();
  28261. // Credits
  28262. chart.addCredits();
  28263. // Handle responsiveness
  28264. if (chart.setResponsive) {
  28265. chart.setResponsive();
  28266. }
  28267. // Handle scaling
  28268. chart.updateContainerScaling();
  28269. // Set flag
  28270. chart.hasRendered = true;
  28271. };
  28272. /**
  28273. * Set a new credits label for the chart.
  28274. *
  28275. * @sample highcharts/credits/credits-update/
  28276. * Add and update credits
  28277. *
  28278. * @function Highcharts.Chart#addCredits
  28279. *
  28280. * @param {Highcharts.CreditsOptions} [credits]
  28281. * A configuration object for the new credits.
  28282. */
  28283. Chart.prototype.addCredits = function (credits) {
  28284. var chart = this,
  28285. creds = merge(true,
  28286. this.options.credits,
  28287. credits);
  28288. if (creds.enabled && !this.credits) {
  28289. /**
  28290. * The chart's credits label. The label has an `update` method that
  28291. * allows setting new options as per the
  28292. * [credits options set](https://api.highcharts.com/highcharts/credits).
  28293. *
  28294. * @name Highcharts.Chart#credits
  28295. * @type {Highcharts.SVGElement}
  28296. */
  28297. this.credits = this.renderer.text(creds.text + (this.mapCredits || ''), 0, 0)
  28298. .addClass('highcharts-credits')
  28299. .on('click', function () {
  28300. if (creds.href) {
  28301. win.location.href = creds.href;
  28302. }
  28303. })
  28304. .attr({
  28305. align: creds.position.align,
  28306. zIndex: 8
  28307. });
  28308. if (!chart.styledMode) {
  28309. this.credits.css(creds.style);
  28310. }
  28311. this.credits
  28312. .add()
  28313. .align(creds.position);
  28314. // Dynamically update
  28315. this.credits.update = function (options) {
  28316. chart.credits = chart.credits.destroy();
  28317. chart.addCredits(options);
  28318. };
  28319. }
  28320. };
  28321. /**
  28322. * Handle scaling, #11329 - when there is scaling/transform on the container
  28323. * or on a parent element, we need to take this into account. We calculate
  28324. * the scaling once here and it is picked up where we need to use it
  28325. * (Pointer, Tooltip).
  28326. *
  28327. * @private
  28328. * @function Highcharts.Chart#updateContainerScaling
  28329. */
  28330. Chart.prototype.updateContainerScaling = function () {
  28331. var container = this.container;
  28332. // #13342 - tooltip was not visible in Chrome, when chart
  28333. // updates height.
  28334. if (container.offsetWidth > 2 && // #13342
  28335. container.offsetHeight > 2 && // #13342
  28336. container.getBoundingClientRect) {
  28337. var bb = container.getBoundingClientRect(),
  28338. scaleX = bb.width / container.offsetWidth,
  28339. scaleY = bb.height / container.offsetHeight;
  28340. if (scaleX !== 1 || scaleY !== 1) {
  28341. this.containerScaling = { scaleX: scaleX, scaleY: scaleY };
  28342. }
  28343. else {
  28344. delete this.containerScaling;
  28345. }
  28346. }
  28347. };
  28348. /**
  28349. * Remove the chart and purge memory. This method is called internally
  28350. * before adding a second chart into the same container, as well as on
  28351. * window unload to prevent leaks.
  28352. *
  28353. * @sample highcharts/members/chart-destroy/
  28354. * Destroy the chart from a button
  28355. * @sample stock/members/chart-destroy/
  28356. * Destroy with Highstock
  28357. *
  28358. * @function Highcharts.Chart#destroy
  28359. *
  28360. * @fires Highcharts.Chart#event:destroy
  28361. */
  28362. Chart.prototype.destroy = function () {
  28363. var chart = this,
  28364. axes = chart.axes,
  28365. series = chart.series,
  28366. container = chart.container,
  28367. i,
  28368. parentNode = container && container.parentNode;
  28369. // fire the chart.destoy event
  28370. fireEvent(chart, 'destroy');
  28371. // Delete the chart from charts lookup array
  28372. if (chart.renderer.forExport) {
  28373. erase(charts, chart); // #6569
  28374. }
  28375. else {
  28376. charts[chart.index] = void 0;
  28377. }
  28378. H.chartCount--;
  28379. chart.renderTo.removeAttribute('data-highcharts-chart');
  28380. // remove events
  28381. removeEvent(chart);
  28382. // ==== Destroy collections:
  28383. // Destroy axes
  28384. i = axes.length;
  28385. while (i--) {
  28386. axes[i] = axes[i].destroy();
  28387. }
  28388. // Destroy scroller & scroller series before destroying base series
  28389. if (this.scroller && this.scroller.destroy) {
  28390. this.scroller.destroy();
  28391. }
  28392. // Destroy each series
  28393. i = series.length;
  28394. while (i--) {
  28395. series[i] = series[i].destroy();
  28396. }
  28397. // ==== Destroy chart properties:
  28398. [
  28399. 'title', 'subtitle', 'chartBackground', 'plotBackground',
  28400. 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
  28401. 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
  28402. 'renderer'
  28403. ].forEach(function (name) {
  28404. var prop = chart[name];
  28405. if (prop && prop.destroy) {
  28406. chart[name] = prop.destroy();
  28407. }
  28408. });
  28409. // Remove container and all SVG, check container as it can break in IE
  28410. // when destroyed before finished loading
  28411. if (container) {
  28412. container.innerHTML = '';
  28413. removeEvent(container);
  28414. if (parentNode) {
  28415. discardElement(container);
  28416. }
  28417. }
  28418. // clean it all up
  28419. objectEach(chart, function (val, key) {
  28420. delete chart[key];
  28421. });
  28422. };
  28423. /**
  28424. * Prepare for first rendering after all data are loaded.
  28425. *
  28426. * @private
  28427. * @function Highcharts.Chart#firstRender
  28428. * @fires Highcharts.Chart#event:beforeRender
  28429. */
  28430. Chart.prototype.firstRender = function () {
  28431. var chart = this,
  28432. options = chart.options;
  28433. // Hook for oldIE to check whether the chart is ready to render
  28434. if (chart.isReadyToRender && !chart.isReadyToRender()) {
  28435. return;
  28436. }
  28437. // Create the container
  28438. chart.getContainer();
  28439. chart.resetMargins();
  28440. chart.setChartSize();
  28441. // Set the common chart properties (mainly invert) from the given series
  28442. chart.propFromSeries();
  28443. // get axes
  28444. chart.getAxes();
  28445. // Initialize the series
  28446. (isArray(options.series) ? options.series : []).forEach(
  28447. // #9680
  28448. function (serieOptions) {
  28449. chart.initSeries(serieOptions);
  28450. });
  28451. chart.linkSeries();
  28452. chart.setSeriesData();
  28453. // Run an event after axes and series are initialized, but before
  28454. // render. At this stage, the series data is indexed and cached in the
  28455. // xData and yData arrays, so we can access those before rendering. Used
  28456. // in Highstock.
  28457. fireEvent(chart, 'beforeRender');
  28458. // depends on inverted and on margins being set
  28459. if (Pointer) {
  28460. if (!H.hasTouch && (win.PointerEvent || win.MSPointerEvent)) {
  28461. chart.pointer = new MSPointer(chart, options);
  28462. }
  28463. else {
  28464. /**
  28465. * The Pointer that keeps track of mouse and touch interaction.
  28466. *
  28467. * @memberof Highcharts.Chart
  28468. * @name pointer
  28469. * @type {Highcharts.Pointer}
  28470. * @instance
  28471. */
  28472. chart.pointer = new Pointer(chart, options);
  28473. }
  28474. }
  28475. chart.render();
  28476. // Fire the load event if there are no external images
  28477. if (!chart.renderer.imgCount && !chart.hasLoaded) {
  28478. chart.onload();
  28479. }
  28480. // If the chart was rendered outside the top container, put it back in
  28481. // (#3679)
  28482. chart.temporaryDisplay(true);
  28483. };
  28484. /**
  28485. * Internal function that runs on chart load, async if any images are loaded
  28486. * in the chart. Runs the callbacks and triggers the `load` and `render`
  28487. * events.
  28488. *
  28489. * @private
  28490. * @function Highcharts.Chart#onload
  28491. * @fires Highcharts.Chart#event:load
  28492. * @fires Highcharts.Chart#event:render
  28493. */
  28494. Chart.prototype.onload = function () {
  28495. // Run callbacks, first the ones registered by modules, then user's one
  28496. this.callbacks.concat([this.callback]).forEach(function (fn) {
  28497. // Chart destroyed in its own callback (#3600)
  28498. if (fn && typeof this.index !== 'undefined') {
  28499. fn.apply(this, [this]);
  28500. }
  28501. }, this);
  28502. fireEvent(this, 'load');
  28503. fireEvent(this, 'render');
  28504. // Set up auto resize, check for not destroyed (#6068)
  28505. if (defined(this.index)) {
  28506. this.setReflow(this.options.chart.reflow);
  28507. }
  28508. // Don't run again
  28509. this.hasLoaded = true;
  28510. };
  28511. return Chart;
  28512. }());
  28513. // Hook for adding callbacks in modules
  28514. Chart.prototype.callbacks = [];
  28515. /**
  28516. * Factory function for basic charts.
  28517. *
  28518. * @example
  28519. * // Render a chart in to div#container
  28520. * var chart = Highcharts.chart('container', {
  28521. * title: {
  28522. * text: 'My chart'
  28523. * },
  28524. * series: [{
  28525. * data: [1, 3, 2, 4]
  28526. * }]
  28527. * });
  28528. *
  28529. * @function Highcharts.chart
  28530. *
  28531. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  28532. * The DOM element to render to, or its id.
  28533. *
  28534. * @param {Highcharts.Options} options
  28535. * The chart options structure.
  28536. *
  28537. * @param {Highcharts.ChartCallbackFunction} [callback]
  28538. * Function to run when the chart has loaded and and all external images
  28539. * are loaded. Defining a
  28540. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  28541. * handler is equivalent.
  28542. *
  28543. * @return {Highcharts.Chart}
  28544. * Returns the Chart object.
  28545. */
  28546. function chart(a, b, c) {
  28547. return new Chart(a, b, c);
  28548. }
  28549. H.chart = chart;
  28550. H.Chart = Chart;
  28551. return Chart;
  28552. });
  28553. _registerModule(_modules, 'Extensions/ScrollablePlotArea.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Chart, H, U) {
  28554. /* *
  28555. *
  28556. * (c) 2010-2020 Torstein Honsi
  28557. *
  28558. * License: www.highcharts.com/license
  28559. *
  28560. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28561. *
  28562. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  28563. * horizontally on mobile devices. Supports left and right side axes.
  28564. */
  28565. /*
  28566. WIP on vertical scrollable plot area (#9378). To do:
  28567. - Bottom axis positioning
  28568. - Test with Gantt
  28569. - Look for size optimizing the code
  28570. - API and demos
  28571. */
  28572. var addEvent = U.addEvent,
  28573. createElement = U.createElement,
  28574. pick = U.pick,
  28575. stop = U.stop;
  28576. /**
  28577. * Options for a scrollable plot area. This feature provides a minimum size for
  28578. * the plot area of the chart. If the size gets smaller than this, typically
  28579. * on mobile devices, a native browser scrollbar is presented. This scrollbar
  28580. * provides smooth scrolling for the contents of the plot area, whereas the
  28581. * title, legend and unaffected axes are fixed.
  28582. *
  28583. * Since v7.1.2, a scrollable plot area can be defined for either horizontal or
  28584. * vertical scrolling, depending on whether the `minWidth` or `minHeight`
  28585. * option is set.
  28586. *
  28587. * @sample highcharts/chart/scrollable-plotarea
  28588. * Scrollable plot area
  28589. * @sample highcharts/chart/scrollable-plotarea-vertical
  28590. * Vertically scrollable plot area
  28591. * @sample {gantt} highcharts/chart/scrollable-plotarea-vertical
  28592. * Gantt chart with vertically scrollable plot area
  28593. *
  28594. * @since 6.1.0
  28595. * @product highcharts gantt
  28596. * @apioption chart.scrollablePlotArea
  28597. */
  28598. /**
  28599. * The minimum height for the plot area. If it gets smaller than this, the plot
  28600. * area will become scrollable.
  28601. *
  28602. * @type {number}
  28603. * @apioption chart.scrollablePlotArea.minHeight
  28604. */
  28605. /**
  28606. * The minimum width for the plot area. If it gets smaller than this, the plot
  28607. * area will become scrollable.
  28608. *
  28609. * @type {number}
  28610. * @apioption chart.scrollablePlotArea.minWidth
  28611. */
  28612. /**
  28613. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  28614. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  28615. * Typically we would use 1 if the chart has right aligned Y axes.
  28616. *
  28617. * @type {number}
  28618. * @apioption chart.scrollablePlotArea.scrollPositionX
  28619. */
  28620. /**
  28621. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  28622. * 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
  28623. *
  28624. * @type {number}
  28625. * @apioption chart.scrollablePlotArea.scrollPositionY
  28626. */
  28627. /**
  28628. * The opacity of mask applied on one of the sides of the plot
  28629. * area.
  28630. *
  28631. * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
  28632. * Disabled opacity for the mask
  28633. *
  28634. * @type {number}
  28635. * @default 0.85
  28636. * @since 7.1.1
  28637. * @apioption chart.scrollablePlotArea.opacity
  28638. */
  28639. ''; // detach API doclets
  28640. /* eslint-disable no-invalid-this, valid-jsdoc */
  28641. addEvent(Chart, 'afterSetChartSize', function (e) {
  28642. var scrollablePlotArea = this.options.chart.scrollablePlotArea,
  28643. scrollableMinWidth = scrollablePlotArea && scrollablePlotArea.minWidth,
  28644. scrollableMinHeight = scrollablePlotArea && scrollablePlotArea.minHeight,
  28645. scrollablePixelsX,
  28646. scrollablePixelsY,
  28647. corrections;
  28648. if (!this.renderer.forExport) {
  28649. // The amount of pixels to scroll, the difference between chart
  28650. // width and scrollable width
  28651. if (scrollableMinWidth) {
  28652. this.scrollablePixelsX = scrollablePixelsX = Math.max(0, scrollableMinWidth - this.chartWidth);
  28653. if (scrollablePixelsX) {
  28654. this.plotWidth += scrollablePixelsX;
  28655. if (this.inverted) {
  28656. this.clipBox.height += scrollablePixelsX;
  28657. this.plotBox.height += scrollablePixelsX;
  28658. }
  28659. else {
  28660. this.clipBox.width += scrollablePixelsX;
  28661. this.plotBox.width += scrollablePixelsX;
  28662. }
  28663. corrections = {
  28664. // Corrections for right side
  28665. 1: { name: 'right', value: scrollablePixelsX }
  28666. };
  28667. }
  28668. // Currently we can only do either X or Y
  28669. }
  28670. else if (scrollableMinHeight) {
  28671. this.scrollablePixelsY = scrollablePixelsY = Math.max(0, scrollableMinHeight - this.chartHeight);
  28672. if (scrollablePixelsY) {
  28673. this.plotHeight += scrollablePixelsY;
  28674. if (this.inverted) {
  28675. this.clipBox.width += scrollablePixelsY;
  28676. this.plotBox.width += scrollablePixelsY;
  28677. }
  28678. else {
  28679. this.clipBox.height += scrollablePixelsY;
  28680. this.plotBox.height += scrollablePixelsY;
  28681. }
  28682. corrections = {
  28683. 2: { name: 'bottom', value: scrollablePixelsY }
  28684. };
  28685. }
  28686. }
  28687. if (corrections && !e.skipAxes) {
  28688. this.axes.forEach(function (axis) {
  28689. // For right and bottom axes, only fix the plot line length
  28690. if (corrections[axis.side]) {
  28691. // Get the plot lines right in getPlotLinePath,
  28692. // temporarily set it to the adjusted plot width.
  28693. axis.getPlotLinePath = function () {
  28694. var marginName = corrections[axis.side].name,
  28695. correctionValue = corrections[axis.side].value,
  28696. // axis.right or axis.bottom
  28697. margin = this[marginName],
  28698. path;
  28699. // Temporarily adjust
  28700. this[marginName] = margin - correctionValue;
  28701. path = H.Axis.prototype.getPlotLinePath.apply(this, arguments);
  28702. // Reset
  28703. this[marginName] = margin;
  28704. return path;
  28705. };
  28706. }
  28707. else {
  28708. // Apply the corrected plotWidth
  28709. axis.setAxisSize();
  28710. axis.setAxisTranslation();
  28711. }
  28712. });
  28713. }
  28714. }
  28715. });
  28716. addEvent(Chart, 'render', function () {
  28717. if (this.scrollablePixelsX || this.scrollablePixelsY) {
  28718. if (this.setUpScrolling) {
  28719. this.setUpScrolling();
  28720. }
  28721. this.applyFixed();
  28722. }
  28723. else if (this.fixedDiv) { // Has been in scrollable mode
  28724. this.applyFixed();
  28725. }
  28726. });
  28727. /**
  28728. * @private
  28729. * @function Highcharts.Chart#setUpScrolling
  28730. * @return {void}
  28731. */
  28732. Chart.prototype.setUpScrolling = function () {
  28733. var _this = this;
  28734. var attribs = {
  28735. WebkitOverflowScrolling: 'touch',
  28736. overflowX: 'hidden',
  28737. overflowY: 'hidden'
  28738. };
  28739. if (this.scrollablePixelsX) {
  28740. attribs.overflowX = 'auto';
  28741. }
  28742. if (this.scrollablePixelsY) {
  28743. attribs.overflowY = 'auto';
  28744. }
  28745. // Insert a container with position relative
  28746. // that scrolling and fixed container renders to (#10555)
  28747. this.scrollingParent = createElement('div', {
  28748. className: 'highcharts-scrolling-parent'
  28749. }, {
  28750. position: 'relative'
  28751. }, this.renderTo);
  28752. // Add the necessary divs to provide scrolling
  28753. this.scrollingContainer = createElement('div', {
  28754. 'className': 'highcharts-scrolling'
  28755. }, attribs, this.scrollingParent);
  28756. // On scroll, reset the chart position because it applies to the scrolled
  28757. // container
  28758. addEvent(this.scrollingContainer, 'scroll', function () {
  28759. if (_this.pointer) {
  28760. delete _this.pointer.chartPosition;
  28761. }
  28762. });
  28763. this.innerContainer = createElement('div', {
  28764. 'className': 'highcharts-inner-container'
  28765. }, null, this.scrollingContainer);
  28766. // Now move the container inside
  28767. this.innerContainer.appendChild(this.container);
  28768. // Don't run again
  28769. this.setUpScrolling = null;
  28770. };
  28771. /**
  28772. * These elements are moved over to the fixed renderer and stay fixed when the
  28773. * user scrolls the chart
  28774. * @private
  28775. */
  28776. Chart.prototype.moveFixedElements = function () {
  28777. var container = this.container,
  28778. fixedRenderer = this.fixedRenderer,
  28779. fixedSelectors = [
  28780. '.highcharts-contextbutton',
  28781. '.highcharts-credits',
  28782. '.highcharts-legend',
  28783. '.highcharts-legend-checkbox',
  28784. '.highcharts-navigator-series',
  28785. '.highcharts-navigator-xaxis',
  28786. '.highcharts-navigator-yaxis',
  28787. '.highcharts-navigator',
  28788. '.highcharts-reset-zoom',
  28789. '.highcharts-scrollbar',
  28790. '.highcharts-subtitle',
  28791. '.highcharts-title'
  28792. ],
  28793. axisClass;
  28794. if (this.scrollablePixelsX && !this.inverted) {
  28795. axisClass = '.highcharts-yaxis';
  28796. }
  28797. else if (this.scrollablePixelsX && this.inverted) {
  28798. axisClass = '.highcharts-xaxis';
  28799. }
  28800. else if (this.scrollablePixelsY && !this.inverted) {
  28801. axisClass = '.highcharts-xaxis';
  28802. }
  28803. else if (this.scrollablePixelsY && this.inverted) {
  28804. axisClass = '.highcharts-yaxis';
  28805. }
  28806. fixedSelectors.push(axisClass, axisClass + '-labels');
  28807. fixedSelectors.forEach(function (className) {
  28808. [].forEach.call(container.querySelectorAll(className), function (elem) {
  28809. (elem.namespaceURI === fixedRenderer.SVG_NS ?
  28810. fixedRenderer.box :
  28811. fixedRenderer.box.parentNode).appendChild(elem);
  28812. elem.style.pointerEvents = 'auto';
  28813. });
  28814. });
  28815. };
  28816. /**
  28817. * @private
  28818. * @function Highcharts.Chart#applyFixed
  28819. * @return {void}
  28820. */
  28821. Chart.prototype.applyFixed = function () {
  28822. var _a,
  28823. _b;
  28824. var fixedRenderer,
  28825. scrollableWidth,
  28826. scrollableHeight,
  28827. firstTime = !this.fixedDiv,
  28828. scrollableOptions = this.options.chart.scrollablePlotArea;
  28829. // First render
  28830. if (firstTime) {
  28831. this.fixedDiv = createElement('div', {
  28832. className: 'highcharts-fixed'
  28833. }, {
  28834. position: 'absolute',
  28835. overflow: 'hidden',
  28836. pointerEvents: 'none',
  28837. zIndex: 2,
  28838. top: 0
  28839. }, null, true);
  28840. (_a = this.scrollingContainer) === null || _a === void 0 ? void 0 : _a.parentNode.insertBefore(this.fixedDiv, this.scrollingContainer);
  28841. this.renderTo.style.overflow = 'visible';
  28842. this.fixedRenderer = fixedRenderer = new H.Renderer(this.fixedDiv, this.chartWidth, this.chartHeight, (_b = this.options.chart) === null || _b === void 0 ? void 0 : _b.style);
  28843. // Mask
  28844. this.scrollableMask = fixedRenderer
  28845. .path()
  28846. .attr({
  28847. fill: this.options.chart.backgroundColor || '#fff',
  28848. 'fill-opacity': pick(scrollableOptions.opacity, 0.85),
  28849. zIndex: -1
  28850. })
  28851. .addClass('highcharts-scrollable-mask')
  28852. .add();
  28853. this.moveFixedElements();
  28854. addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
  28855. addEvent(this, 'afterLayOutTitles', this.moveFixedElements);
  28856. }
  28857. else {
  28858. // Set the size of the fixed renderer to the visible width
  28859. this.fixedRenderer.setSize(this.chartWidth, this.chartHeight);
  28860. }
  28861. // Increase the size of the scrollable renderer and background
  28862. scrollableWidth = this.chartWidth + (this.scrollablePixelsX || 0);
  28863. scrollableHeight = this.chartHeight + (this.scrollablePixelsY || 0);
  28864. stop(this.container);
  28865. this.container.style.width = scrollableWidth + 'px';
  28866. this.container.style.height = scrollableHeight + 'px';
  28867. this.renderer.boxWrapper.attr({
  28868. width: scrollableWidth,
  28869. height: scrollableHeight,
  28870. viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
  28871. });
  28872. this.chartBackground.attr({
  28873. width: scrollableWidth,
  28874. height: scrollableHeight
  28875. });
  28876. this.scrollingContainer.style.height = this.chartHeight + 'px';
  28877. // Set scroll position
  28878. if (firstTime) {
  28879. if (scrollableOptions.scrollPositionX) {
  28880. this.scrollingContainer.scrollLeft =
  28881. this.scrollablePixelsX *
  28882. scrollableOptions.scrollPositionX;
  28883. }
  28884. if (scrollableOptions.scrollPositionY) {
  28885. this.scrollingContainer.scrollTop =
  28886. this.scrollablePixelsY *
  28887. scrollableOptions.scrollPositionY;
  28888. }
  28889. }
  28890. // Mask behind the left and right side
  28891. var axisOffset = this.axisOffset,
  28892. maskTop = this.plotTop - axisOffset[0] - 1,
  28893. maskLeft = this.plotLeft - axisOffset[3] - 1,
  28894. maskBottom = this.plotTop + this.plotHeight + axisOffset[2] + 1,
  28895. maskRight = this.plotLeft + this.plotWidth + axisOffset[1] + 1,
  28896. maskPlotRight = this.plotLeft + this.plotWidth -
  28897. (this.scrollablePixelsX || 0),
  28898. maskPlotBottom = this.plotTop + this.plotHeight -
  28899. (this.scrollablePixelsY || 0),
  28900. d;
  28901. if (this.scrollablePixelsX) {
  28902. d = [
  28903. // Left side
  28904. ['M', 0, maskTop],
  28905. ['L', this.plotLeft - 1, maskTop],
  28906. ['L', this.plotLeft - 1, maskBottom],
  28907. ['L', 0, maskBottom],
  28908. ['Z'],
  28909. // Right side
  28910. ['M', maskPlotRight, maskTop],
  28911. ['L', this.chartWidth, maskTop],
  28912. ['L', this.chartWidth, maskBottom],
  28913. ['L', maskPlotRight, maskBottom],
  28914. ['Z']
  28915. ];
  28916. }
  28917. else if (this.scrollablePixelsY) {
  28918. d = [
  28919. // Top side
  28920. ['M', maskLeft, 0],
  28921. ['L', maskLeft, this.plotTop - 1],
  28922. ['L', maskRight, this.plotTop - 1],
  28923. ['L', maskRight, 0],
  28924. ['Z'],
  28925. // Bottom side
  28926. ['M', maskLeft, maskPlotBottom],
  28927. ['L', maskLeft, this.chartHeight],
  28928. ['L', maskRight, this.chartHeight],
  28929. ['L', maskRight, maskPlotBottom],
  28930. ['Z']
  28931. ];
  28932. }
  28933. else {
  28934. d = [['M', 0, 0]];
  28935. }
  28936. if (this.redrawTrigger !== 'adjustHeight') {
  28937. this.scrollableMask.attr({ d: d });
  28938. }
  28939. };
  28940. });
  28941. _registerModule(_modules, 'Core/Axis/StackingAxis.js', [_modules['Core/Utilities.js']], function (U) {
  28942. /* *
  28943. *
  28944. * (c) 2010-2020 Torstein Honsi
  28945. *
  28946. * License: www.highcharts.com/license
  28947. *
  28948. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28949. *
  28950. * */
  28951. var addEvent = U.addEvent,
  28952. destroyObjectProperties = U.destroyObjectProperties,
  28953. fireEvent = U.fireEvent,
  28954. getDeferredAnimation = U.getDeferredAnimation,
  28955. objectEach = U.objectEach,
  28956. pick = U.pick;
  28957. /* eslint-disable valid-jsdoc */
  28958. /**
  28959. * Adds stacking support to axes.
  28960. * @private
  28961. * @class
  28962. */
  28963. var StackingAxisAdditions = /** @class */ (function () {
  28964. /* *
  28965. *
  28966. * Constructors
  28967. *
  28968. * */
  28969. function StackingAxisAdditions(axis) {
  28970. this.oldStacks = {};
  28971. this.stacks = {};
  28972. this.stacksTouched = 0;
  28973. this.axis = axis;
  28974. }
  28975. /* *
  28976. *
  28977. * Functions
  28978. *
  28979. * */
  28980. /**
  28981. * Build the stacks from top down
  28982. * @private
  28983. */
  28984. StackingAxisAdditions.prototype.buildStacks = function () {
  28985. var stacking = this;
  28986. var axis = stacking.axis;
  28987. var axisSeries = axis.series;
  28988. var reversedStacks = pick(axis.options.reversedStacks,
  28989. true);
  28990. var len = axisSeries.length;
  28991. var actualSeries,
  28992. i;
  28993. if (!axis.isXAxis) {
  28994. stacking.usePercentage = false;
  28995. i = len;
  28996. while (i--) {
  28997. actualSeries = axisSeries[reversedStacks ? i : len - i - 1];
  28998. actualSeries.setStackedPoints();
  28999. actualSeries.setGroupedPoints();
  29000. }
  29001. // Loop up again to compute percent and stream stack
  29002. for (i = 0; i < len; i++) {
  29003. axisSeries[i].modifyStacks();
  29004. }
  29005. fireEvent(axis, 'afterBuildStacks');
  29006. }
  29007. };
  29008. /**
  29009. * @private
  29010. */
  29011. StackingAxisAdditions.prototype.cleanStacks = function () {
  29012. var stacking = this;
  29013. var axis = stacking.axis;
  29014. var stacks;
  29015. if (!axis.isXAxis) {
  29016. if (stacking.oldStacks) {
  29017. stacks = stacking.stacks = stacking.oldStacks;
  29018. }
  29019. // reset stacks
  29020. objectEach(stacks, function (type) {
  29021. objectEach(type, function (stack) {
  29022. stack.cumulative = stack.total;
  29023. });
  29024. });
  29025. }
  29026. };
  29027. /**
  29028. * Set all the stacks to initial states and destroy unused ones.
  29029. * @private
  29030. */
  29031. StackingAxisAdditions.prototype.resetStacks = function () {
  29032. var stacking = this;
  29033. var axis = stacking.axis;
  29034. var stacks = stacking.stacks;
  29035. if (!axis.isXAxis) {
  29036. objectEach(stacks, function (type) {
  29037. objectEach(type, function (stack, key) {
  29038. // Clean up memory after point deletion (#1044, #4320)
  29039. if (stack.touched < stacking.stacksTouched) {
  29040. stack.destroy();
  29041. delete type[key];
  29042. // Reset stacks
  29043. }
  29044. else {
  29045. stack.total = null;
  29046. stack.cumulative = null;
  29047. }
  29048. });
  29049. });
  29050. }
  29051. };
  29052. /**
  29053. * @private
  29054. */
  29055. StackingAxisAdditions.prototype.renderStackTotals = function () {
  29056. var stacking = this;
  29057. var axis = stacking.axis;
  29058. var chart = axis.chart;
  29059. var renderer = chart.renderer;
  29060. var stacks = stacking.stacks;
  29061. var stackLabelsAnim = axis.options.stackLabels.animation;
  29062. var animationConfig = getDeferredAnimation(chart,
  29063. stackLabelsAnim);
  29064. var stackTotalGroup = stacking.stackTotalGroup = (stacking.stackTotalGroup ||
  29065. renderer
  29066. .g('stack-labels')
  29067. .attr({
  29068. visibility: 'visible',
  29069. zIndex: 6,
  29070. opacity: 0
  29071. })
  29072. .add());
  29073. // plotLeft/Top will change when y axis gets wider so we need to
  29074. // translate the stackTotalGroup at every render call. See bug #506
  29075. // and #516
  29076. stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
  29077. // Render each stack total
  29078. objectEach(stacks, function (type) {
  29079. objectEach(type, function (stack) {
  29080. stack.render(stackTotalGroup);
  29081. });
  29082. });
  29083. stackTotalGroup.animate({
  29084. opacity: 1
  29085. }, animationConfig);
  29086. };
  29087. return StackingAxisAdditions;
  29088. }());
  29089. /**
  29090. * Axis with stacking support.
  29091. * @private
  29092. * @class
  29093. */
  29094. var StackingAxis = /** @class */ (function () {
  29095. function StackingAxis() {
  29096. }
  29097. /* *
  29098. *
  29099. * Static Functions
  29100. *
  29101. * */
  29102. /**
  29103. * Extends axis with stacking support.
  29104. * @private
  29105. */
  29106. StackingAxis.compose = function (AxisClass) {
  29107. var axisProto = AxisClass.prototype;
  29108. addEvent(AxisClass, 'init', StackingAxis.onInit);
  29109. addEvent(AxisClass, 'destroy', StackingAxis.onDestroy);
  29110. };
  29111. /**
  29112. * @private
  29113. */
  29114. StackingAxis.onDestroy = function () {
  29115. var stacking = this.stacking;
  29116. if (!stacking) {
  29117. return;
  29118. }
  29119. var stacks = stacking.stacks;
  29120. // Destroy each stack total
  29121. objectEach(stacks, function (stack, stackKey) {
  29122. destroyObjectProperties(stack);
  29123. stacks[stackKey] = null;
  29124. });
  29125. if (stacking &&
  29126. stacking.stackTotalGroup) {
  29127. stacking.stackTotalGroup.destroy();
  29128. }
  29129. };
  29130. /**
  29131. * @private
  29132. */
  29133. StackingAxis.onInit = function () {
  29134. var axis = this;
  29135. if (!axis.stacking) {
  29136. axis.stacking = new StackingAxisAdditions(axis);
  29137. }
  29138. };
  29139. return StackingAxis;
  29140. }());
  29141. return StackingAxis;
  29142. });
  29143. _registerModule(_modules, 'Mixins/LegendSymbol.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  29144. /* *
  29145. *
  29146. * (c) 2010-2020 Torstein Honsi
  29147. *
  29148. * License: www.highcharts.com/license
  29149. *
  29150. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  29151. *
  29152. * */
  29153. var merge = U.merge,
  29154. pick = U.pick;
  29155. /* eslint-disable valid-jsdoc */
  29156. /**
  29157. * Legend symbol mixin.
  29158. *
  29159. * @private
  29160. * @mixin Highcharts.LegendSymbolMixin
  29161. */
  29162. var LegendSymbolMixin = H.LegendSymbolMixin = {
  29163. /**
  29164. * Get the series' symbol in the legend
  29165. *
  29166. * @private
  29167. * @function Highcharts.LegendSymbolMixin.drawRectangle
  29168. *
  29169. * @param {Highcharts.Legend} legend
  29170. * The legend object
  29171. *
  29172. * @param {Highcharts.Point|Highcharts.Series} item
  29173. * The series (this) or point
  29174. */
  29175. drawRectangle: function (legend,
  29176. item) {
  29177. var options = legend.options,
  29178. symbolHeight = legend.symbolHeight,
  29179. square = options.squareSymbol,
  29180. symbolWidth = square ? symbolHeight : legend.symbolWidth;
  29181. item.legendSymbol = this.chart.renderer.rect(square ? (legend.symbolWidth - symbolHeight) / 2 : 0, legend.baseline - symbolHeight + 1, // #3988
  29182. symbolWidth, symbolHeight, pick(legend.options.symbolRadius, symbolHeight / 2))
  29183. .addClass('highcharts-point')
  29184. .attr({
  29185. zIndex: 3
  29186. }).add(item.legendGroup);
  29187. },
  29188. /**
  29189. * Get the series' symbol in the legend. This method should be overridable
  29190. * to create custom symbols through
  29191. * Highcharts.seriesTypes[type].prototype.drawLegendSymbols.
  29192. *
  29193. * @private
  29194. * @function Highcharts.LegendSymbolMixin.drawLineMarker
  29195. *
  29196. * @param {Highcharts.Legend} legend
  29197. * The legend object.
  29198. */
  29199. drawLineMarker: function (legend) {
  29200. var options = this.options,
  29201. markerOptions = options.marker,
  29202. radius,
  29203. legendSymbol,
  29204. symbolWidth = legend.symbolWidth,
  29205. symbolHeight = legend.symbolHeight,
  29206. generalRadius = symbolHeight / 2,
  29207. renderer = this.chart.renderer,
  29208. legendItemGroup = this.legendGroup,
  29209. verticalCenter = legend.baseline -
  29210. Math.round(legend.fontMetrics.b * 0.3),
  29211. attr = {};
  29212. // Draw the line
  29213. if (!this.chart.styledMode) {
  29214. attr = {
  29215. 'stroke-width': options.lineWidth || 0
  29216. };
  29217. if (options.dashStyle) {
  29218. attr.dashstyle = options.dashStyle;
  29219. }
  29220. }
  29221. this.legendLine = renderer
  29222. .path([
  29223. ['M', 0, verticalCenter],
  29224. ['L', symbolWidth, verticalCenter]
  29225. ])
  29226. .addClass('highcharts-graph')
  29227. .attr(attr)
  29228. .add(legendItemGroup);
  29229. // Draw the marker
  29230. if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
  29231. // Do not allow the marker to be larger than the symbolHeight
  29232. radius = Math.min(pick(markerOptions.radius, generalRadius), generalRadius);
  29233. // Restrict symbol markers size
  29234. if (this.symbol.indexOf('url') === 0) {
  29235. markerOptions = merge(markerOptions, {
  29236. width: symbolHeight,
  29237. height: symbolHeight
  29238. });
  29239. radius = 0;
  29240. }
  29241. this.legendSymbol = legendSymbol = renderer.symbol(this.symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, 2 * radius, markerOptions)
  29242. .addClass('highcharts-point')
  29243. .add(legendItemGroup);
  29244. legendSymbol.isMarker = true;
  29245. }
  29246. }
  29247. };
  29248. return LegendSymbolMixin;
  29249. });
  29250. _registerModule(_modules, 'Core/Series/Point.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  29251. /* *
  29252. *
  29253. * (c) 2010-2020 Torstein Honsi
  29254. *
  29255. * License: www.highcharts.com/license
  29256. *
  29257. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  29258. *
  29259. * */
  29260. var animObject = U.animObject,
  29261. defined = U.defined,
  29262. erase = U.erase,
  29263. extend = U.extend,
  29264. fireEvent = U.fireEvent,
  29265. format = U.format,
  29266. getNestedProperty = U.getNestedProperty,
  29267. isArray = U.isArray,
  29268. isNumber = U.isNumber,
  29269. isObject = U.isObject,
  29270. syncTimeout = U.syncTimeout,
  29271. pick = U.pick,
  29272. removeEvent = U.removeEvent,
  29273. uniqueKey = U.uniqueKey;
  29274. /**
  29275. * Function callback when a series point is clicked. Return false to cancel the
  29276. * action.
  29277. *
  29278. * @callback Highcharts.PointClickCallbackFunction
  29279. *
  29280. * @param {Highcharts.Point} this
  29281. * The point where the event occured.
  29282. *
  29283. * @param {Highcharts.PointClickEventObject} event
  29284. * Event arguments.
  29285. */
  29286. /**
  29287. * Common information for a click event on a series point.
  29288. *
  29289. * @interface Highcharts.PointClickEventObject
  29290. * @extends Highcharts.PointerEventObject
  29291. */ /**
  29292. * Clicked point.
  29293. * @name Highcharts.PointClickEventObject#point
  29294. * @type {Highcharts.Point}
  29295. */
  29296. /**
  29297. * Configuration hash for the data label and tooltip formatters.
  29298. *
  29299. * @interface Highcharts.PointLabelObject
  29300. */ /**
  29301. * The point's current color.
  29302. * @name Highcharts.PointLabelObject#color
  29303. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  29304. */ /**
  29305. * The point's current color index, used in styled mode instead of `color`. The
  29306. * color index is inserted in class names used for styling.
  29307. * @name Highcharts.PointLabelObject#colorIndex
  29308. * @type {number}
  29309. */ /**
  29310. * The name of the related point.
  29311. * @name Highcharts.PointLabelObject#key
  29312. * @type {string|undefined}
  29313. */ /**
  29314. * The percentage for related points in a stacked series or pies.
  29315. * @name Highcharts.PointLabelObject#percentage
  29316. * @type {number}
  29317. */ /**
  29318. * The related point. The point name, if defined, is available through
  29319. * `this.point.name`.
  29320. * @name Highcharts.PointLabelObject#point
  29321. * @type {Highcharts.Point}
  29322. */ /**
  29323. * The related series. The series name is available through `this.series.name`.
  29324. * @name Highcharts.PointLabelObject#series
  29325. * @type {Highcharts.Series}
  29326. */ /**
  29327. * The total of values in either a stack for stacked series, or a pie in a pie
  29328. * series.
  29329. * @name Highcharts.PointLabelObject#total
  29330. * @type {number|undefined}
  29331. */ /**
  29332. * For categorized axes this property holds the category name for the point. For
  29333. * other axes it holds the X value.
  29334. * @name Highcharts.PointLabelObject#x
  29335. * @type {number|string|undefined}
  29336. */ /**
  29337. * The y value of the point.
  29338. * @name Highcharts.PointLabelObject#y
  29339. * @type {number|undefined}
  29340. */
  29341. /**
  29342. * Gets fired when the mouse leaves the area close to the point.
  29343. *
  29344. * @callback Highcharts.PointMouseOutCallbackFunction
  29345. *
  29346. * @param {Highcharts.Point} this
  29347. * Point where the event occured.
  29348. *
  29349. * @param {global.PointerEvent} event
  29350. * Event that occured.
  29351. */
  29352. /**
  29353. * Gets fired when the mouse enters the area close to the point.
  29354. *
  29355. * @callback Highcharts.PointMouseOverCallbackFunction
  29356. *
  29357. * @param {Highcharts.Point} this
  29358. * Point where the event occured.
  29359. *
  29360. * @param {global.Event} event
  29361. * Event that occured.
  29362. */
  29363. /**
  29364. * The generic point options for all series.
  29365. *
  29366. * In TypeScript you have to extend `PointOptionsObject` with an additional
  29367. * declaration to allow custom data options:
  29368. *
  29369. * ```
  29370. * declare interface PointOptionsObject {
  29371. * customProperty: string;
  29372. * }
  29373. * ```
  29374. *
  29375. * @interface Highcharts.PointOptionsObject
  29376. */
  29377. /**
  29378. * Possible option types for a data point.
  29379. *
  29380. * @typedef {number|string|Array<(number|string)>|Highcharts.PointOptionsObject|null} Highcharts.PointOptionsType
  29381. */
  29382. /**
  29383. * Gets fired when the point is removed using the `.remove()` method.
  29384. *
  29385. * @callback Highcharts.PointRemoveCallbackFunction
  29386. *
  29387. * @param {Highcharts.Point} this
  29388. * Point where the event occured.
  29389. *
  29390. * @param {global.Event} event
  29391. * Event that occured.
  29392. */
  29393. /**
  29394. * Possible key values for the point state options.
  29395. *
  29396. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.PointStateValue
  29397. */
  29398. /**
  29399. * Gets fired when the point is updated programmatically through the `.update()`
  29400. * method.
  29401. *
  29402. * @callback Highcharts.PointUpdateCallbackFunction
  29403. *
  29404. * @param {Highcharts.Point} this
  29405. * Point where the event occured.
  29406. *
  29407. * @param {Highcharts.PointUpdateEventObject} event
  29408. * Event that occured.
  29409. */
  29410. /**
  29411. * Information about the update event.
  29412. *
  29413. * @interface Highcharts.PointUpdateEventObject
  29414. * @extends global.Event
  29415. */ /**
  29416. * Options data of the update event.
  29417. * @name Highcharts.PointUpdateEventObject#options
  29418. * @type {Highcharts.PointOptionsType}
  29419. */
  29420. ''; // detach doclet above
  29421. /* eslint-disable no-invalid-this, valid-jsdoc */
  29422. /**
  29423. * The Point object. The point objects are generated from the `series.data`
  29424. * configuration objects or raw numbers. They can be accessed from the
  29425. * `Series.points` array. Other ways to instantiate points are through {@link
  29426. * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
  29427. *
  29428. * @class
  29429. * @name Highcharts.Point
  29430. */
  29431. var Point = /** @class */ (function () {
  29432. function Point() {
  29433. /* *
  29434. *
  29435. * Properties
  29436. *
  29437. * */
  29438. /**
  29439. * For categorized axes this property holds the category name for the
  29440. * point. For other axes it holds the X value.
  29441. *
  29442. * @name Highcharts.Point#category
  29443. * @type {string}
  29444. */
  29445. this.category = void 0;
  29446. /**
  29447. * The point's current color index, used in styled mode instead of
  29448. * `color`. The color index is inserted in class names used for styling.
  29449. *
  29450. * @name Highcharts.Point#colorIndex
  29451. * @type {number}
  29452. */
  29453. this.colorIndex = void 0;
  29454. this.formatPrefix = 'point';
  29455. this.id = void 0;
  29456. this.isNull = false;
  29457. /**
  29458. * The name of the point. The name can be given as the first position of the
  29459. * point configuration array, or as a `name` property in the configuration:
  29460. *
  29461. * @example
  29462. * // Array config
  29463. * data: [
  29464. * ['John', 1],
  29465. * ['Jane', 2]
  29466. * ]
  29467. *
  29468. * // Object config
  29469. * data: [{
  29470. * name: 'John',
  29471. * y: 1
  29472. * }, {
  29473. * name: 'Jane',
  29474. * y: 2
  29475. * }]
  29476. *
  29477. * @name Highcharts.Point#name
  29478. * @type {string}
  29479. */
  29480. this.name = void 0;
  29481. /**
  29482. * The point's options as applied in the initial configuration, or
  29483. * extended through `Point.update`.
  29484. *
  29485. * In TypeScript you have to extend `PointOptionsObject` via an
  29486. * additional interface to allow custom data options:
  29487. *
  29488. * ```
  29489. * declare interface PointOptionsObject {
  29490. * customProperty: string;
  29491. * }
  29492. * ```
  29493. *
  29494. * @name Highcharts.Point#options
  29495. * @type {Highcharts.PointOptionsObject}
  29496. */
  29497. this.options = void 0;
  29498. /**
  29499. * The percentage for points in a stacked series or pies.
  29500. *
  29501. * @name Highcharts.Point#percentage
  29502. * @type {number|undefined}
  29503. */
  29504. this.percentage = void 0;
  29505. this.selected = false;
  29506. /**
  29507. * The series object associated with the point.
  29508. *
  29509. * @name Highcharts.Point#series
  29510. * @type {Highcharts.Series}
  29511. */
  29512. this.series = void 0;
  29513. /**
  29514. * The total of values in either a stack for stacked series, or a pie in a
  29515. * pie series.
  29516. *
  29517. * @name Highcharts.Point#total
  29518. * @type {number|undefined}
  29519. */
  29520. this.total = void 0;
  29521. /**
  29522. * For certain series types, like pie charts, where individual points can
  29523. * be shown or hidden.
  29524. *
  29525. * @name Highcharts.Point#visible
  29526. * @type {boolean}
  29527. * @default true
  29528. */
  29529. this.visible = true;
  29530. this.x = void 0;
  29531. }
  29532. /* *
  29533. *
  29534. * Functions
  29535. *
  29536. * */
  29537. /**
  29538. * Animate SVG elements associated with the point.
  29539. *
  29540. * @private
  29541. * @function Highcharts.Point#animateBeforeDestroy
  29542. */
  29543. Point.prototype.animateBeforeDestroy = function () {
  29544. var point = this,
  29545. animateParams = { x: point.startXPos,
  29546. opacity: 0 },
  29547. isDataLabel,
  29548. graphicalProps = point.getGraphicalProps();
  29549. graphicalProps.singular.forEach(function (prop) {
  29550. isDataLabel = prop === 'dataLabel';
  29551. point[prop] = point[prop].animate(isDataLabel ? {
  29552. x: point[prop].startXPos,
  29553. y: point[prop].startYPos,
  29554. opacity: 0
  29555. } : animateParams);
  29556. });
  29557. graphicalProps.plural.forEach(function (plural) {
  29558. point[plural].forEach(function (item) {
  29559. if (item.element) {
  29560. item.animate(extend({ x: point.startXPos }, (item.startYPos ? {
  29561. x: item.startXPos,
  29562. y: item.startYPos
  29563. } : {})));
  29564. }
  29565. });
  29566. });
  29567. };
  29568. /**
  29569. * Apply the options containing the x and y data and possible some extra
  29570. * properties. Called on point init or from point.update.
  29571. *
  29572. * @private
  29573. * @function Highcharts.Point#applyOptions
  29574. *
  29575. * @param {Highcharts.PointOptionsType} options
  29576. * The point options as defined in series.data.
  29577. *
  29578. * @param {number} [x]
  29579. * Optionally, the x value.
  29580. *
  29581. * @return {Highcharts.Point}
  29582. * The Point instance.
  29583. */
  29584. Point.prototype.applyOptions = function (options, x) {
  29585. var point = this,
  29586. series = point.series,
  29587. pointValKey = series.options.pointValKey || series.pointValKey;
  29588. options = Point.prototype.optionsToObject.call(this, options);
  29589. // copy options directly to point
  29590. extend(point, options);
  29591. point.options = point.options ? extend(point.options, options) : options;
  29592. // Since options are copied into the Point instance, some accidental
  29593. // options must be shielded (#5681)
  29594. if (options.group) {
  29595. delete point.group;
  29596. }
  29597. if (options.dataLabels) {
  29598. delete point.dataLabels;
  29599. }
  29600. /**
  29601. * The y value of the point.
  29602. * @name Highcharts.Point#y
  29603. * @type {number|undefined}
  29604. */
  29605. // For higher dimension series types. For instance, for ranges, point.y
  29606. // is mapped to point.low.
  29607. if (pointValKey) {
  29608. point.y = Point.prototype.getNestedProperty.call(point, pointValKey);
  29609. }
  29610. point.isNull = pick(point.isValid && !point.isValid(), point.x === null || !isNumber(point.y)); // #3571, check for NaN
  29611. point.formatPrefix = point.isNull ? 'null' : 'point'; // #9233, #10874
  29612. // The point is initially selected by options (#5777)
  29613. if (point.selected) {
  29614. point.state = 'select';
  29615. }
  29616. /**
  29617. * The x value of the point.
  29618. * @name Highcharts.Point#x
  29619. * @type {number}
  29620. */
  29621. // If no x is set by now, get auto incremented value. All points must
  29622. // have an x value, however the y value can be null to create a gap in
  29623. // the series
  29624. if ('name' in point &&
  29625. typeof x === 'undefined' &&
  29626. series.xAxis &&
  29627. series.xAxis.hasNames) {
  29628. point.x = series.xAxis.nameToX(point);
  29629. }
  29630. if (typeof point.x === 'undefined' && series) {
  29631. if (typeof x === 'undefined') {
  29632. point.x = series.autoIncrement(point);
  29633. }
  29634. else {
  29635. point.x = x;
  29636. }
  29637. }
  29638. return point;
  29639. };
  29640. /**
  29641. * Destroy a point to clear memory. Its reference still stays in
  29642. * `series.data`.
  29643. *
  29644. * @private
  29645. * @function Highcharts.Point#destroy
  29646. */
  29647. Point.prototype.destroy = function () {
  29648. var point = this,
  29649. series = point.series,
  29650. chart = series.chart,
  29651. dataSorting = series.options.dataSorting,
  29652. hoverPoints = chart.hoverPoints,
  29653. globalAnimation = point.series.chart.renderer.globalAnimation,
  29654. animation = animObject(globalAnimation),
  29655. prop;
  29656. /**
  29657. * Allow to call after animation.
  29658. * @private
  29659. */
  29660. function destroyPoint() {
  29661. // Remove all events and elements
  29662. if (point.graphic || point.dataLabel || point.dataLabels) {
  29663. removeEvent(point);
  29664. point.destroyElements();
  29665. }
  29666. for (prop in point) { // eslint-disable-line guard-for-in
  29667. point[prop] = null;
  29668. }
  29669. }
  29670. if (point.legendItem) { // pies have legend items
  29671. chart.legend.destroyItem(point);
  29672. }
  29673. if (hoverPoints) {
  29674. point.setState();
  29675. erase(hoverPoints, point);
  29676. if (!hoverPoints.length) {
  29677. chart.hoverPoints = null;
  29678. }
  29679. }
  29680. if (point === chart.hoverPoint) {
  29681. point.onMouseOut();
  29682. }
  29683. // Remove properties after animation
  29684. if (!dataSorting || !dataSorting.enabled) {
  29685. destroyPoint();
  29686. }
  29687. else {
  29688. this.animateBeforeDestroy();
  29689. syncTimeout(destroyPoint, animation.duration);
  29690. }
  29691. chart.pointCount--;
  29692. };
  29693. /**
  29694. * Destroy SVG elements associated with the point.
  29695. *
  29696. * @private
  29697. * @function Highcharts.Point#destroyElements
  29698. * @param {Highcharts.Dictionary<number>} [kinds]
  29699. */
  29700. Point.prototype.destroyElements = function (kinds) {
  29701. var point = this,
  29702. props = point.getGraphicalProps(kinds);
  29703. props.singular.forEach(function (prop) {
  29704. point[prop] = point[prop].destroy();
  29705. });
  29706. props.plural.forEach(function (plural) {
  29707. point[plural].forEach(function (item) {
  29708. if (item.element) {
  29709. item.destroy();
  29710. }
  29711. });
  29712. delete point[plural];
  29713. });
  29714. };
  29715. /**
  29716. * Fire an event on the Point object.
  29717. *
  29718. * @private
  29719. * @function Highcharts.Point#firePointEvent
  29720. *
  29721. * @param {string} eventType
  29722. * Type of the event.
  29723. *
  29724. * @param {Highcharts.Dictionary<any>|Event} [eventArgs]
  29725. * Additional event arguments.
  29726. *
  29727. * @param {Highcharts.EventCallbackFunction<Highcharts.Point>|Function} [defaultFunction]
  29728. * Default event handler.
  29729. *
  29730. * @fires Highcharts.Point#event:*
  29731. */
  29732. Point.prototype.firePointEvent = function (eventType, eventArgs, defaultFunction) {
  29733. var point = this,
  29734. series = this.series,
  29735. seriesOptions = series.options;
  29736. // load event handlers on demand to save time on mouseover/out
  29737. if (seriesOptions.point.events[eventType] ||
  29738. (point.options &&
  29739. point.options.events &&
  29740. point.options.events[eventType])) {
  29741. point.importEvents();
  29742. }
  29743. // add default handler if in selection mode
  29744. if (eventType === 'click' && seriesOptions.allowPointSelect) {
  29745. defaultFunction = function (event) {
  29746. // Control key is for Windows, meta (= Cmd key) for Mac, Shift
  29747. // for Opera.
  29748. if (point.select) { // #2911
  29749. point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
  29750. }
  29751. };
  29752. }
  29753. fireEvent(point, eventType, eventArgs, defaultFunction);
  29754. };
  29755. /**
  29756. * Get the CSS class names for individual points. Used internally where the
  29757. * returned value is set on every point.
  29758. *
  29759. * @function Highcharts.Point#getClassName
  29760. *
  29761. * @return {string}
  29762. * The class names.
  29763. */
  29764. Point.prototype.getClassName = function () {
  29765. var point = this;
  29766. return 'highcharts-point' +
  29767. (point.selected ? ' highcharts-point-select' : '') +
  29768. (point.negative ? ' highcharts-negative' : '') +
  29769. (point.isNull ? ' highcharts-null-point' : '') +
  29770. (typeof point.colorIndex !== 'undefined' ?
  29771. ' highcharts-color-' + point.colorIndex : '') +
  29772. (point.options.className ? ' ' + point.options.className : '') +
  29773. (point.zone && point.zone.className ? ' ' +
  29774. point.zone.className.replace('highcharts-negative', '') : '');
  29775. };
  29776. /**
  29777. * Get props of all existing graphical point elements.
  29778. *
  29779. * @private
  29780. * @function Highcharts.Point#getGraphicalProps
  29781. * @param {Highcharts.Dictionary<number>} [kinds]
  29782. * @return {Highcharts.PointGraphicalProps}
  29783. */
  29784. Point.prototype.getGraphicalProps = function (kinds) {
  29785. var point = this,
  29786. props = [],
  29787. prop,
  29788. i,
  29789. graphicalProps = { singular: [],
  29790. plural: [] };
  29791. kinds = kinds || { graphic: 1, dataLabel: 1 };
  29792. if (kinds.graphic) {
  29793. props.push('graphic', 'shadowGroup');
  29794. }
  29795. if (kinds.dataLabel) {
  29796. props.push('dataLabel', 'dataLabelUpper', 'connector');
  29797. }
  29798. i = props.length;
  29799. while (i--) {
  29800. prop = props[i];
  29801. if (point[prop]) {
  29802. graphicalProps.singular.push(prop);
  29803. }
  29804. }
  29805. ['dataLabel', 'connector'].forEach(function (prop) {
  29806. var plural = prop + 's';
  29807. if (kinds[prop] && point[plural]) {
  29808. graphicalProps.plural.push(plural);
  29809. }
  29810. });
  29811. return graphicalProps;
  29812. };
  29813. /**
  29814. * Return the configuration hash needed for the data label and tooltip
  29815. * formatters.
  29816. *
  29817. * @function Highcharts.Point#getLabelConfig
  29818. *
  29819. * @return {Highcharts.PointLabelObject}
  29820. * Abstract object used in formatters and formats.
  29821. */
  29822. Point.prototype.getLabelConfig = function () {
  29823. return {
  29824. x: this.category,
  29825. y: this.y,
  29826. color: this.color,
  29827. colorIndex: this.colorIndex,
  29828. key: this.name || this.category,
  29829. series: this.series,
  29830. point: this,
  29831. percentage: this.percentage,
  29832. total: this.total || this.stackTotal
  29833. };
  29834. };
  29835. /**
  29836. * Returns the value of the point property for a given value.
  29837. * @private
  29838. */
  29839. Point.prototype.getNestedProperty = function (key) {
  29840. if (!key) {
  29841. return;
  29842. }
  29843. if (key.indexOf('custom.') === 0) {
  29844. return getNestedProperty(key, this.options);
  29845. }
  29846. return this[key];
  29847. };
  29848. /**
  29849. * In a series with `zones`, return the zone that the point belongs to.
  29850. *
  29851. * @function Highcharts.Point#getZone
  29852. *
  29853. * @return {Highcharts.SeriesZonesOptionsObject}
  29854. * The zone item.
  29855. */
  29856. Point.prototype.getZone = function () {
  29857. var series = this.series,
  29858. zones = series.zones,
  29859. zoneAxis = series.zoneAxis || 'y',
  29860. i = 0,
  29861. zone;
  29862. zone = zones[i];
  29863. while (this[zoneAxis] >= zone.value) {
  29864. zone = zones[++i];
  29865. }
  29866. // For resetting or reusing the point (#8100)
  29867. if (!this.nonZonedColor) {
  29868. this.nonZonedColor = this.color;
  29869. }
  29870. if (zone && zone.color && !this.options.color) {
  29871. this.color = zone.color;
  29872. }
  29873. else {
  29874. this.color = this.nonZonedColor;
  29875. }
  29876. return zone;
  29877. };
  29878. /**
  29879. * Utility to check if point has new shape type. Used in column series and
  29880. * all others that are based on column series.
  29881. *
  29882. * @return boolean|undefined
  29883. */
  29884. Point.prototype.hasNewShapeType = function () {
  29885. var point = this;
  29886. var oldShapeType = point.graphic &&
  29887. (point.graphic.symbolName || point.graphic.element.nodeName);
  29888. return oldShapeType !== this.shapeType;
  29889. };
  29890. /**
  29891. * Initialize the point. Called internally based on the `series.data`
  29892. * option.
  29893. *
  29894. * @function Highcharts.Point#init
  29895. *
  29896. * @param {Highcharts.Series} series
  29897. * The series object containing this point.
  29898. *
  29899. * @param {Highcharts.PointOptionsType} options
  29900. * The data in either number, array or object format.
  29901. *
  29902. * @param {number} [x]
  29903. * Optionally, the X value of the point.
  29904. *
  29905. * @return {Highcharts.Point}
  29906. * The Point instance.
  29907. *
  29908. * @fires Highcharts.Point#event:afterInit
  29909. */
  29910. Point.prototype.init = function (series, options, x) {
  29911. this.series = series;
  29912. this.applyOptions(options, x);
  29913. // Add a unique ID to the point if none is assigned
  29914. this.id = defined(this.id) ? this.id : uniqueKey();
  29915. this.resolveColor();
  29916. series.chart.pointCount++;
  29917. fireEvent(this, 'afterInit');
  29918. return this;
  29919. };
  29920. /**
  29921. * Transform number or array configs into objects. Also called for object
  29922. * configs. Used internally to unify the different configuration formats for
  29923. * points. For example, a simple number `10` in a line series will be
  29924. * transformed to `{ y: 10 }`, and an array config like `[1, 10]` in a
  29925. * scatter series will be transformed to `{ x: 1, y: 10 }`.
  29926. *
  29927. * @function Highcharts.Point#optionsToObject
  29928. *
  29929. * @param {Highcharts.PointOptionsType} options
  29930. * The input option.
  29931. *
  29932. * @return {Highcharts.Dictionary<*>}
  29933. * Transformed options.
  29934. */
  29935. Point.prototype.optionsToObject = function (options) {
  29936. var ret = {},
  29937. series = this.series,
  29938. keys = series.options.keys,
  29939. pointArrayMap = keys || series.pointArrayMap || ['y'],
  29940. valueCount = pointArrayMap.length,
  29941. firstItemType,
  29942. i = 0,
  29943. j = 0;
  29944. if (isNumber(options) || options === null) {
  29945. ret[pointArrayMap[0]] = options;
  29946. }
  29947. else if (isArray(options)) {
  29948. // with leading x value
  29949. if (!keys && options.length > valueCount) {
  29950. firstItemType = typeof options[0];
  29951. if (firstItemType === 'string') {
  29952. ret.name = options[0];
  29953. }
  29954. else if (firstItemType === 'number') {
  29955. ret.x = options[0];
  29956. }
  29957. i++;
  29958. }
  29959. while (j < valueCount) {
  29960. // Skip undefined positions for keys
  29961. if (!keys || typeof options[i] !== 'undefined') {
  29962. if (pointArrayMap[j].indexOf('.') > 0) {
  29963. // Handle nested keys, e.g. ['color.pattern.image']
  29964. // Avoid function call unless necessary.
  29965. Point.prototype.setNestedProperty(ret, options[i], pointArrayMap[j]);
  29966. }
  29967. else {
  29968. ret[pointArrayMap[j]] = options[i];
  29969. }
  29970. }
  29971. i++;
  29972. j++;
  29973. }
  29974. }
  29975. else if (typeof options === 'object') {
  29976. ret = options;
  29977. // This is the fastest way to detect if there are individual point
  29978. // dataLabels that need to be considered in drawDataLabels. These
  29979. // can only occur in object configs.
  29980. if (options.dataLabels) {
  29981. series._hasPointLabels = true;
  29982. }
  29983. // Same approach as above for markers
  29984. if (options.marker) {
  29985. series._hasPointMarkers = true;
  29986. }
  29987. }
  29988. return ret;
  29989. };
  29990. /**
  29991. * @private
  29992. * @function Highcharts.Point#resolveColor
  29993. * @return {void}
  29994. */
  29995. Point.prototype.resolveColor = function () {
  29996. var series = this.series,
  29997. colors,
  29998. optionsChart = series.chart.options.chart,
  29999. colorCount = optionsChart.colorCount,
  30000. styledMode = series.chart.styledMode,
  30001. colorIndex;
  30002. // remove points nonZonedColor for later recalculation
  30003. delete this.nonZonedColor;
  30004. /**
  30005. * The point's current color.
  30006. *
  30007. * @name Highcharts.Point#color
  30008. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  30009. */
  30010. if (!styledMode && !this.options.color) {
  30011. this.color = series.color; // #3445
  30012. }
  30013. if (series.options.colorByPoint) {
  30014. if (!styledMode) {
  30015. colors = series.options.colors || series.chart.options.colors;
  30016. this.color = this.color || colors[series.colorCounter];
  30017. colorCount = colors.length;
  30018. }
  30019. colorIndex = series.colorCounter;
  30020. series.colorCounter++;
  30021. // loop back to zero
  30022. if (series.colorCounter === colorCount) {
  30023. series.colorCounter = 0;
  30024. }
  30025. }
  30026. else {
  30027. colorIndex = series.colorIndex;
  30028. }
  30029. this.colorIndex = pick(this.colorIndex, colorIndex);
  30030. };
  30031. /**
  30032. * Set a value in an object, on the property defined by key. The key
  30033. * supports nested properties using dot notation. The function modifies the
  30034. * input object and does not make a copy.
  30035. *
  30036. * @function Highcharts.Point#setNestedProperty<T>
  30037. *
  30038. * @param {T} object
  30039. * The object to set the value on.
  30040. *
  30041. * @param {*} value
  30042. * The value to set.
  30043. *
  30044. * @param {string} key
  30045. * Key to the property to set.
  30046. *
  30047. * @return {T}
  30048. * The modified object.
  30049. */
  30050. Point.prototype.setNestedProperty = function (object, value, key) {
  30051. var nestedKeys = key.split('.');
  30052. nestedKeys.reduce(function (result, key, i, arr) {
  30053. var isLastKey = arr.length - 1 === i;
  30054. result[key] = (isLastKey ?
  30055. value :
  30056. isObject(result[key], true) ?
  30057. result[key] :
  30058. {});
  30059. return result[key];
  30060. }, object);
  30061. return object;
  30062. };
  30063. /**
  30064. * Extendable method for formatting each point's tooltip line.
  30065. *
  30066. * @function Highcharts.Point#tooltipFormatter
  30067. *
  30068. * @param {string} pointFormat
  30069. * The point format.
  30070. *
  30071. * @return {string}
  30072. * A string to be concatenated in to the common tooltip text.
  30073. */
  30074. Point.prototype.tooltipFormatter = function (pointFormat) {
  30075. // Insert options for valueDecimals, valuePrefix, and valueSuffix
  30076. var series = this.series, seriesTooltipOptions = series.tooltipOptions, valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), valuePrefix = seriesTooltipOptions.valuePrefix || '', valueSuffix = seriesTooltipOptions.valueSuffix || '';
  30077. // Replace default point style with class name
  30078. if (series.chart.styledMode) {
  30079. pointFormat =
  30080. series.chart.tooltip.styledModeFormat(pointFormat);
  30081. }
  30082. // Loop over the point array map and replace unformatted values with
  30083. // sprintf formatting markup
  30084. (series.pointArrayMap || ['y']).forEach(function (key) {
  30085. key = '{point.' + key; // without the closing bracket
  30086. if (valuePrefix || valueSuffix) {
  30087. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), valuePrefix + key + '}' + valueSuffix);
  30088. }
  30089. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), key + ':,.' + valueDecimals + 'f}');
  30090. });
  30091. return format(pointFormat, {
  30092. point: this,
  30093. series: this.series
  30094. }, series.chart);
  30095. };
  30096. return Point;
  30097. }());
  30098. H.Point = Point;
  30099. return Point;
  30100. });
  30101. _registerModule(_modules, 'Core/Series/Series.js', [_modules['Core/Globals.js'], _modules['Mixins/LegendSymbol.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (H, LegendSymbolMixin, O, Point, SVGElement, U) {
  30102. /* *
  30103. *
  30104. * (c) 2010-2020 Torstein Honsi
  30105. *
  30106. * License: www.highcharts.com/license
  30107. *
  30108. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  30109. *
  30110. * */
  30111. var defaultOptions = O.defaultOptions;
  30112. var addEvent = U.addEvent,
  30113. animObject = U.animObject,
  30114. arrayMax = U.arrayMax,
  30115. arrayMin = U.arrayMin,
  30116. clamp = U.clamp,
  30117. correctFloat = U.correctFloat,
  30118. defined = U.defined,
  30119. erase = U.erase,
  30120. error = U.error,
  30121. extend = U.extend,
  30122. find = U.find,
  30123. fireEvent = U.fireEvent,
  30124. getNestedProperty = U.getNestedProperty,
  30125. isArray = U.isArray,
  30126. isFunction = U.isFunction,
  30127. isNumber = U.isNumber,
  30128. isString = U.isString,
  30129. merge = U.merge,
  30130. objectEach = U.objectEach,
  30131. pick = U.pick,
  30132. removeEvent = U.removeEvent,
  30133. seriesType = U.seriesType,
  30134. splat = U.splat,
  30135. syncTimeout = U.syncTimeout;
  30136. /**
  30137. * This is a placeholder type of the possible series options for
  30138. * [Highcharts](../highcharts/series), [Highstock](../highstock/series),
  30139. * [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
  30140. *
  30141. * In TypeScript is this dynamically generated to reference all possible types
  30142. * of series options.
  30143. *
  30144. * @ignore-declaration
  30145. * @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
  30146. */
  30147. /**
  30148. * Options for `dataSorting`.
  30149. *
  30150. * @interface Highcharts.DataSortingOptionsObject
  30151. * @since 8.0.0
  30152. */ /**
  30153. * Enable or disable data sorting for the series.
  30154. * @name Highcharts.DataSortingOptionsObject#enabled
  30155. * @type {boolean|undefined}
  30156. */ /**
  30157. * Whether to allow matching points by name in an update.
  30158. * @name Highcharts.DataSortingOptionsObject#matchByName
  30159. * @type {boolean|undefined}
  30160. */ /**
  30161. * Determines what data value should be used to sort by.
  30162. * @name Highcharts.DataSortingOptionsObject#sortKey
  30163. * @type {string|undefined}
  30164. */
  30165. /**
  30166. * Function callback when a series has been animated.
  30167. *
  30168. * @callback Highcharts.SeriesAfterAnimateCallbackFunction
  30169. *
  30170. * @param {Highcharts.Series} this
  30171. * The series where the event occured.
  30172. *
  30173. * @param {Highcharts.SeriesAfterAnimateEventObject} event
  30174. * Event arguments.
  30175. */
  30176. /**
  30177. * Event information regarding completed animation of a series.
  30178. *
  30179. * @interface Highcharts.SeriesAfterAnimateEventObject
  30180. */ /**
  30181. * Animated series.
  30182. * @name Highcharts.SeriesAfterAnimateEventObject#target
  30183. * @type {Highcharts.Series}
  30184. */ /**
  30185. * Event type.
  30186. * @name Highcharts.SeriesAfterAnimateEventObject#type
  30187. * @type {"afterAnimate"}
  30188. */
  30189. /**
  30190. * Function callback when the checkbox next to the series' name in the legend is
  30191. * clicked.
  30192. *
  30193. * @callback Highcharts.SeriesCheckboxClickCallbackFunction
  30194. *
  30195. * @param {Highcharts.Series} this
  30196. * The series where the event occured.
  30197. *
  30198. * @param {Highcharts.SeriesCheckboxClickEventObject} event
  30199. * Event arguments.
  30200. */
  30201. /**
  30202. * Event information regarding check of a series box.
  30203. *
  30204. * @interface Highcharts.SeriesCheckboxClickEventObject
  30205. */ /**
  30206. * Whether the box has been checked.
  30207. * @name Highcharts.SeriesCheckboxClickEventObject#checked
  30208. * @type {boolean}
  30209. */ /**
  30210. * Related series.
  30211. * @name Highcharts.SeriesCheckboxClickEventObject#item
  30212. * @type {Highcharts.Series}
  30213. */ /**
  30214. * Related series.
  30215. * @name Highcharts.SeriesCheckboxClickEventObject#target
  30216. * @type {Highcharts.Series}
  30217. */ /**
  30218. * Event type.
  30219. * @name Highcharts.SeriesCheckboxClickEventObject#type
  30220. * @type {"checkboxClick"}
  30221. */
  30222. /**
  30223. * Function callback when a series is clicked. Return false to cancel toogle
  30224. * actions.
  30225. *
  30226. * @callback Highcharts.SeriesClickCallbackFunction
  30227. *
  30228. * @param {Highcharts.Series} this
  30229. * The series where the event occured.
  30230. *
  30231. * @param {Highcharts.SeriesClickEventObject} event
  30232. * Event arguments.
  30233. */
  30234. /**
  30235. * Common information for a click event on a series.
  30236. *
  30237. * @interface Highcharts.SeriesClickEventObject
  30238. * @extends global.Event
  30239. */ /**
  30240. * Nearest point on the graph.
  30241. * @name Highcharts.SeriesClickEventObject#point
  30242. * @type {Highcharts.Point}
  30243. */
  30244. /**
  30245. * Gets fired when the series is hidden after chart generation time, either by
  30246. * clicking the legend item or by calling `.hide()`.
  30247. *
  30248. * @callback Highcharts.SeriesHideCallbackFunction
  30249. *
  30250. * @param {Highcharts.Series} this
  30251. * The series where the event occured.
  30252. *
  30253. * @param {global.Event} event
  30254. * The event that occured.
  30255. */
  30256. /**
  30257. * The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
  30258. * graph.
  30259. *
  30260. * @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
  30261. */
  30262. /**
  30263. * Gets fired when the legend item belonging to the series is clicked. The
  30264. * default action is to toggle the visibility of the series. This can be
  30265. * prevented by returning `false` or calling `event.preventDefault()`.
  30266. *
  30267. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  30268. *
  30269. * @param {Highcharts.Series} this
  30270. * The series where the event occured.
  30271. *
  30272. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  30273. * The event that occured.
  30274. */
  30275. /**
  30276. * Information about the event.
  30277. *
  30278. * @interface Highcharts.SeriesLegendItemClickEventObject
  30279. */ /**
  30280. * Related browser event.
  30281. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  30282. * @type {global.PointerEvent}
  30283. */ /**
  30284. * Prevent the default action of toggle the visibility of the series.
  30285. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  30286. * @type {Function}
  30287. */ /**
  30288. * Related series.
  30289. * @name Highcharts.SeriesCheckboxClickEventObject#target
  30290. * @type {Highcharts.Series}
  30291. */ /**
  30292. * Event type.
  30293. * @name Highcharts.SeriesCheckboxClickEventObject#type
  30294. * @type {"checkboxClick"}
  30295. */
  30296. /**
  30297. * Gets fired when the mouse leaves the graph.
  30298. *
  30299. * @callback Highcharts.SeriesMouseOutCallbackFunction
  30300. *
  30301. * @param {Highcharts.Series} this
  30302. * Series where the event occured.
  30303. *
  30304. * @param {global.PointerEvent} event
  30305. * Event that occured.
  30306. */
  30307. /**
  30308. * Gets fired when the mouse enters the graph.
  30309. *
  30310. * @callback Highcharts.SeriesMouseOverCallbackFunction
  30311. *
  30312. * @param {Highcharts.Series} this
  30313. * Series where the event occured.
  30314. *
  30315. * @param {global.PointerEvent} event
  30316. * Event that occured.
  30317. */
  30318. /**
  30319. * Translation and scale for the plot area of a series.
  30320. *
  30321. * @interface Highcharts.SeriesPlotBoxObject
  30322. */ /**
  30323. * @name Highcharts.SeriesPlotBoxObject#scaleX
  30324. * @type {number}
  30325. */ /**
  30326. * @name Highcharts.SeriesPlotBoxObject#scaleY
  30327. * @type {number}
  30328. */ /**
  30329. * @name Highcharts.SeriesPlotBoxObject#translateX
  30330. * @type {number}
  30331. */ /**
  30332. * @name Highcharts.SeriesPlotBoxObject#translateY
  30333. * @type {number}
  30334. */
  30335. /**
  30336. * Gets fired when the series is shown after chart generation time, either by
  30337. * clicking the legend item or by calling `.show()`.
  30338. *
  30339. * @callback Highcharts.SeriesShowCallbackFunction
  30340. *
  30341. * @param {Highcharts.Series} this
  30342. * Series where the event occured.
  30343. *
  30344. * @param {global.Event} event
  30345. * Event that occured.
  30346. */
  30347. /**
  30348. * Possible key values for the series state options.
  30349. *
  30350. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
  30351. */
  30352. ''; // detach doclets above
  30353. var seriesTypes = H.seriesTypes,
  30354. win = H.win;
  30355. /**
  30356. * This is the base series prototype that all other series types inherit from.
  30357. * A new series is initialized either through the
  30358. * [series](https://api.highcharts.com/highcharts/series)
  30359. * option structure, or after the chart is initialized, through
  30360. * {@link Highcharts.Chart#addSeries}.
  30361. *
  30362. * The object can be accessed in a number of ways. All series and point event
  30363. * handlers give a reference to the `series` object. The chart object has a
  30364. * {@link Highcharts.Chart#series|series} property that is a collection of all
  30365. * the chart's series. The point objects and axis objects also have the same
  30366. * reference.
  30367. *
  30368. * Another way to reference the series programmatically is by `id`. Add an id
  30369. * in the series configuration options, and get the series object by
  30370. * {@link Highcharts.Chart#get}.
  30371. *
  30372. * Configuration options for the series are given in three levels. Options for
  30373. * all series in a chart are given in the
  30374. * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
  30375. * object. Then options for all series of a specific type
  30376. * are given in the plotOptions of that type, for example `plotOptions.line`.
  30377. * Next, options for one single series are given in the series array, or as
  30378. * arguments to `chart.addSeries`.
  30379. *
  30380. * The data in the series is stored in various arrays.
  30381. *
  30382. * - First, `series.options.data` contains all the original config options for
  30383. * each point whether added by options or methods like `series.addPoint`.
  30384. *
  30385. * - Next, `series.data` contains those values converted to points, but in case
  30386. * the series data length exceeds the `cropThreshold`, or if the data is
  30387. * grouped, `series.data` doesn't contain all the points. It only contains the
  30388. * points that have been created on demand.
  30389. *
  30390. * - Then there's `series.points` that contains all currently visible point
  30391. * objects. In case of cropping, the cropped-away points are not part of this
  30392. * array. The `series.points` array starts at `series.cropStart` compared to
  30393. * `series.data` and `series.options.data`. If however the series data is
  30394. * grouped, these can't be correlated one to one.
  30395. *
  30396. * - `series.xData` and `series.processedXData` contain clean x values,
  30397. * equivalent to `series.data` and `series.points`.
  30398. *
  30399. * - `series.yData` and `series.processedYData` contain clean y values,
  30400. * equivalent to `series.data` and `series.points`.
  30401. *
  30402. * @class
  30403. * @name Highcharts.Series
  30404. *
  30405. * @param {Highcharts.Chart} chart
  30406. * The chart instance.
  30407. *
  30408. * @param {Highcharts.SeriesOptionsType|object} options
  30409. * The series options.
  30410. */ /**
  30411. * The line series is the base type and is therefor the series base prototype.
  30412. *
  30413. * @private
  30414. * @class
  30415. * @name Highcharts.seriesTypes.line
  30416. *
  30417. * @augments Highcharts.Series
  30418. */
  30419. H.Series = seriesType('line',
  30420. /**
  30421. * Series options for specific data and the data itself. In TypeScript you
  30422. * have to cast the series options to specific series types, to get all
  30423. * possible options for a series.
  30424. *
  30425. * @example
  30426. * // TypeScript example
  30427. * Highcharts.chart('container', {
  30428. * series: [{
  30429. * color: '#06C',
  30430. * data: [[0, 1], [2, 3]]
  30431. * } as Highcharts.SeriesLineOptions ]
  30432. * });
  30433. *
  30434. * @type {Array<*>}
  30435. * @apioption series
  30436. */
  30437. /**
  30438. * An id for the series. This can be used after render time to get a pointer
  30439. * to the series object through `chart.get()`.
  30440. *
  30441. * @sample {highcharts} highcharts/plotoptions/series-id/
  30442. * Get series by id
  30443. *
  30444. * @type {string}
  30445. * @since 1.2.0
  30446. * @apioption series.id
  30447. */
  30448. /**
  30449. * The index of the series in the chart, affecting the internal index in the
  30450. * `chart.series` array, the visible Z index as well as the order in the
  30451. * legend.
  30452. *
  30453. * @type {number}
  30454. * @since 2.3.0
  30455. * @apioption series.index
  30456. */
  30457. /**
  30458. * The sequential index of the series in the legend.
  30459. *
  30460. * @see [legend.reversed](#legend.reversed),
  30461. * [yAxis.reversedStacks](#yAxis.reversedStacks)
  30462. *
  30463. * @sample {highcharts|highstock} highcharts/series/legendindex/
  30464. * Legend in opposite order
  30465. *
  30466. * @type {number}
  30467. * @apioption series.legendIndex
  30468. */
  30469. /**
  30470. * The name of the series as shown in the legend, tooltip etc.
  30471. *
  30472. * @sample {highcharts} highcharts/series/name/
  30473. * Series name
  30474. * @sample {highmaps} maps/demo/category-map/
  30475. * Series name
  30476. *
  30477. * @type {string}
  30478. * @apioption series.name
  30479. */
  30480. /**
  30481. * This option allows grouping series in a stacked chart. The stack option
  30482. * can be a string or anything else, as long as the grouped series' stack
  30483. * options match each other after conversion into a string.
  30484. *
  30485. * @sample {highcharts} highcharts/series/stack/
  30486. * Stacked and grouped columns
  30487. *
  30488. * @type {number|string}
  30489. * @since 2.1
  30490. * @product highcharts highstock
  30491. * @apioption series.stack
  30492. */
  30493. /**
  30494. * The type of series, for example `line` or `column`. By default, the
  30495. * series type is inherited from [chart.type](#chart.type), so unless the
  30496. * chart is a combination of series types, there is no need to set it on the
  30497. * series level.
  30498. *
  30499. * @sample {highcharts} highcharts/series/type/
  30500. * Line and column in the same chart
  30501. * @sample highcharts/series/type-dynamic/
  30502. * Dynamic types with button selector
  30503. * @sample {highmaps} maps/demo/mapline-mappoint/
  30504. * Multiple types in the same map
  30505. *
  30506. * @type {string}
  30507. * @apioption series.type
  30508. */
  30509. /**
  30510. * When using dual or multiple x axes, this number defines which xAxis the
  30511. * particular series is connected to. It refers to either the
  30512. * {@link #xAxis.id|axis id}
  30513. * or the index of the axis in the xAxis array, with 0 being the first.
  30514. *
  30515. * @type {number|string}
  30516. * @default 0
  30517. * @product highcharts highstock
  30518. * @apioption series.xAxis
  30519. */
  30520. /**
  30521. * When using dual or multiple y axes, this number defines which yAxis the
  30522. * particular series is connected to. It refers to either the
  30523. * {@link #yAxis.id|axis id}
  30524. * or the index of the axis in the yAxis array, with 0 being the first.
  30525. *
  30526. * @sample {highcharts} highcharts/series/yaxis/
  30527. * Apply the column series to the secondary Y axis
  30528. *
  30529. * @type {number|string}
  30530. * @default 0
  30531. * @product highcharts highstock
  30532. * @apioption series.yAxis
  30533. */
  30534. /**
  30535. * Define the visual z index of the series.
  30536. *
  30537. * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
  30538. * With no z index, the series defined last are on top
  30539. * @sample {highcharts} highcharts/plotoptions/series-zindex/
  30540. * With a z index, the series with the highest z index is on top
  30541. * @sample {highstock} highcharts/plotoptions/series-zindex-default/
  30542. * With no z index, the series defined last are on top
  30543. * @sample {highstock} highcharts/plotoptions/series-zindex/
  30544. * With a z index, the series with the highest z index is on top
  30545. *
  30546. * @type {number}
  30547. * @product highcharts highstock
  30548. * @apioption series.zIndex
  30549. */
  30550. null,
  30551. /**
  30552. * General options for all series types.
  30553. *
  30554. * @optionparent plotOptions.series
  30555. */
  30556. {
  30557. /**
  30558. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  30559. * of a line graph. Round means that lines are rounded in the ends and
  30560. * bends.
  30561. *
  30562. * @type {Highcharts.SeriesLinecapValue}
  30563. * @default round
  30564. * @since 3.0.7
  30565. * @apioption plotOptions.line.linecap
  30566. */
  30567. /**
  30568. * Pixel width of the graph line.
  30569. *
  30570. * @see In styled mode, the line stroke-width can be set with the
  30571. * `.highcharts-graph` class name.
  30572. *
  30573. * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
  30574. * On all series
  30575. * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
  30576. * On one single series
  30577. *
  30578. * @product highcharts highstock
  30579. *
  30580. * @private
  30581. */
  30582. lineWidth: 2,
  30583. /**
  30584. * For some series, there is a limit that shuts down initial animation
  30585. * by default when the total number of points in the chart is too high.
  30586. * For example, for a column chart and its derivatives, animation does
  30587. * not run if there is more than 250 points totally. To disable this
  30588. * cap, set `animationLimit` to `Infinity`.
  30589. *
  30590. * @type {number}
  30591. * @apioption plotOptions.series.animationLimit
  30592. */
  30593. /**
  30594. * Allow this series' points to be selected by clicking on the graphic
  30595. * (columns, point markers, pie slices, map areas etc).
  30596. *
  30597. * The selected points can be handled by point select and unselect
  30598. * events, or collectively by the [getSelectedPoints
  30599. * ](/class-reference/Highcharts.Chart#getSelectedPoints) function.
  30600. *
  30601. * And alternative way of selecting points is through dragging.
  30602. *
  30603. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
  30604. * Line
  30605. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
  30606. * Column
  30607. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
  30608. * Pie
  30609. * @sample {highcharts} highcharts/chart/events-selection-points/
  30610. * Select a range of points through a drag selection
  30611. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30612. * Map area
  30613. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  30614. * Map bubble
  30615. *
  30616. * @since 1.2.0
  30617. *
  30618. * @private
  30619. */
  30620. allowPointSelect: false,
  30621. /**
  30622. * When true, each point or column edge is rounded to its nearest pixel
  30623. * in order to render sharp on screen. In some cases, when there are a
  30624. * lot of densely packed columns, this leads to visible difference
  30625. * in column widths or distance between columns. In these cases,
  30626. * setting `crisp` to `false` may look better, even though each column
  30627. * is rendered blurry.
  30628. *
  30629. * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
  30630. * Crisp is false
  30631. *
  30632. * @since 5.0.10
  30633. * @product highcharts highstock gantt
  30634. *
  30635. * @private
  30636. */
  30637. crisp: true,
  30638. /**
  30639. * If true, a checkbox is displayed next to the legend item to allow
  30640. * selecting the series. The state of the checkbox is determined by
  30641. * the `selected` option.
  30642. *
  30643. * @productdesc {highmaps}
  30644. * Note that if a `colorAxis` is defined, the color axis is represented
  30645. * in the legend, not the series.
  30646. *
  30647. * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
  30648. * Show select box
  30649. *
  30650. * @since 1.2.0
  30651. *
  30652. * @private
  30653. */
  30654. showCheckbox: false,
  30655. /**
  30656. * Enable or disable the initial animation when a series is displayed.
  30657. * The animation can also be set as a configuration object. Please
  30658. * note that this option only applies to the initial animation of the
  30659. * series itself. For other animations, see [chart.animation](
  30660. * #chart.animation) and the animation parameter under the API methods.
  30661. * The following properties are supported:
  30662. *
  30663. * - `defer`: The animation delay time in milliseconds.
  30664. *
  30665. * - `duration`: The duration of the animation in milliseconds.
  30666. *
  30667. * - `easing`: Can be a string reference to an easing function set on
  30668. * the `Math` object or a function. See the _Custom easing function_
  30669. * demo below.
  30670. *
  30671. * Due to poor performance, animation is disabled in old IE browsers
  30672. * for several chart types.
  30673. *
  30674. * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
  30675. * Animation disabled
  30676. * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
  30677. * Slower animation
  30678. * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
  30679. * Custom easing function
  30680. * @sample {highstock} stock/plotoptions/animation-slower/
  30681. * Slower animation
  30682. * @sample {highstock} stock/plotoptions/animation-easing/
  30683. * Custom easing function
  30684. * @sample {highmaps} maps/plotoptions/series-animation-true/
  30685. * Animation enabled on map series
  30686. * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
  30687. * Disabled on mapbubble series
  30688. *
  30689. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30690. * @default {highcharts} true
  30691. * @default {highstock} true
  30692. * @default {highmaps} false
  30693. *
  30694. * @private
  30695. */
  30696. animation: {
  30697. /** @internal */
  30698. duration: 1000
  30699. },
  30700. /**
  30701. * @default 0
  30702. * @type {number}
  30703. * @since 8.2.0
  30704. * @apioption plotOptions.series.animation.defer
  30705. */
  30706. /**
  30707. * An additional class name to apply to the series' graphical elements.
  30708. * This option does not replace default class names of the graphical
  30709. * element.
  30710. *
  30711. * @type {string}
  30712. * @since 5.0.0
  30713. * @apioption plotOptions.series.className
  30714. */
  30715. /**
  30716. * Disable this option to allow series rendering in the whole plotting
  30717. * area.
  30718. *
  30719. * **Note:** Clipping should be always enabled when
  30720. * [chart.zoomType](#chart.zoomType) is set
  30721. *
  30722. * @sample {highcharts} highcharts/plotoptions/series-clip/
  30723. * Disabled clipping
  30724. *
  30725. * @default true
  30726. * @type {boolean}
  30727. * @since 3.0.0
  30728. * @apioption plotOptions.series.clip
  30729. */
  30730. /**
  30731. * The main color of the series. In line type series it applies to the
  30732. * line and the point markers unless otherwise specified. In bar type
  30733. * series it applies to the bars unless a color is specified per point.
  30734. * The default value is pulled from the `options.colors` array.
  30735. *
  30736. * In styled mode, the color can be defined by the
  30737. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  30738. * color can be set with the `.highcharts-series`,
  30739. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  30740. * `.highcharts-series-{n}` class, or individual classes given by the
  30741. * `className` option.
  30742. *
  30743. * @productdesc {highmaps}
  30744. * In maps, the series color is rarely used, as most choropleth maps use
  30745. * the color to denote the value of each point. The series color can
  30746. * however be used in a map with multiple series holding categorized
  30747. * data.
  30748. *
  30749. * @sample {highcharts} highcharts/plotoptions/series-color-general/
  30750. * General plot option
  30751. * @sample {highcharts} highcharts/plotoptions/series-color-specific/
  30752. * One specific series
  30753. * @sample {highcharts} highcharts/plotoptions/series-color-area/
  30754. * Area color
  30755. * @sample {highcharts} highcharts/series/infographic/
  30756. * Pattern fill
  30757. * @sample {highmaps} maps/demo/category-map/
  30758. * Category map by multiple series
  30759. *
  30760. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30761. * @apioption plotOptions.series.color
  30762. */
  30763. /**
  30764. * Styled mode only. A specific color index to use for the series, so
  30765. * its graphic representations are given the class name
  30766. * `highcharts-color-{n}`.
  30767. *
  30768. * @type {number}
  30769. * @since 5.0.0
  30770. * @apioption plotOptions.series.colorIndex
  30771. */
  30772. /**
  30773. * Whether to connect a graph line across null points, or render a gap
  30774. * between the two points on either side of the null.
  30775. *
  30776. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
  30777. * False by default
  30778. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
  30779. * True
  30780. *
  30781. * @type {boolean}
  30782. * @default false
  30783. * @product highcharts highstock
  30784. * @apioption plotOptions.series.connectNulls
  30785. */
  30786. /**
  30787. * You can set the cursor to "pointer" if you have click events attached
  30788. * to the series, to signal to the user that the points and lines can
  30789. * be clicked.
  30790. *
  30791. * In styled mode, the series cursor can be set with the same classes
  30792. * as listed under [series.color](#plotOptions.series.color).
  30793. *
  30794. * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
  30795. * On line graph
  30796. * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
  30797. * On columns
  30798. * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
  30799. * On scatter markers
  30800. * @sample {highstock} stock/plotoptions/cursor/
  30801. * Pointer on a line graph
  30802. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30803. * Map area
  30804. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  30805. * Map bubble
  30806. *
  30807. * @type {string|Highcharts.CursorValue}
  30808. * @apioption plotOptions.series.cursor
  30809. */
  30810. /**
  30811. * A reserved subspace to store options and values for customized
  30812. * functionality. Here you can add additional data for your own event
  30813. * callbacks and formatter callbacks.
  30814. *
  30815. * @sample {highcharts} highcharts/point/custom/
  30816. * Point and series with custom data
  30817. *
  30818. * @type {Highcharts.Dictionary<*>}
  30819. * @apioption plotOptions.series.custom
  30820. */
  30821. /**
  30822. * Name of the dash style to use for the graph, or for some series types
  30823. * the outline of each shape.
  30824. *
  30825. * In styled mode, the
  30826. * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
  30827. * can be set with the same classes as listed under
  30828. * [series.color](#plotOptions.series.color).
  30829. *
  30830. * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
  30831. * Possible values demonstrated
  30832. * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
  30833. * Chart suitable for printing in black and white
  30834. * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
  30835. * Possible values demonstrated
  30836. * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
  30837. * Possible values demonstrated
  30838. * @sample {highmaps} maps/plotoptions/series-dashstyle/
  30839. * Dotted borders on a map
  30840. *
  30841. * @type {Highcharts.DashStyleValue}
  30842. * @default Solid
  30843. * @since 2.1
  30844. * @apioption plotOptions.series.dashStyle
  30845. */
  30846. /**
  30847. * A description of the series to add to the screen reader information
  30848. * about the series.
  30849. *
  30850. * @type {string}
  30851. * @since 5.0.0
  30852. * @requires modules/accessibility
  30853. * @apioption plotOptions.series.description
  30854. */
  30855. /**
  30856. * Options for the series data sorting.
  30857. *
  30858. * @type {Highcharts.DataSortingOptionsObject}
  30859. * @since 8.0.0
  30860. * @product highcharts highstock
  30861. * @apioption plotOptions.series.dataSorting
  30862. */
  30863. /**
  30864. * Enable or disable data sorting for the series. Use [xAxis.reversed](
  30865. * #xAxis.reversed) to change the sorting order.
  30866. *
  30867. * @sample {highcharts} highcharts/datasorting/animation/
  30868. * Data sorting in scatter-3d
  30869. * @sample {highcharts} highcharts/datasorting/labels-animation/
  30870. * Axis labels animation
  30871. * @sample {highcharts} highcharts/datasorting/dependent-sorting/
  30872. * Dependent series sorting
  30873. * @sample {highcharts} highcharts/datasorting/independent-sorting/
  30874. * Independent series sorting
  30875. *
  30876. * @type {boolean}
  30877. * @since 8.0.0
  30878. * @apioption plotOptions.series.dataSorting.enabled
  30879. */
  30880. /**
  30881. * Whether to allow matching points by name in an update. If this option
  30882. * is disabled, points will be matched by order.
  30883. *
  30884. * @sample {highcharts} highcharts/datasorting/match-by-name/
  30885. * Enabled match by name
  30886. *
  30887. * @type {boolean}
  30888. * @since 8.0.0
  30889. * @apioption plotOptions.series.dataSorting.matchByName
  30890. */
  30891. /**
  30892. * Determines what data value should be used to sort by.
  30893. *
  30894. * @sample {highcharts} highcharts/datasorting/sort-key/
  30895. * Sort key as `z` value
  30896. *
  30897. * @type {string}
  30898. * @since 8.0.0
  30899. * @default y
  30900. * @apioption plotOptions.series.dataSorting.sortKey
  30901. */
  30902. /**
  30903. * Enable or disable the mouse tracking for a specific series. This
  30904. * includes point tooltips and click events on graphs and points. For
  30905. * large datasets it improves performance.
  30906. *
  30907. * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
  30908. * No mouse tracking
  30909. * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
  30910. * No mouse tracking
  30911. *
  30912. * @type {boolean}
  30913. * @default true
  30914. * @apioption plotOptions.series.enableMouseTracking
  30915. */
  30916. /**
  30917. * Whether to use the Y extremes of the total chart width or only the
  30918. * zoomed area when zooming in on parts of the X axis. By default, the
  30919. * Y axis adjusts to the min and max of the visible data. Cartesian
  30920. * series only.
  30921. *
  30922. * @type {boolean}
  30923. * @default false
  30924. * @since 4.1.6
  30925. * @product highcharts highstock gantt
  30926. * @apioption plotOptions.series.getExtremesFromAll
  30927. */
  30928. /**
  30929. * An array specifying which option maps to which key in the data point
  30930. * array. This makes it convenient to work with unstructured data arrays
  30931. * from different sources.
  30932. *
  30933. * @see [series.data](#series.line.data)
  30934. *
  30935. * @sample {highcharts|highstock} highcharts/series/data-keys/
  30936. * An extended data array with keys
  30937. * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
  30938. * Nested keys used to access object properties
  30939. *
  30940. * @type {Array<string>}
  30941. * @since 4.1.6
  30942. * @apioption plotOptions.series.keys
  30943. */
  30944. /**
  30945. * The line cap used for line ends and line joins on the graph.
  30946. *
  30947. * @type {Highcharts.SeriesLinecapValue}
  30948. * @default round
  30949. * @product highcharts highstock
  30950. * @apioption plotOptions.series.linecap
  30951. */
  30952. /**
  30953. * The [id](#series.id) of another series to link to. Additionally,
  30954. * the value can be ":previous" to link to the previous series. When
  30955. * two series are linked, only the first one appears in the legend.
  30956. * Toggling the visibility of this also toggles the linked series.
  30957. *
  30958. * If master series uses data sorting and linked series does not have
  30959. * its own sorting definition, the linked series will be sorted in the
  30960. * same order as the master one.
  30961. *
  30962. * @sample {highcharts|highstock} highcharts/demo/arearange-line/
  30963. * Linked series
  30964. *
  30965. * @type {string}
  30966. * @since 3.0
  30967. * @product highcharts highstock gantt
  30968. * @apioption plotOptions.series.linkedTo
  30969. */
  30970. /**
  30971. * Options for the corresponding navigator series if `showInNavigator`
  30972. * is `true` for this series. Available options are the same as any
  30973. * series, documented at [plotOptions](#plotOptions.series) and
  30974. * [series](#series).
  30975. *
  30976. * These options are merged with options in [navigator.series](
  30977. * #navigator.series), and will take precedence if the same option is
  30978. * defined both places.
  30979. *
  30980. * @see [navigator.series](#navigator.series)
  30981. *
  30982. * @type {Highcharts.PlotSeriesOptions}
  30983. * @since 5.0.0
  30984. * @product highstock
  30985. * @apioption plotOptions.series.navigatorOptions
  30986. */
  30987. /**
  30988. * The color for the parts of the graph or points that are below the
  30989. * [threshold](#plotOptions.series.threshold). Note that `zones` takes
  30990. * precedence over the negative color. Using `negativeColor` is
  30991. * equivalent to applying a zone with value of 0.
  30992. *
  30993. * @see In styled mode, a negative color is applied by setting this option
  30994. * to `true` combined with the `.highcharts-negative` class name.
  30995. *
  30996. * @sample {highcharts} highcharts/plotoptions/series-negative-color/
  30997. * Spline, area and column
  30998. * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
  30999. * Arearange
  31000. * @sample {highcharts} highcharts/css/series-negative-color/
  31001. * Styled mode
  31002. * @sample {highstock} highcharts/plotoptions/series-negative-color/
  31003. * Spline, area and column
  31004. * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
  31005. * Arearange
  31006. * @sample {highmaps} highcharts/plotoptions/series-negative-color/
  31007. * Spline, area and column
  31008. * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
  31009. * Arearange
  31010. *
  31011. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31012. * @since 3.0
  31013. * @apioption plotOptions.series.negativeColor
  31014. */
  31015. /**
  31016. * Same as
  31017. * [accessibility.pointDescriptionFormatter](#accessibility.pointDescriptionFormatter),
  31018. * but for an individual series. Overrides the chart wide configuration.
  31019. *
  31020. * @type {Function}
  31021. * @since 5.0.12
  31022. * @apioption plotOptions.series.pointDescriptionFormatter
  31023. */
  31024. /**
  31025. * If no x values are given for the points in a series, `pointInterval`
  31026. * defines the interval of the x values. For example, if a series
  31027. * contains one value every decade starting from year 0, set
  31028. * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
  31029. * is set in milliseconds.
  31030. *
  31031. * It can be also be combined with `pointIntervalUnit` to draw irregular
  31032. * time intervals.
  31033. *
  31034. * Please note that this options applies to the _series data_, not the
  31035. * interval of the axis ticks, which is independent.
  31036. *
  31037. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  31038. * Datetime X axis
  31039. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  31040. * Using pointStart and pointInterval
  31041. *
  31042. * @type {number}
  31043. * @default 1
  31044. * @product highcharts highstock gantt
  31045. * @apioption plotOptions.series.pointInterval
  31046. */
  31047. /**
  31048. * On datetime series, this allows for setting the
  31049. * [pointInterval](#plotOptions.series.pointInterval) to irregular time
  31050. * units, `day`, `month` and `year`. A day is usually the same as 24
  31051. * hours, but `pointIntervalUnit` also takes the DST crossover into
  31052. * consideration when dealing with local time. Combine this option with
  31053. * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
  31054. *
  31055. * Please note that this options applies to the _series data_, not the
  31056. * interval of the axis ticks, which is independent.
  31057. *
  31058. * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
  31059. * One point a month
  31060. * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
  31061. * One point a month
  31062. *
  31063. * @type {string}
  31064. * @since 4.1.0
  31065. * @product highcharts highstock gantt
  31066. * @validvalue ["day", "month", "year"]
  31067. * @apioption plotOptions.series.pointIntervalUnit
  31068. */
  31069. /**
  31070. * Possible values: `"on"`, `"between"`, `number`.
  31071. *
  31072. * In a column chart, when pointPlacement is `"on"`, the point will not
  31073. * create any padding of the X axis. In a polar column chart this means
  31074. * that the first column points directly north. If the pointPlacement is
  31075. * `"between"`, the columns will be laid out between ticks. This is
  31076. * useful for example for visualising an amount between two points in
  31077. * time or in a certain sector of a polar chart.
  31078. *
  31079. * Since Highcharts 3.0.2, the point placement can also be numeric,
  31080. * where 0 is on the axis value, -0.5 is between this value and the
  31081. * previous, and 0.5 is between this value and the next. Unlike the
  31082. * textual options, numeric point placement options won't affect axis
  31083. * padding.
  31084. *
  31085. * Note that pointPlacement needs a [pointRange](
  31086. * #plotOptions.series.pointRange) to work. For column series this is
  31087. * computed, but for line-type series it needs to be set.
  31088. *
  31089. * For the `xrange` series type and gantt charts, if the Y axis is a
  31090. * category axis, the `pointPlacement` applies to the Y axis rather than
  31091. * the (typically datetime) X axis.
  31092. *
  31093. * Defaults to `undefined` in cartesian charts, `"between"` in polar
  31094. * charts.
  31095. *
  31096. * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
  31097. *
  31098. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
  31099. * Between in a column chart
  31100. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
  31101. * Numeric placement for custom layout
  31102. * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
  31103. * Placement in heatmap
  31104. *
  31105. * @type {string|number}
  31106. * @since 2.3.0
  31107. * @product highcharts highstock gantt
  31108. * @apioption plotOptions.series.pointPlacement
  31109. */
  31110. /**
  31111. * If no x values are given for the points in a series, pointStart
  31112. * defines on what value to start. For example, if a series contains one
  31113. * yearly value starting from 1945, set pointStart to 1945.
  31114. *
  31115. * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
  31116. * Linear
  31117. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  31118. * Datetime
  31119. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  31120. * Using pointStart and pointInterval
  31121. *
  31122. * @type {number}
  31123. * @default 0
  31124. * @product highcharts highstock gantt
  31125. * @apioption plotOptions.series.pointStart
  31126. */
  31127. /**
  31128. * Whether to select the series initially. If `showCheckbox` is true,
  31129. * the checkbox next to the series name in the legend will be checked
  31130. * for a selected series.
  31131. *
  31132. * @sample {highcharts} highcharts/plotoptions/series-selected/
  31133. * One out of two series selected
  31134. *
  31135. * @type {boolean}
  31136. * @default false
  31137. * @since 1.2.0
  31138. * @apioption plotOptions.series.selected
  31139. */
  31140. /**
  31141. * Whether to apply a drop shadow to the graph line. Since 2.3 the
  31142. * shadow can be an object configuration containing `color`, `offsetX`,
  31143. * `offsetY`, `opacity` and `width`.
  31144. *
  31145. * @sample {highcharts} highcharts/plotoptions/series-shadow/
  31146. * Shadow enabled
  31147. *
  31148. * @type {boolean|Highcharts.ShadowOptionsObject}
  31149. * @default false
  31150. * @apioption plotOptions.series.shadow
  31151. */
  31152. /**
  31153. * Whether to display this particular series or series type in the
  31154. * legend. Standalone series are shown in legend by default, and linked
  31155. * series are not. Since v7.2.0 it is possible to show series that use
  31156. * colorAxis by setting this option to `true`.
  31157. *
  31158. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  31159. * One series in the legend, one hidden
  31160. *
  31161. * @type {boolean}
  31162. * @apioption plotOptions.series.showInLegend
  31163. */
  31164. /**
  31165. * Whether or not to show the series in the navigator. Takes precedence
  31166. * over [navigator.baseSeries](#navigator.baseSeries) if defined.
  31167. *
  31168. * @type {boolean}
  31169. * @since 5.0.0
  31170. * @product highstock
  31171. * @apioption plotOptions.series.showInNavigator
  31172. */
  31173. /**
  31174. * If set to `true`, the accessibility module will skip past the points
  31175. * in this series for keyboard navigation.
  31176. *
  31177. * @type {boolean}
  31178. * @since 5.0.12
  31179. * @apioption plotOptions.series.skipKeyboardNavigation
  31180. */
  31181. /**
  31182. * Whether to stack the values of each series on top of each other.
  31183. * Possible values are `undefined` to disable, `"normal"` to stack by
  31184. * value or `"percent"`.
  31185. *
  31186. * When stacking is enabled, data must be sorted
  31187. * in ascending X order.
  31188. *
  31189. * Some stacking options are related to specific series types. In the
  31190. * streamgraph series type, the stacking option is set to `"stream"`.
  31191. * The second one is `"overlap"`, which only applies to waterfall
  31192. * series.
  31193. *
  31194. * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
  31195. *
  31196. * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
  31197. * Line
  31198. * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
  31199. * Column
  31200. * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
  31201. * Bar
  31202. * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
  31203. * Area
  31204. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
  31205. * Line
  31206. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
  31207. * Column
  31208. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
  31209. * Bar
  31210. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
  31211. * Area
  31212. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
  31213. * Waterfall with normal stacking
  31214. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
  31215. * Waterfall with overlap stacking
  31216. * @sample {highstock} stock/plotoptions/stacking/
  31217. * Area
  31218. *
  31219. * @type {string}
  31220. * @product highcharts highstock
  31221. * @validvalue ["normal", "overlap", "percent", "stream"]
  31222. * @apioption plotOptions.series.stacking
  31223. */
  31224. /**
  31225. * Whether to apply steps to the line. Possible values are `left`,
  31226. * `center` and `right`.
  31227. *
  31228. * @sample {highcharts} highcharts/plotoptions/line-step/
  31229. * Different step line options
  31230. * @sample {highcharts} highcharts/plotoptions/area-step/
  31231. * Stepped, stacked area
  31232. * @sample {highstock} stock/plotoptions/line-step/
  31233. * Step line
  31234. *
  31235. * @type {string}
  31236. * @since 1.2.5
  31237. * @product highcharts highstock
  31238. * @validvalue ["left", "center", "right"]
  31239. * @apioption plotOptions.series.step
  31240. */
  31241. /**
  31242. * The threshold, also called zero level or base level. For line type
  31243. * series this is only used in conjunction with
  31244. * [negativeColor](#plotOptions.series.negativeColor).
  31245. *
  31246. * @see [softThreshold](#plotOptions.series.softThreshold).
  31247. *
  31248. * @type {number}
  31249. * @default 0
  31250. * @since 3.0
  31251. * @product highcharts highstock
  31252. * @apioption plotOptions.series.threshold
  31253. */
  31254. /**
  31255. * Set the initial visibility of the series.
  31256. *
  31257. * @sample {highcharts} highcharts/plotoptions/series-visible/
  31258. * Two series, one hidden and one visible
  31259. * @sample {highstock} stock/plotoptions/series-visibility/
  31260. * Hidden series
  31261. *
  31262. * @type {boolean}
  31263. * @default true
  31264. * @apioption plotOptions.series.visible
  31265. */
  31266. /**
  31267. * Defines the Axis on which the zones are applied.
  31268. *
  31269. * @see [zones](#plotOptions.series.zones)
  31270. *
  31271. * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
  31272. * Zones on the X-Axis
  31273. * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
  31274. * Zones on the X-Axis
  31275. *
  31276. * @type {string}
  31277. * @default y
  31278. * @since 4.1.0
  31279. * @product highcharts highstock
  31280. * @apioption plotOptions.series.zoneAxis
  31281. */
  31282. /**
  31283. * General event handlers for the series items. These event hooks can
  31284. * also be attached to the series at run time using the
  31285. * `Highcharts.addEvent` function.
  31286. *
  31287. * @declare Highcharts.SeriesEventsOptionsObject
  31288. *
  31289. * @private
  31290. */
  31291. events: {},
  31292. /**
  31293. * Fires after the series has finished its initial animation, or in case
  31294. * animation is disabled, immediately as the series is displayed.
  31295. *
  31296. * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
  31297. * Show label after animate
  31298. * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
  31299. * Show label after animate
  31300. *
  31301. * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
  31302. * @since 4.0
  31303. * @product highcharts highstock gantt
  31304. * @context Highcharts.Series
  31305. * @apioption plotOptions.series.events.afterAnimate
  31306. */
  31307. /**
  31308. * Fires when the checkbox next to the series' name in the legend is
  31309. * clicked. One parameter, `event`, is passed to the function. The state
  31310. * of the checkbox is found by `event.checked`. The checked item is
  31311. * found by `event.item`. Return `false` to prevent the default action
  31312. * which is to toggle the select state of the series.
  31313. *
  31314. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  31315. * Alert checkbox status
  31316. *
  31317. * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
  31318. * @since 1.2.0
  31319. * @context Highcharts.Series
  31320. * @apioption plotOptions.series.events.checkboxClick
  31321. */
  31322. /**
  31323. * Fires when the series is clicked. One parameter, `event`, is passed
  31324. * to the function, containing common event information. Additionally,
  31325. * `event.point` holds a pointer to the nearest point on the graph.
  31326. *
  31327. * @sample {highcharts} highcharts/plotoptions/series-events-click/
  31328. * Alert click info
  31329. * @sample {highstock} stock/plotoptions/series-events-click/
  31330. * Alert click info
  31331. * @sample {highmaps} maps/plotoptions/series-events-click/
  31332. * Display click info in subtitle
  31333. *
  31334. * @type {Highcharts.SeriesClickCallbackFunction}
  31335. * @context Highcharts.Series
  31336. * @apioption plotOptions.series.events.click
  31337. */
  31338. /**
  31339. * Fires when the series is hidden after chart generation time, either
  31340. * by clicking the legend item or by calling `.hide()`.
  31341. *
  31342. * @sample {highcharts} highcharts/plotoptions/series-events-hide/
  31343. * Alert when the series is hidden by clicking the legend item
  31344. *
  31345. * @type {Highcharts.SeriesHideCallbackFunction}
  31346. * @since 1.2.0
  31347. * @context Highcharts.Series
  31348. * @apioption plotOptions.series.events.hide
  31349. */
  31350. /**
  31351. * Fires when the legend item belonging to the series is clicked. One
  31352. * parameter, `event`, is passed to the function. The default action
  31353. * is to toggle the visibility of the series. This can be prevented
  31354. * by returning `false` or calling `event.preventDefault()`.
  31355. *
  31356. * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
  31357. * Confirm hiding and showing
  31358. *
  31359. * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
  31360. * @context Highcharts.Series
  31361. * @apioption plotOptions.series.events.legendItemClick
  31362. */
  31363. /**
  31364. * Fires when the mouse leaves the graph. One parameter, `event`, is
  31365. * passed to the function, containing common event information. If the
  31366. * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
  31367. * doesn't happen before the mouse enters another graph or leaves the
  31368. * plot area.
  31369. *
  31370. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  31371. * With sticky tracking by default
  31372. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  31373. * Without sticky tracking
  31374. *
  31375. * @type {Highcharts.SeriesMouseOutCallbackFunction}
  31376. * @context Highcharts.Series
  31377. * @apioption plotOptions.series.events.mouseOut
  31378. */
  31379. /**
  31380. * Fires when the mouse enters the graph. One parameter, `event`, is
  31381. * passed to the function, containing common event information.
  31382. *
  31383. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  31384. * With sticky tracking by default
  31385. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  31386. * Without sticky tracking
  31387. *
  31388. * @type {Highcharts.SeriesMouseOverCallbackFunction}
  31389. * @context Highcharts.Series
  31390. * @apioption plotOptions.series.events.mouseOver
  31391. */
  31392. /**
  31393. * Fires when the series is shown after chart generation time, either
  31394. * by clicking the legend item or by calling `.show()`.
  31395. *
  31396. * @sample {highcharts} highcharts/plotoptions/series-events-show/
  31397. * Alert when the series is shown by clicking the legend item.
  31398. *
  31399. * @type {Highcharts.SeriesShowCallbackFunction}
  31400. * @since 1.2.0
  31401. * @context Highcharts.Series
  31402. * @apioption plotOptions.series.events.show
  31403. */
  31404. /**
  31405. * Options for the point markers of line-like series. Properties like
  31406. * `fillColor`, `lineColor` and `lineWidth` define the visual appearance
  31407. * of the markers. Other series types, like column series, don't have
  31408. * markers, but have visual options on the series level instead.
  31409. *
  31410. * In styled mode, the markers can be styled with the
  31411. * `.highcharts-point`, `.highcharts-point-hover` and
  31412. * `.highcharts-point-select` class names.
  31413. *
  31414. * @declare Highcharts.PointMarkerOptionsObject
  31415. *
  31416. * @private
  31417. */
  31418. marker: {
  31419. /**
  31420. * Enable or disable the point marker. If `undefined`, the markers
  31421. * are hidden when the data is dense, and shown for more widespread
  31422. * data points.
  31423. *
  31424. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
  31425. * Disabled markers
  31426. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
  31427. * Disabled in normal state but enabled on hover
  31428. * @sample {highstock} stock/plotoptions/series-marker/
  31429. * Enabled markers
  31430. *
  31431. * @type {boolean}
  31432. * @default {highcharts} undefined
  31433. * @default {highstock} false
  31434. * @apioption plotOptions.series.marker.enabled
  31435. */
  31436. /**
  31437. * The threshold for how dense the point markers should be before
  31438. * they are hidden, given that `enabled` is not defined. The number
  31439. * indicates the horizontal distance between the two closest points
  31440. * in the series, as multiples of the `marker.radius`. In other
  31441. * words, the default value of 2 means points are hidden if
  31442. * overlapping horizontally.
  31443. *
  31444. * @sample highcharts/plotoptions/series-marker-enabledthreshold
  31445. * A higher threshold
  31446. *
  31447. * @since 6.0.5
  31448. */
  31449. enabledThreshold: 2,
  31450. /**
  31451. * The fill color of the point marker. When `undefined`, the series'
  31452. * or point's color is used.
  31453. *
  31454. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  31455. * White fill
  31456. *
  31457. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31458. * @apioption plotOptions.series.marker.fillColor
  31459. */
  31460. /**
  31461. * Image markers only. Set the image width explicitly. When using
  31462. * this option, a `width` must also be set.
  31463. *
  31464. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  31465. * Fixed width and height
  31466. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  31467. * Fixed width and height
  31468. *
  31469. * @type {number}
  31470. * @since 4.0.4
  31471. * @apioption plotOptions.series.marker.height
  31472. */
  31473. /**
  31474. * The color of the point marker's outline. When `undefined`, the
  31475. * series' or point's color is used.
  31476. *
  31477. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  31478. * Inherit from series color (undefined)
  31479. *
  31480. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31481. */
  31482. lineColor: '#ffffff',
  31483. /**
  31484. * The width of the point marker's outline.
  31485. *
  31486. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  31487. * 2px blue marker
  31488. */
  31489. lineWidth: 0,
  31490. /**
  31491. * The radius of the point marker.
  31492. *
  31493. * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
  31494. * Bigger markers
  31495. *
  31496. * @default {highstock} 2
  31497. */
  31498. radius: 4,
  31499. /**
  31500. * A predefined shape or symbol for the marker. When undefined, the
  31501. * symbol is pulled from options.symbols. Other possible values are
  31502. * `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
  31503. * `'triangle-down'`.
  31504. *
  31505. * Additionally, the URL to a graphic can be given on this form:
  31506. * `'url(graphic.png)'`. Note that for the image to be applied to
  31507. * exported charts, its URL needs to be accessible by the export
  31508. * server.
  31509. *
  31510. * Custom callbacks for symbol path generation can also be added to
  31511. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  31512. * used by its method name, as shown in the demo.
  31513. *
  31514. * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
  31515. * Predefined, graphic and custom markers
  31516. * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
  31517. * Predefined, graphic and custom markers
  31518. *
  31519. * @type {string}
  31520. * @apioption plotOptions.series.marker.symbol
  31521. */
  31522. /**
  31523. * Image markers only. Set the image width explicitly. When using
  31524. * this option, a `height` must also be set.
  31525. *
  31526. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  31527. * Fixed width and height
  31528. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  31529. * Fixed width and height
  31530. *
  31531. * @type {number}
  31532. * @since 4.0.4
  31533. * @apioption plotOptions.series.marker.width
  31534. */
  31535. /**
  31536. * States for a single point marker.
  31537. *
  31538. * @declare Highcharts.PointStatesOptionsObject
  31539. */
  31540. states: {
  31541. /**
  31542. * The normal state of a single point marker. Currently only
  31543. * used for setting animation when returning to normal state
  31544. * from hover.
  31545. *
  31546. * @declare Highcharts.PointStatesNormalOptionsObject
  31547. */
  31548. normal: {
  31549. /**
  31550. * Animation when returning to normal state after hovering.
  31551. *
  31552. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  31553. */
  31554. animation: true
  31555. },
  31556. /**
  31557. * The hover state for a single point marker.
  31558. *
  31559. * @declare Highcharts.PointStatesHoverOptionsObject
  31560. */
  31561. hover: {
  31562. /**
  31563. * Animation when hovering over the marker.
  31564. *
  31565. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  31566. */
  31567. animation: {
  31568. /** @internal */
  31569. duration: 50
  31570. },
  31571. /**
  31572. * Enable or disable the point marker.
  31573. *
  31574. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
  31575. * Disabled hover state
  31576. */
  31577. enabled: true,
  31578. /**
  31579. * The fill color of the marker in hover state. When
  31580. * `undefined`, the series' or point's fillColor for normal
  31581. * state is used.
  31582. *
  31583. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31584. * @apioption plotOptions.series.marker.states.hover.fillColor
  31585. */
  31586. /**
  31587. * The color of the point marker's outline. When
  31588. * `undefined`, the series' or point's lineColor for normal
  31589. * state is used.
  31590. *
  31591. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
  31592. * White fill color, black line color
  31593. *
  31594. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31595. * @apioption plotOptions.series.marker.states.hover.lineColor
  31596. */
  31597. /**
  31598. * The width of the point marker's outline. When
  31599. * `undefined`, the series' or point's lineWidth for normal
  31600. * state is used.
  31601. *
  31602. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
  31603. * 3px line width
  31604. *
  31605. * @type {number}
  31606. * @apioption plotOptions.series.marker.states.hover.lineWidth
  31607. */
  31608. /**
  31609. * The radius of the point marker. In hover state, it
  31610. * defaults to the normal state's radius + 2 as per the
  31611. * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
  31612. * option.
  31613. *
  31614. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
  31615. * 10px radius
  31616. *
  31617. * @type {number}
  31618. * @apioption plotOptions.series.marker.states.hover.radius
  31619. */
  31620. /**
  31621. * The number of pixels to increase the radius of the
  31622. * hovered point.
  31623. *
  31624. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  31625. * 5 pixels greater radius on hover
  31626. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  31627. * 5 pixels greater radius on hover
  31628. *
  31629. * @since 4.0.3
  31630. */
  31631. radiusPlus: 2,
  31632. /**
  31633. * The additional line width for a hovered point.
  31634. *
  31635. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  31636. * 2 pixels wider on hover
  31637. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  31638. * 2 pixels wider on hover
  31639. *
  31640. * @since 4.0.3
  31641. */
  31642. lineWidthPlus: 1
  31643. },
  31644. /**
  31645. * The appearance of the point marker when selected. In order to
  31646. * allow a point to be selected, set the
  31647. * `series.allowPointSelect` option to true.
  31648. *
  31649. * @declare Highcharts.PointStatesSelectOptionsObject
  31650. */
  31651. select: {
  31652. /**
  31653. * Enable or disable visible feedback for selection.
  31654. *
  31655. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
  31656. * Disabled select state
  31657. *
  31658. * @type {boolean}
  31659. * @default true
  31660. * @apioption plotOptions.series.marker.states.select.enabled
  31661. */
  31662. /**
  31663. * The radius of the point marker. In hover state, it
  31664. * defaults to the normal state's radius + 2.
  31665. *
  31666. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
  31667. * 10px radius for selected points
  31668. *
  31669. * @type {number}
  31670. * @apioption plotOptions.series.marker.states.select.radius
  31671. */
  31672. /**
  31673. * The fill color of the point marker.
  31674. *
  31675. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
  31676. * Solid red discs for selected points
  31677. *
  31678. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31679. */
  31680. fillColor: '#cccccc',
  31681. /**
  31682. * The color of the point marker's outline. When
  31683. * `undefined`, the series' or point's color is used.
  31684. *
  31685. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
  31686. * Red line color for selected points
  31687. *
  31688. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31689. */
  31690. lineColor: '#000000',
  31691. /**
  31692. * The width of the point marker's outline.
  31693. *
  31694. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
  31695. * 3px line width for selected points
  31696. */
  31697. lineWidth: 2
  31698. }
  31699. }
  31700. },
  31701. /**
  31702. * Properties for each single point.
  31703. *
  31704. * @declare Highcharts.PlotSeriesPointOptions
  31705. *
  31706. * @private
  31707. */
  31708. point: {
  31709. /**
  31710. * Fires when a point is clicked. One parameter, `event`, is passed
  31711. * to the function, containing common event information.
  31712. *
  31713. * If the `series.allowPointSelect` option is true, the default
  31714. * action for the point's click event is to toggle the point's
  31715. * select state. Returning `false` cancels this action.
  31716. *
  31717. * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
  31718. * Click marker to alert values
  31719. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
  31720. * Click column
  31721. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
  31722. * Go to URL
  31723. * @sample {highmaps} maps/plotoptions/series-point-events-click/
  31724. * Click marker to display values
  31725. * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
  31726. * Go to URL
  31727. *
  31728. * @type {Highcharts.PointClickCallbackFunction}
  31729. * @context Highcharts.Point
  31730. * @apioption plotOptions.series.point.events.click
  31731. */
  31732. /**
  31733. * Fires when the mouse leaves the area close to the point. One
  31734. * parameter, `event`, is passed to the function, containing common
  31735. * event information.
  31736. *
  31737. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  31738. * Show values in the chart's corner on mouse over
  31739. *
  31740. * @type {Highcharts.PointMouseOutCallbackFunction}
  31741. * @context Highcharts.Point
  31742. * @apioption plotOptions.series.point.events.mouseOut
  31743. */
  31744. /**
  31745. * Fires when the mouse enters the area close to the point. One
  31746. * parameter, `event`, is passed to the function, containing common
  31747. * event information.
  31748. *
  31749. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  31750. * Show values in the chart's corner on mouse over
  31751. *
  31752. * @type {Highcharts.PointMouseOverCallbackFunction}
  31753. * @context Highcharts.Point
  31754. * @apioption plotOptions.series.point.events.mouseOver
  31755. */
  31756. /**
  31757. * Fires when the point is removed using the `.remove()` method. One
  31758. * parameter, `event`, is passed to the function. Returning `false`
  31759. * cancels the operation.
  31760. *
  31761. * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
  31762. * Remove point and confirm
  31763. *
  31764. * @type {Highcharts.PointRemoveCallbackFunction}
  31765. * @since 1.2.0
  31766. * @context Highcharts.Point
  31767. * @apioption plotOptions.series.point.events.remove
  31768. */
  31769. /**
  31770. * Fires when the point is selected either programmatically or
  31771. * following a click on the point. One parameter, `event`, is passed
  31772. * to the function. Returning `false` cancels the operation.
  31773. *
  31774. * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
  31775. * Report the last selected point
  31776. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  31777. * Report select and unselect
  31778. *
  31779. * @type {Highcharts.PointSelectCallbackFunction}
  31780. * @since 1.2.0
  31781. * @context Highcharts.Point
  31782. * @apioption plotOptions.series.point.events.select
  31783. */
  31784. /**
  31785. * Fires when the point is unselected either programmatically or
  31786. * following a click on the point. One parameter, `event`, is passed
  31787. * to the function.
  31788. * Returning `false` cancels the operation.
  31789. *
  31790. * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
  31791. * Report the last unselected point
  31792. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  31793. * Report select and unselect
  31794. *
  31795. * @type {Highcharts.PointUnselectCallbackFunction}
  31796. * @since 1.2.0
  31797. * @context Highcharts.Point
  31798. * @apioption plotOptions.series.point.events.unselect
  31799. */
  31800. /**
  31801. * Fires when the point is updated programmatically through the
  31802. * `.update()` method. One parameter, `event`, is passed to the
  31803. * function. The new point options can be accessed through
  31804. * `event.options`. Returning `false` cancels the operation.
  31805. *
  31806. * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
  31807. * Confirm point updating
  31808. *
  31809. * @type {Highcharts.PointUpdateCallbackFunction}
  31810. * @since 1.2.0
  31811. * @context Highcharts.Point
  31812. * @apioption plotOptions.series.point.events.update
  31813. */
  31814. /**
  31815. * Events for each single point.
  31816. *
  31817. * @declare Highcharts.PointEventsOptionsObject
  31818. */
  31819. events: {}
  31820. },
  31821. /**
  31822. * Options for the series data labels, appearing next to each data
  31823. * point.
  31824. *
  31825. * Since v6.2.0, multiple data labels can be applied to each single
  31826. * point by defining them as an array of configs.
  31827. *
  31828. * In styled mode, the data labels can be styled with the
  31829. * `.highcharts-data-label-box` and `.highcharts-data-label` class names
  31830. * ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
  31831. *
  31832. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
  31833. * Data labels enabled
  31834. * @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
  31835. * Multiple data labels on a bar series
  31836. * @sample {highcharts} highcharts/css/series-datalabels
  31837. * Style mode example
  31838. *
  31839. * @type {*|Array<*>}
  31840. * @product highcharts highstock highmaps gantt
  31841. *
  31842. * @private
  31843. */
  31844. dataLabels: {
  31845. /**
  31846. * Enable or disable the initial animation when a series is
  31847. * displayed for the `dataLabels`. The animation can also be set as
  31848. * a configuration object. Please note that this option only
  31849. * applies to the initial animation.
  31850. * For other animations, see [chart.animation](#chart.animation)
  31851. * and the animation parameter under the API methods.
  31852. * The following properties are supported:
  31853. *
  31854. * - `defer`: The animation delay time in milliseconds.
  31855. *
  31856. * @sample {highcharts} highcharts/plotoptions/animation-defer/
  31857. * Animation defer settings
  31858. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  31859. * @since 8.2.0
  31860. * @apioption plotOptions.series.dataLabels.animation
  31861. */
  31862. animation: {},
  31863. /**
  31864. * The animation delay time in milliseconds.
  31865. * Set to `0` renders dataLabel immediately.
  31866. * As `undefined` inherits defer time from the [series.animation.defer](#plotOptions.series.animation.defer).
  31867. *
  31868. * @type {number}
  31869. * @since 8.2.0
  31870. * @apioption plotOptions.series.dataLabels.animation.defer
  31871. */
  31872. /**
  31873. * The alignment of the data label compared to the point. If
  31874. * `right`, the right side of the label should be touching the
  31875. * point. For points with an extent, like columns, the alignments
  31876. * also dictates how to align it inside the box, as given with the
  31877. * [inside](#plotOptions.column.dataLabels.inside)
  31878. * option. Can be one of `left`, `center` or `right`.
  31879. *
  31880. * @sample {highcharts} highcharts/plotoptions/series-datalabels-align-left/
  31881. * Left aligned
  31882. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  31883. * Data labels inside the bar
  31884. *
  31885. * @type {Highcharts.AlignValue|null}
  31886. */
  31887. align: 'center',
  31888. /**
  31889. * Whether to allow data labels to overlap. To make the labels less
  31890. * sensitive for overlapping, the
  31891. * [dataLabels.padding](#plotOptions.series.dataLabels.padding)
  31892. * can be set to 0.
  31893. *
  31894. * @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
  31895. * Don't allow overlap
  31896. *
  31897. * @type {boolean}
  31898. * @default false
  31899. * @since 4.1.0
  31900. * @apioption plotOptions.series.dataLabels.allowOverlap
  31901. */
  31902. /**
  31903. * The background color or gradient for the data label.
  31904. *
  31905. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  31906. * Data labels box options
  31907. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  31908. * Data labels box options
  31909. *
  31910. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31911. * @since 2.2.1
  31912. * @apioption plotOptions.series.dataLabels.backgroundColor
  31913. */
  31914. /**
  31915. * The border color for the data label. Defaults to `undefined`.
  31916. *
  31917. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  31918. * Data labels box options
  31919. *
  31920. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31921. * @since 2.2.1
  31922. * @apioption plotOptions.series.dataLabels.borderColor
  31923. */
  31924. /**
  31925. * The border radius in pixels for the data label.
  31926. *
  31927. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  31928. * Data labels box options
  31929. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  31930. * Data labels box options
  31931. *
  31932. * @type {number}
  31933. * @default 0
  31934. * @since 2.2.1
  31935. * @apioption plotOptions.series.dataLabels.borderRadius
  31936. */
  31937. /**
  31938. * The border width in pixels for the data label.
  31939. *
  31940. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  31941. * Data labels box options
  31942. *
  31943. * @type {number}
  31944. * @default 0
  31945. * @since 2.2.1
  31946. * @apioption plotOptions.series.dataLabels.borderWidth
  31947. */
  31948. /**
  31949. * A class name for the data label. Particularly in styled mode,
  31950. * this can be used to give each series' or point's data label
  31951. * unique styling. In addition to this option, a default color class
  31952. * name is added so that we can give the labels a contrast text
  31953. * shadow.
  31954. *
  31955. * @sample {highcharts} highcharts/css/data-label-contrast/
  31956. * Contrast text shadow
  31957. * @sample {highcharts} highcharts/css/series-datalabels/
  31958. * Styling by CSS
  31959. *
  31960. * @type {string}
  31961. * @since 5.0.0
  31962. * @apioption plotOptions.series.dataLabels.className
  31963. */
  31964. /**
  31965. * The text color for the data labels. Defaults to `undefined`. For
  31966. * certain series types, like column or map, the data labels can be
  31967. * drawn inside the points. In this case the data label will be
  31968. * drawn with maximum contrast by default. Additionally, it will be
  31969. * given a `text-outline` style with the opposite color, to further
  31970. * increase the contrast. This can be overridden by setting the
  31971. * `text-outline` style to `none` in the `dataLabels.style` option.
  31972. *
  31973. * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
  31974. * Red data labels
  31975. * @sample {highmaps} maps/demo/color-axis/
  31976. * White data labels
  31977. *
  31978. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31979. * @apioption plotOptions.series.dataLabels.color
  31980. */
  31981. /**
  31982. * Whether to hide data labels that are outside the plot area. By
  31983. * default, the data label is moved inside the plot area according
  31984. * to the
  31985. * [overflow](#plotOptions.series.dataLabels.overflow)
  31986. * option.
  31987. *
  31988. * @type {boolean}
  31989. * @default true
  31990. * @since 2.3.3
  31991. * @apioption plotOptions.series.dataLabels.crop
  31992. */
  31993. /**
  31994. * Whether to defer displaying the data labels until the initial
  31995. * series animation has finished. Setting to `false` renders the
  31996. * data label immediately. If set to `true` inherits the defer
  31997. * time set in [plotOptions.series.animation](#plotOptions.series.animation).
  31998. *
  31999. * @sample highcharts/plotoptions/animation-defer
  32000. * Set defer time
  32001. *
  32002. * @since 4.0.0
  32003. * @product highcharts highstock gantt
  32004. */
  32005. defer: true,
  32006. /**
  32007. * Enable or disable the data labels.
  32008. *
  32009. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
  32010. * Data labels enabled
  32011. * @sample {highmaps} maps/demo/color-axis/
  32012. * Data labels enabled
  32013. *
  32014. * @type {boolean}
  32015. * @default false
  32016. * @apioption plotOptions.series.dataLabels.enabled
  32017. */
  32018. /**
  32019. * A declarative filter to control of which data labels to display.
  32020. * The declarative filter is designed for use when callback
  32021. * functions are not available, like when the chart options require
  32022. * a pure JSON structure or for use with graphical editors. For
  32023. * programmatic control, use the `formatter` instead, and return
  32024. * `undefined` to disable a single data label.
  32025. *
  32026. * @example
  32027. * filter: {
  32028. * property: 'percentage',
  32029. * operator: '>',
  32030. * value: 4
  32031. * }
  32032. *
  32033. * @sample {highcharts} highcharts/demo/pie-monochrome
  32034. * Data labels filtered by percentage
  32035. *
  32036. * @declare Highcharts.DataLabelsFilterOptionsObject
  32037. * @since 6.0.3
  32038. * @apioption plotOptions.series.dataLabels.filter
  32039. */
  32040. /**
  32041. * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
  32042. * `==`, and `===`.
  32043. *
  32044. * @type {string}
  32045. * @validvalue [">", "<", ">=", "<=", "==", "==="]
  32046. * @apioption plotOptions.series.dataLabels.filter.operator
  32047. */
  32048. /**
  32049. * The point property to filter by. Point options are passed
  32050. * directly to properties, additionally there are `y` value,
  32051. * `percentage` and others listed under {@link Highcharts.Point}
  32052. * members.
  32053. *
  32054. * @type {string}
  32055. * @apioption plotOptions.series.dataLabels.filter.property
  32056. */
  32057. /**
  32058. * The value to compare against.
  32059. *
  32060. * @type {number}
  32061. * @apioption plotOptions.series.dataLabels.filter.value
  32062. */
  32063. /**
  32064. * A
  32065. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  32066. * for the data label. Available variables are the same as for
  32067. * `formatter`.
  32068. *
  32069. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  32070. * Add a unit
  32071. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  32072. * Formatted value in the data label
  32073. *
  32074. * @type {string}
  32075. * @default y
  32076. * @default point.value
  32077. * @since 3.0
  32078. * @apioption plotOptions.series.dataLabels.format
  32079. */
  32080. // eslint-disable-next-line valid-jsdoc
  32081. /**
  32082. * Callback JavaScript function to format the data label. Note that
  32083. * if a `format` is defined, the format takes precedence and the
  32084. * formatter is ignored.
  32085. *
  32086. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  32087. * Formatted value
  32088. *
  32089. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  32090. */
  32091. formatter: function () {
  32092. var numberFormatter = this.series.chart.numberFormatter;
  32093. return typeof this.y !== 'number' ? '' : numberFormatter(this.y, -1);
  32094. },
  32095. /**
  32096. * For points with an extent, like columns or map areas, whether to
  32097. * align the data label inside the box or to the actual value point.
  32098. * Defaults to `false` in most cases, `true` in stacked columns.
  32099. *
  32100. * @type {boolean}
  32101. * @since 3.0
  32102. * @apioption plotOptions.series.dataLabels.inside
  32103. */
  32104. /**
  32105. * Format for points with the value of null. Works analogously to
  32106. * [format](#plotOptions.series.dataLabels.format). `nullFormat` can
  32107. * be applied only to series which support displaying null points.
  32108. *
  32109. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  32110. * Format data label and tooltip for null point.
  32111. *
  32112. * @type {boolean|string}
  32113. * @since 7.1.0
  32114. * @apioption plotOptions.series.dataLabels.nullFormat
  32115. */
  32116. /**
  32117. * Callback JavaScript function that defines formatting for points
  32118. * with the value of null. Works analogously to
  32119. * [formatter](#plotOptions.series.dataLabels.formatter).
  32120. * `nullPointFormatter` can be applied only to series which support
  32121. * displaying null points.
  32122. *
  32123. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  32124. * Format data label and tooltip for null point.
  32125. *
  32126. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  32127. * @since 7.1.0
  32128. * @apioption plotOptions.series.dataLabels.nullFormatter
  32129. */
  32130. /**
  32131. * How to handle data labels that flow outside the plot area. The
  32132. * default is `"justify"`, which aligns them inside the plot area.
  32133. * For columns and bars, this means it will be moved inside the bar.
  32134. * To display data labels outside the plot area, set `crop` to
  32135. * `false` and `overflow` to `"allow"`.
  32136. *
  32137. * @type {Highcharts.DataLabelsOverflowValue}
  32138. * @default justify
  32139. * @since 3.0.6
  32140. * @apioption plotOptions.series.dataLabels.overflow
  32141. */
  32142. /**
  32143. * When either the `borderWidth` or the `backgroundColor` is set,
  32144. * this is the padding within the box.
  32145. *
  32146. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  32147. * Data labels box options
  32148. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  32149. * Data labels box options
  32150. *
  32151. * @since 2.2.1
  32152. */
  32153. padding: 5,
  32154. /**
  32155. * Aligns data labels relative to points. If `center` alignment is
  32156. * not possible, it defaults to `right`.
  32157. *
  32158. * @type {Highcharts.AlignValue}
  32159. * @default center
  32160. * @apioption plotOptions.series.dataLabels.position
  32161. */
  32162. /**
  32163. * Text rotation in degrees. Note that due to a more complex
  32164. * structure, backgrounds, borders and padding will be lost on a
  32165. * rotated data label.
  32166. *
  32167. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  32168. * Vertical labels
  32169. *
  32170. * @type {number}
  32171. * @default 0
  32172. * @apioption plotOptions.series.dataLabels.rotation
  32173. */
  32174. /**
  32175. * The shadow of the box. Works best with `borderWidth` or
  32176. * `backgroundColor`. Since 2.3 the shadow can be an object
  32177. * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
  32178. * and `width`.
  32179. *
  32180. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  32181. * Data labels box options
  32182. *
  32183. * @type {boolean|Highcharts.ShadowOptionsObject}
  32184. * @default false
  32185. * @since 2.2.1
  32186. * @apioption plotOptions.series.dataLabels.shadow
  32187. */
  32188. /**
  32189. * The name of a symbol to use for the border around the label.
  32190. * Symbols are predefined functions on the Renderer object.
  32191. *
  32192. * @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
  32193. * A callout for annotations
  32194. *
  32195. * @type {string}
  32196. * @default square
  32197. * @since 4.1.2
  32198. * @apioption plotOptions.series.dataLabels.shape
  32199. */
  32200. /**
  32201. * Styles for the label. The default `color` setting is
  32202. * `"contrast"`, which is a pseudo color that Highcharts picks up
  32203. * and applies the maximum contrast to the underlying point item,
  32204. * for example the bar in a bar chart.
  32205. *
  32206. * The `textOutline` is a pseudo property that applies an outline of
  32207. * the given width with the given color, which by default is the
  32208. * maximum contrast to the text. So a bright text color will result
  32209. * in a black text outline for maximum readability on a mixed
  32210. * background. In some cases, especially with grayscale text, the
  32211. * text outline doesn't work well, in which cases it can be disabled
  32212. * by setting it to `"none"`. When `useHTML` is true, the
  32213. * `textOutline` will not be picked up. In this, case, the same
  32214. * effect can be acheived through the `text-shadow` CSS property.
  32215. *
  32216. * For some series types, where each point has an extent, like for
  32217. * example tree maps, the data label may overflow the point. There
  32218. * are two strategies for handling overflow. By default, the text
  32219. * will wrap to multiple lines. The other strategy is to set
  32220. * `style.textOverflow` to `ellipsis`, which will keep the text on
  32221. * one line plus it will break inside long words.
  32222. *
  32223. * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
  32224. * Bold labels
  32225. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow/
  32226. * Long labels truncated with an ellipsis in a pie
  32227. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap/
  32228. * Long labels are wrapped in a pie
  32229. * @sample {highmaps} maps/demo/color-axis/
  32230. * Bold labels
  32231. *
  32232. * @type {Highcharts.CSSObject}
  32233. * @since 4.1.0
  32234. * @apioption plotOptions.series.dataLabels.style
  32235. */
  32236. style: {
  32237. /** @internal */
  32238. fontSize: '11px',
  32239. /** @internal */
  32240. fontWeight: 'bold',
  32241. /** @internal */
  32242. color: 'contrast',
  32243. /** @internal */
  32244. textOutline: '1px contrast'
  32245. },
  32246. /**
  32247. * Options for a label text which should follow marker's shape.
  32248. * Border and background are disabled for a label that follows a
  32249. * path.
  32250. *
  32251. * **Note:** Only SVG-based renderer supports this option. Setting
  32252. * `useHTML` to true will disable this option.
  32253. *
  32254. * @declare Highcharts.DataLabelsTextPathOptionsObject
  32255. * @since 7.1.0
  32256. * @apioption plotOptions.series.dataLabels.textPath
  32257. */
  32258. /**
  32259. * Presentation attributes for the text path.
  32260. *
  32261. * @type {Highcharts.SVGAttributes}
  32262. * @since 7.1.0
  32263. * @apioption plotOptions.series.dataLabels.textPath.attributes
  32264. */
  32265. /**
  32266. * Enable or disable `textPath` option for link's or marker's data
  32267. * labels.
  32268. *
  32269. * @type {boolean}
  32270. * @since 7.1.0
  32271. * @apioption plotOptions.series.dataLabels.textPath.enabled
  32272. */
  32273. /**
  32274. * Whether to
  32275. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  32276. * to render the labels.
  32277. *
  32278. * @type {boolean}
  32279. * @default false
  32280. * @apioption plotOptions.series.dataLabels.useHTML
  32281. */
  32282. /**
  32283. * The vertical alignment of a data label. Can be one of `top`,
  32284. * `middle` or `bottom`. The default value depends on the data, for
  32285. * instance in a column chart, the label is above positive values
  32286. * and below negative values.
  32287. *
  32288. * @type {Highcharts.VerticalAlignValue|null}
  32289. * @since 2.3.3
  32290. */
  32291. verticalAlign: 'bottom',
  32292. /**
  32293. * The x position offset of the label relative to the point in
  32294. * pixels.
  32295. *
  32296. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  32297. * Vertical and positioned
  32298. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  32299. * Data labels inside the bar
  32300. */
  32301. x: 0,
  32302. /**
  32303. * The Z index of the data labels. The default Z index puts it above
  32304. * the series. Use a Z index of 2 to display it behind the series.
  32305. *
  32306. * @type {number}
  32307. * @default 6
  32308. * @since 2.3.5
  32309. * @apioption plotOptions.series.dataLabels.z
  32310. */
  32311. /**
  32312. * The y position offset of the label relative to the point in
  32313. * pixels.
  32314. *
  32315. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  32316. * Vertical and positioned
  32317. */
  32318. y: 0
  32319. },
  32320. /**
  32321. * When the series contains less points than the crop threshold, all
  32322. * points are drawn, even if the points fall outside the visible plot
  32323. * area at the current zoom. The advantage of drawing all points
  32324. * (including markers and columns), is that animation is performed on
  32325. * updates. On the other hand, when the series contains more points than
  32326. * the crop threshold, the series data is cropped to only contain points
  32327. * that fall within the plot area. The advantage of cropping away
  32328. * invisible points is to increase performance on large series.
  32329. *
  32330. * @since 2.2
  32331. * @product highcharts highstock
  32332. *
  32333. * @private
  32334. */
  32335. cropThreshold: 300,
  32336. /**
  32337. * Opacity of a series parts: line, fill (e.g. area) and dataLabels.
  32338. *
  32339. * @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
  32340. *
  32341. * @since 7.1.0
  32342. *
  32343. * @private
  32344. */
  32345. opacity: 1,
  32346. /**
  32347. * The width of each point on the x axis. For example in a column chart
  32348. * with one value each day, the pointRange would be 1 day (= 24 * 3600
  32349. * * 1000 milliseconds). This is normally computed automatically, but
  32350. * this option can be used to override the automatic value.
  32351. *
  32352. * @product highstock
  32353. *
  32354. * @private
  32355. */
  32356. pointRange: 0,
  32357. /**
  32358. * When this is true, the series will not cause the Y axis to cross
  32359. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  32360. * unless the data actually crosses the plane.
  32361. *
  32362. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  32363. * 3 will make the Y axis show negative values according to the
  32364. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  32365. * at 0.
  32366. *
  32367. * @since 4.1.9
  32368. * @product highcharts highstock
  32369. *
  32370. * @private
  32371. */
  32372. softThreshold: true,
  32373. /**
  32374. * @declare Highcharts.SeriesStatesOptionsObject
  32375. *
  32376. * @private
  32377. */
  32378. states: {
  32379. /**
  32380. * The normal state of a series, or for point items in column, pie
  32381. * and similar series. Currently only used for setting animation
  32382. * when returning to normal state from hover.
  32383. *
  32384. * @declare Highcharts.SeriesStatesNormalOptionsObject
  32385. */
  32386. normal: {
  32387. /**
  32388. * Animation when returning to normal state after hovering.
  32389. *
  32390. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  32391. */
  32392. animation: true
  32393. },
  32394. /**
  32395. * Options for the hovered series. These settings override the
  32396. * normal state options when a series is moused over or touched.
  32397. *
  32398. * @declare Highcharts.SeriesStatesHoverOptionsObject
  32399. */
  32400. hover: {
  32401. /**
  32402. * Enable separate styles for the hovered series to visualize
  32403. * that the user hovers either the series itself or the legend.
  32404. *
  32405. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
  32406. * Line
  32407. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
  32408. * Column
  32409. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
  32410. * Pie
  32411. *
  32412. * @type {boolean}
  32413. * @default true
  32414. * @since 1.2
  32415. * @apioption plotOptions.series.states.hover.enabled
  32416. */
  32417. /**
  32418. * Animation setting for hovering the graph in line-type series.
  32419. *
  32420. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  32421. * @since 5.0.8
  32422. * @product highcharts highstock
  32423. */
  32424. animation: {
  32425. /**
  32426. * The duration of the hover animation in milliseconds. By
  32427. * default the hover state animates quickly in, and slowly
  32428. * back to normal.
  32429. *
  32430. * @internal
  32431. */
  32432. duration: 50
  32433. },
  32434. /**
  32435. * Pixel width of the graph line. By default this property is
  32436. * undefined, and the `lineWidthPlus` property dictates how much
  32437. * to increase the linewidth from normal state.
  32438. *
  32439. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
  32440. * 5px line on hover
  32441. *
  32442. * @type {number}
  32443. * @product highcharts highstock
  32444. * @apioption plotOptions.series.states.hover.lineWidth
  32445. */
  32446. /**
  32447. * The additional line width for the graph of a hovered series.
  32448. *
  32449. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  32450. * 5 pixels wider
  32451. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  32452. * 5 pixels wider
  32453. *
  32454. * @since 4.0.3
  32455. * @product highcharts highstock
  32456. */
  32457. lineWidthPlus: 1,
  32458. /**
  32459. * In Highcharts 1.0, the appearance of all markers belonging
  32460. * to the hovered series. For settings on the hover state of the
  32461. * individual point, see
  32462. * [marker.states.hover](#plotOptions.series.marker.states.hover).
  32463. *
  32464. * @deprecated
  32465. *
  32466. * @extends plotOptions.series.marker
  32467. * @excluding states
  32468. * @product highcharts highstock
  32469. */
  32470. marker: {
  32471. // lineWidth: base + 1,
  32472. // radius: base + 1
  32473. },
  32474. /**
  32475. * Options for the halo appearing around the hovered point in
  32476. * line-type series as well as outside the hovered slice in pie
  32477. * charts. By default the halo is filled by the current point or
  32478. * series color with an opacity of 0.25\. The halo can be
  32479. * disabled by setting the `halo` option to `null`.
  32480. *
  32481. * In styled mode, the halo is styled with the
  32482. * `.highcharts-halo` class, with colors inherited from
  32483. * `.highcharts-color-{n}`.
  32484. *
  32485. * @sample {highcharts} highcharts/plotoptions/halo/
  32486. * Halo options
  32487. * @sample {highstock} highcharts/plotoptions/halo/
  32488. * Halo options
  32489. *
  32490. * @declare Highcharts.SeriesStatesHoverHaloOptionsObject
  32491. * @type {null|*}
  32492. * @since 4.0
  32493. * @product highcharts highstock
  32494. */
  32495. halo: {
  32496. /**
  32497. * A collection of SVG attributes to override the appearance
  32498. * of the halo, for example `fill`, `stroke` and
  32499. * `stroke-width`.
  32500. *
  32501. * @type {Highcharts.SVGAttributes}
  32502. * @since 4.0
  32503. * @product highcharts highstock
  32504. * @apioption plotOptions.series.states.hover.halo.attributes
  32505. */
  32506. /**
  32507. * The pixel size of the halo. For point markers this is the
  32508. * radius of the halo. For pie slices it is the width of the
  32509. * halo outside the slice. For bubbles it defaults to 5 and
  32510. * is the width of the halo outside the bubble.
  32511. *
  32512. * @since 4.0
  32513. * @product highcharts highstock
  32514. */
  32515. size: 10,
  32516. /**
  32517. * Opacity for the halo unless a specific fill is overridden
  32518. * using the `attributes` setting. Note that Highcharts is
  32519. * only able to apply opacity to colors of hex or rgb(a)
  32520. * formats.
  32521. *
  32522. * @since 4.0
  32523. * @product highcharts highstock
  32524. */
  32525. opacity: 0.25
  32526. }
  32527. },
  32528. /**
  32529. * Specific options for point in selected states, after being
  32530. * selected by
  32531. * [allowPointSelect](#plotOptions.series.allowPointSelect)
  32532. * or programmatically.
  32533. *
  32534. * @sample maps/plotoptions/series-allowpointselect/
  32535. * Allow point select demo
  32536. *
  32537. * @declare Highcharts.SeriesStatesSelectOptionsObject
  32538. * @extends plotOptions.series.states.hover
  32539. * @excluding brightness
  32540. */
  32541. select: {
  32542. animation: {
  32543. /** @internal */
  32544. duration: 0
  32545. }
  32546. },
  32547. /**
  32548. * The opposite state of a hover for series.
  32549. *
  32550. * @sample highcharts/plotoptions/series-states-inactive-disabled
  32551. * Disabled inactive state
  32552. *
  32553. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  32554. */
  32555. inactive: {
  32556. /**
  32557. * Enable or disable the inactive state for a series
  32558. *
  32559. * @sample highcharts/plotoptions/series-states-inactive-disabled
  32560. * Disabled inactive state
  32561. *
  32562. * @type {boolean}
  32563. * @default true
  32564. * @apioption plotOptions.series.states.inactive.enabled
  32565. */
  32566. /**
  32567. * The animation for entering the inactive state.
  32568. *
  32569. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  32570. */
  32571. animation: {
  32572. /** @internal */
  32573. duration: 50
  32574. },
  32575. /**
  32576. * Opacity of series elements (dataLabels, line, area).
  32577. *
  32578. * @type {number}
  32579. */
  32580. opacity: 0.2
  32581. }
  32582. },
  32583. /**
  32584. * Sticky tracking of mouse events. When true, the `mouseOut` event on a
  32585. * series isn't triggered until the mouse moves over another series, or
  32586. * out of the plot area. When false, the `mouseOut` event on a series is
  32587. * triggered when the mouse leaves the area around the series' graph or
  32588. * markers. This also implies the tooltip when not shared. When
  32589. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  32590. * will be hidden when moving the mouse between series. Defaults to true
  32591. * for line and area type series, but to false for columns, pies etc.
  32592. *
  32593. * **Note:** The boost module will force this option because of
  32594. * technical limitations.
  32595. *
  32596. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
  32597. * True by default
  32598. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
  32599. * False
  32600. *
  32601. * @default {highcharts} true
  32602. * @default {highstock} true
  32603. * @default {highmaps} false
  32604. * @since 2.0
  32605. *
  32606. * @private
  32607. */
  32608. stickyTracking: true,
  32609. /**
  32610. * A configuration object for the tooltip rendering of each single
  32611. * series. Properties are inherited from [tooltip](#tooltip), but only
  32612. * the following properties can be defined on a series level.
  32613. *
  32614. * @declare Highcharts.SeriesTooltipOptionsObject
  32615. * @since 2.3
  32616. * @extends tooltip
  32617. * @excluding animation, backgroundColor, borderColor, borderRadius,
  32618. * borderWidth, className, crosshairs, enabled, formatter,
  32619. * headerShape, hideDelay, outside, padding, positioner,
  32620. * shadow, shape, shared, snap, split, style, useHTML
  32621. * @apioption plotOptions.series.tooltip
  32622. */
  32623. /**
  32624. * When a series contains a data array that is longer than this, only
  32625. * one dimensional arrays of numbers, or two dimensional arrays with
  32626. * x and y values are allowed. Also, only the first point is tested,
  32627. * and the rest are assumed to be the same format. This saves expensive
  32628. * data checking and indexing in long series. Set it to `0` disable.
  32629. *
  32630. * Note:
  32631. * In boost mode turbo threshold is forced. Only array of numbers or
  32632. * two dimensional arrays are allowed.
  32633. *
  32634. * @since 2.2
  32635. * @product highcharts highstock gantt
  32636. *
  32637. * @private
  32638. */
  32639. turboThreshold: 1000,
  32640. /**
  32641. * An array defining zones within a series. Zones can be applied to the
  32642. * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
  32643. * option. The zone definitions have to be in ascending order regarding
  32644. * to the value.
  32645. *
  32646. * In styled mode, the color zones are styled with the
  32647. * `.highcharts-zone-{n}` class, or custom classed from the `className`
  32648. * option
  32649. * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
  32650. *
  32651. * @see [zoneAxis](#plotOptions.series.zoneAxis)
  32652. *
  32653. * @sample {highcharts} highcharts/series/color-zones-simple/
  32654. * Color zones
  32655. * @sample {highstock} highcharts/series/color-zones-simple/
  32656. * Color zones
  32657. *
  32658. * @declare Highcharts.SeriesZonesOptionsObject
  32659. * @type {Array<*>}
  32660. * @since 4.1.0
  32661. * @product highcharts highstock
  32662. * @apioption plotOptions.series.zones
  32663. */
  32664. /**
  32665. * Styled mode only. A custom class name for the zone.
  32666. *
  32667. * @sample highcharts/css/color-zones/
  32668. * Zones styled by class name
  32669. *
  32670. * @type {string}
  32671. * @since 5.0.0
  32672. * @apioption plotOptions.series.zones.className
  32673. */
  32674. /**
  32675. * Defines the color of the series.
  32676. *
  32677. * @see [series color](#plotOptions.series.color)
  32678. *
  32679. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  32680. * @since 4.1.0
  32681. * @product highcharts highstock
  32682. * @apioption plotOptions.series.zones.color
  32683. */
  32684. /**
  32685. * A name for the dash style to use for the graph.
  32686. *
  32687. * @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  32688. *
  32689. * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
  32690. * Dashed line indicates prognosis
  32691. *
  32692. * @type {Highcharts.DashStyleValue}
  32693. * @since 4.1.0
  32694. * @product highcharts highstock
  32695. * @apioption plotOptions.series.zones.dashStyle
  32696. */
  32697. /**
  32698. * Defines the fill color for the series (in area type series)
  32699. *
  32700. * @see [fillColor](#plotOptions.area.fillColor)
  32701. *
  32702. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  32703. * @since 4.1.0
  32704. * @product highcharts highstock
  32705. * @apioption plotOptions.series.zones.fillColor
  32706. */
  32707. /**
  32708. * The value up to where the zone extends, if undefined the zones
  32709. * stretches to the last value in the series.
  32710. *
  32711. * @type {number}
  32712. * @since 4.1.0
  32713. * @product highcharts highstock
  32714. * @apioption plotOptions.series.zones.value
  32715. */
  32716. /**
  32717. * When using dual or multiple color axes, this number defines which
  32718. * colorAxis the particular series is connected to. It refers to
  32719. * either the
  32720. * {@link #colorAxis.id|axis id}
  32721. * or the index of the axis in the colorAxis array, with 0 being the
  32722. * first. Set this option to false to prevent a series from connecting
  32723. * to the default color axis.
  32724. *
  32725. * Since v7.2.0 the option can also be an axis id or an axis index
  32726. * instead of a boolean flag.
  32727. *
  32728. * @sample highcharts/coloraxis/coloraxis-with-pie/
  32729. * Color axis with pie series
  32730. * @sample highcharts/coloraxis/multiple-coloraxis/
  32731. * Multiple color axis
  32732. *
  32733. * @type {number|string|boolean}
  32734. * @default 0
  32735. * @product highcharts highstock highmaps
  32736. * @apioption plotOptions.series.colorAxis
  32737. */
  32738. /**
  32739. * Determines what data value should be used to calculate point color
  32740. * if `colorAxis` is used. Requires to set `min` and `max` if some
  32741. * custom point property is used or if approximation for data grouping
  32742. * is set to `'sum'`.
  32743. *
  32744. * @sample highcharts/coloraxis/custom-color-key/
  32745. * Custom color key
  32746. * @sample highcharts/coloraxis/changed-default-color-key/
  32747. * Changed default color key
  32748. *
  32749. * @type {string}
  32750. * @default y
  32751. * @since 7.2.0
  32752. * @product highcharts highstock highmaps
  32753. * @apioption plotOptions.series.colorKey
  32754. */
  32755. /**
  32756. * Determines whether the series should look for the nearest point
  32757. * in both dimensions or just the x-dimension when hovering the series.
  32758. * Defaults to `'xy'` for scatter series and `'x'` for most other
  32759. * series. If the data has duplicate x-values, it is recommended to
  32760. * set this to `'xy'` to allow hovering over all points.
  32761. *
  32762. * Applies only to series types using nearest neighbor search (not
  32763. * direct hover) for tooltip.
  32764. *
  32765. * @sample {highcharts} highcharts/series/findnearestpointby/
  32766. * Different hover behaviors
  32767. * @sample {highstock} highcharts/series/findnearestpointby/
  32768. * Different hover behaviors
  32769. * @sample {highmaps} highcharts/series/findnearestpointby/
  32770. * Different hover behaviors
  32771. *
  32772. * @since 5.0.10
  32773. * @validvalue ["x", "xy"]
  32774. *
  32775. * @private
  32776. */
  32777. findNearestPointBy: 'x'
  32778. },
  32779. /* eslint-disable no-invalid-this, valid-jsdoc */
  32780. /** @lends Highcharts.Series.prototype */
  32781. {
  32782. axisTypes: ['xAxis', 'yAxis'],
  32783. coll: 'series',
  32784. colorCounter: 0,
  32785. cropShoulder: 1,
  32786. directTouch: false,
  32787. isCartesian: true,
  32788. // each point's x and y values are stored in this.xData and this.yData
  32789. parallelArrays: ['x', 'y'],
  32790. pointClass: Point,
  32791. requireSorting: true,
  32792. sorted: true,
  32793. init: function (chart, options) {
  32794. fireEvent(this, 'init', { options: options });
  32795. var series = this,
  32796. events,
  32797. chartSeries = chart.series,
  32798. lastSeries;
  32799. // A lookup over those events that are added by _options_ (not
  32800. // programmatically). These are updated through Series.update()
  32801. // (#10861).
  32802. this.eventOptions = this.eventOptions || {};
  32803. // The 'eventsToUnbind' property moved from prototype into the
  32804. // Series init to avoid reference to the same array between
  32805. // the different series and charts. #12959, #13937
  32806. this.eventsToUnbind = [];
  32807. /**
  32808. * Read only. The chart that the series belongs to.
  32809. *
  32810. * @name Highcharts.Series#chart
  32811. * @type {Highcharts.Chart}
  32812. */
  32813. series.chart = chart;
  32814. /**
  32815. * Read only. The series' type, like "line", "area", "column" etc.
  32816. * The type in the series options anc can be altered using
  32817. * {@link Series#update}.
  32818. *
  32819. * @name Highcharts.Series#type
  32820. * @type {string}
  32821. */
  32822. /**
  32823. * Read only. The series' current options. To update, use
  32824. * {@link Series#update}.
  32825. *
  32826. * @name Highcharts.Series#options
  32827. * @type {Highcharts.SeriesOptionsType}
  32828. */
  32829. series.options = options = series.setOptions(options);
  32830. series.linkedSeries = [];
  32831. // bind the axes
  32832. series.bindAxes();
  32833. // set some variables
  32834. extend(series, {
  32835. /**
  32836. * The series name as given in the options. Defaults to
  32837. * "Series {n}".
  32838. *
  32839. * @name Highcharts.Series#name
  32840. * @type {string}
  32841. */
  32842. name: options.name,
  32843. state: '',
  32844. /**
  32845. * Read only. The series' visibility state as set by {@link
  32846. * Series#show}, {@link Series#hide}, or in the initial
  32847. * configuration.
  32848. *
  32849. * @name Highcharts.Series#visible
  32850. * @type {boolean}
  32851. */
  32852. visible: options.visible !== false,
  32853. /**
  32854. * Read only. The series' selected state as set by {@link
  32855. * Highcharts.Series#select}.
  32856. *
  32857. * @name Highcharts.Series#selected
  32858. * @type {boolean}
  32859. */
  32860. selected: options.selected === true // false by default
  32861. });
  32862. // Register event listeners
  32863. events = options.events;
  32864. objectEach(events, function (event, eventType) {
  32865. if (isFunction(event)) {
  32866. // If event does not exist, or is changed by Series.update
  32867. if (series.eventOptions[eventType] !== event) {
  32868. // Remove existing if set by option
  32869. if (isFunction(series.eventOptions[eventType])) {
  32870. removeEvent(series, eventType, series.eventOptions[eventType]);
  32871. }
  32872. series.eventOptions[eventType] = event;
  32873. addEvent(series, eventType, event);
  32874. }
  32875. }
  32876. });
  32877. if ((events && events.click) ||
  32878. (options.point &&
  32879. options.point.events &&
  32880. options.point.events.click) ||
  32881. options.allowPointSelect) {
  32882. chart.runTrackerClick = true;
  32883. }
  32884. series.getColor();
  32885. series.getSymbol();
  32886. // Initialize the parallel data arrays
  32887. series.parallelArrays.forEach(function (key) {
  32888. if (!series[key + 'Data']) {
  32889. series[key + 'Data'] = [];
  32890. }
  32891. });
  32892. // Mark cartesian
  32893. if (series.isCartesian) {
  32894. chart.hasCartesianSeries = true;
  32895. }
  32896. // Get the index and register the series in the chart. The index is
  32897. // one more than the current latest series index (#5960).
  32898. if (chartSeries.length) {
  32899. lastSeries = chartSeries[chartSeries.length - 1];
  32900. }
  32901. series._i = pick(lastSeries && lastSeries._i, -1) + 1;
  32902. series.opacity = series.options.opacity;
  32903. // Insert the series and re-order all series above the insertion
  32904. // point.
  32905. chart.orderSeries(this.insert(chartSeries));
  32906. // Set options for series with sorting and set data later.
  32907. if (options.dataSorting && options.dataSorting.enabled) {
  32908. series.setDataSortingOptions();
  32909. }
  32910. else if (!series.points && !series.data) {
  32911. series.setData(options.data, false);
  32912. }
  32913. fireEvent(this, 'afterInit');
  32914. },
  32915. /**
  32916. * Check whether the series item is itself or inherits from a certain
  32917. * series type.
  32918. *
  32919. * @function Highcharts.Series#is
  32920. * @param {string} type The type of series to check for, can be either
  32921. * featured or custom series types. For example `column`, `pie`,
  32922. * `ohlc` etc.
  32923. *
  32924. * @return {boolean}
  32925. * True if this item is or inherits from the given type.
  32926. */
  32927. is: function (type) {
  32928. return seriesTypes[type] && this instanceof seriesTypes[type];
  32929. },
  32930. /**
  32931. * Insert the series in a collection with other series, either the chart
  32932. * series or yAxis series, in the correct order according to the index
  32933. * option. Used internally when adding series.
  32934. *
  32935. * @private
  32936. * @function Highcharts.Series#insert
  32937. * @param {Array<Highcharts.Series>} collection
  32938. * A collection of series, like `chart.series` or `xAxis.series`.
  32939. * @return {number}
  32940. * The index of the series in the collection.
  32941. */
  32942. insert: function (collection) {
  32943. var indexOption = this.options.index,
  32944. i;
  32945. // Insert by index option
  32946. if (isNumber(indexOption)) {
  32947. i = collection.length;
  32948. while (i--) {
  32949. // Loop down until the interted element has higher index
  32950. if (indexOption >=
  32951. pick(collection[i].options.index, collection[i]._i)) {
  32952. collection.splice(i + 1, 0, this);
  32953. break;
  32954. }
  32955. }
  32956. if (i === -1) {
  32957. collection.unshift(this);
  32958. }
  32959. i = i + 1;
  32960. // Or just push it to the end
  32961. }
  32962. else {
  32963. collection.push(this);
  32964. }
  32965. return pick(i, collection.length - 1);
  32966. },
  32967. /**
  32968. * Set the xAxis and yAxis properties of cartesian series, and register
  32969. * the series in the `axis.series` array.
  32970. *
  32971. * @private
  32972. * @function Highcharts.Series#bindAxes
  32973. * @return {void}
  32974. * @exception 18
  32975. */
  32976. bindAxes: function () {
  32977. var series = this,
  32978. seriesOptions = series.options,
  32979. chart = series.chart,
  32980. axisOptions;
  32981. fireEvent(this, 'bindAxes', null, function () {
  32982. // repeat for xAxis and yAxis
  32983. (series.axisTypes || []).forEach(function (AXIS) {
  32984. // loop through the chart's axis objects
  32985. chart[AXIS].forEach(function (axis) {
  32986. axisOptions = axis.options;
  32987. // apply if the series xAxis or yAxis option mathches
  32988. // the number of the axis, or if undefined, use the
  32989. // first axis
  32990. if (seriesOptions[AXIS] ===
  32991. axisOptions.index ||
  32992. (typeof seriesOptions[AXIS] !==
  32993. 'undefined' &&
  32994. seriesOptions[AXIS] === axisOptions.id) ||
  32995. (typeof seriesOptions[AXIS] ===
  32996. 'undefined' &&
  32997. axisOptions.index === 0)) {
  32998. // register this series in the axis.series lookup
  32999. series.insert(axis.series);
  33000. // set this series.xAxis or series.yAxis reference
  33001. /**
  33002. * Read only. The unique xAxis object associated
  33003. * with the series.
  33004. *
  33005. * @name Highcharts.Series#xAxis
  33006. * @type {Highcharts.Axis}
  33007. */
  33008. /**
  33009. * Read only. The unique yAxis object associated
  33010. * with the series.
  33011. *
  33012. * @name Highcharts.Series#yAxis
  33013. * @type {Highcharts.Axis}
  33014. */
  33015. series[AXIS] = axis;
  33016. // mark dirty for redraw
  33017. axis.isDirty = true;
  33018. }
  33019. });
  33020. // The series needs an X and an Y axis
  33021. if (!series[AXIS] &&
  33022. series.optionalAxis !== AXIS) {
  33023. error(18, true, chart);
  33024. }
  33025. });
  33026. });
  33027. fireEvent(this, 'afterBindAxes');
  33028. },
  33029. /**
  33030. * For simple series types like line and column, the data values are
  33031. * held in arrays like xData and yData for quick lookup to find extremes
  33032. * and more. For multidimensional series like bubble and map, this can
  33033. * be extended with arrays like zData and valueData by adding to the
  33034. * `series.parallelArrays` array.
  33035. *
  33036. * @private
  33037. * @function Highcharts.Series#updateParallelArrays
  33038. * @param {Highcharts.Point} point
  33039. * @param {number|string} i
  33040. * @return {void}
  33041. */
  33042. updateParallelArrays: function (point, i) {
  33043. var series = point.series,
  33044. args = arguments,
  33045. fn = isNumber(i) ?
  33046. // Insert the value in the given position
  33047. function (key) {
  33048. var val = key === 'y' && series.toYData ?
  33049. series.toYData(point) :
  33050. point[key];
  33051. series[key + 'Data'][i] = val;
  33052. } :
  33053. // Apply the method specified in i with the following
  33054. // arguments as arguments
  33055. function (key) {
  33056. Array.prototype[i].apply(series[key + 'Data'], Array.prototype.slice.call(args, 2));
  33057. };
  33058. series.parallelArrays.forEach(fn);
  33059. },
  33060. /**
  33061. * Define hasData functions for series. These return true if there
  33062. * are data points on this series within the plot area.
  33063. *
  33064. * @private
  33065. * @function Highcharts.Series#hasData
  33066. * @return {boolean}
  33067. */
  33068. hasData: function () {
  33069. return ((this.visible &&
  33070. typeof this.dataMax !== 'undefined' &&
  33071. typeof this.dataMin !== 'undefined') || ( // #3703
  33072. this.visible &&
  33073. this.yData &&
  33074. this.yData.length > 0) // #9758
  33075. );
  33076. },
  33077. /**
  33078. * Return an auto incremented x value based on the pointStart and
  33079. * pointInterval options. This is only used if an x value is not given
  33080. * for the point that calls autoIncrement.
  33081. *
  33082. * @private
  33083. * @function Highcharts.Series#autoIncrement
  33084. * @return {number}
  33085. */
  33086. autoIncrement: function () {
  33087. var options = this.options,
  33088. xIncrement = this.xIncrement,
  33089. date,
  33090. pointInterval,
  33091. pointIntervalUnit = options.pointIntervalUnit,
  33092. time = this.chart.time;
  33093. xIncrement = pick(xIncrement, options.pointStart, 0);
  33094. this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
  33095. // Added code for pointInterval strings
  33096. if (pointIntervalUnit) {
  33097. date = new time.Date(xIncrement);
  33098. if (pointIntervalUnit === 'day') {
  33099. time.set('Date', date, time.get('Date', date) + pointInterval);
  33100. }
  33101. else if (pointIntervalUnit === 'month') {
  33102. time.set('Month', date, time.get('Month', date) + pointInterval);
  33103. }
  33104. else if (pointIntervalUnit === 'year') {
  33105. time.set('FullYear', date, time.get('FullYear', date) + pointInterval);
  33106. }
  33107. pointInterval = date.getTime() - xIncrement;
  33108. }
  33109. this.xIncrement = xIncrement + pointInterval;
  33110. return xIncrement;
  33111. },
  33112. /**
  33113. * Internal function to set properties for series if data sorting is
  33114. * enabled.
  33115. *
  33116. * @private
  33117. * @function Highcharts.Series#setDataSortingOptions
  33118. * @return {void}
  33119. */
  33120. setDataSortingOptions: function () {
  33121. var options = this.options;
  33122. extend(this, {
  33123. requireSorting: false,
  33124. sorted: false,
  33125. enabledDataSorting: true,
  33126. allowDG: false
  33127. });
  33128. // To allow unsorted data for column series.
  33129. if (!defined(options.pointRange)) {
  33130. options.pointRange = 1;
  33131. }
  33132. },
  33133. /**
  33134. * Set the series options by merging from the options tree. Called
  33135. * internally on initializing and updating series. This function will
  33136. * not redraw the series. For API usage, use {@link Series#update}.
  33137. * @private
  33138. * @function Highcharts.Series#setOptions
  33139. * @param {Highcharts.SeriesOptionsType} itemOptions
  33140. * The series options.
  33141. * @return {Highcharts.SeriesOptionsType}
  33142. * @fires Highcharts.Series#event:afterSetOptions
  33143. */
  33144. setOptions: function (itemOptions) {
  33145. var chart = this.chart,
  33146. chartOptions = chart.options,
  33147. plotOptions = chartOptions.plotOptions,
  33148. userOptions = chart.userOptions || {},
  33149. seriesUserOptions = merge(itemOptions),
  33150. options,
  33151. zones,
  33152. zone,
  33153. styledMode = chart.styledMode,
  33154. e = {
  33155. plotOptions: plotOptions,
  33156. userOptions: seriesUserOptions
  33157. };
  33158. fireEvent(this, 'setOptions', e);
  33159. // These may be modified by the event
  33160. var typeOptions = e.plotOptions[this.type],
  33161. userPlotOptions = (userOptions.plotOptions || {});
  33162. // use copy to prevent undetected changes (#9762)
  33163. /**
  33164. * Contains series options by the user without defaults.
  33165. * @name Highcharts.Series#userOptions
  33166. * @type {Highcharts.SeriesOptionsType}
  33167. */
  33168. this.userOptions = e.userOptions;
  33169. options = merge(typeOptions, plotOptions.series,
  33170. // #3881, chart instance plotOptions[type] should trump
  33171. // plotOptions.series
  33172. userOptions.plotOptions &&
  33173. userOptions.plotOptions[this.type], seriesUserOptions);
  33174. // The tooltip options are merged between global and series specific
  33175. // options. Importance order asscendingly:
  33176. // globals: (1)tooltip, (2)plotOptions.series,
  33177. // (3)plotOptions[this.type]
  33178. // init userOptions with possible later updates: 4-6 like 1-3 and
  33179. // (7)this series options
  33180. this.tooltipOptions = merge(defaultOptions.tooltip, // 1
  33181. defaultOptions.plotOptions.series &&
  33182. defaultOptions.plotOptions.series.tooltip, // 2
  33183. defaultOptions.plotOptions[this.type].tooltip, // 3
  33184. chartOptions.tooltip.userOptions, // 4
  33185. plotOptions.series &&
  33186. plotOptions.series.tooltip, // 5
  33187. plotOptions[this.type].tooltip, // 6
  33188. seriesUserOptions.tooltip // 7
  33189. );
  33190. // When shared tooltip, stickyTracking is true by default,
  33191. // unless user says otherwise.
  33192. this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptions[this.type] &&
  33193. userPlotOptions[this.type].stickyTracking, userPlotOptions.series && userPlotOptions.series.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
  33194. true :
  33195. options.stickyTracking));
  33196. // Delete marker object if not allowed (#1125)
  33197. if (typeOptions.marker === null) {
  33198. delete options.marker;
  33199. }
  33200. // Handle color zones
  33201. this.zoneAxis = options.zoneAxis;
  33202. zones = this.zones = (options.zones || []).slice();
  33203. if ((options.negativeColor || options.negativeFillColor) &&
  33204. !options.zones) {
  33205. zone = {
  33206. value: options[this.zoneAxis + 'Threshold'] ||
  33207. options.threshold ||
  33208. 0,
  33209. className: 'highcharts-negative'
  33210. };
  33211. if (!styledMode) {
  33212. zone.color = options.negativeColor;
  33213. zone.fillColor = options.negativeFillColor;
  33214. }
  33215. zones.push(zone);
  33216. }
  33217. if (zones.length) { // Push one extra zone for the rest
  33218. if (defined(zones[zones.length - 1].value)) {
  33219. zones.push(styledMode ? {} : {
  33220. color: this.color,
  33221. fillColor: this.fillColor
  33222. });
  33223. }
  33224. }
  33225. fireEvent(this, 'afterSetOptions', { options: options });
  33226. return options;
  33227. },
  33228. /**
  33229. * Return series name in "Series {Number}" format or the one defined by
  33230. * a user. This method can be simply overridden as series name format
  33231. * can vary (e.g. technical indicators).
  33232. *
  33233. * @function Highcharts.Series#getName
  33234. * @return {string}
  33235. * The series name.
  33236. */
  33237. getName: function () {
  33238. // #4119
  33239. return pick(this.options.name, 'Series ' + (this.index + 1));
  33240. },
  33241. /**
  33242. * @private
  33243. * @function Highcharts.Series#getCyclic
  33244. * @param {string} prop
  33245. * @param {*} [value]
  33246. * @param {Highcharts.Dictionary<any>} [defaults]
  33247. * @return {void}
  33248. */
  33249. getCyclic: function (prop, value, defaults) {
  33250. var i, chart = this.chart, userOptions = this.userOptions, indexName = prop + 'Index', counterName = prop + 'Counter', len = defaults ? defaults.length : pick(chart.options.chart[prop + 'Count'], chart[prop + 'Count']), setting;
  33251. if (!value) {
  33252. // Pick up either the colorIndex option, or the _colorIndex
  33253. // after Series.update()
  33254. setting = pick(userOptions[indexName], userOptions['_' + indexName]);
  33255. if (defined(setting)) { // after Series.update()
  33256. i = setting;
  33257. }
  33258. else {
  33259. // #6138
  33260. if (!chart.series.length) {
  33261. chart[counterName] = 0;
  33262. }
  33263. userOptions['_' + indexName] = i =
  33264. chart[counterName] % len;
  33265. chart[counterName] += 1;
  33266. }
  33267. if (defaults) {
  33268. value = defaults[i];
  33269. }
  33270. }
  33271. // Set the colorIndex
  33272. if (typeof i !== 'undefined') {
  33273. this[indexName] = i;
  33274. }
  33275. this[prop] = value;
  33276. },
  33277. /**
  33278. * Get the series' color based on either the options or pulled from
  33279. * global options.
  33280. *
  33281. * @private
  33282. * @function Highcharts.Series#getColor
  33283. * @return {void}
  33284. */
  33285. getColor: function () {
  33286. if (this.chart.styledMode) {
  33287. this.getCyclic('color');
  33288. }
  33289. else if (this.options.colorByPoint) {
  33290. // #4359, selected slice got series.color even when colorByPoint
  33291. // was set.
  33292. this.options.color = null;
  33293. }
  33294. else {
  33295. this.getCyclic('color', this.options.color ||
  33296. defaultOptions.plotOptions[this.type].color, this.chart.options.colors);
  33297. }
  33298. },
  33299. /**
  33300. * Get all points' instances created for this series.
  33301. *
  33302. * @private
  33303. * @function Highcharts.Series#getPointsCollection
  33304. * @return {Array<Highcharts.Point>}
  33305. */
  33306. getPointsCollection: function () {
  33307. return (this.hasGroupedData ? this.points : this.data) || [];
  33308. },
  33309. /**
  33310. * Get the series' symbol based on either the options or pulled from
  33311. * global options.
  33312. *
  33313. * @private
  33314. * @function Highcharts.Series#getSymbol
  33315. * @return {void}
  33316. */
  33317. getSymbol: function () {
  33318. var seriesMarkerOption = this.options.marker;
  33319. this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
  33320. },
  33321. /**
  33322. * Finds the index of an existing point that matches the given point
  33323. * options.
  33324. *
  33325. * @private
  33326. * @function Highcharts.Series#findPointIndex
  33327. * @param {Highcharts.PointOptionsObject} optionsObject
  33328. * The options of the point.
  33329. * @param {number} fromIndex
  33330. * The index to start searching from, used for optimizing
  33331. * series with required sorting.
  33332. * @returns {number|undefined}
  33333. * Returns the index of a matching point, or undefined if no
  33334. * match is found.
  33335. */
  33336. findPointIndex: function (optionsObject, fromIndex) {
  33337. var id = optionsObject.id,
  33338. x = optionsObject.x,
  33339. oldData = this.points,
  33340. matchingPoint,
  33341. matchedById,
  33342. pointIndex,
  33343. matchKey,
  33344. dataSorting = this.options.dataSorting;
  33345. if (id) {
  33346. matchingPoint = this.chart.get(id);
  33347. }
  33348. else if (this.linkedParent || this.enabledDataSorting) {
  33349. matchKey = (dataSorting && dataSorting.matchByName) ?
  33350. 'name' : 'index';
  33351. matchingPoint = find(oldData, function (oldPoint) {
  33352. return !oldPoint.touched && oldPoint[matchKey] ===
  33353. optionsObject[matchKey];
  33354. });
  33355. // Add unmatched point as a new point
  33356. if (!matchingPoint) {
  33357. return void 0;
  33358. }
  33359. }
  33360. if (matchingPoint) {
  33361. pointIndex = matchingPoint && matchingPoint.index;
  33362. if (typeof pointIndex !== 'undefined') {
  33363. matchedById = true;
  33364. }
  33365. }
  33366. // Search for the same X in the existing data set
  33367. if (typeof pointIndex === 'undefined' && isNumber(x)) {
  33368. pointIndex = this.xData.indexOf(x, fromIndex);
  33369. }
  33370. // Reduce pointIndex if data is cropped
  33371. if (pointIndex !== -1 &&
  33372. typeof pointIndex !== 'undefined' &&
  33373. this.cropped) {
  33374. pointIndex = (pointIndex >= this.cropStart) ?
  33375. pointIndex - this.cropStart : pointIndex;
  33376. }
  33377. if (!matchedById &&
  33378. oldData[pointIndex] && oldData[pointIndex].touched) {
  33379. pointIndex = void 0;
  33380. }
  33381. return pointIndex;
  33382. },
  33383. /**
  33384. * @private
  33385. * @borrows LegendSymbolMixin.drawLineMarker as Highcharts.Series#drawLegendSymbol
  33386. */
  33387. drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
  33388. /**
  33389. * Internal function called from setData. If the point count is the same
  33390. * as is was, or if there are overlapping X values, just run
  33391. * Point.update which is cheaper, allows animation, and keeps references
  33392. * to points. This also allows adding or removing points if the X-es
  33393. * don't match.
  33394. *
  33395. * @private
  33396. * @function Highcharts.Series#updateData
  33397. *
  33398. * @param {Array<Highcharts.PointOptionsType>} data
  33399. *
  33400. * @return {boolean}
  33401. */
  33402. updateData: function (data, animation) {
  33403. var options = this.options,
  33404. dataSorting = options.dataSorting,
  33405. oldData = this.points,
  33406. pointsToAdd = [],
  33407. hasUpdatedByKey,
  33408. i,
  33409. point,
  33410. lastIndex,
  33411. requireSorting = this.requireSorting,
  33412. equalLength = data.length === oldData.length,
  33413. succeeded = true;
  33414. this.xIncrement = null;
  33415. // Iterate the new data
  33416. data.forEach(function (pointOptions, i) {
  33417. var id,
  33418. x,
  33419. pointIndex,
  33420. optionsObject = (defined(pointOptions) &&
  33421. this.pointClass.prototype.optionsToObject.call({ series: this },
  33422. pointOptions)) || {};
  33423. // Get the x of the new data point
  33424. x = optionsObject.x;
  33425. id = optionsObject.id;
  33426. if (id || isNumber(x)) {
  33427. pointIndex = this.findPointIndex(optionsObject, lastIndex);
  33428. // Matching X not found
  33429. // or used already due to ununique x values (#8995),
  33430. // add point (but later)
  33431. if (pointIndex === -1 ||
  33432. typeof pointIndex === 'undefined') {
  33433. pointsToAdd.push(pointOptions);
  33434. // Matching X found, update
  33435. }
  33436. else if (oldData[pointIndex] &&
  33437. pointOptions !== options.data[pointIndex]) {
  33438. oldData[pointIndex].update(pointOptions, false, null, false);
  33439. // Mark it touched, below we will remove all points that
  33440. // are not touched.
  33441. oldData[pointIndex].touched = true;
  33442. // Speed optimize by only searching after last known
  33443. // index. Performs ~20% bettor on large data sets.
  33444. if (requireSorting) {
  33445. lastIndex = pointIndex + 1;
  33446. }
  33447. // Point exists, no changes, don't remove it
  33448. }
  33449. else if (oldData[pointIndex]) {
  33450. oldData[pointIndex].touched = true;
  33451. }
  33452. // If the length is equal and some of the nodes had a
  33453. // match in the same position, we don't want to remove
  33454. // non-matches.
  33455. if (!equalLength ||
  33456. i !== pointIndex ||
  33457. (dataSorting && dataSorting.enabled) ||
  33458. this.hasDerivedData) {
  33459. hasUpdatedByKey = true;
  33460. }
  33461. }
  33462. else {
  33463. // Gather all points that are not matched
  33464. pointsToAdd.push(pointOptions);
  33465. }
  33466. }, this);
  33467. // Remove points that don't exist in the updated data set
  33468. if (hasUpdatedByKey) {
  33469. i = oldData.length;
  33470. while (i--) {
  33471. point = oldData[i];
  33472. if (point && !point.touched && point.remove) {
  33473. point.remove(false, animation);
  33474. }
  33475. }
  33476. // If we did not find keys (ids or x-values), and the length is the
  33477. // same, update one-to-one
  33478. }
  33479. else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
  33480. data.forEach(function (point, i) {
  33481. // .update doesn't exist on a linked, hidden series (#3709)
  33482. // (#10187)
  33483. if (oldData[i].update && point !== oldData[i].y) {
  33484. oldData[i].update(point, false, null, false);
  33485. }
  33486. });
  33487. // Don't add new points since those configs are used above
  33488. pointsToAdd.length = 0;
  33489. // Did not succeed in updating data
  33490. }
  33491. else {
  33492. succeeded = false;
  33493. }
  33494. oldData.forEach(function (point) {
  33495. if (point) {
  33496. point.touched = false;
  33497. }
  33498. });
  33499. if (!succeeded) {
  33500. return false;
  33501. }
  33502. // Add new points
  33503. pointsToAdd.forEach(function (point) {
  33504. this.addPoint(point, false, null, null, false);
  33505. }, this);
  33506. if (this.xIncrement === null &&
  33507. this.xData &&
  33508. this.xData.length) {
  33509. this.xIncrement = arrayMax(this.xData);
  33510. this.autoIncrement();
  33511. }
  33512. return true;
  33513. },
  33514. /**
  33515. * Apply a new set of data to the series and optionally redraw it. The
  33516. * new data array is passed by reference (except in case of
  33517. * `updatePoints`), and may later be mutated when updating the chart
  33518. * data.
  33519. *
  33520. * Note the difference in behaviour when setting the same amount of
  33521. * points, or a different amount of points, as handled by the
  33522. * `updatePoints` parameter.
  33523. *
  33524. * @sample highcharts/members/series-setdata/
  33525. * Set new data from a button
  33526. * @sample highcharts/members/series-setdata-pie/
  33527. * Set data in a pie
  33528. * @sample stock/members/series-setdata/
  33529. * Set new data in Highstock
  33530. * @sample maps/members/series-setdata/
  33531. * Set new data in Highmaps
  33532. *
  33533. * @function Highcharts.Series#setData
  33534. *
  33535. * @param {Array<Highcharts.PointOptionsType>} data
  33536. * Takes an array of data in the same format as described under
  33537. * `series.{type}.data` for the given series type, for example a
  33538. * line series would take data in the form described under
  33539. * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
  33540. *
  33541. * @param {boolean} [redraw=true]
  33542. * Whether to redraw the chart after the series is altered. If
  33543. * doing more operations on the chart, it is a good idea to set
  33544. * redraw to false and call {@link Chart#redraw} after.
  33545. *
  33546. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33547. * When the updated data is the same length as the existing data,
  33548. * points will be updated by default, and animation visualizes
  33549. * how the points are changed. Set false to disable animation, or
  33550. * a configuration object to set duration or easing.
  33551. *
  33552. * @param {boolean} [updatePoints=true]
  33553. * When this is true, points will be updated instead of replaced
  33554. * whenever possible. This occurs a) when the updated data is the
  33555. * same length as the existing data, b) when points are matched
  33556. * by their id's, or c) when points can be matched by X values.
  33557. * This allows updating with animation and performs better. In
  33558. * this case, the original array is not passed by reference. Set
  33559. * `false` to prevent.
  33560. *
  33561. * @return {void}
  33562. */
  33563. setData: function (data, redraw, animation, updatePoints) {
  33564. var series = this,
  33565. oldData = series.points,
  33566. oldDataLength = (oldData && oldData.length) || 0,
  33567. dataLength,
  33568. options = series.options,
  33569. chart = series.chart,
  33570. dataSorting = options.dataSorting,
  33571. firstPoint = null,
  33572. xAxis = series.xAxis,
  33573. i,
  33574. turboThreshold = options.turboThreshold,
  33575. pt,
  33576. xData = this.xData,
  33577. yData = this.yData,
  33578. pointArrayMap = series.pointArrayMap,
  33579. valueCount = pointArrayMap && pointArrayMap.length,
  33580. keys = options.keys,
  33581. indexOfX = 0,
  33582. indexOfY = 1,
  33583. updatedData;
  33584. data = data || [];
  33585. dataLength = data.length;
  33586. redraw = pick(redraw, true);
  33587. if (dataSorting && dataSorting.enabled) {
  33588. data = this.sortData(data);
  33589. }
  33590. // First try to run Point.update which is cheaper, allows animation,
  33591. // and keeps references to points.
  33592. if (updatePoints !== false &&
  33593. dataLength &&
  33594. oldDataLength &&
  33595. !series.cropped &&
  33596. !series.hasGroupedData &&
  33597. series.visible &&
  33598. // Soft updating has no benefit in boost, and causes JS error
  33599. // (#8355)
  33600. !series.isSeriesBoosting) {
  33601. updatedData = this.updateData(data, animation);
  33602. }
  33603. if (!updatedData) {
  33604. // Reset properties
  33605. series.xIncrement = null;
  33606. series.colorCounter = 0; // for series with colorByPoint (#1547)
  33607. // Update parallel arrays
  33608. this.parallelArrays.forEach(function (key) {
  33609. series[key + 'Data'].length = 0;
  33610. });
  33611. // In turbo mode, only one- or twodimensional arrays of numbers
  33612. // are allowed. The first value is tested, and we assume that
  33613. // all the rest are defined the same way. Although the 'for'
  33614. // loops are similar, they are repeated inside each if-else
  33615. // conditional for max performance.
  33616. if (turboThreshold && dataLength > turboThreshold) {
  33617. firstPoint = series.getFirstValidPoint(data);
  33618. if (isNumber(firstPoint)) { // assume all points are numbers
  33619. for (i = 0; i < dataLength; i++) {
  33620. xData[i] = this.autoIncrement();
  33621. yData[i] = data[i];
  33622. }
  33623. // Assume all points are arrays when first point is
  33624. }
  33625. else if (isArray(firstPoint)) {
  33626. if (valueCount) { // [x, low, high] or [x, o, h, l, c]
  33627. for (i = 0; i < dataLength; i++) {
  33628. pt = data[i];
  33629. xData[i] = pt[0];
  33630. yData[i] =
  33631. pt.slice(1, valueCount + 1);
  33632. }
  33633. }
  33634. else { // [x, y]
  33635. if (keys) {
  33636. indexOfX = keys.indexOf('x');
  33637. indexOfY = keys.indexOf('y');
  33638. indexOfX = indexOfX >= 0 ? indexOfX : 0;
  33639. indexOfY = indexOfY >= 0 ? indexOfY : 1;
  33640. }
  33641. for (i = 0; i < dataLength; i++) {
  33642. pt = data[i];
  33643. xData[i] = pt[indexOfX];
  33644. yData[i] = pt[indexOfY];
  33645. }
  33646. }
  33647. }
  33648. else {
  33649. // Highcharts expects configs to be numbers or arrays in
  33650. // turbo mode
  33651. error(12, false, chart);
  33652. }
  33653. }
  33654. else {
  33655. for (i = 0; i < dataLength; i++) {
  33656. // stray commas in oldIE:
  33657. if (typeof data[i] !== 'undefined') {
  33658. pt = { series: series };
  33659. series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
  33660. series.updateParallelArrays(pt, i);
  33661. }
  33662. }
  33663. }
  33664. // Forgetting to cast strings to numbers is a common caveat when
  33665. // handling CSV or JSON
  33666. if (yData && isString(yData[0])) {
  33667. error(14, true, chart);
  33668. }
  33669. series.data = [];
  33670. series.options.data = series.userOptions.data = data;
  33671. // destroy old points
  33672. i = oldDataLength;
  33673. while (i--) {
  33674. if (oldData[i] && oldData[i].destroy) {
  33675. oldData[i].destroy();
  33676. }
  33677. }
  33678. // reset minRange (#878)
  33679. if (xAxis) {
  33680. xAxis.minRange = xAxis.userMinRange;
  33681. }
  33682. // redraw
  33683. series.isDirty = chart.isDirtyBox = true;
  33684. series.isDirtyData = !!oldData;
  33685. animation = false;
  33686. }
  33687. // Typically for pie series, points need to be processed and
  33688. // generated prior to rendering the legend
  33689. if (options.legendType === 'point') {
  33690. this.processData();
  33691. this.generatePoints();
  33692. }
  33693. if (redraw) {
  33694. chart.redraw(animation);
  33695. }
  33696. },
  33697. /**
  33698. * Internal function to sort series data
  33699. *
  33700. * @private
  33701. * @function Highcharts.Series#sortData
  33702. * @param {Array<Highcharts.PointOptionsType>} data
  33703. * Force data grouping.
  33704. * @return {Array<Highcharts.PointOptionsObject>}
  33705. */
  33706. sortData: function (data) {
  33707. var series = this,
  33708. options = series.options,
  33709. dataSorting = options.dataSorting,
  33710. sortKey = dataSorting.sortKey || 'y',
  33711. sortedData,
  33712. getPointOptionsObject = function (series,
  33713. pointOptions) {
  33714. return (defined(pointOptions) &&
  33715. series.pointClass.prototype.optionsToObject.call({
  33716. series: series
  33717. },
  33718. pointOptions)) || {};
  33719. };
  33720. data.forEach(function (pointOptions, i) {
  33721. data[i] = getPointOptionsObject(series, pointOptions);
  33722. data[i].index = i;
  33723. }, this);
  33724. // Sorting
  33725. sortedData = data.concat().sort(function (a, b) {
  33726. var aValue = getNestedProperty(sortKey,
  33727. a);
  33728. var bValue = getNestedProperty(sortKey,
  33729. b);
  33730. return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
  33731. });
  33732. // Set x value depending on the position in the array
  33733. sortedData.forEach(function (point, i) {
  33734. point.x = i;
  33735. }, this);
  33736. // Set the same x for linked series points if they don't have their
  33737. // own sorting
  33738. if (series.linkedSeries) {
  33739. series.linkedSeries.forEach(function (linkedSeries) {
  33740. var options = linkedSeries.options,
  33741. seriesData = options.data;
  33742. if ((!options.dataSorting ||
  33743. !options.dataSorting.enabled) &&
  33744. seriesData) {
  33745. seriesData.forEach(function (pointOptions, i) {
  33746. seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
  33747. if (data[i]) {
  33748. seriesData[i].x = data[i].x;
  33749. seriesData[i].index = i;
  33750. }
  33751. });
  33752. linkedSeries.setData(seriesData, false);
  33753. }
  33754. });
  33755. }
  33756. return data;
  33757. },
  33758. /**
  33759. * Internal function to process the data by cropping away unused data
  33760. * points if the series is longer than the crop threshold. This saves
  33761. * computing time for large series.
  33762. *
  33763. * @private
  33764. * @function Highcharts.Series#getProcessedData
  33765. * @param {boolean} [forceExtremesFromAll]
  33766. * Force getting extremes of a total series data range.
  33767. * @return {Highcharts.SeriesProcessedDataObject}
  33768. */
  33769. getProcessedData: function (forceExtremesFromAll) {
  33770. var series = this,
  33771. // copied during slice operation:
  33772. processedXData = series.xData,
  33773. processedYData = series.yData,
  33774. dataLength = processedXData.length,
  33775. croppedData,
  33776. cropStart = 0,
  33777. cropped,
  33778. distance,
  33779. closestPointRange,
  33780. xAxis = series.xAxis,
  33781. i, // loop variable
  33782. options = series.options,
  33783. cropThreshold = options.cropThreshold,
  33784. getExtremesFromAll = forceExtremesFromAll ||
  33785. series.getExtremesFromAll ||
  33786. options.getExtremesFromAll, // #4599
  33787. isCartesian = series.isCartesian,
  33788. xExtremes,
  33789. val2lin = xAxis && xAxis.val2lin,
  33790. isLog = !!(xAxis && xAxis.logarithmic),
  33791. throwOnUnsorted = series.requireSorting,
  33792. min,
  33793. max;
  33794. if (xAxis) {
  33795. // corrected for log axis (#3053)
  33796. xExtremes = xAxis.getExtremes();
  33797. min = xExtremes.min;
  33798. max = xExtremes.max;
  33799. }
  33800. // optionally filter out points outside the plot area
  33801. if (isCartesian &&
  33802. series.sorted &&
  33803. !getExtremesFromAll &&
  33804. (!cropThreshold ||
  33805. dataLength > cropThreshold ||
  33806. series.forceCrop)) {
  33807. // it's outside current extremes
  33808. if (processedXData[dataLength - 1] < min ||
  33809. processedXData[0] > max) {
  33810. processedXData = [];
  33811. processedYData = [];
  33812. // only crop if it's actually spilling out
  33813. }
  33814. else if (series.yData && (processedXData[0] < min ||
  33815. processedXData[dataLength - 1] > max)) {
  33816. croppedData = this.cropData(series.xData, series.yData, min, max);
  33817. processedXData = croppedData.xData;
  33818. processedYData = croppedData.yData;
  33819. cropStart = croppedData.start;
  33820. cropped = true;
  33821. }
  33822. }
  33823. // Find the closest distance between processed points
  33824. i = processedXData.length || 1;
  33825. while (--i) {
  33826. distance = (isLog ?
  33827. (val2lin(processedXData[i]) -
  33828. val2lin(processedXData[i - 1])) :
  33829. (processedXData[i] -
  33830. processedXData[i - 1]));
  33831. if (distance > 0 &&
  33832. (typeof closestPointRange === 'undefined' ||
  33833. distance < closestPointRange)) {
  33834. closestPointRange = distance;
  33835. // Unsorted data is not supported by the line tooltip, as well
  33836. // as data grouping and navigation in Stock charts (#725) and
  33837. // width calculation of columns (#1900)
  33838. }
  33839. else if (distance < 0 && throwOnUnsorted) {
  33840. error(15, false, series.chart);
  33841. throwOnUnsorted = false; // Only once
  33842. }
  33843. }
  33844. return {
  33845. xData: processedXData,
  33846. yData: processedYData,
  33847. cropped: cropped,
  33848. cropStart: cropStart,
  33849. closestPointRange: closestPointRange
  33850. };
  33851. },
  33852. /**
  33853. * Internal function to apply processed data.
  33854. * In Highstock, this function is extended to provide data grouping.
  33855. *
  33856. * @private
  33857. * @function Highcharts.Series#processData
  33858. * @param {boolean} [force]
  33859. * Force data grouping.
  33860. * @return {boolean|undefined}
  33861. */
  33862. processData: function (force) {
  33863. var series = this,
  33864. xAxis = series.xAxis,
  33865. processedData;
  33866. // If the series data or axes haven't changed, don't go through
  33867. // this. Return false to pass the message on to override methods
  33868. // like in data grouping.
  33869. if (series.isCartesian &&
  33870. !series.isDirty &&
  33871. !xAxis.isDirty &&
  33872. !series.yAxis.isDirty &&
  33873. !force) {
  33874. return false;
  33875. }
  33876. processedData = series.getProcessedData();
  33877. // Record the properties
  33878. series.cropped = processedData.cropped; // undefined or true
  33879. series.cropStart = processedData.cropStart;
  33880. series.processedXData = processedData.xData;
  33881. series.processedYData = processedData.yData;
  33882. series.closestPointRange =
  33883. series.basePointRange = processedData.closestPointRange;
  33884. },
  33885. /**
  33886. * Iterate over xData and crop values between min and max. Returns
  33887. * object containing crop start/end cropped xData with corresponding
  33888. * part of yData, dataMin and dataMax within the cropped range.
  33889. *
  33890. * @private
  33891. * @function Highcharts.Series#cropData
  33892. * @param {Array<number>} xData
  33893. * @param {Array<number>} yData
  33894. * @param {number} min
  33895. * @param {number} max
  33896. * @param {number} [cropShoulder]
  33897. * @return {Highcharts.SeriesCropDataObject}
  33898. */
  33899. cropData: function (xData, yData, min, max, cropShoulder) {
  33900. var dataLength = xData.length,
  33901. cropStart = 0,
  33902. cropEnd = dataLength,
  33903. i,
  33904. j;
  33905. // line-type series need one point outside
  33906. cropShoulder = pick(cropShoulder, this.cropShoulder);
  33907. // iterate up to find slice start
  33908. for (i = 0; i < dataLength; i++) {
  33909. if (xData[i] >= min) {
  33910. cropStart = Math.max(0, i - cropShoulder);
  33911. break;
  33912. }
  33913. }
  33914. // proceed to find slice end
  33915. for (j = i; j < dataLength; j++) {
  33916. if (xData[j] > max) {
  33917. cropEnd = j + cropShoulder;
  33918. break;
  33919. }
  33920. }
  33921. return {
  33922. xData: xData.slice(cropStart, cropEnd),
  33923. yData: yData.slice(cropStart, cropEnd),
  33924. start: cropStart,
  33925. end: cropEnd
  33926. };
  33927. },
  33928. /**
  33929. * Generate the data point after the data has been processed by cropping
  33930. * away unused points and optionally grouped in Highcharts Stock.
  33931. *
  33932. * @private
  33933. * @function Highcharts.Series#generatePoints
  33934. */
  33935. generatePoints: function () {
  33936. var series = this,
  33937. options = series.options,
  33938. dataOptions = options.data,
  33939. data = series.data,
  33940. dataLength,
  33941. processedXData = series.processedXData,
  33942. processedYData = series.processedYData,
  33943. PointClass = series.pointClass,
  33944. processedDataLength = processedXData.length,
  33945. cropStart = series.cropStart || 0,
  33946. cursor,
  33947. hasGroupedData = series.hasGroupedData,
  33948. keys = options.keys,
  33949. point,
  33950. points = [],
  33951. i;
  33952. if (!data && !hasGroupedData) {
  33953. var arr = [];
  33954. arr.length = dataOptions.length;
  33955. data = series.data = arr;
  33956. }
  33957. if (keys && hasGroupedData) {
  33958. // grouped data has already applied keys (#6590)
  33959. series.options.keys = false;
  33960. }
  33961. for (i = 0; i < processedDataLength; i++) {
  33962. cursor = cropStart + i;
  33963. if (!hasGroupedData) {
  33964. point = data[cursor];
  33965. // #970:
  33966. if (!point &&
  33967. typeof dataOptions[cursor] !== 'undefined') {
  33968. data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
  33969. }
  33970. }
  33971. else {
  33972. // splat the y data in case of ohlc data array
  33973. point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
  33974. /**
  33975. * Highstock only. If a point object is created by data
  33976. * grouping, it doesn't reflect actual points in the raw
  33977. * data. In this case, the `dataGroup` property holds
  33978. * information that points back to the raw data.
  33979. *
  33980. * - `dataGroup.start` is the index of the first raw data
  33981. * point in the group.
  33982. *
  33983. * - `dataGroup.length` is the amount of points in the
  33984. * group.
  33985. *
  33986. * @product highstock
  33987. *
  33988. * @name Highcharts.Point#dataGroup
  33989. * @type {Highcharts.DataGroupingInfoObject|undefined}
  33990. */
  33991. point.dataGroup = series.groupMap[i];
  33992. if (point.dataGroup.options) {
  33993. point.options = point.dataGroup.options;
  33994. extend(point, point.dataGroup.options);
  33995. // Collision of props and options (#9770)
  33996. delete point.dataLabels;
  33997. }
  33998. }
  33999. if (point) { // #6279
  34000. /**
  34001. * Contains the point's index in the `Series.points` array.
  34002. *
  34003. * @name Highcharts.Point#index
  34004. * @type {number}
  34005. * @readonly
  34006. */
  34007. point.index = cursor; // For faster access in Point.update
  34008. points[i] = point;
  34009. }
  34010. }
  34011. // restore keys options (#6590)
  34012. series.options.keys = keys;
  34013. // Hide cropped-away points - this only runs when the number of
  34014. // points is above cropThreshold, or when swithching view from
  34015. // non-grouped data to grouped data (#637)
  34016. if (data &&
  34017. (processedDataLength !== (dataLength = data.length) ||
  34018. hasGroupedData)) {
  34019. for (i = 0; i < dataLength; i++) {
  34020. // when has grouped data, clear all points
  34021. if (i === cropStart && !hasGroupedData) {
  34022. i += processedDataLength;
  34023. }
  34024. if (data[i]) {
  34025. data[i].destroyElements();
  34026. data[i].plotX = void 0; // #1003
  34027. }
  34028. }
  34029. }
  34030. /**
  34031. * Read only. An array containing those values converted to points.
  34032. * In case the series data length exceeds the `cropThreshold`, or if
  34033. * the data is grouped, `series.data` doesn't contain all the
  34034. * points. Also, in case a series is hidden, the `data` array may be
  34035. * empty. To access raw values, `series.options.data` will always be
  34036. * up to date. `Series.data` only contains the points that have been
  34037. * created on demand. To modify the data, use
  34038. * {@link Highcharts.Series#setData} or
  34039. * {@link Highcharts.Point#update}.
  34040. *
  34041. * @see Series.points
  34042. *
  34043. * @name Highcharts.Series#data
  34044. * @type {Array<Highcharts.Point>}
  34045. */
  34046. series.data = data;
  34047. /**
  34048. * An array containing all currently visible point objects. In case
  34049. * of cropping, the cropped-away points are not part of this array.
  34050. * The `series.points` array starts at `series.cropStart` compared
  34051. * to `series.data` and `series.options.data`. If however the series
  34052. * data is grouped, these can't be correlated one to one. To modify
  34053. * the data, use {@link Highcharts.Series#setData} or
  34054. * {@link Highcharts.Point#update}.
  34055. *
  34056. * @name Highcharts.Series#points
  34057. * @type {Array<Highcharts.Point>}
  34058. */
  34059. series.points = points;
  34060. fireEvent(this, 'afterGeneratePoints');
  34061. },
  34062. /**
  34063. * Get current X extremes for the visible data.
  34064. *
  34065. * @private
  34066. * @function Highcharts.Series#getXExtremes
  34067. *
  34068. * @param {Array<number>} xData
  34069. * The data to inspect. Defaults to the current data within the
  34070. * visible range.
  34071. * @return {Highcharts.RangeObject}
  34072. */
  34073. getXExtremes: function (xData) {
  34074. return {
  34075. min: arrayMin(xData),
  34076. max: arrayMax(xData)
  34077. };
  34078. },
  34079. /**
  34080. * Calculate Y extremes for the visible data. The result is returned
  34081. * as an object with `dataMin` and `dataMax` properties.
  34082. *
  34083. * @private
  34084. * @function Highcharts.Series#getExtremes
  34085. * @param {Array<number>} [yData]
  34086. * The data to inspect. Defaults to the current data within the
  34087. * visible range.
  34088. * @param {boolean} [forceExtremesFromAll]
  34089. * Force getting extremes of a total series data range.
  34090. * @return {Highcharts.DataExtremesObject}
  34091. */
  34092. getExtremes: function (yData, forceExtremesFromAll) {
  34093. var xAxis = this.xAxis,
  34094. yAxis = this.yAxis,
  34095. xData = this.processedXData || this.xData,
  34096. yDataLength,
  34097. activeYData = [],
  34098. activeCounter = 0,
  34099. // #2117, need to compensate for log X axis
  34100. xExtremes,
  34101. xMin = 0,
  34102. xMax = 0,
  34103. validValue,
  34104. withinRange,
  34105. // Handle X outside the viewed area. This does not work with
  34106. // non-sorted data like scatter (#7639).
  34107. shoulder = this.requireSorting ? this.cropShoulder : 0,
  34108. positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false,
  34109. x,
  34110. y,
  34111. i,
  34112. j;
  34113. yData = yData || this.stackedYData || this.processedYData || [];
  34114. yDataLength = yData.length;
  34115. if (xAxis) {
  34116. xExtremes = xAxis.getExtremes();
  34117. xMin = xExtremes.min;
  34118. xMax = xExtremes.max;
  34119. }
  34120. for (i = 0; i < yDataLength; i++) {
  34121. x = xData[i];
  34122. y = yData[i];
  34123. // For points within the visible range, including the first
  34124. // point outside the visible range (#7061), consider y extremes.
  34125. validValue = ((isNumber(y) || isArray(y)) &&
  34126. ((y.length || y > 0) || !positiveValuesOnly));
  34127. withinRange = (forceExtremesFromAll ||
  34128. this.getExtremesFromAll ||
  34129. this.options.getExtremesFromAll ||
  34130. this.cropped ||
  34131. !xAxis || // for colorAxis support
  34132. ((xData[i + shoulder] || x) >= xMin &&
  34133. (xData[i - shoulder] || x) <= xMax));
  34134. if (validValue && withinRange) {
  34135. j = y.length;
  34136. if (j) { // array, like ohlc or range data
  34137. while (j--) {
  34138. if (isNumber(y[j])) { // #7380, #11513
  34139. activeYData[activeCounter++] = y[j];
  34140. }
  34141. }
  34142. }
  34143. else {
  34144. activeYData[activeCounter++] = y;
  34145. }
  34146. }
  34147. }
  34148. var dataExtremes = {
  34149. dataMin: arrayMin(activeYData),
  34150. dataMax: arrayMax(activeYData)
  34151. };
  34152. fireEvent(this, 'afterGetExtremes', { dataExtremes: dataExtremes });
  34153. return dataExtremes;
  34154. },
  34155. /**
  34156. * Set the current data extremes as `dataMin` and `dataMax` on the
  34157. * Series item. Use this only when the series properties should be
  34158. * updated.
  34159. *
  34160. * @private
  34161. * @function Highcharts.Series#applyExtremes
  34162. * @return {void}
  34163. */
  34164. applyExtremes: function () {
  34165. var dataExtremes = this.getExtremes();
  34166. /**
  34167. * Contains the minimum value of the series' data point. Some series
  34168. * types like `networkgraph` do not support this property as they
  34169. * lack a `y`-value.
  34170. * @name Highcharts.Series#dataMin
  34171. * @type {number|undefined}
  34172. * @readonly
  34173. */
  34174. this.dataMin = dataExtremes.dataMin;
  34175. /**
  34176. * Contains the maximum value of the series' data point. Some series
  34177. * types like `networkgraph` do not support this property as they
  34178. * lack a `y`-value.
  34179. * @name Highcharts.Series#dataMax
  34180. * @type {number|undefined}
  34181. * @readonly
  34182. */
  34183. this.dataMax = dataExtremes.dataMax;
  34184. return dataExtremes;
  34185. },
  34186. /**
  34187. * Find and return the first non null point in the data
  34188. *
  34189. * @private
  34190. * @function Highcharts.Series.getFirstValidPoint
  34191. * @param {Array<Highcharts.PointOptionsType>} data
  34192. * Array of options for points
  34193. *
  34194. * @return {Highcharts.PointOptionsType}
  34195. */
  34196. getFirstValidPoint: function (data) {
  34197. var firstPoint = null,
  34198. dataLength = data.length,
  34199. i = 0;
  34200. while (firstPoint === null && i < dataLength) {
  34201. firstPoint = data[i];
  34202. i++;
  34203. }
  34204. return firstPoint;
  34205. },
  34206. /**
  34207. * Translate data points from raw data values to chart specific
  34208. * positioning data needed later in the `drawPoints` and `drawGraph`
  34209. * functions. This function can be overridden in plugins and custom
  34210. * series type implementations.
  34211. *
  34212. * @function Highcharts.Series#translate
  34213. * @return {void}
  34214. * @fires Highcharts.Series#events:translate
  34215. */
  34216. translate: function () {
  34217. if (!this.processedXData) { // hidden series
  34218. this.processData();
  34219. }
  34220. this.generatePoints();
  34221. var series = this,
  34222. options = series.options,
  34223. stacking = options.stacking,
  34224. xAxis = series.xAxis,
  34225. categories = xAxis.categories,
  34226. enabledDataSorting = series.enabledDataSorting,
  34227. yAxis = series.yAxis,
  34228. points = series.points,
  34229. dataLength = points.length,
  34230. hasModifyValue = !!series.modifyValue,
  34231. i,
  34232. pointPlacement = series.pointPlacementToXValue(), // #7860
  34233. dynamicallyPlaced = Boolean(pointPlacement),
  34234. threshold = options.threshold,
  34235. stackThreshold = options.startFromThreshold ? threshold : 0,
  34236. plotX,
  34237. lastPlotX,
  34238. stackIndicator,
  34239. zoneAxis = this.zoneAxis || 'y',
  34240. closestPointRangePx = Number.MAX_VALUE;
  34241. /**
  34242. * Plotted coordinates need to be within a limited range. Drawing
  34243. * too far outside the viewport causes various rendering issues
  34244. * (#3201, #3923, #7555).
  34245. * @private
  34246. */
  34247. function limitedRange(val) {
  34248. return clamp(val, -1e5, 1e5);
  34249. }
  34250. // Translate each point
  34251. for (i = 0; i < dataLength; i++) {
  34252. var point = points[i],
  34253. xValue = point.x,
  34254. yValue = point.y,
  34255. yBottom = point.low,
  34256. stack = stacking && yAxis.stacking && yAxis.stacking.stacks[(series.negStacks &&
  34257. yValue <
  34258. (stackThreshold ? 0 : threshold) ?
  34259. '-' :
  34260. '') + series.stackKey],
  34261. pointStack,
  34262. stackValues;
  34263. if (yAxis.positiveValuesOnly && !yAxis.validatePositiveValue(yValue) ||
  34264. xAxis.positiveValuesOnly && !xAxis.validatePositiveValue(xValue)) {
  34265. point.isNull = true;
  34266. }
  34267. // Get the plotX translation
  34268. point.plotX = plotX = correctFloat(// #5236
  34269. limitedRange(xAxis.translate(// #3923
  34270. xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags')) // #3923
  34271. );
  34272. // Calculate the bottom y value for stacked series
  34273. if (stacking &&
  34274. series.visible &&
  34275. stack &&
  34276. stack[xValue]) {
  34277. stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
  34278. if (!point.isNull) {
  34279. pointStack = stack[xValue];
  34280. stackValues =
  34281. pointStack.points[stackIndicator.key];
  34282. }
  34283. }
  34284. if (isArray(stackValues)) {
  34285. yBottom = stackValues[0];
  34286. yValue = stackValues[1];
  34287. if (yBottom === stackThreshold &&
  34288. stackIndicator.key ===
  34289. stack[xValue].base) {
  34290. yBottom = pick((isNumber(threshold) && threshold), yAxis.min);
  34291. }
  34292. // #1200, #1232
  34293. if (yAxis.positiveValuesOnly && yBottom <= 0) {
  34294. yBottom = null;
  34295. }
  34296. point.total = point.stackTotal = pointStack.total;
  34297. point.percentage =
  34298. pointStack.total &&
  34299. (point.y / pointStack.total * 100);
  34300. point.stackY = yValue;
  34301. // Place the stack label
  34302. // in case of variwide series (where widths of points are
  34303. // different in most cases), stack labels are positioned
  34304. // wrongly, so the call of the setOffset is omited here and
  34305. // labels are correctly positioned later, at the end of the
  34306. // variwide's translate function (#10962)
  34307. if (!series.irregularWidths) {
  34308. pointStack.setOffset(series.pointXOffset || 0, series.barW || 0);
  34309. }
  34310. }
  34311. // Set translated yBottom or remove it
  34312. point.yBottom = defined(yBottom) ?
  34313. limitedRange(yAxis.translate(yBottom, 0, 1, 0, 1)) :
  34314. null;
  34315. // general hook, used for Highstock compare mode
  34316. if (hasModifyValue) {
  34317. yValue = series.modifyValue(yValue, point);
  34318. }
  34319. // Set the the plotY value, reset it for redraws
  34320. // #3201
  34321. point.plotY = ((typeof yValue === 'number' && yValue !== Infinity) ?
  34322. limitedRange(yAxis.translate(yValue, 0, 1, 0, 1)) :
  34323. void 0);
  34324. point.isInside = this.isPointInside(point);
  34325. // Set client related positions for mouse tracking
  34326. point.clientX = dynamicallyPlaced ?
  34327. correctFloat(xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)) :
  34328. plotX; // #1514, #5383, #5518
  34329. // Negative points. For bubble charts, this means negative z
  34330. // values (#9728)
  34331. point.negative = point[zoneAxis] < (options[zoneAxis + 'Threshold'] ||
  34332. threshold ||
  34333. 0);
  34334. // some API data
  34335. point.category = (categories &&
  34336. typeof categories[point.x] !== 'undefined' ?
  34337. categories[point.x] :
  34338. point.x);
  34339. // Determine auto enabling of markers (#3635, #5099)
  34340. if (!point.isNull && point.visible !== false) {
  34341. if (typeof lastPlotX !== 'undefined') {
  34342. closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
  34343. }
  34344. lastPlotX = plotX;
  34345. }
  34346. // Find point zone
  34347. point.zone = (this.zones.length && point.getZone());
  34348. // Animate new points with data sorting
  34349. if (!point.graphic && series.group && enabledDataSorting) {
  34350. point.isNew = true;
  34351. }
  34352. }
  34353. series.closestPointRangePx = closestPointRangePx;
  34354. fireEvent(this, 'afterTranslate');
  34355. },
  34356. /**
  34357. * Return the series points with null points filtered out.
  34358. *
  34359. * @function Highcharts.Series#getValidPoints
  34360. *
  34361. * @param {Array<Highcharts.Point>} [points]
  34362. * The points to inspect, defaults to {@link Series.points}.
  34363. *
  34364. * @param {boolean} [insideOnly=false]
  34365. * Whether to inspect only the points that are inside the visible
  34366. * view.
  34367. *
  34368. * @param {boolean} [allowNull=false]
  34369. * Whether to allow null points to pass as valid points.
  34370. *
  34371. * @return {Array<Highcharts.Point>}
  34372. * The valid points.
  34373. */
  34374. getValidPoints: function (points, insideOnly, allowNull) {
  34375. var chart = this.chart;
  34376. // #3916, #5029, #5085
  34377. return (points || this.points || []).filter(function isValidPoint(point) {
  34378. if (insideOnly && !chart.isInsidePlot(point.plotX, point.plotY, chart.inverted)) {
  34379. return false;
  34380. }
  34381. return point.visible !== false &&
  34382. (allowNull || !point.isNull);
  34383. });
  34384. },
  34385. /**
  34386. * Get the clipping for the series. Could be called for a series to
  34387. * initiate animating the clip or to set the final clip (only width
  34388. * and x).
  34389. *
  34390. * @private
  34391. * @function Highcharts.Series#getClip
  34392. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  34393. * Initialize the animation.
  34394. * @param {boolean} [finalBox]
  34395. * Final size for the clip - end state for the animation.
  34396. * @return {Highcharts.Dictionary<number>}
  34397. */
  34398. getClipBox: function (animation, finalBox) {
  34399. var series = this,
  34400. options = series.options,
  34401. chart = series.chart,
  34402. inverted = chart.inverted,
  34403. xAxis = series.xAxis,
  34404. yAxis = xAxis && series.yAxis,
  34405. clipBox,
  34406. scrollablePlotAreaOptions = chart.options.chart.scrollablePlotArea || {};
  34407. if (animation && options.clip === false && yAxis) {
  34408. // support for not clipped series animation (#10450)
  34409. clipBox = inverted ? {
  34410. y: -chart.chartWidth + yAxis.len + yAxis.pos,
  34411. height: chart.chartWidth,
  34412. width: chart.chartHeight,
  34413. x: -chart.chartHeight + xAxis.len + xAxis.pos
  34414. } : {
  34415. y: -yAxis.pos,
  34416. height: chart.chartHeight,
  34417. width: chart.chartWidth,
  34418. x: -xAxis.pos
  34419. };
  34420. // x and width will be changed later when setting for animation
  34421. // initial state in Series.setClip
  34422. }
  34423. else {
  34424. clipBox = series.clipBox || chart.clipBox;
  34425. if (finalBox) {
  34426. clipBox.width = chart.plotSizeX;
  34427. clipBox.x = (chart.scrollablePixelsX || 0) *
  34428. (scrollablePlotAreaOptions.scrollPositionX || 0);
  34429. }
  34430. }
  34431. return !finalBox ? clipBox : {
  34432. width: clipBox.width,
  34433. x: clipBox.x
  34434. };
  34435. },
  34436. /**
  34437. * Set the clipping for the series. For animated series it is called
  34438. * twice, first to initiate animating the clip then the second time
  34439. * without the animation to set the final clip.
  34440. *
  34441. * @private
  34442. * @function Highcharts.Series#setClip
  34443. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  34444. */
  34445. setClip: function (animation) {
  34446. var chart = this.chart, options = this.options, renderer = chart.renderer, inverted = chart.inverted, seriesClipBox = this.clipBox, clipBox = this.getClipBox(animation), sharedClipKey = this.sharedClipKey ||
  34447. [
  34448. '_sharedClip',
  34449. animation && animation.duration,
  34450. animation && animation.easing,
  34451. clipBox.height,
  34452. options.xAxis,
  34453. options.yAxis
  34454. ].join(','), // #4526
  34455. clipRect = chart[sharedClipKey], markerClipRect = chart[sharedClipKey + 'm'];
  34456. if (animation) {
  34457. clipBox.width = 0;
  34458. if (inverted) {
  34459. clipBox.x = chart.plotHeight +
  34460. (options.clip !== false ? 0 : chart.plotTop);
  34461. }
  34462. }
  34463. // If a clipping rectangle with the same properties is currently
  34464. // present in the chart, use that.
  34465. if (!clipRect) {
  34466. // When animation is set, prepare the initial positions
  34467. if (animation) {
  34468. chart[sharedClipKey + 'm'] = markerClipRect =
  34469. renderer.clipRect(
  34470. // include the width of the first marker
  34471. inverted ? chart.plotSizeX + 99 : -99, inverted ? -chart.plotLeft : -chart.plotTop, 99, inverted ? chart.chartWidth : chart.chartHeight);
  34472. }
  34473. chart[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
  34474. // Create hashmap for series indexes
  34475. clipRect.count = { length: 0 };
  34476. // When the series is rendered again before starting animating, in
  34477. // compliance to a responsive rule
  34478. }
  34479. else if (!chart.hasLoaded) {
  34480. clipRect.attr(clipBox);
  34481. }
  34482. if (animation) {
  34483. if (!clipRect.count[this.index]) {
  34484. clipRect.count[this.index] = true;
  34485. clipRect.count.length += 1;
  34486. }
  34487. }
  34488. if (options.clip !== false || animation) {
  34489. this.group.clip(animation || seriesClipBox ? clipRect : chart.clipRect);
  34490. this.markerGroup.clip(markerClipRect);
  34491. this.sharedClipKey = sharedClipKey;
  34492. }
  34493. // Remove the shared clipping rectangle when all series are shown
  34494. if (!animation) {
  34495. if (clipRect.count[this.index]) {
  34496. delete clipRect.count[this.index];
  34497. clipRect.count.length -= 1;
  34498. }
  34499. if (clipRect.count.length === 0 &&
  34500. sharedClipKey &&
  34501. chart[sharedClipKey]) {
  34502. if (!seriesClipBox) {
  34503. chart[sharedClipKey] =
  34504. chart[sharedClipKey].destroy();
  34505. }
  34506. if (chart[sharedClipKey + 'm']) {
  34507. chart[sharedClipKey + 'm'] =
  34508. chart[sharedClipKey + 'm'].destroy();
  34509. }
  34510. }
  34511. }
  34512. },
  34513. /**
  34514. * Animate in the series. Called internally twice. First with the `init`
  34515. * parameter set to true, which sets up the initial state of the
  34516. * animation. Then when ready, it is called with the `init` parameter
  34517. * undefined, in order to perform the actual animation. After the
  34518. * second run, the function is removed.
  34519. *
  34520. * @function Highcharts.Series#animate
  34521. *
  34522. * @param {boolean} [init]
  34523. * Initialize the animation.
  34524. */
  34525. animate: function (init) {
  34526. var series = this,
  34527. chart = series.chart,
  34528. animation = animObject(series.options.animation),
  34529. clipRect,
  34530. sharedClipKey,
  34531. finalBox;
  34532. // Initialize the animation. Set up the clipping rectangle.
  34533. if (!chart.hasRendered) {
  34534. if (init) {
  34535. series.setClip(animation);
  34536. // Run the animation
  34537. }
  34538. else {
  34539. sharedClipKey = this.sharedClipKey;
  34540. clipRect = chart[sharedClipKey];
  34541. finalBox = series.getClipBox(animation, true);
  34542. if (clipRect) {
  34543. clipRect.animate(finalBox, animation);
  34544. }
  34545. if (chart[sharedClipKey + 'm']) {
  34546. chart[sharedClipKey + 'm'].animate({
  34547. width: finalBox.width + 99,
  34548. x: finalBox.x - (chart.inverted ? 0 : 99)
  34549. }, animation);
  34550. }
  34551. }
  34552. }
  34553. },
  34554. /**
  34555. * This runs after animation to land on the final plot clipping.
  34556. *
  34557. * @private
  34558. * @function Highcharts.Series#afterAnimate
  34559. * @fires Highcharts.Series#event:afterAnimate
  34560. */
  34561. afterAnimate: function () {
  34562. this.setClip();
  34563. fireEvent(this, 'afterAnimate');
  34564. this.finishedAnimating = true;
  34565. },
  34566. /**
  34567. * Draw the markers for line-like series types, and columns or other
  34568. * graphical representation for {@link Point} objects for other series
  34569. * types. The resulting element is typically stored as
  34570. * {@link Point.graphic}, and is created on the first call and updated
  34571. * and moved on subsequent calls.
  34572. *
  34573. * @function Highcharts.Series#drawPoints
  34574. */
  34575. drawPoints: function () {
  34576. var series = this,
  34577. points = series.points,
  34578. chart = series.chart,
  34579. i,
  34580. point,
  34581. graphic,
  34582. verb,
  34583. options = series.options,
  34584. seriesMarkerOptions = options.marker,
  34585. pointMarkerOptions,
  34586. hasPointMarker,
  34587. markerGroup = (series[series.specialGroup] ||
  34588. series.markerGroup),
  34589. xAxis = series.xAxis,
  34590. markerAttribs,
  34591. globallyEnabled = pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null,
  34592. // Use larger or equal as radius is null in bubbles (#6321)
  34593. series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
  34594. seriesMarkerOptions.radius));
  34595. if (seriesMarkerOptions.enabled !== false ||
  34596. series._hasPointMarkers) {
  34597. for (i = 0; i < points.length; i++) {
  34598. point = points[i];
  34599. graphic = point.graphic;
  34600. verb = graphic ? 'animate' : 'attr';
  34601. pointMarkerOptions = point.marker || {};
  34602. hasPointMarker = !!point.marker;
  34603. var shouldDrawMarker = ((globallyEnabled &&
  34604. typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
  34605. // only draw the point if y is defined
  34606. if (shouldDrawMarker) {
  34607. // Shortcuts
  34608. var symbol = pick(pointMarkerOptions.symbol,
  34609. series.symbol);
  34610. markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
  34611. // Set starting position for point sliding animation.
  34612. if (series.enabledDataSorting) {
  34613. point.startXPos = xAxis.reversed ?
  34614. -markerAttribs.width :
  34615. xAxis.width;
  34616. }
  34617. var isInside = point.isInside !== false;
  34618. if (graphic) { // update
  34619. // Since the marker group isn't clipped, each
  34620. // individual marker must be toggled
  34621. graphic[isInside ? 'show' : 'hide'](isInside)
  34622. .animate(markerAttribs);
  34623. }
  34624. else if (isInside &&
  34625. (markerAttribs.width > 0 || point.hasImage)) {
  34626. /**
  34627. * The graphic representation of the point.
  34628. * Typically this is a simple shape, like a `rect`
  34629. * for column charts or `path` for line markers, but
  34630. * for some complex series types like boxplot or 3D
  34631. * charts, the graphic may be a `g` element
  34632. * containing other shapes. The graphic is generated
  34633. * the first time {@link Series#drawPoints} runs,
  34634. * and updated and moved on subsequent runs.
  34635. *
  34636. * @name Point#graphic
  34637. * @type {SVGElement}
  34638. */
  34639. point.graphic = graphic = chart.renderer
  34640. .symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
  34641. pointMarkerOptions :
  34642. seriesMarkerOptions)
  34643. .add(markerGroup);
  34644. // Sliding animation for new points
  34645. if (series.enabledDataSorting &&
  34646. chart.hasRendered) {
  34647. graphic.attr({
  34648. x: point.startXPos
  34649. });
  34650. verb = 'animate';
  34651. }
  34652. }
  34653. if (graphic && verb === 'animate') { // update
  34654. // Since the marker group isn't clipped, each
  34655. // individual marker must be toggled
  34656. graphic[isInside ? 'show' : 'hide'](isInside)
  34657. .animate(markerAttribs);
  34658. }
  34659. // Presentational attributes
  34660. if (graphic && !chart.styledMode) {
  34661. graphic[verb](series.pointAttribs(point, (point.selected && 'select')));
  34662. }
  34663. if (graphic) {
  34664. graphic.addClass(point.getClassName(), true);
  34665. }
  34666. }
  34667. else if (graphic) {
  34668. point.graphic = graphic.destroy(); // #1269
  34669. }
  34670. }
  34671. }
  34672. },
  34673. /**
  34674. * Get non-presentational attributes for a point. Used internally for
  34675. * both styled mode and classic. Can be overridden for different series
  34676. * types.
  34677. *
  34678. * @see Series#pointAttribs
  34679. *
  34680. * @function Highcharts.Series#markerAttribs
  34681. *
  34682. * @param {Highcharts.Point} point
  34683. * The Point to inspect.
  34684. *
  34685. * @param {string} [state]
  34686. * The state, can be either `hover`, `select` or undefined.
  34687. *
  34688. * @return {Highcharts.SVGAttributes}
  34689. * A hash containing those attributes that are not settable from
  34690. * CSS.
  34691. */
  34692. markerAttribs: function (point, state) {
  34693. var seriesOptions = this.options,
  34694. seriesMarkerOptions = seriesOptions.marker,
  34695. seriesStateOptions,
  34696. pointMarkerOptions = point.marker || {},
  34697. symbol = (pointMarkerOptions.symbol ||
  34698. seriesMarkerOptions.symbol),
  34699. pointStateOptions,
  34700. radius = pick(pointMarkerOptions.radius,
  34701. seriesMarkerOptions.radius),
  34702. attribs;
  34703. // Handle hover and select states
  34704. if (state) {
  34705. seriesStateOptions = seriesMarkerOptions.states[state];
  34706. pointStateOptions = pointMarkerOptions.states &&
  34707. pointMarkerOptions.states[state];
  34708. radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
  34709. 0));
  34710. }
  34711. point.hasImage = symbol && symbol.indexOf('url') === 0;
  34712. if (point.hasImage) {
  34713. radius = 0; // and subsequently width and height is not set
  34714. }
  34715. attribs = {
  34716. // Math.floor for #1843:
  34717. x: seriesOptions.crisp ?
  34718. Math.floor(point.plotX) - radius :
  34719. point.plotX - radius,
  34720. y: point.plotY - radius
  34721. };
  34722. if (radius) {
  34723. attribs.width = attribs.height = 2 * radius;
  34724. }
  34725. return attribs;
  34726. },
  34727. /**
  34728. * Internal function to get presentational attributes for each point.
  34729. * Unlike {@link Series#markerAttribs}, this function should return
  34730. * those attributes that can also be set in CSS. In styled mode,
  34731. * `pointAttribs` won't be called.
  34732. *
  34733. * @private
  34734. * @function Highcharts.Series#pointAttribs
  34735. *
  34736. * @param {Highcharts.Point} [point]
  34737. * The point instance to inspect.
  34738. *
  34739. * @param {string} [state]
  34740. * The point state, can be either `hover`, `select` or 'normal'.
  34741. * If undefined, normal state is assumed.
  34742. *
  34743. * @return {Highcharts.SVGAttributes}
  34744. * The presentational attributes to be set on the point.
  34745. */
  34746. pointAttribs: function (point, state) {
  34747. var seriesMarkerOptions = this.options.marker,
  34748. seriesStateOptions,
  34749. pointOptions = point && point.options,
  34750. pointMarkerOptions = ((pointOptions && pointOptions.marker) || {}),
  34751. pointStateOptions,
  34752. color = this.color,
  34753. pointColorOption = pointOptions && pointOptions.color,
  34754. pointColor = point && point.color,
  34755. strokeWidth = pick(pointMarkerOptions.lineWidth,
  34756. seriesMarkerOptions.lineWidth),
  34757. zoneColor = point && point.zone && point.zone.color,
  34758. fill,
  34759. stroke,
  34760. opacity = 1;
  34761. color = (pointColorOption ||
  34762. zoneColor ||
  34763. pointColor ||
  34764. color);
  34765. fill = (pointMarkerOptions.fillColor ||
  34766. seriesMarkerOptions.fillColor ||
  34767. color);
  34768. stroke = (pointMarkerOptions.lineColor ||
  34769. seriesMarkerOptions.lineColor ||
  34770. color);
  34771. // Handle hover and select states
  34772. state = state || 'normal';
  34773. if (state) {
  34774. seriesStateOptions = seriesMarkerOptions.states[state];
  34775. pointStateOptions = (pointMarkerOptions.states &&
  34776. pointMarkerOptions.states[state]) || {};
  34777. strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
  34778. fill = (pointStateOptions.fillColor ||
  34779. seriesStateOptions.fillColor ||
  34780. fill);
  34781. stroke = (pointStateOptions.lineColor ||
  34782. seriesStateOptions.lineColor ||
  34783. stroke);
  34784. opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
  34785. }
  34786. return {
  34787. 'stroke': stroke,
  34788. 'stroke-width': strokeWidth,
  34789. 'fill': fill,
  34790. 'opacity': opacity
  34791. };
  34792. },
  34793. /**
  34794. * Clear DOM objects and free up memory.
  34795. *
  34796. * @private
  34797. * @function Highcharts.Series#destroy
  34798. * @param {boolean} [keepEventsForUpdate]
  34799. * @return {void}
  34800. * @fires Highcharts.Series#event:destroy
  34801. */
  34802. destroy: function (keepEventsForUpdate) {
  34803. var series = this,
  34804. chart = series.chart,
  34805. issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent),
  34806. destroy,
  34807. i,
  34808. data = series.data || [],
  34809. point,
  34810. axis;
  34811. // add event hook
  34812. fireEvent(series, 'destroy');
  34813. // remove events
  34814. this.removeEvents(keepEventsForUpdate);
  34815. // erase from axes
  34816. (series.axisTypes || []).forEach(function (AXIS) {
  34817. axis = series[AXIS];
  34818. if (axis && axis.series) {
  34819. erase(axis.series, series);
  34820. axis.isDirty = axis.forceRedraw = true;
  34821. }
  34822. });
  34823. // remove legend items
  34824. if (series.legendItem) {
  34825. series.chart.legend.destroyItem(series);
  34826. }
  34827. // destroy all points with their elements
  34828. i = data.length;
  34829. while (i--) {
  34830. point = data[i];
  34831. if (point && point.destroy) {
  34832. point.destroy();
  34833. }
  34834. }
  34835. series.points = null;
  34836. // Clear the animation timeout if we are destroying the series
  34837. // during initial animation
  34838. U.clearTimeout(series.animationTimeout);
  34839. // Destroy all SVGElements associated to the series
  34840. objectEach(series, function (val, prop) {
  34841. // Survive provides a hook for not destroying
  34842. if (val instanceof SVGElement && !val.survive) {
  34843. // issue 134 workaround
  34844. destroy = issue134 && prop === 'group' ?
  34845. 'hide' :
  34846. 'destroy';
  34847. val[destroy]();
  34848. }
  34849. });
  34850. // remove from hoverSeries
  34851. if (chart.hoverSeries === series) {
  34852. chart.hoverSeries = null;
  34853. }
  34854. erase(chart.series, series);
  34855. chart.orderSeries();
  34856. // clear all members
  34857. objectEach(series, function (val, prop) {
  34858. if (!keepEventsForUpdate || prop !== 'hcEvents') {
  34859. delete series[prop];
  34860. }
  34861. });
  34862. },
  34863. /**
  34864. * Get the graph path.
  34865. *
  34866. * @private
  34867. * @function Highcharts.Series#getGraphPath
  34868. * @param {Array<Highcharts.Point>} points
  34869. * @param {boolean} [nullsAsZeroes]
  34870. * @param {boolean} [connectCliffs]
  34871. * @return {Highcharts.SVGPathArray}
  34872. */
  34873. getGraphPath: function (points, nullsAsZeroes, connectCliffs) {
  34874. var series = this,
  34875. options = series.options,
  34876. step = options.step,
  34877. reversed,
  34878. graphPath = [],
  34879. xMap = [],
  34880. gap;
  34881. points = points || series.points;
  34882. // Bottom of a stack is reversed
  34883. reversed = points.reversed;
  34884. if (reversed) {
  34885. points.reverse();
  34886. }
  34887. // Reverse the steps (#5004)
  34888. step = {
  34889. right: 1,
  34890. center: 2
  34891. }[step] || (step && 3);
  34892. if (step && reversed) {
  34893. step = 4 - step;
  34894. }
  34895. // Remove invalid points, especially in spline (#5015)
  34896. points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
  34897. // Build the line
  34898. points.forEach(function (point, i) {
  34899. var plotX = point.plotX,
  34900. plotY = point.plotY,
  34901. lastPoint = points[i - 1],
  34902. // the path to this point from the previous
  34903. pathToPoint;
  34904. if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
  34905. !connectCliffs) {
  34906. gap = true; // ... and continue
  34907. }
  34908. // Line series, nullsAsZeroes is not handled
  34909. if (point.isNull && !defined(nullsAsZeroes) && i > 0) {
  34910. gap = !options.connectNulls;
  34911. // Area series, nullsAsZeroes is set
  34912. }
  34913. else if (point.isNull && !nullsAsZeroes) {
  34914. gap = true;
  34915. }
  34916. else {
  34917. if (i === 0 || gap) {
  34918. pathToPoint = [[
  34919. 'M',
  34920. point.plotX,
  34921. point.plotY
  34922. ]];
  34923. // Generate the spline as defined in the SplineSeries object
  34924. }
  34925. else if (series.getPointSpline) {
  34926. pathToPoint = [series.getPointSpline(points, point, i)];
  34927. }
  34928. else if (step) {
  34929. if (step === 1) { // right
  34930. pathToPoint = [[
  34931. 'L',
  34932. lastPoint.plotX,
  34933. plotY
  34934. ]];
  34935. }
  34936. else if (step === 2) { // center
  34937. pathToPoint = [[
  34938. 'L',
  34939. (lastPoint.plotX + plotX) / 2,
  34940. lastPoint.plotY
  34941. ], [
  34942. 'L',
  34943. (lastPoint.plotX + plotX) / 2,
  34944. plotY
  34945. ]];
  34946. }
  34947. else {
  34948. pathToPoint = [[
  34949. 'L',
  34950. plotX,
  34951. lastPoint.plotY
  34952. ]];
  34953. }
  34954. pathToPoint.push([
  34955. 'L',
  34956. plotX,
  34957. plotY
  34958. ]);
  34959. }
  34960. else {
  34961. // normal line to next point
  34962. pathToPoint = [[
  34963. 'L',
  34964. plotX,
  34965. plotY
  34966. ]];
  34967. }
  34968. // Prepare for animation. When step is enabled, there are
  34969. // two path nodes for each x value.
  34970. xMap.push(point.x);
  34971. if (step) {
  34972. xMap.push(point.x);
  34973. if (step === 2) { // step = center (#8073)
  34974. xMap.push(point.x);
  34975. }
  34976. }
  34977. graphPath.push.apply(graphPath, pathToPoint);
  34978. gap = false;
  34979. }
  34980. });
  34981. graphPath.xMap = xMap;
  34982. series.graphPath = graphPath;
  34983. return graphPath;
  34984. },
  34985. /**
  34986. * Draw the graph. Called internally when rendering line-like series
  34987. * types. The first time it generates the `series.graph` item and
  34988. * optionally other series-wide items like `series.area` for area
  34989. * charts. On subsequent calls these items are updated with new
  34990. * positions and attributes.
  34991. *
  34992. * @function Highcharts.Series#drawGraph
  34993. */
  34994. drawGraph: function () {
  34995. var series = this,
  34996. options = this.options,
  34997. graphPath = (this.gappedPath || this.getGraphPath).call(this),
  34998. styledMode = this.chart.styledMode,
  34999. props = [[
  35000. 'graph',
  35001. 'highcharts-graph'
  35002. ]];
  35003. // Presentational properties
  35004. if (!styledMode) {
  35005. props[0].push((options.lineColor ||
  35006. this.color ||
  35007. '#cccccc' // when colorByPoint = true
  35008. ), options.dashStyle);
  35009. }
  35010. props = series.getZonesGraphs(props);
  35011. // Draw the graph
  35012. props.forEach(function (prop, i) {
  35013. var graphKey = prop[0],
  35014. graph = series[graphKey],
  35015. verb = graph ? 'animate' : 'attr',
  35016. attribs;
  35017. if (graph) {
  35018. graph.endX = series.preventGraphAnimation ?
  35019. null :
  35020. graphPath.xMap;
  35021. graph.animate({ d: graphPath });
  35022. }
  35023. else if (graphPath.length) { // #1487
  35024. /**
  35025. * SVG element of area-based charts. Can be used for styling
  35026. * purposes. If zones are configured, this element will be
  35027. * hidden and replaced by multiple zone areas, accessible
  35028. * via `series['zone-area-x']` (where x is a number,
  35029. * starting with 0).
  35030. *
  35031. * @name Highcharts.Series#area
  35032. * @type {Highcharts.SVGElement|undefined}
  35033. */
  35034. /**
  35035. * SVG element of line-based charts. Can be used for styling
  35036. * purposes. If zones are configured, this element will be
  35037. * hidden and replaced by multiple zone lines, accessible
  35038. * via `series['zone-graph-x']` (where x is a number,
  35039. * starting with 0).
  35040. *
  35041. * @name Highcharts.Series#graph
  35042. * @type {Highcharts.SVGElement|undefined}
  35043. */
  35044. series[graphKey] = graph = series.chart.renderer
  35045. .path(graphPath)
  35046. .addClass(prop[1])
  35047. .attr({ zIndex: 1 }) // #1069
  35048. .add(series.group);
  35049. }
  35050. if (graph && !styledMode) {
  35051. attribs = {
  35052. 'stroke': prop[2],
  35053. 'stroke-width': options.lineWidth,
  35054. // Polygon series use filled graph
  35055. 'fill': (series.fillGraph && series.color) || 'none'
  35056. };
  35057. if (prop[3]) {
  35058. attribs.dashstyle = prop[3];
  35059. }
  35060. else if (options.linecap !== 'square') {
  35061. attribs['stroke-linecap'] =
  35062. attribs['stroke-linejoin'] = 'round';
  35063. }
  35064. graph[verb](attribs)
  35065. // Add shadow to normal series (0) or to first
  35066. // zone (1) #3932
  35067. .shadow((i < 2) && options.shadow);
  35068. }
  35069. // Helpers for animation
  35070. if (graph) {
  35071. graph.startX = graphPath.xMap;
  35072. graph.isArea = graphPath.isArea; // For arearange animation
  35073. }
  35074. });
  35075. },
  35076. /**
  35077. * Get zones properties for building graphs. Extendable by series with
  35078. * multiple lines within one series.
  35079. *
  35080. * @private
  35081. * @function Highcharts.Series#getZonesGraphs
  35082. *
  35083. * @param {Array<Array<string>>} props
  35084. *
  35085. * @return {Array<Array<string>>}
  35086. */
  35087. getZonesGraphs: function (props) {
  35088. // Add the zone properties if any
  35089. this.zones.forEach(function (zone, i) {
  35090. var propset = [
  35091. 'zone-graph-' + i,
  35092. 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
  35093. (zone.className || '')
  35094. ];
  35095. if (!this.chart.styledMode) {
  35096. propset.push((zone.color || this.color), (zone.dashStyle || this.options.dashStyle));
  35097. }
  35098. props.push(propset);
  35099. }, this);
  35100. return props;
  35101. },
  35102. /**
  35103. * Clip the graphs into zones for colors and styling.
  35104. *
  35105. * @private
  35106. * @function Highcharts.Series#applyZones
  35107. * @return {void}
  35108. */
  35109. applyZones: function () {
  35110. var series = this,
  35111. chart = this.chart,
  35112. renderer = chart.renderer,
  35113. zones = this.zones,
  35114. translatedFrom,
  35115. translatedTo,
  35116. clips = (this.clips || []),
  35117. clipAttr,
  35118. graph = this.graph,
  35119. area = this.area,
  35120. chartSizeMax = Math.max(chart.chartWidth,
  35121. chart.chartHeight),
  35122. axis = this[(this.zoneAxis || 'y') + 'Axis'],
  35123. extremes,
  35124. reversed,
  35125. inverted = chart.inverted,
  35126. horiz,
  35127. pxRange,
  35128. pxPosMin,
  35129. pxPosMax,
  35130. ignoreZones = false,
  35131. zoneArea,
  35132. zoneGraph;
  35133. if (zones.length &&
  35134. (graph || area) &&
  35135. axis &&
  35136. typeof axis.min !== 'undefined') {
  35137. reversed = axis.reversed;
  35138. horiz = axis.horiz;
  35139. // The use of the Color Threshold assumes there are no gaps
  35140. // so it is safe to hide the original graph and area
  35141. // unless it is not waterfall series, then use showLine property
  35142. // to set lines between columns to be visible (#7862)
  35143. if (graph && !this.showLine) {
  35144. graph.hide();
  35145. }
  35146. if (area) {
  35147. area.hide();
  35148. }
  35149. // Create the clips
  35150. extremes = axis.getExtremes();
  35151. zones.forEach(function (threshold, i) {
  35152. translatedFrom = reversed ?
  35153. (horiz ? chart.plotWidth : 0) :
  35154. (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
  35155. translatedFrom = clamp(pick(translatedTo, translatedFrom), 0, chartSizeMax);
  35156. translatedTo = clamp(Math.round(axis.toPixels(pick(threshold.value, extremes.max), true) || 0), 0, chartSizeMax);
  35157. if (ignoreZones) {
  35158. translatedFrom = translatedTo =
  35159. axis.toPixels(extremes.max);
  35160. }
  35161. pxRange = Math.abs(translatedFrom - translatedTo);
  35162. pxPosMin = Math.min(translatedFrom, translatedTo);
  35163. pxPosMax = Math.max(translatedFrom, translatedTo);
  35164. if (axis.isXAxis) {
  35165. clipAttr = {
  35166. x: inverted ? pxPosMax : pxPosMin,
  35167. y: 0,
  35168. width: pxRange,
  35169. height: chartSizeMax
  35170. };
  35171. if (!horiz) {
  35172. clipAttr.x = chart.plotHeight - clipAttr.x;
  35173. }
  35174. }
  35175. else {
  35176. clipAttr = {
  35177. x: 0,
  35178. y: inverted ? pxPosMax : pxPosMin,
  35179. width: chartSizeMax,
  35180. height: pxRange
  35181. };
  35182. if (horiz) {
  35183. clipAttr.y = chart.plotWidth - clipAttr.y;
  35184. }
  35185. }
  35186. // VML SUPPPORT
  35187. if (inverted && renderer.isVML) {
  35188. if (axis.isXAxis) {
  35189. clipAttr = {
  35190. x: 0,
  35191. y: reversed ? pxPosMin : pxPosMax,
  35192. height: clipAttr.width,
  35193. width: chart.chartWidth
  35194. };
  35195. }
  35196. else {
  35197. clipAttr = {
  35198. x: (clipAttr.y -
  35199. chart.plotLeft -
  35200. chart.spacingBox.x),
  35201. y: 0,
  35202. width: clipAttr.height,
  35203. height: chart.chartHeight
  35204. };
  35205. }
  35206. }
  35207. // END OF VML SUPPORT
  35208. if (clips[i]) {
  35209. clips[i].animate(clipAttr);
  35210. }
  35211. else {
  35212. clips[i] = renderer.clipRect(clipAttr);
  35213. }
  35214. // when no data, graph zone is not applied and after setData
  35215. // clip was ignored. As a result, it should be applied each
  35216. // time.
  35217. zoneArea = series['zone-area-' + i];
  35218. zoneGraph = series['zone-graph-' + i];
  35219. if (graph && zoneGraph) {
  35220. zoneGraph.clip(clips[i]);
  35221. }
  35222. if (area && zoneArea) {
  35223. zoneArea.clip(clips[i]);
  35224. }
  35225. // if this zone extends out of the axis, ignore the others
  35226. ignoreZones = threshold.value > extremes.max;
  35227. // Clear translatedTo for indicators
  35228. if (series.resetZones && translatedTo === 0) {
  35229. translatedTo = void 0;
  35230. }
  35231. });
  35232. this.clips = clips;
  35233. }
  35234. else if (series.visible) {
  35235. // If zones were removed, restore graph and area
  35236. if (graph) {
  35237. graph.show(true);
  35238. }
  35239. if (area) {
  35240. area.show(true);
  35241. }
  35242. }
  35243. },
  35244. /**
  35245. * Initialize and perform group inversion on series.group and
  35246. * series.markerGroup.
  35247. *
  35248. * @private
  35249. * @function Highcharts.Series#invertGroups
  35250. * @param {boolean} [inverted]
  35251. * @return {void}
  35252. */
  35253. invertGroups: function (inverted) {
  35254. var series = this,
  35255. chart = series.chart;
  35256. /**
  35257. * @private
  35258. */
  35259. function setInvert() {
  35260. ['group', 'markerGroup'].forEach(function (groupName) {
  35261. if (series[groupName]) {
  35262. // VML/HTML needs explicit attributes for flipping
  35263. if (chart.renderer.isVML) {
  35264. series[groupName].attr({
  35265. width: series.yAxis.len,
  35266. height: series.xAxis.len
  35267. });
  35268. }
  35269. series[groupName].width = series.yAxis.len;
  35270. series[groupName].height = series.xAxis.len;
  35271. // If inverted polar, don't invert series group
  35272. series[groupName].invert(series.isRadialSeries ? false : inverted);
  35273. }
  35274. });
  35275. }
  35276. // Pie, go away (#1736)
  35277. if (!series.xAxis) {
  35278. return;
  35279. }
  35280. // A fixed size is needed for inversion to work
  35281. series.eventsToUnbind.push(addEvent(chart, 'resize', setInvert));
  35282. // Do it now
  35283. setInvert();
  35284. // On subsequent render and redraw, just do setInvert without
  35285. // setting up events again
  35286. series.invertGroups = setInvert;
  35287. },
  35288. /**
  35289. * General abstraction for creating plot groups like series.group,
  35290. * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
  35291. * the group will only be adjusted to the updated plot size.
  35292. *
  35293. * @private
  35294. * @function Highcharts.Series#plotGroup
  35295. * @param {string} prop
  35296. * @param {string} name
  35297. * @param {string} visibility
  35298. * @param {number} [zIndex]
  35299. * @param {Highcharts.SVGElement} [parent]
  35300. * @return {Highcharts.SVGElement}
  35301. */
  35302. plotGroup: function (prop, name, visibility, zIndex, parent) {
  35303. var group = this[prop],
  35304. isNew = !group,
  35305. attrs = {
  35306. visibility: visibility,
  35307. zIndex: zIndex || 0.1 // IE8 and pointer logic use this
  35308. };
  35309. // Avoid setting undefined opacity, or in styled mode
  35310. if (typeof this.opacity !== 'undefined' &&
  35311. !this.chart.styledMode && this.state !== 'inactive' // #13719
  35312. ) {
  35313. attrs.opacity = this.opacity;
  35314. }
  35315. // Generate it on first call
  35316. if (isNew) {
  35317. this[prop] = group = this.chart.renderer
  35318. .g()
  35319. .add(parent);
  35320. }
  35321. // Add the class names, and replace existing ones as response to
  35322. // Series.update (#6660)
  35323. group.addClass(('highcharts-' + name +
  35324. ' highcharts-series-' + this.index +
  35325. ' highcharts-' + this.type + '-series ' +
  35326. (defined(this.colorIndex) ?
  35327. 'highcharts-color-' + this.colorIndex + ' ' :
  35328. '') +
  35329. (this.options.className || '') +
  35330. (group.hasClass('highcharts-tracker') ?
  35331. ' highcharts-tracker' :
  35332. '')), true);
  35333. // Place it on first and subsequent (redraw) calls
  35334. group.attr(attrs)[isNew ? 'attr' : 'animate'](this.getPlotBox());
  35335. return group;
  35336. },
  35337. /**
  35338. * Get the translation and scale for the plot area of this series.
  35339. *
  35340. * @function Highcharts.Series#getPlotBox
  35341. *
  35342. * @return {Highcharts.SeriesPlotBoxObject}
  35343. */
  35344. getPlotBox: function () {
  35345. var chart = this.chart,
  35346. xAxis = this.xAxis,
  35347. yAxis = this.yAxis;
  35348. // Swap axes for inverted (#2339)
  35349. if (chart.inverted) {
  35350. xAxis = yAxis;
  35351. yAxis = this.xAxis;
  35352. }
  35353. return {
  35354. translateX: xAxis ? xAxis.left : chart.plotLeft,
  35355. translateY: yAxis ? yAxis.top : chart.plotTop,
  35356. scaleX: 1,
  35357. scaleY: 1
  35358. };
  35359. },
  35360. /**
  35361. * Removes the event handlers attached previously with addEvents.
  35362. *
  35363. * @private
  35364. * @function Highcharts.Series#removeEvents
  35365. * @param {boolean} [keepEventsForUpdate]
  35366. * @return {void}
  35367. */
  35368. removeEvents: function (keepEventsForUpdate) {
  35369. var series = this;
  35370. if (!keepEventsForUpdate) {
  35371. // remove all events
  35372. removeEvent(series);
  35373. }
  35374. else if (series.eventsToUnbind.length) {
  35375. // remove only internal events for proper update
  35376. // #12355 - solves problem with multiple destroy events
  35377. series.eventsToUnbind.forEach(function (unbind) {
  35378. unbind();
  35379. });
  35380. series.eventsToUnbind.length = 0;
  35381. }
  35382. },
  35383. /**
  35384. * Render the graph and markers. Called internally when first rendering
  35385. * and later when redrawing the chart. This function can be extended in
  35386. * plugins, but normally shouldn't be called directly.
  35387. *
  35388. * @function Highcharts.Series#render
  35389. *
  35390. * @return {void}
  35391. *
  35392. * @fires Highcharts.Series#event:afterRender
  35393. */
  35394. render: function () {
  35395. var series = this,
  35396. chart = series.chart,
  35397. group,
  35398. options = series.options,
  35399. animOptions = animObject(options.animation),
  35400. // Animation doesn't work in IE8 quirks when the group div is
  35401. // hidden, and looks bad in other oldIE
  35402. animDuration = (!series.finishedAnimating &&
  35403. chart.renderer.isSVG &&
  35404. animOptions.duration),
  35405. visibility = series.visible ? 'inherit' : 'hidden', // #2597
  35406. zIndex = options.zIndex,
  35407. hasRendered = series.hasRendered,
  35408. chartSeriesGroup = chart.seriesGroup,
  35409. inverted = chart.inverted;
  35410. fireEvent(this, 'render');
  35411. // the group
  35412. group = series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
  35413. series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
  35414. // initiate the animation
  35415. if (animDuration && series.animate) {
  35416. series.animate(true);
  35417. }
  35418. // SVGRenderer needs to know this before drawing elements (#1089,
  35419. // #1795)
  35420. group.inverted = series.isCartesian || series.invertable ?
  35421. inverted : false;
  35422. // Draw the graph if any
  35423. if (series.drawGraph) {
  35424. series.drawGraph();
  35425. series.applyZones();
  35426. }
  35427. // Draw the points
  35428. if (series.visible) {
  35429. series.drawPoints();
  35430. }
  35431. /* series.points.forEach(function (point) {
  35432. if (point.redraw) {
  35433. point.redraw();
  35434. }
  35435. }); */
  35436. // Draw the data labels
  35437. if (series.drawDataLabels) {
  35438. series.drawDataLabels();
  35439. }
  35440. // In pie charts, slices are added to the DOM, but actual rendering
  35441. // is postponed until labels reserved their space
  35442. if (series.redrawPoints) {
  35443. series.redrawPoints();
  35444. }
  35445. // draw the mouse tracking area
  35446. if (series.drawTracker &&
  35447. series.options.enableMouseTracking !== false) {
  35448. series.drawTracker();
  35449. }
  35450. // Handle inverted series and tracker groups
  35451. series.invertGroups(inverted);
  35452. // Initial clipping, must be defined after inverting groups for VML.
  35453. // Applies to columns etc. (#3839).
  35454. if (options.clip !== false &&
  35455. !series.sharedClipKey &&
  35456. !hasRendered) {
  35457. group.clip(chart.clipRect);
  35458. }
  35459. // Run the animation
  35460. if (animDuration && series.animate) {
  35461. series.animate();
  35462. }
  35463. // Call the afterAnimate function on animation complete (but don't
  35464. // overwrite the animation.complete option which should be available
  35465. // to the user).
  35466. if (!hasRendered) {
  35467. // Additional time if defer is defined before afterAnimate
  35468. // will be triggered
  35469. if (animDuration && animOptions.defer) {
  35470. animDuration += animOptions.defer;
  35471. }
  35472. series.animationTimeout = syncTimeout(function () {
  35473. series.afterAnimate();
  35474. }, animDuration || 0);
  35475. }
  35476. // Means data is in accordance with what you see
  35477. series.isDirty = false;
  35478. // (See #322) series.isDirty = series.isDirtyData = false; // means
  35479. // data is in accordance with what you see
  35480. series.hasRendered = true;
  35481. fireEvent(series, 'afterRender');
  35482. },
  35483. /**
  35484. * Redraw the series. This function is called internally from
  35485. * `chart.redraw` and normally shouldn't be called directly.
  35486. *
  35487. * @private
  35488. * @function Highcharts.Series#redraw
  35489. * @return {void}
  35490. */
  35491. redraw: function () {
  35492. var series = this,
  35493. chart = series.chart,
  35494. // cache it here as it is set to false in render, but used after
  35495. wasDirty = series.isDirty || series.isDirtyData,
  35496. group = series.group,
  35497. xAxis = series.xAxis,
  35498. yAxis = series.yAxis;
  35499. // reposition on resize
  35500. if (group) {
  35501. if (chart.inverted) {
  35502. group.attr({
  35503. width: chart.plotWidth,
  35504. height: chart.plotHeight
  35505. });
  35506. }
  35507. group.animate({
  35508. translateX: pick(xAxis && xAxis.left, chart.plotLeft),
  35509. translateY: pick(yAxis && yAxis.top, chart.plotTop)
  35510. });
  35511. }
  35512. series.translate();
  35513. series.render();
  35514. if (wasDirty) { // #3868, #3945
  35515. delete this.kdTree;
  35516. }
  35517. },
  35518. kdAxisArray: ['clientX', 'plotY'],
  35519. /**
  35520. * @private
  35521. * @function Highcharts.Series#searchPoint
  35522. * @param {Highcharts.PointerEventObject} e
  35523. * @param {boolean} [compareX]
  35524. * @return {Highcharts.Point}
  35525. */
  35526. searchPoint: function (e, compareX) {
  35527. var series = this,
  35528. xAxis = series.xAxis,
  35529. yAxis = series.yAxis,
  35530. inverted = series.chart.inverted;
  35531. return this.searchKDTree({
  35532. clientX: inverted ?
  35533. xAxis.len - e.chartY + xAxis.pos :
  35534. e.chartX - xAxis.pos,
  35535. plotY: inverted ?
  35536. yAxis.len - e.chartX + yAxis.pos :
  35537. e.chartY - yAxis.pos
  35538. }, compareX, e);
  35539. },
  35540. /**
  35541. * Build the k-d-tree that is used by mouse and touch interaction to get
  35542. * the closest point. Line-like series typically have a one-dimensional
  35543. * tree where points are searched along the X axis, while scatter-like
  35544. * series typically search in two dimensions, X and Y.
  35545. *
  35546. * @private
  35547. * @function Highcharts.Series#buildKDTree
  35548. * @param {Highcharts.PointerEventObject} [e]
  35549. * @return {void}
  35550. */
  35551. buildKDTree: function (e) {
  35552. // Prevent multiple k-d-trees from being built simultaneously
  35553. // (#6235)
  35554. this.buildingKdTree = true;
  35555. var series = this,
  35556. dimensions = series.options.findNearestPointBy
  35557. .indexOf('y') > -1 ? 2 : 1;
  35558. /**
  35559. * Internal function
  35560. * @private
  35561. */
  35562. function _kdtree(points, depth, dimensions) {
  35563. var axis,
  35564. median,
  35565. length = points && points.length;
  35566. if (length) {
  35567. // alternate between the axis
  35568. axis = series.kdAxisArray[depth % dimensions];
  35569. // sort point array
  35570. points.sort(function (a, b) {
  35571. return a[axis] - b[axis];
  35572. });
  35573. median = Math.floor(length / 2);
  35574. // build and return nod
  35575. return {
  35576. point: points[median],
  35577. left: _kdtree(points.slice(0, median), depth + 1, dimensions),
  35578. right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
  35579. };
  35580. }
  35581. }
  35582. /**
  35583. * Start the recursive build process with a clone of the points
  35584. * array and null points filtered out. (#3873)
  35585. * @private
  35586. */
  35587. function startRecursive() {
  35588. series.kdTree = _kdtree(series.getValidPoints(null,
  35589. // For line-type series restrict to plot area, but
  35590. // column-type series not (#3916, #4511)
  35591. !series.directTouch), dimensions, dimensions);
  35592. series.buildingKdTree = false;
  35593. }
  35594. delete series.kdTree;
  35595. // For testing tooltips, don't build async. Also if touchstart, we
  35596. // may be dealing with click events on mobile, so don't delay
  35597. // (#6817).
  35598. syncTimeout(startRecursive, series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1);
  35599. },
  35600. /**
  35601. * @private
  35602. * @function Highcharts.Series#searchKDTree
  35603. * @param {Highcharts.KDPointSearchObject} point
  35604. * @param {boolean} [compareX]
  35605. * @param {Highcharts.PointerEventObject} [e]
  35606. * @return {Highcharts.Point|undefined}
  35607. */
  35608. searchKDTree: function (point, compareX, e) {
  35609. var series = this,
  35610. kdX = this.kdAxisArray[0],
  35611. kdY = this.kdAxisArray[1],
  35612. kdComparer = compareX ? 'distX' : 'dist',
  35613. kdDimensions = series.options.findNearestPointBy
  35614. .indexOf('y') > -1 ? 2 : 1;
  35615. /**
  35616. * Set the one and two dimensional distance on the point object.
  35617. * @private
  35618. */
  35619. function setDistance(p1, p2) {
  35620. var x = (defined(p1[kdX]) &&
  35621. defined(p2[kdX])) ?
  35622. Math.pow(p1[kdX] - p2[kdX], 2) :
  35623. null,
  35624. y = (defined(p1[kdY]) &&
  35625. defined(p2[kdY])) ?
  35626. Math.pow(p1[kdY] - p2[kdY], 2) :
  35627. null,
  35628. r = (x || 0) + (y || 0);
  35629. p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
  35630. p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
  35631. }
  35632. /**
  35633. * @private
  35634. */
  35635. function _search(search, tree, depth, dimensions) {
  35636. var point = tree.point,
  35637. axis = series.kdAxisArray[depth % dimensions],
  35638. tdist,
  35639. sideA,
  35640. sideB,
  35641. ret = point,
  35642. nPoint1,
  35643. nPoint2;
  35644. setDistance(search, point);
  35645. // Pick side based on distance to splitting point
  35646. tdist = search[axis] - point[axis];
  35647. sideA = tdist < 0 ? 'left' : 'right';
  35648. sideB = tdist < 0 ? 'right' : 'left';
  35649. // End of tree
  35650. if (tree[sideA]) {
  35651. nPoint1 = _search(search, tree[sideA], depth + 1, dimensions);
  35652. ret = (nPoint1[kdComparer] <
  35653. ret[kdComparer] ?
  35654. nPoint1 :
  35655. point);
  35656. }
  35657. if (tree[sideB]) {
  35658. // compare distance to current best to splitting point to
  35659. // decide wether to check side B or not
  35660. if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
  35661. nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
  35662. ret = (nPoint2[kdComparer] <
  35663. ret[kdComparer] ?
  35664. nPoint2 :
  35665. ret);
  35666. }
  35667. }
  35668. return ret;
  35669. }
  35670. if (!this.kdTree && !this.buildingKdTree) {
  35671. this.buildKDTree(e);
  35672. }
  35673. if (this.kdTree) {
  35674. return _search(point, this.kdTree, kdDimensions, kdDimensions);
  35675. }
  35676. },
  35677. /**
  35678. * @private
  35679. * @function Highcharts.Series#pointPlacementToXValue
  35680. * @return {number}
  35681. */
  35682. pointPlacementToXValue: function () {
  35683. var _a = this,
  35684. _b = _a.options,
  35685. pointPlacement = _b.pointPlacement,
  35686. pointRange = _b.pointRange,
  35687. axis = _a.xAxis;
  35688. var factor = pointPlacement;
  35689. // Point placement is relative to each series pointRange (#5889)
  35690. if (factor === 'between') {
  35691. factor = axis.reversed ? -0.5 : 0.5; // #11955
  35692. }
  35693. return isNumber(factor) ?
  35694. factor * pick(pointRange, axis.pointRange) :
  35695. 0;
  35696. },
  35697. /**
  35698. * @private
  35699. * @function Highcharts.Series#isPointInside
  35700. * @param {Highcharts.Point} point
  35701. * @return {boolean}
  35702. */
  35703. isPointInside: function (point) {
  35704. var isInside = typeof point.plotY !== 'undefined' &&
  35705. typeof point.plotX !== 'undefined' &&
  35706. point.plotY >= 0 &&
  35707. point.plotY <= this.yAxis.len && // #3519
  35708. point.plotX >= 0 &&
  35709. point.plotX <= this.xAxis.len;
  35710. return isInside;
  35711. }
  35712. }); // end Series prototype
  35713. /**
  35714. * A line series displays information as a series of data points connected by
  35715. * straight line segments.
  35716. *
  35717. * @sample {highcharts} highcharts/demo/line-basic/
  35718. * Line chart
  35719. * @sample {highstock} stock/demo/basic-line/
  35720. * Line chart
  35721. *
  35722. * @extends plotOptions.series
  35723. * @product highcharts highstock
  35724. * @apioption plotOptions.line
  35725. */
  35726. /**
  35727. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  35728. * of a line graph. Round means that lines are rounded in the ends and
  35729. * bends.
  35730. *
  35731. * @type {Highcharts.SeriesLinecapValue}
  35732. * @default round
  35733. * @since 3.0.7
  35734. * @apioption plotOptions.line.linecap
  35735. */
  35736. /**
  35737. * A `line` series. If the [type](#series.line.type) option is not
  35738. * specified, it is inherited from [chart.type](#chart.type).
  35739. *
  35740. * @extends series,plotOptions.line
  35741. * @excluding dataParser,dataURL
  35742. * @product highcharts highstock
  35743. * @apioption series.line
  35744. */
  35745. /**
  35746. * An array of data points for the series. For the `line` series type,
  35747. * points can be given in the following ways:
  35748. *
  35749. * 1. An array of numerical values. In this case, the numerical values will be
  35750. * interpreted as `y` options. The `x` values will be automatically
  35751. * calculated, either starting at 0 and incremented by 1, or from
  35752. * `pointStart` and `pointInterval` given in the series options. If the axis
  35753. * has categories, these will be used. Example:
  35754. * ```js
  35755. * data: [0, 5, 3, 5]
  35756. * ```
  35757. *
  35758. * 2. An array of arrays with 2 values. In this case, the values correspond to
  35759. * `x,y`. If the first value is a string, it is applied as the name of the
  35760. * point, and the `x` value is inferred.
  35761. * ```js
  35762. * data: [
  35763. * [0, 1],
  35764. * [1, 2],
  35765. * [2, 8]
  35766. * ]
  35767. * ```
  35768. *
  35769. * 3. An array of objects with named values. The following snippet shows only a
  35770. * few settings, see the complete options set below. If the total number of
  35771. * data points exceeds the series'
  35772. * [turboThreshold](#series.line.turboThreshold),
  35773. * this option is not available.
  35774. * ```js
  35775. * data: [{
  35776. * x: 1,
  35777. * y: 9,
  35778. * name: "Point2",
  35779. * color: "#00FF00"
  35780. * }, {
  35781. * x: 1,
  35782. * y: 6,
  35783. * name: "Point1",
  35784. * color: "#FF00FF"
  35785. * }]
  35786. * ```
  35787. *
  35788. * **Note:** In TypeScript you have to extend `PointOptionsObject` with an
  35789. * additional declaration to allow custom data types:
  35790. * ```ts
  35791. * declare module `highcharts` {
  35792. * interface PointOptionsObject {
  35793. * custom: Record<string, (boolean|number|string)>;
  35794. * }
  35795. * }
  35796. * ```
  35797. *
  35798. * @sample {highcharts} highcharts/chart/reflow-true/
  35799. * Numerical values
  35800. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  35801. * Arrays of numeric x and y
  35802. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  35803. * Arrays of datetime x and y
  35804. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  35805. * Arrays of point.name and y
  35806. * @sample {highcharts} highcharts/series/data-array-of-objects/
  35807. * Config objects
  35808. *
  35809. * @declare Highcharts.PointOptionsObject
  35810. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  35811. * @apioption series.line.data
  35812. */
  35813. /**
  35814. * An additional, individual class name for the data point's graphic
  35815. * representation.
  35816. *
  35817. * @type {string}
  35818. * @since 5.0.0
  35819. * @product highcharts gantt
  35820. * @apioption series.line.data.className
  35821. */
  35822. /**
  35823. * Individual color for the point. By default the color is pulled from
  35824. * the global `colors` array.
  35825. *
  35826. * In styled mode, the `color` option doesn't take effect. Instead, use
  35827. * `colorIndex`.
  35828. *
  35829. * @sample {highcharts} highcharts/point/color/
  35830. * Mark the highest point
  35831. *
  35832. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35833. * @product highcharts highstock gantt
  35834. * @apioption series.line.data.color
  35835. */
  35836. /**
  35837. * A specific color index to use for the point, so its graphic representations
  35838. * are given the class name `highcharts-color-{n}`. In styled mode this will
  35839. * change the color of the graphic. In non-styled mode, the color by is set by
  35840. * the `fill` attribute, so the change in class name won't have a visual effect
  35841. * by default.
  35842. *
  35843. * @type {number}
  35844. * @since 5.0.0
  35845. * @product highcharts gantt
  35846. * @apioption series.line.data.colorIndex
  35847. */
  35848. /**
  35849. * A reserved subspace to store options and values for customized functionality.
  35850. * Here you can add additional data for your own event callbacks and formatter
  35851. * callbacks.
  35852. *
  35853. * @sample {highcharts} highcharts/point/custom/
  35854. * Point and series with custom data
  35855. *
  35856. * @type {Highcharts.Dictionary<*>}
  35857. * @apioption series.line.data.custom
  35858. */
  35859. /**
  35860. * Individual data label for each point. The options are the same as
  35861. * the ones for [plotOptions.series.dataLabels](
  35862. * #plotOptions.series.dataLabels).
  35863. *
  35864. * @sample highcharts/point/datalabels/
  35865. * Show a label for the last value
  35866. *
  35867. * @declare Highcharts.DataLabelsOptions
  35868. * @extends plotOptions.line.dataLabels
  35869. * @product highcharts highstock gantt
  35870. * @apioption series.line.data.dataLabels
  35871. */
  35872. /**
  35873. * A description of the point to add to the screen reader information
  35874. * about the point.
  35875. *
  35876. * @type {string}
  35877. * @since 5.0.0
  35878. * @requires modules/accessibility
  35879. * @apioption series.line.data.description
  35880. */
  35881. /**
  35882. * An id for the point. This can be used after render time to get a
  35883. * pointer to the point object through `chart.get()`.
  35884. *
  35885. * @sample {highcharts} highcharts/point/id/
  35886. * Remove an id'd point
  35887. *
  35888. * @type {string}
  35889. * @since 1.2.0
  35890. * @product highcharts highstock gantt
  35891. * @apioption series.line.data.id
  35892. */
  35893. /**
  35894. * The rank for this point's data label in case of collision. If two
  35895. * data labels are about to overlap, only the one with the highest `labelrank`
  35896. * will be drawn.
  35897. *
  35898. * @type {number}
  35899. * @apioption series.line.data.labelrank
  35900. */
  35901. /**
  35902. * The name of the point as shown in the legend, tooltip, dataLabels, etc.
  35903. *
  35904. * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
  35905. *
  35906. * @sample {highcharts} highcharts/series/data-array-of-objects/
  35907. * Point names
  35908. *
  35909. * @type {string}
  35910. * @apioption series.line.data.name
  35911. */
  35912. /**
  35913. * Whether the data point is selected initially.
  35914. *
  35915. * @type {boolean}
  35916. * @default false
  35917. * @product highcharts highstock gantt
  35918. * @apioption series.line.data.selected
  35919. */
  35920. /**
  35921. * The x value of the point. For datetime axes, the X value is the timestamp
  35922. * in milliseconds since 1970.
  35923. *
  35924. * @type {number}
  35925. * @product highcharts highstock
  35926. * @apioption series.line.data.x
  35927. */
  35928. /**
  35929. * The y value of the point.
  35930. *
  35931. * @type {number|null}
  35932. * @product highcharts highstock
  35933. * @apioption series.line.data.y
  35934. */
  35935. /**
  35936. * The individual point events.
  35937. *
  35938. * @extends plotOptions.series.point.events
  35939. * @product highcharts highstock gantt
  35940. * @apioption series.line.data.events
  35941. */
  35942. /**
  35943. * Options for the point markers of line-like series.
  35944. *
  35945. * @declare Highcharts.PointMarkerOptionsObject
  35946. * @extends plotOptions.series.marker
  35947. * @product highcharts highstock
  35948. * @apioption series.line.data.marker
  35949. */
  35950. ''; // include precedent doclets in transpilat
  35951. });
  35952. _registerModule(_modules, 'Extensions/Stacking.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Axis/StackingAxis.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, StackingAxis, U) {
  35953. /* *
  35954. *
  35955. * (c) 2010-2020 Torstein Honsi
  35956. *
  35957. * License: www.highcharts.com/license
  35958. *
  35959. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  35960. *
  35961. * */
  35962. var correctFloat = U.correctFloat,
  35963. defined = U.defined,
  35964. destroyObjectProperties = U.destroyObjectProperties,
  35965. format = U.format,
  35966. isNumber = U.isNumber,
  35967. pick = U.pick;
  35968. /**
  35969. * Stack of data points
  35970. *
  35971. * @product highcharts
  35972. *
  35973. * @interface Highcharts.StackItemObject
  35974. */ /**
  35975. * Alignment settings
  35976. * @name Highcharts.StackItemObject#alignOptions
  35977. * @type {Highcharts.AlignObject}
  35978. */ /**
  35979. * Related axis
  35980. * @name Highcharts.StackItemObject#axis
  35981. * @type {Highcharts.Axis}
  35982. */ /**
  35983. * Cumulative value of the stacked data points
  35984. * @name Highcharts.StackItemObject#cumulative
  35985. * @type {number}
  35986. */ /**
  35987. * True if on the negative side
  35988. * @name Highcharts.StackItemObject#isNegative
  35989. * @type {boolean}
  35990. */ /**
  35991. * Related SVG element
  35992. * @name Highcharts.StackItemObject#label
  35993. * @type {Highcharts.SVGElement}
  35994. */ /**
  35995. * Related stack options
  35996. * @name Highcharts.StackItemObject#options
  35997. * @type {Highcharts.YAxisStackLabelsOptions}
  35998. */ /**
  35999. * Total value of the stacked data points
  36000. * @name Highcharts.StackItemObject#total
  36001. * @type {number}
  36002. */ /**
  36003. * Shared x value of the stack
  36004. * @name Highcharts.StackItemObject#x
  36005. * @type {number}
  36006. */
  36007. ''; // detached doclets above
  36008. var Series = H.Series;
  36009. /* eslint-disable no-invalid-this, valid-jsdoc */
  36010. /**
  36011. * The class for stacks. Each stack, on a specific X value and either negative
  36012. * or positive, has its own stack item.
  36013. *
  36014. * @private
  36015. * @class
  36016. * @name Highcharts.StackItem
  36017. * @param {Highcharts.Axis} axis
  36018. * @param {Highcharts.YAxisStackLabelsOptions} options
  36019. * @param {boolean} isNegative
  36020. * @param {number} x
  36021. * @param {Highcharts.OptionsStackingValue} [stackOption]
  36022. */
  36023. var StackItem = /** @class */ (function () {
  36024. function StackItem(axis, options, isNegative, x, stackOption) {
  36025. var inverted = axis.chart.inverted;
  36026. this.axis = axis;
  36027. // Tells if the stack is negative
  36028. this.isNegative = isNegative;
  36029. // Save the options to be able to style the label
  36030. this.options = options = options || {};
  36031. // Save the x value to be able to position the label later
  36032. this.x = x;
  36033. // Initialize total value
  36034. this.total = null;
  36035. // This will keep each points' extremes stored by series.index and point
  36036. // index
  36037. this.points = {};
  36038. this.hasValidPoints = false;
  36039. // Save the stack option on the series configuration object,
  36040. // and whether to treat it as percent
  36041. this.stack = stackOption;
  36042. this.leftCliff = 0;
  36043. this.rightCliff = 0;
  36044. // The align options and text align varies on whether the stack is
  36045. // negative and if the chart is inverted or not.
  36046. // First test the user supplied value, then use the dynamic.
  36047. this.alignOptions = {
  36048. align: options.align ||
  36049. (inverted ? (isNegative ? 'left' : 'right') : 'center'),
  36050. verticalAlign: options.verticalAlign ||
  36051. (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
  36052. y: options.y,
  36053. x: options.x
  36054. };
  36055. this.textAlign = options.textAlign ||
  36056. (inverted ? (isNegative ? 'right' : 'left') : 'center');
  36057. }
  36058. /**
  36059. * @private
  36060. * @function Highcharts.StackItem#destroy
  36061. */
  36062. StackItem.prototype.destroy = function () {
  36063. destroyObjectProperties(this, this.axis);
  36064. };
  36065. /**
  36066. * Renders the stack total label and adds it to the stack label group.
  36067. *
  36068. * @private
  36069. * @function Highcharts.StackItem#render
  36070. * @param {Highcharts.SVGElement} group
  36071. */
  36072. StackItem.prototype.render = function (group) {
  36073. var chart = this.axis.chart,
  36074. options = this.options,
  36075. formatOption = options.format,
  36076. attr = {},
  36077. str = formatOption ? // format the text in the label
  36078. format(formatOption,
  36079. this,
  36080. chart) :
  36081. options.formatter.call(this);
  36082. // Change the text to reflect the new total and set visibility to hidden
  36083. // in case the serie is hidden
  36084. if (this.label) {
  36085. this.label.attr({ text: str, visibility: 'hidden' });
  36086. }
  36087. else {
  36088. // Create new label
  36089. this.label = chart.renderer
  36090. .label(str, null, null, options.shape, null, null, options.useHTML, false, 'stack-labels');
  36091. attr = {
  36092. r: options.borderRadius || 0,
  36093. text: str,
  36094. rotation: options.rotation,
  36095. padding: pick(options.padding, 5),
  36096. visibility: 'hidden' // hidden until setOffset is called
  36097. };
  36098. if (!chart.styledMode) {
  36099. attr.fill = options.backgroundColor;
  36100. attr.stroke = options.borderColor;
  36101. attr['stroke-width'] = options.borderWidth;
  36102. this.label.css(options.style);
  36103. }
  36104. this.label.attr(attr);
  36105. if (!this.label.added) {
  36106. this.label.add(group); // add to the labels-group
  36107. }
  36108. }
  36109. // Rank it higher than data labels (#8742)
  36110. this.label.labelrank = chart.plotHeight;
  36111. };
  36112. /**
  36113. * Sets the offset that the stack has from the x value and repositions the
  36114. * label.
  36115. *
  36116. * @private
  36117. * @function Highcarts.StackItem#setOffset
  36118. * @param {number} xOffset
  36119. * @param {number} xWidth
  36120. * @param {number} [boxBottom]
  36121. * @param {number} [boxTop]
  36122. * @param {number} [defaultX]
  36123. */
  36124. StackItem.prototype.setOffset = function (xOffset, xWidth, boxBottom, boxTop, defaultX) {
  36125. var stackItem = this,
  36126. axis = stackItem.axis,
  36127. chart = axis.chart,
  36128. // stack value translated mapped to chart coordinates
  36129. y = axis.translate(axis.stacking.usePercentage ?
  36130. 100 :
  36131. (boxTop ?
  36132. boxTop :
  36133. stackItem.total), 0, 0, 0, 1),
  36134. yZero = axis.translate(boxBottom ? boxBottom : 0), // stack origin
  36135. // stack height:
  36136. h = defined(y) && Math.abs(y - yZero),
  36137. // x position:
  36138. x = pick(defaultX,
  36139. chart.xAxis[0].translate(stackItem.x)) +
  36140. xOffset,
  36141. stackBox = defined(y) && stackItem.getStackBox(chart,
  36142. stackItem,
  36143. x,
  36144. y,
  36145. xWidth,
  36146. h,
  36147. axis),
  36148. label = stackItem.label,
  36149. isNegative = stackItem.isNegative,
  36150. isJustify = pick(stackItem.options.overflow, 'justify') === 'justify',
  36151. textAlign = stackItem.textAlign,
  36152. visible;
  36153. if (label && stackBox) {
  36154. var bBox = label.getBBox(),
  36155. padding = label.padding,
  36156. boxOffsetX,
  36157. boxOffsetY;
  36158. if (textAlign === 'left') {
  36159. boxOffsetX = chart.inverted ? -padding : padding;
  36160. }
  36161. else if (textAlign === 'right') {
  36162. boxOffsetX = bBox.width;
  36163. }
  36164. else {
  36165. if (chart.inverted && textAlign === 'center') {
  36166. boxOffsetX = bBox.width / 2;
  36167. }
  36168. else {
  36169. boxOffsetX = chart.inverted ?
  36170. (isNegative ? bBox.width + padding : -padding) : bBox.width / 2;
  36171. }
  36172. }
  36173. boxOffsetY = chart.inverted ?
  36174. bBox.height / 2 : (isNegative ? -padding : bBox.height);
  36175. // Reset alignOptions property after justify #12337
  36176. stackItem.alignOptions.x = pick(stackItem.options.x, 0);
  36177. stackItem.alignOptions.y = pick(stackItem.options.y, 0);
  36178. // Set the stackBox position
  36179. stackBox.x -= boxOffsetX;
  36180. stackBox.y -= boxOffsetY;
  36181. // Align the label to the box
  36182. label.align(stackItem.alignOptions, null, stackBox);
  36183. // Check if label is inside the plotArea #12294
  36184. if (chart.isInsidePlot(label.alignAttr.x + boxOffsetX - stackItem.alignOptions.x, label.alignAttr.y + boxOffsetY - stackItem.alignOptions.y)) {
  36185. label.show();
  36186. }
  36187. else {
  36188. // Move label away to avoid the overlapping issues
  36189. label.alignAttr.y = -9999;
  36190. isJustify = false;
  36191. }
  36192. if (isJustify) {
  36193. // Justify stackLabel into the stackBox
  36194. Series.prototype.justifyDataLabel.call(this.axis, label, stackItem.alignOptions, label.alignAttr, bBox, stackBox);
  36195. }
  36196. label.attr({
  36197. x: label.alignAttr.x,
  36198. y: label.alignAttr.y
  36199. });
  36200. if (pick(!isJustify && stackItem.options.crop, true)) {
  36201. visible =
  36202. isNumber(label.x) &&
  36203. isNumber(label.y) &&
  36204. chart.isInsidePlot(label.x - padding + label.width, label.y) &&
  36205. chart.isInsidePlot(label.x + padding, label.y);
  36206. if (!visible) {
  36207. label.hide();
  36208. }
  36209. }
  36210. }
  36211. };
  36212. /**
  36213. * @private
  36214. * @function Highcharts.StackItem#getStackBox
  36215. *
  36216. * @param {Highcharts.Chart} chart
  36217. *
  36218. * @param {Highcharts.StackItem} stackItem
  36219. *
  36220. * @param {number} x
  36221. *
  36222. * @param {number} y
  36223. *
  36224. * @param {number} xWidth
  36225. *
  36226. * @param {number} h
  36227. *
  36228. * @param {Highcharts.Axis} axis
  36229. *
  36230. * @return {Highcharts.BBoxObject}
  36231. */
  36232. StackItem.prototype.getStackBox = function (chart, stackItem, x, y, xWidth, h, axis) {
  36233. var reversed = stackItem.axis.reversed,
  36234. inverted = chart.inverted,
  36235. axisPos = axis.height + axis.pos -
  36236. (inverted ? chart.plotLeft : chart.plotTop),
  36237. neg = (stackItem.isNegative && !reversed) ||
  36238. (!stackItem.isNegative && reversed); // #4056
  36239. return {
  36240. x: inverted ? (neg ? y - axis.right : y - h + axis.pos - chart.plotLeft) :
  36241. x + chart.xAxis[0].transB - chart.plotLeft,
  36242. y: inverted ?
  36243. axis.height - x - xWidth :
  36244. (neg ?
  36245. (axisPos - y - h) :
  36246. axisPos - y),
  36247. width: inverted ? h : xWidth,
  36248. height: inverted ? xWidth : h
  36249. };
  36250. };
  36251. return StackItem;
  36252. }());
  36253. /**
  36254. * Generate stacks for each series and calculate stacks total values
  36255. *
  36256. * @private
  36257. * @function Highcharts.Chart#getStacks
  36258. */
  36259. Chart.prototype.getStacks = function () {
  36260. var chart = this,
  36261. inverted = chart.inverted;
  36262. // reset stacks for each yAxis
  36263. chart.yAxis.forEach(function (axis) {
  36264. if (axis.stacking && axis.stacking.stacks && axis.hasVisibleSeries) {
  36265. axis.stacking.oldStacks = axis.stacking.stacks;
  36266. }
  36267. });
  36268. chart.series.forEach(function (series) {
  36269. var xAxisOptions = series.xAxis && series.xAxis.options || {};
  36270. if (series.options.stacking &&
  36271. (series.visible === true ||
  36272. chart.options.chart.ignoreHiddenSeries === false)) {
  36273. series.stackKey = [
  36274. series.type,
  36275. pick(series.options.stack, ''),
  36276. inverted ? xAxisOptions.top : xAxisOptions.left,
  36277. inverted ? xAxisOptions.height : xAxisOptions.width
  36278. ].join(',');
  36279. }
  36280. });
  36281. };
  36282. // Stacking methods defined on the Axis prototype
  36283. StackingAxis.compose(Axis);
  36284. // Stacking methods defined for Series prototype
  36285. /**
  36286. * Set grouped points in a stack-like object. When `centerInCategory` is true,
  36287. * and `stacking` is not enabled, we need a pseudo (horizontal) stack in order
  36288. * to handle grouping of points within the same category.
  36289. *
  36290. * @private
  36291. * @function Highcharts.Series#setStackedPoints
  36292. * @return {void}
  36293. */
  36294. Series.prototype.setGroupedPoints = function () {
  36295. if (this.options.centerInCategory &&
  36296. (this.is('column') || this.is('columnrange')) &&
  36297. // With stacking enabled, we already have stacks that we can compute
  36298. // from
  36299. !this.options.stacking &&
  36300. // With only one series, we don't need to consider centerInCategory
  36301. this.chart.series.length > 1) {
  36302. Series.prototype.setStackedPoints.call(this, 'group');
  36303. }
  36304. };
  36305. /**
  36306. * Adds series' points value to corresponding stack
  36307. *
  36308. * @private
  36309. * @function Highcharts.Series#setStackedPoints
  36310. */
  36311. Series.prototype.setStackedPoints = function (stackingParam) {
  36312. var stacking = stackingParam || this.options.stacking;
  36313. if (!stacking ||
  36314. (this.visible !== true &&
  36315. this.chart.options.chart.ignoreHiddenSeries !== false)) {
  36316. return;
  36317. }
  36318. var series = this, xData = series.processedXData, yData = series.processedYData, stackedYData = [], yDataLength = yData.length, seriesOptions = series.options, threshold = seriesOptions.threshold, stackThreshold = pick(seriesOptions.startFromThreshold && threshold, 0), stackOption = seriesOptions.stack, stackKey = stackingParam ? series.type + "," + stacking : series.stackKey, negKey = '-' + stackKey, negStacks = series.negStacks, yAxis = series.yAxis, stacks = yAxis.stacking.stacks, oldStacks = yAxis.stacking.oldStacks, stackIndicator, isNegative, stack, other, key, pointKey, i, x, y;
  36319. yAxis.stacking.stacksTouched += 1;
  36320. // loop over the non-null y values and read them into a local array
  36321. for (i = 0; i < yDataLength; i++) {
  36322. x = xData[i];
  36323. y = yData[i];
  36324. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
  36325. pointKey = stackIndicator.key;
  36326. // Read stacked values into a stack based on the x value,
  36327. // the sign of y and the stack key. Stacking is also handled for null
  36328. // values (#739)
  36329. isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
  36330. key = isNegative ? negKey : stackKey;
  36331. // Create empty object for this stack if it doesn't exist yet
  36332. if (!stacks[key]) {
  36333. stacks[key] =
  36334. {};
  36335. }
  36336. // Initialize StackItem for this x
  36337. if (!stacks[key][x]) {
  36338. if (oldStacks[key] &&
  36339. oldStacks[key][x]) {
  36340. stacks[key][x] = oldStacks[key][x];
  36341. stacks[key][x].total = null;
  36342. }
  36343. else {
  36344. stacks[key][x] = new StackItem(yAxis, yAxis.options.stackLabels, isNegative, x, stackOption);
  36345. }
  36346. }
  36347. // If the StackItem doesn't exist, create it first
  36348. stack = stacks[key][x];
  36349. if (y !== null) {
  36350. stack.points[pointKey] = stack.points[series.index] =
  36351. [pick(stack.cumulative, stackThreshold)];
  36352. // Record the base of the stack
  36353. if (!defined(stack.cumulative)) {
  36354. stack.base = pointKey;
  36355. }
  36356. stack.touched = yAxis.stacking.stacksTouched;
  36357. // In area charts, if there are multiple points on the same X value,
  36358. // let the area fill the full span of those points
  36359. if (stackIndicator.index > 0 && series.singleStacks === false) {
  36360. stack.points[pointKey][0] =
  36361. stack.points[series.index + ',' + x + ',0'][0];
  36362. }
  36363. // When updating to null, reset the point stack (#7493)
  36364. }
  36365. else {
  36366. stack.points[pointKey] = stack.points[series.index] =
  36367. null;
  36368. }
  36369. // Add value to the stack total
  36370. if (stacking === 'percent') {
  36371. // Percent stacked column, totals are the same for the positive and
  36372. // negative stacks
  36373. other = isNegative ? stackKey : negKey;
  36374. if (negStacks && stacks[other] && stacks[other][x]) {
  36375. other = stacks[other][x];
  36376. stack.total = other.total =
  36377. Math.max(other.total, stack.total) +
  36378. Math.abs(y) ||
  36379. 0;
  36380. // Percent stacked areas
  36381. }
  36382. else {
  36383. stack.total =
  36384. correctFloat(stack.total + (Math.abs(y) || 0));
  36385. }
  36386. }
  36387. else if (stacking === 'group') {
  36388. // In this stack, the total is the number of valid points
  36389. if (y !== null) {
  36390. stack.total = (stack.total || 0) + 1;
  36391. }
  36392. }
  36393. else {
  36394. stack.total = correctFloat(stack.total + (y || 0));
  36395. }
  36396. if (stacking === 'group') {
  36397. // This point's index within the stack, pushed to stack.points[1]
  36398. stack.cumulative = (stack.total || 1) - 1;
  36399. }
  36400. else {
  36401. stack.cumulative =
  36402. pick(stack.cumulative, stackThreshold) + (y || 0);
  36403. }
  36404. if (y !== null) {
  36405. stack.points[pointKey].push(stack.cumulative);
  36406. stackedYData[i] = stack.cumulative;
  36407. stack.hasValidPoints = true;
  36408. }
  36409. }
  36410. if (stacking === 'percent') {
  36411. yAxis.stacking.usePercentage = true;
  36412. }
  36413. if (stacking !== 'group') {
  36414. this.stackedYData = stackedYData; // To be used in getExtremes
  36415. }
  36416. // Reset old stacks
  36417. yAxis.stacking.oldStacks = {};
  36418. };
  36419. /**
  36420. * Iterate over all stacks and compute the absolute values to percent
  36421. *
  36422. * @private
  36423. * @function Highcharts.Series#modifyStacks
  36424. */
  36425. Series.prototype.modifyStacks = function () {
  36426. var series = this,
  36427. yAxis = series.yAxis,
  36428. stackKey = series.stackKey,
  36429. stacks = yAxis.stacking.stacks,
  36430. processedXData = series.processedXData,
  36431. stackIndicator,
  36432. stacking = series.options.stacking;
  36433. if (series[stacking + 'Stacker']) { // Modifier function exists
  36434. [stackKey, '-' + stackKey].forEach(function (key) {
  36435. var i = processedXData.length,
  36436. x,
  36437. stack,
  36438. pointExtremes;
  36439. while (i--) {
  36440. x = processedXData[i];
  36441. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index, key);
  36442. stack = stacks[key] && stacks[key][x];
  36443. pointExtremes =
  36444. stack && stack.points[stackIndicator.key];
  36445. if (pointExtremes) {
  36446. series[stacking + 'Stacker'](pointExtremes, stack, i);
  36447. }
  36448. }
  36449. });
  36450. }
  36451. };
  36452. /**
  36453. * Modifier function for percent stacks. Blows up the stack to 100%.
  36454. *
  36455. * @private
  36456. * @function Highcharts.Series#percentStacker
  36457. * @param {Array<number>} pointExtremes
  36458. * @param {Highcharts.StackItem} stack
  36459. * @param {number} i
  36460. */
  36461. Series.prototype.percentStacker = function (pointExtremes, stack, i) {
  36462. var totalFactor = stack.total ? 100 / stack.total : 0;
  36463. // Y bottom value
  36464. pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
  36465. // Y value
  36466. pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
  36467. this.stackedYData[i] = pointExtremes[1];
  36468. };
  36469. /**
  36470. * Get stack indicator, according to it's x-value, to determine points with the
  36471. * same x-value
  36472. *
  36473. * @private
  36474. * @function Highcharts.Series#getStackIndicator
  36475. * @param {Highcharts.StackItemIndicatorObject|undefined} stackIndicator
  36476. * @param {number} x
  36477. * @param {number} index
  36478. * @param {string} [key]
  36479. * @return {Highcharts.StackItemIndicatorObject}
  36480. */
  36481. Series.prototype.getStackIndicator = function (stackIndicator, x, index, key) {
  36482. // Update stack indicator, when:
  36483. // first point in a stack || x changed || stack type (negative vs positive)
  36484. // changed:
  36485. if (!defined(stackIndicator) ||
  36486. stackIndicator.x !== x ||
  36487. (key && stackIndicator.key !== key)) {
  36488. stackIndicator = {
  36489. x: x,
  36490. index: 0,
  36491. key: key
  36492. };
  36493. }
  36494. else {
  36495. (stackIndicator).index++;
  36496. }
  36497. stackIndicator.key =
  36498. [index, x, stackIndicator.index].join(',');
  36499. return stackIndicator;
  36500. };
  36501. H.StackItem = StackItem;
  36502. return H.StackItem;
  36503. });
  36504. _registerModule(_modules, 'Core/Dynamics.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Time.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, O, Point, Time, U) {
  36505. /* *
  36506. *
  36507. * (c) 2010-2020 Torstein Honsi
  36508. *
  36509. * License: www.highcharts.com/license
  36510. *
  36511. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  36512. *
  36513. * */
  36514. var time = O.time;
  36515. var addEvent = U.addEvent,
  36516. animate = U.animate,
  36517. createElement = U.createElement,
  36518. css = U.css,
  36519. defined = U.defined,
  36520. erase = U.erase,
  36521. error = U.error,
  36522. extend = U.extend,
  36523. fireEvent = U.fireEvent,
  36524. isArray = U.isArray,
  36525. isNumber = U.isNumber,
  36526. isObject = U.isObject,
  36527. isString = U.isString,
  36528. merge = U.merge,
  36529. objectEach = U.objectEach,
  36530. pick = U.pick,
  36531. relativeLength = U.relativeLength,
  36532. setAnimation = U.setAnimation,
  36533. splat = U.splat;
  36534. var Series = H.Series,
  36535. seriesTypes = H.seriesTypes;
  36536. /* eslint-disable valid-jsdoc */
  36537. /**
  36538. * Remove settings that have not changed, to avoid unnecessary rendering or
  36539. * computing (#9197).
  36540. * @private
  36541. */
  36542. H.cleanRecursively = function (newer, older) {
  36543. var result = {};
  36544. objectEach(newer, function (val, key) {
  36545. var ob;
  36546. // Dive into objects (except DOM nodes)
  36547. if (isObject(newer[key], true) &&
  36548. !newer.nodeType && // #10044
  36549. older[key]) {
  36550. ob = H.cleanRecursively(newer[key], older[key]);
  36551. if (Object.keys(ob).length) {
  36552. result[key] = ob;
  36553. }
  36554. // Arrays, primitives and DOM nodes are copied directly
  36555. }
  36556. else if (isObject(newer[key]) ||
  36557. newer[key] !== older[key]) {
  36558. result[key] = newer[key];
  36559. }
  36560. });
  36561. return result;
  36562. };
  36563. // Extend the Chart prototype for dynamic methods
  36564. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  36565. /**
  36566. * Add a series to the chart after render time. Note that this method should
  36567. * never be used when adding data synchronously at chart render time, as it
  36568. * adds expense to the calculations and rendering. When adding data at the
  36569. * same time as the chart is initialized, add the series as a configuration
  36570. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  36571. *
  36572. * @sample highcharts/members/chart-addseries/
  36573. * Add a series from a button
  36574. * @sample stock/members/chart-addseries/
  36575. * Add a series in Highstock
  36576. *
  36577. * @function Highcharts.Chart#addSeries
  36578. *
  36579. * @param {Highcharts.SeriesOptionsType} options
  36580. * The config options for the series.
  36581. *
  36582. * @param {boolean} [redraw=true]
  36583. * Whether to redraw the chart after adding.
  36584. *
  36585. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36586. * Whether to apply animation, and optionally animation
  36587. * configuration.
  36588. *
  36589. * @return {Highcharts.Series}
  36590. * The newly created series object.
  36591. *
  36592. * @fires Highcharts.Chart#event:addSeries
  36593. * @fires Highcharts.Chart#event:afterAddSeries
  36594. */
  36595. addSeries: function (options, redraw, animation) {
  36596. var series,
  36597. chart = this;
  36598. if (options) { // <- not necessary
  36599. redraw = pick(redraw, true); // defaults to true
  36600. fireEvent(chart, 'addSeries', { options: options }, function () {
  36601. series = chart.initSeries(options);
  36602. chart.isDirtyLegend = true;
  36603. chart.linkSeries();
  36604. if (series.enabledDataSorting) {
  36605. // We need to call `setData` after `linkSeries`
  36606. series.setData(options.data, false);
  36607. }
  36608. fireEvent(chart, 'afterAddSeries', { series: series });
  36609. if (redraw) {
  36610. chart.redraw(animation);
  36611. }
  36612. });
  36613. }
  36614. return series;
  36615. },
  36616. /**
  36617. * Add an axis to the chart after render time. Note that this method should
  36618. * never be used when adding data synchronously at chart render time, as it
  36619. * adds expense to the calculations and rendering. When adding data at the
  36620. * same time as the chart is initialized, add the axis as a configuration
  36621. * option instead.
  36622. *
  36623. * @sample highcharts/members/chart-addaxis/
  36624. * Add and remove axes
  36625. *
  36626. * @function Highcharts.Chart#addAxis
  36627. *
  36628. * @param {Highcharts.AxisOptions} options
  36629. * The axis options.
  36630. *
  36631. * @param {boolean} [isX=false]
  36632. * Whether it is an X axis or a value axis.
  36633. *
  36634. * @param {boolean} [redraw=true]
  36635. * Whether to redraw the chart after adding.
  36636. *
  36637. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  36638. * Whether and how to apply animation in the redraw.
  36639. *
  36640. * @return {Highcharts.Axis}
  36641. * The newly generated Axis object.
  36642. */
  36643. addAxis: function (options, isX, redraw, animation) {
  36644. return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
  36645. },
  36646. /**
  36647. * Add a color axis to the chart after render time. Note that this method
  36648. * should never be used when adding data synchronously at chart render time,
  36649. * as it adds expense to the calculations and rendering. When adding data at
  36650. * the same time as the chart is initialized, add the axis as a
  36651. * configuration option instead.
  36652. *
  36653. * @sample highcharts/members/chart-addaxis/
  36654. * Add and remove axes
  36655. *
  36656. * @function Highcharts.Chart#addColorAxis
  36657. *
  36658. * @param {Highcharts.ColorAxisOptions} options
  36659. * The axis options.
  36660. *
  36661. * @param {boolean} [redraw=true]
  36662. * Whether to redraw the chart after adding.
  36663. *
  36664. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  36665. * Whether and how to apply animation in the redraw.
  36666. *
  36667. * @return {Highcharts.ColorAxis}
  36668. * The newly generated Axis object.
  36669. */
  36670. addColorAxis: function (options, redraw, animation) {
  36671. return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
  36672. },
  36673. /**
  36674. * Factory for creating different axis types.
  36675. *
  36676. * @private
  36677. * @function Highcharts.Chart#createAxis
  36678. *
  36679. * @param {string} type
  36680. * An axis type.
  36681. *
  36682. * @param {...Array<*>} arguments
  36683. * All arguments for the constructor.
  36684. *
  36685. * @return {Highcharts.Axis | Highcharts.ColorAxis}
  36686. * The newly generated Axis object.
  36687. */
  36688. createAxis: function (type, options) {
  36689. var chartOptions = this.options,
  36690. isColorAxis = type === 'colorAxis',
  36691. axisOptions = options.axis,
  36692. redraw = options.redraw,
  36693. animation = options.animation,
  36694. userOptions = merge(axisOptions, {
  36695. index: this[type].length,
  36696. isX: type === 'xAxis'
  36697. }),
  36698. axis;
  36699. if (isColorAxis) {
  36700. axis = new H.ColorAxis(this, userOptions);
  36701. }
  36702. else {
  36703. axis = new Axis(this, userOptions);
  36704. }
  36705. // Push the new axis options to the chart options
  36706. chartOptions[type] = splat(chartOptions[type] || {});
  36707. chartOptions[type].push(userOptions);
  36708. if (isColorAxis) {
  36709. this.isDirtyLegend = true;
  36710. // Clear before 'bindAxes' (#11924)
  36711. this.axes.forEach(function (axis) {
  36712. axis.series = [];
  36713. });
  36714. this.series.forEach(function (series) {
  36715. series.bindAxes();
  36716. series.isDirtyData = true;
  36717. });
  36718. }
  36719. if (pick(redraw, true)) {
  36720. this.redraw(animation);
  36721. }
  36722. return axis;
  36723. },
  36724. /**
  36725. * Dim the chart and show a loading text or symbol. Options for the loading
  36726. * screen are defined in {@link
  36727. * https://api.highcharts.com/highcharts/loading|the loading options}.
  36728. *
  36729. * @sample highcharts/members/chart-hideloading/
  36730. * Show and hide loading from a button
  36731. * @sample highcharts/members/chart-showloading/
  36732. * Apply different text labels
  36733. * @sample stock/members/chart-show-hide-loading/
  36734. * Toggle loading in Highstock
  36735. *
  36736. * @function Highcharts.Chart#showLoading
  36737. *
  36738. * @param {string} [str]
  36739. * An optional text to show in the loading label instead of the
  36740. * default one. The default text is set in
  36741. * [lang.loading](https://api.highcharts.com/highcharts/lang.loading).
  36742. */
  36743. showLoading: function (str) {
  36744. var chart = this,
  36745. options = chart.options,
  36746. loadingDiv = chart.loadingDiv,
  36747. loadingOptions = options.loading,
  36748. setLoadingSize = function () {
  36749. if (loadingDiv) {
  36750. css(loadingDiv, {
  36751. left: chart.plotLeft + 'px',
  36752. top: chart.plotTop + 'px',
  36753. width: chart.plotWidth + 'px',
  36754. height: chart.plotHeight + 'px'
  36755. });
  36756. }
  36757. };
  36758. // create the layer at the first call
  36759. if (!loadingDiv) {
  36760. chart.loadingDiv = loadingDiv = createElement('div', {
  36761. className: 'highcharts-loading highcharts-loading-hidden'
  36762. }, null, chart.container);
  36763. chart.loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
  36764. addEvent(chart, 'redraw', setLoadingSize); // #1080
  36765. }
  36766. loadingDiv.className = 'highcharts-loading';
  36767. // Update text
  36768. chart.loadingSpan.innerHTML =
  36769. pick(str, options.lang.loading, '');
  36770. if (!chart.styledMode) {
  36771. // Update visuals
  36772. css(loadingDiv, extend(loadingOptions.style, {
  36773. zIndex: 10
  36774. }));
  36775. css(chart.loadingSpan, loadingOptions.labelStyle);
  36776. // Show it
  36777. if (!chart.loadingShown) {
  36778. css(loadingDiv, {
  36779. opacity: 0,
  36780. display: ''
  36781. });
  36782. animate(loadingDiv, {
  36783. opacity: loadingOptions.style.opacity || 0.5
  36784. }, {
  36785. duration: loadingOptions.showDuration || 0
  36786. });
  36787. }
  36788. }
  36789. chart.loadingShown = true;
  36790. setLoadingSize();
  36791. },
  36792. /**
  36793. * Hide the loading layer.
  36794. *
  36795. * @see Highcharts.Chart#showLoading
  36796. *
  36797. * @sample highcharts/members/chart-hideloading/
  36798. * Show and hide loading from a button
  36799. * @sample stock/members/chart-show-hide-loading/
  36800. * Toggle loading in Highstock
  36801. *
  36802. * @function Highcharts.Chart#hideLoading
  36803. */
  36804. hideLoading: function () {
  36805. var options = this.options,
  36806. loadingDiv = this.loadingDiv;
  36807. if (loadingDiv) {
  36808. loadingDiv.className =
  36809. 'highcharts-loading highcharts-loading-hidden';
  36810. if (!this.styledMode) {
  36811. animate(loadingDiv, {
  36812. opacity: 0
  36813. }, {
  36814. duration: options.loading.hideDuration || 100,
  36815. complete: function () {
  36816. css(loadingDiv, { display: 'none' });
  36817. }
  36818. });
  36819. }
  36820. }
  36821. this.loadingShown = false;
  36822. },
  36823. /**
  36824. * These properties cause isDirtyBox to be set to true when updating. Can be
  36825. * extended from plugins.
  36826. */
  36827. propsRequireDirtyBox: [
  36828. 'backgroundColor',
  36829. 'borderColor',
  36830. 'borderWidth',
  36831. 'borderRadius',
  36832. 'plotBackgroundColor',
  36833. 'plotBackgroundImage',
  36834. 'plotBorderColor',
  36835. 'plotBorderWidth',
  36836. 'plotShadow',
  36837. 'shadow'
  36838. ],
  36839. /**
  36840. * These properties require a full reflow of chart elements, best
  36841. * implemented through running `Chart.setSize` internally (#8190).
  36842. * @type {Array}
  36843. */
  36844. propsRequireReflow: [
  36845. 'margin',
  36846. 'marginTop',
  36847. 'marginRight',
  36848. 'marginBottom',
  36849. 'marginLeft',
  36850. 'spacing',
  36851. 'spacingTop',
  36852. 'spacingRight',
  36853. 'spacingBottom',
  36854. 'spacingLeft'
  36855. ],
  36856. /**
  36857. * These properties cause all series to be updated when updating. Can be
  36858. * extended from plugins.
  36859. */
  36860. propsRequireUpdateSeries: [
  36861. 'chart.inverted',
  36862. 'chart.polar',
  36863. 'chart.ignoreHiddenSeries',
  36864. 'chart.type',
  36865. 'colors',
  36866. 'plotOptions',
  36867. 'time',
  36868. 'tooltip'
  36869. ],
  36870. /**
  36871. * These collections (arrays) implement update() methods with support for
  36872. * one-to-one option.
  36873. */
  36874. collectionsWithUpdate: [
  36875. 'xAxis',
  36876. 'yAxis',
  36877. 'zAxis',
  36878. 'series'
  36879. ],
  36880. /**
  36881. * A generic function to update any element of the chart. Elements can be
  36882. * enabled and disabled, moved, re-styled, re-formatted etc.
  36883. *
  36884. * A special case is configuration objects that take arrays, for example
  36885. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  36886. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  36887. * [series](https://api.highcharts.com/highcharts/series). For these
  36888. * collections, an `id` option is used to map the new option set to an
  36889. * existing object. If an existing object of the same id is not found, the
  36890. * corresponding item is updated. So for example, running `chart.update`
  36891. * with a series item without an id, will cause the existing chart's series
  36892. * with the same index in the series array to be updated. When the
  36893. * `oneToOne` parameter is true, `chart.update` will also take care of
  36894. * adding and removing items from the collection. Read more under the
  36895. * parameter description below.
  36896. *
  36897. * Note that when changing series data, `chart.update` may mutate the passed
  36898. * data options.
  36899. *
  36900. * See also the
  36901. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  36902. * Switching between `responsive.rules` basically runs `chart.update` under
  36903. * the hood.
  36904. *
  36905. * @sample highcharts/members/chart-update/
  36906. * Update chart geometry
  36907. *
  36908. * @function Highcharts.Chart#update
  36909. *
  36910. * @param {Highcharts.Options} options
  36911. * A configuration object for the new chart options.
  36912. *
  36913. * @param {boolean} [redraw=true]
  36914. * Whether to redraw the chart.
  36915. *
  36916. * @param {boolean} [oneToOne=false]
  36917. * When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
  36918. * collections will be updated one to one, and items will be either
  36919. * added or removed to match the new updated options. For example,
  36920. * if the chart has two series and we call `chart.update` with a
  36921. * configuration containing three series, one will be added. If we
  36922. * call `chart.update` with one series, one will be removed. Setting
  36923. * an empty `series` array will remove all series, but leaving out
  36924. * the`series` property will leave all series untouched. If the
  36925. * series have id's, the new series options will be matched by id,
  36926. * and the remaining ones removed.
  36927. *
  36928. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  36929. * Whether to apply animation, and optionally animation
  36930. * configuration.
  36931. *
  36932. * @fires Highcharts.Chart#event:update
  36933. * @fires Highcharts.Chart#event:afterUpdate
  36934. */
  36935. update: function (options, redraw, oneToOne, animation) {
  36936. var chart = this,
  36937. adders = {
  36938. credits: 'addCredits',
  36939. title: 'setTitle',
  36940. subtitle: 'setSubtitle',
  36941. caption: 'setCaption'
  36942. },
  36943. optionsChart,
  36944. updateAllAxes,
  36945. updateAllSeries,
  36946. newWidth,
  36947. newHeight,
  36948. runSetSize,
  36949. isResponsiveOptions = options.isResponsiveOptions,
  36950. itemsForRemoval = [];
  36951. fireEvent(chart, 'update', { options: options });
  36952. // If there are responsive rules in action, undo the responsive rules
  36953. // before we apply the updated options and replay the responsive rules
  36954. // on top from the chart.redraw function (#9617).
  36955. if (!isResponsiveOptions) {
  36956. chart.setResponsive(false, true);
  36957. }
  36958. options = H.cleanRecursively(options, chart.options);
  36959. merge(true, chart.userOptions, options);
  36960. // If the top-level chart option is present, some special updates are
  36961. // required
  36962. optionsChart = options.chart;
  36963. if (optionsChart) {
  36964. merge(true, chart.options.chart, optionsChart);
  36965. // Setter function
  36966. if ('className' in optionsChart) {
  36967. chart.setClassName(optionsChart.className);
  36968. }
  36969. if ('reflow' in optionsChart) {
  36970. chart.setReflow(optionsChart.reflow);
  36971. }
  36972. if ('inverted' in optionsChart ||
  36973. 'polar' in optionsChart ||
  36974. 'type' in optionsChart) {
  36975. // Parse options.chart.inverted and options.chart.polar together
  36976. // with the available series.
  36977. chart.propFromSeries();
  36978. updateAllAxes = true;
  36979. }
  36980. if ('alignTicks' in optionsChart) { // #6452
  36981. updateAllAxes = true;
  36982. }
  36983. objectEach(optionsChart, function (val, key) {
  36984. if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  36985. -1) {
  36986. updateAllSeries = true;
  36987. }
  36988. // Only dirty box
  36989. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  36990. chart.isDirtyBox = true;
  36991. }
  36992. // Chart setSize
  36993. if (chart.propsRequireReflow.indexOf(key) !== -1) {
  36994. if (isResponsiveOptions) {
  36995. chart.isDirtyBox = true;
  36996. }
  36997. else {
  36998. runSetSize = true;
  36999. }
  37000. }
  37001. });
  37002. if (!chart.styledMode && 'style' in optionsChart) {
  37003. chart.renderer.setStyle(optionsChart.style);
  37004. }
  37005. }
  37006. // Moved up, because tooltip needs updated plotOptions (#6218)
  37007. if (!chart.styledMode && options.colors) {
  37008. this.options.colors = options.colors;
  37009. }
  37010. if (options.plotOptions) {
  37011. merge(true, this.options.plotOptions, options.plotOptions);
  37012. }
  37013. // Maintaining legacy global time. If the chart is instanciated first
  37014. // with global time, then updated with time options, we need to create a
  37015. // new Time instance to avoid mutating the global time (#10536).
  37016. if (options.time && this.time === time) {
  37017. this.time = new Time(options.time);
  37018. }
  37019. // Some option stuctures correspond one-to-one to chart objects that
  37020. // have update methods, for example
  37021. // options.credits => chart.credits
  37022. // options.legend => chart.legend
  37023. // options.title => chart.title
  37024. // options.tooltip => chart.tooltip
  37025. // options.subtitle => chart.subtitle
  37026. // options.mapNavigation => chart.mapNavigation
  37027. // options.navigator => chart.navigator
  37028. // options.scrollbar => chart.scrollbar
  37029. objectEach(options, function (val, key) {
  37030. if (chart[key] &&
  37031. typeof chart[key].update === 'function') {
  37032. chart[key].update(val, false);
  37033. // If a one-to-one object does not exist, look for an adder function
  37034. }
  37035. else if (typeof chart[adders[key]] === 'function') {
  37036. chart[adders[key]](val);
  37037. }
  37038. if (key !== 'chart' &&
  37039. chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
  37040. updateAllSeries = true;
  37041. }
  37042. });
  37043. // Setters for collections. For axes and series, each item is referred
  37044. // by an id. If the id is not found, it defaults to the corresponding
  37045. // item in the collection, so setting one series without an id, will
  37046. // update the first series in the chart. Setting two series without
  37047. // an id will update the first and the second respectively (#6019)
  37048. // chart.update and responsive.
  37049. this.collectionsWithUpdate.forEach(function (coll) {
  37050. var indexMap;
  37051. if (options[coll]) {
  37052. // In stock charts, the navigator series are also part of the
  37053. // chart.series array, but those series should not be handled
  37054. // here (#8196).
  37055. if (coll === 'series') {
  37056. indexMap = [];
  37057. chart[coll].forEach(function (s, i) {
  37058. if (!s.options.isInternal) {
  37059. indexMap.push(pick(s.options.index, i));
  37060. }
  37061. });
  37062. }
  37063. splat(options[coll]).forEach(function (newOptions, i) {
  37064. var hasId = defined(newOptions.id);
  37065. var item;
  37066. // Match by id
  37067. if (hasId) {
  37068. item = chart.get(newOptions.id);
  37069. }
  37070. // No match by id found, match by index instead
  37071. if (!item) {
  37072. item = chart[coll][indexMap ? indexMap[i] : i];
  37073. // Check if we grabbed an item with an exising but
  37074. // different id (#13541)
  37075. if (item && hasId && defined(item.options.id)) {
  37076. item = void 0;
  37077. }
  37078. }
  37079. if (item && item.coll === coll) {
  37080. item.update(newOptions, false);
  37081. if (oneToOne) {
  37082. item.touched = true;
  37083. }
  37084. }
  37085. // If oneToOne and no matching item is found, add one
  37086. if (!item && oneToOne && chart.collectionsWithInit[coll]) {
  37087. chart.collectionsWithInit[coll][0].apply(chart,
  37088. // [newOptions, ...extraArguments, redraw=false]
  37089. [
  37090. newOptions
  37091. ].concat(
  37092. // Not all initializers require extra args
  37093. chart.collectionsWithInit[coll][1] || []).concat([
  37094. false
  37095. ])).touched = true;
  37096. }
  37097. });
  37098. // Add items for removal
  37099. if (oneToOne) {
  37100. chart[coll].forEach(function (item) {
  37101. if (!item.touched && !item.options.isInternal) {
  37102. itemsForRemoval.push(item);
  37103. }
  37104. else {
  37105. delete item.touched;
  37106. }
  37107. });
  37108. }
  37109. }
  37110. });
  37111. itemsForRemoval.forEach(function (item) {
  37112. if (item.remove) {
  37113. item.remove(false);
  37114. }
  37115. });
  37116. if (updateAllAxes) {
  37117. chart.axes.forEach(function (axis) {
  37118. axis.update({}, false);
  37119. });
  37120. }
  37121. // Certain options require the whole series structure to be thrown away
  37122. // and rebuilt
  37123. if (updateAllSeries) {
  37124. chart.getSeriesOrderByLinks().forEach(function (series) {
  37125. // Avoid removed navigator series
  37126. if (series.chart) {
  37127. series.update({}, false);
  37128. }
  37129. }, this);
  37130. }
  37131. // For loading, just update the options, do not redraw
  37132. if (options.loading) {
  37133. merge(true, chart.options.loading, options.loading);
  37134. }
  37135. // Update size. Redraw is forced.
  37136. newWidth = optionsChart && optionsChart.width;
  37137. newHeight = optionsChart && optionsChart.height;
  37138. if (isString(newHeight)) {
  37139. newHeight = relativeLength(newHeight, newWidth || chart.chartWidth);
  37140. }
  37141. if (
  37142. // In this case, run chart.setSize with newWidth and newHeight which
  37143. // are undefined, only for reflowing chart elements because margin
  37144. // or spacing has been set (#8190)
  37145. runSetSize ||
  37146. // In this case, the size is actually set
  37147. (isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  37148. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  37149. chart.setSize(newWidth, newHeight, animation);
  37150. }
  37151. else if (pick(redraw, true)) {
  37152. chart.redraw(animation);
  37153. }
  37154. fireEvent(chart, 'afterUpdate', {
  37155. options: options,
  37156. redraw: redraw,
  37157. animation: animation
  37158. });
  37159. },
  37160. /**
  37161. * Shortcut to set the subtitle options. This can also be done from {@link
  37162. * Chart#update} or {@link Chart#setTitle}.
  37163. *
  37164. * @function Highcharts.Chart#setSubtitle
  37165. *
  37166. * @param {Highcharts.SubtitleOptions} options
  37167. * New subtitle options. The subtitle text itself is set by the
  37168. * `options.text` property.
  37169. */
  37170. setSubtitle: function (options, redraw) {
  37171. this.applyDescription('subtitle', options);
  37172. this.layOutTitles(redraw);
  37173. },
  37174. /**
  37175. * Set the caption options. This can also be done from {@link
  37176. * Chart#update}.
  37177. *
  37178. * @function Highcharts.Chart#setCaption
  37179. *
  37180. * @param {Highcharts.CaptionOptions} options
  37181. * New caption options. The caption text itself is set by the
  37182. * `options.text` property.
  37183. */
  37184. setCaption: function (options, redraw) {
  37185. this.applyDescription('caption', options);
  37186. this.layOutTitles(redraw);
  37187. }
  37188. });
  37189. /**
  37190. * These collections (arrays) implement `Chart.addSomethig` method used in
  37191. * chart.update() to create new object in the collection. Equivalent for
  37192. * deleting is resolved by simple `Somethig.remove()`.
  37193. *
  37194. * Note: We need to define these references after initializers are bound to
  37195. * chart's prototype.
  37196. */
  37197. Chart.prototype.collectionsWithInit = {
  37198. // collectionName: [ initializingMethod, [extraArguments] ]
  37199. xAxis: [Chart.prototype.addAxis, [true]],
  37200. yAxis: [Chart.prototype.addAxis, [false]],
  37201. series: [Chart.prototype.addSeries]
  37202. };
  37203. // extend the Point prototype for dynamic methods
  37204. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  37205. /**
  37206. * Update point with new options (typically x/y data) and optionally redraw
  37207. * the series.
  37208. *
  37209. * @sample highcharts/members/point-update-column/
  37210. * Update column value
  37211. * @sample highcharts/members/point-update-pie/
  37212. * Update pie slice
  37213. * @sample maps/members/point-update/
  37214. * Update map area value in Highmaps
  37215. *
  37216. * @function Highcharts.Point#update
  37217. *
  37218. * @param {Highcharts.PointOptionsType} options
  37219. * The point options. Point options are handled as described under
  37220. * the `series.type.data` item for each series type. For example
  37221. * for a line series, if options is a single number, the point will
  37222. * be given that number as the marin y value. If it is an array, it
  37223. * will be interpreted as x and y values respectively. If it is an
  37224. * object, advanced options are applied.
  37225. *
  37226. * @param {boolean} [redraw=true]
  37227. * Whether to redraw the chart after the point is updated. If doing
  37228. * more operations on the chart, it is best practice to set
  37229. * `redraw` to false and call `chart.redraw()` after.
  37230. *
  37231. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  37232. * Whether to apply animation, and optionally animation
  37233. * configuration.
  37234. *
  37235. * @return {void}
  37236. *
  37237. * @fires Highcharts.Point#event:update
  37238. */
  37239. update: function (options, redraw, animation, runEvent) {
  37240. var point = this,
  37241. series = point.series,
  37242. graphic = point.graphic,
  37243. i,
  37244. chart = series.chart,
  37245. seriesOptions = series.options;
  37246. redraw = pick(redraw, true);
  37247. /**
  37248. * @private
  37249. */
  37250. function update() {
  37251. point.applyOptions(options);
  37252. // Update visuals, #4146
  37253. // Handle dummy graphic elements for a11y, #12718
  37254. var hasDummyGraphic = graphic && point.hasDummyGraphic;
  37255. var shouldDestroyGraphic = point.y === null ? !hasDummyGraphic : hasDummyGraphic;
  37256. if (graphic && shouldDestroyGraphic) {
  37257. point.graphic = graphic.destroy();
  37258. delete point.hasDummyGraphic;
  37259. }
  37260. if (isObject(options, true)) {
  37261. // Destroy so we can get new elements
  37262. if (graphic && graphic.element) {
  37263. // "null" is also a valid symbol
  37264. if (options &&
  37265. options.marker &&
  37266. typeof options.marker.symbol !== 'undefined') {
  37267. point.graphic = graphic.destroy();
  37268. }
  37269. }
  37270. if (options && options.dataLabels && point.dataLabel) {
  37271. point.dataLabel = point.dataLabel.destroy(); // #2468
  37272. }
  37273. if (point.connector) {
  37274. point.connector = point.connector.destroy(); // #7243
  37275. }
  37276. }
  37277. // record changes in the parallel arrays
  37278. i = point.index;
  37279. series.updateParallelArrays(point, i);
  37280. // Record the options to options.data. If the old or the new config
  37281. // is an object, use point options, otherwise use raw options
  37282. // (#4701, #4916).
  37283. seriesOptions.data[i] = (isObject(seriesOptions.data[i], true) ||
  37284. isObject(options, true)) ?
  37285. point.options :
  37286. pick(options, seriesOptions.data[i]);
  37287. // redraw
  37288. series.isDirty = series.isDirtyData = true;
  37289. if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
  37290. chart.isDirtyBox = true;
  37291. }
  37292. if (seriesOptions.legendType === 'point') { // #1831, #1885
  37293. chart.isDirtyLegend = true;
  37294. }
  37295. if (redraw) {
  37296. chart.redraw(animation);
  37297. }
  37298. }
  37299. // Fire the event with a default handler of doing the update
  37300. if (runEvent === false) { // When called from setData
  37301. update();
  37302. }
  37303. else {
  37304. point.firePointEvent('update', { options: options }, update);
  37305. }
  37306. },
  37307. /**
  37308. * Remove a point and optionally redraw the series and if necessary the axes
  37309. *
  37310. * @sample highcharts/plotoptions/series-point-events-remove/
  37311. * Remove point and confirm
  37312. * @sample highcharts/members/point-remove/
  37313. * Remove pie slice
  37314. * @sample maps/members/point-remove/
  37315. * Remove selected points in Highmaps
  37316. *
  37317. * @function Highcharts.Point#remove
  37318. *
  37319. * @param {boolean} [redraw=true]
  37320. * Whether to redraw the chart or wait for an explicit call. When
  37321. * doing more operations on the chart, for example running
  37322. * `point.remove()` in a loop, it is best practice to set `redraw`
  37323. * to false and call `chart.redraw()` after.
  37324. *
  37325. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=false]
  37326. * Whether to apply animation, and optionally animation
  37327. * configuration.
  37328. *
  37329. * @return {void}
  37330. */
  37331. remove: function (redraw, animation) {
  37332. this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
  37333. }
  37334. });
  37335. // Extend the series prototype for dynamic methods
  37336. extend(Series.prototype, /** @lends Series.prototype */ {
  37337. /**
  37338. * Add a point to the series after render time. The point can be added at
  37339. * the end, or by giving it an X value, to the start or in the middle of the
  37340. * series.
  37341. *
  37342. * @sample highcharts/members/series-addpoint-append/
  37343. * Append point
  37344. * @sample highcharts/members/series-addpoint-append-and-shift/
  37345. * Append and shift
  37346. * @sample highcharts/members/series-addpoint-x-and-y/
  37347. * Both X and Y values given
  37348. * @sample highcharts/members/series-addpoint-pie/
  37349. * Append pie slice
  37350. * @sample stock/members/series-addpoint/
  37351. * Append 100 points in Highstock
  37352. * @sample stock/members/series-addpoint-shift/
  37353. * Append and shift in Highstock
  37354. * @sample maps/members/series-addpoint/
  37355. * Add a point in Highmaps
  37356. *
  37357. * @function Highcharts.Series#addPoint
  37358. *
  37359. * @param {Highcharts.PointOptionsType} options
  37360. * The point options. If options is a single number, a point with
  37361. * that y value is appended to the series. If it is an array, it will
  37362. * be interpreted as x and y values respectively. If it is an
  37363. * object, advanced options as outlined under `series.data` are
  37364. * applied.
  37365. *
  37366. * @param {boolean} [redraw=true]
  37367. * Whether to redraw the chart after the point is added. When adding
  37368. * more than one point, it is highly recommended that the redraw
  37369. * option be set to false, and instead {@link Chart#redraw} is
  37370. * explicitly called after the adding of points is finished.
  37371. * Otherwise, the chart will redraw after adding each point.
  37372. *
  37373. * @param {boolean} [shift=false]
  37374. * If true, a point is shifted off the start of the series as one is
  37375. * appended to the end.
  37376. *
  37377. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  37378. * Whether to apply animation, and optionally animation
  37379. * configuration.
  37380. *
  37381. * @param {boolean} [withEvent=true]
  37382. * Used internally, whether to fire the series `addPoint` event.
  37383. *
  37384. * @return {void}
  37385. *
  37386. * @fires Highcharts.Series#event:addPoint
  37387. */
  37388. addPoint: function (options, redraw, shift, animation, withEvent) {
  37389. var series = this,
  37390. seriesOptions = series.options,
  37391. data = series.data,
  37392. chart = series.chart,
  37393. xAxis = series.xAxis,
  37394. names = xAxis && xAxis.hasNames && xAxis.names,
  37395. dataOptions = seriesOptions.data,
  37396. point,
  37397. xData = series.xData,
  37398. isInTheMiddle,
  37399. i,
  37400. x;
  37401. // Optional redraw, defaults to true
  37402. redraw = pick(redraw, true);
  37403. // Get options and push the point to xData, yData and series.options. In
  37404. // series.generatePoints the Point instance will be created on demand
  37405. // and pushed to the series.data array.
  37406. point = { series: series };
  37407. series.pointClass.prototype.applyOptions.apply(point, [options]);
  37408. x = point.x;
  37409. // Get the insertion point
  37410. i = xData.length;
  37411. if (series.requireSorting && x < xData[i - 1]) {
  37412. isInTheMiddle = true;
  37413. while (i && xData[i - 1] > x) {
  37414. i--;
  37415. }
  37416. }
  37417. // Insert undefined item
  37418. series.updateParallelArrays(point, 'splice', i, 0, 0);
  37419. // Update it
  37420. series.updateParallelArrays(point, i);
  37421. if (names && point.name) {
  37422. names[x] = point.name;
  37423. }
  37424. dataOptions.splice(i, 0, options);
  37425. if (isInTheMiddle) {
  37426. series.data.splice(i, 0, null);
  37427. series.processData();
  37428. }
  37429. // Generate points to be added to the legend (#1329)
  37430. if (seriesOptions.legendType === 'point') {
  37431. series.generatePoints();
  37432. }
  37433. // Shift the first point off the parallel arrays
  37434. if (shift) {
  37435. if (data[0] && data[0].remove) {
  37436. data[0].remove(false);
  37437. }
  37438. else {
  37439. data.shift();
  37440. series.updateParallelArrays(point, 'shift');
  37441. dataOptions.shift();
  37442. }
  37443. }
  37444. // Fire event
  37445. if (withEvent !== false) {
  37446. fireEvent(series, 'addPoint', { point: point });
  37447. }
  37448. // redraw
  37449. series.isDirty = true;
  37450. series.isDirtyData = true;
  37451. if (redraw) {
  37452. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  37453. }
  37454. },
  37455. /**
  37456. * Remove a point from the series. Unlike the
  37457. * {@link Highcharts.Point#remove} method, this can also be done on a point
  37458. * that is not instanciated because it is outside the view or subject to
  37459. * Highstock data grouping.
  37460. *
  37461. * @sample highcharts/members/series-removepoint/
  37462. * Remove cropped point
  37463. *
  37464. * @function Highcharts.Series#removePoint
  37465. *
  37466. * @param {number} i
  37467. * The index of the point in the {@link Highcharts.Series.data|data}
  37468. * array.
  37469. *
  37470. * @param {boolean} [redraw=true]
  37471. * Whether to redraw the chart after the point is added. When
  37472. * removing more than one point, it is highly recommended that the
  37473. * `redraw` option be set to `false`, and instead {@link
  37474. * Highcharts.Chart#redraw} is explicitly called after the adding of
  37475. * points is finished.
  37476. *
  37477. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  37478. * Whether and optionally how the series should be animated.
  37479. *
  37480. * @return {void}
  37481. *
  37482. * @fires Highcharts.Point#event:remove
  37483. */
  37484. removePoint: function (i, redraw, animation) {
  37485. var series = this,
  37486. data = series.data,
  37487. point = data[i],
  37488. points = series.points,
  37489. chart = series.chart,
  37490. remove = function () {
  37491. if (points && points.length === data.length) { // #4935
  37492. points.splice(i, 1);
  37493. }
  37494. data.splice(i, 1);
  37495. series.options.data.splice(i, 1);
  37496. series.updateParallelArrays(point || { series: series }, 'splice', i, 1);
  37497. if (point) {
  37498. point.destroy();
  37499. }
  37500. // redraw
  37501. series.isDirty = true;
  37502. series.isDirtyData = true;
  37503. if (redraw) {
  37504. chart.redraw();
  37505. }
  37506. };
  37507. setAnimation(animation, chart);
  37508. redraw = pick(redraw, true);
  37509. // Fire the event with a default handler of removing the point
  37510. if (point) {
  37511. point.firePointEvent('remove', null, remove);
  37512. }
  37513. else {
  37514. remove();
  37515. }
  37516. },
  37517. /**
  37518. * Remove a series and optionally redraw the chart.
  37519. *
  37520. * @sample highcharts/members/series-remove/
  37521. * Remove first series from a button
  37522. *
  37523. * @function Highcharts.Series#remove
  37524. *
  37525. * @param {boolean} [redraw=true]
  37526. * Whether to redraw the chart or wait for an explicit call to
  37527. * {@link Highcharts.Chart#redraw}.
  37528. *
  37529. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  37530. * Whether to apply animation, and optionally animation
  37531. * configuration.
  37532. *
  37533. * @param {boolean} [withEvent=true]
  37534. * Used internally, whether to fire the series `remove` event.
  37535. *
  37536. * @return {void}
  37537. *
  37538. * @fires Highcharts.Series#event:remove
  37539. */
  37540. remove: function (redraw, animation, withEvent, keepEvents) {
  37541. var series = this,
  37542. chart = series.chart;
  37543. /**
  37544. * @private
  37545. */
  37546. function remove() {
  37547. // Destroy elements
  37548. series.destroy(keepEvents);
  37549. series.remove = null; // Prevent from doing again (#9097)
  37550. // Redraw
  37551. chart.isDirtyLegend = chart.isDirtyBox = true;
  37552. chart.linkSeries();
  37553. if (pick(redraw, true)) {
  37554. chart.redraw(animation);
  37555. }
  37556. }
  37557. // Fire the event with a default handler of removing the point
  37558. if (withEvent !== false) {
  37559. fireEvent(series, 'remove', null, remove);
  37560. }
  37561. else {
  37562. remove();
  37563. }
  37564. },
  37565. /**
  37566. * Update the series with a new set of options. For a clean and precise
  37567. * handling of new options, all methods and elements from the series are
  37568. * removed, and it is initialized from scratch. Therefore, this method is
  37569. * more performance expensive than some other utility methods like {@link
  37570. * Series#setData} or {@link Series#setVisible}.
  37571. *
  37572. * Note that `Series.update` may mutate the passed `data` options.
  37573. *
  37574. * @sample highcharts/members/series-update/
  37575. * Updating series options
  37576. * @sample maps/members/series-update/
  37577. * Update series options in Highmaps
  37578. *
  37579. * @function Highcharts.Series#update
  37580. *
  37581. * @param {Highcharts.SeriesOptionsType} options
  37582. * New options that will be merged with the series' existing options.
  37583. *
  37584. * @param {boolean} [redraw=true]
  37585. * Whether to redraw the chart after the series is altered. If doing
  37586. * more operations on the chart, it is a good idea to set redraw to
  37587. * false and call {@link Chart#redraw} after.
  37588. *
  37589. * @return {void}
  37590. *
  37591. * @fires Highcharts.Series#event:update
  37592. * @fires Highcharts.Series#event:afterUpdate
  37593. */
  37594. update: function (options, redraw) {
  37595. options = H.cleanRecursively(options, this.userOptions);
  37596. fireEvent(this, 'update', { options: options });
  37597. var series = this,
  37598. chart = series.chart,
  37599. // must use user options when changing type because series.options
  37600. // is merged in with type specific plotOptions
  37601. oldOptions = series.userOptions,
  37602. seriesOptions,
  37603. initialType = series.initialType || series.type,
  37604. newType = (options.type ||
  37605. oldOptions.type ||
  37606. chart.options.chart.type),
  37607. keepPoints = !(
  37608. // Indicators, histograms etc recalculate the data. It should be
  37609. // possible to omit this.
  37610. this.hasDerivedData ||
  37611. // Changes to data grouping requires new points in new groups
  37612. options.dataGrouping ||
  37613. // New type requires new point classes
  37614. (newType && newType !== this.type) ||
  37615. // New options affecting how the data points are built
  37616. typeof options.pointStart !== 'undefined' ||
  37617. options.pointInterval ||
  37618. options.pointIntervalUnit ||
  37619. options.keys),
  37620. initialSeriesProto = seriesTypes[initialType].prototype,
  37621. n,
  37622. groups = [
  37623. 'group',
  37624. 'markerGroup',
  37625. 'dataLabelsGroup',
  37626. 'transformGroup'
  37627. ],
  37628. preserve = [
  37629. 'eventOptions',
  37630. 'navigatorSeries',
  37631. 'baseSeries'
  37632. ],
  37633. // Animation must be enabled when calling update before the initial
  37634. // animation has first run. This happens when calling update
  37635. // directly after chart initialization, or when applying responsive
  37636. // rules (#6912).
  37637. animation = series.finishedAnimating && { animation: false },
  37638. kinds = {};
  37639. if (keepPoints) {
  37640. preserve.push('data', 'isDirtyData', 'points', 'processedXData', 'processedYData', 'xIncrement', 'cropped', '_hasPointMarkers', '_hasPointLabels',
  37641. // Map specific, consider moving it to series-specific preserve-
  37642. // properties (#10617)
  37643. 'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
  37644. if (options.visible !== false) {
  37645. preserve.push('area', 'graph');
  37646. }
  37647. series.parallelArrays.forEach(function (key) {
  37648. preserve.push(key + 'Data');
  37649. });
  37650. if (options.data) {
  37651. // setData uses dataSorting options so we need to update them
  37652. // earlier
  37653. if (options.dataSorting) {
  37654. extend(series.options.dataSorting, options.dataSorting);
  37655. }
  37656. this.setData(options.data, false);
  37657. }
  37658. }
  37659. // Do the merge, with some forced options
  37660. options = merge(oldOptions, animation, {
  37661. // When oldOptions.index is null it should't be cleared.
  37662. // Otherwise navigator series will have wrong indexes (#10193).
  37663. index: typeof oldOptions.index === 'undefined' ?
  37664. series.index : oldOptions.index,
  37665. pointStart: pick(
  37666. // when updating from blank (#7933)
  37667. oldOptions.pointStart,
  37668. // when updating after addPoint
  37669. series.xData[0])
  37670. }, (!keepPoints && { data: series.options.data }), options);
  37671. // Merge does not merge arrays, but replaces them. Since points were
  37672. // updated, `series.options.data` has correct merged options, use it:
  37673. if (keepPoints && options.data) {
  37674. options.data = series.options.data;
  37675. }
  37676. // Make sure preserved properties are not destroyed (#3094)
  37677. preserve = groups.concat(preserve);
  37678. preserve.forEach(function (prop) {
  37679. preserve[prop] = series[prop];
  37680. delete series[prop];
  37681. });
  37682. // Destroy the series and delete all properties. Reinsert all
  37683. // methods and properties from the new type prototype (#2270,
  37684. // #3719).
  37685. series.remove(false, null, false, true);
  37686. for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
  37687. series[n] = void 0;
  37688. }
  37689. if (seriesTypes[newType || initialType]) {
  37690. extend(series, seriesTypes[newType || initialType].prototype);
  37691. }
  37692. else {
  37693. error(17, true, chart, { missingModuleFor: (newType || initialType) });
  37694. }
  37695. // Re-register groups (#3094) and other preserved properties
  37696. preserve.forEach(function (prop) {
  37697. series[prop] = preserve[prop];
  37698. });
  37699. series.init(chart, options);
  37700. // Remove particular elements of the points. Check `series.options`
  37701. // because we need to consider the options being set on plotOptions as
  37702. // well.
  37703. if (keepPoints && this.points) {
  37704. seriesOptions = series.options;
  37705. // What kind of elements to destroy
  37706. if (seriesOptions.visible === false) {
  37707. kinds.graphic = 1;
  37708. kinds.dataLabel = 1;
  37709. }
  37710. else if (!series._hasPointLabels) {
  37711. var marker = seriesOptions.marker,
  37712. dataLabels = seriesOptions.dataLabels;
  37713. if (marker && (marker.enabled === false ||
  37714. 'symbol' in marker // #10870
  37715. )) {
  37716. kinds.graphic = 1;
  37717. }
  37718. if (dataLabels &&
  37719. dataLabels.enabled === false) {
  37720. kinds.dataLabel = 1;
  37721. }
  37722. }
  37723. this.points.forEach(function (point) {
  37724. if (point && point.series) {
  37725. point.resolveColor();
  37726. // Destroy elements in order to recreate based on updated
  37727. // series options.
  37728. if (Object.keys(kinds).length) {
  37729. point.destroyElements(kinds);
  37730. }
  37731. if (seriesOptions.showInLegend === false &&
  37732. point.legendItem) {
  37733. chart.legend.destroyItem(point);
  37734. }
  37735. }
  37736. }, this);
  37737. }
  37738. series.initialType = initialType;
  37739. chart.linkSeries(); // Links are lost in series.remove (#3028)
  37740. fireEvent(this, 'afterUpdate');
  37741. if (pick(redraw, true)) {
  37742. chart.redraw(keepPoints ? void 0 : false);
  37743. }
  37744. },
  37745. /**
  37746. * Used from within series.update
  37747. *
  37748. * @private
  37749. * @function Highcharts.Series#setName
  37750. *
  37751. * @param {string} name
  37752. *
  37753. * @return {void}
  37754. */
  37755. setName: function (name) {
  37756. this.name = this.options.name = this.userOptions.name = name;
  37757. this.chart.isDirtyLegend = true;
  37758. }
  37759. });
  37760. // Extend the Axis.prototype for dynamic methods
  37761. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  37762. /**
  37763. * Update an axis object with a new set of options. The options are merged
  37764. * with the existing options, so only new or altered options need to be
  37765. * specified.
  37766. *
  37767. * @sample highcharts/members/axis-update/
  37768. * Axis update demo
  37769. *
  37770. * @function Highcharts.Axis#update
  37771. *
  37772. * @param {Highcharts.AxisOptions} options
  37773. * The new options that will be merged in with existing options on
  37774. * the axis.
  37775. *
  37776. * @param {boolean} [redraw=true]
  37777. * Whether to redraw the chart after the axis is altered. If doing
  37778. * more operations on the chart, it is a good idea to set redraw to
  37779. * false and call {@link Chart#redraw} after.
  37780. *
  37781. * @return {void}
  37782. */
  37783. update: function (options, redraw) {
  37784. var chart = this.chart,
  37785. newEvents = ((options && options.events) || {});
  37786. options = merge(this.userOptions, options);
  37787. // Color Axis is not an array,
  37788. // This change is applied in the ColorAxis wrapper
  37789. if (chart.options[this.coll].indexOf) {
  37790. // Don't use this.options.index,
  37791. // StockChart has Axes in navigator too
  37792. chart.options[this.coll][chart.options[this.coll].indexOf(this.userOptions)] = options;
  37793. }
  37794. // Remove old events, if no new exist (#8161)
  37795. objectEach(chart.options[this.coll].events, function (fn, ev) {
  37796. if (typeof newEvents[ev] === 'undefined') {
  37797. newEvents[ev] = void 0;
  37798. }
  37799. });
  37800. this.destroy(true);
  37801. this.init(chart, extend(options, { events: newEvents }));
  37802. chart.isDirtyBox = true;
  37803. if (pick(redraw, true)) {
  37804. chart.redraw();
  37805. }
  37806. },
  37807. /**
  37808. * Remove the axis from the chart.
  37809. *
  37810. * @sample highcharts/members/chart-addaxis/
  37811. * Add and remove axes
  37812. *
  37813. * @function Highcharts.Axis#remove
  37814. *
  37815. * @param {boolean} [redraw=true]
  37816. * Whether to redraw the chart following the remove.
  37817. *
  37818. * @return {void}
  37819. */
  37820. remove: function (redraw) {
  37821. var chart = this.chart,
  37822. key = this.coll, // xAxis or yAxis
  37823. axisSeries = this.series,
  37824. i = axisSeries.length;
  37825. // Remove associated series (#2687)
  37826. while (i--) {
  37827. if (axisSeries[i]) {
  37828. axisSeries[i].remove(false);
  37829. }
  37830. }
  37831. // Remove the axis
  37832. erase(chart.axes, this);
  37833. erase(chart[key], this);
  37834. if (isArray(chart.options[key])) {
  37835. chart.options[key].splice(this.options.index, 1);
  37836. }
  37837. else { // color axis, #6488
  37838. delete chart.options[key];
  37839. }
  37840. chart[key].forEach(function (axis, i) {
  37841. // Re-index, #1706, #8075
  37842. axis.options.index = axis.userOptions.index = i;
  37843. });
  37844. this.destroy();
  37845. chart.isDirtyBox = true;
  37846. if (pick(redraw, true)) {
  37847. chart.redraw();
  37848. }
  37849. },
  37850. /**
  37851. * Update the axis title by options after render time.
  37852. *
  37853. * @sample highcharts/members/axis-settitle/
  37854. * Set a new Y axis title
  37855. *
  37856. * @function Highcharts.Axis#setTitle
  37857. *
  37858. * @param {Highcharts.AxisTitleOptions} titleOptions
  37859. * The additional title options.
  37860. *
  37861. * @param {boolean} [redraw=true]
  37862. * Whether to redraw the chart after setting the title.
  37863. *
  37864. * @return {void}
  37865. */
  37866. setTitle: function (titleOptions, redraw) {
  37867. this.update({ title: titleOptions }, redraw);
  37868. },
  37869. /**
  37870. * Set new axis categories and optionally redraw.
  37871. *
  37872. * @sample highcharts/members/axis-setcategories/
  37873. * Set categories by click on a button
  37874. *
  37875. * @function Highcharts.Axis#setCategories
  37876. *
  37877. * @param {Array<string>} categories
  37878. * The new categories.
  37879. *
  37880. * @param {boolean} [redraw=true]
  37881. * Whether to redraw the chart.
  37882. *
  37883. * @return {void}
  37884. */
  37885. setCategories: function (categories, redraw) {
  37886. this.update({ categories: categories }, redraw);
  37887. }
  37888. });
  37889. });
  37890. _registerModule(_modules, 'Series/AreaSeries.js', [_modules['Core/Globals.js'], _modules['Core/Color.js'], _modules['Mixins/LegendSymbol.js'], _modules['Core/Utilities.js']], function (H, Color, LegendSymbolMixin, U) {
  37891. /* *
  37892. *
  37893. * (c) 2010-2020 Torstein Honsi
  37894. *
  37895. * License: www.highcharts.com/license
  37896. *
  37897. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37898. *
  37899. * */
  37900. var color = Color.parse;
  37901. var objectEach = U.objectEach,
  37902. pick = U.pick,
  37903. seriesType = U.seriesType;
  37904. var Series = H.Series;
  37905. /**
  37906. * Area series type.
  37907. *
  37908. * @private
  37909. * @class
  37910. * @name Highcharts.seriesTypes.area
  37911. *
  37912. * @augments Highcharts.Series
  37913. */
  37914. seriesType('area', 'line',
  37915. /**
  37916. * The area series type.
  37917. *
  37918. * @sample {highcharts} highcharts/demo/area-basic/
  37919. * Area chart
  37920. * @sample {highstock} stock/demo/area/
  37921. * Area chart
  37922. *
  37923. * @extends plotOptions.line
  37924. * @excluding useOhlcData
  37925. * @product highcharts highstock
  37926. * @optionparent plotOptions.area
  37927. */
  37928. {
  37929. /**
  37930. * Fill color or gradient for the area. When `null`, the series' `color`
  37931. * is used with the series' `fillOpacity`.
  37932. *
  37933. * In styled mode, the fill color can be set with the `.highcharts-area`
  37934. * class name.
  37935. *
  37936. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  37937. * Null by default
  37938. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  37939. * Gradient
  37940. *
  37941. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  37942. * @product highcharts highstock
  37943. * @apioption plotOptions.area.fillColor
  37944. */
  37945. /**
  37946. * Fill opacity for the area. When you set an explicit `fillColor`,
  37947. * the `fillOpacity` is not applied. Instead, you should define the
  37948. * opacity in the `fillColor` with an rgba color definition. The
  37949. * `fillOpacity` setting, also the default setting, overrides the alpha
  37950. * component of the `color` setting.
  37951. *
  37952. * In styled mode, the fill opacity can be set with the
  37953. * `.highcharts-area` class name.
  37954. *
  37955. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  37956. * Automatic fill color and fill opacity of 0.1
  37957. *
  37958. * @type {number}
  37959. * @default {highcharts} 0.75
  37960. * @default {highstock} 0.75
  37961. * @product highcharts highstock
  37962. * @apioption plotOptions.area.fillOpacity
  37963. */
  37964. /**
  37965. * A separate color for the graph line. By default the line takes the
  37966. * `color` of the series, but the lineColor setting allows setting a
  37967. * separate color for the line without altering the `fillColor`.
  37968. *
  37969. * In styled mode, the line stroke can be set with the
  37970. * `.highcharts-graph` class name.
  37971. *
  37972. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  37973. * Dark gray line
  37974. *
  37975. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  37976. * @product highcharts highstock
  37977. * @apioption plotOptions.area.lineColor
  37978. */
  37979. /**
  37980. * A separate color for the negative part of the area.
  37981. *
  37982. * In styled mode, a negative color is set with the
  37983. * `.highcharts-negative` class name.
  37984. *
  37985. * @see [negativeColor](#plotOptions.area.negativeColor)
  37986. *
  37987. * @sample {highcharts} highcharts/css/series-negative-color/
  37988. * Negative color in styled mode
  37989. *
  37990. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  37991. * @since 3.0
  37992. * @product highcharts
  37993. * @apioption plotOptions.area.negativeFillColor
  37994. */
  37995. /**
  37996. * Whether the whole area or just the line should respond to mouseover
  37997. * tooltips and other mouse or touch events.
  37998. *
  37999. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  38000. * Display the tooltip when the area is hovered
  38001. *
  38002. * @type {boolean}
  38003. * @default false
  38004. * @since 1.1.6
  38005. * @product highcharts highstock
  38006. * @apioption plotOptions.area.trackByArea
  38007. */
  38008. /**
  38009. * The Y axis value to serve as the base for the area, for
  38010. * distinguishing between values above and below a threshold. The area
  38011. * between the graph and the threshold is filled.
  38012. *
  38013. * * If a number is given, the Y axis will scale to the threshold.
  38014. * * If `null`, the scaling behaves like a line series with fill between
  38015. * the graph and the Y axis minimum.
  38016. * * If `Infinity` or `-Infinity`, the area between the graph and the
  38017. * corresponding Y axis extreme is filled (since v6.1.0).
  38018. *
  38019. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  38020. * A threshold of 100
  38021. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  38022. * A threshold of Infinity
  38023. *
  38024. * @type {number|null}
  38025. * @since 2.0
  38026. * @product highcharts highstock
  38027. */
  38028. threshold: 0
  38029. },
  38030. /* eslint-disable valid-jsdoc */
  38031. /**
  38032. * @lends seriesTypes.area.prototype
  38033. */
  38034. {
  38035. singleStacks: false,
  38036. /**
  38037. * Return an array of stacked points, where null and missing points are
  38038. * replaced by dummy points in order for gaps to be drawn correctly in
  38039. * stacks.
  38040. * @private
  38041. */
  38042. getStackPoints: function (points) {
  38043. var series = this,
  38044. segment = [],
  38045. keys = [],
  38046. xAxis = this.xAxis,
  38047. yAxis = this.yAxis,
  38048. stack = yAxis.stacking.stacks[this.stackKey],
  38049. pointMap = {},
  38050. seriesIndex = series.index,
  38051. yAxisSeries = yAxis.series,
  38052. seriesLength = yAxisSeries.length,
  38053. visibleSeries,
  38054. upOrDown = pick(yAxis.options.reversedStacks,
  38055. true) ? 1 : -1,
  38056. i;
  38057. points = points || this.points;
  38058. if (this.options.stacking) {
  38059. for (i = 0; i < points.length; i++) {
  38060. // Reset after point update (#7326)
  38061. points[i].leftNull = points[i].rightNull = void 0;
  38062. // Create a map where we can quickly look up the points by
  38063. // their X values.
  38064. pointMap[points[i].x] = points[i];
  38065. }
  38066. // Sort the keys (#1651)
  38067. objectEach(stack, function (stackX, x) {
  38068. // nulled after switching between
  38069. // grouping and not (#1651, #2336)
  38070. if (stackX.total !== null) {
  38071. keys.push(x);
  38072. }
  38073. });
  38074. keys.sort(function (a, b) {
  38075. return a - b;
  38076. });
  38077. visibleSeries = yAxisSeries.map(function (s) {
  38078. return s.visible;
  38079. });
  38080. keys.forEach(function (x, idx) {
  38081. var y = 0,
  38082. stackPoint,
  38083. stackedValues;
  38084. if (pointMap[x] && !pointMap[x].isNull) {
  38085. segment.push(pointMap[x]);
  38086. // Find left and right cliff. -1 goes left, 1 goes
  38087. // right.
  38088. [-1, 1].forEach(function (direction) {
  38089. var nullName = direction === 1 ?
  38090. 'rightNull' :
  38091. 'leftNull',
  38092. cliffName = direction === 1 ?
  38093. 'rightCliff' :
  38094. 'leftCliff',
  38095. cliff = 0,
  38096. otherStack = stack[keys[idx + direction]];
  38097. // If there is a stack next to this one,
  38098. // to the left or to the right...
  38099. if (otherStack) {
  38100. i = seriesIndex;
  38101. // Can go either up or down,
  38102. // depending on reversedStacks
  38103. while (i >= 0 && i < seriesLength) {
  38104. stackPoint = otherStack.points[i];
  38105. if (!stackPoint) {
  38106. // If the next point in this series
  38107. // is missing, mark the point
  38108. // with point.leftNull or
  38109. // point.rightNull = true.
  38110. if (i === seriesIndex) {
  38111. pointMap[x][nullName] =
  38112. true;
  38113. // If there are missing points in
  38114. // the next stack in any of the
  38115. // series below this one, we need
  38116. // to substract the missing values
  38117. // and add a hiatus to the left or
  38118. // right.
  38119. }
  38120. else if (visibleSeries[i]) {
  38121. stackedValues =
  38122. stack[x].points[i];
  38123. if (stackedValues) {
  38124. cliff -=
  38125. stackedValues[1] -
  38126. stackedValues[0];
  38127. }
  38128. }
  38129. }
  38130. // When reversedStacks is true, loop up,
  38131. // else loop down
  38132. i += upOrDown;
  38133. }
  38134. }
  38135. pointMap[x][cliffName] = cliff;
  38136. });
  38137. // There is no point for this X value in this series, so we
  38138. // insert a dummy point in order for the areas to be drawn
  38139. // correctly.
  38140. }
  38141. else {
  38142. // Loop down the stack to find the series below this
  38143. // one that has a value (#1991)
  38144. i = seriesIndex;
  38145. while (i >= 0 && i < seriesLength) {
  38146. stackPoint = stack[x].points[i];
  38147. if (stackPoint) {
  38148. y = stackPoint[1];
  38149. break;
  38150. }
  38151. // When reversedStacks is true, loop up, else loop
  38152. // down
  38153. i += upOrDown;
  38154. }
  38155. y = yAxis.translate(// #6272
  38156. y, 0, 1, 0, 1);
  38157. segment.push({
  38158. isNull: true,
  38159. plotX: xAxis.translate(// #6272
  38160. x, 0, 0, 0, 1),
  38161. x: x,
  38162. plotY: y,
  38163. yBottom: y
  38164. });
  38165. }
  38166. });
  38167. }
  38168. return segment;
  38169. },
  38170. /**
  38171. * @private
  38172. */
  38173. getGraphPath: function (points) {
  38174. var getGraphPath = Series.prototype.getGraphPath, graphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, topPath, bottomPath, bottomPoints = [], graphPoints = [], seriesIndex = this.index, i, areaPath, plotX, stacks = yAxis.stacking.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
  38175. yAxis.getThreshold(options.threshold)), isNull, yBottom, connectNulls = pick(// #10574
  38176. options.connectNulls, stacking === 'percent'),
  38177. // To display null points in underlying stacked series, this
  38178. // series graph must be broken, and the area also fall down to
  38179. // fill the gap left by the null point. #2069
  38180. addDummyPoints = function (i, otherI, side) {
  38181. var point = points[i], stackedValues = stacking &&
  38182. stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0, top, bottom, isNull = true;
  38183. if (cliffVal || nullVal) {
  38184. top = (nullVal ?
  38185. stackedValues[0] :
  38186. stackedValues[1]) + cliffVal;
  38187. bottom = stackedValues[0] + cliffVal;
  38188. isNull = !!nullVal;
  38189. }
  38190. else if (!stacking &&
  38191. points[otherI] &&
  38192. points[otherI].isNull) {
  38193. top = bottom = threshold;
  38194. }
  38195. // Add to the top and bottom line of the area
  38196. if (typeof top !== 'undefined') {
  38197. graphPoints.push({
  38198. plotX: plotX,
  38199. plotY: top === null ?
  38200. translatedThreshold :
  38201. yAxis.getThreshold(top),
  38202. isNull: isNull,
  38203. isCliff: true
  38204. });
  38205. bottomPoints.push({
  38206. plotX: plotX,
  38207. plotY: bottom === null ?
  38208. translatedThreshold :
  38209. yAxis.getThreshold(bottom),
  38210. doCurve: false // #1041, gaps in areaspline areas
  38211. });
  38212. }
  38213. };
  38214. // Find what points to use
  38215. points = points || this.points;
  38216. // Fill in missing points
  38217. if (stacking) {
  38218. points = this.getStackPoints(points);
  38219. }
  38220. for (i = 0; i < points.length; i++) {
  38221. // Reset after series.update of stacking property (#12033)
  38222. if (!stacking) {
  38223. points[i].leftCliff = points[i].rightCliff =
  38224. points[i].leftNull = points[i].rightNull = void 0;
  38225. }
  38226. isNull = points[i].isNull;
  38227. plotX = pick(points[i].rectPlotX, points[i].plotX);
  38228. yBottom = stacking ? points[i].yBottom : translatedThreshold;
  38229. if (!isNull || connectNulls) {
  38230. if (!connectNulls) {
  38231. addDummyPoints(i, i - 1, 'left');
  38232. }
  38233. // Skip null point when stacking is false and connectNulls
  38234. // true
  38235. if (!(isNull && !stacking && connectNulls)) {
  38236. graphPoints.push(points[i]);
  38237. bottomPoints.push({
  38238. x: i,
  38239. plotX: plotX,
  38240. plotY: yBottom
  38241. });
  38242. }
  38243. if (!connectNulls) {
  38244. addDummyPoints(i, i + 1, 'right');
  38245. }
  38246. }
  38247. }
  38248. topPath = getGraphPath.call(this, graphPoints, true, true);
  38249. bottomPoints.reversed = true;
  38250. bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  38251. var firstBottomPoint = bottomPath[0];
  38252. if (firstBottomPoint && firstBottomPoint[0] === 'M') {
  38253. bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]];
  38254. }
  38255. areaPath = topPath.concat(bottomPath);
  38256. // TODO: don't set leftCliff and rightCliff when connectNulls?
  38257. graphPath = getGraphPath
  38258. .call(this, graphPoints, false, connectNulls);
  38259. areaPath.xMap = topPath.xMap;
  38260. this.areaPath = areaPath;
  38261. return graphPath;
  38262. },
  38263. /**
  38264. * Draw the graph and the underlying area. This method calls the Series
  38265. * base function and adds the area. The areaPath is calculated in the
  38266. * getSegmentPath method called from Series.prototype.drawGraph.
  38267. * @private
  38268. */
  38269. drawGraph: function () {
  38270. // Define or reset areaPath
  38271. this.areaPath = [];
  38272. // Call the base method
  38273. Series.prototype.drawGraph.apply(this);
  38274. // Define local variables
  38275. var series = this,
  38276. areaPath = this.areaPath,
  38277. options = this.options,
  38278. zones = this.zones,
  38279. props = [[
  38280. 'area',
  38281. 'highcharts-area',
  38282. this.color,
  38283. options.fillColor
  38284. ]]; // area name, main color, fill color
  38285. zones.forEach(function (zone,
  38286. i) {
  38287. props.push([
  38288. 'zone-area-' + i,
  38289. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  38290. zone.className,
  38291. zone.color || series.color,
  38292. zone.fillColor || options.fillColor
  38293. ]);
  38294. });
  38295. props.forEach(function (prop) {
  38296. var areaKey = prop[0],
  38297. area = series[areaKey],
  38298. verb = area ? 'animate' : 'attr',
  38299. attribs = {};
  38300. // Create or update the area
  38301. if (area) { // update
  38302. area.endX = series.preventGraphAnimation ?
  38303. null :
  38304. areaPath.xMap;
  38305. area.animate({ d: areaPath });
  38306. }
  38307. else { // create
  38308. attribs.zIndex = 0; // #1069
  38309. area = series[areaKey] = series.chart.renderer
  38310. .path(areaPath)
  38311. .addClass(prop[1])
  38312. .add(series.group);
  38313. area.isArea = true;
  38314. }
  38315. if (!series.chart.styledMode) {
  38316. attribs.fill = pick(prop[3], color(prop[2])
  38317. .setOpacity(pick(options.fillOpacity, 0.75))
  38318. .get());
  38319. }
  38320. area[verb](attribs);
  38321. area.startX = areaPath.xMap;
  38322. area.shiftUnit = options.step ? 2 : 1;
  38323. });
  38324. },
  38325. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  38326. });
  38327. /* eslint-enable valid-jsdoc */
  38328. /**
  38329. * A `area` series. If the [type](#series.area.type) option is not
  38330. * specified, it is inherited from [chart.type](#chart.type).
  38331. *
  38332. * @extends series,plotOptions.area
  38333. * @excluding dataParser, dataURL, useOhlcData
  38334. * @product highcharts highstock
  38335. * @apioption series.area
  38336. */
  38337. /**
  38338. * An array of data points for the series. For the `area` series type,
  38339. * points can be given in the following ways:
  38340. *
  38341. * 1. An array of numerical values. In this case, the numerical values will be
  38342. * interpreted as `y` options. The `x` values will be automatically
  38343. * calculated, either starting at 0 and incremented by 1, or from
  38344. * `pointStart` * and `pointInterval` given in the series options. If the
  38345. * axis has categories, these will be used. Example:
  38346. * ```js
  38347. * data: [0, 5, 3, 5]
  38348. * ```
  38349. *
  38350. * 2. An array of arrays with 2 values. In this case, the values correspond to
  38351. * `x,y`. If the first value is a string, it is applied as the name of the
  38352. * point, and the `x` value is inferred.
  38353. * ```js
  38354. * data: [
  38355. * [0, 9],
  38356. * [1, 7],
  38357. * [2, 6]
  38358. * ]
  38359. * ```
  38360. *
  38361. * 3. An array of objects with named values. The following snippet shows only a
  38362. * few settings, see the complete options set below. If the total number of
  38363. * data points exceeds the series'
  38364. * [turboThreshold](#series.area.turboThreshold), this option is not
  38365. * available.
  38366. * ```js
  38367. * data: [{
  38368. * x: 1,
  38369. * y: 9,
  38370. * name: "Point2",
  38371. * color: "#00FF00"
  38372. * }, {
  38373. * x: 1,
  38374. * y: 6,
  38375. * name: "Point1",
  38376. * color: "#FF00FF"
  38377. * }]
  38378. * ```
  38379. *
  38380. * @sample {highcharts} highcharts/chart/reflow-true/
  38381. * Numerical values
  38382. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  38383. * Arrays of numeric x and y
  38384. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  38385. * Arrays of datetime x and y
  38386. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  38387. * Arrays of point.name and y
  38388. * @sample {highcharts} highcharts/series/data-array-of-objects/
  38389. * Config objects
  38390. *
  38391. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  38392. * @extends series.line.data
  38393. * @product highcharts highstock
  38394. * @apioption series.area.data
  38395. */
  38396. ''; // adds doclets above to transpilat
  38397. });
  38398. _registerModule(_modules, 'Series/SplineSeries.js', [_modules['Core/Utilities.js']], function (U) {
  38399. /* *
  38400. *
  38401. * (c) 2010-2020 Torstein Honsi
  38402. *
  38403. * License: www.highcharts.com/license
  38404. *
  38405. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38406. *
  38407. * */
  38408. var pick = U.pick,
  38409. seriesType = U.seriesType;
  38410. /**
  38411. * Spline series type.
  38412. *
  38413. * @private
  38414. * @class
  38415. * @name Highcharts.seriesTypes.spline
  38416. *
  38417. * @augments Highcarts.Series
  38418. */
  38419. seriesType('spline', 'line',
  38420. /**
  38421. * A spline series is a special type of line series, where the segments
  38422. * between the data points are smoothed.
  38423. *
  38424. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  38425. * Spline chart
  38426. * @sample {highstock} stock/demo/spline/
  38427. * Spline chart
  38428. *
  38429. * @extends plotOptions.series
  38430. * @excluding step, boostThreshold, boostBlending
  38431. * @product highcharts highstock
  38432. * @optionparent plotOptions.spline
  38433. */
  38434. {},
  38435. /**
  38436. * @lends seriesTypes.spline.prototype
  38437. */
  38438. {
  38439. /* eslint-disable valid-jsdoc */
  38440. /**
  38441. * Get the spline segment from a given point's previous neighbour to the
  38442. * given point.
  38443. *
  38444. * @private
  38445. * @function Highcharts.seriesTypes.spline#getPointSpline
  38446. *
  38447. * @param {Array<Highcharts.Point>}
  38448. *
  38449. * @param {Highcharts.Point} point
  38450. *
  38451. * @param {number} i
  38452. *
  38453. * @return {Highcharts.SVGPathArray}
  38454. */
  38455. getPointSpline: function (points, point, i) {
  38456. var
  38457. // 1 means control points midway between points, 2 means 1/3
  38458. // from the point, 3 is 1/4 etc
  38459. smoothing = 1.5,
  38460. denom = smoothing + 1,
  38461. plotX = point.plotX || 0,
  38462. plotY = point.plotY || 0,
  38463. lastPoint = points[i - 1],
  38464. nextPoint = points[i + 1],
  38465. leftContX,
  38466. leftContY,
  38467. rightContX,
  38468. rightContY,
  38469. ret;
  38470. /**
  38471. * @private
  38472. */
  38473. function doCurve(otherPoint) {
  38474. return otherPoint &&
  38475. !otherPoint.isNull &&
  38476. otherPoint.doCurve !== false &&
  38477. // #6387, area splines next to null:
  38478. !point.isCliff;
  38479. }
  38480. // Find control points
  38481. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  38482. var lastX = lastPoint.plotX || 0,
  38483. lastY = lastPoint.plotY || 0,
  38484. nextX = nextPoint.plotX || 0,
  38485. nextY = nextPoint.plotY || 0,
  38486. correction = 0;
  38487. leftContX = (smoothing * plotX + lastX) / denom;
  38488. leftContY = (smoothing * plotY + lastY) / denom;
  38489. rightContX = (smoothing * plotX + nextX) / denom;
  38490. rightContY = (smoothing * plotY + nextY) / denom;
  38491. // Have the two control points make a straight line through main
  38492. // point
  38493. if (rightContX !== leftContX) { // #5016, division by zero
  38494. correction = (((rightContY - leftContY) *
  38495. (rightContX - plotX)) /
  38496. (rightContX - leftContX) + plotY - rightContY);
  38497. }
  38498. leftContY += correction;
  38499. rightContY += correction;
  38500. // to prevent false extremes, check that control points are
  38501. // between neighbouring points' y values
  38502. if (leftContY > lastY && leftContY > plotY) {
  38503. leftContY = Math.max(lastY, plotY);
  38504. // mirror of left control point
  38505. rightContY = 2 * plotY - leftContY;
  38506. }
  38507. else if (leftContY < lastY && leftContY < plotY) {
  38508. leftContY = Math.min(lastY, plotY);
  38509. rightContY = 2 * plotY - leftContY;
  38510. }
  38511. if (rightContY > nextY && rightContY > plotY) {
  38512. rightContY = Math.max(nextY, plotY);
  38513. leftContY = 2 * plotY - rightContY;
  38514. }
  38515. else if (rightContY < nextY && rightContY < plotY) {
  38516. rightContY = Math.min(nextY, plotY);
  38517. leftContY = 2 * plotY - rightContY;
  38518. }
  38519. // record for drawing in next point
  38520. point.rightContX = rightContX;
  38521. point.rightContY = rightContY;
  38522. }
  38523. // Visualize control points for debugging
  38524. /*
  38525. if (leftContX) {
  38526. this.chart.renderer.circle(
  38527. leftContX + this.chart.plotLeft,
  38528. leftContY + this.chart.plotTop,
  38529. 2
  38530. )
  38531. .attr({
  38532. stroke: 'red',
  38533. 'stroke-width': 2,
  38534. fill: 'none',
  38535. zIndex: 9
  38536. })
  38537. .add();
  38538. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  38539. leftContY + this.chart.plotTop,
  38540. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  38541. .attr({
  38542. stroke: 'red',
  38543. 'stroke-width': 2,
  38544. zIndex: 9
  38545. })
  38546. .add();
  38547. }
  38548. if (rightContX) {
  38549. this.chart.renderer.circle(
  38550. rightContX + this.chart.plotLeft,
  38551. rightContY + this.chart.plotTop,
  38552. 2
  38553. )
  38554. .attr({
  38555. stroke: 'green',
  38556. 'stroke-width': 2,
  38557. fill: 'none',
  38558. zIndex: 9
  38559. })
  38560. .add();
  38561. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  38562. rightContY + this.chart.plotTop,
  38563. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  38564. .attr({
  38565. stroke: 'green',
  38566. 'stroke-width': 2,
  38567. zIndex: 9
  38568. })
  38569. .add();
  38570. }
  38571. // */
  38572. ret = [
  38573. 'C',
  38574. pick(lastPoint.rightContX, lastPoint.plotX, 0),
  38575. pick(lastPoint.rightContY, lastPoint.plotY, 0),
  38576. pick(leftContX, plotX, 0),
  38577. pick(leftContY, plotY, 0),
  38578. plotX,
  38579. plotY
  38580. ];
  38581. // reset for updating series later
  38582. lastPoint.rightContX = lastPoint.rightContY = void 0;
  38583. return ret;
  38584. }
  38585. /* eslint-enable valid-jsdoc */
  38586. });
  38587. /**
  38588. * A `spline` series. If the [type](#series.spline.type) option is
  38589. * not specified, it is inherited from [chart.type](#chart.type).
  38590. *
  38591. * @extends series,plotOptions.spline
  38592. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  38593. * @product highcharts highstock
  38594. * @apioption series.spline
  38595. */
  38596. /**
  38597. * An array of data points for the series. For the `spline` series type,
  38598. * points can be given in the following ways:
  38599. *
  38600. * 1. An array of numerical values. In this case, the numerical values will be
  38601. * interpreted as `y` options. The `x` values will be automatically
  38602. * calculated, either starting at 0 and incremented by 1, or from
  38603. * `pointStart` and `pointInterval` given in the series options. If the axis
  38604. * has categories, these will be used. Example:
  38605. * ```js
  38606. * data: [0, 5, 3, 5]
  38607. * ```
  38608. *
  38609. * 2. An array of arrays with 2 values. In this case, the values correspond to
  38610. * `x,y`. If the first value is a string, it is applied as the name of the
  38611. * point, and the `x` value is inferred.
  38612. * ```js
  38613. * data: [
  38614. * [0, 9],
  38615. * [1, 2],
  38616. * [2, 8]
  38617. * ]
  38618. * ```
  38619. *
  38620. * 3. An array of objects with named values. The following snippet shows only a
  38621. * few settings, see the complete options set below. If the total number of
  38622. * data points exceeds the series'
  38623. * [turboThreshold](#series.spline.turboThreshold),
  38624. * this option is not available.
  38625. * ```js
  38626. * data: [{
  38627. * x: 1,
  38628. * y: 9,
  38629. * name: "Point2",
  38630. * color: "#00FF00"
  38631. * }, {
  38632. * x: 1,
  38633. * y: 0,
  38634. * name: "Point1",
  38635. * color: "#FF00FF"
  38636. * }]
  38637. * ```
  38638. *
  38639. * @sample {highcharts} highcharts/chart/reflow-true/
  38640. * Numerical values
  38641. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  38642. * Arrays of numeric x and y
  38643. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  38644. * Arrays of datetime x and y
  38645. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  38646. * Arrays of point.name and y
  38647. * @sample {highcharts} highcharts/series/data-array-of-objects/
  38648. * Config objects
  38649. *
  38650. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  38651. * @extends series.line.data
  38652. * @product highcharts highstock
  38653. * @apioption series.spline.data
  38654. */
  38655. ''; // adds doclets above intro transpilat
  38656. });
  38657. _registerModule(_modules, 'Series/AreaSplineSeries.js', [_modules['Core/Globals.js'], _modules['Mixins/LegendSymbol.js'], _modules['Core/Options.js'], _modules['Core/Utilities.js']], function (H, LegendSymbolMixin, O, U) {
  38658. /* *
  38659. *
  38660. * (c) 2010-2020 Torstein Honsi
  38661. *
  38662. * License: www.highcharts.com/license
  38663. *
  38664. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38665. *
  38666. * */
  38667. var defaultOptions = O.defaultOptions;
  38668. var seriesType = U.seriesType;
  38669. var areaProto = H.seriesTypes.area.prototype;
  38670. /**
  38671. * AreaSpline series type.
  38672. *
  38673. * @private
  38674. * @class
  38675. * @name Highcharts.seriesTypes.areaspline
  38676. *
  38677. * @augments Highcharts.Series
  38678. */
  38679. seriesType('areaspline', 'spline',
  38680. /**
  38681. * The area spline series is an area series where the graph between the
  38682. * points is smoothed into a spline.
  38683. *
  38684. * @sample {highcharts} highcharts/demo/areaspline/
  38685. * Area spline chart
  38686. * @sample {highstock} stock/demo/areaspline/
  38687. * Area spline chart
  38688. *
  38689. * @extends plotOptions.area
  38690. * @excluding step, boostThreshold, boostBlending
  38691. * @product highcharts highstock
  38692. * @apioption plotOptions.areaspline
  38693. */
  38694. defaultOptions.plotOptions.area, {
  38695. getStackPoints: areaProto.getStackPoints,
  38696. getGraphPath: areaProto.getGraphPath,
  38697. drawGraph: areaProto.drawGraph,
  38698. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  38699. });
  38700. /**
  38701. * A `areaspline` series. If the [type](#series.areaspline.type) option
  38702. * is not specified, it is inherited from [chart.type](#chart.type).
  38703. *
  38704. *
  38705. * @extends series,plotOptions.areaspline
  38706. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  38707. * @product highcharts highstock
  38708. * @apioption series.areaspline
  38709. */
  38710. /**
  38711. * An array of data points for the series. For the `areaspline` series
  38712. * type, points can be given in the following ways:
  38713. *
  38714. * 1. An array of numerical values. In this case, the numerical values will be
  38715. * interpreted as `y` options. The `x` values will be automatically
  38716. * calculated, either starting at 0 and incremented by 1, or from
  38717. * `pointStart` and `pointInterval` given in the series options. If the axis
  38718. * has categories, these will be used. Example:
  38719. * ```js
  38720. * data: [0, 5, 3, 5]
  38721. * ```
  38722. *
  38723. * 2. An array of arrays with 2 values. In this case, the values correspond to
  38724. * `x,y`. If the first value is a string, it is applied as the name of the
  38725. * point, and the `x` value is inferred.
  38726. * ```js
  38727. * data: [
  38728. * [0, 10],
  38729. * [1, 9],
  38730. * [2, 3]
  38731. * ]
  38732. * ```
  38733. *
  38734. * 3. An array of objects with named values. The following snippet shows only a
  38735. * few settings, see the complete options set below. If the total number of
  38736. * data points exceeds the series'
  38737. * [turboThreshold](#series.areaspline.turboThreshold), this option is not
  38738. * available.
  38739. * ```js
  38740. * data: [{
  38741. * x: 1,
  38742. * y: 4,
  38743. * name: "Point2",
  38744. * color: "#00FF00"
  38745. * }, {
  38746. * x: 1,
  38747. * y: 4,
  38748. * name: "Point1",
  38749. * color: "#FF00FF"
  38750. * }]
  38751. * ```
  38752. *
  38753. * @sample {highcharts} highcharts/chart/reflow-true/
  38754. * Numerical values
  38755. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  38756. * Arrays of numeric x and y
  38757. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  38758. * Arrays of datetime x and y
  38759. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  38760. * Arrays of point.name and y
  38761. * @sample {highcharts} highcharts/series/data-array-of-objects/
  38762. * Config objects
  38763. *
  38764. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  38765. * @extends series.line.data
  38766. * @product highcharts highstock
  38767. * @apioption series.areaspline.data
  38768. */
  38769. ''; // adds doclets above into transpilat
  38770. });
  38771. _registerModule(_modules, 'Series/ColumnSeries.js', [_modules['Core/Globals.js'], _modules['Core/Color.js'], _modules['Mixins/LegendSymbol.js'], _modules['Core/Utilities.js']], function (H, Color, LegendSymbolMixin, U) {
  38772. /* *
  38773. *
  38774. * (c) 2010-2020 Torstein Honsi
  38775. *
  38776. * License: www.highcharts.com/license
  38777. *
  38778. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38779. *
  38780. * */
  38781. /**
  38782. * Adjusted width and x offset of the columns for grouping.
  38783. *
  38784. * @private
  38785. * @interface Highcharts.ColumnMetricsObject
  38786. */ /**
  38787. * Width of the columns.
  38788. * @name Highcharts.ColumnMetricsObject#width
  38789. * @type {number}
  38790. */ /**
  38791. * Offset of the columns.
  38792. * @name Highcharts.ColumnMetricsObject#offset
  38793. * @type {number}
  38794. */
  38795. ''; // detach doclets above
  38796. var color = Color.parse;
  38797. var animObject = U.animObject,
  38798. clamp = U.clamp,
  38799. defined = U.defined,
  38800. extend = U.extend,
  38801. isNumber = U.isNumber,
  38802. merge = U.merge,
  38803. pick = U.pick,
  38804. seriesType = U.seriesType,
  38805. objectEach = U.objectEach;
  38806. var noop = H.noop,
  38807. Series = H.Series,
  38808. svg = H.svg;
  38809. /**
  38810. * The column series type.
  38811. *
  38812. * @private
  38813. * @class
  38814. * @name Highcharts.seriesTypes.column
  38815. *
  38816. * @augments Highcharts.Series
  38817. */
  38818. seriesType('column', 'line',
  38819. /**
  38820. * Column series display one column per value along an X axis.
  38821. *
  38822. * @sample {highcharts} highcharts/demo/column-basic/
  38823. * Column chart
  38824. * @sample {highstock} stock/demo/column/
  38825. * Column chart
  38826. *
  38827. * @extends plotOptions.line
  38828. * @excluding connectEnds, connectNulls, gapSize, gapUnit, linecap,
  38829. * lineWidth, marker, step, useOhlcData
  38830. * @product highcharts highstock
  38831. * @optionparent plotOptions.column
  38832. */
  38833. {
  38834. /**
  38835. * The corner radius of the border surrounding each column or bar.
  38836. *
  38837. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  38838. * Rounded columns
  38839. *
  38840. * @product highcharts highstock gantt
  38841. *
  38842. * @private
  38843. */
  38844. borderRadius: 0,
  38845. /**
  38846. * When using automatic point colors pulled from the global
  38847. * [colors](colors) or series-specific
  38848. * [plotOptions.column.colors](series.colors) collections, this option
  38849. * determines whether the chart should receive one color per series or
  38850. * one color per point.
  38851. *
  38852. * In styled mode, the `colors` or `series.colors` arrays are not
  38853. * supported, and instead this option gives the points individual color
  38854. * class names on the form `highcharts-color-{n}`.
  38855. *
  38856. * @see [series colors](#plotOptions.column.colors)
  38857. *
  38858. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
  38859. * False by default
  38860. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
  38861. * True
  38862. *
  38863. * @type {boolean}
  38864. * @default false
  38865. * @since 2.0
  38866. * @product highcharts highstock gantt
  38867. * @apioption plotOptions.column.colorByPoint
  38868. */
  38869. /**
  38870. * A series specific or series type specific color set to apply instead
  38871. * of the global [colors](#colors) when [colorByPoint](
  38872. * #plotOptions.column.colorByPoint) is true.
  38873. *
  38874. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  38875. * @since 3.0
  38876. * @product highcharts highstock gantt
  38877. * @apioption plotOptions.column.colors
  38878. */
  38879. /**
  38880. * When `true`, the columns will center in the category, ignoring null
  38881. * or missing points. When `false`, space will be reserved for null or
  38882. * missing points.
  38883. *
  38884. * @sample {highcharts} highcharts/series-column/centerincategory/
  38885. * Center in category
  38886. *
  38887. * @since 8.0.1
  38888. * @product highcharts highstock gantt
  38889. */
  38890. centerInCategory: false,
  38891. /**
  38892. * Padding between each value groups, in x axis units.
  38893. *
  38894. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
  38895. * 0.2 by default
  38896. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
  38897. * No group padding - all columns are evenly spaced
  38898. *
  38899. * @product highcharts highstock gantt
  38900. *
  38901. * @private
  38902. */
  38903. groupPadding: 0.2,
  38904. /**
  38905. * Whether to group non-stacked columns or to let them render
  38906. * independent of each other. Non-grouped columns will be laid out
  38907. * individually and overlap each other.
  38908. *
  38909. * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
  38910. * Grouping disabled
  38911. * @sample {highstock} highcharts/plotoptions/column-grouping-false/
  38912. * Grouping disabled
  38913. *
  38914. * @type {boolean}
  38915. * @default true
  38916. * @since 2.3.0
  38917. * @product highcharts highstock gantt
  38918. * @apioption plotOptions.column.grouping
  38919. */
  38920. /**
  38921. * @ignore-option
  38922. * @private
  38923. */
  38924. marker: null,
  38925. /**
  38926. * The maximum allowed pixel width for a column, translated to the
  38927. * height of a bar in a bar chart. This prevents the columns from
  38928. * becoming too wide when there is a small number of points in the
  38929. * chart.
  38930. *
  38931. * @see [pointWidth](#plotOptions.column.pointWidth)
  38932. *
  38933. * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
  38934. * Limited to 50
  38935. * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
  38936. * Limited to 50
  38937. *
  38938. * @type {number}
  38939. * @since 4.1.8
  38940. * @product highcharts highstock gantt
  38941. * @apioption plotOptions.column.maxPointWidth
  38942. */
  38943. /**
  38944. * Padding between each column or bar, in x axis units.
  38945. *
  38946. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
  38947. * 0.1 by default
  38948. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
  38949. * 0.25
  38950. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
  38951. * 0 for tightly packed columns
  38952. *
  38953. * @product highcharts highstock gantt
  38954. *
  38955. * @private
  38956. */
  38957. pointPadding: 0.1,
  38958. /**
  38959. * A pixel value specifying a fixed width for each column or bar point.
  38960. * When `null`, the width is calculated from the `pointPadding` and
  38961. * `groupPadding`. The width effects the dimension that is not based on
  38962. * the point value. For column series it is the hoizontal length and for
  38963. * bar series it is the vertical length.
  38964. *
  38965. * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
  38966. *
  38967. * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
  38968. * 20px wide columns regardless of chart width or the amount of
  38969. * data points
  38970. *
  38971. * @type {number}
  38972. * @since 1.2.5
  38973. * @product highcharts highstock gantt
  38974. * @apioption plotOptions.column.pointWidth
  38975. */
  38976. /**
  38977. * A pixel value specifying a fixed width for the column or bar.
  38978. * Overrides pointWidth on the series.
  38979. *
  38980. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  38981. *
  38982. * @type {number}
  38983. * @default undefined
  38984. * @since 7.0.0
  38985. * @product highcharts highstock gantt
  38986. * @apioption series.column.data.pointWidth
  38987. */
  38988. /**
  38989. * The minimal height for a column or width for a bar. By default,
  38990. * 0 values are not shown. To visualize a 0 (or close to zero) point,
  38991. * set the minimal point length to a pixel value like 3\. In stacked
  38992. * column charts, minPointLength might not be respected for tightly
  38993. * packed values.
  38994. *
  38995. * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
  38996. * Zero base value
  38997. * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
  38998. * Positive and negative close to zero values
  38999. *
  39000. * @product highcharts highstock gantt
  39001. *
  39002. * @private
  39003. */
  39004. minPointLength: 0,
  39005. /**
  39006. * When the series contains less points than the crop threshold, all
  39007. * points are drawn, event if the points fall outside the visible plot
  39008. * area at the current zoom. The advantage of drawing all points
  39009. * (including markers and columns), is that animation is performed on
  39010. * updates. On the other hand, when the series contains more points than
  39011. * the crop threshold, the series data is cropped to only contain points
  39012. * that fall within the plot area. The advantage of cropping away
  39013. * invisible points is to increase performance on large series.
  39014. *
  39015. * @product highcharts highstock gantt
  39016. *
  39017. * @private
  39018. */
  39019. cropThreshold: 50,
  39020. /**
  39021. * The X axis range that each point is valid for. This determines the
  39022. * width of the column. On a categorized axis, the range will be 1
  39023. * by default (one category unit). On linear and datetime axes, the
  39024. * range will be computed as the distance between the two closest data
  39025. * points.
  39026. *
  39027. * The default `null` means it is computed automatically, but this
  39028. * option can be used to override the automatic value.
  39029. *
  39030. * This option is set by default to 1 if data sorting is enabled.
  39031. *
  39032. * @sample {highcharts} highcharts/plotoptions/column-pointrange/
  39033. * Set the point range to one day on a data set with one week
  39034. * between the points
  39035. *
  39036. * @type {number|null}
  39037. * @since 2.3
  39038. * @product highcharts highstock gantt
  39039. *
  39040. * @private
  39041. */
  39042. pointRange: null,
  39043. states: {
  39044. /**
  39045. * Options for the hovered point. These settings override the normal
  39046. * state options when a point is moused over or touched.
  39047. *
  39048. * @extends plotOptions.series.states.hover
  39049. * @excluding halo, lineWidth, lineWidthPlus, marker
  39050. * @product highcharts highstock gantt
  39051. */
  39052. hover: {
  39053. /** @ignore-option */
  39054. halo: false,
  39055. /**
  39056. * A specific border color for the hovered point. Defaults to
  39057. * inherit the normal state border color.
  39058. *
  39059. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39060. * @product highcharts gantt
  39061. * @apioption plotOptions.column.states.hover.borderColor
  39062. */
  39063. /**
  39064. * A specific color for the hovered point.
  39065. *
  39066. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39067. * @product highcharts gantt
  39068. * @apioption plotOptions.column.states.hover.color
  39069. */
  39070. /**
  39071. * How much to brighten the point on interaction. Requires the
  39072. * main color to be defined in hex or rgb(a) format.
  39073. *
  39074. * In styled mode, the hover brightening is by default replaced
  39075. * with a fill-opacity set in the `.highcharts-point:hover`
  39076. * rule.
  39077. *
  39078. * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
  39079. * Brighten by 0.5
  39080. *
  39081. * @product highcharts highstock gantt
  39082. */
  39083. brightness: 0.1
  39084. },
  39085. /**
  39086. * Options for the selected point. These settings override the
  39087. * normal state options when a point is selected.
  39088. *
  39089. * @extends plotOptions.series.states.select
  39090. * @excluding halo, lineWidth, lineWidthPlus, marker
  39091. * @product highcharts highstock gantt
  39092. */
  39093. select: {
  39094. /**
  39095. * A specific color for the selected point.
  39096. *
  39097. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39098. * @default #cccccc
  39099. * @product highcharts highstock gantt
  39100. */
  39101. color: '#cccccc',
  39102. /**
  39103. * A specific border color for the selected point.
  39104. *
  39105. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39106. * @default #000000
  39107. * @product highcharts highstock gantt
  39108. */
  39109. borderColor: '#000000'
  39110. }
  39111. },
  39112. dataLabels: {
  39113. align: void 0,
  39114. verticalAlign: void 0,
  39115. /**
  39116. * The y position offset of the label relative to the point in
  39117. * pixels.
  39118. *
  39119. * @type {number}
  39120. */
  39121. y: void 0
  39122. },
  39123. // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
  39124. /**
  39125. * @ignore-option
  39126. * @private
  39127. */
  39128. startFromThreshold: true,
  39129. stickyTracking: false,
  39130. tooltip: {
  39131. distance: 6
  39132. },
  39133. /**
  39134. * The Y axis value to serve as the base for the columns, for
  39135. * distinguishing between values above and below a threshold. If `null`,
  39136. * the columns extend from the padding Y axis minimum.
  39137. *
  39138. * @type {number|null}
  39139. * @since 2.0
  39140. * @product highcharts
  39141. *
  39142. * @private
  39143. */
  39144. threshold: 0,
  39145. /**
  39146. * The width of the border surrounding each column or bar. Defaults to
  39147. * `1` when there is room for a border, but to `0` when the columns are
  39148. * so dense that a border would cover the next column.
  39149. *
  39150. * In styled mode, the stroke width can be set with the
  39151. * `.highcharts-point` rule.
  39152. *
  39153. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  39154. * 2px black border
  39155. *
  39156. * @type {number}
  39157. * @default undefined
  39158. * @product highcharts highstock gantt
  39159. * @apioption plotOptions.column.borderWidth
  39160. */
  39161. /**
  39162. * The color of the border surrounding each column or bar.
  39163. *
  39164. * In styled mode, the border stroke can be set with the
  39165. * `.highcharts-point` rule.
  39166. *
  39167. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  39168. * Dark gray border
  39169. *
  39170. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39171. * @default #ffffff
  39172. * @product highcharts highstock gantt
  39173. *
  39174. * @private
  39175. */
  39176. borderColor: '#ffffff'
  39177. },
  39178. /**
  39179. * @lends seriesTypes.column.prototype
  39180. */
  39181. {
  39182. cropShoulder: 0,
  39183. // When tooltip is not shared, this series (and derivatives) requires
  39184. // direct touch/hover. KD-tree does not apply.
  39185. directTouch: true,
  39186. trackerGroups: ['group', 'dataLabelsGroup'],
  39187. // use separate negative stacks, unlike area stacks where a negative
  39188. // point is substracted from previous (#1910)
  39189. negStacks: true,
  39190. /* eslint-disable valid-jsdoc */
  39191. /**
  39192. * Initialize the series. Extends the basic Series.init method by
  39193. * marking other series of the same type as dirty.
  39194. *
  39195. * @private
  39196. * @function Highcharts.seriesTypes.column#init
  39197. * @return {void}
  39198. */
  39199. init: function () {
  39200. Series.prototype.init.apply(this, arguments);
  39201. var series = this,
  39202. chart = series.chart;
  39203. // if the series is added dynamically, force redraw of other
  39204. // series affected by a new column
  39205. if (chart.hasRendered) {
  39206. chart.series.forEach(function (otherSeries) {
  39207. if (otherSeries.type === series.type) {
  39208. otherSeries.isDirty = true;
  39209. }
  39210. });
  39211. }
  39212. },
  39213. /**
  39214. * Return the width and x offset of the columns adjusted for grouping,
  39215. * groupPadding, pointPadding, pointWidth etc.
  39216. *
  39217. * @private
  39218. * @function Highcharts.seriesTypes.column#getColumnMetrics
  39219. * @return {Highcharts.ColumnMetricsObject}
  39220. */
  39221. getColumnMetrics: function () {
  39222. var series = this,
  39223. options = series.options,
  39224. xAxis = series.xAxis,
  39225. yAxis = series.yAxis,
  39226. reversedStacks = xAxis.options.reversedStacks,
  39227. // Keep backward compatibility: reversed xAxis had reversed
  39228. // stacks
  39229. reverseStacks = (xAxis.reversed && !reversedStacks) ||
  39230. (!xAxis.reversed && reversedStacks),
  39231. stackKey,
  39232. stackGroups = {},
  39233. columnCount = 0;
  39234. // Get the total number of column type series. This is called on
  39235. // every series. Consider moving this logic to a chart.orderStacks()
  39236. // function and call it on init, addSeries and removeSeries
  39237. if (options.grouping === false) {
  39238. columnCount = 1;
  39239. }
  39240. else {
  39241. series.chart.series.forEach(function (otherSeries) {
  39242. var otherYAxis = otherSeries.yAxis,
  39243. otherOptions = otherSeries.options,
  39244. columnIndex;
  39245. if (otherSeries.type === series.type &&
  39246. (otherSeries.visible ||
  39247. !series.chart.options.chart
  39248. .ignoreHiddenSeries) &&
  39249. yAxis.len === otherYAxis.len &&
  39250. yAxis.pos === otherYAxis.pos) { // #642, #2086
  39251. if (otherOptions.stacking && otherOptions.stacking !== 'group') {
  39252. stackKey = otherSeries.stackKey;
  39253. if (typeof stackGroups[stackKey] ===
  39254. 'undefined') {
  39255. stackGroups[stackKey] = columnCount++;
  39256. }
  39257. columnIndex = stackGroups[stackKey];
  39258. }
  39259. else if (otherOptions.grouping !== false) { // #1162
  39260. columnIndex = columnCount++;
  39261. }
  39262. otherSeries.columnIndex = columnIndex;
  39263. }
  39264. });
  39265. }
  39266. var categoryWidth = Math.min(Math.abs(xAxis.transA) * ((xAxis.ordinal && xAxis.ordinal.slope) ||
  39267. options.pointRange ||
  39268. xAxis.closestPointRange ||
  39269. xAxis.tickInterval ||
  39270. 1), // #2610
  39271. xAxis.len // #1535
  39272. ),
  39273. groupPadding = categoryWidth * options.groupPadding,
  39274. groupWidth = categoryWidth - 2 * groupPadding,
  39275. pointOffsetWidth = groupWidth / (columnCount || 1),
  39276. pointWidth = Math.min(options.maxPointWidth || xAxis.len,
  39277. pick(options.pointWidth,
  39278. pointOffsetWidth * (1 - 2 * options.pointPadding))),
  39279. pointPadding = (pointOffsetWidth - pointWidth) / 2,
  39280. // #1251, #3737
  39281. colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0),
  39282. pointXOffset = pointPadding +
  39283. (groupPadding +
  39284. colIndex * pointOffsetWidth -
  39285. (categoryWidth / 2)) * (reverseStacks ? -1 : 1);
  39286. // Save it for reading in linked series (Error bars particularly)
  39287. series.columnMetrics = {
  39288. width: pointWidth,
  39289. offset: pointXOffset,
  39290. paddedWidth: pointOffsetWidth,
  39291. columnCount: columnCount
  39292. };
  39293. return series.columnMetrics;
  39294. },
  39295. /**
  39296. * Make the columns crisp. The edges are rounded to the nearest full
  39297. * pixel.
  39298. *
  39299. * @private
  39300. * @function Highcharts.seriesTypes.column#crispCol
  39301. * @param {number} x
  39302. * @param {number} y
  39303. * @param {number} w
  39304. * @param {number} h
  39305. * @return {Highcharts.BBoxObject}
  39306. */
  39307. crispCol: function (x, y, w, h) {
  39308. var chart = this.chart,
  39309. borderWidth = this.borderWidth,
  39310. xCrisp = -(borderWidth % 2 ? 0.5 : 0),
  39311. yCrisp = borderWidth % 2 ? 0.5 : 1,
  39312. right,
  39313. bottom,
  39314. fromTop;
  39315. if (chart.inverted && chart.renderer.isVML) {
  39316. yCrisp += 1;
  39317. }
  39318. // Horizontal. We need to first compute the exact right edge, then
  39319. // round it and compute the width from there.
  39320. if (this.options.crisp) {
  39321. right = Math.round(x + w) + xCrisp;
  39322. x = Math.round(x) + xCrisp;
  39323. w = right - x;
  39324. }
  39325. // Vertical
  39326. bottom = Math.round(y + h) + yCrisp;
  39327. fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
  39328. y = Math.round(y) + yCrisp;
  39329. h = bottom - y;
  39330. // Top edges are exceptions
  39331. if (fromTop && h) { // #5146
  39332. y -= 1;
  39333. h += 1;
  39334. }
  39335. return {
  39336. x: x,
  39337. y: y,
  39338. width: w,
  39339. height: h
  39340. };
  39341. },
  39342. /**
  39343. * Adjust for missing columns, according to the `centerInCategory`
  39344. * option. Missing columns are either single points or stacks where the
  39345. * point or points are either missing or null.
  39346. *
  39347. * @private
  39348. * @function Highcharts.seriesTypes.column#adjustForMissingColumns
  39349. * @param {number} x
  39350. * The x coordinate of the column, left side
  39351. * @param {number} pointWidth
  39352. * The pointWidth, already computed upstream
  39353. * @param {Highcharts.ColumnPoint} point
  39354. * The point instance
  39355. * @param {Highcharts.ColumnMetricsObject} metrics
  39356. * The series-wide column metrics
  39357. * @return {number}
  39358. * The adjusted x position, or the original if not adjusted
  39359. */
  39360. adjustForMissingColumns: function (x, pointWidth, point, metrics) {
  39361. var _this = this;
  39362. var stacking = this.options.stacking;
  39363. if (!point.isNull && metrics.columnCount > 1) {
  39364. var indexInCategory_1 = 0;
  39365. var totalInCategory_1 = 0;
  39366. // Loop over all the stacks on the Y axis. When stacking is
  39367. // enabled, these are real point stacks. When stacking is not
  39368. // enabled, but `centerInCategory` is true, there is one stack
  39369. // handling the grouping of points in each category. This is
  39370. // done in the `setGroupedPoints` function.
  39371. objectEach(this.yAxis.stacking && this.yAxis.stacking.stacks, function (stack) {
  39372. if (typeof point.x === 'number') {
  39373. var stackItem = stack[point.x.toString()];
  39374. if (stackItem) {
  39375. var pointValues = stackItem.points[_this.index],
  39376. total = stackItem.total;
  39377. // If true `stacking` is enabled, count the
  39378. // total number of non-null stacks in the
  39379. // category, and note which index this point is
  39380. // within those stacks.
  39381. if (stacking) {
  39382. if (pointValues) {
  39383. indexInCategory_1 = totalInCategory_1;
  39384. }
  39385. if (stackItem.hasValidPoints) {
  39386. totalInCategory_1++;
  39387. }
  39388. // If `stacking` is not enabled, look for the
  39389. // index and total of the `group` stack.
  39390. }
  39391. else if (H.isArray(pointValues)) {
  39392. indexInCategory_1 = pointValues[1];
  39393. totalInCategory_1 = total || 0;
  39394. }
  39395. }
  39396. }
  39397. });
  39398. // Compute the adjusted x position
  39399. var boxWidth = (totalInCategory_1 - 1) * metrics.paddedWidth +
  39400. pointWidth;
  39401. x = (point.plotX || 0) + boxWidth / 2 - pointWidth -
  39402. indexInCategory_1 * metrics.paddedWidth;
  39403. }
  39404. return x;
  39405. },
  39406. /**
  39407. * Translate each point to the plot area coordinate system and find
  39408. * shape positions
  39409. *
  39410. * @private
  39411. * @function Highcharts.seriesTypes.column#translate
  39412. */
  39413. translate: function () {
  39414. var series = this,
  39415. chart = series.chart,
  39416. options = series.options,
  39417. dense = series.dense =
  39418. series.closestPointRange * series.xAxis.transA < 2,
  39419. borderWidth = series.borderWidth = pick(options.borderWidth,
  39420. dense ? 0 : 1 // #3635
  39421. ),
  39422. xAxis = series.xAxis,
  39423. yAxis = series.yAxis,
  39424. threshold = options.threshold,
  39425. translatedThreshold = series.translatedThreshold =
  39426. yAxis.getThreshold(threshold),
  39427. minPointLength = pick(options.minPointLength, 5),
  39428. metrics = series.getColumnMetrics(),
  39429. seriesPointWidth = metrics.width,
  39430. // postprocessed for border width
  39431. seriesBarW = series.barW =
  39432. Math.max(seriesPointWidth, 1 + 2 * borderWidth),
  39433. seriesXOffset = series.pointXOffset = metrics.offset,
  39434. dataMin = series.dataMin,
  39435. dataMax = series.dataMax;
  39436. if (chart.inverted) {
  39437. translatedThreshold -= 0.5; // #3355
  39438. }
  39439. // When the pointPadding is 0, we want the columns to be packed
  39440. // tightly, so we allow individual columns to have individual sizes.
  39441. // When pointPadding is greater, we strive for equal-width columns
  39442. // (#2694).
  39443. if (options.pointPadding) {
  39444. seriesBarW = Math.ceil(seriesBarW);
  39445. }
  39446. Series.prototype.translate.apply(series);
  39447. // Record the new values
  39448. series.points.forEach(function (point) {
  39449. var yBottom = pick(point.yBottom,
  39450. translatedThreshold),
  39451. safeDistance = 999 + Math.abs(yBottom),
  39452. pointWidth = seriesPointWidth,
  39453. plotX = point.plotX || 0,
  39454. // Don't draw too far outside plot area (#1303, #2241,
  39455. // #4264)
  39456. plotY = clamp(point.plotY, -safeDistance,
  39457. yAxis.len + safeDistance),
  39458. barX = plotX + seriesXOffset,
  39459. barW = seriesBarW,
  39460. barY = Math.min(plotY,
  39461. yBottom),
  39462. up,
  39463. barH = Math.max(plotY,
  39464. yBottom) - barY;
  39465. // Handle options.minPointLength
  39466. if (minPointLength && Math.abs(barH) < minPointLength) {
  39467. barH = minPointLength;
  39468. up = (!yAxis.reversed && !point.negative) ||
  39469. (yAxis.reversed && point.negative);
  39470. // Reverse zeros if there's no positive value in the series
  39471. // in visible range (#7046)
  39472. if (isNumber(threshold) &&
  39473. isNumber(dataMax) &&
  39474. point.y === threshold &&
  39475. dataMax <= threshold &&
  39476. // and if there's room for it (#7311)
  39477. (yAxis.min || 0) < threshold &&
  39478. // if all points are the same value (i.e zero) not draw
  39479. // as negative points (#10646)
  39480. dataMin !== dataMax) {
  39481. up = !up;
  39482. }
  39483. // If stacked...
  39484. barY = (Math.abs(barY - translatedThreshold) > minPointLength ?
  39485. // ...keep position
  39486. yBottom - minPointLength :
  39487. // #1485, #4051
  39488. translatedThreshold -
  39489. (up ? minPointLength : 0));
  39490. }
  39491. // Handle point.options.pointWidth
  39492. // @todo Handle grouping/stacking too. Calculate offset properly
  39493. if (defined(point.options.pointWidth)) {
  39494. pointWidth = barW =
  39495. Math.ceil(point.options.pointWidth);
  39496. barX -= Math.round((pointWidth - seriesPointWidth) / 2);
  39497. }
  39498. // Adjust for null or missing points
  39499. if (options.centerInCategory) {
  39500. barX = series.adjustForMissingColumns(barX, pointWidth, point, metrics);
  39501. }
  39502. // Cache for access in polar
  39503. point.barX = barX;
  39504. point.pointWidth = pointWidth;
  39505. // Fix the tooltip on center of grouped columns (#1216, #424,
  39506. // #3648)
  39507. point.tooltipPos = chart.inverted ?
  39508. [
  39509. yAxis.len + yAxis.pos - chart.plotLeft - plotY,
  39510. xAxis.len + xAxis.pos - chart.plotTop - (plotX || 0) - seriesXOffset - barW / 2,
  39511. barH
  39512. ] :
  39513. [barX + barW / 2, plotY + yAxis.pos -
  39514. chart.plotTop, barH];
  39515. // Register shape type and arguments to be used in drawPoints
  39516. // Allow shapeType defined on pointClass level
  39517. point.shapeType =
  39518. series.pointClass.prototype.shapeType || 'rect';
  39519. point.shapeArgs = series.crispCol.apply(series, point.isNull ?
  39520. // #3169, drilldown from null must have a position to work
  39521. // from #6585, dataLabel should be placed on xAxis, not
  39522. // floating in the middle of the chart
  39523. [barX, translatedThreshold, barW, 0] :
  39524. [barX, barY, barW, barH]);
  39525. });
  39526. },
  39527. getSymbol: noop,
  39528. /**
  39529. * Use a solid rectangle like the area series types
  39530. *
  39531. * @private
  39532. * @function Highcharts.seriesTypes.column#drawLegendSymbol
  39533. *
  39534. * @param {Highcharts.Legend} legend
  39535. * The legend object
  39536. *
  39537. * @param {Highcharts.Series|Highcharts.Point} item
  39538. * The series (this) or point
  39539. */
  39540. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  39541. /**
  39542. * Columns have no graph
  39543. *
  39544. * @private
  39545. * @function Highcharts.seriesTypes.column#drawGraph
  39546. */
  39547. drawGraph: function () {
  39548. this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data');
  39549. },
  39550. /**
  39551. * Get presentational attributes
  39552. *
  39553. * @private
  39554. * @function Highcharts.seriesTypes.column#pointAttribs
  39555. *
  39556. * @param {Highcharts.ColumnPoint} point
  39557. *
  39558. * @param {string} state
  39559. *
  39560. * @return {Highcharts.SVGAttributes}
  39561. */
  39562. pointAttribs: function (point, state) {
  39563. var options = this.options, stateOptions, ret, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth', fill = (point && point.color) || this.color,
  39564. // set to fill when borderColor null:
  39565. stroke = ((point && point[strokeOption]) ||
  39566. options[strokeOption] ||
  39567. this.color ||
  39568. fill), strokeWidth = (point && point[strokeWidthOption]) ||
  39569. options[strokeWidthOption] ||
  39570. this[strokeWidthOption] || 0, dashstyle = (point && point.options.dashStyle) || options.dashStyle, opacity = pick(point && point.opacity, options.opacity, 1), zone, brightness;
  39571. // Handle zone colors
  39572. if (point && this.zones.length) {
  39573. zone = point.getZone();
  39574. // When zones are present, don't use point.color (#4267).
  39575. // Changed order (#6527), added support for colorAxis (#10670)
  39576. fill = (point.options.color ||
  39577. (zone && (zone.color || point.nonZonedColor)) ||
  39578. this.color);
  39579. if (zone) {
  39580. stroke = zone.borderColor || stroke;
  39581. dashstyle = zone.dashStyle || dashstyle;
  39582. strokeWidth = zone.borderWidth || strokeWidth;
  39583. }
  39584. }
  39585. // Select or hover states
  39586. if (state && point) {
  39587. stateOptions = merge(options.states[state],
  39588. // #6401
  39589. point.options.states &&
  39590. point.options.states[state] ||
  39591. {});
  39592. brightness = stateOptions.brightness;
  39593. fill =
  39594. stateOptions.color || (typeof brightness !== 'undefined' &&
  39595. color(fill)
  39596. .brighten(stateOptions.brightness)
  39597. .get()) || fill;
  39598. stroke = stateOptions[strokeOption] || stroke;
  39599. strokeWidth =
  39600. stateOptions[strokeWidthOption] || strokeWidth;
  39601. dashstyle = stateOptions.dashStyle || dashstyle;
  39602. opacity = pick(stateOptions.opacity, opacity);
  39603. }
  39604. ret = {
  39605. fill: fill,
  39606. stroke: stroke,
  39607. 'stroke-width': strokeWidth,
  39608. opacity: opacity
  39609. };
  39610. if (dashstyle) {
  39611. ret.dashstyle = dashstyle;
  39612. }
  39613. return ret;
  39614. },
  39615. /**
  39616. * Draw the columns. For bars, the series.group is rotated, so the same
  39617. * coordinates apply for columns and bars. This method is inherited by
  39618. * scatter series.
  39619. *
  39620. * @private
  39621. * @function Highcharts.seriesTypes.column#drawPoints
  39622. */
  39623. drawPoints: function () {
  39624. var series = this,
  39625. chart = this.chart,
  39626. options = series.options,
  39627. renderer = chart.renderer,
  39628. animationLimit = options.animationLimit || 250,
  39629. shapeArgs;
  39630. // draw the columns
  39631. series.points.forEach(function (point) {
  39632. var plotY = point.plotY,
  39633. graphic = point.graphic,
  39634. hasGraphic = !!graphic,
  39635. verb = graphic && chart.pointCount < animationLimit ?
  39636. 'animate' : 'attr';
  39637. if (isNumber(plotY) && point.y !== null) {
  39638. shapeArgs = point.shapeArgs;
  39639. // When updating a series between 2d and 3d or cartesian and
  39640. // polar, the shape type changes.
  39641. if (graphic && point.hasNewShapeType()) {
  39642. graphic = graphic.destroy();
  39643. }
  39644. // Set starting position for point sliding animation.
  39645. if (series.enabledDataSorting) {
  39646. point.startXPos = series.xAxis.reversed ?
  39647. -(shapeArgs ? shapeArgs.width : 0) :
  39648. series.xAxis.width;
  39649. }
  39650. if (!graphic) {
  39651. point.graphic = graphic =
  39652. renderer[point.shapeType](shapeArgs)
  39653. .add(point.group || series.group);
  39654. if (graphic &&
  39655. series.enabledDataSorting &&
  39656. chart.hasRendered &&
  39657. chart.pointCount < animationLimit) {
  39658. graphic.attr({
  39659. x: point.startXPos
  39660. });
  39661. hasGraphic = true;
  39662. verb = 'animate';
  39663. }
  39664. }
  39665. if (graphic && hasGraphic) { // update
  39666. graphic[verb](merge(shapeArgs));
  39667. }
  39668. // Border radius is not stylable (#6900)
  39669. if (options.borderRadius) {
  39670. graphic[verb]({
  39671. r: options.borderRadius
  39672. });
  39673. }
  39674. // Presentational
  39675. if (!chart.styledMode) {
  39676. graphic[verb](series.pointAttribs(point, (point.selected && 'select')))
  39677. .shadow(point.allowShadow !== false && options.shadow, null, options.stacking && !options.borderRadius);
  39678. }
  39679. graphic.addClass(point.getClassName(), true);
  39680. }
  39681. else if (graphic) {
  39682. point.graphic = graphic.destroy(); // #1269
  39683. }
  39684. });
  39685. },
  39686. /**
  39687. * Animate the column heights one by one from zero.
  39688. *
  39689. * @private
  39690. * @function Highcharts.seriesTypes.column#animate
  39691. *
  39692. * @param {boolean} init
  39693. * Whether to initialize the animation or run it
  39694. */
  39695. animate: function (init) {
  39696. var series = this,
  39697. yAxis = this.yAxis,
  39698. options = series.options,
  39699. inverted = this.chart.inverted,
  39700. attr = {},
  39701. translateProp = inverted ? 'translateX' : 'translateY',
  39702. translateStart,
  39703. translatedThreshold;
  39704. if (init) {
  39705. attr.scaleY = 0.001;
  39706. translatedThreshold = clamp(yAxis.toPixels(options.threshold), yAxis.pos, yAxis.pos + yAxis.len);
  39707. if (inverted) {
  39708. attr.translateX = translatedThreshold - yAxis.len;
  39709. }
  39710. else {
  39711. attr.translateY = translatedThreshold;
  39712. }
  39713. // apply finnal clipping (used in Highstock) (#7083)
  39714. // animation is done by scaleY, so cliping is for panes
  39715. if (series.clipBox) {
  39716. series.setClip();
  39717. }
  39718. series.group.attr(attr);
  39719. }
  39720. else { // run the animation
  39721. translateStart = series.group.attr(translateProp);
  39722. series.group.animate({ scaleY: 1 }, extend(animObject(series.options.animation), {
  39723. // Do the scale synchronously to ensure smooth
  39724. // updating (#5030, #7228)
  39725. step: function (val, fx) {
  39726. if (series.group) {
  39727. attr[translateProp] = translateStart +
  39728. fx.pos * (yAxis.pos - translateStart);
  39729. series.group.attr(attr);
  39730. }
  39731. }
  39732. }));
  39733. }
  39734. },
  39735. /**
  39736. * Remove this series from the chart
  39737. *
  39738. * @private
  39739. * @function Highcharts.seriesTypes.column#remove
  39740. */
  39741. remove: function () {
  39742. var series = this,
  39743. chart = series.chart;
  39744. // column and bar series affects other series of the same type
  39745. // as they are either stacked or grouped
  39746. if (chart.hasRendered) {
  39747. chart.series.forEach(function (otherSeries) {
  39748. if (otherSeries.type === series.type) {
  39749. otherSeries.isDirty = true;
  39750. }
  39751. });
  39752. }
  39753. Series.prototype.remove.apply(series, arguments);
  39754. }
  39755. });
  39756. /* eslint-enable valid-jsdoc */
  39757. /**
  39758. * A `column` series. If the [type](#series.column.type) option is
  39759. * not specified, it is inherited from [chart.type](#chart.type).
  39760. *
  39761. * @extends series,plotOptions.column
  39762. * @excluding connectNulls, dataParser, dataURL, gapSize, gapUnit, linecap,
  39763. * lineWidth, marker, connectEnds, step
  39764. * @product highcharts highstock
  39765. * @apioption series.column
  39766. */
  39767. /**
  39768. * An array of data points for the series. For the `column` series type,
  39769. * points can be given in the following ways:
  39770. *
  39771. * 1. An array of numerical values. In this case, the numerical values will be
  39772. * interpreted as `y` options. The `x` values will be automatically
  39773. * calculated, either starting at 0 and incremented by 1, or from
  39774. * `pointStart` and `pointInterval` given in the series options. If the axis
  39775. * has categories, these will be used. Example:
  39776. * ```js
  39777. * data: [0, 5, 3, 5]
  39778. * ```
  39779. *
  39780. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39781. * `x,y`. If the first value is a string, it is applied as the name of the
  39782. * point, and the `x` value is inferred.
  39783. * ```js
  39784. * data: [
  39785. * [0, 6],
  39786. * [1, 2],
  39787. * [2, 6]
  39788. * ]
  39789. * ```
  39790. *
  39791. * 3. An array of objects with named values. The following snippet shows only a
  39792. * few settings, see the complete options set below. If the total number of
  39793. * data points exceeds the series'
  39794. * [turboThreshold](#series.column.turboThreshold), this option is not
  39795. * available.
  39796. * ```js
  39797. * data: [{
  39798. * x: 1,
  39799. * y: 9,
  39800. * name: "Point2",
  39801. * color: "#00FF00"
  39802. * }, {
  39803. * x: 1,
  39804. * y: 6,
  39805. * name: "Point1",
  39806. * color: "#FF00FF"
  39807. * }]
  39808. * ```
  39809. *
  39810. * @sample {highcharts} highcharts/chart/reflow-true/
  39811. * Numerical values
  39812. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39813. * Arrays of numeric x and y
  39814. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39815. * Arrays of datetime x and y
  39816. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39817. * Arrays of point.name and y
  39818. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39819. * Config objects
  39820. *
  39821. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39822. * @extends series.line.data
  39823. * @excluding marker
  39824. * @product highcharts highstock
  39825. * @apioption series.column.data
  39826. */
  39827. /**
  39828. * The color of the border surrounding the column or bar.
  39829. *
  39830. * In styled mode, the border stroke can be set with the `.highcharts-point`
  39831. * rule.
  39832. *
  39833. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  39834. * Dark gray border
  39835. *
  39836. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39837. * @product highcharts highstock
  39838. * @apioption series.column.data.borderColor
  39839. */
  39840. /**
  39841. * The width of the border surrounding the column or bar.
  39842. *
  39843. * In styled mode, the stroke width can be set with the `.highcharts-point`
  39844. * rule.
  39845. *
  39846. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  39847. * 2px black border
  39848. *
  39849. * @type {number}
  39850. * @product highcharts highstock
  39851. * @apioption series.column.data.borderWidth
  39852. */
  39853. /**
  39854. * A name for the dash style to use for the column or bar. Overrides
  39855. * dashStyle on the series.
  39856. *
  39857. * In styled mode, the stroke dash-array can be set with the same classes as
  39858. * listed under [data.color](#series.column.data.color).
  39859. *
  39860. * @see [series.pointWidth](#plotOptions.column.dashStyle)
  39861. *
  39862. * @type {Highcharts.DashStyleValue}
  39863. * @apioption series.column.data.dashStyle
  39864. */
  39865. /**
  39866. * A pixel value specifying a fixed width for the column or bar. Overrides
  39867. * pointWidth on the series. The width effects the dimension that is not based
  39868. * on the point value.
  39869. *
  39870. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  39871. *
  39872. * @type {number}
  39873. * @apioption series.column.data.pointWidth
  39874. */
  39875. /**
  39876. * @excluding halo, lineWidth, lineWidthPlus, marker
  39877. * @product highcharts highstock
  39878. * @apioption series.column.states.hover
  39879. */
  39880. /**
  39881. * @excluding halo, lineWidth, lineWidthPlus, marker
  39882. * @product highcharts highstock
  39883. * @apioption series.column.states.select
  39884. */
  39885. ''; // includes above doclets in transpilat
  39886. });
  39887. _registerModule(_modules, 'Series/BarSeries.js', [_modules['Core/Utilities.js']], function (U) {
  39888. /* *
  39889. *
  39890. * (c) 2010-2020 Torstein Honsi
  39891. *
  39892. * License: www.highcharts.com/license
  39893. *
  39894. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39895. *
  39896. * */
  39897. var seriesType = U.seriesType;
  39898. /**
  39899. * Bar series type.
  39900. *
  39901. * @private
  39902. * @class
  39903. * @name Highcharts.seriesTypes.bar
  39904. *
  39905. * @augments Highcharts.Series
  39906. */
  39907. seriesType('bar', 'column',
  39908. /**
  39909. * A bar series is a special type of column series where the columns are
  39910. * horizontal.
  39911. *
  39912. * @sample highcharts/demo/bar-basic/
  39913. * Bar chart
  39914. *
  39915. * @extends plotOptions.column
  39916. * @product highcharts
  39917. * @apioption plotOptions.bar
  39918. */
  39919. /**
  39920. * @ignore
  39921. */
  39922. null, {
  39923. inverted: true
  39924. });
  39925. /**
  39926. * A `bar` series. If the [type](#series.bar.type) option is not specified,
  39927. * it is inherited from [chart.type](#chart.type).
  39928. *
  39929. * @extends series,plotOptions.bar
  39930. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  39931. * linecap, lineWidth, marker, connectEnds, step
  39932. * @product highcharts
  39933. * @apioption series.bar
  39934. */
  39935. /**
  39936. * An array of data points for the series. For the `bar` series type,
  39937. * points can be given in the following ways:
  39938. *
  39939. * 1. An array of numerical values. In this case, the numerical values will be
  39940. * interpreted as `y` options. The `x` values will be automatically
  39941. * calculated, either starting at 0 and incremented by 1, or from
  39942. * `pointStart` and `pointInterval` given in the series options. If the axis
  39943. * has categories, these will be used. Example:
  39944. * ```js
  39945. * data: [0, 5, 3, 5]
  39946. * ```
  39947. *
  39948. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39949. * `x,y`. If the first value is a string, it is applied as the name of the
  39950. * point, and the `x` value is inferred.
  39951. * ```js
  39952. * data: [
  39953. * [0, 5],
  39954. * [1, 10],
  39955. * [2, 3]
  39956. * ]
  39957. * ```
  39958. *
  39959. * 3. An array of objects with named values. The following snippet shows only a
  39960. * few settings, see the complete options set below. If the total number of
  39961. * data points exceeds the series'
  39962. * [turboThreshold](#series.bar.turboThreshold), this option is not
  39963. * available.
  39964. * ```js
  39965. * data: [{
  39966. * x: 1,
  39967. * y: 1,
  39968. * name: "Point2",
  39969. * color: "#00FF00"
  39970. * }, {
  39971. * x: 1,
  39972. * y: 10,
  39973. * name: "Point1",
  39974. * color: "#FF00FF"
  39975. * }]
  39976. * ```
  39977. *
  39978. * @sample {highcharts} highcharts/chart/reflow-true/
  39979. * Numerical values
  39980. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39981. * Arrays of numeric x and y
  39982. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39983. * Arrays of datetime x and y
  39984. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39985. * Arrays of point.name and y
  39986. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39987. * Config objects
  39988. *
  39989. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39990. * @extends series.column.data
  39991. * @product highcharts
  39992. * @apioption series.bar.data
  39993. */
  39994. /**
  39995. * @excluding halo,lineWidth,lineWidthPlus,marker
  39996. * @product highcharts highstock
  39997. * @apioption series.bar.states.hover
  39998. */
  39999. /**
  40000. * @excluding halo,lineWidth,lineWidthPlus,marker
  40001. * @product highcharts highstock
  40002. * @apioption series.bar.states.select
  40003. */
  40004. ''; // gets doclets above into transpilat
  40005. });
  40006. _registerModule(_modules, 'Series/ScatterSeries.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  40007. /* *
  40008. *
  40009. * (c) 2010-2020 Torstein Honsi
  40010. *
  40011. * License: www.highcharts.com/license
  40012. *
  40013. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40014. *
  40015. * */
  40016. var addEvent = U.addEvent,
  40017. seriesType = U.seriesType;
  40018. var Series = H.Series;
  40019. /**
  40020. * Scatter series type.
  40021. *
  40022. * @private
  40023. * @class
  40024. * @name Highcharts.seriesTypes.scatter
  40025. *
  40026. * @augments Highcharts.Series
  40027. */
  40028. seriesType('scatter', 'line',
  40029. /**
  40030. * A scatter plot uses cartesian coordinates to display values for two
  40031. * variables for a set of data.
  40032. *
  40033. * @sample {highcharts} highcharts/demo/scatter/
  40034. * Scatter plot
  40035. *
  40036. * @extends plotOptions.line
  40037. * @excluding cropThreshold, pointPlacement, shadow, useOhlcData
  40038. * @product highcharts highstock
  40039. * @optionparent plotOptions.scatter
  40040. */
  40041. {
  40042. /**
  40043. * The width of the line connecting the data points.
  40044. *
  40045. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
  40046. * 0 by default
  40047. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
  40048. * 1px
  40049. *
  40050. * @product highcharts highstock
  40051. */
  40052. lineWidth: 0,
  40053. findNearestPointBy: 'xy',
  40054. /**
  40055. * Apply a jitter effect for the rendered markers. When plotting
  40056. * discrete values, a little random noise may help telling the points
  40057. * apart. The jitter setting applies a random displacement of up to `n`
  40058. * axis units in either direction. So for example on a horizontal X
  40059. * axis, setting the `jitter.x` to 0.24 will render the point in a
  40060. * random position between 0.24 units to the left and 0.24 units to the
  40061. * right of the true axis position. On a category axis, setting it to
  40062. * 0.5 will fill up the bin and make the data appear continuous.
  40063. *
  40064. * When rendered on top of a box plot or a column series, a jitter value
  40065. * of 0.24 will correspond to the underlying series' default
  40066. * [groupPadding](
  40067. * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
  40068. * and [pointPadding](
  40069. * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
  40070. * settings.
  40071. *
  40072. * @sample {highcharts} highcharts/series-scatter/jitter
  40073. * Jitter on a scatter plot
  40074. *
  40075. * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
  40076. * Jittered scatter plot on top of a box plot
  40077. *
  40078. * @product highcharts highstock
  40079. * @since 7.0.2
  40080. */
  40081. jitter: {
  40082. /**
  40083. * The maximal X offset for the random jitter effect.
  40084. */
  40085. x: 0,
  40086. /**
  40087. * The maximal Y offset for the random jitter effect.
  40088. */
  40089. y: 0
  40090. },
  40091. marker: {
  40092. enabled: true // Overrides auto-enabling in line series (#3647)
  40093. },
  40094. /**
  40095. * Sticky tracking of mouse events. When true, the `mouseOut` event
  40096. * on a series isn't triggered until the mouse moves over another
  40097. * series, or out of the plot area. When false, the `mouseOut` event on
  40098. * a series is triggered when the mouse leaves the area around the
  40099. * series' graph or markers. This also implies the tooltip. When
  40100. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  40101. * will be hidden when moving the mouse between series.
  40102. *
  40103. * @type {boolean}
  40104. * @default false
  40105. * @product highcharts highstock
  40106. * @apioption plotOptions.scatter.stickyTracking
  40107. */
  40108. /**
  40109. * A configuration object for the tooltip rendering of each single
  40110. * series. Properties are inherited from [tooltip](#tooltip).
  40111. * Overridable properties are `headerFormat`, `pointFormat`,
  40112. * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
  40113. * series, in a scatter plot the series.name by default shows in the
  40114. * headerFormat and point.x and point.y in the pointFormat.
  40115. *
  40116. * @product highcharts highstock
  40117. */
  40118. tooltip: {
  40119. headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  40120. '<span style="font-size: 10px"> {series.name}</span><br/>',
  40121. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
  40122. }
  40123. // Prototype members
  40124. }, {
  40125. sorted: false,
  40126. requireSorting: false,
  40127. noSharedTooltip: true,
  40128. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  40129. takeOrdinalPosition: false,
  40130. /* eslint-disable valid-jsdoc */
  40131. /**
  40132. * @private
  40133. * @function Highcharts.seriesTypes.scatter#drawGraph
  40134. */
  40135. drawGraph: function () {
  40136. if (this.options.lineWidth) {
  40137. Series.prototype.drawGraph.call(this);
  40138. }
  40139. },
  40140. // Optionally add the jitter effect
  40141. applyJitter: function () {
  40142. var series = this,
  40143. jitter = this.options.jitter,
  40144. len = this.points.length;
  40145. /**
  40146. * Return a repeatable, pseudo-random number based on an integer
  40147. * seed.
  40148. * @private
  40149. */
  40150. function unrandom(seed) {
  40151. var rand = Math.sin(seed) * 10000;
  40152. return rand - Math.floor(rand);
  40153. }
  40154. if (jitter) {
  40155. this.points.forEach(function (point, i) {
  40156. ['x', 'y'].forEach(function (dim, j) {
  40157. var axis,
  40158. plotProp = 'plot' + dim.toUpperCase(),
  40159. min,
  40160. max,
  40161. translatedJitter;
  40162. if (jitter[dim] && !point.isNull) {
  40163. axis = series[dim + 'Axis'];
  40164. translatedJitter =
  40165. jitter[dim] * axis.transA;
  40166. if (axis && !axis.isLog) {
  40167. // Identify the outer bounds of the jitter range
  40168. min = Math.max(0, point[plotProp] - translatedJitter);
  40169. max = Math.min(axis.len, point[plotProp] + translatedJitter);
  40170. // Find a random position within this range
  40171. point[plotProp] = min +
  40172. (max - min) * unrandom(i + j * len);
  40173. // Update clientX for the tooltip k-d-tree
  40174. if (dim === 'x') {
  40175. point.clientX = point.plotX;
  40176. }
  40177. }
  40178. }
  40179. });
  40180. });
  40181. }
  40182. }
  40183. /* eslint-enable valid-jsdoc */
  40184. });
  40185. /* eslint-disable no-invalid-this */
  40186. addEvent(Series, 'afterTranslate', function () {
  40187. if (this.applyJitter) {
  40188. this.applyJitter();
  40189. }
  40190. });
  40191. /* eslint-enable no-invalid-this */
  40192. /**
  40193. * A `scatter` series. If the [type](#series.scatter.type) option is
  40194. * not specified, it is inherited from [chart.type](#chart.type).
  40195. *
  40196. * @extends series,plotOptions.scatter
  40197. * @excluding cropThreshold, dataParser, dataURL, useOhlcData
  40198. * @product highcharts highstock
  40199. * @apioption series.scatter
  40200. */
  40201. /**
  40202. * An array of data points for the series. For the `scatter` series
  40203. * type, points can be given in the following ways:
  40204. *
  40205. * 1. An array of numerical values. In this case, the numerical values will be
  40206. * interpreted as `y` options. The `x` values will be automatically
  40207. * calculated, either starting at 0 and incremented by 1, or from
  40208. * `pointStart` and `pointInterval` given in the series options. If the axis
  40209. * has categories, these will be used. Example:
  40210. * ```js
  40211. * data: [0, 5, 3, 5]
  40212. * ```
  40213. *
  40214. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40215. * `x,y`. If the first value is a string, it is applied as the name of the
  40216. * point, and the `x` value is inferred.
  40217. * ```js
  40218. * data: [
  40219. * [0, 0],
  40220. * [1, 8],
  40221. * [2, 9]
  40222. * ]
  40223. * ```
  40224. *
  40225. * 3. An array of objects with named values. The following snippet shows only a
  40226. * few settings, see the complete options set below. If the total number of
  40227. * data points exceeds the series'
  40228. * [turboThreshold](#series.scatter.turboThreshold), this option is not
  40229. * available.
  40230. * ```js
  40231. * data: [{
  40232. * x: 1,
  40233. * y: 2,
  40234. * name: "Point2",
  40235. * color: "#00FF00"
  40236. * }, {
  40237. * x: 1,
  40238. * y: 4,
  40239. * name: "Point1",
  40240. * color: "#FF00FF"
  40241. * }]
  40242. * ```
  40243. *
  40244. * @sample {highcharts} highcharts/chart/reflow-true/
  40245. * Numerical values
  40246. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40247. * Arrays of numeric x and y
  40248. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40249. * Arrays of datetime x and y
  40250. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40251. * Arrays of point.name and y
  40252. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40253. * Config objects
  40254. *
  40255. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40256. * @extends series.line.data
  40257. * @product highcharts highstock
  40258. * @apioption series.scatter.data
  40259. */
  40260. ''; // adds doclets above to transpilat
  40261. });
  40262. _registerModule(_modules, 'Mixins/CenteredSeries.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  40263. /* *
  40264. *
  40265. * (c) 2010-2020 Torstein Honsi
  40266. *
  40267. * License: www.highcharts.com/license
  40268. *
  40269. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40270. *
  40271. * */
  40272. /**
  40273. * @private
  40274. * @interface Highcharts.RadianAngles
  40275. */ /**
  40276. * @name Highcharts.RadianAngles#end
  40277. * @type {number}
  40278. */ /**
  40279. * @name Highcharts.RadianAngles#start
  40280. * @type {number}
  40281. */
  40282. var isNumber = U.isNumber,
  40283. pick = U.pick,
  40284. relativeLength = U.relativeLength;
  40285. var deg2rad = H.deg2rad;
  40286. /* eslint-disable valid-jsdoc */
  40287. /**
  40288. * @private
  40289. * @mixin Highcharts.CenteredSeriesMixin
  40290. */
  40291. var centeredSeriesMixin = H.CenteredSeriesMixin = {
  40292. /**
  40293. * Get the center of the pie based on the size and center options relative
  40294. * to the plot area. Borrowed by the polar and gauge series types.
  40295. *
  40296. * @private
  40297. * @function Highcharts.CenteredSeriesMixin.getCenter
  40298. *
  40299. * @return {Array<number>}
  40300. */
  40301. getCenter: function () {
  40302. var options = this.options,
  40303. chart = this.chart,
  40304. slicingRoom = 2 * (options.slicedOffset || 0),
  40305. handleSlicingRoom,
  40306. plotWidth = chart.plotWidth - 2 * slicingRoom,
  40307. plotHeight = chart.plotHeight - 2 * slicingRoom,
  40308. centerOption = options.center,
  40309. smallestSize = Math.min(plotWidth,
  40310. plotHeight),
  40311. size = options.size,
  40312. innerSize = options.innerSize || 0,
  40313. positions,
  40314. i,
  40315. value;
  40316. if (typeof size === 'string') {
  40317. size = parseFloat(size);
  40318. }
  40319. if (typeof innerSize === 'string') {
  40320. innerSize = parseFloat(innerSize);
  40321. }
  40322. positions = [
  40323. pick(centerOption[0], '50%'),
  40324. pick(centerOption[1], '50%'),
  40325. // Prevent from negative values
  40326. pick(size && size < 0 ? void 0 : options.size, '100%'),
  40327. pick(innerSize && innerSize < 0 ? void 0 : options.innerSize || 0, '0%')
  40328. ];
  40329. // No need for inner size in angular (gauges) series but still required
  40330. // for pie series
  40331. if (chart.angular && !(this instanceof H.Series)) {
  40332. positions[3] = 0;
  40333. }
  40334. for (i = 0; i < 4; ++i) {
  40335. value = positions[i];
  40336. handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
  40337. // i == 0: centerX, relative to width
  40338. // i == 1: centerY, relative to height
  40339. // i == 2: size, relative to smallestSize
  40340. // i == 3: innerSize, relative to size
  40341. positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) + (handleSlicingRoom ? slicingRoom : 0);
  40342. }
  40343. // innerSize cannot be larger than size (#3632)
  40344. if (positions[3] > positions[2]) {
  40345. positions[3] = positions[2];
  40346. }
  40347. return positions;
  40348. },
  40349. /**
  40350. * getStartAndEndRadians - Calculates start and end angles in radians.
  40351. * Used in series types such as pie and sunburst.
  40352. *
  40353. * @private
  40354. * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
  40355. *
  40356. * @param {number} [start]
  40357. * Start angle in degrees.
  40358. *
  40359. * @param {number} [end]
  40360. * Start angle in degrees.
  40361. *
  40362. * @return {Highcharts.RadianAngles}
  40363. * Returns an object containing start and end angles as radians.
  40364. */
  40365. getStartAndEndRadians: function (start, end) {
  40366. var startAngle = isNumber(start) ? start : 0, // must be a number
  40367. endAngle = ((isNumber(end) && // must be a number
  40368. end > startAngle && // must be larger than the start angle
  40369. // difference must be less than 360 degrees
  40370. (end - startAngle) < 360) ?
  40371. end :
  40372. startAngle + 360),
  40373. correction = -90;
  40374. return {
  40375. start: deg2rad * (startAngle + correction),
  40376. end: deg2rad * (endAngle + correction)
  40377. };
  40378. }
  40379. };
  40380. return centeredSeriesMixin;
  40381. });
  40382. _registerModule(_modules, 'Series/PieSeries.js', [_modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Mixins/LegendSymbol.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js'], _modules['Mixins/CenteredSeries.js']], function (H, SVGRenderer, LegendSymbolMixin, Point, U, centeredSeriesMixin) {
  40383. /* *
  40384. *
  40385. * (c) 2010-2020 Torstein Honsi
  40386. *
  40387. * License: www.highcharts.com/license
  40388. *
  40389. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40390. *
  40391. * */
  40392. var addEvent = U.addEvent,
  40393. clamp = U.clamp,
  40394. defined = U.defined,
  40395. fireEvent = U.fireEvent,
  40396. isNumber = U.isNumber,
  40397. merge = U.merge,
  40398. pick = U.pick,
  40399. relativeLength = U.relativeLength,
  40400. seriesType = U.seriesType,
  40401. setAnimation = U.setAnimation;
  40402. var getStartAndEndRadians = centeredSeriesMixin.getStartAndEndRadians,
  40403. noop = H.noop,
  40404. Series = H.Series,
  40405. seriesTypes = H.seriesTypes;
  40406. /**
  40407. * Pie series type.
  40408. *
  40409. * @private
  40410. * @class
  40411. * @name Highcharts.seriesTypes.pie
  40412. *
  40413. * @augments Highcharts.Series
  40414. */
  40415. seriesType('pie', 'line',
  40416. /**
  40417. * A pie chart is a circular graphic which is divided into slices to
  40418. * illustrate numerical proportion.
  40419. *
  40420. * @sample highcharts/demo/pie-basic/
  40421. * Pie chart
  40422. *
  40423. * @extends plotOptions.line
  40424. * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
  40425. * cropThreshold, dashStyle, dataSorting, dragDrop,
  40426. * findNearestPointBy, getExtremesFromAll, label, lineWidth,
  40427. * marker, negativeColor, pointInterval, pointIntervalUnit,
  40428. * pointPlacement, pointStart, softThreshold, stacking, step,
  40429. * threshold, turboThreshold, zoneAxis, zones, dataSorting,
  40430. * boostBlending
  40431. * @product highcharts
  40432. * @optionparent plotOptions.pie
  40433. */
  40434. {
  40435. /**
  40436. * @excluding legendItemClick
  40437. * @apioption plotOptions.pie.events
  40438. */
  40439. /**
  40440. * Fires when the checkbox next to the point name in the legend is
  40441. * clicked. One parameter, event, is passed to the function. The state
  40442. * of the checkbox is found by event.checked. The checked item is found
  40443. * by event.item. Return false to prevent the default action which is to
  40444. * toggle the select state of the series.
  40445. *
  40446. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  40447. * Alert checkbox status
  40448. *
  40449. * @type {Function}
  40450. * @since 1.2.0
  40451. * @product highcharts
  40452. * @context Highcharts.Point
  40453. * @apioption plotOptions.pie.events.checkboxClick
  40454. */
  40455. /**
  40456. * Fires when the legend item belonging to the pie point (slice) is
  40457. * clicked. The `this` keyword refers to the point itself. One
  40458. * parameter, `event`, is passed to the function, containing common
  40459. * event information. The default action is to toggle the visibility of
  40460. * the point. This can be prevented by calling `event.preventDefault()`.
  40461. *
  40462. * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
  40463. * Confirm toggle visibility
  40464. *
  40465. * @type {Highcharts.PointLegendItemClickCallbackFunction}
  40466. * @since 1.2.0
  40467. * @product highcharts
  40468. * @apioption plotOptions.pie.point.events.legendItemClick
  40469. */
  40470. /**
  40471. * The center of the pie chart relative to the plot area. Can be
  40472. * percentages or pixel values. The default behaviour (as of 3.0) is to
  40473. * center the pie so that all slices and data labels are within the plot
  40474. * area. As a consequence, the pie may actually jump around in a chart
  40475. * with dynamic values, as the data labels move. In that case, the
  40476. * center should be explicitly set, for example to `["50%", "50%"]`.
  40477. *
  40478. * @sample {highcharts} highcharts/plotoptions/pie-center/
  40479. * Centered at 100, 100
  40480. *
  40481. * @type {Array<(number|string|null),(number|string|null)>}
  40482. * @default [null, null]
  40483. * @product highcharts
  40484. *
  40485. * @private
  40486. */
  40487. center: [null, null],
  40488. /**
  40489. * The color of the pie series. A pie series is represented as an empty
  40490. * circle if the total sum of its values is 0. Use this property to
  40491. * define the color of its border.
  40492. *
  40493. * In styled mode, the color can be defined by the
  40494. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  40495. * color can be set with the `.highcharts-series`,
  40496. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  40497. * `.highcharts-series-{n}` class, or individual classes given by the
  40498. * `className` option.
  40499. *
  40500. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  40501. * Empty pie series
  40502. *
  40503. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40504. * @default #cccccc
  40505. * @apioption plotOptions.pie.color
  40506. */
  40507. /**
  40508. * @product highcharts
  40509. *
  40510. * @private
  40511. */
  40512. clip: false,
  40513. /**
  40514. * @ignore-option
  40515. *
  40516. * @private
  40517. */
  40518. colorByPoint: true,
  40519. /**
  40520. * A series specific or series type specific color set to use instead
  40521. * of the global [colors](#colors).
  40522. *
  40523. * @sample {highcharts} highcharts/demo/pie-monochrome/
  40524. * Set default colors for all pies
  40525. *
  40526. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  40527. * @since 3.0
  40528. * @product highcharts
  40529. * @apioption plotOptions.pie.colors
  40530. */
  40531. /**
  40532. * @declare Highcharts.SeriesPieDataLabelsOptionsObject
  40533. * @extends plotOptions.series.dataLabels
  40534. * @excluding align, allowOverlap, inside, staggerLines, step
  40535. * @private
  40536. */
  40537. dataLabels: {
  40538. /**
  40539. * Alignment method for data labels. Possible values are:
  40540. *
  40541. * - `toPlotEdges`: Each label touches the nearest vertical edge of
  40542. * the plot area.
  40543. *
  40544. * - `connectors`: Connectors have the same x position and the
  40545. * widest label of each half (left & right) touches the nearest
  40546. * vertical edge of the plot area.
  40547. *
  40548. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/
  40549. * alignTo: connectors
  40550. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/
  40551. * alignTo: plotEdges
  40552. *
  40553. * @type {string}
  40554. * @since 7.0.0
  40555. * @product highcharts
  40556. * @apioption plotOptions.pie.dataLabels.alignTo
  40557. */
  40558. allowOverlap: true,
  40559. /**
  40560. * The color of the line connecting the data label to the pie slice.
  40561. * The default color is the same as the point's color.
  40562. *
  40563. * In styled mode, the connector stroke is given in the
  40564. * `.highcharts-data-label-connector` class.
  40565. *
  40566. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
  40567. * Blue connectors
  40568. * @sample {highcharts} highcharts/css/pie-point/
  40569. * Styled connectors
  40570. *
  40571. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40572. * @since 2.1
  40573. * @product highcharts
  40574. * @apioption plotOptions.pie.dataLabels.connectorColor
  40575. */
  40576. /**
  40577. * The distance from the data label to the connector. Note that
  40578. * data labels also have a default `padding`, so in order for the
  40579. * connector to touch the text, the `padding` must also be 0.
  40580. *
  40581. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
  40582. * No padding
  40583. *
  40584. * @since 2.1
  40585. * @product highcharts
  40586. */
  40587. connectorPadding: 5,
  40588. /**
  40589. * Specifies the method that is used to generate the connector path.
  40590. * Highcharts provides 3 built-in connector shapes: `'fixedOffset'`
  40591. * (default), `'straight'` and `'crookedLine'`. Using
  40592. * `'crookedLine'` has the most sense (in most of the cases) when
  40593. * `'alignTo'` is set.
  40594. *
  40595. * Users can provide their own method by passing a function instead
  40596. * of a String. 3 arguments are passed to the callback:
  40597. *
  40598. * - Object that holds the information about the coordinates of the
  40599. * label (`x` & `y` properties) and how the label is located in
  40600. * relation to the pie (`alignment` property). `alignment` can by
  40601. * one of the following:
  40602. * `'left'` (pie on the left side of the data label),
  40603. * `'right'` (pie on the right side of the data label) or
  40604. * `'center'` (data label overlaps the pie).
  40605. *
  40606. * - Object that holds the information about the position of the
  40607. * connector. Its `touchingSliceAt` porperty tells the position
  40608. * of the place where the connector touches the slice.
  40609. *
  40610. * - Data label options
  40611. *
  40612. * The function has to return an SVG path definition in array form
  40613. * (see the example).
  40614. *
  40615. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-string/
  40616. * connectorShape is a String
  40617. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-function/
  40618. * connectorShape is a function
  40619. *
  40620. * @type {string|Function}
  40621. * @since 7.0.0
  40622. * @product highcharts
  40623. */
  40624. connectorShape: 'fixedOffset',
  40625. /**
  40626. * The width of the line connecting the data label to the pie slice.
  40627. *
  40628. * In styled mode, the connector stroke width is given in the
  40629. * `.highcharts-data-label-connector` class.
  40630. *
  40631. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
  40632. * Disable the connector
  40633. * @sample {highcharts} highcharts/css/pie-point/
  40634. * Styled connectors
  40635. *
  40636. * @type {number}
  40637. * @default 1
  40638. * @since 2.1
  40639. * @product highcharts
  40640. * @apioption plotOptions.pie.dataLabels.connectorWidth
  40641. */
  40642. /**
  40643. * Works only if `connectorShape` is `'crookedLine'`. It defines how
  40644. * far from the vertical plot edge the coonnector path should be
  40645. * crooked.
  40646. *
  40647. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
  40648. * crookDistance set to 90%
  40649. *
  40650. * @since 7.0.0
  40651. * @product highcharts
  40652. */
  40653. crookDistance: '70%',
  40654. /**
  40655. * The distance of the data label from the pie's edge. Negative
  40656. * numbers put the data label on top of the pie slices. Can also be
  40657. * defined as a percentage of pie's radius. Connectors are only
  40658. * shown for data labels outside the pie.
  40659. *
  40660. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
  40661. * Data labels on top of the pie
  40662. *
  40663. * @type {number|string}
  40664. * @since 2.1
  40665. * @product highcharts
  40666. */
  40667. distance: 30,
  40668. enabled: true,
  40669. formatter: function () {
  40670. return this.point.isNull ? void 0 : this.point.name;
  40671. },
  40672. /**
  40673. * Whether to render the connector as a soft arc or a line with
  40674. * sharp break. Works only if `connectorShape` equals to
  40675. * `fixedOffset`.
  40676. *
  40677. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-true/
  40678. * Soft
  40679. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-false/
  40680. * Non soft
  40681. *
  40682. * @since 2.1.7
  40683. * @product highcharts
  40684. */
  40685. softConnector: true,
  40686. /**
  40687. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow
  40688. * Long labels truncated with an ellipsis
  40689. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap
  40690. * Long labels are wrapped
  40691. *
  40692. * @type {Highcharts.CSSObject}
  40693. * @apioption plotOptions.pie.dataLabels.style
  40694. */
  40695. x: 0
  40696. },
  40697. /**
  40698. * If the total sum of the pie's values is 0, the series is represented
  40699. * as an empty circle . The `fillColor` option defines the color of that
  40700. * circle. Use [pie.borderWidth](#plotOptions.pie.borderWidth) to set
  40701. * the border thickness.
  40702. *
  40703. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  40704. * Empty pie series
  40705. *
  40706. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40707. * @private
  40708. */
  40709. fillColor: void 0,
  40710. /**
  40711. * The end angle of the pie in degrees where 0 is top and 90 is right.
  40712. * Defaults to `startAngle` plus 360.
  40713. *
  40714. * @sample {highcharts} highcharts/demo/pie-semi-circle/
  40715. * Semi-circle donut
  40716. *
  40717. * @type {number}
  40718. * @since 1.3.6
  40719. * @product highcharts
  40720. * @apioption plotOptions.pie.endAngle
  40721. */
  40722. /**
  40723. * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
  40724. * this option tells whether the series shall be redrawn as if the
  40725. * hidden point were `null`.
  40726. *
  40727. * The default value changed from `false` to `true` with Highcharts
  40728. * 3.0.
  40729. *
  40730. * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
  40731. * True, the hiddden point is ignored
  40732. *
  40733. * @since 2.3.0
  40734. * @product highcharts
  40735. *
  40736. * @private
  40737. */
  40738. ignoreHiddenPoint: true,
  40739. /**
  40740. * @ignore-option
  40741. *
  40742. * @private
  40743. */
  40744. inactiveOtherPoints: true,
  40745. /**
  40746. * The size of the inner diameter for the pie. A size greater than 0
  40747. * renders a donut chart. Can be a percentage or pixel value.
  40748. * Percentages are relative to the pie size. Pixel values are given as
  40749. * integers.
  40750. *
  40751. *
  40752. * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
  40753. * area, not the pie size.
  40754. *
  40755. * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
  40756. * 80px inner size
  40757. * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
  40758. * 50% of the plot area
  40759. * @sample {highcharts} highcharts/demo/3d-pie-donut/
  40760. * 3D donut
  40761. *
  40762. * @type {number|string}
  40763. * @default 0
  40764. * @since 2.0
  40765. * @product highcharts
  40766. * @apioption plotOptions.pie.innerSize
  40767. */
  40768. /**
  40769. * @ignore-option
  40770. *
  40771. * @private
  40772. */
  40773. legendType: 'point',
  40774. /**
  40775. * @ignore-option
  40776. *
  40777. * @private
  40778. */
  40779. marker: null,
  40780. /**
  40781. * The minimum size for a pie in response to auto margins. The pie will
  40782. * try to shrink to make room for data labels in side the plot area,
  40783. * but only to this size.
  40784. *
  40785. * @type {number|string}
  40786. * @default 80
  40787. * @since 3.0
  40788. * @product highcharts
  40789. * @apioption plotOptions.pie.minSize
  40790. */
  40791. /**
  40792. * The diameter of the pie relative to the plot area. Can be a
  40793. * percentage or pixel value. Pixel values are given as integers. The
  40794. * default behaviour (as of 3.0) is to scale to the plot area and give
  40795. * room for data labels within the plot area.
  40796. * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
  40797. * default size calculation. As a consequence, the size of the pie may
  40798. * vary when points are updated and data labels more around. In that
  40799. * case it is best to set a fixed value, for example `"75%"`.
  40800. *
  40801. * @sample {highcharts} highcharts/plotoptions/pie-size/
  40802. * Smaller pie
  40803. *
  40804. * @type {number|string|null}
  40805. * @product highcharts
  40806. *
  40807. * @private
  40808. */
  40809. size: null,
  40810. /**
  40811. * Whether to display this particular series or series type in the
  40812. * legend. Since 2.1, pies are not shown in the legend by default.
  40813. *
  40814. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  40815. * One series in the legend, one hidden
  40816. *
  40817. * @product highcharts
  40818. *
  40819. * @private
  40820. */
  40821. showInLegend: false,
  40822. /**
  40823. * If a point is sliced, moved out from the center, how many pixels
  40824. * should it be moved?.
  40825. *
  40826. * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
  40827. * 20px offset
  40828. *
  40829. * @product highcharts
  40830. *
  40831. * @private
  40832. */
  40833. slicedOffset: 10,
  40834. /**
  40835. * The start angle of the pie slices in degrees where 0 is top and 90
  40836. * right.
  40837. *
  40838. * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
  40839. * Start from right
  40840. *
  40841. * @type {number}
  40842. * @default 0
  40843. * @since 2.3.4
  40844. * @product highcharts
  40845. * @apioption plotOptions.pie.startAngle
  40846. */
  40847. /**
  40848. * Sticky tracking of mouse events. When true, the `mouseOut` event
  40849. * on a series isn't triggered until the mouse moves over another
  40850. * series, or out of the plot area. When false, the `mouseOut` event on
  40851. * a series is triggered when the mouse leaves the area around the
  40852. * series' graph or markers. This also implies the tooltip. When
  40853. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  40854. * will be hidden when moving the mouse between series.
  40855. *
  40856. * @product highcharts
  40857. *
  40858. * @private
  40859. */
  40860. stickyTracking: false,
  40861. tooltip: {
  40862. followPointer: true
  40863. },
  40864. /**
  40865. * The color of the border surrounding each slice. When `null`, the
  40866. * border takes the same color as the slice fill. This can be used
  40867. * together with a `borderWidth` to fill drawing gaps created by
  40868. * antialiazing artefacts in borderless pies.
  40869. *
  40870. * In styled mode, the border stroke is given in the `.highcharts-point`
  40871. * class.
  40872. *
  40873. * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
  40874. * Black border
  40875. *
  40876. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40877. * @default #ffffff
  40878. * @product highcharts
  40879. *
  40880. * @private
  40881. */
  40882. borderColor: '#ffffff',
  40883. /**
  40884. * The width of the border surrounding each slice.
  40885. *
  40886. * When setting the border width to 0, there may be small gaps between
  40887. * the slices due to SVG antialiasing artefacts. To work around this,
  40888. * keep the border width at 0.5 or 1, but set the `borderColor` to
  40889. * `null` instead.
  40890. *
  40891. * In styled mode, the border stroke width is given in the
  40892. * `.highcharts-point` class.
  40893. *
  40894. * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
  40895. * 3px border
  40896. *
  40897. * @product highcharts
  40898. *
  40899. * @private
  40900. */
  40901. borderWidth: 1,
  40902. /**
  40903. * @ignore-options
  40904. * @private
  40905. */
  40906. lineWidth: void 0,
  40907. states: {
  40908. /**
  40909. * @extends plotOptions.series.states.hover
  40910. * @excluding marker, lineWidth, lineWidthPlus
  40911. * @product highcharts
  40912. */
  40913. hover: {
  40914. /**
  40915. * How much to brighten the point on interaction. Requires the
  40916. * main color to be defined in hex or rgb(a) format.
  40917. *
  40918. * In styled mode, the hover brightness is by default replaced
  40919. * by a fill-opacity given in the `.highcharts-point-hover`
  40920. * class.
  40921. *
  40922. * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
  40923. * Brightened by 0.5
  40924. *
  40925. * @product highcharts
  40926. */
  40927. brightness: 0.1
  40928. }
  40929. }
  40930. },
  40931. /* eslint-disable valid-jsdoc */
  40932. /**
  40933. * @lends seriesTypes.pie.prototype
  40934. */
  40935. {
  40936. isCartesian: false,
  40937. requireSorting: false,
  40938. directTouch: true,
  40939. noSharedTooltip: true,
  40940. trackerGroups: ['group', 'dataLabelsGroup'],
  40941. axisTypes: [],
  40942. pointAttribs: seriesTypes.column.prototype.pointAttribs,
  40943. /**
  40944. * Animate the pies in
  40945. *
  40946. * @private
  40947. * @function Highcharts.seriesTypes.pie#animate
  40948. *
  40949. * @param {boolean} [init=false]
  40950. */
  40951. animate: function (init) {
  40952. var series = this,
  40953. points = series.points,
  40954. startAngleRad = series.startAngleRad;
  40955. if (!init) {
  40956. points.forEach(function (point) {
  40957. var graphic = point.graphic,
  40958. args = point.shapeArgs;
  40959. if (graphic && args) {
  40960. // start values
  40961. graphic.attr({
  40962. // animate from inner radius (#779)
  40963. r: pick(point.startR, (series.center && series.center[3] / 2)),
  40964. start: startAngleRad,
  40965. end: startAngleRad
  40966. });
  40967. // animate
  40968. graphic.animate({
  40969. r: args.r,
  40970. start: args.start,
  40971. end: args.end
  40972. }, series.options.animation);
  40973. }
  40974. });
  40975. }
  40976. },
  40977. // Define hasData function for non-cartesian series.
  40978. // Returns true if the series has points at all.
  40979. hasData: function () {
  40980. return !!this.processedXData.length; // != 0
  40981. },
  40982. /**
  40983. * Recompute total chart sum and update percentages of points.
  40984. *
  40985. * @private
  40986. * @function Highcharts.seriesTypes.pie#updateTotals
  40987. * @return {void}
  40988. */
  40989. updateTotals: function () {
  40990. var i,
  40991. total = 0,
  40992. points = this.points,
  40993. len = points.length,
  40994. point,
  40995. ignoreHiddenPoint = this.options.ignoreHiddenPoint;
  40996. // Get the total sum
  40997. for (i = 0; i < len; i++) {
  40998. point = points[i];
  40999. total += (ignoreHiddenPoint && !point.visible) ?
  41000. 0 :
  41001. point.isNull ?
  41002. 0 :
  41003. point.y;
  41004. }
  41005. this.total = total;
  41006. // Set each point's properties
  41007. for (i = 0; i < len; i++) {
  41008. point = points[i];
  41009. point.percentage =
  41010. (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
  41011. point.y / total * 100 :
  41012. 0;
  41013. point.total = total;
  41014. }
  41015. },
  41016. /**
  41017. * Extend the generatePoints method by adding total and percentage
  41018. * properties to each point
  41019. *
  41020. * @private
  41021. * @function Highcharts.seriesTypes.pie#generatePoints
  41022. * @return {void}
  41023. */
  41024. generatePoints: function () {
  41025. Series.prototype.generatePoints.call(this);
  41026. this.updateTotals();
  41027. },
  41028. /**
  41029. * Utility for getting the x value from a given y, used for
  41030. * anticollision logic in data labels. Added point for using specific
  41031. * points' label distance.
  41032. * @private
  41033. */
  41034. getX: function (y, left, point) {
  41035. var center = this.center,
  41036. // Variable pie has individual radius
  41037. radius = this.radii ?
  41038. this.radii[point.index] :
  41039. center[2] / 2,
  41040. angle,
  41041. x;
  41042. angle = Math.asin(clamp((y - center[1]) / (radius + point.labelDistance), -1, 1));
  41043. x = center[0] +
  41044. (left ? -1 : 1) *
  41045. (Math.cos(angle) * (radius + point.labelDistance)) +
  41046. (point.labelDistance > 0 ?
  41047. (left ? -1 : 1) * this.options.dataLabels.padding :
  41048. 0);
  41049. return x;
  41050. },
  41051. /**
  41052. * Do translation for pie slices
  41053. *
  41054. * @private
  41055. * @function Highcharts.seriesTypes.pie#translate
  41056. * @param {Array<number>} [positions]
  41057. * @return {void}
  41058. */
  41059. translate: function (positions) {
  41060. this.generatePoints();
  41061. var series = this,
  41062. cumulative = 0,
  41063. precision = 1000, // issue #172
  41064. options = series.options,
  41065. slicedOffset = options.slicedOffset,
  41066. connectorOffset = slicedOffset + (options.borderWidth || 0),
  41067. finalConnectorOffset,
  41068. start,
  41069. end,
  41070. angle,
  41071. radians = getStartAndEndRadians(options.startAngle,
  41072. options.endAngle),
  41073. startAngleRad = series.startAngleRad = radians.start,
  41074. endAngleRad = series.endAngleRad = radians.end,
  41075. circ = endAngleRad - startAngleRad, // 2 * Math.PI,
  41076. points = series.points,
  41077. // the x component of the radius vector for a given point
  41078. radiusX,
  41079. radiusY,
  41080. labelDistance = options.dataLabels.distance,
  41081. ignoreHiddenPoint = options.ignoreHiddenPoint,
  41082. i,
  41083. len = points.length,
  41084. point;
  41085. // Get positions - either an integer or a percentage string must be
  41086. // given. If positions are passed as a parameter, we're in a
  41087. // recursive loop for adjusting space for data labels.
  41088. if (!positions) {
  41089. series.center = positions = series.getCenter();
  41090. }
  41091. // Calculate the geometry for each point
  41092. for (i = 0; i < len; i++) {
  41093. point = points[i];
  41094. // set start and end angle
  41095. start = startAngleRad + (cumulative * circ);
  41096. if (!ignoreHiddenPoint || point.visible) {
  41097. cumulative += point.percentage / 100;
  41098. }
  41099. end = startAngleRad + (cumulative * circ);
  41100. // set the shape
  41101. point.shapeType = 'arc';
  41102. point.shapeArgs = {
  41103. x: positions[0],
  41104. y: positions[1],
  41105. r: positions[2] / 2,
  41106. innerR: positions[3] / 2,
  41107. start: Math.round(start * precision) / precision,
  41108. end: Math.round(end * precision) / precision
  41109. };
  41110. // Used for distance calculation for specific point.
  41111. point.labelDistance = pick((point.options.dataLabels &&
  41112. point.options.dataLabels.distance), labelDistance);
  41113. // Compute point.labelDistance if it's defined as percentage
  41114. // of slice radius (#8854)
  41115. point.labelDistance = relativeLength(point.labelDistance, point.shapeArgs.r);
  41116. // Saved for later dataLabels distance calculation.
  41117. series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
  41118. // The angle must stay within -90 and 270 (#2645)
  41119. angle = (end + start) / 2;
  41120. if (angle > 1.5 * Math.PI) {
  41121. angle -= 2 * Math.PI;
  41122. }
  41123. else if (angle < -Math.PI / 2) {
  41124. angle += 2 * Math.PI;
  41125. }
  41126. // Center for the sliced out slice
  41127. point.slicedTranslation = {
  41128. translateX: Math.round(Math.cos(angle) * slicedOffset),
  41129. translateY: Math.round(Math.sin(angle) * slicedOffset)
  41130. };
  41131. // set the anchor point for tooltips
  41132. radiusX = Math.cos(angle) * positions[2] / 2;
  41133. radiusY = Math.sin(angle) * positions[2] / 2;
  41134. point.tooltipPos = [
  41135. positions[0] + radiusX * 0.7,
  41136. positions[1] + radiusY * 0.7
  41137. ];
  41138. point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
  41139. 1 :
  41140. 0;
  41141. point.angle = angle;
  41142. // Set the anchor point for data labels. Use point.labelDistance
  41143. // instead of labelDistance // #1174
  41144. // finalConnectorOffset - not override connectorOffset value.
  41145. finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
  41146. point.labelPosition = {
  41147. natural: {
  41148. // initial position of the data label - it's utilized for
  41149. // finding the final position for the label
  41150. x: positions[0] + radiusX + Math.cos(angle) *
  41151. point.labelDistance,
  41152. y: positions[1] + radiusY + Math.sin(angle) *
  41153. point.labelDistance
  41154. },
  41155. 'final': {
  41156. // used for generating connector path -
  41157. // initialized later in drawDataLabels function
  41158. // x: undefined,
  41159. // y: undefined
  41160. },
  41161. // left - pie on the left side of the data label
  41162. // right - pie on the right side of the data label
  41163. // center - data label overlaps the pie
  41164. alignment: point.labelDistance < 0 ?
  41165. 'center' : point.half ? 'right' : 'left',
  41166. connectorPosition: {
  41167. breakAt: {
  41168. x: positions[0] + radiusX + Math.cos(angle) *
  41169. finalConnectorOffset,
  41170. y: positions[1] + radiusY + Math.sin(angle) *
  41171. finalConnectorOffset
  41172. },
  41173. touchingSliceAt: {
  41174. x: positions[0] + radiusX,
  41175. y: positions[1] + radiusY
  41176. }
  41177. }
  41178. };
  41179. }
  41180. fireEvent(series, 'afterTranslate');
  41181. },
  41182. /**
  41183. * Called internally to draw auxiliary graph in pie-like series in
  41184. * situtation when the default graph is not sufficient enough to present
  41185. * the data well. Auxiliary graph is saved in the same object as
  41186. * regular graph.
  41187. *
  41188. * @private
  41189. * @function Highcharts.seriesTypes.pie#drawEmpty
  41190. */
  41191. drawEmpty: function () {
  41192. var centerX,
  41193. centerY,
  41194. start = this.startAngleRad,
  41195. end = this.endAngleRad,
  41196. options = this.options;
  41197. // Draw auxiliary graph if there're no visible points.
  41198. if (this.total === 0 && this.center) {
  41199. centerX = this.center[0];
  41200. centerY = this.center[1];
  41201. if (!this.graph) {
  41202. this.graph = this.chart.renderer
  41203. .arc(centerX, centerY, this.center[1] / 2, 0, start, end)
  41204. .addClass('highcharts-empty-series')
  41205. .add(this.group);
  41206. }
  41207. this.graph.attr({
  41208. d: SVGRenderer.prototype.symbols.arc(centerX, centerY, this.center[2] / 2, 0, {
  41209. start: start,
  41210. end: end,
  41211. innerR: this.center[3] / 2
  41212. })
  41213. });
  41214. if (!this.chart.styledMode) {
  41215. this.graph.attr({
  41216. 'stroke-width': options.borderWidth,
  41217. fill: options.fillColor || 'none',
  41218. stroke: options.color ||
  41219. '#cccccc'
  41220. });
  41221. }
  41222. }
  41223. else if (this.graph) { // Destroy the graph object.
  41224. this.graph = this.graph.destroy();
  41225. }
  41226. },
  41227. /**
  41228. * Draw the data points
  41229. *
  41230. * @private
  41231. * @function Highcharts.seriesTypes.pie#drawPoints
  41232. * @return {void}
  41233. */
  41234. redrawPoints: function () {
  41235. var series = this,
  41236. chart = series.chart,
  41237. renderer = chart.renderer,
  41238. groupTranslation,
  41239. graphic,
  41240. pointAttr,
  41241. shapeArgs,
  41242. shadow = series.options.shadow;
  41243. this.drawEmpty();
  41244. if (shadow && !series.shadowGroup && !chart.styledMode) {
  41245. series.shadowGroup = renderer.g('shadow')
  41246. .attr({ zIndex: -1 })
  41247. .add(series.group);
  41248. }
  41249. // draw the slices
  41250. series.points.forEach(function (point) {
  41251. var animateTo = {};
  41252. graphic = point.graphic;
  41253. if (!point.isNull && graphic) {
  41254. shapeArgs = point.shapeArgs;
  41255. // If the point is sliced, use special translation, else use
  41256. // plot area translation
  41257. groupTranslation = point.getTranslate();
  41258. if (!chart.styledMode) {
  41259. // Put the shadow behind all points
  41260. var shadowGroup = point.shadowGroup;
  41261. if (shadow && !shadowGroup) {
  41262. shadowGroup = point.shadowGroup = renderer
  41263. .g('shadow')
  41264. .add(series.shadowGroup);
  41265. }
  41266. if (shadowGroup) {
  41267. shadowGroup.attr(groupTranslation);
  41268. }
  41269. pointAttr = series.pointAttribs(point, (point.selected && 'select'));
  41270. }
  41271. // Draw the slice
  41272. if (!point.delayedRendering) {
  41273. graphic
  41274. .setRadialReference(series.center);
  41275. if (!chart.styledMode) {
  41276. merge(true, animateTo, pointAttr);
  41277. }
  41278. merge(true, animateTo, shapeArgs, groupTranslation);
  41279. graphic.animate(animateTo);
  41280. }
  41281. else {
  41282. graphic
  41283. .setRadialReference(series.center)
  41284. .attr(shapeArgs)
  41285. .attr(groupTranslation);
  41286. if (!chart.styledMode) {
  41287. graphic
  41288. .attr(pointAttr)
  41289. .attr({ 'stroke-linejoin': 'round' })
  41290. .shadow(shadow, shadowGroup);
  41291. }
  41292. point.delayedRendering = false;
  41293. }
  41294. graphic.attr({
  41295. visibility: point.visible ? 'inherit' : 'hidden'
  41296. });
  41297. graphic.addClass(point.getClassName());
  41298. }
  41299. else if (graphic) {
  41300. point.graphic = graphic.destroy();
  41301. }
  41302. });
  41303. },
  41304. /**
  41305. * Slices in pie chart are initialized in DOM, but it's shapes and
  41306. * animations are normally run in `drawPoints()`.
  41307. * @private
  41308. */
  41309. drawPoints: function () {
  41310. var renderer = this.chart.renderer;
  41311. this.points.forEach(function (point) {
  41312. // When updating a series between 2d and 3d or cartesian and
  41313. // polar, the shape type changes.
  41314. if (point.graphic && point.hasNewShapeType()) {
  41315. point.graphic = point.graphic.destroy();
  41316. }
  41317. if (!point.graphic) {
  41318. point.graphic = renderer[point.shapeType](point.shapeArgs)
  41319. .add(point.series.group);
  41320. point.delayedRendering = true;
  41321. }
  41322. });
  41323. },
  41324. /**
  41325. * @private
  41326. * @deprecated
  41327. * @function Highcharts.seriesTypes.pie#searchPoint
  41328. */
  41329. searchPoint: noop,
  41330. /**
  41331. * Utility for sorting data labels
  41332. *
  41333. * @private
  41334. * @function Highcharts.seriesTypes.pie#sortByAngle
  41335. * @param {Array<Highcharts.Point>} points
  41336. * @param {number} sign
  41337. * @return {void}
  41338. */
  41339. sortByAngle: function (points, sign) {
  41340. points.sort(function (a, b) {
  41341. return ((typeof a.angle !== 'undefined') &&
  41342. (b.angle - a.angle) * sign);
  41343. });
  41344. },
  41345. /**
  41346. * Use a simple symbol from LegendSymbolMixin.
  41347. *
  41348. * @private
  41349. * @borrows Highcharts.LegendSymbolMixin.drawRectangle as Highcharts.seriesTypes.pie#drawLegendSymbol
  41350. */
  41351. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  41352. /**
  41353. * Use the getCenter method from drawLegendSymbol.
  41354. *
  41355. * @private
  41356. * @borrows Highcharts.CenteredSeriesMixin.getCenter as Highcharts.seriesTypes.pie#getCenter
  41357. */
  41358. getCenter: centeredSeriesMixin.getCenter,
  41359. /**
  41360. * Pies don't have point marker symbols.
  41361. *
  41362. * @deprecated
  41363. * @private
  41364. * @function Highcharts.seriesTypes.pie#getSymbol
  41365. */
  41366. getSymbol: noop,
  41367. /**
  41368. * @private
  41369. * @type {null}
  41370. */
  41371. drawGraph: null
  41372. },
  41373. /**
  41374. * @lends seriesTypes.pie.prototype.pointClass.prototype
  41375. */
  41376. {
  41377. /**
  41378. * Initialize the pie slice
  41379. *
  41380. * @private
  41381. * @function Highcharts.seriesTypes.pie#pointClass#init
  41382. * @return {Highcharts.Point}
  41383. */
  41384. init: function () {
  41385. Point.prototype.init.apply(this, arguments);
  41386. var point = this,
  41387. toggleSlice;
  41388. point.name = pick(point.name, 'Slice');
  41389. // add event listener for select
  41390. toggleSlice = function (e) {
  41391. point.slice(e.type === 'select');
  41392. };
  41393. addEvent(point, 'select', toggleSlice);
  41394. addEvent(point, 'unselect', toggleSlice);
  41395. return point;
  41396. },
  41397. /**
  41398. * Negative points are not valid (#1530, #3623, #5322)
  41399. *
  41400. * @private
  41401. * @function Highcharts.seriesTypes.pie#pointClass#isValid
  41402. * @return {boolean}
  41403. */
  41404. isValid: function () {
  41405. return isNumber(this.y) && this.y >= 0;
  41406. },
  41407. /**
  41408. * Toggle the visibility of the pie slice
  41409. *
  41410. * @private
  41411. * @function Highcharts.seriesTypes.pie#pointClass#setVisible
  41412. * @param {boolean} vis
  41413. * Whether to show the slice or not. If undefined, the visibility
  41414. * is toggled.
  41415. * @param {boolean} [redraw=false]
  41416. * @return {void}
  41417. */
  41418. setVisible: function (vis, redraw) {
  41419. var point = this,
  41420. series = point.series,
  41421. chart = series.chart,
  41422. ignoreHiddenPoint = series.options.ignoreHiddenPoint;
  41423. redraw = pick(redraw, ignoreHiddenPoint);
  41424. if (vis !== point.visible) {
  41425. // If called without an argument, toggle visibility
  41426. point.visible = point.options.visible = vis =
  41427. typeof vis === 'undefined' ? !point.visible : vis;
  41428. // update userOptions.data
  41429. series.options.data[series.data.indexOf(point)] =
  41430. point.options;
  41431. // Show and hide associated elements. This is performed
  41432. // regardless of redraw or not, because chart.redraw only
  41433. // handles full series.
  41434. ['graphic', 'dataLabel', 'connector', 'shadowGroup'].forEach(function (key) {
  41435. if (point[key]) {
  41436. point[key][vis ? 'show' : 'hide'](true);
  41437. }
  41438. });
  41439. if (point.legendItem) {
  41440. chart.legend.colorizeItem(point, vis);
  41441. }
  41442. // #4170, hide halo after hiding point
  41443. if (!vis && point.state === 'hover') {
  41444. point.setState('');
  41445. }
  41446. // Handle ignore hidden slices
  41447. if (ignoreHiddenPoint) {
  41448. series.isDirty = true;
  41449. }
  41450. if (redraw) {
  41451. chart.redraw();
  41452. }
  41453. }
  41454. },
  41455. /**
  41456. * Set or toggle whether the slice is cut out from the pie
  41457. *
  41458. * @private
  41459. * @function Highcharts.seriesTypes.pie#pointClass#slice
  41460. * @param {boolean} sliced
  41461. * When undefined, the slice state is toggled.
  41462. * @param {boolean} redraw
  41463. * Whether to redraw the chart. True by default.
  41464. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>}
  41465. * Animation options.
  41466. * @return {void}
  41467. */
  41468. slice: function (sliced, redraw, animation) {
  41469. var point = this,
  41470. series = point.series,
  41471. chart = series.chart;
  41472. setAnimation(animation, chart);
  41473. // redraw is true by default
  41474. redraw = pick(redraw, true);
  41475. /**
  41476. * Pie series only. Whether to display a slice offset from the
  41477. * center.
  41478. * @name Highcharts.Point#sliced
  41479. * @type {boolean|undefined}
  41480. */
  41481. // if called without an argument, toggle
  41482. point.sliced = point.options.sliced = sliced =
  41483. defined(sliced) ? sliced : !point.sliced;
  41484. // update userOptions.data
  41485. series.options.data[series.data.indexOf(point)] =
  41486. point.options;
  41487. if (point.graphic) {
  41488. point.graphic.animate(this.getTranslate());
  41489. }
  41490. if (point.shadowGroup) {
  41491. point.shadowGroup.animate(this.getTranslate());
  41492. }
  41493. },
  41494. /**
  41495. * @private
  41496. * @function Highcharts.seriesTypes.pie#pointClass#getTranslate
  41497. * @return {Highcharts.TranslationAttributes}
  41498. */
  41499. getTranslate: function () {
  41500. return this.sliced ? this.slicedTranslation : {
  41501. translateX: 0,
  41502. translateY: 0
  41503. };
  41504. },
  41505. /**
  41506. * @private
  41507. * @function Highcharts.seriesTypes.pie#pointClass#haloPath
  41508. * @param {number} size
  41509. * @return {Highcharts.SVGPathArray}
  41510. */
  41511. haloPath: function (size) {
  41512. var shapeArgs = this.shapeArgs;
  41513. return this.sliced || !this.visible ?
  41514. [] :
  41515. this.series.chart.renderer.symbols.arc(shapeArgs.x, shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
  41516. // Substract 1px to ensure the background is not bleeding
  41517. // through between the halo and the slice (#7495).
  41518. innerR: shapeArgs.r - 1,
  41519. start: shapeArgs.start,
  41520. end: shapeArgs.end
  41521. });
  41522. },
  41523. connectorShapes: {
  41524. // only one available before v7.0.0
  41525. fixedOffset: function (labelPosition, connectorPosition, options) {
  41526. var breakAt = connectorPosition.breakAt,
  41527. touchingSliceAt = connectorPosition.touchingSliceAt,
  41528. lineSegment = options.softConnector ? [
  41529. 'C',
  41530. // 1st control point (of the curve)
  41531. labelPosition.x +
  41532. // 5 gives the connector a little horizontal bend
  41533. (labelPosition.alignment === 'left' ? -5 : 5),
  41534. labelPosition.y,
  41535. 2 * breakAt.x - touchingSliceAt.x,
  41536. 2 * breakAt.y - touchingSliceAt.y,
  41537. breakAt.x,
  41538. breakAt.y //
  41539. ] : [
  41540. 'L',
  41541. breakAt.x,
  41542. breakAt.y
  41543. ];
  41544. // assemble the path
  41545. return ([
  41546. ['M', labelPosition.x, labelPosition.y],
  41547. lineSegment,
  41548. ['L', touchingSliceAt.x, touchingSliceAt.y]
  41549. ]);
  41550. },
  41551. straight: function (labelPosition, connectorPosition) {
  41552. var touchingSliceAt = connectorPosition.touchingSliceAt;
  41553. // direct line to the slice
  41554. return [
  41555. ['M', labelPosition.x, labelPosition.y],
  41556. ['L', touchingSliceAt.x, touchingSliceAt.y]
  41557. ];
  41558. },
  41559. crookedLine: function (labelPosition, connectorPosition, options) {
  41560. var touchingSliceAt = connectorPosition.touchingSliceAt,
  41561. series = this.series,
  41562. pieCenterX = series.center[0],
  41563. plotWidth = series.chart.plotWidth,
  41564. plotLeft = series.chart.plotLeft,
  41565. alignment = labelPosition.alignment,
  41566. radius = this.shapeArgs.r,
  41567. crookDistance = relativeLength(// % to fraction
  41568. options.crookDistance, 1),
  41569. crookX = alignment === 'left' ?
  41570. pieCenterX + radius + (plotWidth + plotLeft -
  41571. pieCenterX - radius) * (1 - crookDistance) :
  41572. plotLeft + (pieCenterX - radius) * crookDistance,
  41573. segmentWithCrook = [
  41574. 'L',
  41575. crookX,
  41576. labelPosition.y
  41577. ],
  41578. useCrook = true;
  41579. // crookedLine formula doesn't make sense if the path overlaps
  41580. // the label - use straight line instead in that case
  41581. if (alignment === 'left' ?
  41582. (crookX > labelPosition.x || crookX < touchingSliceAt.x) :
  41583. (crookX < labelPosition.x || crookX > touchingSliceAt.x)) {
  41584. useCrook = false;
  41585. }
  41586. // assemble the path
  41587. var path = [
  41588. ['M',
  41589. labelPosition.x,
  41590. labelPosition.y]
  41591. ];
  41592. if (useCrook) {
  41593. path.push(segmentWithCrook);
  41594. }
  41595. path.push(['L', touchingSliceAt.x, touchingSliceAt.y]);
  41596. return path;
  41597. }
  41598. },
  41599. /**
  41600. * Extendable method for getting the path of the connector between the
  41601. * data label and the pie slice.
  41602. */
  41603. getConnectorPath: function () {
  41604. var labelPosition = this.labelPosition,
  41605. options = this.series.options.dataLabels,
  41606. connectorShape = options.connectorShape,
  41607. predefinedShapes = this.connectorShapes;
  41608. // find out whether to use the predefined shape
  41609. if (predefinedShapes[connectorShape]) {
  41610. connectorShape = predefinedShapes[connectorShape];
  41611. }
  41612. return connectorShape.call(this, {
  41613. // pass simplified label position object for user's convenience
  41614. x: labelPosition.final.x,
  41615. y: labelPosition.final.y,
  41616. alignment: labelPosition.alignment
  41617. }, labelPosition.connectorPosition, options);
  41618. }
  41619. }
  41620. /* eslint-enable valid-jsdoc */
  41621. );
  41622. /**
  41623. * A `pie` series. If the [type](#series.pie.type) option is not specified,
  41624. * it is inherited from [chart.type](#chart.type).
  41625. *
  41626. * @extends series,plotOptions.pie
  41627. * @excluding cropThreshold, dataParser, dataURL, stack, xAxis, yAxis,
  41628. * dataSorting, step, boostThreshold, boostBlending
  41629. * @product highcharts
  41630. * @apioption series.pie
  41631. */
  41632. /**
  41633. * An array of data points for the series. For the `pie` series type,
  41634. * points can be given in the following ways:
  41635. *
  41636. * 1. An array of numerical values. In this case, the numerical values will be
  41637. * interpreted as `y` options. Example:
  41638. * ```js
  41639. * data: [0, 5, 3, 5]
  41640. * ```
  41641. *
  41642. * 2. An array of objects with named values. The following snippet shows only a
  41643. * few settings, see the complete options set below. If the total number of
  41644. * data points exceeds the series'
  41645. * [turboThreshold](#series.pie.turboThreshold),
  41646. * this option is not available.
  41647. * ```js
  41648. * data: [{
  41649. * y: 1,
  41650. * name: "Point2",
  41651. * color: "#00FF00"
  41652. * }, {
  41653. * y: 7,
  41654. * name: "Point1",
  41655. * color: "#FF00FF"
  41656. * }]
  41657. * ```
  41658. *
  41659. * @sample {highcharts} highcharts/chart/reflow-true/
  41660. * Numerical values
  41661. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  41662. * Arrays of numeric x and y
  41663. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  41664. * Arrays of datetime x and y
  41665. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  41666. * Arrays of point.name and y
  41667. * @sample {highcharts} highcharts/series/data-array-of-objects/
  41668. * Config objects
  41669. *
  41670. * @type {Array<number|Array<string,(number|null)>|null|*>}
  41671. * @extends series.line.data
  41672. * @excluding marker, x
  41673. * @product highcharts
  41674. * @apioption series.pie.data
  41675. */
  41676. /**
  41677. * @type {Highcharts.SeriesPieDataLabelsOptionsObject}
  41678. * @product highcharts
  41679. * @apioption series.pie.data.dataLabels
  41680. */
  41681. /**
  41682. * The sequential index of the data point in the legend.
  41683. *
  41684. * @type {number}
  41685. * @product highcharts
  41686. * @apioption series.pie.data.legendIndex
  41687. */
  41688. /**
  41689. * Whether to display a slice offset from the center.
  41690. *
  41691. * @sample {highcharts} highcharts/point/sliced/
  41692. * One sliced point
  41693. *
  41694. * @type {boolean}
  41695. * @product highcharts
  41696. * @apioption series.pie.data.sliced
  41697. */
  41698. /**
  41699. * @excluding legendItemClick
  41700. * @product highcharts
  41701. * @apioption series.pie.events
  41702. */
  41703. ''; // placeholder for transpiled doclets above
  41704. });
  41705. _registerModule(_modules, 'Core/Series/DataLabels.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  41706. /* *
  41707. *
  41708. * (c) 2010-2020 Torstein Honsi
  41709. *
  41710. * License: www.highcharts.com/license
  41711. *
  41712. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41713. *
  41714. * */
  41715. var noop = H.noop,
  41716. seriesTypes = H.seriesTypes;
  41717. var arrayMax = U.arrayMax,
  41718. clamp = U.clamp,
  41719. defined = U.defined,
  41720. extend = U.extend,
  41721. fireEvent = U.fireEvent,
  41722. format = U.format,
  41723. getDeferredAnimation = U.getDeferredAnimation,
  41724. isArray = U.isArray,
  41725. merge = U.merge,
  41726. objectEach = U.objectEach,
  41727. pick = U.pick,
  41728. relativeLength = U.relativeLength,
  41729. splat = U.splat,
  41730. stableSort = U.stableSort;
  41731. /**
  41732. * Callback JavaScript function to format the data label as a string. Note that
  41733. * if a `format` is defined, the format takes precedence and the formatter is
  41734. * ignored.
  41735. *
  41736. * @callback Highcharts.DataLabelsFormatterCallbackFunction
  41737. *
  41738. * @param {Highcharts.PointLabelObject} this
  41739. * Data label context to format
  41740. *
  41741. * @param {Highcharts.DataLabelsOptions} options
  41742. * [API options](/highcharts/plotOptions.series.dataLabels) of the data label
  41743. *
  41744. * @return {number|string|null|undefined}
  41745. * Formatted data label text
  41746. */
  41747. /**
  41748. * Values for handling data labels that flow outside the plot area.
  41749. *
  41750. * @typedef {"allow"|"justify"} Highcharts.DataLabelsOverflowValue
  41751. */
  41752. var Series = H.Series;
  41753. /* eslint-disable valid-jsdoc */
  41754. /**
  41755. * General distribution algorithm for distributing labels of differing size
  41756. * along a confined length in two dimensions. The algorithm takes an array of
  41757. * objects containing a size, a target and a rank. It will place the labels as
  41758. * close as possible to their targets, skipping the lowest ranked labels if
  41759. * necessary.
  41760. *
  41761. * @private
  41762. * @function Highcharts.distribute
  41763. * @param {Highcharts.DataLabelsBoxArray} boxes
  41764. * @param {number} len
  41765. * @param {number} [maxDistance]
  41766. * @return {void}
  41767. */
  41768. H.distribute = function (boxes, len, maxDistance) {
  41769. var i,
  41770. overlapping = true,
  41771. origBoxes = boxes, // Original array will be altered with added .pos
  41772. restBoxes = [], // The outranked overshoot
  41773. box,
  41774. target,
  41775. total = 0,
  41776. reducedLen = origBoxes.reducedLen || len;
  41777. /**
  41778. * @private
  41779. */
  41780. function sortByTarget(a, b) {
  41781. return a.target - b.target;
  41782. }
  41783. // If the total size exceeds the len, remove those boxes with the lowest
  41784. // rank
  41785. i = boxes.length;
  41786. while (i--) {
  41787. total += boxes[i].size;
  41788. }
  41789. // Sort by rank, then slice away overshoot
  41790. if (total > reducedLen) {
  41791. stableSort(boxes, function (a, b) {
  41792. return (b.rank || 0) - (a.rank || 0);
  41793. });
  41794. i = 0;
  41795. total = 0;
  41796. while (total <= reducedLen) {
  41797. total += boxes[i].size;
  41798. i++;
  41799. }
  41800. restBoxes = boxes.splice(i - 1, boxes.length);
  41801. }
  41802. // Order by target
  41803. stableSort(boxes, sortByTarget);
  41804. // So far we have been mutating the original array. Now
  41805. // create a copy with target arrays
  41806. boxes = boxes.map(function (box) {
  41807. return {
  41808. size: box.size,
  41809. targets: [box.target],
  41810. align: pick(box.align, 0.5)
  41811. };
  41812. });
  41813. while (overlapping) {
  41814. // Initial positions: target centered in box
  41815. i = boxes.length;
  41816. while (i--) {
  41817. box = boxes[i];
  41818. // Composite box, average of targets
  41819. target = (Math.min.apply(0, box.targets) +
  41820. Math.max.apply(0, box.targets)) / 2;
  41821. box.pos = clamp(target - box.size * box.align, 0, len - box.size);
  41822. }
  41823. // Detect overlap and join boxes
  41824. i = boxes.length;
  41825. overlapping = false;
  41826. while (i--) {
  41827. // Overlap
  41828. if (i > 0 &&
  41829. boxes[i - 1].pos + boxes[i - 1].size >
  41830. boxes[i].pos) {
  41831. // Add this size to the previous box
  41832. boxes[i - 1].size += boxes[i].size;
  41833. boxes[i - 1].targets = boxes[i - 1]
  41834. .targets
  41835. .concat(boxes[i].targets);
  41836. boxes[i - 1].align = 0.5;
  41837. // Overlapping right, push left
  41838. if (boxes[i - 1].pos + boxes[i - 1].size > len) {
  41839. boxes[i - 1].pos = len - boxes[i - 1].size;
  41840. }
  41841. boxes.splice(i, 1); // Remove this item
  41842. overlapping = true;
  41843. }
  41844. }
  41845. }
  41846. // Add the rest (hidden boxes)
  41847. origBoxes.push.apply(origBoxes, restBoxes);
  41848. // Now the composite boxes are placed, we need to put the original boxes
  41849. // within them
  41850. i = 0;
  41851. boxes.some(function (box) {
  41852. var posInCompositeBox = 0;
  41853. if (box.targets.some(function () {
  41854. origBoxes[i].pos = box.pos + posInCompositeBox;
  41855. // If the distance between the position and the target exceeds
  41856. // maxDistance, abort the loop and decrease the length in increments
  41857. // of 10% to recursively reduce the number of visible boxes by
  41858. // rank. Once all boxes are within the maxDistance, we're good.
  41859. if (typeof maxDistance !== 'undefined' &&
  41860. Math.abs(origBoxes[i].pos - origBoxes[i].target) > maxDistance) {
  41861. // Reset the positions that are already set
  41862. origBoxes.slice(0, i + 1).forEach(function (box) {
  41863. delete box.pos;
  41864. });
  41865. // Try with a smaller length
  41866. origBoxes.reducedLen =
  41867. (origBoxes.reducedLen || len) - (len * 0.1);
  41868. // Recurse
  41869. if (origBoxes.reducedLen > len * 0.1) {
  41870. H.distribute(origBoxes, len, maxDistance);
  41871. }
  41872. // Exceeded maxDistance => abort
  41873. return true;
  41874. }
  41875. posInCompositeBox += origBoxes[i].size;
  41876. i++;
  41877. })) {
  41878. // Exceeded maxDistance => abort
  41879. return true;
  41880. }
  41881. });
  41882. // Add the rest (hidden) boxes and sort by target
  41883. stableSort(origBoxes, sortByTarget);
  41884. };
  41885. /**
  41886. * Draw the data labels
  41887. *
  41888. * @private
  41889. * @function Highcharts.Series#drawDataLabels
  41890. * @return {void}
  41891. * @fires Highcharts.Series#event:afterDrawDataLabels
  41892. */
  41893. Series.prototype.drawDataLabels = function () {
  41894. var series = this,
  41895. chart = series.chart,
  41896. seriesOptions = series.options,
  41897. seriesDlOptions = seriesOptions.dataLabels,
  41898. points = series.points,
  41899. pointOptions,
  41900. hasRendered = series.hasRendered || 0,
  41901. dataLabelsGroup,
  41902. dataLabelAnim = seriesDlOptions.animation,
  41903. animationConfig = seriesDlOptions.defer ?
  41904. getDeferredAnimation(chart,
  41905. dataLabelAnim,
  41906. series) :
  41907. { defer: 0,
  41908. duration: 0 },
  41909. renderer = chart.renderer;
  41910. /**
  41911. * Handle the dataLabels.filter option.
  41912. * @private
  41913. */
  41914. function applyFilter(point, options) {
  41915. var filter = options.filter,
  41916. op,
  41917. prop,
  41918. val;
  41919. if (filter) {
  41920. op = filter.operator;
  41921. prop = point[filter.property];
  41922. val = filter.value;
  41923. if ((op === '>' && prop > val) ||
  41924. (op === '<' && prop < val) ||
  41925. (op === '>=' && prop >= val) ||
  41926. (op === '<=' && prop <= val) ||
  41927. (op === '==' && prop == val) || // eslint-disable-line eqeqeq
  41928. (op === '===' && prop === val)) {
  41929. return true;
  41930. }
  41931. return false;
  41932. }
  41933. return true;
  41934. }
  41935. /**
  41936. * Merge two objects that can be arrays. If one of them is an array, the
  41937. * other is merged into each element. If both are arrays, each element is
  41938. * merged by index. If neither are arrays, we use normal merge.
  41939. * @private
  41940. */
  41941. function mergeArrays(one, two) {
  41942. var res = [],
  41943. i;
  41944. if (isArray(one) && !isArray(two)) {
  41945. res = one.map(function (el) {
  41946. return merge(el, two);
  41947. });
  41948. }
  41949. else if (isArray(two) && !isArray(one)) {
  41950. res = two.map(function (el) {
  41951. return merge(one, el);
  41952. });
  41953. }
  41954. else if (!isArray(one) && !isArray(two)) {
  41955. res = merge(one, two);
  41956. }
  41957. else {
  41958. i = Math.max(one.length, two.length);
  41959. while (i--) {
  41960. res[i] = merge(one[i], two[i]);
  41961. }
  41962. }
  41963. return res;
  41964. }
  41965. // Merge in plotOptions.dataLabels for series
  41966. seriesDlOptions = mergeArrays(mergeArrays(chart.options.plotOptions &&
  41967. chart.options.plotOptions.series &&
  41968. chart.options.plotOptions.series.dataLabels, chart.options.plotOptions &&
  41969. chart.options.plotOptions[series.type] &&
  41970. chart.options.plotOptions[series.type].dataLabels), seriesDlOptions);
  41971. fireEvent(this, 'drawDataLabels');
  41972. if (isArray(seriesDlOptions) ||
  41973. seriesDlOptions.enabled ||
  41974. series._hasPointLabels) {
  41975. // Create a separate group for the data labels to avoid rotation
  41976. dataLabelsGroup = series.plotGroup('dataLabelsGroup', 'data-labels', !hasRendered ? 'hidden' : 'inherit', // #5133, #10220
  41977. seriesDlOptions.zIndex || 6);
  41978. dataLabelsGroup.attr({ opacity: +hasRendered }); // #3300
  41979. if (!hasRendered) {
  41980. var group = series.dataLabelsGroup;
  41981. if (group) {
  41982. if (series.visible) { // #2597, #3023, #3024
  41983. dataLabelsGroup.show(true);
  41984. }
  41985. group[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, animationConfig);
  41986. }
  41987. }
  41988. // Make the labels for each point
  41989. points.forEach(function (point) {
  41990. // Merge in series options for the point.
  41991. // @note dataLabelAttribs (like pointAttribs) would eradicate
  41992. // the need for dlOptions, and simplify the section below.
  41993. pointOptions = splat(mergeArrays(seriesDlOptions, point.dlOptions || // dlOptions is used in treemaps
  41994. (point.options && point.options.dataLabels)));
  41995. // Handle each individual data label for this point
  41996. pointOptions.forEach(function (labelOptions, i) {
  41997. // Options for one datalabel
  41998. var labelEnabled = (labelOptions.enabled &&
  41999. // #2282, #4641, #7112, #10049
  42000. (!point.isNull || point.dataLabelOnNull) &&
  42001. applyFilter(point,
  42002. labelOptions)),
  42003. labelConfig,
  42004. formatString,
  42005. labelText,
  42006. style,
  42007. rotation,
  42008. attr,
  42009. dataLabel = point.dataLabels ? point.dataLabels[i] :
  42010. point.dataLabel,
  42011. connector = point.connectors ? point.connectors[i] :
  42012. point.connector,
  42013. labelDistance = pick(labelOptions.distance,
  42014. point.labelDistance),
  42015. isNew = !dataLabel;
  42016. if (labelEnabled) {
  42017. // Create individual options structure that can be extended
  42018. // without affecting others
  42019. labelConfig = point.getLabelConfig();
  42020. formatString = pick(labelOptions[point.formatPrefix + 'Format'], labelOptions.format);
  42021. labelText = defined(formatString) ?
  42022. format(formatString, labelConfig, chart) :
  42023. (labelOptions[point.formatPrefix + 'Formatter'] ||
  42024. labelOptions.formatter).call(labelConfig, labelOptions);
  42025. style = labelOptions.style;
  42026. rotation = labelOptions.rotation;
  42027. if (!chart.styledMode) {
  42028. // Determine the color
  42029. style.color = pick(labelOptions.color, style.color, series.color, '#000000');
  42030. // Get automated contrast color
  42031. if (style.color === 'contrast') {
  42032. point.contrastColor = renderer.getContrast((point.color || series.color));
  42033. style.color = (!defined(labelDistance) &&
  42034. labelOptions.inside) ||
  42035. labelDistance < 0 ||
  42036. !!seriesOptions.stacking ?
  42037. point.contrastColor :
  42038. '#000000';
  42039. }
  42040. else {
  42041. delete point.contrastColor;
  42042. }
  42043. if (seriesOptions.cursor) {
  42044. style.cursor = seriesOptions.cursor;
  42045. }
  42046. }
  42047. attr = {
  42048. r: labelOptions.borderRadius || 0,
  42049. rotation: rotation,
  42050. padding: labelOptions.padding,
  42051. zIndex: 1
  42052. };
  42053. if (!chart.styledMode) {
  42054. attr.fill = labelOptions.backgroundColor;
  42055. attr.stroke = labelOptions.borderColor;
  42056. attr['stroke-width'] = labelOptions.borderWidth;
  42057. }
  42058. // Remove unused attributes (#947)
  42059. objectEach(attr, function (val, name) {
  42060. if (typeof val === 'undefined') {
  42061. delete attr[name];
  42062. }
  42063. });
  42064. }
  42065. // If the point is outside the plot area, destroy it. #678, #820
  42066. if (dataLabel && (!labelEnabled || !defined(labelText))) {
  42067. point.dataLabel =
  42068. point.dataLabel && point.dataLabel.destroy();
  42069. if (point.dataLabels) {
  42070. // Remove point.dataLabels if this was the last one
  42071. if (point.dataLabels.length === 1) {
  42072. delete point.dataLabels;
  42073. }
  42074. else {
  42075. delete point.dataLabels[i];
  42076. }
  42077. }
  42078. if (!i) {
  42079. delete point.dataLabel;
  42080. }
  42081. if (connector) {
  42082. point.connector = point.connector.destroy();
  42083. if (point.connectors) {
  42084. // Remove point.connectors if this was the last one
  42085. if (point.connectors.length === 1) {
  42086. delete point.connectors;
  42087. }
  42088. else {
  42089. delete point.connectors[i];
  42090. }
  42091. }
  42092. }
  42093. // Individual labels are disabled if the are explicitly disabled
  42094. // in the point options, or if they fall outside the plot area.
  42095. }
  42096. else if (labelEnabled && defined(labelText)) {
  42097. if (!dataLabel) {
  42098. // Create new label element
  42099. point.dataLabels = point.dataLabels || [];
  42100. dataLabel = point.dataLabels[i] = rotation ?
  42101. // Labels don't rotate, use text element
  42102. renderer.text(labelText, 0, -9999, labelOptions.useHTML)
  42103. .addClass('highcharts-data-label') :
  42104. // We can use label
  42105. renderer.label(labelText, 0, -9999, labelOptions.shape, null, null, labelOptions.useHTML, null, 'data-label');
  42106. // Store for backwards compatibility
  42107. if (!i) {
  42108. point.dataLabel = dataLabel;
  42109. }
  42110. dataLabel.addClass(' highcharts-data-label-color-' + point.colorIndex +
  42111. ' ' + (labelOptions.className || '') +
  42112. ( // #3398
  42113. labelOptions.useHTML ?
  42114. ' highcharts-tracker' :
  42115. ''));
  42116. }
  42117. else {
  42118. // Use old element and just update text
  42119. attr.text = labelText;
  42120. }
  42121. // Store data label options for later access
  42122. dataLabel.options = labelOptions;
  42123. dataLabel.attr(attr);
  42124. if (!chart.styledMode) {
  42125. // Styles must be applied before add in order to read
  42126. // text bounding box
  42127. dataLabel.css(style).shadow(labelOptions.shadow);
  42128. }
  42129. if (!dataLabel.added) {
  42130. dataLabel.add(dataLabelsGroup);
  42131. }
  42132. if (labelOptions.textPath && !labelOptions.useHTML) {
  42133. dataLabel.setTextPath((point.getDataLabelPath &&
  42134. point.getDataLabelPath(dataLabel)) || point.graphic, labelOptions.textPath);
  42135. if (point.dataLabelPath &&
  42136. !labelOptions.textPath.enabled) {
  42137. // clean the DOM
  42138. point.dataLabelPath = point.dataLabelPath.destroy();
  42139. }
  42140. }
  42141. // Now the data label is created and placed at 0,0, so we
  42142. // need to align it
  42143. series.alignDataLabel(point, dataLabel, labelOptions, null, isNew);
  42144. }
  42145. });
  42146. });
  42147. }
  42148. fireEvent(this, 'afterDrawDataLabels');
  42149. };
  42150. /**
  42151. * Align each individual data label.
  42152. *
  42153. * @private
  42154. * @function Highcharts.Series#alignDataLabel
  42155. * @param {Highcharts.Point} point
  42156. * @param {Highcharts.SVGElement} dataLabel
  42157. * @param {Highcharts.DataLabelsOptions} options
  42158. * @param {Highcharts.BBoxObject} alignTo
  42159. * @param {boolean} [isNew]
  42160. * @return {void}
  42161. */
  42162. Series.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
  42163. var series = this,
  42164. chart = this.chart,
  42165. inverted = this.isCartesian && chart.inverted,
  42166. enabledDataSorting = this.enabledDataSorting,
  42167. plotX = pick(point.dlBox && point.dlBox.centerX,
  42168. point.plotX, -9999),
  42169. plotY = pick(point.plotY, -9999),
  42170. bBox = dataLabel.getBBox(),
  42171. baseline,
  42172. rotation = options.rotation,
  42173. normRotation,
  42174. negRotation,
  42175. align = options.align,
  42176. rotCorr, // rotation correction
  42177. isInsidePlot = chart.isInsidePlot(plotX,
  42178. Math.round(plotY),
  42179. inverted),
  42180. // Math.round for rounding errors (#2683), alignTo to allow column
  42181. // labels (#2700)
  42182. alignAttr, // the final position;
  42183. justify = pick(options.overflow, (enabledDataSorting ? 'none' : 'justify')) === 'justify', visible = this.visible &&
  42184. point.visible !== false &&
  42185. (point.series.forceDL ||
  42186. (enabledDataSorting && !justify) ||
  42187. isInsidePlot ||
  42188. (
  42189. // If the data label is inside the align box, it is enough
  42190. // that parts of the align box is inside the plot area
  42191. // (#12370)
  42192. options.inside && alignTo && chart.isInsidePlot(plotX, inverted ?
  42193. alignTo.x + 1 :
  42194. alignTo.y + alignTo.height - 1, inverted))), setStartPos = function (alignOptions) {
  42195. if (enabledDataSorting && series.xAxis && !justify) {
  42196. series.setDataLabelStartPos(point, dataLabel, isNew, isInsidePlot, alignOptions);
  42197. }
  42198. };
  42199. if (visible) {
  42200. baseline = chart.renderer.fontMetrics(chart.styledMode ? void 0 : options.style.fontSize, dataLabel).b;
  42201. // The alignment box is a singular point
  42202. alignTo = extend({
  42203. x: inverted ? this.yAxis.len - plotY : plotX,
  42204. y: Math.round(inverted ? this.xAxis.len - plotX : plotY),
  42205. width: 0,
  42206. height: 0
  42207. }, alignTo);
  42208. // Add the text size for alignment calculation
  42209. extend(options, {
  42210. width: bBox.width,
  42211. height: bBox.height
  42212. });
  42213. // Allow a hook for changing alignment in the last moment, then do the
  42214. // alignment
  42215. if (rotation) {
  42216. justify = false; // Not supported for rotated text
  42217. rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
  42218. alignAttr = {
  42219. x: (alignTo.x +
  42220. (options.x || 0) +
  42221. alignTo.width / 2 +
  42222. rotCorr.x),
  42223. y: (alignTo.y +
  42224. (options.y || 0) +
  42225. { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
  42226. alignTo.height)
  42227. };
  42228. setStartPos(alignAttr); // data sorting
  42229. dataLabel[isNew ? 'attr' : 'animate'](alignAttr)
  42230. .attr({
  42231. align: align
  42232. });
  42233. // Compensate for the rotated label sticking out on the sides
  42234. normRotation = (rotation + 720) % 360;
  42235. negRotation = normRotation > 180 && normRotation < 360;
  42236. if (align === 'left') {
  42237. alignAttr.y -= negRotation ? bBox.height : 0;
  42238. }
  42239. else if (align === 'center') {
  42240. alignAttr.x -= bBox.width / 2;
  42241. alignAttr.y -= bBox.height / 2;
  42242. }
  42243. else if (align === 'right') {
  42244. alignAttr.x -= bBox.width;
  42245. alignAttr.y -= negRotation ? 0 : bBox.height;
  42246. }
  42247. dataLabel.placed = true;
  42248. dataLabel.alignAttr = alignAttr;
  42249. }
  42250. else {
  42251. setStartPos(alignTo); // data sorting
  42252. dataLabel.align(options, null, alignTo);
  42253. alignAttr = dataLabel.alignAttr;
  42254. }
  42255. // Handle justify or crop
  42256. if (justify && alignTo.height >= 0) { // #8830
  42257. this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew);
  42258. // Now check that the data label is within the plot area
  42259. }
  42260. else if (pick(options.crop, true)) {
  42261. visible =
  42262. chart.isInsidePlot(alignAttr.x, alignAttr.y) &&
  42263. chart.isInsidePlot(alignAttr.x + bBox.width, alignAttr.y + bBox.height);
  42264. }
  42265. // When we're using a shape, make it possible with a connector or an
  42266. // arrow pointing to thie point
  42267. if (options.shape && !rotation) {
  42268. dataLabel[isNew ? 'attr' : 'animate']({
  42269. anchorX: inverted ?
  42270. chart.plotWidth - point.plotY :
  42271. point.plotX,
  42272. anchorY: inverted ?
  42273. chart.plotHeight - point.plotX :
  42274. point.plotY
  42275. });
  42276. }
  42277. }
  42278. // To use alignAttr property in hideOverlappingLabels
  42279. if (isNew && enabledDataSorting) {
  42280. dataLabel.placed = false;
  42281. }
  42282. // Show or hide based on the final aligned position
  42283. if (!visible && (!enabledDataSorting || justify)) {
  42284. dataLabel.hide(true);
  42285. dataLabel.placed = false; // don't animate back in
  42286. }
  42287. };
  42288. /**
  42289. * Set starting position for data label sorting animation.
  42290. *
  42291. * @private
  42292. * @function Highcharts.Series#setDataLabelStartPos
  42293. * @param {Highcharts.SVGElement} dataLabel
  42294. * @param {Highcharts.ColumnPoint} point
  42295. * @param {boolean | undefined} [isNew]
  42296. * @param {boolean} [isInside]
  42297. * @param {Highcharts.AlignObject} [alignOptions]
  42298. *
  42299. * @return {void}
  42300. */
  42301. Series.prototype.setDataLabelStartPos = function (point, dataLabel, isNew, isInside, alignOptions) {
  42302. var chart = this.chart,
  42303. inverted = chart.inverted,
  42304. xAxis = this.xAxis,
  42305. reversed = xAxis.reversed,
  42306. labelCenter = inverted ? dataLabel.height / 2 : dataLabel.width / 2,
  42307. pointWidth = point.pointWidth,
  42308. halfWidth = pointWidth ? pointWidth / 2 : 0,
  42309. startXPos,
  42310. startYPos;
  42311. startXPos = inverted ?
  42312. alignOptions.x :
  42313. (reversed ?
  42314. -labelCenter - halfWidth :
  42315. xAxis.width - labelCenter + halfWidth);
  42316. startYPos = inverted ?
  42317. (reversed ?
  42318. this.yAxis.height - labelCenter + halfWidth :
  42319. -labelCenter - halfWidth) : alignOptions.y;
  42320. dataLabel.startXPos = startXPos;
  42321. dataLabel.startYPos = startYPos;
  42322. // We need to handle visibility in case of sorting point outside plot area
  42323. if (!isInside) {
  42324. dataLabel
  42325. .attr({ opacity: 1 })
  42326. .animate({ opacity: 0 }, void 0, dataLabel.hide);
  42327. }
  42328. else if (dataLabel.visibility === 'hidden') {
  42329. dataLabel.show();
  42330. dataLabel
  42331. .attr({ opacity: 0 })
  42332. .animate({ opacity: 1 });
  42333. }
  42334. // Save start position on first render, but do not change position
  42335. if (!chart.hasRendered) {
  42336. return;
  42337. }
  42338. // Set start position
  42339. if (isNew) {
  42340. dataLabel.attr({ x: dataLabel.startXPos, y: dataLabel.startYPos });
  42341. }
  42342. dataLabel.placed = true;
  42343. };
  42344. /**
  42345. * If data labels fall partly outside the plot area, align them back in, in a
  42346. * way that doesn't hide the point.
  42347. *
  42348. * @private
  42349. * @function Highcharts.Series#justifyDataLabel
  42350. * @param {Highcharts.SVGElement} dataLabel
  42351. * @param {Highcharts.DataLabelsOptions} options
  42352. * @param {Highcharts.SVGAttributes} alignAttr
  42353. * @param {Highcharts.BBoxObject} bBox
  42354. * @param {Highcharts.BBoxObject} [alignTo]
  42355. * @param {boolean} [isNew]
  42356. * @return {boolean|undefined}
  42357. */
  42358. Series.prototype.justifyDataLabel = function (dataLabel, options, alignAttr, bBox, alignTo, isNew) {
  42359. var chart = this.chart,
  42360. align = options.align,
  42361. verticalAlign = options.verticalAlign,
  42362. off,
  42363. justified,
  42364. padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
  42365. var _a = options.x,
  42366. x = _a === void 0 ? 0 : _a,
  42367. _b = options.y,
  42368. y = _b === void 0 ? 0 : _b;
  42369. // Off left
  42370. off = alignAttr.x + padding;
  42371. if (off < 0) {
  42372. if (align === 'right' && x >= 0) {
  42373. options.align = 'left';
  42374. options.inside = true;
  42375. }
  42376. else {
  42377. x -= off;
  42378. }
  42379. justified = true;
  42380. }
  42381. // Off right
  42382. off = alignAttr.x + bBox.width - padding;
  42383. if (off > chart.plotWidth) {
  42384. if (align === 'left' && x <= 0) {
  42385. options.align = 'right';
  42386. options.inside = true;
  42387. }
  42388. else {
  42389. x += chart.plotWidth - off;
  42390. }
  42391. justified = true;
  42392. }
  42393. // Off top
  42394. off = alignAttr.y + padding;
  42395. if (off < 0) {
  42396. if (verticalAlign === 'bottom' && y >= 0) {
  42397. options.verticalAlign = 'top';
  42398. options.inside = true;
  42399. }
  42400. else {
  42401. y -= off;
  42402. }
  42403. justified = true;
  42404. }
  42405. // Off bottom
  42406. off = alignAttr.y + bBox.height - padding;
  42407. if (off > chart.plotHeight) {
  42408. if (verticalAlign === 'top' && y <= 0) {
  42409. options.verticalAlign = 'bottom';
  42410. options.inside = true;
  42411. }
  42412. else {
  42413. y += chart.plotHeight - off;
  42414. }
  42415. justified = true;
  42416. }
  42417. if (justified) {
  42418. options.x = x;
  42419. options.y = y;
  42420. dataLabel.placed = !isNew;
  42421. dataLabel.align(options, void 0, alignTo);
  42422. }
  42423. return justified;
  42424. };
  42425. if (seriesTypes.pie) {
  42426. seriesTypes.pie.prototype.dataLabelPositioners = {
  42427. // Based on the value computed in Highcharts' distribute algorithm.
  42428. radialDistributionY: function (point) {
  42429. return point.top + point.distributeBox.pos;
  42430. },
  42431. // get the x - use the natural x position for labels near the
  42432. // top and bottom, to prevent the top and botton slice
  42433. // connectors from touching each other on either side
  42434. // Based on the value computed in Highcharts' distribute algorithm.
  42435. radialDistributionX: function (series, point, y, naturalY) {
  42436. return series.getX(y < point.top + 2 || y > point.bottom - 2 ?
  42437. naturalY :
  42438. y, point.half, point);
  42439. },
  42440. // dataLabels.distance determines the x position of the label
  42441. justify: function (point, radius, seriesCenter) {
  42442. return seriesCenter[0] + (point.half ? -1 : 1) *
  42443. (radius + point.labelDistance);
  42444. },
  42445. // Left edges of the left-half labels touch the left edge of the plot
  42446. // area. Right edges of the right-half labels touch the right edge of
  42447. // the plot area.
  42448. alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
  42449. var dataLabelWidth = dataLabel.getBBox().width;
  42450. return half ? dataLabelWidth + plotLeft :
  42451. plotWidth - dataLabelWidth - plotLeft;
  42452. },
  42453. // Connectors of each side end in the same x position. Labels are
  42454. // aligned to them. Left edge of the widest left-half label touches the
  42455. // left edge of the plot area. Right edge of the widest right-half label
  42456. // touches the right edge of the plot area.
  42457. alignToConnectors: function (points, half, plotWidth, plotLeft) {
  42458. var maxDataLabelWidth = 0,
  42459. dataLabelWidth;
  42460. // find widest data label
  42461. points.forEach(function (point) {
  42462. dataLabelWidth = point.dataLabel.getBBox().width;
  42463. if (dataLabelWidth > maxDataLabelWidth) {
  42464. maxDataLabelWidth = dataLabelWidth;
  42465. }
  42466. });
  42467. return half ? maxDataLabelWidth + plotLeft :
  42468. plotWidth - maxDataLabelWidth - plotLeft;
  42469. }
  42470. };
  42471. /**
  42472. * Override the base drawDataLabels method by pie specific functionality
  42473. *
  42474. * @private
  42475. * @function Highcharts.seriesTypes.pie#drawDataLabels
  42476. * @return {void}
  42477. */
  42478. seriesTypes.pie.prototype.drawDataLabels = function () {
  42479. var series = this,
  42480. data = series.data,
  42481. point,
  42482. chart = series.chart,
  42483. options = series.options.dataLabels || {},
  42484. connectorPadding = options.connectorPadding,
  42485. connectorWidth,
  42486. plotWidth = chart.plotWidth,
  42487. plotHeight = chart.plotHeight,
  42488. plotLeft = chart.plotLeft,
  42489. maxWidth = Math.round(chart.chartWidth / 3),
  42490. connector,
  42491. seriesCenter = series.center,
  42492. radius = seriesCenter[2] / 2,
  42493. centerY = seriesCenter[1],
  42494. dataLabel,
  42495. dataLabelWidth,
  42496. // labelPos,
  42497. labelPosition,
  42498. labelHeight,
  42499. // divide the points into right and left halves for anti collision
  42500. halves = [
  42501. [],
  42502. [] // left
  42503. ],
  42504. x,
  42505. y,
  42506. visibility,
  42507. j,
  42508. overflow = [0, 0, 0, 0], // top, right, bottom, left
  42509. dataLabelPositioners = series.dataLabelPositioners,
  42510. pointDataLabelsOptions;
  42511. // get out if not enabled
  42512. if (!series.visible ||
  42513. (!options.enabled &&
  42514. !series._hasPointLabels)) {
  42515. return;
  42516. }
  42517. // Reset all labels that have been shortened
  42518. data.forEach(function (point) {
  42519. if (point.dataLabel && point.visible && point.dataLabel.shortened) {
  42520. point.dataLabel
  42521. .attr({
  42522. width: 'auto'
  42523. }).css({
  42524. width: 'auto',
  42525. textOverflow: 'clip'
  42526. });
  42527. point.dataLabel.shortened = false;
  42528. }
  42529. });
  42530. // run parent method
  42531. Series.prototype.drawDataLabels.apply(series);
  42532. data.forEach(function (point) {
  42533. if (point.dataLabel) {
  42534. if (point.visible) { // #407, #2510
  42535. // Arrange points for detection collision
  42536. halves[point.half].push(point);
  42537. // Reset positions (#4905)
  42538. point.dataLabel._pos = null;
  42539. // Avoid long labels squeezing the pie size too far down
  42540. if (!defined(options.style.width) &&
  42541. !defined(point.options.dataLabels &&
  42542. point.options.dataLabels.style &&
  42543. point.options.dataLabels.style.width)) {
  42544. if (point.dataLabel.getBBox().width > maxWidth) {
  42545. point.dataLabel.css({
  42546. // Use a fraction of the maxWidth to avoid
  42547. // wrapping close to the end of the string.
  42548. width: Math.round(maxWidth * 0.7) + 'px'
  42549. });
  42550. point.dataLabel.shortened = true;
  42551. }
  42552. }
  42553. }
  42554. else {
  42555. point.dataLabel = point.dataLabel.destroy();
  42556. // Workaround to make pies destroy multiple datalabels
  42557. // correctly. This logic needs rewriting to support multiple
  42558. // datalabels fully.
  42559. if (point.dataLabels && point.dataLabels.length === 1) {
  42560. delete point.dataLabels;
  42561. }
  42562. }
  42563. }
  42564. });
  42565. /* Loop over the points in each half, starting from the top and bottom
  42566. * of the pie to detect overlapping labels.
  42567. */
  42568. halves.forEach(function (points, i) {
  42569. var top,
  42570. bottom,
  42571. length = points.length,
  42572. positions = [],
  42573. naturalY,
  42574. sideOverflow,
  42575. size,
  42576. distributionLength;
  42577. if (!length) {
  42578. return;
  42579. }
  42580. // Sort by angle
  42581. series.sortByAngle(points, i - 0.5);
  42582. // Only do anti-collision when we have dataLabels outside the pie
  42583. // and have connectors. (#856)
  42584. if (series.maxLabelDistance > 0) {
  42585. top = Math.max(0, centerY - radius - series.maxLabelDistance);
  42586. bottom = Math.min(centerY + radius + series.maxLabelDistance, chart.plotHeight);
  42587. points.forEach(function (point) {
  42588. // check if specific points' label is outside the pie
  42589. if (point.labelDistance > 0 && point.dataLabel) {
  42590. // point.top depends on point.labelDistance value
  42591. // Used for calculation of y value in getX method
  42592. point.top = Math.max(0, centerY - radius - point.labelDistance);
  42593. point.bottom = Math.min(centerY + radius + point.labelDistance, chart.plotHeight);
  42594. size = point.dataLabel.getBBox().height || 21;
  42595. // point.positionsIndex is needed for getting index of
  42596. // parameter related to specific point inside positions
  42597. // array - not every point is in positions array.
  42598. point.distributeBox = {
  42599. target: point.labelPosition.natural.y -
  42600. point.top + size / 2,
  42601. size: size,
  42602. rank: point.y
  42603. };
  42604. positions.push(point.distributeBox);
  42605. }
  42606. });
  42607. distributionLength = bottom + size - top;
  42608. H.distribute(positions, distributionLength, distributionLength / 5);
  42609. }
  42610. // Now the used slots are sorted, fill them up sequentially
  42611. for (j = 0; j < length; j++) {
  42612. point = points[j];
  42613. // labelPos = point.labelPos;
  42614. labelPosition = point.labelPosition;
  42615. dataLabel = point.dataLabel;
  42616. visibility = point.visible === false ? 'hidden' : 'inherit';
  42617. naturalY = labelPosition.natural.y;
  42618. y = naturalY;
  42619. if (positions && defined(point.distributeBox)) {
  42620. if (typeof point.distributeBox.pos === 'undefined') {
  42621. visibility = 'hidden';
  42622. }
  42623. else {
  42624. labelHeight = point.distributeBox.size;
  42625. // Find label's y position
  42626. y = dataLabelPositioners
  42627. .radialDistributionY(point);
  42628. }
  42629. }
  42630. // It is needed to delete point.positionIndex for
  42631. // dynamically added points etc.
  42632. delete point.positionIndex; // @todo unused
  42633. // Find label's x position
  42634. // justify is undocumented in the API - preserve support for it
  42635. if (options.justify) {
  42636. x = dataLabelPositioners.justify(point, radius, seriesCenter);
  42637. }
  42638. else {
  42639. switch (options.alignTo) {
  42640. case 'connectors':
  42641. x = dataLabelPositioners.alignToConnectors(points, i, plotWidth, plotLeft);
  42642. break;
  42643. case 'plotEdges':
  42644. x = dataLabelPositioners.alignToPlotEdges(dataLabel, i, plotWidth, plotLeft);
  42645. break;
  42646. default:
  42647. x = dataLabelPositioners.radialDistributionX(series, point, y, naturalY);
  42648. }
  42649. }
  42650. // Record the placement and visibility
  42651. dataLabel._attr = {
  42652. visibility: visibility,
  42653. align: labelPosition.alignment
  42654. };
  42655. pointDataLabelsOptions = point.options.dataLabels || {};
  42656. dataLabel._pos = {
  42657. x: (x +
  42658. pick(pointDataLabelsOptions.x, options.x) + // (#12985)
  42659. ({
  42660. left: connectorPadding,
  42661. right: -connectorPadding
  42662. }[labelPosition.alignment] || 0)),
  42663. // 10 is for the baseline (label vs text)
  42664. y: (y +
  42665. pick(pointDataLabelsOptions.y, options.y) - // (#12985)
  42666. 10)
  42667. };
  42668. // labelPos.x = x;
  42669. // labelPos.y = y;
  42670. labelPosition.final.x = x;
  42671. labelPosition.final.y = y;
  42672. // Detect overflowing data labels
  42673. if (pick(options.crop, true)) {
  42674. dataLabelWidth = dataLabel.getBBox().width;
  42675. sideOverflow = null;
  42676. // Overflow left
  42677. if (x - dataLabelWidth < connectorPadding &&
  42678. i === 1 // left half
  42679. ) {
  42680. sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
  42681. overflow[3] = Math.max(sideOverflow, overflow[3]);
  42682. // Overflow right
  42683. }
  42684. else if (x + dataLabelWidth > plotWidth - connectorPadding &&
  42685. i === 0 // right half
  42686. ) {
  42687. sideOverflow = Math.round(x + dataLabelWidth - plotWidth + connectorPadding);
  42688. overflow[1] = Math.max(sideOverflow, overflow[1]);
  42689. }
  42690. // Overflow top
  42691. if (y - labelHeight / 2 < 0) {
  42692. overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
  42693. // Overflow left
  42694. }
  42695. else if (y + labelHeight / 2 > plotHeight) {
  42696. overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
  42697. }
  42698. dataLabel.sideOverflow = sideOverflow;
  42699. }
  42700. } // for each point
  42701. }); // for each half
  42702. // Do not apply the final placement and draw the connectors until we
  42703. // have verified that labels are not spilling over.
  42704. if (arrayMax(overflow) === 0 ||
  42705. this.verifyDataLabelOverflow(overflow)) {
  42706. // Place the labels in the final position
  42707. this.placeDataLabels();
  42708. this.points.forEach(function (point) {
  42709. // #8864: every connector can have individual options
  42710. pointDataLabelsOptions =
  42711. merge(options, point.options.dataLabels);
  42712. connectorWidth =
  42713. pick(pointDataLabelsOptions.connectorWidth, 1);
  42714. // Draw the connector
  42715. if (connectorWidth) {
  42716. var isNew;
  42717. connector = point.connector;
  42718. dataLabel = point.dataLabel;
  42719. if (dataLabel &&
  42720. dataLabel._pos &&
  42721. point.visible &&
  42722. point.labelDistance > 0) {
  42723. visibility = dataLabel._attr.visibility;
  42724. isNew = !connector;
  42725. if (isNew) {
  42726. point.connector = connector = chart.renderer
  42727. .path()
  42728. .addClass('highcharts-data-label-connector ' +
  42729. ' highcharts-color-' + point.colorIndex +
  42730. (point.className ?
  42731. ' ' + point.className :
  42732. ''))
  42733. .add(series.dataLabelsGroup);
  42734. if (!chart.styledMode) {
  42735. connector.attr({
  42736. 'stroke-width': connectorWidth,
  42737. 'stroke': (pointDataLabelsOptions.connectorColor ||
  42738. point.color ||
  42739. '#666666')
  42740. });
  42741. }
  42742. }
  42743. connector[isNew ? 'attr' : 'animate']({
  42744. d: point.getConnectorPath()
  42745. });
  42746. connector.attr('visibility', visibility);
  42747. }
  42748. else if (connector) {
  42749. point.connector = connector.destroy();
  42750. }
  42751. }
  42752. });
  42753. }
  42754. };
  42755. /**
  42756. * Extendable method for getting the path of the connector between the data
  42757. * label and the pie slice.
  42758. *
  42759. * @private
  42760. * @function Highcharts.seriesTypes.pie#connectorPath
  42761. *
  42762. * @param {*} labelPos
  42763. *
  42764. * @return {Highcharts.SVGPathArray}
  42765. */
  42766. // TODO: depracated - remove it
  42767. /*
  42768. seriesTypes.pie.prototype.connectorPath = function (labelPos) {
  42769. var x = labelPos.x,
  42770. y = labelPos.y;
  42771. return pick(this.options.dataLabels.softConnector, true) ? [
  42772. 'M',
  42773. // end of the string at the label
  42774. x + (labelPos[6] === 'left' ? 5 : -5), y,
  42775. 'C',
  42776. x, y, // first break, next to the label
  42777. 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
  42778. labelPos[2], labelPos[3], // second break
  42779. 'L',
  42780. labelPos[4], labelPos[5] // base
  42781. ] : [
  42782. 'M',
  42783. // end of the string at the label
  42784. x + (labelPos[6] === 'left' ? 5 : -5), y,
  42785. 'L',
  42786. labelPos[2], labelPos[3], // second break
  42787. 'L',
  42788. labelPos[4], labelPos[5] // base
  42789. ];
  42790. };
  42791. */
  42792. /**
  42793. * Perform the final placement of the data labels after we have verified
  42794. * that they fall within the plot area.
  42795. *
  42796. * @private
  42797. * @function Highcharts.seriesTypes.pie#placeDataLabels
  42798. * @return {void}
  42799. */
  42800. seriesTypes.pie.prototype.placeDataLabels = function () {
  42801. this.points.forEach(function (point) {
  42802. var dataLabel = point.dataLabel,
  42803. _pos;
  42804. if (dataLabel && point.visible) {
  42805. _pos = dataLabel._pos;
  42806. if (_pos) {
  42807. // Shorten data labels with ellipsis if they still overflow
  42808. // after the pie has reached minSize (#223).
  42809. if (dataLabel.sideOverflow) {
  42810. dataLabel._attr.width =
  42811. Math.max(dataLabel.getBBox().width -
  42812. dataLabel.sideOverflow, 0);
  42813. dataLabel.css({
  42814. width: dataLabel._attr.width + 'px',
  42815. textOverflow: ((this.options.dataLabels.style || {})
  42816. .textOverflow ||
  42817. 'ellipsis')
  42818. });
  42819. dataLabel.shortened = true;
  42820. }
  42821. dataLabel.attr(dataLabel._attr);
  42822. dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
  42823. dataLabel.moved = true;
  42824. }
  42825. else if (dataLabel) {
  42826. dataLabel.attr({ y: -9999 });
  42827. }
  42828. }
  42829. // Clear for update
  42830. delete point.distributeBox;
  42831. }, this);
  42832. };
  42833. seriesTypes.pie.prototype.alignDataLabel = noop;
  42834. /**
  42835. * Verify whether the data labels are allowed to draw, or we should run more
  42836. * translation and data label positioning to keep them inside the plot area.
  42837. * Returns true when data labels are ready to draw.
  42838. *
  42839. * @private
  42840. * @function Highcharts.seriesTypes.pie#verifyDataLabelOverflow
  42841. * @param {Array<number>} overflow
  42842. * @return {boolean}
  42843. */
  42844. seriesTypes.pie.prototype.verifyDataLabelOverflow = function (overflow) {
  42845. var center = this.center,
  42846. options = this.options,
  42847. centerOption = options.center,
  42848. minSize = options.minSize || 80,
  42849. newSize = minSize,
  42850. // If a size is set, return true and don't try to shrink the pie
  42851. // to fit the labels.
  42852. ret = options.size !== null;
  42853. if (!ret) {
  42854. // Handle horizontal size and center
  42855. if (centerOption[0] !== null) { // Fixed center
  42856. newSize = Math.max(center[2] -
  42857. Math.max(overflow[1], overflow[3]), minSize);
  42858. }
  42859. else { // Auto center
  42860. newSize = Math.max(
  42861. // horizontal overflow
  42862. center[2] - overflow[1] - overflow[3], minSize);
  42863. // horizontal center
  42864. center[0] += (overflow[3] - overflow[1]) / 2;
  42865. }
  42866. // Handle vertical size and center
  42867. if (centerOption[1] !== null) { // Fixed center
  42868. newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
  42869. }
  42870. else { // Auto center
  42871. newSize = clamp(newSize, minSize,
  42872. // vertical overflow
  42873. center[2] - overflow[0] - overflow[2]);
  42874. // vertical center
  42875. center[1] += (overflow[0] - overflow[2]) / 2;
  42876. }
  42877. // If the size must be decreased, we need to run translate and
  42878. // drawDataLabels again
  42879. if (newSize < center[2]) {
  42880. center[2] = newSize;
  42881. center[3] = Math.min(// #3632
  42882. relativeLength(options.innerSize || 0, newSize), newSize);
  42883. this.translate(center);
  42884. if (this.drawDataLabels) {
  42885. this.drawDataLabels();
  42886. }
  42887. // Else, return true to indicate that the pie and its labels is
  42888. // within the plot area
  42889. }
  42890. else {
  42891. ret = true;
  42892. }
  42893. }
  42894. return ret;
  42895. };
  42896. }
  42897. if (seriesTypes.column) {
  42898. /**
  42899. * Override the basic data label alignment by adjusting for the position of
  42900. * the column.
  42901. *
  42902. * @private
  42903. * @function Highcharts.seriesTypes.column#alignDataLabel
  42904. * @param {Highcharts.Point} point
  42905. * @param {Highcharts.SVGElement} dataLabel
  42906. * @param {Highcharts.DataLabelsOptions} options
  42907. * @param {Highcharts.BBoxObject} alignTo
  42908. * @param {boolean} [isNew]
  42909. * @return {void}
  42910. */
  42911. seriesTypes.column.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
  42912. var inverted = this.chart.inverted,
  42913. series = point.series,
  42914. // data label box for alignment
  42915. dlBox = point.dlBox || point.shapeArgs,
  42916. below = pick(point.below, // range series
  42917. point.plotY >
  42918. pick(this.translatedThreshold,
  42919. series.yAxis.len)),
  42920. // draw it inside the box?
  42921. inside = pick(options.inside, !!this.options.stacking),
  42922. overshoot;
  42923. // Align to the column itself, or the top of it
  42924. if (dlBox) { // Area range uses this method but not alignTo
  42925. alignTo = merge(dlBox);
  42926. if (alignTo.y < 0) {
  42927. alignTo.height += alignTo.y;
  42928. alignTo.y = 0;
  42929. }
  42930. // If parts of the box overshoots outside the plot area, modify the
  42931. // box to center the label inside
  42932. overshoot = alignTo.y + alignTo.height - series.yAxis.len;
  42933. if (overshoot > 0 && overshoot < alignTo.height) {
  42934. alignTo.height -= overshoot;
  42935. }
  42936. if (inverted) {
  42937. alignTo = {
  42938. x: series.yAxis.len - alignTo.y - alignTo.height,
  42939. y: series.xAxis.len - alignTo.x - alignTo.width,
  42940. width: alignTo.height,
  42941. height: alignTo.width
  42942. };
  42943. }
  42944. // Compute the alignment box
  42945. if (!inside) {
  42946. if (inverted) {
  42947. alignTo.x += below ? 0 : alignTo.width;
  42948. alignTo.width = 0;
  42949. }
  42950. else {
  42951. alignTo.y += below ? alignTo.height : 0;
  42952. alignTo.height = 0;
  42953. }
  42954. }
  42955. }
  42956. // When alignment is undefined (typically columns and bars), display the
  42957. // individual point below or above the point depending on the threshold
  42958. options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
  42959. options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
  42960. // Call the parent method
  42961. Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
  42962. // If label was justified and we have contrast, set it:
  42963. if (options.inside && point.contrastColor) {
  42964. dataLabel.css({
  42965. color: point.contrastColor
  42966. });
  42967. }
  42968. };
  42969. }
  42970. });
  42971. _registerModule(_modules, 'Extensions/OverlappingDataLabels.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js']], function (Chart, U) {
  42972. /* *
  42973. *
  42974. * Highcharts module to hide overlapping data labels.
  42975. * This module is included in Highcharts.
  42976. *
  42977. * (c) 2009-2020 Torstein Honsi
  42978. *
  42979. * License: www.highcharts.com/license
  42980. *
  42981. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42982. *
  42983. * */
  42984. var addEvent = U.addEvent,
  42985. fireEvent = U.fireEvent,
  42986. isArray = U.isArray,
  42987. isNumber = U.isNumber,
  42988. objectEach = U.objectEach,
  42989. pick = U.pick;
  42990. /* eslint-disable no-invalid-this */
  42991. // Collect potensial overlapping data labels. Stack labels probably don't need
  42992. // to be considered because they are usually accompanied by data labels that lie
  42993. // inside the columns.
  42994. addEvent(Chart, 'render', function collectAndHide() {
  42995. var labels = [];
  42996. // Consider external label collectors
  42997. (this.labelCollectors || []).forEach(function (collector) {
  42998. labels = labels.concat(collector());
  42999. });
  43000. (this.yAxis || []).forEach(function (yAxis) {
  43001. if (yAxis.stacking &&
  43002. yAxis.options.stackLabels &&
  43003. !yAxis.options.stackLabels.allowOverlap) {
  43004. objectEach(yAxis.stacking.stacks, function (stack) {
  43005. objectEach(stack, function (stackItem) {
  43006. labels.push(stackItem.label);
  43007. });
  43008. });
  43009. }
  43010. });
  43011. (this.series || []).forEach(function (series) {
  43012. var dlOptions = series.options.dataLabels;
  43013. if (series.visible &&
  43014. !(dlOptions.enabled === false && !series._hasPointLabels)) { // #3866
  43015. (series.nodes || series.points).forEach(function (point) {
  43016. if (point.visible) {
  43017. var dataLabels = (isArray(point.dataLabels) ?
  43018. point.dataLabels :
  43019. (point.dataLabel ? [point.dataLabel] : []));
  43020. dataLabels.forEach(function (label) {
  43021. var options = label.options;
  43022. label.labelrank = pick(options.labelrank, point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
  43023. if (!options.allowOverlap) {
  43024. labels.push(label);
  43025. }
  43026. });
  43027. }
  43028. });
  43029. }
  43030. });
  43031. this.hideOverlappingLabels(labels);
  43032. });
  43033. /**
  43034. * Hide overlapping labels. Labels are moved and faded in and out on zoom to
  43035. * provide a smooth visual imression.
  43036. *
  43037. * @private
  43038. * @function Highcharts.Chart#hideOverlappingLabels
  43039. * @param {Array<Highcharts.SVGElement>} labels
  43040. * Rendered data labels
  43041. * @requires modules/overlapping-datalabels
  43042. */
  43043. Chart.prototype.hideOverlappingLabels = function (labels) {
  43044. var chart = this,
  43045. len = labels.length,
  43046. ren = chart.renderer,
  43047. label,
  43048. i,
  43049. j,
  43050. label1,
  43051. label2,
  43052. box1,
  43053. box2,
  43054. isLabelAffected = false,
  43055. isIntersectRect = function (box1,
  43056. box2) {
  43057. return !(box2.x >= box1.x + box1.width ||
  43058. box2.x + box2.width <= box1.x ||
  43059. box2.y >= box1.y + box1.height ||
  43060. box2.y + box2.height <= box1.y);
  43061. },
  43062. // Get the box with its position inside the chart, as opposed to getBBox
  43063. // that only reports the position relative to the parent.
  43064. getAbsoluteBox = function (label) {
  43065. var pos,
  43066. parent,
  43067. bBox,
  43068. // Substract the padding if no background or border (#4333)
  43069. padding = label.box ? 0 : (label.padding || 0),
  43070. lineHeightCorrection = 0,
  43071. xOffset = 0,
  43072. boxWidth,
  43073. alignValue;
  43074. if (label &&
  43075. (!label.alignAttr || label.placed)) {
  43076. pos = label.alignAttr || {
  43077. x: label.attr('x'),
  43078. y: label.attr('y')
  43079. };
  43080. parent = label.parentGroup;
  43081. // Get width and height if pure text nodes (stack labels)
  43082. if (!label.width) {
  43083. bBox = label.getBBox();
  43084. label.width = bBox.width;
  43085. label.height = bBox.height;
  43086. // Labels positions are computed from top left corner, so
  43087. // we need to substract the text height from text nodes too.
  43088. lineHeightCorrection = ren
  43089. .fontMetrics(null, label.element).h;
  43090. }
  43091. boxWidth = label.width - 2 * padding;
  43092. alignValue = {
  43093. left: '0',
  43094. center: '0.5',
  43095. right: '1'
  43096. }[label.alignValue];
  43097. if (alignValue) {
  43098. xOffset = +alignValue * boxWidth;
  43099. }
  43100. else if (isNumber(label.x) && Math.round(label.x) !== label.translateX) {
  43101. xOffset = label.x - label.translateX;
  43102. }
  43103. return {
  43104. x: pos.x + (parent.translateX || 0) + padding -
  43105. (xOffset || 0),
  43106. y: pos.y + (parent.translateY || 0) + padding -
  43107. lineHeightCorrection,
  43108. width: label.width - 2 * padding,
  43109. height: label.height - 2 * padding
  43110. };
  43111. }
  43112. };
  43113. for (i = 0; i < len; i++) {
  43114. label = labels[i];
  43115. if (label) {
  43116. // Mark with initial opacity
  43117. label.oldOpacity = label.opacity;
  43118. label.newOpacity = 1;
  43119. label.absoluteBox = getAbsoluteBox(label);
  43120. }
  43121. }
  43122. // Prevent a situation in a gradually rising slope, that each label will
  43123. // hide the previous one because the previous one always has lower rank.
  43124. labels.sort(function (a, b) {
  43125. return (b.labelrank || 0) - (a.labelrank || 0);
  43126. });
  43127. // Detect overlapping labels
  43128. for (i = 0; i < len; i++) {
  43129. label1 = labels[i];
  43130. box1 = label1 && label1.absoluteBox;
  43131. for (j = i + 1; j < len; ++j) {
  43132. label2 = labels[j];
  43133. box2 = label2 && label2.absoluteBox;
  43134. if (box1 &&
  43135. box2 &&
  43136. label1 !== label2 && // #6465, polar chart with connectEnds
  43137. label1.newOpacity !== 0 &&
  43138. label2.newOpacity !== 0) {
  43139. if (isIntersectRect(box1, box2)) {
  43140. (label1.labelrank < label2.labelrank ? label1 : label2)
  43141. .newOpacity = 0;
  43142. }
  43143. }
  43144. }
  43145. }
  43146. // Hide or show
  43147. labels.forEach(function (label) {
  43148. var complete,
  43149. newOpacity;
  43150. if (label) {
  43151. newOpacity = label.newOpacity;
  43152. if (label.oldOpacity !== newOpacity) {
  43153. // Make sure the label is completely hidden to avoid catching
  43154. // clicks (#4362)
  43155. if (label.alignAttr && label.placed) { // data labels
  43156. label[newOpacity ? 'removeClass' : 'addClass']('highcharts-data-label-hidden');
  43157. complete = function () {
  43158. if (!chart.styledMode) {
  43159. label.css({ pointerEvents: newOpacity ? 'auto' : 'none' });
  43160. }
  43161. label.visibility = newOpacity ? 'inherit' : 'hidden';
  43162. };
  43163. isLabelAffected = true;
  43164. // Animate or set the opacity
  43165. label.alignAttr.opacity = newOpacity;
  43166. label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
  43167. fireEvent(chart, 'afterHideOverlappingLabel');
  43168. }
  43169. else { // other labels, tick labels
  43170. label.attr({
  43171. opacity: newOpacity
  43172. });
  43173. }
  43174. }
  43175. label.isOld = true;
  43176. }
  43177. });
  43178. if (isLabelAffected) {
  43179. fireEvent(chart, 'afterHideAllOverlappingLabels');
  43180. }
  43181. };
  43182. });
  43183. _registerModule(_modules, 'Core/Interaction.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Legend.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (Chart, H, Legend, O, Point, U) {
  43184. /* *
  43185. *
  43186. * (c) 2010-2020 Torstein Honsi
  43187. *
  43188. * License: www.highcharts.com/license
  43189. *
  43190. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43191. *
  43192. * */
  43193. var defaultOptions = O.defaultOptions;
  43194. var addEvent = U.addEvent,
  43195. createElement = U.createElement,
  43196. css = U.css,
  43197. defined = U.defined,
  43198. extend = U.extend,
  43199. fireEvent = U.fireEvent,
  43200. isArray = U.isArray,
  43201. isFunction = U.isFunction,
  43202. isNumber = U.isNumber,
  43203. isObject = U.isObject,
  43204. merge = U.merge,
  43205. objectEach = U.objectEach,
  43206. pick = U.pick;
  43207. /**
  43208. * @interface Highcharts.PointEventsOptionsObject
  43209. */ /**
  43210. * Fires when the point is selected either programmatically or following a click
  43211. * on the point. One parameter, `event`, is passed to the function. Returning
  43212. * `false` cancels the operation.
  43213. * @name Highcharts.PointEventsOptionsObject#select
  43214. * @type {Highcharts.PointSelectCallbackFunction|undefined}
  43215. */ /**
  43216. * Fires when the point is unselected either programmatically or following a
  43217. * click on the point. One parameter, `event`, is passed to the function.
  43218. * Returning `false` cancels the operation.
  43219. * @name Highcharts.PointEventsOptionsObject#unselect
  43220. * @type {Highcharts.PointUnselectCallbackFunction|undefined}
  43221. */
  43222. /**
  43223. * Information about the select/unselect event.
  43224. *
  43225. * @interface Highcharts.PointInteractionEventObject
  43226. * @extends global.Event
  43227. */ /**
  43228. * @name Highcharts.PointInteractionEventObject#accumulate
  43229. * @type {boolean}
  43230. */
  43231. /**
  43232. * Gets fired when the point is selected either programmatically or following a
  43233. * click on the point.
  43234. *
  43235. * @callback Highcharts.PointSelectCallbackFunction
  43236. *
  43237. * @param {Highcharts.Point} this
  43238. * Point where the event occured.
  43239. *
  43240. * @param {Highcharts.PointInteractionEventObject} event
  43241. * Event that occured.
  43242. */
  43243. /**
  43244. * Fires when the point is unselected either programmatically or following a
  43245. * click on the point.
  43246. *
  43247. * @callback Highcharts.PointUnselectCallbackFunction
  43248. *
  43249. * @param {Highcharts.Point} this
  43250. * Point where the event occured.
  43251. *
  43252. * @param {Highcharts.PointInteractionEventObject} event
  43253. * Event that occured.
  43254. */
  43255. var hasTouch = H.hasTouch,
  43256. Series = H.Series,
  43257. seriesTypes = H.seriesTypes,
  43258. svg = H.svg,
  43259. TrackerMixin;
  43260. /* eslint-disable valid-jsdoc */
  43261. /**
  43262. * TrackerMixin for points and graphs.
  43263. *
  43264. * @private
  43265. * @mixin Highcharts.TrackerMixin
  43266. */
  43267. TrackerMixin = H.TrackerMixin = {
  43268. /**
  43269. * Draw the tracker for a point.
  43270. *
  43271. * @private
  43272. * @function Highcharts.TrackerMixin.drawTrackerPoint
  43273. * @param {Highcharts.Series} this
  43274. * @fires Highcharts.Series#event:afterDrawTracker
  43275. */
  43276. drawTrackerPoint: function () {
  43277. var series = this,
  43278. chart = series.chart,
  43279. pointer = chart.pointer,
  43280. onMouseOver = function (e) {
  43281. var point = pointer.getPointFromEvent(e);
  43282. // undefined on graph in scatterchart
  43283. if (typeof point !== 'undefined') {
  43284. pointer.isDirectTouch = true;
  43285. point.onMouseOver(e);
  43286. }
  43287. }, dataLabels;
  43288. // Add reference to the point
  43289. series.points.forEach(function (point) {
  43290. dataLabels = (isArray(point.dataLabels) ?
  43291. point.dataLabels :
  43292. (point.dataLabel ? [point.dataLabel] : []));
  43293. if (point.graphic) {
  43294. point.graphic.element.point = point;
  43295. }
  43296. dataLabels.forEach(function (dataLabel) {
  43297. if (dataLabel.div) {
  43298. dataLabel.div.point = point;
  43299. }
  43300. else {
  43301. dataLabel.element.point = point;
  43302. }
  43303. });
  43304. });
  43305. // Add the event listeners, we need to do this only once
  43306. if (!series._hasTracking) {
  43307. series.trackerGroups.forEach(function (key) {
  43308. if (series[key]) {
  43309. // we don't always have dataLabelsGroup
  43310. series[key]
  43311. .addClass('highcharts-tracker')
  43312. .on('mouseover', onMouseOver)
  43313. .on('mouseout', function (e) {
  43314. pointer.onTrackerMouseOut(e);
  43315. });
  43316. if (hasTouch) {
  43317. series[key].on('touchstart', onMouseOver);
  43318. }
  43319. if (!chart.styledMode && series.options.cursor) {
  43320. series[key]
  43321. .css(css)
  43322. .css({ cursor: series.options.cursor });
  43323. }
  43324. }
  43325. });
  43326. series._hasTracking = true;
  43327. }
  43328. fireEvent(this, 'afterDrawTracker');
  43329. },
  43330. /**
  43331. * Draw the tracker object that sits above all data labels and markers to
  43332. * track mouse events on the graph or points. For the line type charts
  43333. * the tracker uses the same graphPath, but with a greater stroke width
  43334. * for better control.
  43335. *
  43336. * @private
  43337. * @function Highcharts.TrackerMixin.drawTrackerGraph
  43338. * @param {Highcharts.Series} this
  43339. * @fires Highcharts.Series#event:afterDrawTracker
  43340. */
  43341. drawTrackerGraph: function () {
  43342. var series = this,
  43343. options = series.options,
  43344. trackByArea = options.trackByArea,
  43345. trackerPath = [].concat(trackByArea ?
  43346. series.areaPath :
  43347. series.graphPath),
  43348. // trackerPathLength = trackerPath.length,
  43349. chart = series.chart,
  43350. pointer = chart.pointer,
  43351. renderer = chart.renderer,
  43352. snap = chart.options.tooltip.snap,
  43353. tracker = series.tracker,
  43354. i,
  43355. onMouseOver = function (e) {
  43356. if (chart.hoverSeries !== series) {
  43357. series.onMouseOver();
  43358. }
  43359. },
  43360. /*
  43361. * Empirical lowest possible opacities for TRACKER_FILL for an
  43362. * element to stay invisible but clickable
  43363. * IE6: 0.002
  43364. * IE7: 0.002
  43365. * IE8: 0.002
  43366. * IE9: 0.00000000001 (unlimited)
  43367. * IE10: 0.0001 (exporting only)
  43368. * FF: 0.00000000001 (unlimited)
  43369. * Chrome: 0.000001
  43370. * Safari: 0.000001
  43371. * Opera: 0.00000000001 (unlimited)
  43372. */
  43373. TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
  43374. // Draw the tracker
  43375. if (tracker) {
  43376. tracker.attr({ d: trackerPath });
  43377. }
  43378. else if (series.graph) { // create
  43379. series.tracker = renderer.path(trackerPath)
  43380. .attr({
  43381. visibility: series.visible ? 'visible' : 'hidden',
  43382. zIndex: 2
  43383. })
  43384. .addClass(trackByArea ?
  43385. 'highcharts-tracker-area' :
  43386. 'highcharts-tracker-line')
  43387. .add(series.group);
  43388. if (!chart.styledMode) {
  43389. series.tracker.attr({
  43390. 'stroke-linecap': 'round',
  43391. 'stroke-linejoin': 'round',
  43392. stroke: TRACKER_FILL,
  43393. fill: trackByArea ? TRACKER_FILL : 'none',
  43394. 'stroke-width': series.graph.strokeWidth() +
  43395. (trackByArea ? 0 : 2 * snap)
  43396. });
  43397. }
  43398. // The tracker is added to the series group, which is clipped, but
  43399. // is covered by the marker group. So the marker group also needs to
  43400. // capture events.
  43401. [series.tracker, series.markerGroup].forEach(function (tracker) {
  43402. tracker.addClass('highcharts-tracker')
  43403. .on('mouseover', onMouseOver)
  43404. .on('mouseout', function (e) {
  43405. pointer.onTrackerMouseOut(e);
  43406. });
  43407. if (options.cursor && !chart.styledMode) {
  43408. tracker.css({ cursor: options.cursor });
  43409. }
  43410. if (hasTouch) {
  43411. tracker.on('touchstart', onMouseOver);
  43412. }
  43413. });
  43414. }
  43415. fireEvent(this, 'afterDrawTracker');
  43416. }
  43417. };
  43418. /* End TrackerMixin */
  43419. // Add tracking event listener to the series group, so the point graphics
  43420. // themselves act as trackers
  43421. if (seriesTypes.column) {
  43422. /**
  43423. * @private
  43424. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.column#drawTracker
  43425. */
  43426. seriesTypes.column.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  43427. }
  43428. if (seriesTypes.pie) {
  43429. /**
  43430. * @private
  43431. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.pie#drawTracker
  43432. */
  43433. seriesTypes.pie.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  43434. }
  43435. if (seriesTypes.scatter) {
  43436. /**
  43437. * @private
  43438. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.scatter#drawTracker
  43439. */
  43440. seriesTypes.scatter.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  43441. }
  43442. // Extend Legend for item events.
  43443. extend(Legend.prototype, {
  43444. /**
  43445. * @private
  43446. * @function Highcharts.Legend#setItemEvents
  43447. * @param {Highcharts.BubbleLegend|Point|Highcharts.Series} item
  43448. * @param {Highcharts.SVGElement} legendItem
  43449. * @param {boolean} [useHTML=false]
  43450. * @fires Highcharts.Point#event:legendItemClick
  43451. * @fires Highcharts.Series#event:legendItemClick
  43452. */
  43453. setItemEvents: function (item, legendItem, useHTML) {
  43454. var legend = this,
  43455. boxWrapper = legend.chart.renderer.boxWrapper,
  43456. isPoint = item instanceof Point,
  43457. activeClass = 'highcharts-legend-' +
  43458. (isPoint ? 'point' : 'series') + '-active',
  43459. styledMode = legend.chart.styledMode,
  43460. // When `useHTML`, the symbol is rendered in other group, so
  43461. // we need to apply events listeners to both places
  43462. legendItems = useHTML ?
  43463. [legendItem,
  43464. item.legendSymbol] :
  43465. [item.legendGroup];
  43466. // Set the events on the item group, or in case of useHTML, the item
  43467. // itself (#1249)
  43468. legendItems.forEach(function (element) {
  43469. if (element) {
  43470. element
  43471. .on('mouseover', function () {
  43472. if (item.visible) {
  43473. legend.allItems.forEach(function (inactiveItem) {
  43474. if (item !== inactiveItem) {
  43475. inactiveItem.setState('inactive', !isPoint);
  43476. }
  43477. });
  43478. }
  43479. item.setState('hover');
  43480. // A CSS class to dim or hide other than the hovered
  43481. // series.
  43482. // Works only if hovered series is visible (#10071).
  43483. if (item.visible) {
  43484. boxWrapper.addClass(activeClass);
  43485. }
  43486. if (!styledMode) {
  43487. legendItem.css(legend.options.itemHoverStyle);
  43488. }
  43489. })
  43490. .on('mouseout', function () {
  43491. if (!legend.chart.styledMode) {
  43492. legendItem.css(merge(item.visible ?
  43493. legend.itemStyle :
  43494. legend.itemHiddenStyle));
  43495. }
  43496. legend.allItems.forEach(function (inactiveItem) {
  43497. if (item !== inactiveItem) {
  43498. inactiveItem.setState('', !isPoint);
  43499. }
  43500. });
  43501. // A CSS class to dim or hide other than the hovered
  43502. // series.
  43503. boxWrapper.removeClass(activeClass);
  43504. item.setState();
  43505. })
  43506. .on('click', function (event) {
  43507. var strLegendItemClick = 'legendItemClick',
  43508. fnLegendItemClick = function () {
  43509. if (item.setVisible) {
  43510. item.setVisible();
  43511. }
  43512. // Reset inactive state
  43513. legend.allItems.forEach(function (inactiveItem) {
  43514. if (item !== inactiveItem) {
  43515. inactiveItem.setState(item.visible ? 'inactive' : '', !isPoint);
  43516. }
  43517. });
  43518. };
  43519. // A CSS class to dim or hide other than the hovered
  43520. // series. Event handling in iOS causes the activeClass
  43521. // to be added prior to click in some cases (#7418).
  43522. boxWrapper.removeClass(activeClass);
  43523. // Pass over the click/touch event. #4.
  43524. event = {
  43525. browserEvent: event
  43526. };
  43527. // click the name or symbol
  43528. if (item.firePointEvent) { // point
  43529. item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
  43530. }
  43531. else {
  43532. fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
  43533. }
  43534. });
  43535. }
  43536. });
  43537. },
  43538. /**
  43539. * @private
  43540. * @function Highcharts.Legend#createCheckboxForItem
  43541. * @param {Highcharts.BubbleLegend|Point|Highcharts.Series} item
  43542. * @fires Highcharts.Series#event:checkboxClick
  43543. */
  43544. createCheckboxForItem: function (item) {
  43545. var legend = this;
  43546. item.checkbox = createElement('input', {
  43547. type: 'checkbox',
  43548. className: 'highcharts-legend-checkbox',
  43549. checked: item.selected,
  43550. defaultChecked: item.selected // required by IE7
  43551. }, legend.options.itemCheckboxStyle, legend.chart.container);
  43552. addEvent(item.checkbox, 'click', function (event) {
  43553. var target = event.target;
  43554. fireEvent(item.series || item, 'checkboxClick', {
  43555. checked: target.checked,
  43556. item: item
  43557. }, function () {
  43558. item.select();
  43559. });
  43560. });
  43561. }
  43562. });
  43563. // Extend the Chart object with interaction
  43564. extend(Chart.prototype, /** @lends Chart.prototype */ {
  43565. /**
  43566. * Display the zoom button, so users can reset zoom to the default view
  43567. * settings.
  43568. *
  43569. * @function Highcharts.Chart#showResetZoom
  43570. *
  43571. * @fires Highcharts.Chart#event:afterShowResetZoom
  43572. * @fires Highcharts.Chart#event:beforeShowResetZoom
  43573. */
  43574. showResetZoom: function () {
  43575. var chart = this,
  43576. lang = defaultOptions.lang,
  43577. btnOptions = chart.options.chart.resetZoomButton,
  43578. theme = btnOptions.theme,
  43579. states = theme.states,
  43580. alignTo = (btnOptions.relativeTo === 'chart' ||
  43581. btnOptions.relativeTo === 'spaceBox' ?
  43582. null :
  43583. 'plotBox');
  43584. /**
  43585. * @private
  43586. */
  43587. function zoomOut() {
  43588. chart.zoomOut();
  43589. }
  43590. fireEvent(this, 'beforeShowResetZoom', null, function () {
  43591. chart.resetZoomButton = chart.renderer
  43592. .button(lang.resetZoom, null, null, zoomOut, theme, states && states.hover)
  43593. .attr({
  43594. align: btnOptions.position.align,
  43595. title: lang.resetZoomTitle
  43596. })
  43597. .addClass('highcharts-reset-zoom')
  43598. .add()
  43599. .align(btnOptions.position, false, alignTo);
  43600. });
  43601. fireEvent(this, 'afterShowResetZoom');
  43602. },
  43603. /**
  43604. * Zoom the chart out after a user has zoomed in. See also
  43605. * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
  43606. *
  43607. * @function Highcharts.Chart#zoomOut
  43608. *
  43609. * @fires Highcharts.Chart#event:selection
  43610. */
  43611. zoomOut: function () {
  43612. fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
  43613. },
  43614. /**
  43615. * Zoom into a given portion of the chart given by axis coordinates.
  43616. *
  43617. * @private
  43618. * @function Highcharts.Chart#zoom
  43619. * @param {Highcharts.SelectEventObject} event
  43620. */
  43621. zoom: function (event) {
  43622. var chart = this,
  43623. hasZoomed,
  43624. pointer = chart.pointer,
  43625. displayButton = false,
  43626. mouseDownPos = chart.inverted ? pointer.mouseDownX : pointer.mouseDownY,
  43627. resetZoomButton;
  43628. // If zoom is called with no arguments, reset the axes
  43629. if (!event || event.resetSelection) {
  43630. chart.axes.forEach(function (axis) {
  43631. hasZoomed = axis.zoom();
  43632. });
  43633. pointer.initiated = false; // #6804
  43634. }
  43635. else { // else, zoom in on all axes
  43636. event.xAxis.concat(event.yAxis).forEach(function (axisData) {
  43637. var axis = axisData.axis,
  43638. axisStartPos = chart.inverted ? axis.left : axis.top,
  43639. axisEndPos = chart.inverted ?
  43640. axisStartPos + axis.width : axisStartPos + axis.height,
  43641. isXAxis = axis.isXAxis,
  43642. isWithinPane = false;
  43643. // Check if zoomed area is within the pane (#1289).
  43644. // In case of multiple panes only one pane should be zoomed.
  43645. if ((!isXAxis &&
  43646. mouseDownPos >= axisStartPos &&
  43647. mouseDownPos <= axisEndPos) ||
  43648. isXAxis ||
  43649. !defined(mouseDownPos)) {
  43650. isWithinPane = true;
  43651. }
  43652. // don't zoom more than minRange
  43653. if (pointer[isXAxis ? 'zoomX' : 'zoomY'] && isWithinPane) {
  43654. hasZoomed = axis.zoom(axisData.min, axisData.max);
  43655. if (axis.displayBtn) {
  43656. displayButton = true;
  43657. }
  43658. }
  43659. });
  43660. }
  43661. // Show or hide the Reset zoom button
  43662. resetZoomButton = chart.resetZoomButton;
  43663. if (displayButton && !resetZoomButton) {
  43664. chart.showResetZoom();
  43665. }
  43666. else if (!displayButton && isObject(resetZoomButton)) {
  43667. chart.resetZoomButton = resetZoomButton.destroy();
  43668. }
  43669. // Redraw
  43670. if (hasZoomed) {
  43671. chart.redraw(pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100));
  43672. }
  43673. },
  43674. /**
  43675. * Pan the chart by dragging the mouse across the pane. This function is
  43676. * called on mouse move, and the distance to pan is computed from chartX
  43677. * compared to the first chartX position in the dragging operation.
  43678. *
  43679. * @private
  43680. * @function Highcharts.Chart#pan
  43681. * @param {Highcharts.PointerEventObject} e
  43682. * @param {string} panning
  43683. */
  43684. pan: function (e, panning) {
  43685. var chart = this,
  43686. hoverPoints = chart.hoverPoints,
  43687. panningOptions,
  43688. chartOptions = chart.options.chart,
  43689. hasMapNavigation = chart.options.mapNavigation &&
  43690. chart.options.mapNavigation.enabled,
  43691. doRedraw,
  43692. type;
  43693. if (typeof panning === 'object') {
  43694. panningOptions = panning;
  43695. }
  43696. else {
  43697. panningOptions = {
  43698. enabled: panning,
  43699. type: 'x'
  43700. };
  43701. }
  43702. if (chartOptions && chartOptions.panning) {
  43703. chartOptions.panning = panningOptions;
  43704. }
  43705. type = panningOptions.type;
  43706. fireEvent(this, 'pan', { originalEvent: e }, function () {
  43707. // remove active points for shared tooltip
  43708. if (hoverPoints) {
  43709. hoverPoints.forEach(function (point) {
  43710. point.setState();
  43711. });
  43712. }
  43713. // panning axis mapping
  43714. var xy = [1]; // x
  43715. if (type === 'xy') {
  43716. xy = [1, 0];
  43717. }
  43718. else if (type === 'y') {
  43719. xy = [0];
  43720. }
  43721. xy.forEach(function (isX) {
  43722. var axis = chart[isX ? 'xAxis' : 'yAxis'][0], horiz = axis.horiz, mousePos = e[horiz ? 'chartX' : 'chartY'], mouseDown = horiz ? 'mouseDownX' : 'mouseDownY', startPos = chart[mouseDown], halfPointRange = (axis.pointRange || 0) / 2, pointRangeDirection = (axis.reversed && !chart.inverted) ||
  43723. (!axis.reversed && chart.inverted) ?
  43724. -1 :
  43725. 1, extremes = axis.getExtremes(), panMin = axis.toValue(startPos - mousePos, true) +
  43726. halfPointRange * pointRangeDirection, panMax = axis.toValue(startPos + axis.len - mousePos, true) -
  43727. halfPointRange * pointRangeDirection, flipped = panMax < panMin, newMin = flipped ? panMax : panMin, newMax = flipped ? panMin : panMax, hasVerticalPanning = axis.hasVerticalPanning(), paddedMin, paddedMax, spill, panningState = axis.panningState;
  43728. // General calculations of panning state.
  43729. // This is related to using vertical panning. (#11315).
  43730. axis.series.forEach(function (series) {
  43731. if (hasVerticalPanning &&
  43732. !isX && (!panningState || panningState.isDirty)) {
  43733. var processedData = series.getProcessedData(true),
  43734. dataExtremes = series.getExtremes(processedData.yData,
  43735. true);
  43736. if (!panningState) {
  43737. panningState = {
  43738. startMin: Number.MAX_VALUE,
  43739. startMax: -Number.MAX_VALUE
  43740. };
  43741. }
  43742. if (isNumber(dataExtremes.dataMin) &&
  43743. isNumber(dataExtremes.dataMax)) {
  43744. panningState.startMin = Math.min(dataExtremes.dataMin, panningState.startMin);
  43745. panningState.startMax = Math.max(dataExtremes.dataMax, panningState.startMax);
  43746. }
  43747. }
  43748. });
  43749. paddedMin = Math.min(H.pick(panningState === null || panningState === void 0 ? void 0 : panningState.startMin, extremes.dataMin), halfPointRange ?
  43750. extremes.min :
  43751. axis.toValue(axis.toPixels(extremes.min) -
  43752. axis.minPixelPadding));
  43753. paddedMax = Math.max(H.pick(panningState === null || panningState === void 0 ? void 0 : panningState.startMax, extremes.dataMax), halfPointRange ?
  43754. extremes.max :
  43755. axis.toValue(axis.toPixels(extremes.max) +
  43756. axis.minPixelPadding));
  43757. axis.panningState = panningState;
  43758. // It is not necessary to calculate extremes on ordinal axis,
  43759. // because they are already calculated, so we don't want to
  43760. // override them.
  43761. if (!axis.isOrdinal) {
  43762. // If the new range spills over, either to the min or max,
  43763. // adjust the new range.
  43764. spill = paddedMin - newMin;
  43765. if (spill > 0) {
  43766. newMax += spill;
  43767. newMin = paddedMin;
  43768. }
  43769. spill = newMax - paddedMax;
  43770. if (spill > 0) {
  43771. newMax = paddedMax;
  43772. newMin -= spill;
  43773. }
  43774. // Set new extremes if they are actually new
  43775. if (axis.series.length &&
  43776. newMin !== extremes.min &&
  43777. newMax !== extremes.max &&
  43778. newMin >= paddedMin &&
  43779. newMax <= paddedMax) {
  43780. axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' });
  43781. if (!chart.resetZoomButton &&
  43782. !hasMapNavigation &&
  43783. // Show reset zoom button only when both newMin and
  43784. // newMax values are between padded axis range.
  43785. newMin !== paddedMin &&
  43786. newMax !== paddedMax &&
  43787. type.match('y')) {
  43788. chart.showResetZoom();
  43789. axis.displayBtn = false;
  43790. }
  43791. doRedraw = true;
  43792. }
  43793. // set new reference for next run:
  43794. chart[mouseDown] = mousePos;
  43795. }
  43796. });
  43797. if (doRedraw) {
  43798. chart.redraw(false);
  43799. }
  43800. css(chart.container, { cursor: 'move' });
  43801. });
  43802. }
  43803. });
  43804. // Extend the Point object with interaction
  43805. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  43806. /**
  43807. * Toggle the selection status of a point.
  43808. *
  43809. * @see Highcharts.Chart#getSelectedPoints
  43810. *
  43811. * @sample highcharts/members/point-select/
  43812. * Select a point from a button
  43813. * @sample highcharts/chart/events-selection-points/
  43814. * Select a range of points through a drag selection
  43815. * @sample maps/series/data-id/
  43816. * Select a point in Highmaps
  43817. *
  43818. * @function Highcharts.Point#select
  43819. *
  43820. * @param {boolean} [selected]
  43821. * When `true`, the point is selected. When `false`, the point is
  43822. * unselected. When `null` or `undefined`, the selection state is toggled.
  43823. *
  43824. * @param {boolean} [accumulate=false]
  43825. * When `true`, the selection is added to other selected points.
  43826. * When `false`, other selected points are deselected. Internally in
  43827. * Highcharts, when
  43828. * [allowPointSelect](https://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
  43829. * is `true`, selected points are accumulated on Control, Shift or Cmd
  43830. * clicking the point.
  43831. *
  43832. * @fires Highcharts.Point#event:select
  43833. * @fires Highcharts.Point#event:unselect
  43834. */
  43835. select: function (selected, accumulate) {
  43836. var point = this,
  43837. series = point.series,
  43838. chart = series.chart;
  43839. selected = pick(selected, !point.selected);
  43840. this.selectedStaging = selected;
  43841. // fire the event with the default handler
  43842. point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
  43843. /**
  43844. * Whether the point is selected or not.
  43845. *
  43846. * @see Point#select
  43847. * @see Chart#getSelectedPoints
  43848. *
  43849. * @name Highcharts.Point#selected
  43850. * @type {boolean}
  43851. */
  43852. point.selected = point.options.selected = selected;
  43853. series.options.data[series.data.indexOf(point)] =
  43854. point.options;
  43855. point.setState(selected && 'select');
  43856. // unselect all other points unless Ctrl or Cmd + click
  43857. if (!accumulate) {
  43858. chart.getSelectedPoints().forEach(function (loopPoint) {
  43859. var loopSeries = loopPoint.series;
  43860. if (loopPoint.selected && loopPoint !== point) {
  43861. loopPoint.selected = loopPoint.options.selected =
  43862. false;
  43863. loopSeries.options.data[loopSeries.data.indexOf(loopPoint)] = loopPoint.options;
  43864. // Programatically selecting a point should restore
  43865. // normal state, but when click happened on other
  43866. // point, set inactive state to match other points
  43867. loopPoint.setState(chart.hoverPoints &&
  43868. loopSeries.options.inactiveOtherPoints ?
  43869. 'inactive' : '');
  43870. loopPoint.firePointEvent('unselect');
  43871. }
  43872. });
  43873. }
  43874. });
  43875. delete this.selectedStaging;
  43876. },
  43877. /**
  43878. * Runs on mouse over the point. Called internally from mouse and touch
  43879. * events.
  43880. *
  43881. * @function Highcharts.Point#onMouseOver
  43882. *
  43883. * @param {Highcharts.PointerEventObject} [e]
  43884. * The event arguments.
  43885. */
  43886. onMouseOver: function (e) {
  43887. var point = this,
  43888. series = point.series,
  43889. chart = series.chart,
  43890. pointer = chart.pointer;
  43891. e = e ?
  43892. pointer.normalize(e) :
  43893. // In cases where onMouseOver is called directly without an event
  43894. pointer.getChartCoordinatesFromPoint(point, chart.inverted);
  43895. pointer.runPointActions(e, point);
  43896. },
  43897. /**
  43898. * Runs on mouse out from the point. Called internally from mouse and touch
  43899. * events.
  43900. *
  43901. * @function Highcharts.Point#onMouseOut
  43902. * @fires Highcharts.Point#event:mouseOut
  43903. */
  43904. onMouseOut: function () {
  43905. var point = this,
  43906. chart = point.series.chart;
  43907. point.firePointEvent('mouseOut');
  43908. if (!point.series.options.inactiveOtherPoints) {
  43909. (chart.hoverPoints || []).forEach(function (p) {
  43910. p.setState();
  43911. });
  43912. }
  43913. chart.hoverPoints = chart.hoverPoint = null;
  43914. },
  43915. /**
  43916. * Import events from the series' and point's options. Only do it on
  43917. * demand, to save processing time on hovering.
  43918. *
  43919. * @private
  43920. * @function Highcharts.Point#importEvents
  43921. */
  43922. importEvents: function () {
  43923. if (!this.hasImportedEvents) {
  43924. var point = this,
  43925. options = merge(point.series.options.point,
  43926. point.options),
  43927. events = options.events;
  43928. point.events = events;
  43929. objectEach(events, function (event, eventType) {
  43930. if (isFunction(event)) {
  43931. addEvent(point, eventType, event);
  43932. }
  43933. });
  43934. this.hasImportedEvents = true;
  43935. }
  43936. },
  43937. /**
  43938. * Set the point's state.
  43939. *
  43940. * @function Highcharts.Point#setState
  43941. *
  43942. * @param {Highcharts.PointStateValue|""} [state]
  43943. * The new state, can be one of `'hover'`, `'select'`, `'inactive'`,
  43944. * or `''` (an empty string), `'normal'` or `undefined` to set to
  43945. * normal state.
  43946. * @param {boolean} [move]
  43947. * State for animation.
  43948. *
  43949. * @fires Highcharts.Point#event:afterSetState
  43950. */
  43951. setState: function (state, move) {
  43952. var point = this,
  43953. series = point.series,
  43954. previousState = point.state,
  43955. stateOptions = (series.options.states[state || 'normal'] ||
  43956. {}),
  43957. markerOptions = (defaultOptions.plotOptions[series.type].marker &&
  43958. series.options.marker),
  43959. normalDisabled = (markerOptions && markerOptions.enabled === false),
  43960. markerStateOptions = ((markerOptions &&
  43961. markerOptions.states &&
  43962. markerOptions.states[state || 'normal']) || {}),
  43963. stateDisabled = markerStateOptions.enabled === false,
  43964. stateMarkerGraphic = series.stateMarkerGraphic,
  43965. pointMarker = point.marker || {},
  43966. chart = series.chart,
  43967. halo = series.halo,
  43968. haloOptions,
  43969. markerAttribs,
  43970. pointAttribs,
  43971. pointAttribsAnimation,
  43972. hasMarkers = (markerOptions && series.markerAttribs),
  43973. newSymbol;
  43974. state = state || ''; // empty string
  43975. if (
  43976. // already has this state
  43977. (state === point.state && !move) ||
  43978. // selected points don't respond to hover
  43979. (point.selected && state !== 'select') ||
  43980. // series' state options is disabled
  43981. (stateOptions.enabled === false) ||
  43982. // general point marker's state options is disabled
  43983. (state && (stateDisabled ||
  43984. (normalDisabled &&
  43985. markerStateOptions.enabled === false))) ||
  43986. // individual point marker's state options is disabled
  43987. (state &&
  43988. pointMarker.states &&
  43989. pointMarker.states[state] &&
  43990. pointMarker.states[state].enabled === false) // #1610
  43991. ) {
  43992. return;
  43993. }
  43994. point.state = state;
  43995. if (hasMarkers) {
  43996. markerAttribs = series.markerAttribs(point, state);
  43997. }
  43998. // Apply hover styles to the existing point
  43999. if (point.graphic) {
  44000. if (previousState) {
  44001. point.graphic.removeClass('highcharts-point-' + previousState);
  44002. }
  44003. if (state) {
  44004. point.graphic.addClass('highcharts-point-' + state);
  44005. }
  44006. if (!chart.styledMode) {
  44007. pointAttribs = series.pointAttribs(point, state);
  44008. pointAttribsAnimation = pick(chart.options.chart.animation, stateOptions.animation);
  44009. // Some inactive points (e.g. slices in pie) should apply
  44010. // oppacity also for it's labels
  44011. if (series.options.inactiveOtherPoints && pointAttribs.opacity) {
  44012. (point.dataLabels || []).forEach(function (label) {
  44013. if (label) {
  44014. label.animate({
  44015. opacity: pointAttribs.opacity
  44016. }, pointAttribsAnimation);
  44017. }
  44018. });
  44019. if (point.connector) {
  44020. point.connector.animate({
  44021. opacity: pointAttribs.opacity
  44022. }, pointAttribsAnimation);
  44023. }
  44024. }
  44025. point.graphic.animate(pointAttribs, pointAttribsAnimation);
  44026. }
  44027. if (markerAttribs) {
  44028. point.graphic.animate(markerAttribs, pick(
  44029. // Turn off globally:
  44030. chart.options.chart.animation, markerStateOptions.animation, markerOptions.animation));
  44031. }
  44032. // Zooming in from a range with no markers to a range with markers
  44033. if (stateMarkerGraphic) {
  44034. stateMarkerGraphic.hide();
  44035. }
  44036. }
  44037. else {
  44038. // if a graphic is not applied to each point in the normal state,
  44039. // create a shared graphic for the hover state
  44040. if (state && markerStateOptions) {
  44041. newSymbol = pointMarker.symbol || series.symbol;
  44042. // If the point has another symbol than the previous one, throw
  44043. // away the state marker graphic and force a new one (#1459)
  44044. if (stateMarkerGraphic &&
  44045. stateMarkerGraphic.currentSymbol !== newSymbol) {
  44046. stateMarkerGraphic = stateMarkerGraphic.destroy();
  44047. }
  44048. // Add a new state marker graphic
  44049. if (markerAttribs) {
  44050. if (!stateMarkerGraphic) {
  44051. if (newSymbol) {
  44052. series.stateMarkerGraphic = stateMarkerGraphic =
  44053. chart.renderer
  44054. .symbol(newSymbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height)
  44055. .add(series.markerGroup);
  44056. stateMarkerGraphic.currentSymbol = newSymbol;
  44057. }
  44058. // Move the existing graphic
  44059. }
  44060. else {
  44061. stateMarkerGraphic[move ? 'animate' : 'attr']({
  44062. x: markerAttribs.x,
  44063. y: markerAttribs.y
  44064. });
  44065. }
  44066. }
  44067. if (!chart.styledMode && stateMarkerGraphic) {
  44068. stateMarkerGraphic.attr(series.pointAttribs(point, state));
  44069. }
  44070. }
  44071. if (stateMarkerGraphic) {
  44072. stateMarkerGraphic[state && point.isInside ? 'show' : 'hide'](); // #2450
  44073. stateMarkerGraphic.element.point = point; // #4310
  44074. }
  44075. }
  44076. // Show me your halo
  44077. haloOptions = stateOptions.halo;
  44078. var markerGraphic = (point.graphic || stateMarkerGraphic);
  44079. var markerVisibility = (markerGraphic && markerGraphic.visibility || 'inherit');
  44080. if (haloOptions &&
  44081. haloOptions.size &&
  44082. markerGraphic &&
  44083. markerVisibility !== 'hidden' &&
  44084. !point.isCluster) {
  44085. if (!halo) {
  44086. series.halo = halo = chart.renderer.path()
  44087. // #5818, #5903, #6705
  44088. .add(markerGraphic.parentGroup);
  44089. }
  44090. halo.show()[move ? 'animate' : 'attr']({
  44091. d: point.haloPath(haloOptions.size)
  44092. });
  44093. halo.attr({
  44094. 'class': 'highcharts-halo highcharts-color-' +
  44095. pick(point.colorIndex, series.colorIndex) +
  44096. (point.className ? ' ' + point.className : ''),
  44097. 'visibility': markerVisibility,
  44098. 'zIndex': -1 // #4929, #8276
  44099. });
  44100. halo.point = point; // #6055
  44101. if (!chart.styledMode) {
  44102. halo.attr(extend({
  44103. 'fill': point.color || series.color,
  44104. 'fill-opacity': haloOptions.opacity
  44105. }, haloOptions.attributes));
  44106. }
  44107. }
  44108. else if (halo && halo.point && halo.point.haloPath) {
  44109. // Animate back to 0 on the current halo point (#6055)
  44110. halo.animate({ d: halo.point.haloPath(0) }, null,
  44111. // Hide after unhovering. The `complete` callback runs in the
  44112. // halo's context (#7681).
  44113. halo.hide);
  44114. }
  44115. fireEvent(point, 'afterSetState');
  44116. },
  44117. /**
  44118. * Get the path definition for the halo, which is usually a shadow-like
  44119. * circle around the currently hovered point.
  44120. *
  44121. * @function Highcharts.Point#haloPath
  44122. *
  44123. * @param {number} size
  44124. * The radius of the circular halo.
  44125. *
  44126. * @return {Highcharts.SVGPathArray}
  44127. * The path definition.
  44128. */
  44129. haloPath: function (size) {
  44130. var series = this.series,
  44131. chart = series.chart;
  44132. return chart.renderer.symbols.circle(Math.floor(this.plotX) - size, this.plotY - size, size * 2, size * 2);
  44133. }
  44134. });
  44135. // Extend the Series object with interaction
  44136. extend(Series.prototype, /** @lends Highcharts.Series.prototype */ {
  44137. /**
  44138. * Runs on mouse over the series graphical items.
  44139. *
  44140. * @function Highcharts.Series#onMouseOver
  44141. * @fires Highcharts.Series#event:mouseOver
  44142. */
  44143. onMouseOver: function () {
  44144. var series = this,
  44145. chart = series.chart,
  44146. hoverSeries = chart.hoverSeries,
  44147. pointer = chart.pointer;
  44148. pointer.setHoverChartIndex();
  44149. // set normal state to previous series
  44150. if (hoverSeries && hoverSeries !== series) {
  44151. hoverSeries.onMouseOut();
  44152. }
  44153. // trigger the event, but to save processing time,
  44154. // only if defined
  44155. if (series.options.events.mouseOver) {
  44156. fireEvent(series, 'mouseOver');
  44157. }
  44158. // hover this
  44159. series.setState('hover');
  44160. /**
  44161. * Contains the original hovered series.
  44162. *
  44163. * @name Highcharts.Chart#hoverSeries
  44164. * @type {Highcharts.Series|null}
  44165. */
  44166. chart.hoverSeries = series;
  44167. },
  44168. /**
  44169. * Runs on mouse out of the series graphical items.
  44170. *
  44171. * @function Highcharts.Series#onMouseOut
  44172. *
  44173. * @fires Highcharts.Series#event:mouseOut
  44174. */
  44175. onMouseOut: function () {
  44176. // trigger the event only if listeners exist
  44177. var series = this,
  44178. options = series.options,
  44179. chart = series.chart,
  44180. tooltip = chart.tooltip,
  44181. hoverPoint = chart.hoverPoint;
  44182. // #182, set to null before the mouseOut event fires
  44183. chart.hoverSeries = null;
  44184. // trigger mouse out on the point, which must be in this series
  44185. if (hoverPoint) {
  44186. hoverPoint.onMouseOut();
  44187. }
  44188. // fire the mouse out event
  44189. if (series && options.events.mouseOut) {
  44190. fireEvent(series, 'mouseOut');
  44191. }
  44192. // hide the tooltip
  44193. if (tooltip &&
  44194. !series.stickyTracking &&
  44195. (!tooltip.shared || series.noSharedTooltip)) {
  44196. tooltip.hide();
  44197. }
  44198. // Reset all inactive states
  44199. chart.series.forEach(function (s) {
  44200. s.setState('', true);
  44201. });
  44202. },
  44203. /**
  44204. * Set the state of the series. Called internally on mouse interaction
  44205. * operations, but it can also be called directly to visually
  44206. * highlight a series.
  44207. *
  44208. * @function Highcharts.Series#setState
  44209. *
  44210. * @param {Highcharts.SeriesStateValue|""} [state]
  44211. * The new state, can be either `'hover'`, `'inactive'`, `'select'`,
  44212. * or `''` (an empty string), `'normal'` or `undefined` to set to
  44213. * normal state.
  44214. * @param {boolean} [inherit]
  44215. * Determines if state should be inherited by points too.
  44216. */
  44217. setState: function (state, inherit) {
  44218. var series = this,
  44219. options = series.options,
  44220. graph = series.graph,
  44221. inactiveOtherPoints = options.inactiveOtherPoints,
  44222. stateOptions = options.states,
  44223. lineWidth = options.lineWidth,
  44224. opacity = options.opacity,
  44225. // By default a quick animation to hover/inactive,
  44226. // slower to un-hover
  44227. stateAnimation = pick((stateOptions[state || 'normal'] &&
  44228. stateOptions[state || 'normal'].animation),
  44229. series.chart.options.chart.animation),
  44230. attribs,
  44231. i = 0;
  44232. state = state || '';
  44233. if (series.state !== state) {
  44234. // Toggle class names
  44235. [
  44236. series.group,
  44237. series.markerGroup,
  44238. series.dataLabelsGroup
  44239. ].forEach(function (group) {
  44240. if (group) {
  44241. // Old state
  44242. if (series.state) {
  44243. group.removeClass('highcharts-series-' + series.state);
  44244. }
  44245. // New state
  44246. if (state) {
  44247. group.addClass('highcharts-series-' + state);
  44248. }
  44249. }
  44250. });
  44251. series.state = state;
  44252. if (!series.chart.styledMode) {
  44253. if (stateOptions[state] &&
  44254. stateOptions[state].enabled === false) {
  44255. return;
  44256. }
  44257. if (state) {
  44258. lineWidth = (stateOptions[state].lineWidth ||
  44259. lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
  44260. opacity = pick(stateOptions[state].opacity, opacity);
  44261. }
  44262. if (graph && !graph.dashstyle) {
  44263. attribs = {
  44264. 'stroke-width': lineWidth
  44265. };
  44266. // Animate the graph stroke-width.
  44267. graph.animate(attribs, stateAnimation);
  44268. while (series['zone-graph-' + i]) {
  44269. series['zone-graph-' + i].attr(attribs);
  44270. i = i + 1;
  44271. }
  44272. }
  44273. // For some types (pie, networkgraph, sankey) opacity is
  44274. // resolved on a point level
  44275. if (!inactiveOtherPoints) {
  44276. [
  44277. series.group,
  44278. series.markerGroup,
  44279. series.dataLabelsGroup,
  44280. series.labelBySeries
  44281. ].forEach(function (group) {
  44282. if (group) {
  44283. group.animate({
  44284. opacity: opacity
  44285. }, stateAnimation);
  44286. }
  44287. });
  44288. }
  44289. }
  44290. }
  44291. // Don't loop over points on a series that doesn't apply inactive state
  44292. // to siblings markers (e.g. line, column)
  44293. if (inherit && inactiveOtherPoints && series.points) {
  44294. series.setAllPointsToState(state);
  44295. }
  44296. },
  44297. /**
  44298. * Set the state for all points in the series.
  44299. *
  44300. * @function Highcharts.Series#setAllPointsToState
  44301. *
  44302. * @private
  44303. *
  44304. * @param {string} [state]
  44305. * Can be either `hover` or undefined to set to normal state.
  44306. */
  44307. setAllPointsToState: function (state) {
  44308. this.points.forEach(function (point) {
  44309. if (point.setState) {
  44310. point.setState(state);
  44311. }
  44312. });
  44313. },
  44314. /**
  44315. * Show or hide the series.
  44316. *
  44317. * @function Highcharts.Series#setVisible
  44318. *
  44319. * @param {boolean} [visible]
  44320. * True to show the series, false to hide. If undefined, the visibility is
  44321. * toggled.
  44322. *
  44323. * @param {boolean} [redraw=true]
  44324. * Whether to redraw the chart after the series is altered. If doing more
  44325. * operations on the chart, it is a good idea to set redraw to false and
  44326. * call {@link Chart#redraw|chart.redraw()} after.
  44327. *
  44328. * @fires Highcharts.Series#event:hide
  44329. * @fires Highcharts.Series#event:show
  44330. */
  44331. setVisible: function (vis, redraw) {
  44332. var series = this,
  44333. chart = series.chart,
  44334. legendItem = series.legendItem,
  44335. showOrHide,
  44336. ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
  44337. oldVisibility = series.visible;
  44338. // if called without an argument, toggle visibility
  44339. series.visible =
  44340. vis =
  44341. series.options.visible =
  44342. series.userOptions.visible =
  44343. typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
  44344. showOrHide = vis ? 'show' : 'hide';
  44345. // show or hide elements
  44346. [
  44347. 'group',
  44348. 'dataLabelsGroup',
  44349. 'markerGroup',
  44350. 'tracker',
  44351. 'tt'
  44352. ].forEach(function (key) {
  44353. if (series[key]) {
  44354. series[key][showOrHide]();
  44355. }
  44356. });
  44357. // hide tooltip (#1361)
  44358. if (chart.hoverSeries === series ||
  44359. (chart.hoverPoint && chart.hoverPoint.series) === series) {
  44360. series.onMouseOut();
  44361. }
  44362. if (legendItem) {
  44363. chart.legend.colorizeItem(series, vis);
  44364. }
  44365. // rescale or adapt to resized chart
  44366. series.isDirty = true;
  44367. // in a stack, all other series are affected
  44368. if (series.options.stacking) {
  44369. chart.series.forEach(function (otherSeries) {
  44370. if (otherSeries.options.stacking && otherSeries.visible) {
  44371. otherSeries.isDirty = true;
  44372. }
  44373. });
  44374. }
  44375. // show or hide linked series
  44376. series.linkedSeries.forEach(function (otherSeries) {
  44377. otherSeries.setVisible(vis, false);
  44378. });
  44379. if (ignoreHiddenSeries) {
  44380. chart.isDirtyBox = true;
  44381. }
  44382. fireEvent(series, showOrHide);
  44383. if (redraw !== false) {
  44384. chart.redraw();
  44385. }
  44386. },
  44387. /**
  44388. * Show the series if hidden.
  44389. *
  44390. * @sample highcharts/members/series-hide/
  44391. * Toggle visibility from a button
  44392. *
  44393. * @function Highcharts.Series#show
  44394. * @fires Highcharts.Series#event:show
  44395. */
  44396. show: function () {
  44397. this.setVisible(true);
  44398. },
  44399. /**
  44400. * Hide the series if visible. If the
  44401. * [chart.ignoreHiddenSeries](https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries)
  44402. * option is true, the chart is redrawn without this series.
  44403. *
  44404. * @sample highcharts/members/series-hide/
  44405. * Toggle visibility from a button
  44406. *
  44407. * @function Highcharts.Series#hide
  44408. * @fires Highcharts.Series#event:hide
  44409. */
  44410. hide: function () {
  44411. this.setVisible(false);
  44412. },
  44413. /**
  44414. * Select or unselect the series. This means its
  44415. * {@link Highcharts.Series.selected|selected}
  44416. * property is set, the checkbox in the legend is toggled and when selected,
  44417. * the series is returned by the {@link Highcharts.Chart#getSelectedSeries}
  44418. * function.
  44419. *
  44420. * @sample highcharts/members/series-select/
  44421. * Select a series from a button
  44422. *
  44423. * @function Highcharts.Series#select
  44424. *
  44425. * @param {boolean} [selected]
  44426. * True to select the series, false to unselect. If undefined, the selection
  44427. * state is toggled.
  44428. *
  44429. * @fires Highcharts.Series#event:select
  44430. * @fires Highcharts.Series#event:unselect
  44431. */
  44432. select: function (selected) {
  44433. var series = this;
  44434. series.selected =
  44435. selected =
  44436. this.options.selected = (typeof selected === 'undefined' ?
  44437. !series.selected :
  44438. selected);
  44439. if (series.checkbox) {
  44440. series.checkbox.checked = selected;
  44441. }
  44442. fireEvent(series, selected ? 'select' : 'unselect');
  44443. },
  44444. /**
  44445. * @private
  44446. * @borrows Highcharts.TrackerMixin.drawTrackerGraph as Highcharts.Series#drawTracker
  44447. */
  44448. drawTracker: TrackerMixin.drawTrackerGraph
  44449. });
  44450. });
  44451. _registerModule(_modules, 'Core/Responsive.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js']], function (Chart, U) {
  44452. /* *
  44453. *
  44454. * (c) 2010-2020 Torstein Honsi
  44455. *
  44456. * License: www.highcharts.com/license
  44457. *
  44458. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44459. *
  44460. * */
  44461. var find = U.find,
  44462. isArray = U.isArray,
  44463. isObject = U.isObject,
  44464. merge = U.merge,
  44465. objectEach = U.objectEach,
  44466. pick = U.pick,
  44467. splat = U.splat,
  44468. uniqueKey = U.uniqueKey;
  44469. /**
  44470. * A callback function to gain complete control on when the responsive rule
  44471. * applies.
  44472. *
  44473. * @callback Highcharts.ResponsiveCallbackFunction
  44474. *
  44475. * @param {Highcharts.Chart} this
  44476. * Chart context.
  44477. *
  44478. * @return {boolean}
  44479. * Return `true` if it applies.
  44480. */
  44481. /**
  44482. * Allows setting a set of rules to apply for different screen or chart
  44483. * sizes. Each rule specifies additional chart options.
  44484. *
  44485. * @sample {highstock} stock/demo/responsive/
  44486. * Stock chart
  44487. * @sample highcharts/responsive/axis/
  44488. * Axis
  44489. * @sample highcharts/responsive/legend/
  44490. * Legend
  44491. * @sample highcharts/responsive/classname/
  44492. * Class name
  44493. *
  44494. * @since 5.0.0
  44495. * @apioption responsive
  44496. */
  44497. /**
  44498. * A set of rules for responsive settings. The rules are executed from
  44499. * the top down.
  44500. *
  44501. * @sample {highcharts} highcharts/responsive/axis/
  44502. * Axis changes
  44503. * @sample {highstock} highcharts/responsive/axis/
  44504. * Axis changes
  44505. * @sample {highmaps} highcharts/responsive/axis/
  44506. * Axis changes
  44507. *
  44508. * @type {Array<*>}
  44509. * @since 5.0.0
  44510. * @apioption responsive.rules
  44511. */
  44512. /**
  44513. * A full set of chart options to apply as overrides to the general
  44514. * chart options. The chart options are applied when the given rule
  44515. * is active.
  44516. *
  44517. * A special case is configuration objects that take arrays, for example
  44518. * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
  44519. * collections, an `id` option is used to map the new option set to
  44520. * an existing object. If an existing object of the same id is not found,
  44521. * the item of the same indexupdated. So for example, setting `chartOptions`
  44522. * with two series items without an `id`, will cause the existing chart's
  44523. * two series to be updated with respective options.
  44524. *
  44525. * @sample {highstock} stock/demo/responsive/
  44526. * Stock chart
  44527. * @sample highcharts/responsive/axis/
  44528. * Axis
  44529. * @sample highcharts/responsive/legend/
  44530. * Legend
  44531. * @sample highcharts/responsive/classname/
  44532. * Class name
  44533. *
  44534. * @type {Highcharts.Options}
  44535. * @since 5.0.0
  44536. * @apioption responsive.rules.chartOptions
  44537. */
  44538. /**
  44539. * Under which conditions the rule applies.
  44540. *
  44541. * @since 5.0.0
  44542. * @apioption responsive.rules.condition
  44543. */
  44544. /**
  44545. * A callback function to gain complete control on when the responsive
  44546. * rule applies. Return `true` if it applies. This opens for checking
  44547. * against other metrics than the chart size, for example the document
  44548. * size or other elements.
  44549. *
  44550. * @type {Highcharts.ResponsiveCallbackFunction}
  44551. * @since 5.0.0
  44552. * @context Highcharts.Chart
  44553. * @apioption responsive.rules.condition.callback
  44554. */
  44555. /**
  44556. * The responsive rule applies if the chart height is less than this.
  44557. *
  44558. * @type {number}
  44559. * @since 5.0.0
  44560. * @apioption responsive.rules.condition.maxHeight
  44561. */
  44562. /**
  44563. * The responsive rule applies if the chart width is less than this.
  44564. *
  44565. * @sample highcharts/responsive/axis/
  44566. * Max width is 500
  44567. *
  44568. * @type {number}
  44569. * @since 5.0.0
  44570. * @apioption responsive.rules.condition.maxWidth
  44571. */
  44572. /**
  44573. * The responsive rule applies if the chart height is greater than this.
  44574. *
  44575. * @type {number}
  44576. * @default 0
  44577. * @since 5.0.0
  44578. * @apioption responsive.rules.condition.minHeight
  44579. */
  44580. /**
  44581. * The responsive rule applies if the chart width is greater than this.
  44582. *
  44583. * @type {number}
  44584. * @default 0
  44585. * @since 5.0.0
  44586. * @apioption responsive.rules.condition.minWidth
  44587. */
  44588. /* eslint-disable no-invalid-this, valid-jsdoc */
  44589. /**
  44590. * Update the chart based on the current chart/document size and options for
  44591. * responsiveness.
  44592. *
  44593. * @private
  44594. * @function Highcharts.Chart#setResponsive
  44595. * @param {boolean} [redraw=true]
  44596. * @param {boolean} [reset=false]
  44597. * Reset by un-applying all rules. Chart.update resets all rules before applying
  44598. * updated options.
  44599. */
  44600. Chart.prototype.setResponsive = function (redraw, reset) {
  44601. var options = this.options.responsive,
  44602. ruleIds = [],
  44603. currentResponsive = this.currentResponsive,
  44604. currentRuleIds,
  44605. undoOptions;
  44606. if (!reset && options && options.rules) {
  44607. options.rules.forEach(function (rule) {
  44608. if (typeof rule._id === 'undefined') {
  44609. rule._id = uniqueKey();
  44610. }
  44611. this.matchResponsiveRule(rule, ruleIds /* , redraw */);
  44612. }, this);
  44613. }
  44614. // Merge matching rules
  44615. var mergedOptions = merge.apply(0,
  44616. ruleIds.map(function (ruleId) {
  44617. return find(options.rules,
  44618. function (rule) {
  44619. return rule._id === ruleId;
  44620. }).chartOptions;
  44621. }));
  44622. mergedOptions.isResponsiveOptions = true;
  44623. // Stringified key for the rules that currently apply.
  44624. ruleIds = (ruleIds.toString() || void 0);
  44625. currentRuleIds = currentResponsive && currentResponsive.ruleIds;
  44626. // Changes in what rules apply
  44627. if (ruleIds !== currentRuleIds) {
  44628. // Undo previous rules. Before we apply a new set of rules, we need to
  44629. // roll back completely to base options (#6291).
  44630. if (currentResponsive) {
  44631. this.update(currentResponsive.undoOptions, redraw, true);
  44632. }
  44633. if (ruleIds) {
  44634. // Get undo-options for matching rules
  44635. undoOptions = this.currentOptions(mergedOptions);
  44636. undoOptions.isResponsiveOptions = true;
  44637. this.currentResponsive = {
  44638. ruleIds: ruleIds,
  44639. mergedOptions: mergedOptions,
  44640. undoOptions: undoOptions
  44641. };
  44642. this.update(mergedOptions, redraw, true);
  44643. }
  44644. else {
  44645. this.currentResponsive = void 0;
  44646. }
  44647. }
  44648. };
  44649. /**
  44650. * Handle a single responsiveness rule.
  44651. *
  44652. * @private
  44653. * @function Highcharts.Chart#matchResponsiveRule
  44654. * @param {Highcharts.ResponsiveRulesOptions} rule
  44655. * @param {Array<string>} matches
  44656. */
  44657. Chart.prototype.matchResponsiveRule = function (rule, matches) {
  44658. var condition = rule.condition,
  44659. fn = condition.callback || function () {
  44660. return (this.chartWidth <= pick(condition.maxWidth,
  44661. Number.MAX_VALUE) &&
  44662. this.chartHeight <=
  44663. pick(condition.maxHeight,
  44664. Number.MAX_VALUE) &&
  44665. this.chartWidth >= pick(condition.minWidth, 0) &&
  44666. this.chartHeight >= pick(condition.minHeight, 0));
  44667. };
  44668. if (fn.call(this)) {
  44669. matches.push(rule._id);
  44670. }
  44671. };
  44672. /**
  44673. * Get the current values for a given set of options. Used before we update
  44674. * the chart with a new responsiveness rule.
  44675. *
  44676. * @todo Restore axis options (by id?). The matching of items in collections
  44677. * bears resemblance to the oneToOne matching in Chart.update. Probably we can
  44678. * refactor out that matching and reuse it in both functions.
  44679. *
  44680. * @private
  44681. * @function Highcharts.Chart#currentOptions
  44682. * @param {Highcharts.Options} options
  44683. * @return {Highcharts.Options}
  44684. */
  44685. Chart.prototype.currentOptions = function (options) {
  44686. var chart = this,
  44687. ret = {};
  44688. /**
  44689. * Recurse over a set of options and its current values,
  44690. * and store the current values in the ret object.
  44691. */
  44692. function getCurrent(options, curr, ret, depth) {
  44693. var i;
  44694. objectEach(options, function (val, key) {
  44695. if (!depth &&
  44696. chart.collectionsWithUpdate.indexOf(key) > -1) {
  44697. val = splat(val);
  44698. ret[key] = [];
  44699. // Iterate over collections like series, xAxis or yAxis and map
  44700. // the items by index.
  44701. for (i = 0; i < Math.max(val.length, curr[key].length); i++) {
  44702. // Item exists in current data (#6347)
  44703. if (curr[key][i]) {
  44704. // If the item is missing from the new data, we need to
  44705. // save the whole config structure. Like when
  44706. // responsively updating from a dual axis layout to a
  44707. // single axis and back (#13544).
  44708. if (val[i] === void 0) {
  44709. ret[key][i] = curr[key][i];
  44710. // Otherwise, proceed
  44711. }
  44712. else {
  44713. ret[key][i] = {};
  44714. getCurrent(val[i], curr[key][i], ret[key][i], depth + 1);
  44715. }
  44716. }
  44717. }
  44718. }
  44719. else if (isObject(val)) {
  44720. ret[key] = isArray(val) ? [] : {};
  44721. getCurrent(val, curr[key] || {}, ret[key], depth + 1);
  44722. }
  44723. else if (typeof curr[key] === 'undefined') { // #10286
  44724. ret[key] = null;
  44725. }
  44726. else {
  44727. ret[key] = curr[key];
  44728. }
  44729. });
  44730. }
  44731. getCurrent(options, this.options, ret, 0);
  44732. return ret;
  44733. };
  44734. });
  44735. _registerModule(_modules, 'masters/highcharts.src.js', [_modules['Core/Globals.js']], function (Highcharts) {
  44736. return Highcharts;
  44737. });
  44738. _registerModule(_modules, 'Gantt/Tree.js', [_modules['Core/Utilities.js']], function (U) {
  44739. /* *
  44740. *
  44741. * (c) 2016-2020 Highsoft AS
  44742. *
  44743. * Authors: Jon Arild Nygard
  44744. *
  44745. * License: www.highcharts.com/license
  44746. *
  44747. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44748. *
  44749. * */
  44750. /* eslint no-console: 0 */
  44751. var extend = U.extend,
  44752. isNumber = U.isNumber,
  44753. pick = U.pick;
  44754. /**
  44755. * Creates an object map from parent id to childrens index.
  44756. *
  44757. * @private
  44758. * @function Highcharts.Tree#getListOfParents
  44759. *
  44760. * @param {Array<*>} data
  44761. * List of points set in options. `Array.parent` is parent id of point.
  44762. *
  44763. * @param {Array<string>} ids
  44764. * List of all point ids.
  44765. *
  44766. * @return {Highcharts.Dictionary<Array<*>>}
  44767. * Map from parent id to children index in data
  44768. */
  44769. var getListOfParents = function (data,
  44770. ids) {
  44771. var listOfParents = data.reduce(function (prev,
  44772. curr) {
  44773. var parent = pick(curr.parent, '');
  44774. if (typeof prev[parent] === 'undefined') {
  44775. prev[parent] = [];
  44776. }
  44777. prev[parent].push(curr);
  44778. return prev;
  44779. }, {}), parents = Object.keys(listOfParents);
  44780. // If parent does not exist, hoist parent to root of tree.
  44781. parents.forEach(function (parent, list) {
  44782. var children = listOfParents[parent];
  44783. if ((parent !== '') && (ids.indexOf(parent) === -1)) {
  44784. children.forEach(function (child) {
  44785. list[''].push(child);
  44786. });
  44787. delete list[parent];
  44788. }
  44789. });
  44790. return listOfParents;
  44791. };
  44792. var getNode = function (id,
  44793. parent,
  44794. level,
  44795. data,
  44796. mapOfIdToChildren,
  44797. options) {
  44798. var descendants = 0,
  44799. height = 0,
  44800. after = options && options.after,
  44801. before = options && options.before,
  44802. node = {
  44803. data: data,
  44804. depth: level - 1,
  44805. id: id,
  44806. level: level,
  44807. parent: parent
  44808. },
  44809. start,
  44810. end,
  44811. children;
  44812. // Allow custom logic before the children has been created.
  44813. if (typeof before === 'function') {
  44814. before(node, options);
  44815. }
  44816. // Call getNode recursively on the children. Calulate the height of the
  44817. // node, and the number of descendants.
  44818. children = ((mapOfIdToChildren[id] || [])).map(function (child) {
  44819. var node = getNode(child.id,
  44820. id, (level + 1),
  44821. child,
  44822. mapOfIdToChildren,
  44823. options),
  44824. childStart = child.start,
  44825. childEnd = (child.milestone === true ?
  44826. childStart :
  44827. child.end);
  44828. // Start should be the lowest child.start.
  44829. start = ((!isNumber(start) || childStart < start) ?
  44830. childStart :
  44831. start);
  44832. // End should be the largest child.end.
  44833. // If child is milestone, then use start as end.
  44834. end = ((!isNumber(end) || childEnd > end) ?
  44835. childEnd :
  44836. end);
  44837. descendants = descendants + 1 + node.descendants;
  44838. height = Math.max(node.height + 1, height);
  44839. return node;
  44840. });
  44841. // Calculate start and end for point if it is not already explicitly set.
  44842. if (data) {
  44843. data.start = pick(data.start, start);
  44844. data.end = pick(data.end, end);
  44845. }
  44846. extend(node, {
  44847. children: children,
  44848. descendants: descendants,
  44849. height: height
  44850. });
  44851. // Allow custom logic after the children has been created.
  44852. if (typeof after === 'function') {
  44853. after(node, options);
  44854. }
  44855. return node;
  44856. };
  44857. var getTree = function (data,
  44858. options) {
  44859. var ids = data.map(function (d) {
  44860. return d.id;
  44861. }), mapOfIdToChildren = getListOfParents(data, ids);
  44862. return getNode('', null, 1, null, mapOfIdToChildren, options);
  44863. };
  44864. var Tree = {
  44865. getListOfParents: getListOfParents,
  44866. getNode: getNode,
  44867. getTree: getTree
  44868. };
  44869. return Tree;
  44870. });
  44871. _registerModule(_modules, 'Core/Axis/TreeGridTick.js', [_modules['Core/Utilities.js']], function (U) {
  44872. /* *
  44873. *
  44874. * (c) 2016 Highsoft AS
  44875. * Authors: Jon Arild Nygard
  44876. *
  44877. * License: www.highcharts.com/license
  44878. *
  44879. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44880. *
  44881. * */
  44882. var addEvent = U.addEvent,
  44883. defined = U.defined,
  44884. isObject = U.isObject,
  44885. isNumber = U.isNumber,
  44886. pick = U.pick,
  44887. wrap = U.wrap;
  44888. /**
  44889. * @private
  44890. */
  44891. var TreeGridTick;
  44892. (function (TreeGridTick) {
  44893. /* *
  44894. *
  44895. * Interfaces
  44896. *
  44897. * */
  44898. /* *
  44899. *
  44900. * Variables
  44901. *
  44902. * */
  44903. var applied = false;
  44904. /* *
  44905. *
  44906. * Functions
  44907. *
  44908. * */
  44909. /**
  44910. * @private
  44911. */
  44912. function compose(TickClass) {
  44913. if (!applied) {
  44914. addEvent(TickClass, 'init', onInit);
  44915. wrap(TickClass.prototype, 'getLabelPosition', wrapGetLabelPosition);
  44916. wrap(TickClass.prototype, 'renderLabel', wrapRenderLabel);
  44917. // backwards compatibility
  44918. TickClass.prototype.collapse = function (redraw) {
  44919. this.treeGrid.collapse(redraw);
  44920. };
  44921. TickClass.prototype.expand = function (redraw) {
  44922. this.treeGrid.expand(redraw);
  44923. };
  44924. TickClass.prototype.toggleCollapse = function (redraw) {
  44925. this.treeGrid.toggleCollapse(redraw);
  44926. };
  44927. applied = true;
  44928. }
  44929. }
  44930. TreeGridTick.compose = compose;
  44931. /**
  44932. * @private
  44933. */
  44934. function onInit() {
  44935. var tick = this;
  44936. if (!tick.treeGrid) {
  44937. tick.treeGrid = new Additions(tick);
  44938. }
  44939. }
  44940. /**
  44941. * @private
  44942. */
  44943. function onTickHover(label) {
  44944. label.addClass('highcharts-treegrid-node-active');
  44945. if (!label.renderer.styledMode) {
  44946. label.css({
  44947. textDecoration: 'underline'
  44948. });
  44949. }
  44950. }
  44951. /**
  44952. * @private
  44953. */
  44954. function onTickHoverExit(label, options) {
  44955. var css = defined(options.style) ? options.style : {};
  44956. label.removeClass('highcharts-treegrid-node-active');
  44957. if (!label.renderer.styledMode) {
  44958. label.css({ textDecoration: css.textDecoration });
  44959. }
  44960. }
  44961. /**
  44962. * @private
  44963. */
  44964. function renderLabelIcon(tick, params) {
  44965. var treeGrid = tick.treeGrid,
  44966. isNew = !treeGrid.labelIcon,
  44967. renderer = params.renderer,
  44968. labelBox = params.xy,
  44969. options = params.options,
  44970. width = options.width,
  44971. height = options.height,
  44972. iconCenter = {
  44973. x: labelBox.x - (width / 2) - options.padding,
  44974. y: labelBox.y - (height / 2)
  44975. },
  44976. rotation = params.collapsed ? 90 : 180,
  44977. shouldRender = params.show && isNumber(iconCenter.y);
  44978. var icon = treeGrid.labelIcon;
  44979. if (!icon) {
  44980. treeGrid.labelIcon = icon = renderer
  44981. .path(renderer.symbols[options.type](options.x, options.y, width, height))
  44982. .addClass('highcharts-label-icon')
  44983. .add(params.group);
  44984. }
  44985. // Set the new position, and show or hide
  44986. if (!shouldRender) {
  44987. icon.attr({ y: -9999 }); // #1338
  44988. }
  44989. // Presentational attributes
  44990. if (!renderer.styledMode) {
  44991. icon
  44992. .attr({
  44993. 'stroke-width': 1,
  44994. 'fill': pick(params.color, '#666666')
  44995. })
  44996. .css({
  44997. cursor: 'pointer',
  44998. stroke: options.lineColor,
  44999. strokeWidth: options.lineWidth
  45000. });
  45001. }
  45002. // Update the icon positions
  45003. icon[isNew ? 'attr' : 'animate']({
  45004. translateX: iconCenter.x,
  45005. translateY: iconCenter.y,
  45006. rotation: rotation
  45007. });
  45008. }
  45009. /**
  45010. * @private
  45011. */
  45012. function wrapGetLabelPosition(proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  45013. var tick = this,
  45014. lbOptions = pick(tick.options && tick.options.labels,
  45015. labelOptions),
  45016. pos = tick.pos,
  45017. axis = tick.axis,
  45018. options = axis.options,
  45019. isTreeGrid = options.type === 'treegrid',
  45020. result = proceed.apply(tick,
  45021. [x,
  45022. y,
  45023. label,
  45024. horiz,
  45025. lbOptions,
  45026. tickmarkOffset,
  45027. index,
  45028. step]);
  45029. var symbolOptions,
  45030. indentation,
  45031. mapOfPosToGridNode,
  45032. node,
  45033. level;
  45034. if (isTreeGrid) {
  45035. symbolOptions = (lbOptions && isObject(lbOptions.symbol, true) ?
  45036. lbOptions.symbol :
  45037. {});
  45038. indentation = (lbOptions && isNumber(lbOptions.indentation) ?
  45039. lbOptions.indentation :
  45040. 0);
  45041. mapOfPosToGridNode = axis.treeGrid.mapOfPosToGridNode;
  45042. node = mapOfPosToGridNode && mapOfPosToGridNode[pos];
  45043. level = (node && node.depth) || 1;
  45044. result.x += (
  45045. // Add space for symbols
  45046. ((symbolOptions.width) + (symbolOptions.padding * 2)) +
  45047. // Apply indentation
  45048. ((level - 1) * indentation));
  45049. }
  45050. return result;
  45051. }
  45052. /**
  45053. * @private
  45054. */
  45055. function wrapRenderLabel(proceed) {
  45056. var tick = this, pos = tick.pos, axis = tick.axis, label = tick.label, mapOfPosToGridNode = axis.treeGrid.mapOfPosToGridNode, options = axis.options, labelOptions = pick(tick.options && tick.options.labels, options && options.labels), symbolOptions = (labelOptions && isObject(labelOptions.symbol, true) ?
  45057. labelOptions.symbol :
  45058. {}), node = mapOfPosToGridNode && mapOfPosToGridNode[pos], level = node && node.depth, isTreeGrid = options.type === 'treegrid', shouldRender = axis.tickPositions.indexOf(pos) > -1, prefixClassName = 'highcharts-treegrid-node-', styledMode = axis.chart.styledMode;
  45059. var collapsed,
  45060. addClassName,
  45061. removeClassName;
  45062. if (isTreeGrid && node) {
  45063. // Add class name for hierarchical styling.
  45064. if (label &&
  45065. label.element) {
  45066. label.addClass(prefixClassName + 'level-' + level);
  45067. }
  45068. }
  45069. proceed.apply(tick, Array.prototype.slice.call(arguments, 1));
  45070. if (isTreeGrid &&
  45071. label &&
  45072. label.element &&
  45073. node &&
  45074. node.descendants &&
  45075. node.descendants > 0) {
  45076. collapsed = axis.treeGrid.isCollapsed(node);
  45077. renderLabelIcon(tick, {
  45078. color: !styledMode && label.styles && label.styles.color || '',
  45079. collapsed: collapsed,
  45080. group: label.parentGroup,
  45081. options: symbolOptions,
  45082. renderer: label.renderer,
  45083. show: shouldRender,
  45084. xy: label.xy
  45085. });
  45086. // Add class name for the node.
  45087. addClassName = prefixClassName +
  45088. (collapsed ? 'collapsed' : 'expanded');
  45089. removeClassName = prefixClassName +
  45090. (collapsed ? 'expanded' : 'collapsed');
  45091. label
  45092. .addClass(addClassName)
  45093. .removeClass(removeClassName);
  45094. if (!styledMode) {
  45095. label.css({
  45096. cursor: 'pointer'
  45097. });
  45098. }
  45099. // Add events to both label text and icon
  45100. [label, tick.treeGrid.labelIcon].forEach(function (object) {
  45101. if (object && !object.attachedTreeGridEvents) {
  45102. // On hover
  45103. addEvent(object.element, 'mouseover', function () {
  45104. onTickHover(label);
  45105. });
  45106. // On hover out
  45107. addEvent(object.element, 'mouseout', function () {
  45108. onTickHoverExit(label, labelOptions);
  45109. });
  45110. addEvent(object.element, 'click', function () {
  45111. tick.treeGrid.toggleCollapse();
  45112. });
  45113. object.attachedTreeGridEvents = true;
  45114. }
  45115. });
  45116. }
  45117. }
  45118. /* *
  45119. *
  45120. * Classes
  45121. *
  45122. * */
  45123. /**
  45124. * @private
  45125. * @class
  45126. */
  45127. var Additions = /** @class */ (function () {
  45128. /* *
  45129. *
  45130. * Constructors
  45131. *
  45132. * */
  45133. /**
  45134. * @private
  45135. */
  45136. function Additions(tick) {
  45137. this.tick = tick;
  45138. }
  45139. /* *
  45140. *
  45141. * Functions
  45142. *
  45143. * */
  45144. /**
  45145. * Collapse the grid cell. Used when axis is of type treegrid.
  45146. *
  45147. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  45148. *
  45149. * @private
  45150. * @function Highcharts.Tick#collapse
  45151. *
  45152. * @param {boolean} [redraw=true]
  45153. * Whether to redraw the chart or wait for an explicit call to
  45154. * {@link Highcharts.Chart#redraw}
  45155. */
  45156. Additions.prototype.collapse = function (redraw) {
  45157. var tick = this.tick,
  45158. axis = tick.axis,
  45159. brokenAxis = axis.brokenAxis;
  45160. if (brokenAxis &&
  45161. axis.treeGrid.mapOfPosToGridNode) {
  45162. var pos = tick.pos,
  45163. node = axis.treeGrid.mapOfPosToGridNode[pos],
  45164. breaks = axis.treeGrid.collapse(node);
  45165. brokenAxis.setBreaks(breaks, pick(redraw, true));
  45166. }
  45167. };
  45168. /**
  45169. * Expand the grid cell. Used when axis is of type treegrid.
  45170. *
  45171. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  45172. *
  45173. * @private
  45174. * @function Highcharts.Tick#expand
  45175. *
  45176. * @param {boolean} [redraw=true]
  45177. * Whether to redraw the chart or wait for an explicit call to
  45178. * {@link Highcharts.Chart#redraw}
  45179. */
  45180. Additions.prototype.expand = function (redraw) {
  45181. var tick = this.tick,
  45182. axis = tick.axis,
  45183. brokenAxis = axis.brokenAxis;
  45184. if (brokenAxis &&
  45185. axis.treeGrid.mapOfPosToGridNode) {
  45186. var pos = tick.pos,
  45187. node = axis.treeGrid.mapOfPosToGridNode[pos],
  45188. breaks = axis.treeGrid.expand(node);
  45189. brokenAxis.setBreaks(breaks, pick(redraw, true));
  45190. }
  45191. };
  45192. /**
  45193. * Toggle the collapse/expand state of the grid cell. Used when axis is
  45194. * of type treegrid.
  45195. *
  45196. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  45197. *
  45198. * @private
  45199. * @function Highcharts.Tick#toggleCollapse
  45200. *
  45201. * @param {boolean} [redraw=true]
  45202. * Whether to redraw the chart or wait for an explicit call to
  45203. * {@link Highcharts.Chart#redraw}
  45204. */
  45205. Additions.prototype.toggleCollapse = function (redraw) {
  45206. var tick = this.tick,
  45207. axis = tick.axis,
  45208. brokenAxis = axis.brokenAxis;
  45209. if (brokenAxis &&
  45210. axis.treeGrid.mapOfPosToGridNode) {
  45211. var pos = tick.pos,
  45212. node = axis.treeGrid.mapOfPosToGridNode[pos],
  45213. breaks = axis.treeGrid.toggleCollapse(node);
  45214. brokenAxis.setBreaks(breaks, pick(redraw, true));
  45215. }
  45216. };
  45217. return Additions;
  45218. }());
  45219. TreeGridTick.Additions = Additions;
  45220. })(TreeGridTick || (TreeGridTick = {}));
  45221. return TreeGridTick;
  45222. });
  45223. _registerModule(_modules, 'Mixins/TreeSeries.js', [_modules['Core/Color.js'], _modules['Core/Utilities.js']], function (Color, U) {
  45224. /* *
  45225. *
  45226. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45227. *
  45228. * */
  45229. var extend = U.extend,
  45230. isArray = U.isArray,
  45231. isNumber = U.isNumber,
  45232. isObject = U.isObject,
  45233. merge = U.merge,
  45234. pick = U.pick;
  45235. var isBoolean = function (x) {
  45236. return typeof x === 'boolean';
  45237. }, isFn = function (x) {
  45238. return typeof x === 'function';
  45239. };
  45240. /* eslint-disable valid-jsdoc */
  45241. /**
  45242. * @todo Combine buildTree and buildNode with setTreeValues
  45243. * @todo Remove logic from Treemap and make it utilize this mixin.
  45244. * @private
  45245. */
  45246. var setTreeValues = function setTreeValues(tree,
  45247. options) {
  45248. var before = options.before,
  45249. idRoot = options.idRoot,
  45250. mapIdToNode = options.mapIdToNode,
  45251. nodeRoot = mapIdToNode[idRoot],
  45252. levelIsConstant = (isBoolean(options.levelIsConstant) ?
  45253. options.levelIsConstant :
  45254. true),
  45255. points = options.points,
  45256. point = points[tree.i],
  45257. optionsPoint = point && point.options || {},
  45258. childrenTotal = 0,
  45259. children = [],
  45260. value;
  45261. extend(tree, {
  45262. levelDynamic: tree.level - (levelIsConstant ? 0 : nodeRoot.level),
  45263. name: pick(point && point.name, ''),
  45264. visible: (idRoot === tree.id ||
  45265. (isBoolean(options.visible) ? options.visible : false))
  45266. });
  45267. if (isFn(before)) {
  45268. tree = before(tree, options);
  45269. }
  45270. // First give the children some values
  45271. tree.children.forEach(function (child, i) {
  45272. var newOptions = extend({},
  45273. options);
  45274. extend(newOptions, {
  45275. index: i,
  45276. siblings: tree.children.length,
  45277. visible: tree.visible
  45278. });
  45279. child = setTreeValues(child, newOptions);
  45280. children.push(child);
  45281. if (child.visible) {
  45282. childrenTotal += child.val;
  45283. }
  45284. });
  45285. tree.visible = childrenTotal > 0 || tree.visible;
  45286. // Set the values
  45287. value = pick(optionsPoint.value, childrenTotal);
  45288. extend(tree, {
  45289. children: children,
  45290. childrenTotal: childrenTotal,
  45291. isLeaf: tree.visible && !childrenTotal,
  45292. val: value
  45293. });
  45294. return tree;
  45295. };
  45296. /**
  45297. * @private
  45298. */
  45299. var getColor = function getColor(node,
  45300. options) {
  45301. var index = options.index,
  45302. mapOptionsToLevel = options.mapOptionsToLevel,
  45303. parentColor = options.parentColor,
  45304. parentColorIndex = options.parentColorIndex,
  45305. series = options.series,
  45306. colors = options.colors,
  45307. siblings = options.siblings,
  45308. points = series.points,
  45309. getColorByPoint,
  45310. chartOptionsChart = series.chart.options.chart,
  45311. point,
  45312. level,
  45313. colorByPoint,
  45314. colorIndexByPoint,
  45315. color,
  45316. colorIndex;
  45317. /**
  45318. * @private
  45319. */
  45320. function variation(color) {
  45321. var colorVariation = level && level.colorVariation;
  45322. if (colorVariation) {
  45323. if (colorVariation.key === 'brightness') {
  45324. return Color.parse(color).brighten(colorVariation.to * (index / siblings)).get();
  45325. }
  45326. }
  45327. return color;
  45328. }
  45329. if (node) {
  45330. point = points[node.i];
  45331. level = mapOptionsToLevel[node.level] || {};
  45332. getColorByPoint = point && level.colorByPoint;
  45333. if (getColorByPoint) {
  45334. colorIndexByPoint = point.index % (colors ?
  45335. colors.length :
  45336. chartOptionsChart.colorCount);
  45337. colorByPoint = colors && colors[colorIndexByPoint];
  45338. }
  45339. // Select either point color, level color or inherited color.
  45340. if (!series.chart.styledMode) {
  45341. color = pick(point && point.options.color, level && level.color, colorByPoint, parentColor && variation(parentColor), series.color);
  45342. }
  45343. colorIndex = pick(point && point.options.colorIndex, level && level.colorIndex, colorIndexByPoint, parentColorIndex, options.colorIndex);
  45344. }
  45345. return {
  45346. color: color,
  45347. colorIndex: colorIndex
  45348. };
  45349. };
  45350. /**
  45351. * Creates a map from level number to its given options.
  45352. *
  45353. * @private
  45354. * @function getLevelOptions
  45355. * @param {object} params
  45356. * Object containing parameters.
  45357. * - `defaults` Object containing default options. The default options
  45358. * are merged with the userOptions to get the final options for a
  45359. * specific level.
  45360. * - `from` The lowest level number.
  45361. * - `levels` User options from series.levels.
  45362. * - `to` The highest level number.
  45363. * @return {Highcharts.Dictionary<object>|null}
  45364. * Returns a map from level number to its given options.
  45365. */
  45366. var getLevelOptions = function getLevelOptions(params) {
  45367. var result = null,
  45368. defaults,
  45369. converted,
  45370. i,
  45371. from,
  45372. to,
  45373. levels;
  45374. if (isObject(params)) {
  45375. result = {};
  45376. from = isNumber(params.from) ? params.from : 1;
  45377. levels = params.levels;
  45378. converted = {};
  45379. defaults = isObject(params.defaults) ? params.defaults : {};
  45380. if (isArray(levels)) {
  45381. converted = levels.reduce(function (obj, item) {
  45382. var level,
  45383. levelIsConstant,
  45384. options;
  45385. if (isObject(item) && isNumber(item.level)) {
  45386. options = merge({}, item);
  45387. levelIsConstant = (isBoolean(options.levelIsConstant) ?
  45388. options.levelIsConstant :
  45389. defaults.levelIsConstant);
  45390. // Delete redundant properties.
  45391. delete options.levelIsConstant;
  45392. delete options.level;
  45393. // Calculate which level these options apply to.
  45394. level = item.level + (levelIsConstant ? 0 : from - 1);
  45395. if (isObject(obj[level])) {
  45396. extend(obj[level], options);
  45397. }
  45398. else {
  45399. obj[level] = options;
  45400. }
  45401. }
  45402. return obj;
  45403. }, {});
  45404. }
  45405. to = isNumber(params.to) ? params.to : 1;
  45406. for (i = 0; i <= to; i++) {
  45407. result[i] = merge({}, defaults, isObject(converted[i]) ? converted[i] : {});
  45408. }
  45409. }
  45410. return result;
  45411. };
  45412. /**
  45413. * Update the rootId property on the series. Also makes sure that it is
  45414. * accessible to exporting.
  45415. *
  45416. * @private
  45417. * @function updateRootId
  45418. *
  45419. * @param {object} series
  45420. * The series to operate on.
  45421. *
  45422. * @return {string}
  45423. * Returns the resulting rootId after update.
  45424. */
  45425. var updateRootId = function (series) {
  45426. var rootId,
  45427. options;
  45428. if (isObject(series)) {
  45429. // Get the series options.
  45430. options = isObject(series.options) ? series.options : {};
  45431. // Calculate the rootId.
  45432. rootId = pick(series.rootNode, options.rootId, '');
  45433. // Set rootId on series.userOptions to pick it up in exporting.
  45434. if (isObject(series.userOptions)) {
  45435. series.userOptions.rootId = rootId;
  45436. }
  45437. // Set rootId on series to pick it up on next update.
  45438. series.rootNode = rootId;
  45439. }
  45440. return rootId;
  45441. };
  45442. var result = {
  45443. getColor: getColor,
  45444. getLevelOptions: getLevelOptions,
  45445. setTreeValues: setTreeValues,
  45446. updateRootId: updateRootId
  45447. };
  45448. return result;
  45449. });
  45450. _registerModule(_modules, 'Core/Axis/GridAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Utilities.js']], function (Axis, H, O, Tick, U) {
  45451. /* *
  45452. *
  45453. * (c) 2016 Highsoft AS
  45454. * Authors: Lars A. V. Cabrera
  45455. *
  45456. * License: www.highcharts.com/license
  45457. *
  45458. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45459. *
  45460. * */
  45461. var dateFormat = O.dateFormat;
  45462. var addEvent = U.addEvent,
  45463. defined = U.defined,
  45464. erase = U.erase,
  45465. find = U.find,
  45466. isArray = U.isArray,
  45467. isNumber = U.isNumber,
  45468. merge = U.merge,
  45469. pick = U.pick,
  45470. timeUnits = U.timeUnits,
  45471. wrap = U.wrap;
  45472. var argsToArray = function (args) {
  45473. return Array.prototype.slice.call(args, 1);
  45474. }, isObject = function (x) {
  45475. // Always use strict mode
  45476. return U.isObject(x, true);
  45477. }, Chart = H.Chart;
  45478. var applyGridOptions = function applyGridOptions(axis) {
  45479. var options = axis.options;
  45480. // Center-align by default
  45481. if (!options.labels) {
  45482. options.labels = {};
  45483. }
  45484. options.labels.align = pick(options.labels.align, 'center');
  45485. // @todo: Check against tickLabelPlacement between/on etc
  45486. /* Prevents adding the last tick label if the axis is not a category
  45487. axis.
  45488. Since numeric labels are normally placed at starts and ends of a
  45489. range of value, and this module makes the label point at the value,
  45490. an "extra" label would appear. */
  45491. if (!axis.categories) {
  45492. options.showLastLabel = false;
  45493. }
  45494. // Prevents rotation of labels when squished, as rotating them would not
  45495. // help.
  45496. axis.labelRotation = 0;
  45497. options.labels.rotation = 0;
  45498. };
  45499. /**
  45500. * For a datetime axis, the scale will automatically adjust to the
  45501. * appropriate unit. This member gives the default string
  45502. * representations used for each unit. For intermediate values,
  45503. * different units may be used, for example the `day` unit can be used
  45504. * on midnight and `hour` unit be used for intermediate values on the
  45505. * same axis.
  45506. * For grid axes (like in Gantt charts),
  45507. * it is possible to declare as a list to provide different
  45508. * formats depending on available space.
  45509. * For an overview of the replacement codes, see
  45510. * [dateFormat](/class-reference/Highcharts#dateFormat).
  45511. *
  45512. * Defaults to:
  45513. * ```js
  45514. * {
  45515. hour: {
  45516. list: ['%H:%M', '%H']
  45517. },
  45518. day: {
  45519. list: ['%A, %e. %B', '%a, %e. %b', '%E']
  45520. },
  45521. week: {
  45522. list: ['Week %W', 'W%W']
  45523. },
  45524. month: {
  45525. list: ['%B', '%b', '%o']
  45526. }
  45527. },
  45528. * ```
  45529. *
  45530. * @sample {gantt} gantt/demo/left-axis-table
  45531. * Gantt Chart with custom axis date format.
  45532. *
  45533. * @product gantt
  45534. * @apioption xAxis.dateTimeLabelFormats
  45535. */
  45536. /**
  45537. * Set grid options for the axis labels. Requires Highcharts Gantt.
  45538. *
  45539. * @since 6.2.0
  45540. * @product gantt
  45541. * @apioption xAxis.grid
  45542. */
  45543. /**
  45544. * Enable grid on the axis labels. Defaults to true for Gantt charts.
  45545. *
  45546. * @type {boolean}
  45547. * @default true
  45548. * @since 6.2.0
  45549. * @product gantt
  45550. * @apioption xAxis.grid.enabled
  45551. */
  45552. /**
  45553. * Set specific options for each column (or row for horizontal axes) in the
  45554. * grid. Each extra column/row is its own axis, and the axis options can be set
  45555. * here.
  45556. *
  45557. * @sample gantt/demo/left-axis-table
  45558. * Left axis as a table
  45559. *
  45560. * @type {Array<Highcharts.XAxisOptions>}
  45561. * @apioption xAxis.grid.columns
  45562. */
  45563. /**
  45564. * Set border color for the label grid lines.
  45565. *
  45566. * @type {Highcharts.ColorString}
  45567. * @apioption xAxis.grid.borderColor
  45568. */
  45569. /**
  45570. * Set border width of the label grid lines.
  45571. *
  45572. * @type {number}
  45573. * @default 1
  45574. * @apioption xAxis.grid.borderWidth
  45575. */
  45576. /**
  45577. * Set cell height for grid axis labels. By default this is calculated from font
  45578. * size. This option only applies to horizontal axes.
  45579. *
  45580. * @sample gantt/grid-axis/cellheight
  45581. * Gant chart with custom cell height
  45582. * @type {number}
  45583. * @apioption xAxis.grid.cellHeight
  45584. */
  45585. ''; // detach doclets above
  45586. /**
  45587. * Get the largest label width and height.
  45588. *
  45589. * @private
  45590. * @function Highcharts.Axis#getMaxLabelDimensions
  45591. *
  45592. * @param {Highcharts.Dictionary<Highcharts.Tick>} ticks
  45593. * All the ticks on one axis.
  45594. *
  45595. * @param {Array<number|string>} tickPositions
  45596. * All the tick positions on one axis.
  45597. *
  45598. * @return {Highcharts.SizeObject}
  45599. * Object containing the properties height and width.
  45600. *
  45601. * @todo Move this to the generic axis implementation, as it is used there.
  45602. */
  45603. Axis.prototype.getMaxLabelDimensions = function (ticks, tickPositions) {
  45604. var dimensions = {
  45605. width: 0,
  45606. height: 0
  45607. };
  45608. tickPositions.forEach(function (pos) {
  45609. var tick = ticks[pos],
  45610. tickHeight = 0,
  45611. tickWidth = 0,
  45612. label;
  45613. if (isObject(tick)) {
  45614. label = isObject(tick.label) ? tick.label : {};
  45615. // Find width and height of tick
  45616. tickHeight = label.getBBox ? label.getBBox().height : 0;
  45617. if (label.textStr) {
  45618. // Set the tickWidth same as the label width after ellipsis
  45619. // applied #10281
  45620. tickWidth = Math.round(label.getBBox().width);
  45621. }
  45622. // Update the result if width and/or height are larger
  45623. dimensions.height = Math.max(tickHeight, dimensions.height);
  45624. dimensions.width = Math.max(tickWidth, dimensions.width);
  45625. }
  45626. });
  45627. return dimensions;
  45628. };
  45629. // Adds week date format
  45630. H.dateFormats.W = function (timestamp) {
  45631. var d = new this.Date(timestamp);
  45632. var firstDay = (this.get('Day',
  45633. d) + 6) % 7;
  45634. var thursday = new this.Date(d.valueOf());
  45635. this.set('Date', thursday, this.get('Date', d) - firstDay + 3);
  45636. var firstThursday = new this.Date(this.get('FullYear',
  45637. thursday), 0, 1);
  45638. if (this.get('Day', firstThursday) !== 4) {
  45639. this.set('Month', d, 0);
  45640. this.set('Date', d, 1 + (11 - this.get('Day', firstThursday)) % 7);
  45641. }
  45642. return (1 +
  45643. Math.floor((thursday.valueOf() - firstThursday.valueOf()) / 604800000)).toString();
  45644. };
  45645. // First letter of the day of the week, e.g. 'M' for 'Monday'.
  45646. H.dateFormats.E = function (timestamp) {
  45647. return dateFormat('%a', timestamp, true).charAt(0);
  45648. };
  45649. /* eslint-disable no-invalid-this */
  45650. addEvent(Chart, 'afterSetChartSize', function () {
  45651. this.axes.forEach(function (axis) {
  45652. (axis.grid && axis.grid.columns || []).forEach(function (column) {
  45653. column.setAxisSize();
  45654. column.setAxisTranslation();
  45655. });
  45656. });
  45657. });
  45658. // Center tick labels in cells.
  45659. addEvent(Tick, 'afterGetLabelPosition', function (e) {
  45660. var tick = this,
  45661. label = tick.label,
  45662. axis = tick.axis,
  45663. reversed = axis.reversed,
  45664. chart = axis.chart,
  45665. options = axis.options,
  45666. gridOptions = options.grid || {},
  45667. labelOpts = axis.options.labels,
  45668. align = labelOpts.align,
  45669. // verticalAlign is currently not supported for axis.labels.
  45670. verticalAlign = 'middle', // labelOpts.verticalAlign,
  45671. side = GridAxis.Side[axis.side],
  45672. tickmarkOffset = e.tickmarkOffset,
  45673. tickPositions = axis.tickPositions,
  45674. tickPos = tick.pos - tickmarkOffset,
  45675. nextTickPos = (isNumber(tickPositions[e.index + 1]) ?
  45676. tickPositions[e.index + 1] - tickmarkOffset :
  45677. axis.max + tickmarkOffset),
  45678. tickSize = axis.tickSize('tick'),
  45679. tickWidth = tickSize ? tickSize[0] : 0,
  45680. crispCorr = tickSize ? tickSize[1] / 2 : 0,
  45681. labelHeight,
  45682. lblMetrics,
  45683. lines,
  45684. bottom,
  45685. top,
  45686. left,
  45687. right;
  45688. // Only center tick labels in grid axes
  45689. if (gridOptions.enabled === true) {
  45690. // Calculate top and bottom positions of the cell.
  45691. if (side === 'top') {
  45692. bottom = axis.top + axis.offset;
  45693. top = bottom - tickWidth;
  45694. }
  45695. else if (side === 'bottom') {
  45696. top = chart.chartHeight - axis.bottom + axis.offset;
  45697. bottom = top + tickWidth;
  45698. }
  45699. else {
  45700. bottom = axis.top + axis.len - axis.translate(reversed ? nextTickPos : tickPos);
  45701. top = axis.top + axis.len - axis.translate(reversed ? tickPos : nextTickPos);
  45702. }
  45703. // Calculate left and right positions of the cell.
  45704. if (side === 'right') {
  45705. left = chart.chartWidth - axis.right + axis.offset;
  45706. right = left + tickWidth;
  45707. }
  45708. else if (side === 'left') {
  45709. right = axis.left + axis.offset;
  45710. left = right - tickWidth;
  45711. }
  45712. else {
  45713. left = Math.round(axis.left + axis.translate(reversed ? nextTickPos : tickPos)) - crispCorr;
  45714. right = Math.round(axis.left + axis.translate(reversed ? tickPos : nextTickPos)) - crispCorr;
  45715. }
  45716. tick.slotWidth = right - left;
  45717. // Calculate the positioning of the label based on
  45718. // alignment.
  45719. e.pos.x = (align === 'left' ?
  45720. left :
  45721. align === 'right' ?
  45722. right :
  45723. left + ((right - left) / 2) // default to center
  45724. );
  45725. e.pos.y = (verticalAlign === 'top' ?
  45726. top :
  45727. verticalAlign === 'bottom' ?
  45728. bottom :
  45729. top + ((bottom - top) / 2) // default to middle
  45730. );
  45731. lblMetrics = chart.renderer.fontMetrics(labelOpts.style.fontSize, label.element);
  45732. labelHeight = label.getBBox().height;
  45733. // Adjustment to y position to align the label correctly.
  45734. // Would be better to have a setter or similar for this.
  45735. if (!labelOpts.useHTML) {
  45736. lines = Math.round(labelHeight / lblMetrics.h);
  45737. e.pos.y += (
  45738. // Center the label
  45739. // TODO: why does this actually center the label?
  45740. ((lblMetrics.b - (lblMetrics.h - lblMetrics.f)) / 2) +
  45741. // Adjust for height of additional lines.
  45742. -(((lines - 1) * lblMetrics.h) / 2));
  45743. }
  45744. else {
  45745. e.pos.y += (
  45746. // Readjust yCorr in htmlUpdateTransform
  45747. lblMetrics.b +
  45748. // Adjust for height of html label
  45749. -(labelHeight / 2));
  45750. }
  45751. e.pos.x += (axis.horiz && labelOpts.x || 0);
  45752. }
  45753. });
  45754. /* eslint-enable no-invalid-this */
  45755. /**
  45756. * Additions for grid axes.
  45757. * @private
  45758. * @class
  45759. */
  45760. var GridAxisAdditions = /** @class */ (function () {
  45761. /* *
  45762. *
  45763. * Constructors
  45764. *
  45765. * */
  45766. function GridAxisAdditions(axis) {
  45767. this.axis = axis;
  45768. }
  45769. /* *
  45770. *
  45771. * Functions
  45772. *
  45773. * */
  45774. /**
  45775. * Checks if an axis is the outer axis in its dimension. Since
  45776. * axes are placed outwards in order, the axis with the highest
  45777. * index is the outermost axis.
  45778. *
  45779. * Example: If there are multiple x-axes at the top of the chart,
  45780. * this function returns true if the axis supplied is the last
  45781. * of the x-axes.
  45782. *
  45783. * @private
  45784. *
  45785. * @return {boolean}
  45786. * True if the axis is the outermost axis in its dimension; false if
  45787. * not.
  45788. */
  45789. GridAxisAdditions.prototype.isOuterAxis = function () {
  45790. var axis = this.axis;
  45791. var chart = axis.chart;
  45792. var columnIndex = axis.grid.columnIndex;
  45793. var columns = (axis.linkedParent && axis.linkedParent.grid.columns ||
  45794. axis.grid.columns);
  45795. var parentAxis = columnIndex ? axis.linkedParent : axis;
  45796. var thisIndex = -1,
  45797. lastIndex = 0;
  45798. chart[axis.coll].forEach(function (otherAxis, index) {
  45799. if (otherAxis.side === axis.side && !otherAxis.options.isInternal) {
  45800. lastIndex = index;
  45801. if (otherAxis === parentAxis) {
  45802. // Get the index of the axis in question
  45803. thisIndex = index;
  45804. }
  45805. }
  45806. });
  45807. return (lastIndex === thisIndex &&
  45808. (isNumber(columnIndex) ? columns.length === columnIndex : true));
  45809. };
  45810. return GridAxisAdditions;
  45811. }());
  45812. /**
  45813. * Axis with grid support.
  45814. * @private
  45815. * @class
  45816. */
  45817. var GridAxis = /** @class */ (function () {
  45818. function GridAxis() {
  45819. }
  45820. /* *
  45821. *
  45822. * Static Functions
  45823. *
  45824. * */
  45825. /* eslint-disable valid-jsdoc */
  45826. /**
  45827. * Extends axis class with grid support.
  45828. * @private
  45829. */
  45830. GridAxis.compose = function (AxisClass) {
  45831. Axis.keepProps.push('grid');
  45832. wrap(AxisClass.prototype, 'unsquish', GridAxis.wrapUnsquish);
  45833. // Add event handlers
  45834. addEvent(AxisClass, 'init', GridAxis.onInit);
  45835. addEvent(AxisClass, 'afterGetOffset', GridAxis.onAfterGetOffset);
  45836. addEvent(AxisClass, 'afterGetTitlePosition', GridAxis.onAfterGetTitlePosition);
  45837. addEvent(AxisClass, 'afterInit', GridAxis.onAfterInit);
  45838. addEvent(AxisClass, 'afterRender', GridAxis.onAfterRender);
  45839. addEvent(AxisClass, 'afterSetAxisTranslation', GridAxis.onAfterSetAxisTranslation);
  45840. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions);
  45841. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions2);
  45842. addEvent(AxisClass, 'afterSetScale', GridAxis.onAfterSetScale);
  45843. addEvent(AxisClass, 'afterTickSize', GridAxis.onAfterTickSize);
  45844. addEvent(AxisClass, 'trimTicks', GridAxis.onTrimTicks);
  45845. addEvent(AxisClass, 'destroy', GridAxis.onDestroy);
  45846. };
  45847. /**
  45848. * Handle columns and getOffset.
  45849. * @private
  45850. */
  45851. GridAxis.onAfterGetOffset = function () {
  45852. var grid = this.grid;
  45853. (grid && grid.columns || []).forEach(function (column) {
  45854. column.getOffset();
  45855. });
  45856. };
  45857. /**
  45858. * @private
  45859. */
  45860. GridAxis.onAfterGetTitlePosition = function (e) {
  45861. var axis = this;
  45862. var options = axis.options;
  45863. var gridOptions = options.grid || {};
  45864. if (gridOptions.enabled === true) {
  45865. // compute anchor points for each of the title align options
  45866. var title = axis.axisTitle,
  45867. axisHeight = axis.height,
  45868. horiz = axis.horiz,
  45869. axisLeft = axis.left,
  45870. offset = axis.offset,
  45871. opposite = axis.opposite,
  45872. _a = axis.options.title,
  45873. axisTitleOptions = _a === void 0 ? {} : _a,
  45874. axisTop = axis.top,
  45875. axisWidth = axis.width;
  45876. var tickSize = axis.tickSize();
  45877. var titleWidth = title && title.getBBox().width;
  45878. var xOption = axisTitleOptions.x || 0;
  45879. var yOption = axisTitleOptions.y || 0;
  45880. var titleMargin = pick(axisTitleOptions.margin,
  45881. horiz ? 5 : 10);
  45882. var titleFontSize = axis.chart.renderer.fontMetrics(axisTitleOptions.style &&
  45883. axisTitleOptions.style.fontSize,
  45884. title).f;
  45885. var crispCorr = tickSize ? tickSize[0] / 2 : 0;
  45886. // TODO account for alignment
  45887. // the position in the perpendicular direction of the axis
  45888. var offAxis = ((horiz ? axisTop + axisHeight : axisLeft) +
  45889. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  45890. (opposite ? -1 : 1) * // so does opposite axes
  45891. crispCorr +
  45892. (axis.side === GridAxis.Side.bottom ? titleFontSize : 0));
  45893. e.titlePosition.x = horiz ?
  45894. axisLeft - titleWidth / 2 - titleMargin + xOption :
  45895. offAxis + (opposite ? axisWidth : 0) + offset + xOption;
  45896. e.titlePosition.y = horiz ?
  45897. (offAxis -
  45898. (opposite ? axisHeight : 0) +
  45899. (opposite ? titleFontSize : -titleFontSize) / 2 +
  45900. offset +
  45901. yOption) :
  45902. axisTop - titleMargin + yOption;
  45903. }
  45904. };
  45905. /**
  45906. * @private
  45907. */
  45908. GridAxis.onAfterInit = function () {
  45909. var axis = this;
  45910. var chart = axis.chart,
  45911. _a = axis.options.grid,
  45912. gridOptions = _a === void 0 ? {} : _a,
  45913. userOptions = axis.userOptions;
  45914. if (gridOptions.enabled) {
  45915. applyGridOptions(axis);
  45916. /* eslint-disable no-invalid-this */
  45917. // TODO: wrap the axis instead
  45918. wrap(axis, 'labelFormatter', function (proceed) {
  45919. var _a = this,
  45920. axis = _a.axis,
  45921. value = _a.value;
  45922. var tickPos = axis.tickPositions;
  45923. var series = (axis.isLinked ?
  45924. axis.linkedParent :
  45925. axis).series[0];
  45926. var isFirst = value === tickPos[0];
  45927. var isLast = value === tickPos[tickPos.length - 1];
  45928. var point = series && find(series.options.data,
  45929. function (p) {
  45930. return p[axis.isXAxis ? 'x' : 'y'] === value;
  45931. });
  45932. // Make additional properties available for the
  45933. // formatter
  45934. this.isFirst = isFirst;
  45935. this.isLast = isLast;
  45936. this.point = point;
  45937. // Call original labelFormatter
  45938. return proceed.call(this);
  45939. });
  45940. /* eslint-enable no-invalid-this */
  45941. }
  45942. if (gridOptions.columns) {
  45943. var columns = axis.grid.columns = [],
  45944. columnIndex = axis.grid.columnIndex = 0;
  45945. // Handle columns, each column is a grid axis
  45946. while (++columnIndex < gridOptions.columns.length) {
  45947. var columnOptions = merge(userOptions,
  45948. gridOptions.columns[gridOptions.columns.length - columnIndex - 1], {
  45949. linkedTo: 0,
  45950. // Force to behave like category axis
  45951. type: 'category',
  45952. // Disable by default the scrollbar on the grid axis
  45953. scrollbar: {
  45954. enabled: false
  45955. }
  45956. });
  45957. delete columnOptions.grid.columns; // Prevent recursion
  45958. var column = new Axis(axis.chart,
  45959. columnOptions);
  45960. column.grid.isColumn = true;
  45961. column.grid.columnIndex = columnIndex;
  45962. // Remove column axis from chart axes array, and place it
  45963. // in the columns array.
  45964. erase(chart.axes, column);
  45965. erase(chart[axis.coll], column);
  45966. columns.push(column);
  45967. }
  45968. }
  45969. };
  45970. /**
  45971. * Draw an extra line on the far side of the outermost axis,
  45972. * creating floor/roof/wall of a grid. And some padding.
  45973. * ```
  45974. * Make this:
  45975. * (axis.min) __________________________ (axis.max)
  45976. * | | | | |
  45977. * Into this:
  45978. * (axis.min) __________________________ (axis.max)
  45979. * ___|____|____|____|____|__
  45980. * ```
  45981. * @private
  45982. */
  45983. GridAxis.onAfterRender = function () {
  45984. var axis = this;
  45985. var grid = axis.grid;
  45986. var options = axis.options;
  45987. var renderer = axis.chart.renderer;
  45988. var gridOptions = options.grid || {};
  45989. var yStartIndex,
  45990. yEndIndex,
  45991. xStartIndex,
  45992. xEndIndex;
  45993. if (gridOptions.enabled === true) {
  45994. // @todo acutual label padding (top, bottom, left, right)
  45995. axis.maxLabelDimensions = axis.getMaxLabelDimensions(axis.ticks, axis.tickPositions);
  45996. // Remove right wall before rendering if updating
  45997. if (axis.rightWall) {
  45998. axis.rightWall.destroy();
  45999. }
  46000. /*
  46001. Draw an extra axis line on outer axes
  46002. >
  46003. Make this: |______|______|______|___
  46004. > _________________________
  46005. Into this: |______|______|______|__|
  46006. */
  46007. if (axis.grid && axis.grid.isOuterAxis() && axis.axisLine) {
  46008. var lineWidth = options.lineWidth;
  46009. if (lineWidth) {
  46010. var linePath = axis.getLinePath(lineWidth);
  46011. var startPoint = linePath[0];
  46012. var endPoint = linePath[1];
  46013. // Negate distance if top or left axis
  46014. // Subtract 1px to draw the line at the end of the tick
  46015. var tickLength = (axis.tickSize('tick') || [1])[0];
  46016. var distance = (tickLength - 1) * ((axis.side === GridAxis.Side.top ||
  46017. axis.side === GridAxis.Side.left) ? -1 : 1);
  46018. // If axis is horizontal, reposition line path vertically
  46019. if (startPoint[0] === 'M' && endPoint[0] === 'L') {
  46020. if (axis.horiz) {
  46021. startPoint[2] += distance;
  46022. endPoint[2] += distance;
  46023. }
  46024. else {
  46025. // If axis is vertical, reposition line path
  46026. // horizontally
  46027. startPoint[1] += distance;
  46028. endPoint[1] += distance;
  46029. }
  46030. }
  46031. if (!axis.grid.axisLineExtra) {
  46032. axis.grid.axisLineExtra = renderer
  46033. .path(linePath)
  46034. .attr({
  46035. zIndex: 7
  46036. })
  46037. .addClass('highcharts-axis-line')
  46038. .add(axis.axisGroup);
  46039. if (!renderer.styledMode) {
  46040. axis.grid.axisLineExtra.attr({
  46041. stroke: options.lineColor,
  46042. 'stroke-width': lineWidth
  46043. });
  46044. }
  46045. }
  46046. else {
  46047. axis.grid.axisLineExtra.animate({
  46048. d: linePath
  46049. });
  46050. }
  46051. // show or hide the line depending on
  46052. // options.showEmpty
  46053. axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
  46054. }
  46055. }
  46056. (grid && grid.columns || []).forEach(function (column) {
  46057. column.render();
  46058. });
  46059. }
  46060. };
  46061. /**
  46062. * @private
  46063. */
  46064. GridAxis.onAfterSetAxisTranslation = function () {
  46065. var axis = this;
  46066. var tickInfo = axis.tickPositions && axis.tickPositions.info;
  46067. var options = axis.options;
  46068. var gridOptions = options.grid || {};
  46069. var userLabels = axis.userOptions.labels || {};
  46070. if (axis.horiz) {
  46071. if (gridOptions.enabled === true) {
  46072. axis.series.forEach(function (series) {
  46073. series.options.pointRange = 0;
  46074. });
  46075. }
  46076. // Lower level time ticks, like hours or minutes, represent
  46077. // points in time and not ranges. These should be aligned
  46078. // left in the grid cell by default. The same applies to
  46079. // years of higher order.
  46080. if (tickInfo &&
  46081. options.dateTimeLabelFormats &&
  46082. options.labels &&
  46083. !defined(userLabels.align) &&
  46084. (options.dateTimeLabelFormats[tickInfo.unitName].range === false ||
  46085. tickInfo.count > 1 // years
  46086. )) {
  46087. options.labels.align = 'left';
  46088. if (!defined(userLabels.x)) {
  46089. options.labels.x = 3;
  46090. }
  46091. }
  46092. }
  46093. };
  46094. /**
  46095. * Creates a left and right wall on horizontal axes:
  46096. * - Places leftmost tick at the start of the axis, to create a left
  46097. * wall
  46098. * - Ensures that the rightmost tick is at the end of the axis, to
  46099. * create a right wall.
  46100. * @private
  46101. */
  46102. GridAxis.onAfterSetOptions = function (e) {
  46103. var options = this.options,
  46104. userOptions = e.userOptions,
  46105. gridAxisOptions,
  46106. gridOptions = ((options && isObject(options.grid)) ? options.grid : {});
  46107. if (gridOptions.enabled === true) {
  46108. // Merge the user options into default grid axis options so
  46109. // that when a user option is set, it takes presedence.
  46110. gridAxisOptions = merge(true, {
  46111. className: ('highcharts-grid-axis ' + (userOptions.className || '')),
  46112. dateTimeLabelFormats: {
  46113. hour: {
  46114. list: ['%H:%M', '%H']
  46115. },
  46116. day: {
  46117. list: ['%A, %e. %B', '%a, %e. %b', '%E']
  46118. },
  46119. week: {
  46120. list: ['Week %W', 'W%W']
  46121. },
  46122. month: {
  46123. list: ['%B', '%b', '%o']
  46124. }
  46125. },
  46126. grid: {
  46127. borderWidth: 1
  46128. },
  46129. labels: {
  46130. padding: 2,
  46131. style: {
  46132. fontSize: '13px'
  46133. }
  46134. },
  46135. margin: 0,
  46136. title: {
  46137. text: null,
  46138. reserveSpace: false,
  46139. rotation: 0
  46140. },
  46141. // In a grid axis, only allow one unit of certain types,
  46142. // for example we shouln't have one grid cell spanning
  46143. // two days.
  46144. units: [[
  46145. 'millisecond',
  46146. [1, 10, 100]
  46147. ], [
  46148. 'second',
  46149. [1, 10]
  46150. ], [
  46151. 'minute',
  46152. [1, 5, 15]
  46153. ], [
  46154. 'hour',
  46155. [1, 6]
  46156. ], [
  46157. 'day',
  46158. [1]
  46159. ], [
  46160. 'week',
  46161. [1]
  46162. ], [
  46163. 'month',
  46164. [1]
  46165. ], [
  46166. 'year',
  46167. null
  46168. ]]
  46169. }, userOptions);
  46170. // X-axis specific options
  46171. if (this.coll === 'xAxis') {
  46172. // For linked axes, tickPixelInterval is used only if
  46173. // the tickPositioner below doesn't run or returns
  46174. // undefined (like multiple years)
  46175. if (defined(userOptions.linkedTo) &&
  46176. !defined(userOptions.tickPixelInterval)) {
  46177. gridAxisOptions.tickPixelInterval = 350;
  46178. }
  46179. // For the secondary grid axis, use the primary axis'
  46180. // tick intervals and return ticks one level higher.
  46181. if (
  46182. // Check for tick pixel interval in options
  46183. !defined(userOptions.tickPixelInterval) &&
  46184. // Only for linked axes
  46185. defined(userOptions.linkedTo) &&
  46186. !defined(userOptions.tickPositioner) &&
  46187. !defined(userOptions.tickInterval)) {
  46188. gridAxisOptions.tickPositioner = function (min, max) {
  46189. var parentInfo = (this.linkedParent &&
  46190. this.linkedParent.tickPositions &&
  46191. this.linkedParent.tickPositions.info);
  46192. if (parentInfo) {
  46193. var unitIdx,
  46194. count,
  46195. unitName,
  46196. i,
  46197. units = gridAxisOptions.units,
  46198. unitRange;
  46199. for (i = 0; i < units.length; i++) {
  46200. if (units[i][0] ===
  46201. parentInfo.unitName) {
  46202. unitIdx = i;
  46203. break;
  46204. }
  46205. }
  46206. // Get the first allowed count on the next
  46207. // unit.
  46208. if (units[unitIdx + 1]) {
  46209. unitName = units[unitIdx + 1][0];
  46210. count =
  46211. (units[unitIdx + 1][1] || [1])[0];
  46212. // In case the base X axis shows years, make
  46213. // the secondary axis show ten times the
  46214. // years (#11427)
  46215. }
  46216. else if (parentInfo.unitName === 'year') {
  46217. unitName = 'year';
  46218. count = parentInfo.count * 10;
  46219. }
  46220. unitRange = timeUnits[unitName];
  46221. this.tickInterval = unitRange * count;
  46222. return this.getTimeTicks({
  46223. unitRange: unitRange,
  46224. count: count,
  46225. unitName: unitName
  46226. }, min, max, this.options.startOfWeek);
  46227. }
  46228. };
  46229. }
  46230. }
  46231. // Now merge the combined options into the axis options
  46232. merge(true, this.options, gridAxisOptions);
  46233. if (this.horiz) {
  46234. /* _________________________
  46235. Make this: ___|_____|_____|_____|__|
  46236. ^ ^
  46237. _________________________
  46238. Into this: |_____|_____|_____|_____|
  46239. ^ ^ */
  46240. options.minPadding = pick(userOptions.minPadding, 0);
  46241. options.maxPadding = pick(userOptions.maxPadding, 0);
  46242. }
  46243. // If borderWidth is set, then use its value for tick and
  46244. // line width.
  46245. if (isNumber(options.grid.borderWidth)) {
  46246. options.tickWidth = options.lineWidth = gridOptions.borderWidth;
  46247. }
  46248. }
  46249. };
  46250. /**
  46251. * @private
  46252. */
  46253. GridAxis.onAfterSetOptions2 = function (e) {
  46254. var axis = this;
  46255. var userOptions = e.userOptions;
  46256. var gridOptions = userOptions && userOptions.grid || {};
  46257. var columns = gridOptions.columns;
  46258. // Add column options to the parent axis. Children has their column
  46259. // options set on init in onGridAxisAfterInit.
  46260. if (gridOptions.enabled && columns) {
  46261. merge(true, axis.options, columns[columns.length - 1]);
  46262. }
  46263. };
  46264. /**
  46265. * Handle columns and setScale.
  46266. * @private
  46267. */
  46268. GridAxis.onAfterSetScale = function () {
  46269. var axis = this;
  46270. (axis.grid.columns || []).forEach(function (column) {
  46271. column.setScale();
  46272. });
  46273. };
  46274. /**
  46275. * Draw vertical axis ticks extra long to create cell floors and roofs.
  46276. * Overrides the tickLength for vertical axes.
  46277. * @private
  46278. */
  46279. GridAxis.onAfterTickSize = function (e) {
  46280. var defaultLeftAxisOptions = Axis.defaultLeftAxisOptions;
  46281. var _a = this,
  46282. horiz = _a.horiz,
  46283. maxLabelDimensions = _a.maxLabelDimensions,
  46284. _b = _a.options.grid,
  46285. gridOptions = _b === void 0 ? {} : _b;
  46286. if (gridOptions.enabled && maxLabelDimensions) {
  46287. var labelPadding = (Math.abs(defaultLeftAxisOptions.labels.x) * 2);
  46288. var distance = horiz ?
  46289. gridOptions.cellHeight || labelPadding + maxLabelDimensions.height :
  46290. labelPadding + maxLabelDimensions.width;
  46291. if (isArray(e.tickSize)) {
  46292. e.tickSize[0] = distance;
  46293. }
  46294. else {
  46295. e.tickSize = [distance, 0];
  46296. }
  46297. }
  46298. };
  46299. /**
  46300. * @private
  46301. */
  46302. GridAxis.onDestroy = function (e) {
  46303. var grid = this.grid;
  46304. (grid.columns || []).forEach(function (column) {
  46305. column.destroy(e.keepEvents);
  46306. });
  46307. grid.columns = void 0;
  46308. };
  46309. /**
  46310. * Wraps axis init to draw cell walls on vertical axes.
  46311. * @private
  46312. */
  46313. GridAxis.onInit = function (e) {
  46314. var axis = this;
  46315. var userOptions = e.userOptions || {};
  46316. var gridOptions = userOptions.grid || {};
  46317. if (gridOptions.enabled && defined(gridOptions.borderColor)) {
  46318. userOptions.tickColor = userOptions.lineColor = gridOptions.borderColor;
  46319. }
  46320. if (!axis.grid) {
  46321. axis.grid = new GridAxisAdditions(axis);
  46322. }
  46323. };
  46324. /**
  46325. * Makes tick labels which are usually ignored in a linked axis
  46326. * displayed if they are within range of linkedParent.min.
  46327. * ```
  46328. * _____________________________
  46329. * | | | | |
  46330. * Make this: | | 2 | 3 | 4 |
  46331. * |___|_______|_______|_______|
  46332. * ^
  46333. * _____________________________
  46334. * | | | | |
  46335. * Into this: | 1 | 2 | 3 | 4 |
  46336. * |___|_______|_______|_______|
  46337. * ^
  46338. * ```
  46339. * @private
  46340. * @todo Does this function do what the drawing says? Seems to affect
  46341. * ticks and not the labels directly?
  46342. */
  46343. GridAxis.onTrimTicks = function () {
  46344. var axis = this;
  46345. var options = axis.options;
  46346. var gridOptions = options.grid || {};
  46347. var categoryAxis = axis.categories;
  46348. var tickPositions = axis.tickPositions;
  46349. var firstPos = tickPositions[0];
  46350. var lastPos = tickPositions[tickPositions.length - 1];
  46351. var linkedMin = axis.linkedParent && axis.linkedParent.min;
  46352. var linkedMax = axis.linkedParent && axis.linkedParent.max;
  46353. var min = linkedMin || axis.min;
  46354. var max = linkedMax || axis.max;
  46355. var tickInterval = axis.tickInterval;
  46356. var endMoreThanMin = (firstPos < min &&
  46357. firstPos + tickInterval > min);
  46358. var startLessThanMax = (lastPos > max &&
  46359. lastPos - tickInterval < max);
  46360. if (gridOptions.enabled === true &&
  46361. !categoryAxis &&
  46362. (axis.horiz || axis.isLinked)) {
  46363. if (endMoreThanMin && !options.startOnTick) {
  46364. tickPositions[0] = min;
  46365. }
  46366. if (startLessThanMax && !options.endOnTick) {
  46367. tickPositions[tickPositions.length - 1] = max;
  46368. }
  46369. }
  46370. };
  46371. /**
  46372. * Avoid altering tickInterval when reserving space.
  46373. * @private
  46374. */
  46375. GridAxis.wrapUnsquish = function (proceed) {
  46376. var axis = this;
  46377. var _a = axis.options.grid,
  46378. gridOptions = _a === void 0 ? {} : _a;
  46379. if (gridOptions.enabled === true && axis.categories) {
  46380. return axis.tickInterval;
  46381. }
  46382. return proceed.apply(axis, argsToArray(arguments));
  46383. };
  46384. return GridAxis;
  46385. }());
  46386. (function (GridAxis) {
  46387. /**
  46388. * Enum for which side the axis is on. Maps to axis.side.
  46389. * @private
  46390. */
  46391. var Side;
  46392. (function (Side) {
  46393. Side[Side["top"] = 0] = "top";
  46394. Side[Side["right"] = 1] = "right";
  46395. Side[Side["bottom"] = 2] = "bottom";
  46396. Side[Side["left"] = 3] = "left";
  46397. })(Side = GridAxis.Side || (GridAxis.Side = {}));
  46398. })(GridAxis || (GridAxis = {}));
  46399. GridAxis.compose(Axis);
  46400. return GridAxis;
  46401. });
  46402. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Extensions/Stacking.js']], function (Axis, H, U, StackItem) {
  46403. /* *
  46404. *
  46405. * (c) 2009-2020 Torstein Honsi
  46406. *
  46407. * License: www.highcharts.com/license
  46408. *
  46409. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46410. *
  46411. * */
  46412. var addEvent = U.addEvent,
  46413. find = U.find,
  46414. fireEvent = U.fireEvent,
  46415. isArray = U.isArray,
  46416. isNumber = U.isNumber,
  46417. pick = U.pick;
  46418. var Series = H.Series;
  46419. /* eslint-disable valid-jsdoc */
  46420. /**
  46421. * Provides support for broken axes.
  46422. * @private
  46423. * @class
  46424. */
  46425. var BrokenAxisAdditions = /** @class */ (function () {
  46426. /* *
  46427. *
  46428. * Constructors
  46429. *
  46430. * */
  46431. function BrokenAxisAdditions(axis) {
  46432. this.hasBreaks = false;
  46433. this.axis = axis;
  46434. }
  46435. /* *
  46436. *
  46437. * Static Functions
  46438. *
  46439. * */
  46440. /**
  46441. * @private
  46442. */
  46443. BrokenAxisAdditions.isInBreak = function (brk, val) {
  46444. var ret,
  46445. repeat = brk.repeat || Infinity,
  46446. from = brk.from,
  46447. length = brk.to - brk.from,
  46448. test = (val >= from ?
  46449. (val - from) % repeat :
  46450. repeat - ((from - val) % repeat));
  46451. if (!brk.inclusive) {
  46452. ret = test < length && test !== 0;
  46453. }
  46454. else {
  46455. ret = test <= length;
  46456. }
  46457. return ret;
  46458. };
  46459. /**
  46460. * @private
  46461. */
  46462. BrokenAxisAdditions.lin2Val = function (val) {
  46463. var axis = this;
  46464. var brokenAxis = axis.brokenAxis;
  46465. var breakArray = brokenAxis && brokenAxis.breakArray;
  46466. if (!breakArray) {
  46467. return val;
  46468. }
  46469. var nval = val,
  46470. brk,
  46471. i;
  46472. for (i = 0; i < breakArray.length; i++) {
  46473. brk = breakArray[i];
  46474. if (brk.from >= nval) {
  46475. break;
  46476. }
  46477. else if (brk.to < nval) {
  46478. nval += brk.len;
  46479. }
  46480. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  46481. nval += brk.len;
  46482. }
  46483. }
  46484. return nval;
  46485. };
  46486. /**
  46487. * @private
  46488. */
  46489. BrokenAxisAdditions.val2Lin = function (val) {
  46490. var axis = this;
  46491. var brokenAxis = axis.brokenAxis;
  46492. var breakArray = brokenAxis && brokenAxis.breakArray;
  46493. if (!breakArray) {
  46494. return val;
  46495. }
  46496. var nval = val,
  46497. brk,
  46498. i;
  46499. for (i = 0; i < breakArray.length; i++) {
  46500. brk = breakArray[i];
  46501. if (brk.to <= val) {
  46502. nval -= brk.len;
  46503. }
  46504. else if (brk.from >= val) {
  46505. break;
  46506. }
  46507. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  46508. nval -= (val - brk.from);
  46509. break;
  46510. }
  46511. }
  46512. return nval;
  46513. };
  46514. /* *
  46515. *
  46516. * Functions
  46517. *
  46518. * */
  46519. /**
  46520. * Returns the first break found where the x is larger then break.from and
  46521. * smaller then break.to.
  46522. *
  46523. * @param {number} x
  46524. * The number which should be within a break.
  46525. *
  46526. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  46527. * The array of breaks to search within.
  46528. *
  46529. * @return {Highcharts.XAxisBreaksOptions|undefined}
  46530. * Returns the first break found that matches, returns false if no break is
  46531. * found.
  46532. */
  46533. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  46534. return find(breaks, function (b) {
  46535. return b.from < x && x < b.to;
  46536. });
  46537. };
  46538. /**
  46539. * @private
  46540. */
  46541. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  46542. var brokenAxis = this;
  46543. var axis = brokenAxis.axis;
  46544. var breaks = axis.options.breaks,
  46545. i = breaks && breaks.length,
  46546. inbrk,
  46547. keep,
  46548. ret;
  46549. if (i) {
  46550. while (i--) {
  46551. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  46552. inbrk = true;
  46553. if (!keep) {
  46554. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  46555. }
  46556. }
  46557. }
  46558. if (inbrk && testKeep) {
  46559. ret = inbrk && !keep;
  46560. }
  46561. else {
  46562. ret = inbrk;
  46563. }
  46564. }
  46565. return ret;
  46566. };
  46567. /**
  46568. * Dynamically set or unset breaks in an axis. This function in lighter than
  46569. * usin Axis.update, and it also preserves animation.
  46570. *
  46571. * @private
  46572. * @function Highcharts.Axis#setBreaks
  46573. *
  46574. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  46575. * The breaks to add. When `undefined` it removes existing breaks.
  46576. *
  46577. * @param {boolean} [redraw=true]
  46578. * Whether to redraw the chart immediately.
  46579. *
  46580. * @return {void}
  46581. */
  46582. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  46583. var brokenAxis = this;
  46584. var axis = brokenAxis.axis;
  46585. var hasBreaks = (isArray(breaks) && !!breaks.length);
  46586. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  46587. brokenAxis.hasBreaks = hasBreaks;
  46588. axis.options.breaks = axis.userOptions.breaks = breaks;
  46589. axis.forceRedraw = true; // Force recalculation in setScale
  46590. // Recalculate series related to the axis.
  46591. axis.series.forEach(function (series) {
  46592. series.isDirty = true;
  46593. });
  46594. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  46595. // Revert to prototype functions
  46596. delete axis.val2lin;
  46597. delete axis.lin2val;
  46598. }
  46599. if (hasBreaks) {
  46600. axis.userOptions.ordinal = false;
  46601. axis.lin2val = BrokenAxisAdditions.lin2Val;
  46602. axis.val2lin = BrokenAxisAdditions.val2Lin;
  46603. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  46604. // If trying to set extremes inside a break, extend min to
  46605. // after, and max to before the break ( #3857 )
  46606. if (brokenAxis.hasBreaks) {
  46607. var axisBreak,
  46608. breaks = this.options.breaks;
  46609. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  46610. newMin = axisBreak.to;
  46611. }
  46612. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  46613. newMax = axisBreak.from;
  46614. }
  46615. // If both min and max is within the same break.
  46616. if (newMax < newMin) {
  46617. newMax = newMin;
  46618. }
  46619. }
  46620. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  46621. };
  46622. axis.setAxisTranslation = function (saveOld) {
  46623. Axis.prototype.setAxisTranslation.call(this, saveOld);
  46624. brokenAxis.unitLength = null;
  46625. if (brokenAxis.hasBreaks) {
  46626. var breaks = axis.options.breaks || [],
  46627. // Temporary one:
  46628. breakArrayT = [],
  46629. breakArray = [],
  46630. length = 0,
  46631. inBrk,
  46632. repeat,
  46633. min = axis.userMin || axis.min,
  46634. max = axis.userMax || axis.max,
  46635. pointRangePadding = pick(axis.pointRangePadding, 0),
  46636. start,
  46637. i;
  46638. // Min & max check (#4247)
  46639. breaks.forEach(function (brk) {
  46640. repeat = brk.repeat || Infinity;
  46641. if (BrokenAxisAdditions.isInBreak(brk, min)) {
  46642. min +=
  46643. (brk.to % repeat) -
  46644. (min % repeat);
  46645. }
  46646. if (BrokenAxisAdditions.isInBreak(brk, max)) {
  46647. max -=
  46648. (max % repeat) -
  46649. (brk.from % repeat);
  46650. }
  46651. });
  46652. // Construct an array holding all breaks in the axis
  46653. breaks.forEach(function (brk) {
  46654. start = brk.from;
  46655. repeat = brk.repeat || Infinity;
  46656. while (start - repeat > min) {
  46657. start -= repeat;
  46658. }
  46659. while (start < min) {
  46660. start += repeat;
  46661. }
  46662. for (i = start; i < max; i += repeat) {
  46663. breakArrayT.push({
  46664. value: i,
  46665. move: 'in'
  46666. });
  46667. breakArrayT.push({
  46668. value: i + (brk.to - brk.from),
  46669. move: 'out',
  46670. size: brk.breakSize
  46671. });
  46672. }
  46673. });
  46674. breakArrayT.sort(function (a, b) {
  46675. return ((a.value === b.value) ?
  46676. ((a.move === 'in' ? 0 : 1) -
  46677. (b.move === 'in' ? 0 : 1)) :
  46678. a.value - b.value);
  46679. });
  46680. // Simplify the breaks
  46681. inBrk = 0;
  46682. start = min;
  46683. breakArrayT.forEach(function (brk) {
  46684. inBrk += (brk.move === 'in' ? 1 : -1);
  46685. if (inBrk === 1 && brk.move === 'in') {
  46686. start = brk.value;
  46687. }
  46688. if (inBrk === 0) {
  46689. breakArray.push({
  46690. from: start,
  46691. to: brk.value,
  46692. len: brk.value - start - (brk.size || 0)
  46693. });
  46694. length += brk.value - start - (brk.size || 0);
  46695. }
  46696. });
  46697. /**
  46698. * HC <= 8 backwards compatibility, used by demo samples.
  46699. * @deprecated
  46700. * @private
  46701. * @requires modules/broken-axis
  46702. */
  46703. axis.breakArray = brokenAxis.breakArray = breakArray;
  46704. // Used with staticScale, and below the actual axis length,
  46705. // when breaks are substracted.
  46706. brokenAxis.unitLength = max - min - length + pointRangePadding;
  46707. fireEvent(axis, 'afterBreaks');
  46708. if (axis.staticScale) {
  46709. axis.transA = axis.staticScale;
  46710. }
  46711. else if (brokenAxis.unitLength) {
  46712. axis.transA *=
  46713. (max - axis.min + pointRangePadding) /
  46714. brokenAxis.unitLength;
  46715. }
  46716. if (pointRangePadding) {
  46717. axis.minPixelPadding =
  46718. axis.transA * axis.minPointOffset;
  46719. }
  46720. axis.min = min;
  46721. axis.max = max;
  46722. }
  46723. };
  46724. }
  46725. if (pick(redraw, true)) {
  46726. axis.chart.redraw();
  46727. }
  46728. };
  46729. return BrokenAxisAdditions;
  46730. }());
  46731. /**
  46732. * Axis with support of broken data rows.
  46733. * @private
  46734. * @class
  46735. */
  46736. var BrokenAxis = /** @class */ (function () {
  46737. function BrokenAxis() {
  46738. }
  46739. /**
  46740. * Adds support for broken axes.
  46741. * @private
  46742. */
  46743. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  46744. AxisClass.keepProps.push('brokenAxis');
  46745. var seriesProto = Series.prototype;
  46746. /**
  46747. * @private
  46748. */
  46749. seriesProto.drawBreaks = function (axis, keys) {
  46750. var series = this,
  46751. points = series.points,
  46752. breaks,
  46753. threshold,
  46754. eventName,
  46755. y;
  46756. if (axis && // #5950
  46757. axis.brokenAxis &&
  46758. axis.brokenAxis.hasBreaks) {
  46759. var brokenAxis_1 = axis.brokenAxis;
  46760. keys.forEach(function (key) {
  46761. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  46762. threshold = axis.isXAxis ?
  46763. axis.min :
  46764. pick(series.options.threshold, axis.min);
  46765. points.forEach(function (point) {
  46766. y = pick(point['stack' + key.toUpperCase()], point[key]);
  46767. breaks.forEach(function (brk) {
  46768. if (isNumber(threshold) && isNumber(y)) {
  46769. eventName = false;
  46770. if ((threshold < brk.from && y > brk.to) ||
  46771. (threshold > brk.from && y < brk.from)) {
  46772. eventName = 'pointBreak';
  46773. }
  46774. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  46775. (threshold > brk.from && y > brk.to && y < brk.from)) {
  46776. eventName = 'pointInBreak';
  46777. }
  46778. if (eventName) {
  46779. fireEvent(axis, eventName, { point: point, brk: brk });
  46780. }
  46781. }
  46782. });
  46783. });
  46784. });
  46785. }
  46786. };
  46787. /**
  46788. * Extend getGraphPath by identifying gaps in the data so that we can
  46789. * draw a gap in the line or area. This was moved from ordinal axis
  46790. * module to broken axis module as of #5045.
  46791. *
  46792. * @private
  46793. * @function Highcharts.Series#gappedPath
  46794. *
  46795. * @return {Highcharts.SVGPathArray}
  46796. * Gapped path
  46797. */
  46798. seriesProto.gappedPath = function () {
  46799. var currentDataGrouping = this.currentDataGrouping,
  46800. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  46801. gapSize = this.options.gapSize,
  46802. points = this.points.slice(),
  46803. i = points.length - 1,
  46804. yAxis = this.yAxis,
  46805. stack;
  46806. /**
  46807. * Defines when to display a gap in the graph, together with the
  46808. * [gapUnit](plotOptions.series.gapUnit) option.
  46809. *
  46810. * In case when `dataGrouping` is enabled, points can be grouped
  46811. * into a larger time span. This can make the grouped points to have
  46812. * a greater distance than the absolute value of `gapSize` property,
  46813. * which will result in disappearing graph completely. To prevent
  46814. * this situation the mentioned distance between grouped points is
  46815. * used instead of previously defined `gapSize`.
  46816. *
  46817. * In practice, this option is most often used to visualize gaps in
  46818. * time series. In a stock chart, intraday data is available for
  46819. * daytime hours, while gaps will appear in nights and weekends.
  46820. *
  46821. * @see [gapUnit](plotOptions.series.gapUnit)
  46822. * @see [xAxis.breaks](#xAxis.breaks)
  46823. *
  46824. * @sample {highstock} stock/plotoptions/series-gapsize/
  46825. * Setting the gap size to 2 introduces gaps for weekends
  46826. * in daily datasets.
  46827. *
  46828. * @type {number}
  46829. * @default 0
  46830. * @product highstock
  46831. * @requires modules/broken-axis
  46832. * @apioption plotOptions.series.gapSize
  46833. */
  46834. /**
  46835. * Together with [gapSize](plotOptions.series.gapSize), this option
  46836. * defines where to draw gaps in the graph.
  46837. *
  46838. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  46839. * means that if the distance between two points is greater than
  46840. * 5 times that of the two closest points, the graph will be broken.
  46841. *
  46842. * When the `gapUnit` is `"value"`, the gap is based on absolute
  46843. * axis values, which on a datetime axis is milliseconds. This also
  46844. * applies to the navigator series that inherits gap options from
  46845. * the base series.
  46846. *
  46847. * @see [gapSize](plotOptions.series.gapSize)
  46848. *
  46849. * @type {string}
  46850. * @default relative
  46851. * @since 5.0.13
  46852. * @product highstock
  46853. * @validvalue ["relative", "value"]
  46854. * @requires modules/broken-axis
  46855. * @apioption plotOptions.series.gapUnit
  46856. */
  46857. if (gapSize && i > 0) { // #5008
  46858. // Gap unit is relative
  46859. if (this.options.gapUnit !== 'value') {
  46860. gapSize *= this.basePointRange;
  46861. }
  46862. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  46863. if (groupingSize &&
  46864. groupingSize > gapSize &&
  46865. // Except when DG is forced (e.g. from other series)
  46866. // and has lower granularity than actual points (#11351)
  46867. groupingSize >= this.basePointRange) {
  46868. gapSize = groupingSize;
  46869. }
  46870. // extension for ordinal breaks
  46871. var current = void 0,
  46872. next = void 0;
  46873. while (i--) {
  46874. // Reassign next if it is not visible
  46875. if (!(next && next.visible !== false)) {
  46876. next = points[i + 1];
  46877. }
  46878. current = points[i];
  46879. // Skip iteration if one of the points is not visible
  46880. if (next.visible === false || current.visible === false) {
  46881. continue;
  46882. }
  46883. if (next.x - current.x > gapSize) {
  46884. var xRange = (current.x + next.x) / 2;
  46885. points.splice(// insert after this one
  46886. i + 1, 0, {
  46887. isNull: true,
  46888. x: xRange
  46889. });
  46890. // For stacked chart generate empty stack items, #6546
  46891. if (yAxis.stacking && this.options.stacking) {
  46892. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  46893. new StackItem(yAxis, yAxis.options
  46894. .stackLabels, false, xRange, this.stack);
  46895. stack.total = 0;
  46896. }
  46897. }
  46898. // Assign current to next for the upcoming iteration
  46899. next = current;
  46900. }
  46901. }
  46902. // Call base method
  46903. return this.getGraphPath(points);
  46904. };
  46905. /* eslint-disable no-invalid-this */
  46906. addEvent(AxisClass, 'init', function () {
  46907. var axis = this;
  46908. if (!axis.brokenAxis) {
  46909. axis.brokenAxis = new BrokenAxisAdditions(axis);
  46910. }
  46911. });
  46912. addEvent(AxisClass, 'afterInit', function () {
  46913. if (typeof this.brokenAxis !== 'undefined') {
  46914. this.brokenAxis.setBreaks(this.options.breaks, false);
  46915. }
  46916. });
  46917. addEvent(AxisClass, 'afterSetTickPositions', function () {
  46918. var axis = this;
  46919. var brokenAxis = axis.brokenAxis;
  46920. if (brokenAxis &&
  46921. brokenAxis.hasBreaks) {
  46922. var tickPositions = this.tickPositions,
  46923. info = this.tickPositions.info,
  46924. newPositions = [],
  46925. i;
  46926. for (i = 0; i < tickPositions.length; i++) {
  46927. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  46928. newPositions.push(tickPositions[i]);
  46929. }
  46930. }
  46931. this.tickPositions = newPositions;
  46932. this.tickPositions.info = info;
  46933. }
  46934. });
  46935. // Force Axis to be not-ordinal when breaks are defined
  46936. addEvent(AxisClass, 'afterSetOptions', function () {
  46937. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  46938. this.options.ordinal = false;
  46939. }
  46940. });
  46941. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  46942. var _a = this,
  46943. isDirty = _a.isDirty,
  46944. connectNulls = _a.options.connectNulls,
  46945. points = _a.points,
  46946. xAxis = _a.xAxis,
  46947. yAxis = _a.yAxis;
  46948. // Set, or reset visibility of the points. Axis.setBreaks marks the
  46949. // series as isDirty
  46950. if (isDirty) {
  46951. var i = points.length;
  46952. while (i--) {
  46953. var point = points[i];
  46954. // Respect nulls inside the break (#4275)
  46955. var nullGap = point.y === null && connectNulls === false;
  46956. var isPointInBreak = (!nullGap && ((xAxis &&
  46957. xAxis.brokenAxis &&
  46958. xAxis.brokenAxis.isInAnyBreak(point.x,
  46959. true)) || (yAxis &&
  46960. yAxis.brokenAxis &&
  46961. yAxis.brokenAxis.isInAnyBreak(point.y,
  46962. true))));
  46963. // Set point.visible if in any break.
  46964. // If not in break, reset visible to original value.
  46965. point.visible = isPointInBreak ?
  46966. false :
  46967. point.options.visible !== false;
  46968. }
  46969. }
  46970. });
  46971. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  46972. this.drawBreaks(this.xAxis, ['x']);
  46973. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  46974. });
  46975. };
  46976. return BrokenAxis;
  46977. }());
  46978. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  46979. return BrokenAxis;
  46980. });
  46981. _registerModule(_modules, 'Core/Axis/TreeGridAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Axis/Tick.js'], _modules['Gantt/Tree.js'], _modules['Core/Axis/TreeGridTick.js'], _modules['Mixins/TreeSeries.js'], _modules['Core/Utilities.js']], function (Axis, Tick, Tree, TreeGridTick, mixinTreeSeries, U) {
  46982. /* *
  46983. *
  46984. * (c) 2016 Highsoft AS
  46985. * Authors: Jon Arild Nygard
  46986. *
  46987. * License: www.highcharts.com/license
  46988. *
  46989. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46990. *
  46991. * */
  46992. var getLevelOptions = mixinTreeSeries.getLevelOptions;
  46993. var addEvent = U.addEvent,
  46994. find = U.find,
  46995. fireEvent = U.fireEvent,
  46996. isNumber = U.isNumber,
  46997. isObject = U.isObject,
  46998. isString = U.isString,
  46999. merge = U.merge,
  47000. pick = U.pick,
  47001. wrap = U.wrap;
  47002. /**
  47003. * @private
  47004. */
  47005. var TreeGridAxis;
  47006. (function (TreeGridAxis) {
  47007. /* *
  47008. *
  47009. * Interfaces
  47010. *
  47011. * */
  47012. /* *
  47013. *
  47014. * Variables
  47015. *
  47016. * */
  47017. var applied = false;
  47018. /* *
  47019. *
  47020. * Functions
  47021. *
  47022. * */
  47023. /**
  47024. * @private
  47025. */
  47026. function compose(AxisClass) {
  47027. if (!applied) {
  47028. wrap(AxisClass.prototype, 'generateTick', wrapGenerateTick);
  47029. wrap(AxisClass.prototype, 'getMaxLabelDimensions', wrapGetMaxLabelDimensions);
  47030. wrap(AxisClass.prototype, 'init', wrapInit);
  47031. wrap(AxisClass.prototype, 'setTickInterval', wrapSetTickInterval);
  47032. TreeGridTick.compose(Tick);
  47033. applied = true;
  47034. }
  47035. }
  47036. TreeGridAxis.compose = compose;
  47037. /**
  47038. * @private
  47039. */
  47040. function getBreakFromNode(node, max) {
  47041. var from = node.collapseStart || 0,
  47042. to = node.collapseEnd || 0;
  47043. // In broken-axis, the axis.max is minimized until it is not within a
  47044. // break. Therefore, if break.to is larger than axis.max, the axis.to
  47045. // should not add the 0.5 axis.tickMarkOffset, to avoid adding a break
  47046. // larger than axis.max.
  47047. // TODO consider simplifying broken-axis and this might solve itself
  47048. if (to >= max) {
  47049. from -= 0.5;
  47050. }
  47051. return {
  47052. from: from,
  47053. to: to,
  47054. showPoints: false
  47055. };
  47056. }
  47057. /**
  47058. * Creates a tree structure of the data, and the treegrid. Calculates
  47059. * categories, and y-values of points based on the tree.
  47060. *
  47061. * @private
  47062. * @function getTreeGridFromData
  47063. *
  47064. * @param {Array<Highcharts.GanttPointOptions>} data
  47065. * All the data points to display in the axis.
  47066. *
  47067. * @param {boolean} uniqueNames
  47068. * Wether or not the data node with the same name should share grid cell. If
  47069. * true they do share cell. False by default.
  47070. *
  47071. * @param {number} numberOfSeries
  47072. *
  47073. * @return {object}
  47074. * Returns an object containing categories, mapOfIdToNode,
  47075. * mapOfPosToGridNode, and tree.
  47076. *
  47077. * @todo There should be only one point per line.
  47078. * @todo It should be optional to have one category per point, or merge
  47079. * cells
  47080. * @todo Add unit-tests.
  47081. */
  47082. function getTreeGridFromData(data, uniqueNames, numberOfSeries) {
  47083. var categories = [],
  47084. collapsedNodes = [],
  47085. mapOfIdToNode = {},
  47086. mapOfPosToGridNode = {},
  47087. posIterator = -1,
  47088. uniqueNamesEnabled = typeof uniqueNames === 'boolean' ? uniqueNames : false,
  47089. tree;
  47090. // Build the tree from the series data.
  47091. var treeParams = {
  47092. // After the children has been created.
  47093. after: function (node) {
  47094. var gridNode = mapOfPosToGridNode[node.pos],
  47095. height = 0,
  47096. descendants = 0;
  47097. gridNode.children.forEach(function (child) {
  47098. descendants += (child.descendants || 0) + 1;
  47099. height = Math.max((child.height || 0) + 1, height);
  47100. });
  47101. gridNode.descendants = descendants;
  47102. gridNode.height = height;
  47103. if (gridNode.collapsed) {
  47104. collapsedNodes.push(gridNode);
  47105. }
  47106. },
  47107. // Before the children has been created.
  47108. before: function (node) {
  47109. var data = isObject(node.data,
  47110. true) ? node.data : {},
  47111. name = isString(data.name) ? data.name : '',
  47112. parentNode = mapOfIdToNode[node.parent],
  47113. parentGridNode = (isObject(parentNode,
  47114. true) ?
  47115. mapOfPosToGridNode[parentNode.pos] :
  47116. null),
  47117. hasSameName = function (x) {
  47118. return x.name === name;
  47119. }, gridNode, pos;
  47120. // If not unique names, look for sibling node with the same name
  47121. if (uniqueNamesEnabled &&
  47122. isObject(parentGridNode, true) &&
  47123. !!(gridNode = find(parentGridNode.children, hasSameName))) {
  47124. // If there is a gridNode with the same name, reuse position
  47125. pos = gridNode.pos;
  47126. // Add data node to list of nodes in the grid node.
  47127. gridNode.nodes.push(node);
  47128. }
  47129. else {
  47130. // If it is a new grid node, increment position.
  47131. pos = posIterator++;
  47132. }
  47133. // Add new grid node to map.
  47134. if (!mapOfPosToGridNode[pos]) {
  47135. mapOfPosToGridNode[pos] = gridNode = {
  47136. depth: parentGridNode ? parentGridNode.depth + 1 : 0,
  47137. name: name,
  47138. nodes: [node],
  47139. children: [],
  47140. pos: pos
  47141. };
  47142. // If not root, then add name to categories.
  47143. if (pos !== -1) {
  47144. categories.push(name);
  47145. }
  47146. // Add name to list of children.
  47147. if (isObject(parentGridNode, true)) {
  47148. parentGridNode.children.push(gridNode);
  47149. }
  47150. }
  47151. // Add data node to map
  47152. if (isString(node.id)) {
  47153. mapOfIdToNode[node.id] = node;
  47154. }
  47155. // If one of the points are collapsed, then start the grid node
  47156. // in collapsed state.
  47157. if (gridNode &&
  47158. data.collapsed === true) {
  47159. gridNode.collapsed = true;
  47160. }
  47161. // Assign pos to data node
  47162. node.pos = pos;
  47163. }
  47164. };
  47165. var updateYValuesAndTickPos = function (map,
  47166. numberOfSeries) {
  47167. var setValues = function (gridNode,
  47168. start,
  47169. result) {
  47170. var nodes = gridNode.nodes,
  47171. end = start + (start === -1 ? 0 : numberOfSeries - 1),
  47172. diff = (end - start) / 2,
  47173. padding = 0.5,
  47174. pos = start + diff;
  47175. nodes.forEach(function (node) {
  47176. var data = node.data;
  47177. if (isObject(data, true)) {
  47178. // Update point
  47179. data.y = start + (data.seriesIndex || 0);
  47180. // Remove the property once used
  47181. delete data.seriesIndex;
  47182. }
  47183. node.pos = pos;
  47184. });
  47185. result[pos] = gridNode;
  47186. gridNode.pos = pos;
  47187. gridNode.tickmarkOffset = diff + padding;
  47188. gridNode.collapseStart = end + padding;
  47189. gridNode.children.forEach(function (child) {
  47190. setValues(child, end + 1, result);
  47191. end = (child.collapseEnd || 0) - padding;
  47192. });
  47193. // Set collapseEnd to the end of the last child node.
  47194. gridNode.collapseEnd = end + padding;
  47195. return result;
  47196. };
  47197. return setValues(map['-1'], -1, {});
  47198. };
  47199. // Create tree from data
  47200. tree = Tree.getTree(data, treeParams);
  47201. // Update y values of data, and set calculate tick positions.
  47202. mapOfPosToGridNode = updateYValuesAndTickPos(mapOfPosToGridNode, numberOfSeries);
  47203. // Return the resulting data.
  47204. return {
  47205. categories: categories,
  47206. mapOfIdToNode: mapOfIdToNode,
  47207. mapOfPosToGridNode: mapOfPosToGridNode,
  47208. collapsedNodes: collapsedNodes,
  47209. tree: tree
  47210. };
  47211. }
  47212. /**
  47213. * Builds the tree of categories and calculates its positions.
  47214. * @private
  47215. * @param {object} e Event object
  47216. * @param {object} e.target The chart instance which the event was fired on.
  47217. * @param {object[]} e.target.axes The axes of the chart.
  47218. */
  47219. function onBeforeRender(e) {
  47220. var chart = e.target,
  47221. axes = chart.axes;
  47222. axes.filter(function (axis) {
  47223. return axis.options.type === 'treegrid';
  47224. }).forEach(function (axis) {
  47225. var options = axis.options || {},
  47226. labelOptions = options.labels,
  47227. uniqueNames = options.uniqueNames,
  47228. numberOfSeries = 0,
  47229. isDirty,
  47230. data,
  47231. treeGrid,
  47232. max = options.max;
  47233. // Check whether any of series is rendering for the first time,
  47234. // visibility has changed, or its data is dirty,
  47235. // and only then update. #10570, #10580
  47236. // Also check if mapOfPosToGridNode exists. #10887
  47237. isDirty = (!axis.treeGrid.mapOfPosToGridNode ||
  47238. axis.series.some(function (series) {
  47239. return !series.hasRendered ||
  47240. series.isDirtyData ||
  47241. series.isDirty;
  47242. }));
  47243. if (isDirty) {
  47244. // Concatenate data from all series assigned to this axis.
  47245. data = axis.series.reduce(function (arr, s) {
  47246. if (s.visible) {
  47247. // Push all data to array
  47248. (s.options.data || []).forEach(function (data) {
  47249. if (isObject(data, true)) {
  47250. // Set series index on data. Removed again
  47251. // after use.
  47252. data.seriesIndex = numberOfSeries;
  47253. arr.push(data);
  47254. }
  47255. });
  47256. // Increment series index
  47257. if (uniqueNames === true) {
  47258. numberOfSeries++;
  47259. }
  47260. }
  47261. return arr;
  47262. }, []);
  47263. // If max is higher than set data - add a
  47264. // dummy data to render categories #10779
  47265. if (max && data.length < max) {
  47266. for (var i = data.length; i <= max; i++) {
  47267. data.push({
  47268. // Use the zero-width character
  47269. // to avoid conflict with uniqueNames
  47270. name: i + '\u200B'
  47271. });
  47272. }
  47273. }
  47274. // setScale is fired after all the series is initialized,
  47275. // which is an ideal time to update the axis.categories.
  47276. treeGrid = getTreeGridFromData(data, uniqueNames || false, (uniqueNames === true) ? numberOfSeries : 1);
  47277. // Assign values to the axis.
  47278. axis.categories = treeGrid.categories;
  47279. axis.treeGrid.mapOfPosToGridNode = treeGrid.mapOfPosToGridNode;
  47280. axis.hasNames = true;
  47281. axis.treeGrid.tree = treeGrid.tree;
  47282. // Update yData now that we have calculated the y values
  47283. axis.series.forEach(function (series) {
  47284. var data = (series.options.data || []).map(function (d) {
  47285. return isObject(d,
  47286. true) ? merge(d) : d;
  47287. });
  47288. // Avoid destroying points when series is not visible
  47289. if (series.visible) {
  47290. series.setData(data, false);
  47291. }
  47292. });
  47293. // Calculate the label options for each level in the tree.
  47294. axis.treeGrid.mapOptionsToLevel =
  47295. getLevelOptions({
  47296. defaults: labelOptions,
  47297. from: 1,
  47298. levels: labelOptions && labelOptions.levels,
  47299. to: axis.treeGrid.tree && axis.treeGrid.tree.height
  47300. });
  47301. // Setting initial collapsed nodes
  47302. if (e.type === 'beforeRender') {
  47303. axis.treeGrid.collapsedNodes = treeGrid.collapsedNodes;
  47304. }
  47305. }
  47306. });
  47307. }
  47308. /**
  47309. * Generates a tick for initial positioning.
  47310. *
  47311. * @private
  47312. * @function Highcharts.GridAxis#generateTick
  47313. *
  47314. * @param {Function} proceed
  47315. * The original generateTick function.
  47316. *
  47317. * @param {number} pos
  47318. * The tick position in axis values.
  47319. */
  47320. function wrapGenerateTick(proceed, pos) {
  47321. var axis = this,
  47322. mapOptionsToLevel = axis.treeGrid.mapOptionsToLevel || {},
  47323. isTreeGrid = axis.options.type === 'treegrid',
  47324. ticks = axis.ticks;
  47325. var tick = ticks[pos],
  47326. levelOptions,
  47327. options,
  47328. gridNode;
  47329. if (isTreeGrid &&
  47330. axis.treeGrid.mapOfPosToGridNode) {
  47331. gridNode = axis.treeGrid.mapOfPosToGridNode[pos];
  47332. levelOptions = mapOptionsToLevel[gridNode.depth];
  47333. if (levelOptions) {
  47334. options = {
  47335. labels: levelOptions
  47336. };
  47337. }
  47338. if (!tick) {
  47339. ticks[pos] = tick =
  47340. new Tick(axis, pos, void 0, void 0, {
  47341. category: gridNode.name,
  47342. tickmarkOffset: gridNode.tickmarkOffset,
  47343. options: options
  47344. });
  47345. }
  47346. else {
  47347. // update labels depending on tick interval
  47348. tick.parameters.category = gridNode.name;
  47349. tick.options = options;
  47350. tick.addLabel();
  47351. }
  47352. }
  47353. else {
  47354. proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
  47355. }
  47356. }
  47357. /**
  47358. * Override to add indentation to axis.maxLabelDimensions.
  47359. *
  47360. * @private
  47361. * @function Highcharts.GridAxis#getMaxLabelDimensions
  47362. *
  47363. * @param {Function} proceed
  47364. * The original function
  47365. */
  47366. function wrapGetMaxLabelDimensions(proceed) {
  47367. var axis = this,
  47368. options = axis.options,
  47369. labelOptions = options && options.labels,
  47370. indentation = (labelOptions && isNumber(labelOptions.indentation) ?
  47371. labelOptions.indentation :
  47372. 0),
  47373. retVal = proceed.apply(axis,
  47374. Array.prototype.slice.call(arguments, 1)),
  47375. isTreeGrid = axis.options.type === 'treegrid';
  47376. var treeDepth;
  47377. if (isTreeGrid && axis.treeGrid.mapOfPosToGridNode) {
  47378. treeDepth = axis.treeGrid.mapOfPosToGridNode[-1].height || 0;
  47379. retVal.width += indentation * (treeDepth - 1);
  47380. }
  47381. return retVal;
  47382. }
  47383. /**
  47384. * @private
  47385. */
  47386. function wrapInit(proceed, chart, userOptions) {
  47387. var axis = this,
  47388. isTreeGrid = userOptions.type === 'treegrid';
  47389. if (!axis.treeGrid) {
  47390. axis.treeGrid = new Additions(axis);
  47391. }
  47392. // Set default and forced options for TreeGrid
  47393. if (isTreeGrid) {
  47394. // Add event for updating the categories of a treegrid.
  47395. // NOTE Preferably these events should be set on the axis.
  47396. addEvent(chart, 'beforeRender', onBeforeRender);
  47397. addEvent(chart, 'beforeRedraw', onBeforeRender);
  47398. // Add new collapsed nodes on addseries
  47399. addEvent(chart, 'addSeries', function (e) {
  47400. if (e.options.data) {
  47401. var treeGrid = getTreeGridFromData(e.options.data,
  47402. userOptions.uniqueNames || false, 1);
  47403. axis.treeGrid.collapsedNodes = (axis.treeGrid.collapsedNodes || []).concat(treeGrid.collapsedNodes);
  47404. }
  47405. });
  47406. // Collapse all nodes in axis.treegrid.collapsednodes
  47407. // where collapsed equals true.
  47408. addEvent(axis, 'foundExtremes', function () {
  47409. if (axis.treeGrid.collapsedNodes) {
  47410. axis.treeGrid.collapsedNodes.forEach(function (node) {
  47411. var breaks = axis.treeGrid.collapse(node);
  47412. if (axis.brokenAxis) {
  47413. axis.brokenAxis.setBreaks(breaks, false);
  47414. // remove the node from the axis collapsedNodes
  47415. if (axis.treeGrid.collapsedNodes) {
  47416. axis.treeGrid.collapsedNodes = axis.treeGrid.collapsedNodes.filter(function (n) {
  47417. return node.collapseStart !== n.collapseStart ||
  47418. node.collapseEnd !== n.collapseEnd;
  47419. });
  47420. }
  47421. }
  47422. });
  47423. }
  47424. });
  47425. // If staticScale is not defined on the yAxis
  47426. // and chart height is set, set axis.isDirty
  47427. // to ensure collapsing works (#12012)
  47428. addEvent(axis, 'afterBreaks', function () {
  47429. var _a;
  47430. if (axis.coll === 'yAxis' && !axis.staticScale && ((_a = axis.chart.options.chart) === null || _a === void 0 ? void 0 : _a.height)) {
  47431. axis.isDirty = true;
  47432. }
  47433. });
  47434. userOptions = merge({
  47435. // Default options
  47436. grid: {
  47437. enabled: true
  47438. },
  47439. // TODO: add support for align in treegrid.
  47440. labels: {
  47441. align: 'left',
  47442. /**
  47443. * Set options on specific levels in a tree grid axis. Takes
  47444. * precedence over labels options.
  47445. *
  47446. * @sample {gantt} gantt/treegrid-axis/labels-levels
  47447. * Levels on TreeGrid Labels
  47448. *
  47449. * @type {Array<*>}
  47450. * @product gantt
  47451. * @apioption yAxis.labels.levels
  47452. *
  47453. * @private
  47454. */
  47455. levels: [{
  47456. /**
  47457. * Specify the level which the options within this object
  47458. * applies to.
  47459. *
  47460. * @type {number}
  47461. * @product gantt
  47462. * @apioption yAxis.labels.levels.level
  47463. *
  47464. * @private
  47465. */
  47466. level: void 0
  47467. }, {
  47468. level: 1,
  47469. /**
  47470. * @type {Highcharts.CSSObject}
  47471. * @product gantt
  47472. * @apioption yAxis.labels.levels.style
  47473. *
  47474. * @private
  47475. */
  47476. style: {
  47477. /** @ignore-option */
  47478. fontWeight: 'bold'
  47479. }
  47480. }],
  47481. /**
  47482. * The symbol for the collapse and expand icon in a
  47483. * treegrid.
  47484. *
  47485. * @product gantt
  47486. * @optionparent yAxis.labels.symbol
  47487. *
  47488. * @private
  47489. */
  47490. symbol: {
  47491. /**
  47492. * The symbol type. Points to a definition function in
  47493. * the `Highcharts.Renderer.symbols` collection.
  47494. *
  47495. * @type {Highcharts.SymbolKeyValue}
  47496. *
  47497. * @private
  47498. */
  47499. type: 'triangle',
  47500. x: -5,
  47501. y: -5,
  47502. height: 10,
  47503. width: 10,
  47504. padding: 5
  47505. }
  47506. },
  47507. uniqueNames: false
  47508. }, userOptions, {
  47509. // Forced options
  47510. reversed: true,
  47511. // grid.columns is not supported in treegrid
  47512. grid: {
  47513. columns: void 0
  47514. }
  47515. });
  47516. }
  47517. // Now apply the original function with the original arguments,
  47518. // which are sliced off this function's arguments
  47519. proceed.apply(axis, [chart, userOptions]);
  47520. if (isTreeGrid) {
  47521. axis.hasNames = true;
  47522. axis.options.showLastLabel = true;
  47523. }
  47524. }
  47525. /**
  47526. * Set the tick positions, tickInterval, axis min and max.
  47527. *
  47528. * @private
  47529. * @function Highcharts.GridAxis#setTickInterval
  47530. *
  47531. * @param {Function} proceed
  47532. * The original setTickInterval function.
  47533. */
  47534. function wrapSetTickInterval(proceed) {
  47535. var axis = this,
  47536. options = axis.options,
  47537. isTreeGrid = options.type === 'treegrid';
  47538. if (isTreeGrid) {
  47539. axis.min = pick(axis.userMin, options.min, axis.dataMin);
  47540. axis.max = pick(axis.userMax, options.max, axis.dataMax);
  47541. fireEvent(axis, 'foundExtremes');
  47542. // setAxisTranslation modifies the min and max according to
  47543. // axis breaks.
  47544. axis.setAxisTranslation(true);
  47545. axis.tickmarkOffset = 0.5;
  47546. axis.tickInterval = 1;
  47547. axis.tickPositions = axis.treeGrid.mapOfPosToGridNode ?
  47548. axis.treeGrid.getTickPositions() :
  47549. [];
  47550. }
  47551. else {
  47552. proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
  47553. }
  47554. }
  47555. /* *
  47556. *
  47557. * Classes
  47558. *
  47559. * */
  47560. /**
  47561. * @private
  47562. * @class
  47563. */
  47564. var Additions = /** @class */ (function () {
  47565. /* *
  47566. *
  47567. * Constructors
  47568. *
  47569. * */
  47570. /**
  47571. * @private
  47572. */
  47573. function Additions(axis) {
  47574. this.axis = axis;
  47575. }
  47576. /* *
  47577. *
  47578. * Functions
  47579. *
  47580. * */
  47581. /**
  47582. * Calculates the new axis breaks to collapse a node.
  47583. *
  47584. * @private
  47585. *
  47586. * @param {Highcharts.Axis} axis
  47587. * The axis to check against.
  47588. *
  47589. * @param {Highcharts.GridNode} node
  47590. * The node to collapse.
  47591. *
  47592. * @param {number} pos
  47593. * The tick position to collapse.
  47594. *
  47595. * @return {Array<object>}
  47596. * Returns an array of the new breaks for the axis.
  47597. */
  47598. Additions.prototype.collapse = function (node) {
  47599. var axis = this.axis,
  47600. breaks = (axis.options.breaks || []),
  47601. obj = getBreakFromNode(node,
  47602. axis.max);
  47603. breaks.push(obj);
  47604. return breaks;
  47605. };
  47606. /**
  47607. * Calculates the new axis breaks to expand a node.
  47608. *
  47609. * @private
  47610. *
  47611. * @param {Highcharts.Axis} axis
  47612. * The axis to check against.
  47613. *
  47614. * @param {Highcharts.GridNode} node
  47615. * The node to expand.
  47616. *
  47617. * @param {number} pos
  47618. * The tick position to expand.
  47619. *
  47620. * @return {Array<object>}
  47621. * Returns an array of the new breaks for the axis.
  47622. */
  47623. Additions.prototype.expand = function (node) {
  47624. var axis = this.axis,
  47625. breaks = (axis.options.breaks || []),
  47626. obj = getBreakFromNode(node,
  47627. axis.max);
  47628. // Remove the break from the axis breaks array.
  47629. return breaks.reduce(function (arr, b) {
  47630. if (b.to !== obj.to || b.from !== obj.from) {
  47631. arr.push(b);
  47632. }
  47633. return arr;
  47634. }, []);
  47635. };
  47636. /**
  47637. * Creates a list of positions for the ticks on the axis. Filters out
  47638. * positions that are outside min and max, or is inside an axis break.
  47639. *
  47640. * @private
  47641. *
  47642. * @return {Array<number>}
  47643. * List of positions.
  47644. */
  47645. Additions.prototype.getTickPositions = function () {
  47646. var axis = this.axis;
  47647. return Object.keys(axis.treeGrid.mapOfPosToGridNode || {}).reduce(function (arr, key) {
  47648. var pos = +key;
  47649. if (axis.min <= pos &&
  47650. axis.max >= pos &&
  47651. !(axis.brokenAxis && axis.brokenAxis.isInAnyBreak(pos))) {
  47652. arr.push(pos);
  47653. }
  47654. return arr;
  47655. }, []);
  47656. };
  47657. /**
  47658. * Check if a node is collapsed.
  47659. *
  47660. * @private
  47661. *
  47662. * @param {Highcharts.Axis} axis
  47663. * The axis to check against.
  47664. *
  47665. * @param {object} node
  47666. * The node to check if is collapsed.
  47667. *
  47668. * @param {number} pos
  47669. * The tick position to collapse.
  47670. *
  47671. * @return {boolean}
  47672. * Returns true if collapsed, false if expanded.
  47673. */
  47674. Additions.prototype.isCollapsed = function (node) {
  47675. var axis = this.axis,
  47676. breaks = (axis.options.breaks || []),
  47677. obj = getBreakFromNode(node,
  47678. axis.max);
  47679. return breaks.some(function (b) {
  47680. return b.from === obj.from && b.to === obj.to;
  47681. });
  47682. };
  47683. /**
  47684. * Calculates the new axis breaks after toggling the collapse/expand
  47685. * state of a node. If it is collapsed it will be expanded, and if it is
  47686. * exapended it will be collapsed.
  47687. *
  47688. * @private
  47689. *
  47690. * @param {Highcharts.Axis} axis
  47691. * The axis to check against.
  47692. *
  47693. * @param {Highcharts.GridNode} node
  47694. * The node to toggle.
  47695. *
  47696. * @return {Array<object>}
  47697. * Returns an array of the new breaks for the axis.
  47698. */
  47699. Additions.prototype.toggleCollapse = function (node) {
  47700. return (this.isCollapsed(node) ?
  47701. this.expand(node) :
  47702. this.collapse(node));
  47703. };
  47704. return Additions;
  47705. }());
  47706. TreeGridAxis.Additions = Additions;
  47707. })(TreeGridAxis || (TreeGridAxis = {}));
  47708. // Make utility functions available for testing.
  47709. Axis.prototype.utils = {
  47710. getNode: Tree.getNode
  47711. };
  47712. TreeGridAxis.compose(Axis);
  47713. return TreeGridAxis;
  47714. });
  47715. _registerModule(_modules, 'Extensions/CurrentDateIndication.js', [_modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Utilities.js'], _modules['Core/Axis/PlotLineOrBand.js']], function (H, O, U, PlotLineOrBand) {
  47716. /* *
  47717. *
  47718. * (c) 2016-2020 Highsoft AS
  47719. *
  47720. * Author: Lars A. V. Cabrera
  47721. *
  47722. * License: www.highcharts.com/license
  47723. *
  47724. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47725. *
  47726. * */
  47727. var dateFormat = O.dateFormat;
  47728. var addEvent = U.addEvent,
  47729. merge = U.merge,
  47730. wrap = U.wrap;
  47731. var Axis = H.Axis;
  47732. var defaultConfig = {
  47733. /**
  47734. * Show an indicator on the axis for the current date and time. Can be a
  47735. * boolean or a configuration object similar to
  47736. * [xAxis.plotLines](#xAxis.plotLines).
  47737. *
  47738. * @sample gantt/current-date-indicator/demo
  47739. * Current date indicator enabled
  47740. * @sample gantt/current-date-indicator/object-config
  47741. * Current date indicator with custom options
  47742. *
  47743. * @declare Highcharts.AxisCurrentDateIndicatorOptions
  47744. * @type {boolean|*}
  47745. * @default true
  47746. * @extends xAxis.plotLines
  47747. * @excluding value
  47748. * @product gantt
  47749. * @apioption xAxis.currentDateIndicator
  47750. */
  47751. currentDateIndicator: true,
  47752. color: '#ccd6eb',
  47753. width: 2,
  47754. /**
  47755. * @declare Highcharts.AxisCurrentDateIndicatorLabelOptions
  47756. */
  47757. label: {
  47758. /**
  47759. * Format of the label. This options is passed as the fist argument to
  47760. * [dateFormat](/class-reference/Highcharts#dateFormat) function.
  47761. *
  47762. * @type {string}
  47763. * @default '%a, %b %d %Y, %H:%M'
  47764. * @product gantt
  47765. * @apioption xAxis.currentDateIndicator.label.format
  47766. */
  47767. format: '%a, %b %d %Y, %H:%M',
  47768. formatter: function (value, format) {
  47769. return dateFormat(format, value);
  47770. },
  47771. rotation: 0,
  47772. /**
  47773. * @type {Highcharts.CSSObject}
  47774. */
  47775. style: {
  47776. /** @internal */
  47777. fontSize: '10px'
  47778. }
  47779. }
  47780. };
  47781. /* eslint-disable no-invalid-this */
  47782. addEvent(Axis, 'afterSetOptions', function () {
  47783. var options = this.options,
  47784. cdiOptions = options.currentDateIndicator;
  47785. if (cdiOptions) {
  47786. cdiOptions = typeof cdiOptions === 'object' ?
  47787. merge(defaultConfig, cdiOptions) : merge(defaultConfig);
  47788. cdiOptions.value = new Date();
  47789. if (!options.plotLines) {
  47790. options.plotLines = [];
  47791. }
  47792. options.plotLines.push(cdiOptions);
  47793. }
  47794. });
  47795. addEvent(PlotLineOrBand, 'render', function () {
  47796. // If the label already exists, update its text
  47797. if (this.label) {
  47798. this.label.attr({
  47799. text: this.getLabelText(this.options.label)
  47800. });
  47801. }
  47802. });
  47803. wrap(PlotLineOrBand.prototype, 'getLabelText', function (defaultMethod, defaultLabelOptions) {
  47804. var options = this.options;
  47805. if (options.currentDateIndicator && options.label &&
  47806. typeof options.label.formatter === 'function') {
  47807. options.value = new Date();
  47808. return options.label.formatter
  47809. .call(this, options.value, options.label.format);
  47810. }
  47811. return defaultMethod.call(this, defaultLabelOptions);
  47812. });
  47813. });
  47814. _registerModule(_modules, 'Extensions/StaticScale.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  47815. /* *
  47816. *
  47817. * (c) 2016-2020 Torstein Honsi, Lars Cabrera
  47818. *
  47819. * License: www.highcharts.com/license
  47820. *
  47821. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47822. *
  47823. * */
  47824. var addEvent = U.addEvent,
  47825. defined = U.defined,
  47826. isNumber = U.isNumber,
  47827. pick = U.pick;
  47828. var Chart = H.Chart;
  47829. /* eslint-disable no-invalid-this */
  47830. /**
  47831. * For vertical axes only. Setting the static scale ensures that each tick unit
  47832. * is translated into a fixed pixel height. For example, setting the static
  47833. * scale to 24 results in each Y axis category taking up 24 pixels, and the
  47834. * height of the chart adjusts. Adding or removing items will make the chart
  47835. * resize.
  47836. *
  47837. * @sample gantt/xrange-series/demo/
  47838. * X-range series with static scale
  47839. *
  47840. * @type {number}
  47841. * @default 50
  47842. * @since 6.2.0
  47843. * @product gantt
  47844. * @apioption yAxis.staticScale
  47845. */
  47846. addEvent(H.Axis, 'afterSetOptions', function () {
  47847. var chartOptions = this.chart.options && this.chart.options.chart;
  47848. if (!this.horiz &&
  47849. isNumber(this.options.staticScale) &&
  47850. (!chartOptions.height ||
  47851. (chartOptions.scrollablePlotArea &&
  47852. chartOptions.scrollablePlotArea.minHeight))) {
  47853. this.staticScale = this.options.staticScale;
  47854. }
  47855. });
  47856. Chart.prototype.adjustHeight = function () {
  47857. if (this.redrawTrigger !== 'adjustHeight') {
  47858. (this.axes || []).forEach(function (axis) {
  47859. var chart = axis.chart,
  47860. animate = !!chart.initiatedScale &&
  47861. chart.options.animation,
  47862. staticScale = axis.options.staticScale,
  47863. height,
  47864. diff;
  47865. if (axis.staticScale && defined(axis.min)) {
  47866. height = pick(axis.brokenAxis && axis.brokenAxis.unitLength, axis.max + axis.tickInterval - axis.min) * staticScale;
  47867. // Minimum height is 1 x staticScale.
  47868. height = Math.max(height, staticScale);
  47869. diff = height - chart.plotHeight;
  47870. if (Math.abs(diff) >= 1) {
  47871. chart.plotHeight = height;
  47872. chart.redrawTrigger = 'adjustHeight';
  47873. chart.setSize(void 0, chart.chartHeight + diff, animate);
  47874. }
  47875. // Make sure clip rects have the right height before initial
  47876. // animation.
  47877. axis.series.forEach(function (series) {
  47878. var clipRect = series.sharedClipKey &&
  47879. chart[series.sharedClipKey];
  47880. if (clipRect) {
  47881. clipRect.attr({
  47882. height: chart.plotHeight
  47883. });
  47884. }
  47885. });
  47886. }
  47887. });
  47888. this.initiatedScale = true;
  47889. }
  47890. this.redrawTrigger = null;
  47891. };
  47892. addEvent(Chart, 'render', Chart.prototype.adjustHeight);
  47893. });
  47894. _registerModule(_modules, 'Extensions/ArrowSymbols.js', [_modules['Core/Renderer/SVG/SVGRenderer.js']], function (SVGRenderer) {
  47895. /* *
  47896. *
  47897. * (c) 2017 Highsoft AS
  47898. * Authors: Lars A. V. Cabrera
  47899. *
  47900. * License: www.highcharts.com/license
  47901. *
  47902. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47903. *
  47904. * */
  47905. /**
  47906. * Creates an arrow symbol. Like a triangle, except not filled.
  47907. * ```
  47908. * o
  47909. * o
  47910. * o
  47911. * o
  47912. * o
  47913. * o
  47914. * o
  47915. * ```
  47916. *
  47917. * @private
  47918. * @function
  47919. *
  47920. * @param {number} x
  47921. * x position of the arrow
  47922. *
  47923. * @param {number} y
  47924. * y position of the arrow
  47925. *
  47926. * @param {number} w
  47927. * width of the arrow
  47928. *
  47929. * @param {number} h
  47930. * height of the arrow
  47931. *
  47932. * @return {Highcharts.SVGPathArray}
  47933. * Path array
  47934. */
  47935. SVGRenderer.prototype.symbols.arrow = function (x, y, w, h) {
  47936. return [
  47937. ['M', x, y + h / 2],
  47938. ['L', x + w, y],
  47939. ['L', x, y + h / 2],
  47940. ['L', x + w, y + h]
  47941. ];
  47942. };
  47943. /**
  47944. * Creates a half-width arrow symbol. Like a triangle, except not filled.
  47945. * ```
  47946. * o
  47947. * o
  47948. * o
  47949. * o
  47950. * o
  47951. * ```
  47952. *
  47953. * @private
  47954. * @function
  47955. *
  47956. * @param {number} x
  47957. * x position of the arrow
  47958. *
  47959. * @param {number} y
  47960. * y position of the arrow
  47961. *
  47962. * @param {number} w
  47963. * width of the arrow
  47964. *
  47965. * @param {number} h
  47966. * height of the arrow
  47967. *
  47968. * @return {Highcharts.SVGPathArray}
  47969. * Path array
  47970. */
  47971. SVGRenderer.prototype.symbols['arrow-half'] = function (x, y, w, h) {
  47972. return SVGRenderer.prototype.symbols.arrow(x, y, w / 2, h);
  47973. };
  47974. /**
  47975. * Creates a left-oriented triangle.
  47976. * ```
  47977. * o
  47978. * ooooooo
  47979. * ooooooooooooo
  47980. * ooooooo
  47981. * o
  47982. * ```
  47983. *
  47984. * @private
  47985. * @function
  47986. *
  47987. * @param {number} x
  47988. * x position of the triangle
  47989. *
  47990. * @param {number} y
  47991. * y position of the triangle
  47992. *
  47993. * @param {number} w
  47994. * width of the triangle
  47995. *
  47996. * @param {number} h
  47997. * height of the triangle
  47998. *
  47999. * @return {Highcharts.SVGPathArray}
  48000. * Path array
  48001. */
  48002. SVGRenderer.prototype.symbols['triangle-left'] = function (x, y, w, h) {
  48003. return [
  48004. ['M', x + w, y],
  48005. ['L', x, y + h / 2],
  48006. ['L', x + w, y + h],
  48007. ['Z']
  48008. ];
  48009. };
  48010. /**
  48011. * Alias function for triangle-left.
  48012. *
  48013. * @private
  48014. * @function
  48015. *
  48016. * @param {number} x
  48017. * x position of the arrow
  48018. *
  48019. * @param {number} y
  48020. * y position of the arrow
  48021. *
  48022. * @param {number} w
  48023. * width of the arrow
  48024. *
  48025. * @param {number} h
  48026. * height of the arrow
  48027. *
  48028. * @return {Highcharts.SVGPathArray}
  48029. * Path array
  48030. */
  48031. SVGRenderer.prototype.symbols['arrow-filled'] = SVGRenderer.prototype.symbols['triangle-left'];
  48032. /**
  48033. * Creates a half-width, left-oriented triangle.
  48034. * ```
  48035. * o
  48036. * oooo
  48037. * ooooooo
  48038. * oooo
  48039. * o
  48040. * ```
  48041. *
  48042. * @private
  48043. * @function
  48044. *
  48045. * @param {number} x
  48046. * x position of the triangle
  48047. *
  48048. * @param {number} y
  48049. * y position of the triangle
  48050. *
  48051. * @param {number} w
  48052. * width of the triangle
  48053. *
  48054. * @param {number} h
  48055. * height of the triangle
  48056. *
  48057. * @return {Highcharts.SVGPathArray}
  48058. * Path array
  48059. */
  48060. SVGRenderer.prototype.symbols['triangle-left-half'] = function (x, y, w, h) {
  48061. return SVGRenderer.prototype.symbols['triangle-left'](x, y, w / 2, h);
  48062. };
  48063. /**
  48064. * Alias function for triangle-left-half.
  48065. *
  48066. * @private
  48067. * @function
  48068. *
  48069. * @param {number} x
  48070. * x position of the arrow
  48071. *
  48072. * @param {number} y
  48073. * y position of the arrow
  48074. *
  48075. * @param {number} w
  48076. * width of the arrow
  48077. *
  48078. * @param {number} h
  48079. * height of the arrow
  48080. *
  48081. * @return {Highcharts.SVGPathArray}
  48082. * Path array
  48083. */
  48084. SVGRenderer.prototype.symbols['arrow-filled-half'] = SVGRenderer.prototype.symbols['triangle-left-half'];
  48085. });
  48086. _registerModule(_modules, 'Gantt/Connection.js', [_modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (H, O, Point, U) {
  48087. /* *
  48088. *
  48089. * (c) 2016 Highsoft AS
  48090. * Authors: Øystein Moseng, Lars A. V. Cabrera
  48091. *
  48092. * License: www.highcharts.com/license
  48093. *
  48094. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48095. *
  48096. * */
  48097. /**
  48098. * The default pathfinder algorithm to use for a chart. It is possible to define
  48099. * your own algorithms by adding them to the
  48100. * `Highcharts.Pathfinder.prototype.algorithms`
  48101. * object before the chart has been created.
  48102. *
  48103. * The default algorithms are as follows:
  48104. *
  48105. * `straight`: Draws a straight line between the connecting
  48106. * points. Does not avoid other points when drawing.
  48107. *
  48108. * `simpleConnect`: Finds a path between the points using right angles
  48109. * only. Takes only starting/ending points into
  48110. * account, and will not avoid other points.
  48111. *
  48112. * `fastAvoid`: Finds a path between the points using right angles
  48113. * only. Will attempt to avoid other points, but its
  48114. * focus is performance over accuracy. Works well with
  48115. * less dense datasets.
  48116. *
  48117. * @typedef {"fastAvoid"|"simpleConnect"|"straight"|string} Highcharts.PathfinderTypeValue
  48118. */
  48119. ''; // detach doclets above
  48120. var defaultOptions = O.defaultOptions;
  48121. var addEvent = U.addEvent,
  48122. defined = U.defined,
  48123. error = U.error,
  48124. extend = U.extend,
  48125. merge = U.merge,
  48126. objectEach = U.objectEach,
  48127. pick = U.pick,
  48128. splat = U.splat;
  48129. var deg2rad = H.deg2rad,
  48130. max = Math.max,
  48131. min = Math.min;
  48132. /*
  48133. @todo:
  48134. - Document how to write your own algorithms
  48135. - Consider adding a Point.pathTo method that wraps creating a connection
  48136. and rendering it
  48137. */
  48138. // Set default Pathfinder options
  48139. extend(defaultOptions, {
  48140. /**
  48141. * The Pathfinder module allows you to define connections between any two
  48142. * points, represented as lines - optionally with markers for the start
  48143. * and/or end points. Multiple algorithms are available for calculating how
  48144. * the connecting lines are drawn.
  48145. *
  48146. * Connector functionality requires Highcharts Gantt to be loaded. In Gantt
  48147. * charts, the connectors are used to draw dependencies between tasks.
  48148. *
  48149. * @see [dependency](series.gantt.data.dependency)
  48150. *
  48151. * @sample gantt/pathfinder/demo
  48152. * Pathfinder connections
  48153. *
  48154. * @declare Highcharts.ConnectorsOptions
  48155. * @product gantt
  48156. * @optionparent connectors
  48157. */
  48158. connectors: {
  48159. /**
  48160. * Enable connectors for this chart. Requires Highcharts Gantt.
  48161. *
  48162. * @type {boolean}
  48163. * @default true
  48164. * @since 6.2.0
  48165. * @apioption connectors.enabled
  48166. */
  48167. /**
  48168. * Set the default dash style for this chart's connecting lines.
  48169. *
  48170. * @type {string}
  48171. * @default solid
  48172. * @since 6.2.0
  48173. * @apioption connectors.dashStyle
  48174. */
  48175. /**
  48176. * Set the default color for this chart's Pathfinder connecting lines.
  48177. * Defaults to the color of the point being connected.
  48178. *
  48179. * @type {Highcharts.ColorString}
  48180. * @since 6.2.0
  48181. * @apioption connectors.lineColor
  48182. */
  48183. /**
  48184. * Set the default pathfinder margin to use, in pixels. Some Pathfinder
  48185. * algorithms attempt to avoid obstacles, such as other points in the
  48186. * chart. These algorithms use this margin to determine how close lines
  48187. * can be to an obstacle. The default is to compute this automatically
  48188. * from the size of the obstacles in the chart.
  48189. *
  48190. * To draw connecting lines close to existing points, set this to a low
  48191. * number. For more space around existing points, set this number
  48192. * higher.
  48193. *
  48194. * @sample gantt/pathfinder/algorithm-margin
  48195. * Small algorithmMargin
  48196. *
  48197. * @type {number}
  48198. * @since 6.2.0
  48199. * @apioption connectors.algorithmMargin
  48200. */
  48201. /**
  48202. * Set the default pathfinder algorithm to use for this chart. It is
  48203. * possible to define your own algorithms by adding them to the
  48204. * Highcharts.Pathfinder.prototype.algorithms object before the chart
  48205. * has been created.
  48206. *
  48207. * The default algorithms are as follows:
  48208. *
  48209. * `straight`: Draws a straight line between the connecting
  48210. * points. Does not avoid other points when drawing.
  48211. *
  48212. * `simpleConnect`: Finds a path between the points using right angles
  48213. * only. Takes only starting/ending points into
  48214. * account, and will not avoid other points.
  48215. *
  48216. * `fastAvoid`: Finds a path between the points using right angles
  48217. * only. Will attempt to avoid other points, but its
  48218. * focus is performance over accuracy. Works well with
  48219. * less dense datasets.
  48220. *
  48221. * Default value: `straight` is used as default for most series types,
  48222. * while `simpleConnect` is used as default for Gantt series, to show
  48223. * dependencies between points.
  48224. *
  48225. * @sample gantt/pathfinder/demo
  48226. * Different types used
  48227. *
  48228. * @type {Highcharts.PathfinderTypeValue}
  48229. * @default undefined
  48230. * @since 6.2.0
  48231. */
  48232. type: 'straight',
  48233. /**
  48234. * Set the default pixel width for this chart's Pathfinder connecting
  48235. * lines.
  48236. *
  48237. * @since 6.2.0
  48238. */
  48239. lineWidth: 1,
  48240. /**
  48241. * Marker options for this chart's Pathfinder connectors. Note that
  48242. * this option is overridden by the `startMarker` and `endMarker`
  48243. * options.
  48244. *
  48245. * @declare Highcharts.ConnectorsMarkerOptions
  48246. * @since 6.2.0
  48247. */
  48248. marker: {
  48249. /**
  48250. * Set the radius of the connector markers. The default is
  48251. * automatically computed based on the algorithmMargin setting.
  48252. *
  48253. * Setting marker.width and marker.height will override this
  48254. * setting.
  48255. *
  48256. * @type {number}
  48257. * @since 6.2.0
  48258. * @apioption connectors.marker.radius
  48259. */
  48260. /**
  48261. * Set the width of the connector markers. If not supplied, this
  48262. * is inferred from the marker radius.
  48263. *
  48264. * @type {number}
  48265. * @since 6.2.0
  48266. * @apioption connectors.marker.width
  48267. */
  48268. /**
  48269. * Set the height of the connector markers. If not supplied, this
  48270. * is inferred from the marker radius.
  48271. *
  48272. * @type {number}
  48273. * @since 6.2.0
  48274. * @apioption connectors.marker.height
  48275. */
  48276. /**
  48277. * Set the color of the connector markers. By default this is the
  48278. * same as the connector color.
  48279. *
  48280. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48281. * @since 6.2.0
  48282. * @apioption connectors.marker.color
  48283. */
  48284. /**
  48285. * Set the line/border color of the connector markers. By default
  48286. * this is the same as the marker color.
  48287. *
  48288. * @type {Highcharts.ColorString}
  48289. * @since 6.2.0
  48290. * @apioption connectors.marker.lineColor
  48291. */
  48292. /**
  48293. * Enable markers for the connectors.
  48294. */
  48295. enabled: false,
  48296. /**
  48297. * Horizontal alignment of the markers relative to the points.
  48298. *
  48299. * @type {Highcharts.AlignValue}
  48300. */
  48301. align: 'center',
  48302. /**
  48303. * Vertical alignment of the markers relative to the points.
  48304. *
  48305. * @type {Highcharts.VerticalAlignValue}
  48306. */
  48307. verticalAlign: 'middle',
  48308. /**
  48309. * Whether or not to draw the markers inside the points.
  48310. */
  48311. inside: false,
  48312. /**
  48313. * Set the line/border width of the pathfinder markers.
  48314. */
  48315. lineWidth: 1
  48316. },
  48317. /**
  48318. * Marker options specific to the start markers for this chart's
  48319. * Pathfinder connectors. Overrides the generic marker options.
  48320. *
  48321. * @declare Highcharts.ConnectorsStartMarkerOptions
  48322. * @extends connectors.marker
  48323. * @since 6.2.0
  48324. */
  48325. startMarker: {
  48326. /**
  48327. * Set the symbol of the connector start markers.
  48328. */
  48329. symbol: 'diamond'
  48330. },
  48331. /**
  48332. * Marker options specific to the end markers for this chart's
  48333. * Pathfinder connectors. Overrides the generic marker options.
  48334. *
  48335. * @declare Highcharts.ConnectorsEndMarkerOptions
  48336. * @extends connectors.marker
  48337. * @since 6.2.0
  48338. */
  48339. endMarker: {
  48340. /**
  48341. * Set the symbol of the connector end markers.
  48342. */
  48343. symbol: 'arrow-filled'
  48344. }
  48345. }
  48346. });
  48347. /**
  48348. * Override Pathfinder connector options for a series. Requires Highcharts Gantt
  48349. * to be loaded.
  48350. *
  48351. * @declare Highcharts.SeriesConnectorsOptionsObject
  48352. * @extends connectors
  48353. * @since 6.2.0
  48354. * @excluding enabled, algorithmMargin
  48355. * @product gantt
  48356. * @apioption plotOptions.series.connectors
  48357. */
  48358. /**
  48359. * Connect to a point. This option can be either a string, referring to the ID
  48360. * of another point, or an object, or an array of either. If the option is an
  48361. * array, each element defines a connection.
  48362. *
  48363. * @sample gantt/pathfinder/demo
  48364. * Different connection types
  48365. *
  48366. * @declare Highcharts.XrangePointConnectorsOptionsObject
  48367. * @type {string|Array<string|*>|*}
  48368. * @extends plotOptions.series.connectors
  48369. * @since 6.2.0
  48370. * @excluding enabled
  48371. * @product gantt
  48372. * @requires highcharts-gantt
  48373. * @apioption series.xrange.data.connect
  48374. */
  48375. /**
  48376. * The ID of the point to connect to.
  48377. *
  48378. * @type {string}
  48379. * @since 6.2.0
  48380. * @product gantt
  48381. * @apioption series.xrange.data.connect.to
  48382. */
  48383. /**
  48384. * Get point bounding box using plotX/plotY and shapeArgs. If using
  48385. * graphic.getBBox() directly, the bbox will be affected by animation.
  48386. *
  48387. * @private
  48388. * @function
  48389. *
  48390. * @param {Highcharts.Point} point
  48391. * The point to get BB of.
  48392. *
  48393. * @return {Highcharts.Dictionary<number>|null}
  48394. * Result xMax, xMin, yMax, yMin.
  48395. */
  48396. function getPointBB(point) {
  48397. var shapeArgs = point.shapeArgs,
  48398. bb;
  48399. // Prefer using shapeArgs (columns)
  48400. if (shapeArgs) {
  48401. return {
  48402. xMin: shapeArgs.x,
  48403. xMax: shapeArgs.x + shapeArgs.width,
  48404. yMin: shapeArgs.y,
  48405. yMax: shapeArgs.y + shapeArgs.height
  48406. };
  48407. }
  48408. // Otherwise use plotX/plotY and bb
  48409. bb = point.graphic && point.graphic.getBBox();
  48410. return bb ? {
  48411. xMin: point.plotX - bb.width / 2,
  48412. xMax: point.plotX + bb.width / 2,
  48413. yMin: point.plotY - bb.height / 2,
  48414. yMax: point.plotY + bb.height / 2
  48415. } : null;
  48416. }
  48417. /**
  48418. * Calculate margin to place around obstacles for the pathfinder in pixels.
  48419. * Returns a minimum of 1 pixel margin.
  48420. *
  48421. * @private
  48422. * @function
  48423. *
  48424. * @param {Array<object>} obstacles
  48425. * Obstacles to calculate margin from.
  48426. *
  48427. * @return {number}
  48428. * The calculated margin in pixels. At least 1.
  48429. */
  48430. function calculateObstacleMargin(obstacles) {
  48431. var len = obstacles.length,
  48432. i = 0,
  48433. j,
  48434. obstacleDistance,
  48435. distances = [],
  48436. // Compute smallest distance between two rectangles
  48437. distance = function (a,
  48438. b,
  48439. bbMargin) {
  48440. // Count the distance even if we are slightly off
  48441. var margin = pick(bbMargin, 10),
  48442. yOverlap = a.yMax + margin > b.yMin - margin &&
  48443. a.yMin - margin < b.yMax + margin,
  48444. xOverlap = a.xMax + margin > b.xMin - margin &&
  48445. a.xMin - margin < b.xMax + margin,
  48446. xDistance = yOverlap ? (a.xMin > b.xMax ? a.xMin - b.xMax : b.xMin - a.xMax) : Infinity,
  48447. yDistance = xOverlap ? (a.yMin > b.yMax ? a.yMin - b.yMax : b.yMin - a.yMax) : Infinity;
  48448. // If the rectangles collide, try recomputing with smaller margin.
  48449. // If they collide anyway, discard the obstacle.
  48450. if (xOverlap && yOverlap) {
  48451. return (margin ?
  48452. distance(a, b, Math.floor(margin / 2)) :
  48453. Infinity);
  48454. }
  48455. return min(xDistance, yDistance);
  48456. };
  48457. // Go over all obstacles and compare them to the others.
  48458. for (; i < len; ++i) {
  48459. // Compare to all obstacles ahead. We will already have compared this
  48460. // obstacle to the ones before.
  48461. for (j = i + 1; j < len; ++j) {
  48462. obstacleDistance = distance(obstacles[i], obstacles[j]);
  48463. // TODO: Magic number 80
  48464. if (obstacleDistance < 80) { // Ignore large distances
  48465. distances.push(obstacleDistance);
  48466. }
  48467. }
  48468. }
  48469. // Ensure we always have at least one value, even in very spaceous charts
  48470. distances.push(80);
  48471. return max(Math.floor(distances.sort(function (a, b) {
  48472. return (a - b);
  48473. })[
  48474. // Discard first 10% of the relevant distances, and then grab
  48475. // the smallest one.
  48476. Math.floor(distances.length / 10)] / 2 - 1 // Divide the distance by 2 and subtract 1.
  48477. ), 1 // 1 is the minimum margin
  48478. );
  48479. }
  48480. /* eslint-disable no-invalid-this, valid-jsdoc */
  48481. /**
  48482. * The Connection class. Used internally to represent a connection between two
  48483. * points.
  48484. *
  48485. * @private
  48486. * @class
  48487. * @name Highcharts.Connection
  48488. *
  48489. * @param {Highcharts.Point} from
  48490. * Connection runs from this Point.
  48491. *
  48492. * @param {Highcharts.Point} to
  48493. * Connection runs to this Point.
  48494. *
  48495. * @param {Highcharts.ConnectorsOptions} [options]
  48496. * Connection options.
  48497. */
  48498. var Connection = /** @class */ (function () {
  48499. function Connection(from, to, options) {
  48500. /* *
  48501. *
  48502. * Properties
  48503. *
  48504. * */
  48505. this.chart = void 0;
  48506. this.fromPoint = void 0;
  48507. this.graphics = void 0;
  48508. this.pathfinder = void 0;
  48509. this.toPoint = void 0;
  48510. this.init(from, to, options);
  48511. }
  48512. /**
  48513. * Initialize the Connection object. Used as constructor only.
  48514. *
  48515. * @function Highcharts.Connection#init
  48516. *
  48517. * @param {Highcharts.Point} from
  48518. * Connection runs from this Point.
  48519. *
  48520. * @param {Highcharts.Point} to
  48521. * Connection runs to this Point.
  48522. *
  48523. * @param {Highcharts.ConnectorsOptions} [options]
  48524. * Connection options.
  48525. */
  48526. Connection.prototype.init = function (from, to, options) {
  48527. this.fromPoint = from;
  48528. this.toPoint = to;
  48529. this.options = options;
  48530. this.chart = from.series.chart;
  48531. this.pathfinder = this.chart.pathfinder;
  48532. };
  48533. /**
  48534. * Add (or update) this connection's path on chart. Stores reference to the
  48535. * created element on this.graphics.path.
  48536. *
  48537. * @function Highcharts.Connection#renderPath
  48538. *
  48539. * @param {Highcharts.SVGPathArray} path
  48540. * Path to render, in array format. E.g. ['M', 0, 0, 'L', 10, 10]
  48541. *
  48542. * @param {Highcharts.SVGAttributes} [attribs]
  48543. * SVG attributes for the path.
  48544. *
  48545. * @param {Partial<Highcharts.AnimationOptionsObject>} [animation]
  48546. * Animation options for the rendering.
  48547. */
  48548. Connection.prototype.renderPath = function (path, attribs, animation) {
  48549. var connection = this,
  48550. chart = this.chart,
  48551. styledMode = chart.styledMode,
  48552. pathfinder = chart.pathfinder,
  48553. animate = !chart.options.chart.forExport && animation !== false,
  48554. pathGraphic = connection.graphics && connection.graphics.path,
  48555. anim;
  48556. // Add the SVG element of the pathfinder group if it doesn't exist
  48557. if (!pathfinder.group) {
  48558. pathfinder.group = chart.renderer.g()
  48559. .addClass('highcharts-pathfinder-group')
  48560. .attr({ zIndex: -1 })
  48561. .add(chart.seriesGroup);
  48562. }
  48563. // Shift the group to compensate for plot area.
  48564. // Note: Do this always (even when redrawing a path) to avoid issues
  48565. // when updating chart in a way that changes plot metrics.
  48566. pathfinder.group.translate(chart.plotLeft, chart.plotTop);
  48567. // Create path if does not exist
  48568. if (!(pathGraphic && pathGraphic.renderer)) {
  48569. pathGraphic = chart.renderer.path()
  48570. .add(pathfinder.group);
  48571. if (!styledMode) {
  48572. pathGraphic.attr({
  48573. opacity: 0
  48574. });
  48575. }
  48576. }
  48577. // Set path attribs and animate to the new path
  48578. pathGraphic.attr(attribs);
  48579. anim = { d: path };
  48580. if (!styledMode) {
  48581. anim.opacity = 1;
  48582. }
  48583. pathGraphic[animate ? 'animate' : 'attr'](anim, animation);
  48584. // Store reference on connection
  48585. this.graphics = this.graphics || {};
  48586. this.graphics.path = pathGraphic;
  48587. };
  48588. /**
  48589. * Calculate and add marker graphics for connection to the chart. The
  48590. * created/updated elements are stored on this.graphics.start and
  48591. * this.graphics.end.
  48592. *
  48593. * @function Highcharts.Connection#addMarker
  48594. *
  48595. * @param {string} type
  48596. * Marker type, either 'start' or 'end'.
  48597. *
  48598. * @param {Highcharts.ConnectorsMarkerOptions} options
  48599. * All options for this marker. Not calculated or merged with other
  48600. * options.
  48601. *
  48602. * @param {Highcharts.SVGPathArray} path
  48603. * Connection path in array format. This is used to calculate the
  48604. * rotation angle of the markers.
  48605. */
  48606. Connection.prototype.addMarker = function (type, options, path) {
  48607. var connection = this,
  48608. chart = connection.fromPoint.series.chart,
  48609. pathfinder = chart.pathfinder,
  48610. renderer = chart.renderer,
  48611. point = (type === 'start' ?
  48612. connection.fromPoint :
  48613. connection.toPoint),
  48614. anchor = point.getPathfinderAnchorPoint(options),
  48615. markerVector,
  48616. radians,
  48617. rotation,
  48618. box,
  48619. width,
  48620. height,
  48621. pathVector,
  48622. segment;
  48623. if (!options.enabled) {
  48624. return;
  48625. }
  48626. // Last vector before start/end of path, used to get angle
  48627. if (type === 'start') {
  48628. segment = path[1];
  48629. }
  48630. else { // 'end'
  48631. segment = path[path.length - 2];
  48632. }
  48633. if (segment && segment[0] === 'M' || segment[0] === 'L') {
  48634. pathVector = {
  48635. x: segment[1],
  48636. y: segment[2]
  48637. };
  48638. // Get angle between pathVector and anchor point and use it to
  48639. // create marker position.
  48640. radians = point.getRadiansToVector(pathVector, anchor);
  48641. markerVector = point.getMarkerVector(radians, options.radius, anchor);
  48642. // Rotation of marker is calculated from angle between pathVector
  48643. // and markerVector.
  48644. // (Note:
  48645. // Used to recalculate radians between markerVector and pathVector,
  48646. // but this should be the same as between pathVector and anchor.)
  48647. rotation = -radians / deg2rad;
  48648. if (options.width && options.height) {
  48649. width = options.width;
  48650. height = options.height;
  48651. }
  48652. else {
  48653. width = height = options.radius * 2;
  48654. }
  48655. // Add graphics object if it does not exist
  48656. connection.graphics = connection.graphics || {};
  48657. box = {
  48658. x: markerVector.x - (width / 2),
  48659. y: markerVector.y - (height / 2),
  48660. width: width,
  48661. height: height,
  48662. rotation: rotation,
  48663. rotationOriginX: markerVector.x,
  48664. rotationOriginY: markerVector.y
  48665. };
  48666. if (!connection.graphics[type]) {
  48667. // Create new marker element
  48668. connection.graphics[type] = renderer
  48669. .symbol(options.symbol)
  48670. .addClass('highcharts-point-connecting-path-' + type + '-marker')
  48671. .attr(box)
  48672. .add(pathfinder.group);
  48673. if (!renderer.styledMode) {
  48674. connection.graphics[type].attr({
  48675. fill: options.color || connection.fromPoint.color,
  48676. stroke: options.lineColor,
  48677. 'stroke-width': options.lineWidth,
  48678. opacity: 0
  48679. })
  48680. .animate({
  48681. opacity: 1
  48682. }, point.series.options.animation);
  48683. }
  48684. }
  48685. else {
  48686. connection.graphics[type].animate(box);
  48687. }
  48688. }
  48689. };
  48690. /**
  48691. * Calculate and return connection path.
  48692. * Note: Recalculates chart obstacles on demand if they aren't calculated.
  48693. *
  48694. * @function Highcharts.Connection#getPath
  48695. *
  48696. * @param {Highcharts.ConnectorsOptions} options
  48697. * Connector options. Not calculated or merged with other options.
  48698. *
  48699. * @return {object|undefined}
  48700. * Calculated SVG path data in array format.
  48701. */
  48702. Connection.prototype.getPath = function (options) {
  48703. var pathfinder = this.pathfinder,
  48704. chart = this.chart,
  48705. algorithm = pathfinder.algorithms[options.type],
  48706. chartObstacles = pathfinder.chartObstacles;
  48707. if (typeof algorithm !== 'function') {
  48708. error('"' + options.type + '" is not a Pathfinder algorithm.');
  48709. return {
  48710. path: [],
  48711. obstacles: []
  48712. };
  48713. }
  48714. // This function calculates obstacles on demand if they don't exist
  48715. if (algorithm.requiresObstacles && !chartObstacles) {
  48716. chartObstacles =
  48717. pathfinder.chartObstacles =
  48718. pathfinder.getChartObstacles(options);
  48719. // If the algorithmMargin was computed, store the result in default
  48720. // options.
  48721. chart.options.connectors.algorithmMargin =
  48722. options.algorithmMargin;
  48723. // Cache some metrics too
  48724. pathfinder.chartObstacleMetrics =
  48725. pathfinder.getObstacleMetrics(chartObstacles);
  48726. }
  48727. // Get the SVG path
  48728. return algorithm(
  48729. // From
  48730. this.fromPoint.getPathfinderAnchorPoint(options.startMarker),
  48731. // To
  48732. this.toPoint.getPathfinderAnchorPoint(options.endMarker), merge({
  48733. chartObstacles: chartObstacles,
  48734. lineObstacles: pathfinder.lineObstacles || [],
  48735. obstacleMetrics: pathfinder.chartObstacleMetrics,
  48736. hardBounds: {
  48737. xMin: 0,
  48738. xMax: chart.plotWidth,
  48739. yMin: 0,
  48740. yMax: chart.plotHeight
  48741. },
  48742. obstacleOptions: {
  48743. margin: options.algorithmMargin
  48744. },
  48745. startDirectionX: pathfinder.getAlgorithmStartDirection(options.startMarker)
  48746. }, options));
  48747. };
  48748. /**
  48749. * (re)Calculate and (re)draw the connection.
  48750. *
  48751. * @function Highcharts.Connection#render
  48752. */
  48753. Connection.prototype.render = function () {
  48754. var connection = this,
  48755. fromPoint = connection.fromPoint,
  48756. series = fromPoint.series,
  48757. chart = series.chart,
  48758. pathfinder = chart.pathfinder,
  48759. pathResult,
  48760. path,
  48761. options = merge(chart.options.connectors,
  48762. series.options.connectors,
  48763. fromPoint.options.connectors,
  48764. connection.options),
  48765. attribs = {};
  48766. // Set path attribs
  48767. if (!chart.styledMode) {
  48768. attribs.stroke = options.lineColor || fromPoint.color;
  48769. attribs['stroke-width'] = options.lineWidth;
  48770. if (options.dashStyle) {
  48771. attribs.dashstyle = options.dashStyle;
  48772. }
  48773. }
  48774. attribs['class'] = // eslint-disable-line dot-notation
  48775. 'highcharts-point-connecting-path ' +
  48776. 'highcharts-color-' + fromPoint.colorIndex;
  48777. options = merge(attribs, options);
  48778. // Set common marker options
  48779. if (!defined(options.marker.radius)) {
  48780. options.marker.radius = min(max(Math.ceil((options.algorithmMargin || 8) / 2) - 1, 1), 5);
  48781. }
  48782. // Get the path
  48783. pathResult = connection.getPath(options);
  48784. path = pathResult.path;
  48785. // Always update obstacle storage with obstacles from this path.
  48786. // We don't know if future calls will need this for their algorithm.
  48787. if (pathResult.obstacles) {
  48788. pathfinder.lineObstacles =
  48789. pathfinder.lineObstacles || [];
  48790. pathfinder.lineObstacles =
  48791. pathfinder.lineObstacles.concat(pathResult.obstacles);
  48792. }
  48793. // Add the calculated path to the pathfinder group
  48794. connection.renderPath(path, attribs, series.options.animation);
  48795. // Render the markers
  48796. connection.addMarker('start', merge(options.marker, options.startMarker), path);
  48797. connection.addMarker('end', merge(options.marker, options.endMarker), path);
  48798. };
  48799. /**
  48800. * Destroy connection by destroying the added graphics elements.
  48801. *
  48802. * @function Highcharts.Connection#destroy
  48803. */
  48804. Connection.prototype.destroy = function () {
  48805. if (this.graphics) {
  48806. objectEach(this.graphics, function (val) {
  48807. val.destroy();
  48808. });
  48809. delete this.graphics;
  48810. }
  48811. };
  48812. return Connection;
  48813. }());
  48814. // Add to Highcharts namespace
  48815. H.Connection = Connection;
  48816. // Add pathfinding capabilities to Points
  48817. extend(Point.prototype, /** @lends Point.prototype */ {
  48818. /**
  48819. * Get coordinates of anchor point for pathfinder connection.
  48820. *
  48821. * @private
  48822. * @function Highcharts.Point#getPathfinderAnchorPoint
  48823. *
  48824. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  48825. * Connection options for position on point.
  48826. *
  48827. * @return {Highcharts.PositionObject}
  48828. * An object with x/y properties for the position. Coordinates are
  48829. * in plot values, not relative to point.
  48830. */
  48831. getPathfinderAnchorPoint: function (markerOptions) {
  48832. var bb = getPointBB(this),
  48833. x,
  48834. y;
  48835. switch (markerOptions.align) { // eslint-disable-line default-case
  48836. case 'right':
  48837. x = 'xMax';
  48838. break;
  48839. case 'left':
  48840. x = 'xMin';
  48841. }
  48842. switch (markerOptions.verticalAlign) { // eslint-disable-line default-case
  48843. case 'top':
  48844. y = 'yMin';
  48845. break;
  48846. case 'bottom':
  48847. y = 'yMax';
  48848. }
  48849. return {
  48850. x: x ? bb[x] : (bb.xMin + bb.xMax) / 2,
  48851. y: y ? bb[y] : (bb.yMin + bb.yMax) / 2
  48852. };
  48853. },
  48854. /**
  48855. * Utility to get the angle from one point to another.
  48856. *
  48857. * @private
  48858. * @function Highcharts.Point#getRadiansToVector
  48859. *
  48860. * @param {Highcharts.PositionObject} v1
  48861. * The first vector, as an object with x/y properties.
  48862. *
  48863. * @param {Highcharts.PositionObject} v2
  48864. * The second vector, as an object with x/y properties.
  48865. *
  48866. * @return {number}
  48867. * The angle in degrees
  48868. */
  48869. getRadiansToVector: function (v1, v2) {
  48870. var box;
  48871. if (!defined(v2)) {
  48872. box = getPointBB(this);
  48873. if (box) {
  48874. v2 = {
  48875. x: (box.xMin + box.xMax) / 2,
  48876. y: (box.yMin + box.yMax) / 2
  48877. };
  48878. }
  48879. }
  48880. return Math.atan2(v2.y - v1.y, v1.x - v2.x);
  48881. },
  48882. /**
  48883. * Utility to get the position of the marker, based on the path angle and
  48884. * the marker's radius.
  48885. *
  48886. * @private
  48887. * @function Highcharts.Point#getMarkerVector
  48888. *
  48889. * @param {number} radians
  48890. * The angle in radians from the point center to another vector.
  48891. *
  48892. * @param {number} markerRadius
  48893. * The radius of the marker, to calculate the additional distance to
  48894. * the center of the marker.
  48895. *
  48896. * @param {object} anchor
  48897. * The anchor point of the path and marker as an object with x/y
  48898. * properties.
  48899. *
  48900. * @return {object}
  48901. * The marker vector as an object with x/y properties.
  48902. */
  48903. getMarkerVector: function (radians, markerRadius, anchor) {
  48904. var twoPI = Math.PI * 2.0,
  48905. theta = radians,
  48906. bb = getPointBB(this),
  48907. rectWidth = bb.xMax - bb.xMin,
  48908. rectHeight = bb.yMax - bb.yMin,
  48909. rAtan = Math.atan2(rectHeight,
  48910. rectWidth),
  48911. tanTheta = 1,
  48912. leftOrRightRegion = false,
  48913. rectHalfWidth = rectWidth / 2.0,
  48914. rectHalfHeight = rectHeight / 2.0,
  48915. rectHorizontalCenter = bb.xMin + rectHalfWidth,
  48916. rectVerticalCenter = bb.yMin + rectHalfHeight,
  48917. edgePoint = {
  48918. x: rectHorizontalCenter,
  48919. y: rectVerticalCenter
  48920. },
  48921. markerPoint = {},
  48922. xFactor = 1,
  48923. yFactor = 1;
  48924. while (theta < -Math.PI) {
  48925. theta += twoPI;
  48926. }
  48927. while (theta > Math.PI) {
  48928. theta -= twoPI;
  48929. }
  48930. tanTheta = Math.tan(theta);
  48931. if ((theta > -rAtan) && (theta <= rAtan)) {
  48932. // Right side
  48933. yFactor = -1;
  48934. leftOrRightRegion = true;
  48935. }
  48936. else if (theta > rAtan && theta <= (Math.PI - rAtan)) {
  48937. // Top side
  48938. yFactor = -1;
  48939. }
  48940. else if (theta > (Math.PI - rAtan) || theta <= -(Math.PI - rAtan)) {
  48941. // Left side
  48942. xFactor = -1;
  48943. leftOrRightRegion = true;
  48944. }
  48945. else {
  48946. // Bottom side
  48947. xFactor = -1;
  48948. }
  48949. // Correct the edgePoint according to the placement of the marker
  48950. if (leftOrRightRegion) {
  48951. edgePoint.x += xFactor * (rectHalfWidth);
  48952. edgePoint.y += yFactor * (rectHalfWidth) * tanTheta;
  48953. }
  48954. else {
  48955. edgePoint.x += xFactor * (rectHeight / (2.0 * tanTheta));
  48956. edgePoint.y += yFactor * (rectHalfHeight);
  48957. }
  48958. if (anchor.x !== rectHorizontalCenter) {
  48959. edgePoint.x = anchor.x;
  48960. }
  48961. if (anchor.y !== rectVerticalCenter) {
  48962. edgePoint.y = anchor.y;
  48963. }
  48964. markerPoint.x = edgePoint.x + (markerRadius * Math.cos(theta));
  48965. markerPoint.y = edgePoint.y - (markerRadius * Math.sin(theta));
  48966. return markerPoint;
  48967. }
  48968. });
  48969. /**
  48970. * Warn if using legacy options. Copy the options over. Note that this will
  48971. * still break if using the legacy options in chart.update, addSeries etc.
  48972. * @private
  48973. */
  48974. function warnLegacy(chart) {
  48975. if (chart.options.pathfinder ||
  48976. chart.series.reduce(function (acc, series) {
  48977. if (series.options) {
  48978. merge(true, (series.options.connectors = series.options.connectors ||
  48979. {}), series.options.pathfinder);
  48980. }
  48981. return acc || series.options && series.options.pathfinder;
  48982. }, false)) {
  48983. merge(true, (chart.options.connectors = chart.options.connectors || {}), chart.options.pathfinder);
  48984. error('WARNING: Pathfinder options have been renamed. ' +
  48985. 'Use "chart.connectors" or "series.connectors" instead.');
  48986. }
  48987. }
  48988. return Connection;
  48989. });
  48990. _registerModule(_modules, 'Gantt/PathfinderAlgorithms.js', [_modules['Core/Utilities.js']], function (U) {
  48991. /* *
  48992. *
  48993. * (c) 2016 Highsoft AS
  48994. * Author: Øystein Moseng
  48995. *
  48996. * License: www.highcharts.com/license
  48997. *
  48998. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48999. *
  49000. * */
  49001. var extend = U.extend,
  49002. pick = U.pick;
  49003. var min = Math.min,
  49004. max = Math.max,
  49005. abs = Math.abs;
  49006. /**
  49007. * Get index of last obstacle before xMin. Employs a type of binary search, and
  49008. * thus requires that obstacles are sorted by xMin value.
  49009. *
  49010. * @private
  49011. * @function findLastObstacleBefore
  49012. *
  49013. * @param {Array<object>} obstacles
  49014. * Array of obstacles to search in.
  49015. *
  49016. * @param {number} xMin
  49017. * The xMin threshold.
  49018. *
  49019. * @param {number} [startIx]
  49020. * Starting index to search from. Must be within array range.
  49021. *
  49022. * @return {number}
  49023. * The index of the last obstacle element before xMin.
  49024. */
  49025. function findLastObstacleBefore(obstacles, xMin, startIx) {
  49026. var left = startIx || 0, // left limit
  49027. right = obstacles.length - 1, // right limit
  49028. min = xMin - 0.0000001, // Make sure we include all obstacles at xMin
  49029. cursor,
  49030. cmp;
  49031. while (left <= right) {
  49032. cursor = (right + left) >> 1;
  49033. cmp = min - obstacles[cursor].xMin;
  49034. if (cmp > 0) {
  49035. left = cursor + 1;
  49036. }
  49037. else if (cmp < 0) {
  49038. right = cursor - 1;
  49039. }
  49040. else {
  49041. return cursor;
  49042. }
  49043. }
  49044. return left > 0 ? left - 1 : 0;
  49045. }
  49046. /**
  49047. * Test if a point lays within an obstacle.
  49048. *
  49049. * @private
  49050. * @function pointWithinObstacle
  49051. *
  49052. * @param {object} obstacle
  49053. * Obstacle to test.
  49054. *
  49055. * @param {Highcharts.Point} point
  49056. * Point with x/y props.
  49057. *
  49058. * @return {boolean}
  49059. * Whether point is within the obstacle or not.
  49060. */
  49061. function pointWithinObstacle(obstacle, point) {
  49062. return (point.x <= obstacle.xMax &&
  49063. point.x >= obstacle.xMin &&
  49064. point.y <= obstacle.yMax &&
  49065. point.y >= obstacle.yMin);
  49066. }
  49067. /**
  49068. * Find the index of an obstacle that wraps around a point.
  49069. * Returns -1 if not found.
  49070. *
  49071. * @private
  49072. * @function findObstacleFromPoint
  49073. *
  49074. * @param {Array<object>} obstacles
  49075. * Obstacles to test.
  49076. *
  49077. * @param {Highcharts.Point} point
  49078. * Point with x/y props.
  49079. *
  49080. * @return {number}
  49081. * Ix of the obstacle in the array, or -1 if not found.
  49082. */
  49083. function findObstacleFromPoint(obstacles, point) {
  49084. var i = findLastObstacleBefore(obstacles,
  49085. point.x + 1) + 1;
  49086. while (i--) {
  49087. if (obstacles[i].xMax >= point.x &&
  49088. // optimization using lazy evaluation
  49089. pointWithinObstacle(obstacles[i], point)) {
  49090. return i;
  49091. }
  49092. }
  49093. return -1;
  49094. }
  49095. /**
  49096. * Get SVG path array from array of line segments.
  49097. *
  49098. * @private
  49099. * @function pathFromSegments
  49100. *
  49101. * @param {Array<object>} segments
  49102. * The segments to build the path from.
  49103. *
  49104. * @return {Highcharts.SVGPathArray}
  49105. * SVG path array as accepted by the SVG Renderer.
  49106. */
  49107. function pathFromSegments(segments) {
  49108. var path = [];
  49109. if (segments.length) {
  49110. path.push(['M', segments[0].start.x, segments[0].start.y]);
  49111. for (var i = 0; i < segments.length; ++i) {
  49112. path.push(['L', segments[i].end.x, segments[i].end.y]);
  49113. }
  49114. }
  49115. return path;
  49116. }
  49117. /**
  49118. * Limits obstacle max/mins in all directions to bounds. Modifies input
  49119. * obstacle.
  49120. *
  49121. * @private
  49122. * @function limitObstacleToBounds
  49123. *
  49124. * @param {object} obstacle
  49125. * Obstacle to limit.
  49126. *
  49127. * @param {object} bounds
  49128. * Bounds to use as limit.
  49129. *
  49130. * @return {void}
  49131. */
  49132. function limitObstacleToBounds(obstacle, bounds) {
  49133. obstacle.yMin = max(obstacle.yMin, bounds.yMin);
  49134. obstacle.yMax = min(obstacle.yMax, bounds.yMax);
  49135. obstacle.xMin = max(obstacle.xMin, bounds.xMin);
  49136. obstacle.xMax = min(obstacle.xMax, bounds.xMax);
  49137. }
  49138. /**
  49139. * Get an SVG path from a starting coordinate to an ending coordinate.
  49140. * Draws a straight line.
  49141. *
  49142. * @function Highcharts.Pathfinder.algorithms.straight
  49143. *
  49144. * @param {Highcharts.PositionObject} start
  49145. * Starting coordinate, object with x/y props.
  49146. *
  49147. * @param {Highcharts.PositionObject} end
  49148. * Ending coordinate, object with x/y props.
  49149. *
  49150. * @return {object}
  49151. * An object with the SVG path in Array form as accepted by the SVG
  49152. * renderer, as well as an array of new obstacles making up this
  49153. * path.
  49154. */
  49155. function straight(start, end) {
  49156. return {
  49157. path: [
  49158. ['M', start.x, start.y],
  49159. ['L', end.x, end.y]
  49160. ],
  49161. obstacles: [{ start: start, end: end }]
  49162. };
  49163. }
  49164. /**
  49165. * Find a path from a starting coordinate to an ending coordinate, using
  49166. * right angles only, and taking only starting/ending obstacle into
  49167. * consideration.
  49168. *
  49169. * @function Highcharts.Pathfinder.algorithms.simpleConnect
  49170. *
  49171. * @param {Highcharts.PositionObject} start
  49172. * Starting coordinate, object with x/y props.
  49173. *
  49174. * @param {Highcharts.PositionObject} end
  49175. * Ending coordinate, object with x/y props.
  49176. *
  49177. * @param {object} options
  49178. * Options for the algorithm:
  49179. * - chartObstacles: Array of chart obstacles to avoid
  49180. * - startDirectionX: Optional. True if starting in the X direction.
  49181. * If not provided, the algorithm starts in the direction that is
  49182. * the furthest between start/end.
  49183. *
  49184. * @return {object}
  49185. * An object with the SVG path in Array form as accepted by the SVG
  49186. * renderer, as well as an array of new obstacles making up this
  49187. * path.
  49188. */
  49189. var simpleConnect = extend(function (start,
  49190. end,
  49191. options) {
  49192. var segments = [],
  49193. endSegment,
  49194. dir = pick(options.startDirectionX,
  49195. abs(end.x - start.x) > abs(end.y - start.y)) ? 'x' : 'y',
  49196. chartObstacles = options.chartObstacles,
  49197. startObstacleIx = findObstacleFromPoint(chartObstacles,
  49198. start),
  49199. endObstacleIx = findObstacleFromPoint(chartObstacles,
  49200. end),
  49201. startObstacle,
  49202. endObstacle,
  49203. prevWaypoint,
  49204. waypoint,
  49205. waypoint2,
  49206. useMax,
  49207. endPoint;
  49208. // eslint-disable-next-line valid-jsdoc
  49209. /**
  49210. * Return a clone of a point with a property set from a target object,
  49211. * optionally with an offset
  49212. * @private
  49213. */
  49214. function copyFromPoint(from, fromKey, to, toKey, offset) {
  49215. var point = {
  49216. x: from.x,
  49217. y: from.y
  49218. };
  49219. point[fromKey] = to[toKey || fromKey] + (offset || 0);
  49220. return point;
  49221. }
  49222. // eslint-disable-next-line valid-jsdoc
  49223. /**
  49224. * Return waypoint outside obstacle.
  49225. * @private
  49226. */
  49227. function getMeOut(obstacle, point, direction) {
  49228. var useMax = abs(point[direction] - obstacle[direction + 'Min']) >
  49229. abs(point[direction] - obstacle[direction + 'Max']);
  49230. return copyFromPoint(point, direction, obstacle, direction + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1);
  49231. }
  49232. // Pull out end point
  49233. if (endObstacleIx > -1) {
  49234. endObstacle = chartObstacles[endObstacleIx];
  49235. waypoint = getMeOut(endObstacle, end, dir);
  49236. endSegment = {
  49237. start: waypoint,
  49238. end: end
  49239. };
  49240. endPoint = waypoint;
  49241. }
  49242. else {
  49243. endPoint = end;
  49244. }
  49245. // If an obstacle envelops the start point, add a segment to get out,
  49246. // and around it.
  49247. if (startObstacleIx > -1) {
  49248. startObstacle = chartObstacles[startObstacleIx];
  49249. waypoint = getMeOut(startObstacle, start, dir);
  49250. segments.push({
  49251. start: start,
  49252. end: waypoint
  49253. });
  49254. // If we are going back again, switch direction to get around start
  49255. // obstacle.
  49256. if (
  49257. // Going towards max from start:
  49258. waypoint[dir] >= start[dir] ===
  49259. // Going towards min to end:
  49260. waypoint[dir] >= endPoint[dir]) {
  49261. dir = dir === 'y' ? 'x' : 'y';
  49262. useMax = start[dir] < end[dir];
  49263. segments.push({
  49264. start: waypoint,
  49265. end: copyFromPoint(waypoint, dir, startObstacle, dir + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1)
  49266. });
  49267. // Switch direction again
  49268. dir = dir === 'y' ? 'x' : 'y';
  49269. }
  49270. }
  49271. // We are around the start obstacle. Go towards the end in one
  49272. // direction.
  49273. prevWaypoint = segments.length ?
  49274. segments[segments.length - 1].end :
  49275. start;
  49276. waypoint = copyFromPoint(prevWaypoint, dir, endPoint);
  49277. segments.push({
  49278. start: prevWaypoint,
  49279. end: waypoint
  49280. });
  49281. // Final run to end point in the other direction
  49282. dir = dir === 'y' ? 'x' : 'y';
  49283. waypoint2 = copyFromPoint(waypoint, dir, endPoint);
  49284. segments.push({
  49285. start: waypoint,
  49286. end: waypoint2
  49287. });
  49288. // Finally add the endSegment
  49289. segments.push(endSegment);
  49290. return {
  49291. path: pathFromSegments(segments),
  49292. obstacles: segments
  49293. };
  49294. }, {
  49295. requiresObstacles: true
  49296. });
  49297. /**
  49298. * Find a path from a starting coordinate to an ending coordinate, taking
  49299. * obstacles into consideration. Might not always find the optimal path,
  49300. * but is fast, and usually good enough.
  49301. *
  49302. * @function Highcharts.Pathfinder.algorithms.fastAvoid
  49303. *
  49304. * @param {Highcharts.PositionObject} start
  49305. * Starting coordinate, object with x/y props.
  49306. *
  49307. * @param {Highcharts.PositionObject} end
  49308. * Ending coordinate, object with x/y props.
  49309. *
  49310. * @param {object} options
  49311. * Options for the algorithm.
  49312. * - chartObstacles: Array of chart obstacles to avoid
  49313. * - lineObstacles: Array of line obstacles to jump over
  49314. * - obstacleMetrics: Object with metrics of chartObstacles cached
  49315. * - hardBounds: Hard boundaries to not cross
  49316. * - obstacleOptions: Options for the obstacles, including margin
  49317. * - startDirectionX: Optional. True if starting in the X direction.
  49318. * If not provided, the algorithm starts in the
  49319. * direction that is the furthest between
  49320. * start/end.
  49321. *
  49322. * @return {object}
  49323. * An object with the SVG path in Array form as accepted by the SVG
  49324. * renderer, as well as an array of new obstacles making up this
  49325. * path.
  49326. */
  49327. var fastAvoid = extend(function (start,
  49328. end,
  49329. options) {
  49330. /*
  49331. Algorithm rules/description
  49332. - Find initial direction
  49333. - Determine soft/hard max for each direction.
  49334. - Move along initial direction until obstacle.
  49335. - Change direction.
  49336. - If hitting obstacle,
  49337. first try to change length of previous line
  49338. before changing direction again.
  49339. Soft min/max x = start/destination x +/- widest obstacle + margin
  49340. Soft min/max y = start/destination y +/- tallest obstacle + margin
  49341. @todo:
  49342. - Make retrospective,
  49343. try changing prev segment to reduce
  49344. corners
  49345. - Fix logic for breaking out of end-points - not always picking
  49346. the best direction currently
  49347. - When going around the end obstacle we should not always go the
  49348. shortest route,
  49349. rather pick the one closer to the end point
  49350. */
  49351. var dirIsX = pick(options.startDirectionX,
  49352. abs(end.x - start.x) > abs(end.y - start.y)),
  49353. dir = dirIsX ? 'x' : 'y',
  49354. segments,
  49355. useMax,
  49356. extractedEndPoint,
  49357. endSegments = [],
  49358. forceObstacleBreak = false, // Used in clearPathTo to keep track of
  49359. // when to force break through an obstacle.
  49360. // Boundaries to stay within. If beyond soft boundary, prefer to
  49361. // change direction ASAP. If at hard max, always change immediately.
  49362. metrics = options.obstacleMetrics,
  49363. softMinX = min(start.x,
  49364. end.x) - metrics.maxWidth - 10,
  49365. softMaxX = max(start.x,
  49366. end.x) + metrics.maxWidth + 10,
  49367. softMinY = min(start.y,
  49368. end.y) - metrics.maxHeight - 10,
  49369. softMaxY = max(start.y,
  49370. end.y) + metrics.maxHeight + 10,
  49371. // Obstacles
  49372. chartObstacles = options.chartObstacles,
  49373. startObstacleIx = findLastObstacleBefore(chartObstacles,
  49374. softMinX),
  49375. endObstacleIx = findLastObstacleBefore(chartObstacles,
  49376. softMaxX);
  49377. // eslint-disable-next-line valid-jsdoc
  49378. /**
  49379. * How far can you go between two points before hitting an obstacle?
  49380. * Does not work for diagonal lines (because it doesn't have to).
  49381. * @private
  49382. */
  49383. function pivotPoint(fromPoint, toPoint, directionIsX) {
  49384. var firstPoint,
  49385. lastPoint,
  49386. highestPoint,
  49387. lowestPoint,
  49388. i,
  49389. searchDirection = fromPoint.x < toPoint.x ? 1 : -1;
  49390. if (fromPoint.x < toPoint.x) {
  49391. firstPoint = fromPoint;
  49392. lastPoint = toPoint;
  49393. }
  49394. else {
  49395. firstPoint = toPoint;
  49396. lastPoint = fromPoint;
  49397. }
  49398. if (fromPoint.y < toPoint.y) {
  49399. lowestPoint = fromPoint;
  49400. highestPoint = toPoint;
  49401. }
  49402. else {
  49403. lowestPoint = toPoint;
  49404. highestPoint = fromPoint;
  49405. }
  49406. // Go through obstacle range in reverse if toPoint is before
  49407. // fromPoint in the X-dimension.
  49408. i = searchDirection < 0 ?
  49409. // Searching backwards, start at last obstacle before last point
  49410. min(findLastObstacleBefore(chartObstacles, lastPoint.x), chartObstacles.length - 1) :
  49411. // Forwards. Since we're not sorted by xMax, we have to look
  49412. // at all obstacles.
  49413. 0;
  49414. // Go through obstacles in this X range
  49415. while (chartObstacles[i] && (searchDirection > 0 && chartObstacles[i].xMin <= lastPoint.x ||
  49416. searchDirection < 0 && chartObstacles[i].xMax >= firstPoint.x)) {
  49417. // If this obstacle is between from and to points in a straight
  49418. // line, pivot at the intersection.
  49419. if (chartObstacles[i].xMin <= lastPoint.x &&
  49420. chartObstacles[i].xMax >= firstPoint.x &&
  49421. chartObstacles[i].yMin <= highestPoint.y &&
  49422. chartObstacles[i].yMax >= lowestPoint.y) {
  49423. if (directionIsX) {
  49424. return {
  49425. y: fromPoint.y,
  49426. x: fromPoint.x < toPoint.x ?
  49427. chartObstacles[i].xMin - 1 :
  49428. chartObstacles[i].xMax + 1,
  49429. obstacle: chartObstacles[i]
  49430. };
  49431. }
  49432. // else ...
  49433. return {
  49434. x: fromPoint.x,
  49435. y: fromPoint.y < toPoint.y ?
  49436. chartObstacles[i].yMin - 1 :
  49437. chartObstacles[i].yMax + 1,
  49438. obstacle: chartObstacles[i]
  49439. };
  49440. }
  49441. i += searchDirection;
  49442. }
  49443. return toPoint;
  49444. }
  49445. /**
  49446. * Decide in which direction to dodge or get out of an obstacle.
  49447. * Considers desired direction, which way is shortest, soft and hard
  49448. * bounds.
  49449. *
  49450. * (? Returns a string, either xMin, xMax, yMin or yMax.)
  49451. *
  49452. * @private
  49453. * @function
  49454. *
  49455. * @param {object} obstacle
  49456. * Obstacle to dodge/escape.
  49457. *
  49458. * @param {object} fromPoint
  49459. * Point with x/y props that's dodging/escaping.
  49460. *
  49461. * @param {object} toPoint
  49462. * Goal point.
  49463. *
  49464. * @param {boolean} dirIsX
  49465. * Dodge in X dimension.
  49466. *
  49467. * @param {object} bounds
  49468. * Hard and soft boundaries.
  49469. *
  49470. * @return {boolean}
  49471. * Use max or not.
  49472. */
  49473. function getDodgeDirection(obstacle, fromPoint, toPoint, dirIsX, bounds) {
  49474. var softBounds = bounds.soft, hardBounds = bounds.hard, dir = dirIsX ? 'x' : 'y', toPointMax = { x: fromPoint.x, y: fromPoint.y }, toPointMin = { x: fromPoint.x, y: fromPoint.y }, minPivot, maxPivot, maxOutOfSoftBounds = obstacle[dir + 'Max'] >=
  49475. softBounds[dir + 'Max'], minOutOfSoftBounds = obstacle[dir + 'Min'] <=
  49476. softBounds[dir + 'Min'], maxOutOfHardBounds = obstacle[dir + 'Max'] >=
  49477. hardBounds[dir + 'Max'], minOutOfHardBounds = obstacle[dir + 'Min'] <=
  49478. hardBounds[dir + 'Min'],
  49479. // Find out if we should prefer one direction over the other if
  49480. // we can choose freely
  49481. minDistance = abs(obstacle[dir + 'Min'] - fromPoint[dir]), maxDistance = abs(obstacle[dir + 'Max'] - fromPoint[dir]),
  49482. // If it's a small difference, pick the one leading towards dest
  49483. // point. Otherwise pick the shortest distance
  49484. useMax = abs(minDistance - maxDistance) < 10 ?
  49485. fromPoint[dir] < toPoint[dir] :
  49486. maxDistance < minDistance;
  49487. // Check if we hit any obstacles trying to go around in either
  49488. // direction.
  49489. toPointMin[dir] = obstacle[dir + 'Min'];
  49490. toPointMax[dir] = obstacle[dir + 'Max'];
  49491. minPivot = pivotPoint(fromPoint, toPointMin, dirIsX)[dir] !==
  49492. toPointMin[dir];
  49493. maxPivot = pivotPoint(fromPoint, toPointMax, dirIsX)[dir] !==
  49494. toPointMax[dir];
  49495. useMax = minPivot ?
  49496. (maxPivot ? useMax : true) :
  49497. (maxPivot ? false : useMax);
  49498. // useMax now contains our preferred choice, bounds not taken into
  49499. // account. If both or neither direction is out of bounds we want to
  49500. // use this.
  49501. // Deal with soft bounds
  49502. useMax = minOutOfSoftBounds ?
  49503. (maxOutOfSoftBounds ? useMax : true) : // Out on min
  49504. (maxOutOfSoftBounds ? false : useMax); // Not out on min
  49505. // Deal with hard bounds
  49506. useMax = minOutOfHardBounds ?
  49507. (maxOutOfHardBounds ? useMax : true) : // Out on min
  49508. (maxOutOfHardBounds ? false : useMax); // Not out on min
  49509. return useMax;
  49510. }
  49511. // eslint-disable-next-line valid-jsdoc
  49512. /**
  49513. * Find a clear path between point.
  49514. * @private
  49515. */
  49516. function clearPathTo(fromPoint, toPoint, dirIsX) {
  49517. // Don't waste time if we've hit goal
  49518. if (fromPoint.x === toPoint.x && fromPoint.y === toPoint.y) {
  49519. return [];
  49520. }
  49521. var dir = dirIsX ? 'x' : 'y',
  49522. pivot,
  49523. segments,
  49524. waypoint,
  49525. waypointUseMax,
  49526. envelopingObstacle,
  49527. secondEnvelopingObstacle,
  49528. envelopWaypoint,
  49529. obstacleMargin = options.obstacleOptions.margin,
  49530. bounds = {
  49531. soft: {
  49532. xMin: softMinX,
  49533. xMax: softMaxX,
  49534. yMin: softMinY,
  49535. yMax: softMaxY
  49536. },
  49537. hard: options.hardBounds
  49538. };
  49539. // If fromPoint is inside an obstacle we have a problem. Break out
  49540. // by just going to the outside of this obstacle. We prefer to go to
  49541. // the nearest edge in the chosen direction.
  49542. envelopingObstacle =
  49543. findObstacleFromPoint(chartObstacles, fromPoint);
  49544. if (envelopingObstacle > -1) {
  49545. envelopingObstacle = chartObstacles[envelopingObstacle];
  49546. waypointUseMax = getDodgeDirection(envelopingObstacle, fromPoint, toPoint, dirIsX, bounds);
  49547. // Cut obstacle to hard bounds to make sure we stay within
  49548. limitObstacleToBounds(envelopingObstacle, options.hardBounds);
  49549. envelopWaypoint = dirIsX ? {
  49550. y: fromPoint.y,
  49551. x: envelopingObstacle[waypointUseMax ? 'xMax' : 'xMin'] +
  49552. (waypointUseMax ? 1 : -1)
  49553. } : {
  49554. x: fromPoint.x,
  49555. y: envelopingObstacle[waypointUseMax ? 'yMax' : 'yMin'] +
  49556. (waypointUseMax ? 1 : -1)
  49557. };
  49558. // If we crashed into another obstacle doing this, we put the
  49559. // waypoint between them instead
  49560. secondEnvelopingObstacle = findObstacleFromPoint(chartObstacles, envelopWaypoint);
  49561. if (secondEnvelopingObstacle > -1) {
  49562. secondEnvelopingObstacle = chartObstacles[secondEnvelopingObstacle];
  49563. // Cut obstacle to hard bounds
  49564. limitObstacleToBounds(secondEnvelopingObstacle, options.hardBounds);
  49565. // Modify waypoint to lay between obstacles
  49566. envelopWaypoint[dir] = waypointUseMax ? max(envelopingObstacle[dir + 'Max'] - obstacleMargin + 1, (secondEnvelopingObstacle[dir + 'Min'] +
  49567. envelopingObstacle[dir + 'Max']) / 2) :
  49568. min((envelopingObstacle[dir + 'Min'] + obstacleMargin - 1), ((secondEnvelopingObstacle[dir + 'Max'] +
  49569. envelopingObstacle[dir + 'Min']) / 2));
  49570. // We are not going anywhere. If this happens for the first
  49571. // time, do nothing. Otherwise, try to go to the extreme of
  49572. // the obstacle pair in the current direction.
  49573. if (fromPoint.x === envelopWaypoint.x &&
  49574. fromPoint.y === envelopWaypoint.y) {
  49575. if (forceObstacleBreak) {
  49576. envelopWaypoint[dir] = waypointUseMax ?
  49577. max(envelopingObstacle[dir + 'Max'], secondEnvelopingObstacle[dir + 'Max']) + 1 :
  49578. min(envelopingObstacle[dir + 'Min'], secondEnvelopingObstacle[dir + 'Min']) - 1;
  49579. }
  49580. // Toggle on if off, and the opposite
  49581. forceObstacleBreak = !forceObstacleBreak;
  49582. }
  49583. else {
  49584. // This point is not identical to previous.
  49585. // Clear break trigger.
  49586. forceObstacleBreak = false;
  49587. }
  49588. }
  49589. segments = [{
  49590. start: fromPoint,
  49591. end: envelopWaypoint
  49592. }];
  49593. }
  49594. else { // If not enveloping, use standard pivot calculation
  49595. pivot = pivotPoint(fromPoint, {
  49596. x: dirIsX ? toPoint.x : fromPoint.x,
  49597. y: dirIsX ? fromPoint.y : toPoint.y
  49598. }, dirIsX);
  49599. segments = [{
  49600. start: fromPoint,
  49601. end: {
  49602. x: pivot.x,
  49603. y: pivot.y
  49604. }
  49605. }];
  49606. // Pivot before goal, use a waypoint to dodge obstacle
  49607. if (pivot[dirIsX ? 'x' : 'y'] !== toPoint[dirIsX ? 'x' : 'y']) {
  49608. // Find direction of waypoint
  49609. waypointUseMax = getDodgeDirection(pivot.obstacle, pivot, toPoint, !dirIsX, bounds);
  49610. // Cut waypoint to hard bounds
  49611. limitObstacleToBounds(pivot.obstacle, options.hardBounds);
  49612. waypoint = {
  49613. x: dirIsX ?
  49614. pivot.x :
  49615. pivot.obstacle[waypointUseMax ? 'xMax' : 'xMin'] +
  49616. (waypointUseMax ? 1 : -1),
  49617. y: dirIsX ?
  49618. pivot.obstacle[waypointUseMax ? 'yMax' : 'yMin'] +
  49619. (waypointUseMax ? 1 : -1) :
  49620. pivot.y
  49621. };
  49622. // We're changing direction here, store that to make sure we
  49623. // also change direction when adding the last segment array
  49624. // after handling waypoint.
  49625. dirIsX = !dirIsX;
  49626. segments = segments.concat(clearPathTo({
  49627. x: pivot.x,
  49628. y: pivot.y
  49629. }, waypoint, dirIsX));
  49630. }
  49631. }
  49632. // Get segments for the other direction too
  49633. // Recursion is our friend
  49634. segments = segments.concat(clearPathTo(segments[segments.length - 1].end, toPoint, !dirIsX));
  49635. return segments;
  49636. }
  49637. // eslint-disable-next-line valid-jsdoc
  49638. /**
  49639. * Extract point to outside of obstacle in whichever direction is
  49640. * closest. Returns new point outside obstacle.
  49641. * @private
  49642. */
  49643. function extractFromObstacle(obstacle, point, goalPoint) {
  49644. var dirIsX = min(obstacle.xMax - point.x,
  49645. point.x - obstacle.xMin) <
  49646. min(obstacle.yMax - point.y,
  49647. point.y - obstacle.yMin),
  49648. bounds = {
  49649. soft: options.hardBounds,
  49650. hard: options.hardBounds
  49651. },
  49652. useMax = getDodgeDirection(obstacle,
  49653. point,
  49654. goalPoint,
  49655. dirIsX,
  49656. bounds);
  49657. return dirIsX ? {
  49658. y: point.y,
  49659. x: obstacle[useMax ? 'xMax' : 'xMin'] + (useMax ? 1 : -1)
  49660. } : {
  49661. x: point.x,
  49662. y: obstacle[useMax ? 'yMax' : 'yMin'] + (useMax ? 1 : -1)
  49663. };
  49664. }
  49665. // Cut the obstacle array to soft bounds for optimization in large
  49666. // datasets.
  49667. chartObstacles =
  49668. chartObstacles.slice(startObstacleIx, endObstacleIx + 1);
  49669. // If an obstacle envelops the end point, move it out of there and add
  49670. // a little segment to where it was.
  49671. if ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
  49672. extractedEndPoint = extractFromObstacle(chartObstacles[endObstacleIx], end, start);
  49673. endSegments.push({
  49674. end: end,
  49675. start: extractedEndPoint
  49676. });
  49677. end = extractedEndPoint;
  49678. }
  49679. // If it's still inside one or more obstacles, get out of there by
  49680. // force-moving towards the start point.
  49681. while ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
  49682. useMax = end[dir] - start[dir] < 0;
  49683. extractedEndPoint = {
  49684. x: end.x,
  49685. y: end.y
  49686. };
  49687. extractedEndPoint[dir] = chartObstacles[endObstacleIx][useMax ? dir + 'Max' : dir + 'Min'] + (useMax ? 1 : -1);
  49688. endSegments.push({
  49689. end: end,
  49690. start: extractedEndPoint
  49691. });
  49692. end = extractedEndPoint;
  49693. }
  49694. // Find the path
  49695. segments = clearPathTo(start, end, dirIsX);
  49696. // Add the end-point segments
  49697. segments = segments.concat(endSegments.reverse());
  49698. return {
  49699. path: pathFromSegments(segments),
  49700. obstacles: segments
  49701. };
  49702. }, {
  49703. requiresObstacles: true
  49704. });
  49705. // Define the available pathfinding algorithms.
  49706. // Algorithms take up to 3 arguments: starting point, ending point, and an
  49707. // options object.
  49708. var algorithms = {
  49709. fastAvoid: fastAvoid,
  49710. straight: straight,
  49711. simpleConnect: simpleConnect
  49712. };
  49713. return algorithms;
  49714. });
  49715. _registerModule(_modules, 'Gantt/Pathfinder.js', [_modules['Gantt/Connection.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js'], _modules['Gantt/PathfinderAlgorithms.js']], function (Connection, Chart, H, O, Point, U, pathfinderAlgorithms) {
  49716. /* *
  49717. *
  49718. * (c) 2016 Highsoft AS
  49719. * Authors: Øystein Moseng, Lars A. V. Cabrera
  49720. *
  49721. * License: www.highcharts.com/license
  49722. *
  49723. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49724. *
  49725. * */
  49726. /**
  49727. * The default pathfinder algorithm to use for a chart. It is possible to define
  49728. * your own algorithms by adding them to the
  49729. * `Highcharts.Pathfinder.prototype.algorithms`
  49730. * object before the chart has been created.
  49731. *
  49732. * The default algorithms are as follows:
  49733. *
  49734. * `straight`: Draws a straight line between the connecting
  49735. * points. Does not avoid other points when drawing.
  49736. *
  49737. * `simpleConnect`: Finds a path between the points using right angles
  49738. * only. Takes only starting/ending points into
  49739. * account, and will not avoid other points.
  49740. *
  49741. * `fastAvoid`: Finds a path between the points using right angles
  49742. * only. Will attempt to avoid other points, but its
  49743. * focus is performance over accuracy. Works well with
  49744. * less dense datasets.
  49745. *
  49746. * @typedef {"fastAvoid"|"simpleConnect"|"straight"|string} Highcharts.PathfinderTypeValue
  49747. */
  49748. ''; // detach doclets above
  49749. var defaultOptions = O.defaultOptions;
  49750. var addEvent = U.addEvent,
  49751. defined = U.defined,
  49752. error = U.error,
  49753. extend = U.extend,
  49754. merge = U.merge,
  49755. objectEach = U.objectEach,
  49756. pick = U.pick,
  49757. splat = U.splat;
  49758. var deg2rad = H.deg2rad,
  49759. max = Math.max,
  49760. min = Math.min;
  49761. /*
  49762. @todo:
  49763. - Document how to write your own algorithms
  49764. - Consider adding a Point.pathTo method that wraps creating a connection
  49765. and rendering it
  49766. */
  49767. // Set default Pathfinder options
  49768. extend(defaultOptions, {
  49769. /**
  49770. * The Pathfinder module allows you to define connections between any two
  49771. * points, represented as lines - optionally with markers for the start
  49772. * and/or end points. Multiple algorithms are available for calculating how
  49773. * the connecting lines are drawn.
  49774. *
  49775. * Connector functionality requires Highcharts Gantt to be loaded. In Gantt
  49776. * charts, the connectors are used to draw dependencies between tasks.
  49777. *
  49778. * @see [dependency](series.gantt.data.dependency)
  49779. *
  49780. * @sample gantt/pathfinder/demo
  49781. * Pathfinder connections
  49782. *
  49783. * @declare Highcharts.ConnectorsOptions
  49784. * @product gantt
  49785. * @optionparent connectors
  49786. */
  49787. connectors: {
  49788. /**
  49789. * Enable connectors for this chart. Requires Highcharts Gantt.
  49790. *
  49791. * @type {boolean}
  49792. * @default true
  49793. * @since 6.2.0
  49794. * @apioption connectors.enabled
  49795. */
  49796. /**
  49797. * Set the default dash style for this chart's connecting lines.
  49798. *
  49799. * @type {string}
  49800. * @default solid
  49801. * @since 6.2.0
  49802. * @apioption connectors.dashStyle
  49803. */
  49804. /**
  49805. * Set the default color for this chart's Pathfinder connecting lines.
  49806. * Defaults to the color of the point being connected.
  49807. *
  49808. * @type {Highcharts.ColorString}
  49809. * @since 6.2.0
  49810. * @apioption connectors.lineColor
  49811. */
  49812. /**
  49813. * Set the default pathfinder margin to use, in pixels. Some Pathfinder
  49814. * algorithms attempt to avoid obstacles, such as other points in the
  49815. * chart. These algorithms use this margin to determine how close lines
  49816. * can be to an obstacle. The default is to compute this automatically
  49817. * from the size of the obstacles in the chart.
  49818. *
  49819. * To draw connecting lines close to existing points, set this to a low
  49820. * number. For more space around existing points, set this number
  49821. * higher.
  49822. *
  49823. * @sample gantt/pathfinder/algorithm-margin
  49824. * Small algorithmMargin
  49825. *
  49826. * @type {number}
  49827. * @since 6.2.0
  49828. * @apioption connectors.algorithmMargin
  49829. */
  49830. /**
  49831. * Set the default pathfinder algorithm to use for this chart. It is
  49832. * possible to define your own algorithms by adding them to the
  49833. * Highcharts.Pathfinder.prototype.algorithms object before the chart
  49834. * has been created.
  49835. *
  49836. * The default algorithms are as follows:
  49837. *
  49838. * `straight`: Draws a straight line between the connecting
  49839. * points. Does not avoid other points when drawing.
  49840. *
  49841. * `simpleConnect`: Finds a path between the points using right angles
  49842. * only. Takes only starting/ending points into
  49843. * account, and will not avoid other points.
  49844. *
  49845. * `fastAvoid`: Finds a path between the points using right angles
  49846. * only. Will attempt to avoid other points, but its
  49847. * focus is performance over accuracy. Works well with
  49848. * less dense datasets.
  49849. *
  49850. * Default value: `straight` is used as default for most series types,
  49851. * while `simpleConnect` is used as default for Gantt series, to show
  49852. * dependencies between points.
  49853. *
  49854. * @sample gantt/pathfinder/demo
  49855. * Different types used
  49856. *
  49857. * @type {Highcharts.PathfinderTypeValue}
  49858. * @default undefined
  49859. * @since 6.2.0
  49860. */
  49861. type: 'straight',
  49862. /**
  49863. * Set the default pixel width for this chart's Pathfinder connecting
  49864. * lines.
  49865. *
  49866. * @since 6.2.0
  49867. */
  49868. lineWidth: 1,
  49869. /**
  49870. * Marker options for this chart's Pathfinder connectors. Note that
  49871. * this option is overridden by the `startMarker` and `endMarker`
  49872. * options.
  49873. *
  49874. * @declare Highcharts.ConnectorsMarkerOptions
  49875. * @since 6.2.0
  49876. */
  49877. marker: {
  49878. /**
  49879. * Set the radius of the connector markers. The default is
  49880. * automatically computed based on the algorithmMargin setting.
  49881. *
  49882. * Setting marker.width and marker.height will override this
  49883. * setting.
  49884. *
  49885. * @type {number}
  49886. * @since 6.2.0
  49887. * @apioption connectors.marker.radius
  49888. */
  49889. /**
  49890. * Set the width of the connector markers. If not supplied, this
  49891. * is inferred from the marker radius.
  49892. *
  49893. * @type {number}
  49894. * @since 6.2.0
  49895. * @apioption connectors.marker.width
  49896. */
  49897. /**
  49898. * Set the height of the connector markers. If not supplied, this
  49899. * is inferred from the marker radius.
  49900. *
  49901. * @type {number}
  49902. * @since 6.2.0
  49903. * @apioption connectors.marker.height
  49904. */
  49905. /**
  49906. * Set the color of the connector markers. By default this is the
  49907. * same as the connector color.
  49908. *
  49909. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49910. * @since 6.2.0
  49911. * @apioption connectors.marker.color
  49912. */
  49913. /**
  49914. * Set the line/border color of the connector markers. By default
  49915. * this is the same as the marker color.
  49916. *
  49917. * @type {Highcharts.ColorString}
  49918. * @since 6.2.0
  49919. * @apioption connectors.marker.lineColor
  49920. */
  49921. /**
  49922. * Enable markers for the connectors.
  49923. */
  49924. enabled: false,
  49925. /**
  49926. * Horizontal alignment of the markers relative to the points.
  49927. *
  49928. * @type {Highcharts.AlignValue}
  49929. */
  49930. align: 'center',
  49931. /**
  49932. * Vertical alignment of the markers relative to the points.
  49933. *
  49934. * @type {Highcharts.VerticalAlignValue}
  49935. */
  49936. verticalAlign: 'middle',
  49937. /**
  49938. * Whether or not to draw the markers inside the points.
  49939. */
  49940. inside: false,
  49941. /**
  49942. * Set the line/border width of the pathfinder markers.
  49943. */
  49944. lineWidth: 1
  49945. },
  49946. /**
  49947. * Marker options specific to the start markers for this chart's
  49948. * Pathfinder connectors. Overrides the generic marker options.
  49949. *
  49950. * @declare Highcharts.ConnectorsStartMarkerOptions
  49951. * @extends connectors.marker
  49952. * @since 6.2.0
  49953. */
  49954. startMarker: {
  49955. /**
  49956. * Set the symbol of the connector start markers.
  49957. */
  49958. symbol: 'diamond'
  49959. },
  49960. /**
  49961. * Marker options specific to the end markers for this chart's
  49962. * Pathfinder connectors. Overrides the generic marker options.
  49963. *
  49964. * @declare Highcharts.ConnectorsEndMarkerOptions
  49965. * @extends connectors.marker
  49966. * @since 6.2.0
  49967. */
  49968. endMarker: {
  49969. /**
  49970. * Set the symbol of the connector end markers.
  49971. */
  49972. symbol: 'arrow-filled'
  49973. }
  49974. }
  49975. });
  49976. /**
  49977. * Override Pathfinder connector options for a series. Requires Highcharts Gantt
  49978. * to be loaded.
  49979. *
  49980. * @declare Highcharts.SeriesConnectorsOptionsObject
  49981. * @extends connectors
  49982. * @since 6.2.0
  49983. * @excluding enabled, algorithmMargin
  49984. * @product gantt
  49985. * @apioption plotOptions.series.connectors
  49986. */
  49987. /**
  49988. * Connect to a point. This option can be either a string, referring to the ID
  49989. * of another point, or an object, or an array of either. If the option is an
  49990. * array, each element defines a connection.
  49991. *
  49992. * @sample gantt/pathfinder/demo
  49993. * Different connection types
  49994. *
  49995. * @declare Highcharts.XrangePointConnectorsOptionsObject
  49996. * @type {string|Array<string|*>|*}
  49997. * @extends plotOptions.series.connectors
  49998. * @since 6.2.0
  49999. * @excluding enabled
  50000. * @product gantt
  50001. * @requires highcharts-gantt
  50002. * @apioption series.xrange.data.connect
  50003. */
  50004. /**
  50005. * The ID of the point to connect to.
  50006. *
  50007. * @type {string}
  50008. * @since 6.2.0
  50009. * @product gantt
  50010. * @apioption series.xrange.data.connect.to
  50011. */
  50012. /**
  50013. * Get point bounding box using plotX/plotY and shapeArgs. If using
  50014. * graphic.getBBox() directly, the bbox will be affected by animation.
  50015. *
  50016. * @private
  50017. * @function
  50018. *
  50019. * @param {Highcharts.Point} point
  50020. * The point to get BB of.
  50021. *
  50022. * @return {Highcharts.Dictionary<number>|null}
  50023. * Result xMax, xMin, yMax, yMin.
  50024. */
  50025. function getPointBB(point) {
  50026. var shapeArgs = point.shapeArgs,
  50027. bb;
  50028. // Prefer using shapeArgs (columns)
  50029. if (shapeArgs) {
  50030. return {
  50031. xMin: shapeArgs.x,
  50032. xMax: shapeArgs.x + shapeArgs.width,
  50033. yMin: shapeArgs.y,
  50034. yMax: shapeArgs.y + shapeArgs.height
  50035. };
  50036. }
  50037. // Otherwise use plotX/plotY and bb
  50038. bb = point.graphic && point.graphic.getBBox();
  50039. return bb ? {
  50040. xMin: point.plotX - bb.width / 2,
  50041. xMax: point.plotX + bb.width / 2,
  50042. yMin: point.plotY - bb.height / 2,
  50043. yMax: point.plotY + bb.height / 2
  50044. } : null;
  50045. }
  50046. /**
  50047. * Calculate margin to place around obstacles for the pathfinder in pixels.
  50048. * Returns a minimum of 1 pixel margin.
  50049. *
  50050. * @private
  50051. * @function
  50052. *
  50053. * @param {Array<object>} obstacles
  50054. * Obstacles to calculate margin from.
  50055. *
  50056. * @return {number}
  50057. * The calculated margin in pixels. At least 1.
  50058. */
  50059. function calculateObstacleMargin(obstacles) {
  50060. var len = obstacles.length,
  50061. i = 0,
  50062. j,
  50063. obstacleDistance,
  50064. distances = [],
  50065. // Compute smallest distance between two rectangles
  50066. distance = function (a,
  50067. b,
  50068. bbMargin) {
  50069. // Count the distance even if we are slightly off
  50070. var margin = pick(bbMargin, 10),
  50071. yOverlap = a.yMax + margin > b.yMin - margin &&
  50072. a.yMin - margin < b.yMax + margin,
  50073. xOverlap = a.xMax + margin > b.xMin - margin &&
  50074. a.xMin - margin < b.xMax + margin,
  50075. xDistance = yOverlap ? (a.xMin > b.xMax ? a.xMin - b.xMax : b.xMin - a.xMax) : Infinity,
  50076. yDistance = xOverlap ? (a.yMin > b.yMax ? a.yMin - b.yMax : b.yMin - a.yMax) : Infinity;
  50077. // If the rectangles collide, try recomputing with smaller margin.
  50078. // If they collide anyway, discard the obstacle.
  50079. if (xOverlap && yOverlap) {
  50080. return (margin ?
  50081. distance(a, b, Math.floor(margin / 2)) :
  50082. Infinity);
  50083. }
  50084. return min(xDistance, yDistance);
  50085. };
  50086. // Go over all obstacles and compare them to the others.
  50087. for (; i < len; ++i) {
  50088. // Compare to all obstacles ahead. We will already have compared this
  50089. // obstacle to the ones before.
  50090. for (j = i + 1; j < len; ++j) {
  50091. obstacleDistance = distance(obstacles[i], obstacles[j]);
  50092. // TODO: Magic number 80
  50093. if (obstacleDistance < 80) { // Ignore large distances
  50094. distances.push(obstacleDistance);
  50095. }
  50096. }
  50097. }
  50098. // Ensure we always have at least one value, even in very spaceous charts
  50099. distances.push(80);
  50100. return max(Math.floor(distances.sort(function (a, b) {
  50101. return (a - b);
  50102. })[
  50103. // Discard first 10% of the relevant distances, and then grab
  50104. // the smallest one.
  50105. Math.floor(distances.length / 10)] / 2 - 1 // Divide the distance by 2 and subtract 1.
  50106. ), 1 // 1 is the minimum margin
  50107. );
  50108. }
  50109. /* eslint-disable no-invalid-this, valid-jsdoc */
  50110. /**
  50111. * The Pathfinder class.
  50112. *
  50113. * @private
  50114. * @class
  50115. * @name Highcharts.Pathfinder
  50116. *
  50117. * @param {Highcharts.Chart} chart
  50118. * The chart to operate on.
  50119. */
  50120. var Pathfinder = /** @class */ (function () {
  50121. function Pathfinder(chart) {
  50122. /* *
  50123. *
  50124. * Properties
  50125. *
  50126. * */
  50127. this.chart = void 0;
  50128. this.chartObstacles = void 0;
  50129. this.chartObstacleMetrics = void 0;
  50130. this.connections = void 0;
  50131. this.group = void 0;
  50132. this.lineObstacles = void 0;
  50133. this.init(chart);
  50134. }
  50135. /**
  50136. * @name Highcharts.Pathfinder#algorithms
  50137. * @type {Highcharts.Dictionary<Function>}
  50138. */
  50139. /**
  50140. * Initialize the Pathfinder object.
  50141. *
  50142. * @function Highcharts.Pathfinder#init
  50143. *
  50144. * @param {Highcharts.Chart} chart
  50145. * The chart context.
  50146. */
  50147. Pathfinder.prototype.init = function (chart) {
  50148. // Initialize pathfinder with chart context
  50149. this.chart = chart;
  50150. // Init connection reference list
  50151. this.connections = [];
  50152. // Recalculate paths/obstacles on chart redraw
  50153. addEvent(chart, 'redraw', function () {
  50154. this.pathfinder.update();
  50155. });
  50156. };
  50157. /**
  50158. * Update Pathfinder connections from scratch.
  50159. *
  50160. * @function Highcharts.Pathfinder#update
  50161. *
  50162. * @param {boolean} [deferRender]
  50163. * Whether or not to defer rendering of connections until
  50164. * series.afterAnimate event has fired. Used on first render.
  50165. */
  50166. Pathfinder.prototype.update = function (deferRender) {
  50167. var chart = this.chart,
  50168. pathfinder = this,
  50169. oldConnections = pathfinder.connections;
  50170. // Rebuild pathfinder connections from options
  50171. pathfinder.connections = [];
  50172. chart.series.forEach(function (series) {
  50173. if (series.visible && !series.options.isInternal) {
  50174. series.points.forEach(function (point) {
  50175. var to,
  50176. connects = (point.options &&
  50177. point.options.connect &&
  50178. splat(point.options.connect));
  50179. if (point.visible && point.isInside !== false && connects) {
  50180. connects.forEach(function (connect) {
  50181. to = chart.get(typeof connect === 'string' ?
  50182. connect : connect.to);
  50183. if (to instanceof Point &&
  50184. to.series.visible &&
  50185. to.visible &&
  50186. to.isInside !== false) {
  50187. // Add new connection
  50188. pathfinder.connections.push(new Connection(point, // from
  50189. to, typeof connect === 'string' ?
  50190. {} :
  50191. connect));
  50192. }
  50193. });
  50194. }
  50195. });
  50196. }
  50197. });
  50198. // Clear connections that should not be updated, and move old info over
  50199. // to new connections.
  50200. for (var j = 0, k, found, lenOld = oldConnections.length, lenNew = pathfinder.connections.length; j < lenOld; ++j) {
  50201. found = false;
  50202. for (k = 0; k < lenNew; ++k) {
  50203. if (oldConnections[j].fromPoint ===
  50204. pathfinder.connections[k].fromPoint &&
  50205. oldConnections[j].toPoint ===
  50206. pathfinder.connections[k].toPoint) {
  50207. pathfinder.connections[k].graphics =
  50208. oldConnections[j].graphics;
  50209. found = true;
  50210. break;
  50211. }
  50212. }
  50213. if (!found) {
  50214. oldConnections[j].destroy();
  50215. }
  50216. }
  50217. // Clear obstacles to force recalculation. This must be done on every
  50218. // redraw in case positions have changed. Recalculation is handled in
  50219. // Connection.getPath on demand.
  50220. delete this.chartObstacles;
  50221. delete this.lineObstacles;
  50222. // Draw the pending connections
  50223. pathfinder.renderConnections(deferRender);
  50224. };
  50225. /**
  50226. * Draw the chart's connecting paths.
  50227. *
  50228. * @function Highcharts.Pathfinder#renderConnections
  50229. *
  50230. * @param {boolean} [deferRender]
  50231. * Whether or not to defer render until series animation is finished.
  50232. * Used on first render.
  50233. */
  50234. Pathfinder.prototype.renderConnections = function (deferRender) {
  50235. if (deferRender) {
  50236. // Render after series are done animating
  50237. this.chart.series.forEach(function (series) {
  50238. var render = function () {
  50239. // Find pathfinder connections belonging to this series
  50240. // that haven't rendered, and render them now.
  50241. var pathfinder = series.chart.pathfinder,
  50242. conns = pathfinder && pathfinder.connections || [];
  50243. conns.forEach(function (connection) {
  50244. if (connection.fromPoint &&
  50245. connection.fromPoint.series === series) {
  50246. connection.render();
  50247. }
  50248. });
  50249. if (series.pathfinderRemoveRenderEvent) {
  50250. series.pathfinderRemoveRenderEvent();
  50251. delete series.pathfinderRemoveRenderEvent;
  50252. }
  50253. };
  50254. if (series.options.animation === false) {
  50255. render();
  50256. }
  50257. else {
  50258. series.pathfinderRemoveRenderEvent = addEvent(series, 'afterAnimate', render);
  50259. }
  50260. });
  50261. }
  50262. else {
  50263. // Go through connections and render them
  50264. this.connections.forEach(function (connection) {
  50265. connection.render();
  50266. });
  50267. }
  50268. };
  50269. /**
  50270. * Get obstacles for the points in the chart. Does not include connecting
  50271. * lines from Pathfinder. Applies algorithmMargin to the obstacles.
  50272. *
  50273. * @function Highcharts.Pathfinder#getChartObstacles
  50274. *
  50275. * @param {object} options
  50276. * Options for the calculation. Currenlty only
  50277. * options.algorithmMargin.
  50278. *
  50279. * @return {Array<object>}
  50280. * An array of calculated obstacles. Each obstacle is defined as an
  50281. * object with xMin, xMax, yMin and yMax properties.
  50282. */
  50283. Pathfinder.prototype.getChartObstacles = function (options) {
  50284. var obstacles = [],
  50285. series = this.chart.series,
  50286. margin = pick(options.algorithmMargin, 0),
  50287. calculatedMargin;
  50288. for (var i = 0, sLen = series.length; i < sLen; ++i) {
  50289. if (series[i].visible && !series[i].options.isInternal) {
  50290. for (var j = 0, pLen = series[i].points.length, bb, point; j < pLen; ++j) {
  50291. point = series[i].points[j];
  50292. if (point.visible) {
  50293. bb = getPointBB(point);
  50294. if (bb) {
  50295. obstacles.push({
  50296. xMin: bb.xMin - margin,
  50297. xMax: bb.xMax + margin,
  50298. yMin: bb.yMin - margin,
  50299. yMax: bb.yMax + margin
  50300. });
  50301. }
  50302. }
  50303. }
  50304. }
  50305. }
  50306. // Sort obstacles by xMin for optimization
  50307. obstacles = obstacles.sort(function (a, b) {
  50308. return a.xMin - b.xMin;
  50309. });
  50310. // Add auto-calculated margin if the option is not defined
  50311. if (!defined(options.algorithmMargin)) {
  50312. calculatedMargin =
  50313. options.algorithmMargin =
  50314. calculateObstacleMargin(obstacles);
  50315. obstacles.forEach(function (obstacle) {
  50316. obstacle.xMin -= calculatedMargin;
  50317. obstacle.xMax += calculatedMargin;
  50318. obstacle.yMin -= calculatedMargin;
  50319. obstacle.yMax += calculatedMargin;
  50320. });
  50321. }
  50322. return obstacles;
  50323. };
  50324. /**
  50325. * Utility function to get metrics for obstacles:
  50326. * - Widest obstacle width
  50327. * - Tallest obstacle height
  50328. *
  50329. * @function Highcharts.Pathfinder#getObstacleMetrics
  50330. *
  50331. * @param {Array<object>} obstacles
  50332. * An array of obstacles to inspect.
  50333. *
  50334. * @return {object}
  50335. * The calculated metrics, as an object with maxHeight and maxWidth
  50336. * properties.
  50337. */
  50338. Pathfinder.prototype.getObstacleMetrics = function (obstacles) {
  50339. var maxWidth = 0,
  50340. maxHeight = 0,
  50341. width,
  50342. height,
  50343. i = obstacles.length;
  50344. while (i--) {
  50345. width = obstacles[i].xMax - obstacles[i].xMin;
  50346. height = obstacles[i].yMax - obstacles[i].yMin;
  50347. if (maxWidth < width) {
  50348. maxWidth = width;
  50349. }
  50350. if (maxHeight < height) {
  50351. maxHeight = height;
  50352. }
  50353. }
  50354. return {
  50355. maxHeight: maxHeight,
  50356. maxWidth: maxWidth
  50357. };
  50358. };
  50359. /**
  50360. * Utility to get which direction to start the pathfinding algorithm
  50361. * (X vs Y), calculated from a set of marker options.
  50362. *
  50363. * @function Highcharts.Pathfinder#getAlgorithmStartDirection
  50364. *
  50365. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  50366. * Marker options to calculate from.
  50367. *
  50368. * @return {boolean}
  50369. * Returns true for X, false for Y, and undefined for autocalculate.
  50370. */
  50371. Pathfinder.prototype.getAlgorithmStartDirection = function (markerOptions) {
  50372. var xCenter = markerOptions.align !== 'left' &&
  50373. markerOptions.align !== 'right', yCenter = markerOptions.verticalAlign !== 'top' &&
  50374. markerOptions.verticalAlign !== 'bottom', undef;
  50375. return xCenter ?
  50376. (yCenter ? undef : false) : // x is centered
  50377. (yCenter ? true : undef); // x is off-center
  50378. };
  50379. return Pathfinder;
  50380. }());
  50381. Pathfinder.prototype.algorithms = pathfinderAlgorithms;
  50382. // Add to Highcharts namespace
  50383. H.Pathfinder = Pathfinder;
  50384. // Add pathfinding capabilities to Points
  50385. extend(Point.prototype, /** @lends Point.prototype */ {
  50386. /**
  50387. * Get coordinates of anchor point for pathfinder connection.
  50388. *
  50389. * @private
  50390. * @function Highcharts.Point#getPathfinderAnchorPoint
  50391. *
  50392. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  50393. * Connection options for position on point.
  50394. *
  50395. * @return {Highcharts.PositionObject}
  50396. * An object with x/y properties for the position. Coordinates are
  50397. * in plot values, not relative to point.
  50398. */
  50399. getPathfinderAnchorPoint: function (markerOptions) {
  50400. var bb = getPointBB(this),
  50401. x,
  50402. y;
  50403. switch (markerOptions.align) { // eslint-disable-line default-case
  50404. case 'right':
  50405. x = 'xMax';
  50406. break;
  50407. case 'left':
  50408. x = 'xMin';
  50409. }
  50410. switch (markerOptions.verticalAlign) { // eslint-disable-line default-case
  50411. case 'top':
  50412. y = 'yMin';
  50413. break;
  50414. case 'bottom':
  50415. y = 'yMax';
  50416. }
  50417. return {
  50418. x: x ? bb[x] : (bb.xMin + bb.xMax) / 2,
  50419. y: y ? bb[y] : (bb.yMin + bb.yMax) / 2
  50420. };
  50421. },
  50422. /**
  50423. * Utility to get the angle from one point to another.
  50424. *
  50425. * @private
  50426. * @function Highcharts.Point#getRadiansToVector
  50427. *
  50428. * @param {Highcharts.PositionObject} v1
  50429. * The first vector, as an object with x/y properties.
  50430. *
  50431. * @param {Highcharts.PositionObject} v2
  50432. * The second vector, as an object with x/y properties.
  50433. *
  50434. * @return {number}
  50435. * The angle in degrees
  50436. */
  50437. getRadiansToVector: function (v1, v2) {
  50438. var box;
  50439. if (!defined(v2)) {
  50440. box = getPointBB(this);
  50441. if (box) {
  50442. v2 = {
  50443. x: (box.xMin + box.xMax) / 2,
  50444. y: (box.yMin + box.yMax) / 2
  50445. };
  50446. }
  50447. }
  50448. return Math.atan2(v2.y - v1.y, v1.x - v2.x);
  50449. },
  50450. /**
  50451. * Utility to get the position of the marker, based on the path angle and
  50452. * the marker's radius.
  50453. *
  50454. * @private
  50455. * @function Highcharts.Point#getMarkerVector
  50456. *
  50457. * @param {number} radians
  50458. * The angle in radians from the point center to another vector.
  50459. *
  50460. * @param {number} markerRadius
  50461. * The radius of the marker, to calculate the additional distance to
  50462. * the center of the marker.
  50463. *
  50464. * @param {object} anchor
  50465. * The anchor point of the path and marker as an object with x/y
  50466. * properties.
  50467. *
  50468. * @return {object}
  50469. * The marker vector as an object with x/y properties.
  50470. */
  50471. getMarkerVector: function (radians, markerRadius, anchor) {
  50472. var twoPI = Math.PI * 2.0,
  50473. theta = radians,
  50474. bb = getPointBB(this),
  50475. rectWidth = bb.xMax - bb.xMin,
  50476. rectHeight = bb.yMax - bb.yMin,
  50477. rAtan = Math.atan2(rectHeight,
  50478. rectWidth),
  50479. tanTheta = 1,
  50480. leftOrRightRegion = false,
  50481. rectHalfWidth = rectWidth / 2.0,
  50482. rectHalfHeight = rectHeight / 2.0,
  50483. rectHorizontalCenter = bb.xMin + rectHalfWidth,
  50484. rectVerticalCenter = bb.yMin + rectHalfHeight,
  50485. edgePoint = {
  50486. x: rectHorizontalCenter,
  50487. y: rectVerticalCenter
  50488. },
  50489. markerPoint = {},
  50490. xFactor = 1,
  50491. yFactor = 1;
  50492. while (theta < -Math.PI) {
  50493. theta += twoPI;
  50494. }
  50495. while (theta > Math.PI) {
  50496. theta -= twoPI;
  50497. }
  50498. tanTheta = Math.tan(theta);
  50499. if ((theta > -rAtan) && (theta <= rAtan)) {
  50500. // Right side
  50501. yFactor = -1;
  50502. leftOrRightRegion = true;
  50503. }
  50504. else if (theta > rAtan && theta <= (Math.PI - rAtan)) {
  50505. // Top side
  50506. yFactor = -1;
  50507. }
  50508. else if (theta > (Math.PI - rAtan) || theta <= -(Math.PI - rAtan)) {
  50509. // Left side
  50510. xFactor = -1;
  50511. leftOrRightRegion = true;
  50512. }
  50513. else {
  50514. // Bottom side
  50515. xFactor = -1;
  50516. }
  50517. // Correct the edgePoint according to the placement of the marker
  50518. if (leftOrRightRegion) {
  50519. edgePoint.x += xFactor * (rectHalfWidth);
  50520. edgePoint.y += yFactor * (rectHalfWidth) * tanTheta;
  50521. }
  50522. else {
  50523. edgePoint.x += xFactor * (rectHeight / (2.0 * tanTheta));
  50524. edgePoint.y += yFactor * (rectHalfHeight);
  50525. }
  50526. if (anchor.x !== rectHorizontalCenter) {
  50527. edgePoint.x = anchor.x;
  50528. }
  50529. if (anchor.y !== rectVerticalCenter) {
  50530. edgePoint.y = anchor.y;
  50531. }
  50532. markerPoint.x = edgePoint.x + (markerRadius * Math.cos(theta));
  50533. markerPoint.y = edgePoint.y - (markerRadius * Math.sin(theta));
  50534. return markerPoint;
  50535. }
  50536. });
  50537. /**
  50538. * Warn if using legacy options. Copy the options over. Note that this will
  50539. * still break if using the legacy options in chart.update, addSeries etc.
  50540. * @private
  50541. */
  50542. function warnLegacy(chart) {
  50543. if (chart.options.pathfinder ||
  50544. chart.series.reduce(function (acc, series) {
  50545. if (series.options) {
  50546. merge(true, (series.options.connectors = series.options.connectors ||
  50547. {}), series.options.pathfinder);
  50548. }
  50549. return acc || series.options && series.options.pathfinder;
  50550. }, false)) {
  50551. merge(true, (chart.options.connectors = chart.options.connectors || {}), chart.options.pathfinder);
  50552. error('WARNING: Pathfinder options have been renamed. ' +
  50553. 'Use "chart.connectors" or "series.connectors" instead.');
  50554. }
  50555. }
  50556. // Initialize Pathfinder for charts
  50557. Chart.prototype.callbacks.push(function (chart) {
  50558. var options = chart.options;
  50559. if (options.connectors.enabled !== false) {
  50560. warnLegacy(chart);
  50561. this.pathfinder = new Pathfinder(this);
  50562. this.pathfinder.update(true); // First draw, defer render
  50563. }
  50564. });
  50565. return Pathfinder;
  50566. });
  50567. _registerModule(_modules, 'Series/XRangeSeries.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Color.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (Axis, H, Color, Point, U) {
  50568. /* *
  50569. *
  50570. * X-range series module
  50571. *
  50572. * (c) 2010-2020 Torstein Honsi, Lars A. V. Cabrera
  50573. *
  50574. * License: www.highcharts.com/license
  50575. *
  50576. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  50577. *
  50578. * */
  50579. var color = Color.parse;
  50580. var addEvent = U.addEvent,
  50581. clamp = U.clamp,
  50582. correctFloat = U.correctFloat,
  50583. defined = U.defined,
  50584. find = U.find,
  50585. isNumber = U.isNumber,
  50586. isObject = U.isObject,
  50587. merge = U.merge,
  50588. pick = U.pick,
  50589. seriesType = U.seriesType;
  50590. /* *
  50591. * @interface Highcharts.PointOptionsObject in parts/Point.ts
  50592. */ /**
  50593. * The ending X value of the range point.
  50594. * @name Highcharts.PointOptionsObject#x2
  50595. * @type {number|undefined}
  50596. * @requires modules/xrange
  50597. */
  50598. var columnType = H.seriesTypes.column,
  50599. seriesTypes = H.seriesTypes,
  50600. Series = H.Series;
  50601. /**
  50602. * Return color of a point based on its category.
  50603. *
  50604. * @private
  50605. * @function getColorByCategory
  50606. *
  50607. * @param {object} series
  50608. * The series which the point belongs to.
  50609. *
  50610. * @param {object} point
  50611. * The point to calculate its color for.
  50612. *
  50613. * @return {object}
  50614. * Returns an object containing the properties color and colorIndex.
  50615. */
  50616. function getColorByCategory(series, point) {
  50617. var colors = series.options.colors || series.chart.options.colors,
  50618. colorCount = colors ?
  50619. colors.length :
  50620. series.chart.options.chart.colorCount,
  50621. colorIndex = point.y % colorCount,
  50622. color = colors && colors[colorIndex];
  50623. return {
  50624. colorIndex: colorIndex,
  50625. color: color
  50626. };
  50627. }
  50628. /**
  50629. * @private
  50630. * @class
  50631. * @name Highcharts.seriesTypes.xrange
  50632. *
  50633. * @augments Highcharts.Series
  50634. */
  50635. seriesType('xrange', 'column'
  50636. /**
  50637. * The X-range series displays ranges on the X axis, typically time
  50638. * intervals with a start and end date.
  50639. *
  50640. * @sample {highcharts} highcharts/demo/x-range/
  50641. * X-range
  50642. * @sample {highcharts} highcharts/css/x-range/
  50643. * Styled mode X-range
  50644. * @sample {highcharts} highcharts/chart/inverted-xrange/
  50645. * Inverted X-range
  50646. *
  50647. * @extends plotOptions.column
  50648. * @since 6.0.0
  50649. * @product highcharts highstock gantt
  50650. * @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor,
  50651. * edgeWidth, findNearestPointBy, getExtremesFromAll,
  50652. * negativeColor, pointInterval, pointIntervalUnit,
  50653. * pointPlacement, pointRange, pointStart, softThreshold,
  50654. * stacking, threshold, data, dataSorting, boostBlending
  50655. * @requires modules/xrange
  50656. * @optionparent plotOptions.xrange
  50657. */
  50658. , {
  50659. /**
  50660. * A partial fill for each point, typically used to visualize how much
  50661. * of a task is performed. The partial fill object can be set either on
  50662. * series or point level.
  50663. *
  50664. * @sample {highcharts} highcharts/demo/x-range
  50665. * X-range with partial fill
  50666. *
  50667. * @product highcharts highstock gantt
  50668. * @apioption plotOptions.xrange.partialFill
  50669. */
  50670. /**
  50671. * The fill color to be used for partial fills. Defaults to a darker
  50672. * shade of the point color.
  50673. *
  50674. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  50675. * @product highcharts highstock gantt
  50676. * @apioption plotOptions.xrange.partialFill.fill
  50677. */
  50678. /**
  50679. * A partial fill for each point, typically used to visualize how much
  50680. * of a task is performed. See [completed](series.gantt.data.completed).
  50681. *
  50682. * @sample gantt/demo/progress-indicator
  50683. * Gantt with progress indicator
  50684. *
  50685. * @product gantt
  50686. * @apioption plotOptions.gantt.partialFill
  50687. */
  50688. /**
  50689. * In an X-range series, this option makes all points of the same Y-axis
  50690. * category the same color.
  50691. */
  50692. colorByPoint: true,
  50693. dataLabels: {
  50694. formatter: function () {
  50695. var point = this.point,
  50696. amount = point.partialFill;
  50697. if (isObject(amount)) {
  50698. amount = amount.amount;
  50699. }
  50700. if (isNumber(amount) && amount > 0) {
  50701. return correctFloat(amount * 100) + '%';
  50702. }
  50703. },
  50704. inside: true,
  50705. verticalAlign: 'middle'
  50706. },
  50707. tooltip: {
  50708. headerFormat: '<span style="font-size: 10px">{point.x} - {point.x2}</span><br/>',
  50709. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.yCategory}</b><br/>'
  50710. },
  50711. borderRadius: 3,
  50712. pointRange: 0
  50713. }, {
  50714. type: 'xrange',
  50715. parallelArrays: ['x', 'x2', 'y'],
  50716. requireSorting: false,
  50717. animate: seriesTypes.line.prototype.animate,
  50718. cropShoulder: 1,
  50719. getExtremesFromAll: true,
  50720. autoIncrement: H.noop,
  50721. buildKDTree: H.noop,
  50722. /* eslint-disable valid-jsdoc */
  50723. /**
  50724. * @private
  50725. * @function Highcarts.seriesTypes.xrange#init
  50726. * @return {void}
  50727. */
  50728. init: function () {
  50729. seriesTypes.column.prototype.init.apply(this, arguments);
  50730. this.options.stacking = void 0; // #13161
  50731. },
  50732. /**
  50733. * Borrow the column series metrics, but with swapped axes. This gives
  50734. * free access to features like groupPadding, grouping, pointWidth etc.
  50735. *
  50736. * @private
  50737. * @function Highcharts.Series#getColumnMetrics
  50738. *
  50739. * @return {Highcharts.ColumnMetricsObject}
  50740. */
  50741. getColumnMetrics: function () {
  50742. var metrics,
  50743. chart = this.chart;
  50744. /**
  50745. * @private
  50746. */
  50747. function swapAxes() {
  50748. chart.series.forEach(function (s) {
  50749. var xAxis = s.xAxis;
  50750. s.xAxis = s.yAxis;
  50751. s.yAxis = xAxis;
  50752. });
  50753. }
  50754. swapAxes();
  50755. metrics = columnType.prototype.getColumnMetrics.call(this);
  50756. swapAxes();
  50757. return metrics;
  50758. },
  50759. /**
  50760. * Override cropData to show a point where x or x2 is outside visible
  50761. * range, but one of them is inside.
  50762. *
  50763. * @private
  50764. * @function Highcharts.Series#cropData
  50765. *
  50766. * @param {Array<number>} xData
  50767. *
  50768. * @param {Array<number>} yData
  50769. *
  50770. * @param {number} min
  50771. *
  50772. * @param {number} max
  50773. *
  50774. * @param {number} [cropShoulder]
  50775. *
  50776. * @return {*}
  50777. */
  50778. cropData: function (xData, yData, min, max) {
  50779. // Replace xData with x2Data to find the appropriate cropStart
  50780. var cropData = Series.prototype.cropData,
  50781. crop = cropData.call(this,
  50782. this.x2Data,
  50783. yData,
  50784. min,
  50785. max);
  50786. // Re-insert the cropped xData
  50787. crop.xData = xData.slice(crop.start, crop.end);
  50788. return crop;
  50789. },
  50790. /**
  50791. * Finds the index of an existing point that matches the given point
  50792. * options.
  50793. *
  50794. * @private
  50795. * @function Highcharts.Series#findPointIndex
  50796. * @param {object} options The options of the point.
  50797. * @returns {number|undefined} Returns index of a matching point,
  50798. * returns undefined if no match is found.
  50799. */
  50800. findPointIndex: function (options) {
  50801. var _a = this,
  50802. cropped = _a.cropped,
  50803. cropStart = _a.cropStart,
  50804. points = _a.points;
  50805. var id = options.id;
  50806. var pointIndex;
  50807. if (id) {
  50808. var point = find(points,
  50809. function (point) {
  50810. return point.id === id;
  50811. });
  50812. pointIndex = point ? point.index : void 0;
  50813. }
  50814. if (typeof pointIndex === 'undefined') {
  50815. var point = find(points,
  50816. function (point) {
  50817. return (point.x === options.x &&
  50818. point.x2 === options.x2 &&
  50819. !point.touched);
  50820. });
  50821. pointIndex = point ? point.index : void 0;
  50822. }
  50823. // Reduce pointIndex if data is cropped
  50824. if (cropped &&
  50825. isNumber(pointIndex) &&
  50826. isNumber(cropStart) &&
  50827. pointIndex >= cropStart) {
  50828. pointIndex -= cropStart;
  50829. }
  50830. return pointIndex;
  50831. },
  50832. /**
  50833. * @private
  50834. * @function Highcharts.Series#translatePoint
  50835. *
  50836. * @param {Highcharts.Point} point
  50837. */
  50838. translatePoint: function (point) {
  50839. var series = this,
  50840. xAxis = series.xAxis,
  50841. yAxis = series.yAxis,
  50842. metrics = series.columnMetrics,
  50843. options = series.options,
  50844. minPointLength = options.minPointLength || 0,
  50845. plotX = point.plotX,
  50846. posX = pick(point.x2,
  50847. point.x + (point.len || 0)),
  50848. plotX2 = xAxis.translate(posX, 0, 0, 0, 1),
  50849. length = Math.abs(plotX2 - plotX),
  50850. widthDifference,
  50851. shapeArgs,
  50852. partialFill,
  50853. inverted = this.chart.inverted,
  50854. borderWidth = pick(options.borderWidth, 1),
  50855. crisper = borderWidth % 2 / 2,
  50856. yOffset = metrics.offset,
  50857. pointHeight = Math.round(metrics.width),
  50858. dlLeft,
  50859. dlRight,
  50860. dlWidth,
  50861. clipRectWidth,
  50862. tooltipYOffset;
  50863. if (minPointLength) {
  50864. widthDifference = minPointLength - length;
  50865. if (widthDifference < 0) {
  50866. widthDifference = 0;
  50867. }
  50868. plotX -= widthDifference / 2;
  50869. plotX2 += widthDifference / 2;
  50870. }
  50871. plotX = Math.max(plotX, -10);
  50872. plotX2 = clamp(plotX2, -10, xAxis.len + 10);
  50873. // Handle individual pointWidth
  50874. if (defined(point.options.pointWidth)) {
  50875. yOffset -= ((Math.ceil(point.options.pointWidth) - pointHeight) / 2);
  50876. pointHeight = Math.ceil(point.options.pointWidth);
  50877. }
  50878. // Apply pointPlacement to the Y axis
  50879. if (options.pointPlacement &&
  50880. isNumber(point.plotY) &&
  50881. yAxis.categories) {
  50882. point.plotY = yAxis.translate(point.y, 0, 1, 0, 1, options.pointPlacement);
  50883. }
  50884. point.shapeArgs = {
  50885. x: Math.floor(Math.min(plotX, plotX2)) + crisper,
  50886. y: Math.floor(point.plotY + yOffset) + crisper,
  50887. width: Math.round(Math.abs(plotX2 - plotX)),
  50888. height: pointHeight,
  50889. r: series.options.borderRadius
  50890. };
  50891. // Align data labels inside the shape and inside the plot area
  50892. dlLeft = point.shapeArgs.x;
  50893. dlRight = dlLeft + point.shapeArgs.width;
  50894. if (dlLeft < 0 || dlRight > xAxis.len) {
  50895. dlLeft = clamp(dlLeft, 0, xAxis.len);
  50896. dlRight = clamp(dlRight, 0, xAxis.len);
  50897. dlWidth = dlRight - dlLeft;
  50898. point.dlBox = merge(point.shapeArgs, {
  50899. x: dlLeft,
  50900. width: dlRight - dlLeft,
  50901. centerX: dlWidth ? dlWidth / 2 : null
  50902. });
  50903. }
  50904. else {
  50905. point.dlBox = null;
  50906. }
  50907. // Tooltip position
  50908. var tooltipPos = point.tooltipPos;
  50909. var xIndex = !inverted ? 0 : 1;
  50910. var yIndex = !inverted ? 1 : 0;
  50911. tooltipYOffset = series.columnMetrics ?
  50912. series.columnMetrics.offset : -metrics.width / 2;
  50913. // Limit position by the correct axis size (#9727)
  50914. tooltipPos[xIndex] = clamp(tooltipPos[xIndex] + ((!inverted ? 1 : -1) * (xAxis.reversed ? -1 : 1) *
  50915. (length / 2)), 0, xAxis.len - 1);
  50916. tooltipPos[yIndex] = clamp(tooltipPos[yIndex] + ((inverted ? -1 : 1) * tooltipYOffset), 0, yAxis.len - 1);
  50917. // Add a partShapeArgs to the point, based on the shapeArgs property
  50918. partialFill = point.partialFill;
  50919. if (partialFill) {
  50920. // Get the partial fill amount
  50921. if (isObject(partialFill)) {
  50922. partialFill = partialFill.amount;
  50923. }
  50924. // If it was not a number, assume 0
  50925. if (!isNumber(partialFill)) {
  50926. partialFill = 0;
  50927. }
  50928. shapeArgs = point.shapeArgs;
  50929. point.partShapeArgs = {
  50930. x: shapeArgs.x,
  50931. y: shapeArgs.y,
  50932. width: shapeArgs.width,
  50933. height: shapeArgs.height,
  50934. r: series.options.borderRadius
  50935. };
  50936. clipRectWidth = Math.max(Math.round(length * partialFill + point.plotX -
  50937. plotX), 0);
  50938. point.clipRectArgs = {
  50939. x: xAxis.reversed ? // #10717
  50940. shapeArgs.x + length - clipRectWidth :
  50941. shapeArgs.x,
  50942. y: shapeArgs.y,
  50943. width: clipRectWidth,
  50944. height: shapeArgs.height
  50945. };
  50946. }
  50947. },
  50948. /**
  50949. * @private
  50950. * @function Highcharts.Series#translate
  50951. */
  50952. translate: function () {
  50953. columnType.prototype.translate.apply(this, arguments);
  50954. this.points.forEach(function (point) {
  50955. this.translatePoint(point);
  50956. }, this);
  50957. },
  50958. /**
  50959. * Draws a single point in the series. Needed for partial fill.
  50960. *
  50961. * This override turns point.graphic into a group containing the
  50962. * original graphic and an overlay displaying the partial fill.
  50963. *
  50964. * @private
  50965. * @function Highcharts.Series#drawPoint
  50966. *
  50967. * @param {Highcharts.Point} point
  50968. * An instance of Point in the series.
  50969. *
  50970. * @param {"animate"|"attr"} verb
  50971. * 'animate' (animates changes) or 'attr' (sets options)
  50972. */
  50973. drawPoint: function (point, verb) {
  50974. var series = this,
  50975. seriesOpts = series.options,
  50976. renderer = series.chart.renderer,
  50977. graphic = point.graphic,
  50978. type = point.shapeType,
  50979. shapeArgs = point.shapeArgs,
  50980. partShapeArgs = point.partShapeArgs,
  50981. clipRectArgs = point.clipRectArgs,
  50982. pfOptions = point.partialFill,
  50983. cutOff = seriesOpts.stacking && !seriesOpts.borderRadius,
  50984. pointState = point.state,
  50985. stateOpts = (seriesOpts.states[pointState || 'normal'] ||
  50986. {}),
  50987. pointStateVerb = typeof pointState === 'undefined' ?
  50988. 'attr' : verb,
  50989. pointAttr = series.pointAttribs(point,
  50990. pointState),
  50991. animation = pick(series.chart.options.chart.animation,
  50992. stateOpts.animation),
  50993. fill;
  50994. if (!point.isNull && point.visible !== false) {
  50995. // Original graphic
  50996. if (graphic) { // update
  50997. graphic.rect[verb](shapeArgs);
  50998. }
  50999. else {
  51000. point.graphic = graphic = renderer.g('point')
  51001. .addClass(point.getClassName())
  51002. .add(point.group || series.group);
  51003. graphic.rect = renderer[type](merge(shapeArgs))
  51004. .addClass(point.getClassName())
  51005. .addClass('highcharts-partfill-original')
  51006. .add(graphic);
  51007. }
  51008. // Partial fill graphic
  51009. if (partShapeArgs) {
  51010. if (graphic.partRect) {
  51011. graphic.partRect[verb](merge(partShapeArgs));
  51012. graphic.partialClipRect[verb](merge(clipRectArgs));
  51013. }
  51014. else {
  51015. graphic.partialClipRect = renderer.clipRect(clipRectArgs.x, clipRectArgs.y, clipRectArgs.width, clipRectArgs.height);
  51016. graphic.partRect =
  51017. renderer[type](partShapeArgs)
  51018. .addClass('highcharts-partfill-overlay')
  51019. .add(graphic)
  51020. .clip(graphic.partialClipRect);
  51021. }
  51022. }
  51023. // Presentational
  51024. if (!series.chart.styledMode) {
  51025. graphic
  51026. .rect[verb](pointAttr, animation)
  51027. .shadow(seriesOpts.shadow, null, cutOff);
  51028. if (partShapeArgs) {
  51029. // Ensure pfOptions is an object
  51030. if (!isObject(pfOptions)) {
  51031. pfOptions = {};
  51032. }
  51033. if (isObject(seriesOpts.partialFill)) {
  51034. pfOptions = merge(pfOptions, seriesOpts.partialFill);
  51035. }
  51036. fill = (pfOptions.fill ||
  51037. color(pointAttr.fill).brighten(-0.3).get() ||
  51038. color(point.color || series.color)
  51039. .brighten(-0.3).get());
  51040. pointAttr.fill = fill;
  51041. graphic
  51042. .partRect[pointStateVerb](pointAttr, animation)
  51043. .shadow(seriesOpts.shadow, null, cutOff);
  51044. }
  51045. }
  51046. }
  51047. else if (graphic) {
  51048. point.graphic = graphic.destroy(); // #1269
  51049. }
  51050. },
  51051. /**
  51052. * @private
  51053. * @function Highcharts.Series#drawPoints
  51054. */
  51055. drawPoints: function () {
  51056. var series = this,
  51057. verb = series.getAnimationVerb();
  51058. // Draw the columns
  51059. series.points.forEach(function (point) {
  51060. series.drawPoint(point, verb);
  51061. });
  51062. },
  51063. /**
  51064. * Returns "animate", or "attr" if the number of points is above the
  51065. * animation limit.
  51066. *
  51067. * @private
  51068. * @function Highcharts.Series#getAnimationVerb
  51069. *
  51070. * @return {string}
  51071. */
  51072. getAnimationVerb: function () {
  51073. return (this.chart.pointCount < (this.options.animationLimit || 250) ?
  51074. 'animate' :
  51075. 'attr');
  51076. }
  51077. /*
  51078. // Override to remove stroke from points. For partial fill.
  51079. pointAttribs: function () {
  51080. var series = this,
  51081. retVal = columnType.prototype.pointAttribs
  51082. .apply(series,
  51083. arguments);
  51084. //retVal['stroke-width'] = 0;
  51085. return retVal;
  51086. }
  51087. //*/
  51088. /* eslint-enable valid-jsdoc */
  51089. }, {
  51090. /**
  51091. * The ending X value of the range point.
  51092. * @name Highcharts.Point#x2
  51093. * @type {number|undefined}
  51094. * @requires modules/xrange
  51095. */
  51096. /**
  51097. * Extend applyOptions so that `colorByPoint` for x-range means that one
  51098. * color is applied per Y axis category.
  51099. *
  51100. * @private
  51101. * @function Highcharts.Point#applyOptions
  51102. *
  51103. * @return {Highcharts.Series}
  51104. */
  51105. /* eslint-disable valid-jsdoc */
  51106. /**
  51107. * @private
  51108. */
  51109. resolveColor: function () {
  51110. var series = this.series,
  51111. colorByPoint;
  51112. if (series.options.colorByPoint && !this.options.color) {
  51113. colorByPoint = getColorByCategory(series, this);
  51114. if (!series.chart.styledMode) {
  51115. this.color = colorByPoint.color;
  51116. }
  51117. if (!this.options.colorIndex) {
  51118. this.colorIndex = colorByPoint.colorIndex;
  51119. }
  51120. }
  51121. else if (!this.color) {
  51122. this.color = series.color;
  51123. }
  51124. },
  51125. /**
  51126. * Extend init to have y default to 0.
  51127. *
  51128. * @private
  51129. * @function Highcharts.Point#init
  51130. *
  51131. * @return {Highcharts.Point}
  51132. */
  51133. init: function () {
  51134. Point.prototype.init.apply(this, arguments);
  51135. if (!this.y) {
  51136. this.y = 0;
  51137. }
  51138. return this;
  51139. },
  51140. /**
  51141. * @private
  51142. * @function Highcharts.Point#setState
  51143. */
  51144. setState: function () {
  51145. Point.prototype.setState.apply(this, arguments);
  51146. this.series.drawPoint(this, this.series.getAnimationVerb());
  51147. },
  51148. /**
  51149. * @private
  51150. * @function Highcharts.Point#getLabelConfig
  51151. *
  51152. * @return {Highcharts.PointLabelObject}
  51153. */
  51154. // Add x2 and yCategory to the available properties for tooltip formats
  51155. getLabelConfig: function () {
  51156. var point = this,
  51157. cfg = Point.prototype.getLabelConfig.call(point),
  51158. yCats = point.series.yAxis.categories;
  51159. cfg.x2 = point.x2;
  51160. cfg.yCategory = point.yCategory = yCats && yCats[point.y];
  51161. return cfg;
  51162. },
  51163. tooltipDateKeys: ['x', 'x2'],
  51164. /**
  51165. * @private
  51166. * @function Highcharts.Point#isValid
  51167. *
  51168. * @return {boolean}
  51169. */
  51170. isValid: function () {
  51171. return typeof this.x === 'number' &&
  51172. typeof this.x2 === 'number';
  51173. }
  51174. /* eslint-enable valid-jsdoc */
  51175. });
  51176. /**
  51177. * Max x2 should be considered in xAxis extremes
  51178. */
  51179. addEvent(Axis, 'afterGetSeriesExtremes', function () {
  51180. var axis = this, // eslint-disable-line no-invalid-this
  51181. axisSeries = axis.series,
  51182. dataMax,
  51183. modMax;
  51184. if (axis.isXAxis) {
  51185. dataMax = pick(axis.dataMax, -Number.MAX_VALUE);
  51186. axisSeries.forEach(function (series) {
  51187. if (series.x2Data) {
  51188. series.x2Data
  51189. .forEach(function (val) {
  51190. if (val > dataMax) {
  51191. dataMax = val;
  51192. modMax = true;
  51193. }
  51194. });
  51195. }
  51196. });
  51197. if (modMax) {
  51198. axis.dataMax = dataMax;
  51199. }
  51200. }
  51201. });
  51202. /**
  51203. * An `xrange` series. If the [type](#series.xrange.type) option is not
  51204. * specified, it is inherited from [chart.type](#chart.type).
  51205. *
  51206. * @extends series,plotOptions.xrange
  51207. * @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor, edgeWidth,
  51208. * findNearestPointBy, getExtremesFromAll, negativeColor,
  51209. * pointInterval, pointIntervalUnit, pointPlacement, pointRange,
  51210. * pointStart, softThreshold, stacking, threshold, dataSorting,
  51211. * boostBlending
  51212. * @product highcharts highstock gantt
  51213. * @requires modules/xrange
  51214. * @apioption series.xrange
  51215. */
  51216. /**
  51217. * An array of data points for the series. For the `xrange` series type,
  51218. * points can be given in the following ways:
  51219. *
  51220. * 1. An array of objects with named values. The objects are point configuration
  51221. * objects as seen below.
  51222. * ```js
  51223. * data: [{
  51224. * x: Date.UTC(2017, 0, 1),
  51225. * x2: Date.UTC(2017, 0, 3),
  51226. * name: "Test",
  51227. * y: 0,
  51228. * color: "#00FF00"
  51229. * }, {
  51230. * x: Date.UTC(2017, 0, 4),
  51231. * x2: Date.UTC(2017, 0, 5),
  51232. * name: "Deploy",
  51233. * y: 1,
  51234. * color: "#FF0000"
  51235. * }]
  51236. * ```
  51237. *
  51238. * @sample {highcharts} highcharts/series/data-array-of-objects/
  51239. * Config objects
  51240. *
  51241. * @declare Highcharts.XrangePointOptionsObject
  51242. * @type {Array<*>}
  51243. * @extends series.line.data
  51244. * @product highcharts highstock gantt
  51245. * @apioption series.xrange.data
  51246. */
  51247. /**
  51248. * The starting X value of the range point.
  51249. *
  51250. * @sample {highcharts} highcharts/demo/x-range
  51251. * X-range
  51252. *
  51253. * @type {number}
  51254. * @product highcharts highstock gantt
  51255. * @apioption series.xrange.data.x
  51256. */
  51257. /**
  51258. * The ending X value of the range point.
  51259. *
  51260. * @sample {highcharts} highcharts/demo/x-range
  51261. * X-range
  51262. *
  51263. * @type {number}
  51264. * @product highcharts highstock gantt
  51265. * @apioption series.xrange.data.x2
  51266. */
  51267. /**
  51268. * The Y value of the range point.
  51269. *
  51270. * @sample {highcharts} highcharts/demo/x-range
  51271. * X-range
  51272. *
  51273. * @type {number}
  51274. * @product highcharts highstock gantt
  51275. * @apioption series.xrange.data.y
  51276. */
  51277. /**
  51278. * A partial fill for each point, typically used to visualize how much of
  51279. * a task is performed. The partial fill object can be set either on series
  51280. * or point level.
  51281. *
  51282. * @sample {highcharts} highcharts/demo/x-range
  51283. * X-range with partial fill
  51284. *
  51285. * @declare Highcharts.XrangePointPartialFillOptionsObject
  51286. * @product highcharts highstock gantt
  51287. * @apioption series.xrange.data.partialFill
  51288. */
  51289. /**
  51290. * The amount of the X-range point to be filled. Values can be 0-1 and are
  51291. * converted to percentages in the default data label formatter.
  51292. *
  51293. * @type {number}
  51294. * @product highcharts highstock gantt
  51295. * @apioption series.xrange.data.partialFill.amount
  51296. */
  51297. /**
  51298. * The fill color to be used for partial fills. Defaults to a darker shade
  51299. * of the point color.
  51300. *
  51301. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  51302. * @product highcharts highstock gantt
  51303. * @apioption series.xrange.data.partialFill.fill
  51304. */
  51305. ''; // adds doclets above to transpiled file
  51306. });
  51307. _registerModule(_modules, 'Series/GanttSeries.js', [_modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Utilities.js']], function (H, O, U) {
  51308. /* *
  51309. *
  51310. * (c) 2016-2020 Highsoft AS
  51311. *
  51312. * Author: Lars A. V. Cabrera
  51313. *
  51314. * License: www.highcharts.com/license
  51315. *
  51316. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  51317. *
  51318. * */
  51319. var dateFormat = O.dateFormat;
  51320. var isNumber = U.isNumber,
  51321. merge = U.merge,
  51322. pick = U.pick,
  51323. seriesType = U.seriesType,
  51324. splat = U.splat;
  51325. var seriesTypes = H.seriesTypes,
  51326. Series = H.Series,
  51327. parent = seriesTypes.xrange;
  51328. /**
  51329. * @private
  51330. * @class
  51331. * @name Highcharts.seriesTypes.gantt
  51332. *
  51333. * @augments Highcharts.Series
  51334. */
  51335. seriesType('gantt', 'xrange'
  51336. /**
  51337. * A `gantt` series. If the [type](#series.gantt.type) option is not specified,
  51338. * it is inherited from [chart.type](#chart.type).
  51339. *
  51340. * @extends plotOptions.xrange
  51341. * @product gantt
  51342. * @requires highcharts-gantt
  51343. * @optionparent plotOptions.gantt
  51344. */
  51345. , {
  51346. // options - default options merged with parent
  51347. grouping: false,
  51348. dataLabels: {
  51349. enabled: true
  51350. },
  51351. tooltip: {
  51352. headerFormat: '<span style="font-size: 10px">{series.name}</span><br/>',
  51353. pointFormat: null,
  51354. pointFormatter: function () {
  51355. var point = this,
  51356. series = point.series,
  51357. tooltip = series.chart.tooltip,
  51358. xAxis = series.xAxis,
  51359. formats = series.tooltipOptions.dateTimeLabelFormats,
  51360. startOfWeek = xAxis.options.startOfWeek,
  51361. ttOptions = series.tooltipOptions,
  51362. format = ttOptions.xDateFormat,
  51363. start,
  51364. end,
  51365. milestone = point.options.milestone,
  51366. retVal = '<b>' + (point.name || point.yCategory) + '</b>';
  51367. if (ttOptions.pointFormat) {
  51368. return point.tooltipFormatter(ttOptions.pointFormat);
  51369. }
  51370. if (!format) {
  51371. format = splat(tooltip.getDateFormat(xAxis.closestPointRange, point.start, startOfWeek, formats))[0];
  51372. }
  51373. start = dateFormat(format, point.start);
  51374. end = dateFormat(format, point.end);
  51375. retVal += '<br/>';
  51376. if (!milestone) {
  51377. retVal += 'Start: ' + start + '<br/>';
  51378. retVal += 'End: ' + end + '<br/>';
  51379. }
  51380. else {
  51381. retVal += start + '<br/>';
  51382. }
  51383. return retVal;
  51384. }
  51385. },
  51386. connectors: {
  51387. type: 'simpleConnect',
  51388. /**
  51389. * @declare Highcharts.ConnectorsAnimationOptionsObject
  51390. */
  51391. animation: {
  51392. reversed: true // Dependencies go from child to parent
  51393. },
  51394. startMarker: {
  51395. enabled: true,
  51396. symbol: 'arrow-filled',
  51397. radius: 4,
  51398. fill: '#fa0',
  51399. align: 'left'
  51400. },
  51401. endMarker: {
  51402. enabled: false,
  51403. align: 'right'
  51404. }
  51405. }
  51406. }, {
  51407. pointArrayMap: ['start', 'end', 'y'],
  51408. // Keyboard navigation, don't use nearest vertical mode
  51409. keyboardMoveVertical: false,
  51410. /* eslint-disable valid-jsdoc */
  51411. /**
  51412. * Handle milestones, as they have no x2.
  51413. * @private
  51414. */
  51415. translatePoint: function (point) {
  51416. var series = this,
  51417. shapeArgs,
  51418. size;
  51419. parent.prototype.translatePoint.call(series, point);
  51420. if (point.options.milestone) {
  51421. shapeArgs = point.shapeArgs;
  51422. size = shapeArgs.height;
  51423. point.shapeArgs = {
  51424. x: shapeArgs.x - (size / 2),
  51425. y: shapeArgs.y,
  51426. width: size,
  51427. height: size
  51428. };
  51429. }
  51430. },
  51431. /**
  51432. * Draws a single point in the series.
  51433. *
  51434. * This override draws the point as a diamond if point.options.milestone
  51435. * is true, and uses the original drawPoint() if it is false or not set.
  51436. *
  51437. * @requires highcharts-gantt
  51438. *
  51439. * @private
  51440. * @function Highcharts.seriesTypes.gantt#drawPoint
  51441. *
  51442. * @param {Highcharts.Point} point
  51443. * An instance of Point in the series
  51444. *
  51445. * @param {"animate"|"attr"} verb
  51446. * 'animate' (animates changes) or 'attr' (sets options)
  51447. *
  51448. * @return {void}
  51449. */
  51450. drawPoint: function (point, verb) {
  51451. var series = this,
  51452. seriesOpts = series.options,
  51453. renderer = series.chart.renderer,
  51454. shapeArgs = point.shapeArgs,
  51455. plotY = point.plotY,
  51456. graphic = point.graphic,
  51457. state = point.selected && 'select',
  51458. cutOff = seriesOpts.stacking && !seriesOpts.borderRadius,
  51459. diamondShape;
  51460. if (point.options.milestone) {
  51461. if (isNumber(plotY) && point.y !== null && point.visible !== false) {
  51462. diamondShape = renderer.symbols.diamond(shapeArgs.x, shapeArgs.y, shapeArgs.width, shapeArgs.height);
  51463. if (graphic) {
  51464. graphic[verb]({
  51465. d: diamondShape
  51466. });
  51467. }
  51468. else {
  51469. point.graphic = graphic = renderer.path(diamondShape)
  51470. .addClass(point.getClassName(), true)
  51471. .add(point.group || series.group);
  51472. }
  51473. // Presentational
  51474. if (!series.chart.styledMode) {
  51475. point.graphic
  51476. .attr(series.pointAttribs(point, state))
  51477. .shadow(seriesOpts.shadow, null, cutOff);
  51478. }
  51479. }
  51480. else if (graphic) {
  51481. point.graphic = graphic.destroy(); // #1269
  51482. }
  51483. }
  51484. else {
  51485. parent.prototype.drawPoint.call(series, point, verb);
  51486. }
  51487. },
  51488. setData: Series.prototype.setData,
  51489. /**
  51490. * @private
  51491. */
  51492. setGanttPointAliases: function (options) {
  51493. /**
  51494. * Add a value to options if the value exists.
  51495. * @private
  51496. */
  51497. function addIfExists(prop, val) {
  51498. if (typeof val !== 'undefined') {
  51499. options[prop] = val;
  51500. }
  51501. }
  51502. addIfExists('x', pick(options.start, options.x));
  51503. addIfExists('x2', pick(options.end, options.x2));
  51504. addIfExists('partialFill', pick(options.completed, options.partialFill));
  51505. addIfExists('connect', pick(options.dependency, options.connect));
  51506. }
  51507. /* eslint-enable valid-jsdoc */
  51508. }, merge(parent.prototype.pointClass.prototype, {
  51509. // pointProps - point member overrides. We inherit from parent as well.
  51510. /* eslint-disable valid-jsdoc */
  51511. /**
  51512. * Applies the options containing the x and y data and possible some
  51513. * extra properties. This is called on point init or from point.update.
  51514. *
  51515. * @private
  51516. * @function Highcharts.Point#applyOptions
  51517. *
  51518. * @param {object} options
  51519. * The point options
  51520. *
  51521. * @param {number} x
  51522. * The x value
  51523. *
  51524. * @return {Highcharts.Point}
  51525. * The Point instance
  51526. */
  51527. applyOptions: function (options, x) {
  51528. var point = this,
  51529. retVal = merge(options);
  51530. H.seriesTypes.gantt.prototype.setGanttPointAliases(retVal);
  51531. retVal = parent.prototype.pointClass.prototype.applyOptions
  51532. .call(point, retVal, x);
  51533. return retVal;
  51534. },
  51535. isValid: function () {
  51536. return ((typeof this.start === 'number' ||
  51537. typeof this.x === 'number') &&
  51538. (typeof this.end === 'number' ||
  51539. typeof this.x2 === 'number' ||
  51540. this.milestone));
  51541. }
  51542. /* eslint-enable valid-jsdoc */
  51543. }));
  51544. /**
  51545. * A `gantt` series.
  51546. *
  51547. * @extends series,plotOptions.gantt
  51548. * @excluding boostThreshold, connectors, dashStyle, findNearestPointBy,
  51549. * getExtremesFromAll, marker, negativeColor, pointInterval,
  51550. * pointIntervalUnit, pointPlacement, pointStart
  51551. * @product gantt
  51552. * @requires highcharts-gantt
  51553. * @apioption series.gantt
  51554. */
  51555. /**
  51556. * Data for a Gantt series.
  51557. *
  51558. * @declare Highcharts.GanttPointOptionsObject
  51559. * @type {Array<*>}
  51560. * @extends series.xrange.data
  51561. * @excluding className, color, colorIndex, connect, dataLabels, events,
  51562. * partialFill, selected, x, x2
  51563. * @product gantt
  51564. * @apioption series.gantt.data
  51565. */
  51566. /**
  51567. * Whether the grid node belonging to this point should start as collapsed. Used
  51568. * in axes of type treegrid.
  51569. *
  51570. * @sample {gantt} gantt/treegrid-axis/collapsed/
  51571. * Start as collapsed
  51572. *
  51573. * @type {boolean}
  51574. * @default false
  51575. * @product gantt
  51576. * @apioption series.gantt.data.collapsed
  51577. */
  51578. /**
  51579. * The start time of a task.
  51580. *
  51581. * @type {number}
  51582. * @product gantt
  51583. * @apioption series.gantt.data.start
  51584. */
  51585. /**
  51586. * The end time of a task.
  51587. *
  51588. * @type {number}
  51589. * @product gantt
  51590. * @apioption series.gantt.data.end
  51591. */
  51592. /**
  51593. * The Y value of a task.
  51594. *
  51595. * @type {number}
  51596. * @product gantt
  51597. * @apioption series.gantt.data.y
  51598. */
  51599. /**
  51600. * The name of a task. If a `treegrid` y-axis is used (default in Gantt charts),
  51601. * this will be picked up automatically, and used to calculate the y-value.
  51602. *
  51603. * @type {string}
  51604. * @product gantt
  51605. * @apioption series.gantt.data.name
  51606. */
  51607. /**
  51608. * Progress indicator, how much of the task completed. If it is a number, the
  51609. * `fill` will be applied automatically.
  51610. *
  51611. * @sample {gantt} gantt/demo/progress-indicator
  51612. * Progress indicator
  51613. *
  51614. * @type {number|*}
  51615. * @extends series.xrange.data.partialFill
  51616. * @product gantt
  51617. * @apioption series.gantt.data.completed
  51618. */
  51619. /**
  51620. * The amount of the progress indicator, ranging from 0 (not started) to 1
  51621. * (finished).
  51622. *
  51623. * @type {number}
  51624. * @default 0
  51625. * @apioption series.gantt.data.completed.amount
  51626. */
  51627. /**
  51628. * The fill of the progress indicator. Defaults to a darkened variety of the
  51629. * main color.
  51630. *
  51631. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  51632. * @apioption series.gantt.data.completed.fill
  51633. */
  51634. /**
  51635. * The ID of the point (task) that this point depends on in Gantt charts.
  51636. * Aliases [connect](series.xrange.data.connect). Can also be an object,
  51637. * specifying further connecting [options](series.gantt.connectors) between the
  51638. * points. Multiple connections can be specified by providing an array.
  51639. *
  51640. * @sample gantt/demo/project-management
  51641. * Dependencies
  51642. * @sample gantt/pathfinder/demo
  51643. * Different connection types
  51644. *
  51645. * @type {string|Array<string|*>|*}
  51646. * @extends series.xrange.data.connect
  51647. * @since 6.2.0
  51648. * @product gantt
  51649. * @apioption series.gantt.data.dependency
  51650. */
  51651. /**
  51652. * Whether this point is a milestone. If so, only the `start` option is handled,
  51653. * while `end` is ignored.
  51654. *
  51655. * @sample gantt/gantt/milestones
  51656. * Milestones
  51657. *
  51658. * @type {boolean}
  51659. * @since 6.2.0
  51660. * @product gantt
  51661. * @apioption series.gantt.data.milestone
  51662. */
  51663. /**
  51664. * The ID of the parent point (task) of this point in Gantt charts.
  51665. *
  51666. * @sample gantt/demo/subtasks
  51667. * Gantt chart with subtasks
  51668. *
  51669. * @type {string}
  51670. * @since 6.2.0
  51671. * @product gantt
  51672. * @apioption series.gantt.data.parent
  51673. */
  51674. /**
  51675. * @excluding afterAnimate
  51676. * @apioption series.gantt.events
  51677. */
  51678. ''; // adds doclets above to the transpiled file
  51679. });
  51680. _registerModule(_modules, 'Core/Chart/GanttChart.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Chart, H, U) {
  51681. /* *
  51682. *
  51683. * (c) 2016-2020 Highsoft AS
  51684. *
  51685. * Author: Lars A. V. Cabrera
  51686. *
  51687. * License: www.highcharts.com/license
  51688. *
  51689. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  51690. *
  51691. * */
  51692. var getOptions = U.getOptions,
  51693. isArray = U.isArray,
  51694. merge = U.merge,
  51695. splat = U.splat;
  51696. /**
  51697. * Factory function for Gantt charts.
  51698. *
  51699. * @example
  51700. * // Render a chart in to div#container
  51701. * var chart = Highcharts.ganttChart('container', {
  51702. * title: {
  51703. * text: 'My chart'
  51704. * },
  51705. * series: [{
  51706. * data: ...
  51707. * }]
  51708. * });
  51709. *
  51710. * @function Highcharts.ganttChart
  51711. *
  51712. * @param {string|Highcharts.HTMLDOMElement} renderTo
  51713. * The DOM element to render to, or its id.
  51714. *
  51715. * @param {Highcharts.Options} options
  51716. * The chart options structure.
  51717. *
  51718. * @param {Highcharts.ChartCallbackFunction} [callback]
  51719. * Function to run when the chart has loaded and and all external images
  51720. * are loaded. Defining a
  51721. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  51722. * handler is equivalent.
  51723. *
  51724. * @return {Highcharts.Chart}
  51725. * Returns the Chart object.
  51726. */
  51727. H.ganttChart = function (renderTo, options, callback) {
  51728. var hasRenderToArg = typeof renderTo === 'string' || renderTo.nodeName,
  51729. seriesOptions = options.series,
  51730. defaultOptions = getOptions(),
  51731. defaultLinkedTo,
  51732. userOptions = options;
  51733. options = arguments[hasRenderToArg ? 1 : 0];
  51734. // If user hasn't defined axes as array, make it into an array and add a
  51735. // second axis by default.
  51736. if (!isArray(options.xAxis)) {
  51737. options.xAxis = [options.xAxis || {}, {}];
  51738. }
  51739. // apply X axis options to both single and multi x axes
  51740. options.xAxis = options.xAxis.map(function (xAxisOptions, i) {
  51741. if (i === 1) { // Second xAxis
  51742. defaultLinkedTo = 0;
  51743. }
  51744. return merge(defaultOptions.xAxis, {
  51745. grid: {
  51746. enabled: true
  51747. },
  51748. opposite: true,
  51749. linkedTo: defaultLinkedTo
  51750. }, xAxisOptions, // user options
  51751. {
  51752. type: 'datetime'
  51753. });
  51754. });
  51755. // apply Y axis options to both single and multi y axes
  51756. options.yAxis = (splat(options.yAxis || {})).map(function (yAxisOptions) {
  51757. return merge(defaultOptions.yAxis, // #3802
  51758. {
  51759. grid: {
  51760. enabled: true
  51761. },
  51762. staticScale: 50,
  51763. reversed: true,
  51764. // Set default type treegrid, but only if 'categories' is
  51765. // undefined
  51766. type: yAxisOptions.categories ? yAxisOptions.type : 'treegrid'
  51767. }, yAxisOptions // user options
  51768. );
  51769. });
  51770. options.series = null;
  51771. options = merge(true, {
  51772. chart: {
  51773. type: 'gantt'
  51774. },
  51775. title: {
  51776. text: null
  51777. },
  51778. legend: {
  51779. enabled: false
  51780. },
  51781. navigator: {
  51782. series: { type: 'gantt' }
  51783. }
  51784. }, options, // user's options
  51785. // forced options
  51786. {
  51787. isGantt: true
  51788. });
  51789. options.series = userOptions.series = seriesOptions;
  51790. (options.series || []).forEach(function (series) {
  51791. if (series.data) {
  51792. series.data.forEach(function (point) {
  51793. H.seriesTypes.gantt.prototype.setGanttPointAliases(point);
  51794. });
  51795. }
  51796. });
  51797. return hasRenderToArg ?
  51798. new Chart(renderTo, options, callback) :
  51799. new Chart(options, options); // @todo does not look correct
  51800. };
  51801. });
  51802. _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  51803. /* *
  51804. *
  51805. * (c) 2010-2020 Torstein Honsi
  51806. *
  51807. * License: www.highcharts.com/license
  51808. *
  51809. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  51810. *
  51811. * */
  51812. var addEvent = U.addEvent,
  51813. defined = U.defined,
  51814. pick = U.pick;
  51815. /* eslint-disable no-invalid-this, valid-jsdoc */
  51816. /**
  51817. * Creates scrollbars if enabled.
  51818. *
  51819. * @private
  51820. */
  51821. var ScrollbarAxis = /** @class */ (function () {
  51822. function ScrollbarAxis() {
  51823. }
  51824. /**
  51825. * Attaches to axis events to create scrollbars if enabled.
  51826. *
  51827. * @private
  51828. *
  51829. * @param AxisClass
  51830. * Axis class to extend.
  51831. *
  51832. * @param ScrollbarClass
  51833. * Scrollbar class to use.
  51834. */
  51835. ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) {
  51836. // Wrap axis initialization and create scrollbar if enabled:
  51837. addEvent(AxisClass, 'afterInit', function () {
  51838. var axis = this;
  51839. if (axis.options &&
  51840. axis.options.scrollbar &&
  51841. axis.options.scrollbar.enabled) {
  51842. // Predefined options:
  51843. axis.options.scrollbar.vertical = !axis.horiz;
  51844. axis.options.startOnTick = axis.options.endOnTick = false;
  51845. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  51846. addEvent(axis.scrollbar, 'changed', function (e) {
  51847. var axisMin = pick(axis.options && axis.options.min,
  51848. axis.min),
  51849. axisMax = pick(axis.options && axis.options.max,
  51850. axis.max),
  51851. unitedMin = defined(axis.dataMin) ?
  51852. Math.min(axisMin,
  51853. axis.min,
  51854. axis.dataMin) : axisMin,
  51855. unitedMax = defined(axis.dataMax) ?
  51856. Math.max(axisMax,
  51857. axis.max,
  51858. axis.dataMax) : axisMax,
  51859. range = unitedMax - unitedMin,
  51860. to,
  51861. from;
  51862. // #12834, scroll when show/hide series, wrong extremes
  51863. if (!defined(axisMin) || !defined(axisMax)) {
  51864. return;
  51865. }
  51866. if ((axis.horiz && !axis.reversed) ||
  51867. (!axis.horiz && axis.reversed)) {
  51868. to = unitedMin + range * this.to;
  51869. from = unitedMin + range * this.from;
  51870. }
  51871. else {
  51872. // y-values in browser are reversed, but this also
  51873. // applies for reversed horizontal axis:
  51874. to = unitedMin + range * (1 - this.from);
  51875. from = unitedMin + range * (1 - this.to);
  51876. }
  51877. if (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  51878. // Mouseup always should change extremes
  51879. e.DOMType === 'mouseup' ||
  51880. // Internal events
  51881. !defined(e.DOMType)) {
  51882. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove', e);
  51883. }
  51884. else {
  51885. // When live redraw is disabled, don't change extremes
  51886. // Only change the position of the scollbar thumb
  51887. this.setRange(this.from, this.to);
  51888. }
  51889. });
  51890. }
  51891. });
  51892. // Wrap rendering axis, and update scrollbar if one is created:
  51893. addEvent(AxisClass, 'afterRender', function () {
  51894. var axis = this,
  51895. scrollMin = Math.min(pick(axis.options.min,
  51896. axis.min),
  51897. axis.min,
  51898. pick(axis.dataMin,
  51899. axis.min) // #6930
  51900. ),
  51901. scrollMax = Math.max(pick(axis.options.max,
  51902. axis.max),
  51903. axis.max,
  51904. pick(axis.dataMax,
  51905. axis.max) // #6930
  51906. ),
  51907. scrollbar = axis.scrollbar,
  51908. offset = axis.axisTitleMargin + (axis.titleOffset || 0),
  51909. scrollbarsOffsets = axis.chart.scrollbarsOffsets,
  51910. axisMargin = axis.options.margin || 0,
  51911. offsetsIndex,
  51912. from,
  51913. to;
  51914. if (scrollbar) {
  51915. if (axis.horiz) {
  51916. // Reserve space for labels/title
  51917. if (!axis.opposite) {
  51918. scrollbarsOffsets[1] += offset;
  51919. }
  51920. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  51921. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  51922. // Next scrollbar should reserve space for margin (if set)
  51923. if (!axis.opposite) {
  51924. scrollbarsOffsets[1] += axisMargin;
  51925. }
  51926. offsetsIndex = 1;
  51927. }
  51928. else {
  51929. // Reserve space for labels/title
  51930. if (axis.opposite) {
  51931. scrollbarsOffsets[0] += offset;
  51932. }
  51933. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  51934. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  51935. // Next scrollbar should reserve space for margin (if set)
  51936. if (axis.opposite) {
  51937. scrollbarsOffsets[0] += axisMargin;
  51938. }
  51939. offsetsIndex = 0;
  51940. }
  51941. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  51942. scrollbar.options.margin;
  51943. if (isNaN(scrollMin) ||
  51944. isNaN(scrollMax) ||
  51945. !defined(axis.min) ||
  51946. !defined(axis.max) ||
  51947. axis.min === axis.max // #10733
  51948. ) {
  51949. // default action: when extremes are the same or there is
  51950. // not extremes on the axis, but scrollbar exists, make it
  51951. // full size
  51952. scrollbar.setRange(0, 1);
  51953. }
  51954. else {
  51955. from =
  51956. (axis.min - scrollMin) / (scrollMax - scrollMin);
  51957. to =
  51958. (axis.max - scrollMin) / (scrollMax - scrollMin);
  51959. if ((axis.horiz && !axis.reversed) ||
  51960. (!axis.horiz && axis.reversed)) {
  51961. scrollbar.setRange(from, to);
  51962. }
  51963. else {
  51964. // inverse vertical axis
  51965. scrollbar.setRange(1 - to, 1 - from);
  51966. }
  51967. }
  51968. }
  51969. });
  51970. // Make space for a scrollbar:
  51971. addEvent(AxisClass, 'afterGetOffset', function () {
  51972. var axis = this,
  51973. index = axis.horiz ? 2 : 1,
  51974. scrollbar = axis.scrollbar;
  51975. if (scrollbar) {
  51976. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  51977. axis.chart.axisOffset[index] +=
  51978. scrollbar.size + scrollbar.options.margin;
  51979. }
  51980. });
  51981. };
  51982. return ScrollbarAxis;
  51983. }());
  51984. return ScrollbarAxis;
  51985. });
  51986. _registerModule(_modules, 'Core/Scrollbar.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Core/Utilities.js'], _modules['Core/Options.js']], function (Axis, H, ScrollbarAxis, U, O) {
  51987. /* *
  51988. *
  51989. * (c) 2010-2020 Torstein Honsi
  51990. *
  51991. * License: www.highcharts.com/license
  51992. *
  51993. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  51994. *
  51995. * */
  51996. var addEvent = U.addEvent,
  51997. correctFloat = U.correctFloat,
  51998. defined = U.defined,
  51999. destroyObjectProperties = U.destroyObjectProperties,
  52000. fireEvent = U.fireEvent,
  52001. merge = U.merge,
  52002. pick = U.pick,
  52003. removeEvent = U.removeEvent;
  52004. var defaultOptions = O.defaultOptions;
  52005. var hasTouch = H.hasTouch,
  52006. isTouchDevice = H.isTouchDevice;
  52007. /**
  52008. * When we have vertical scrollbar, rifles and arrow in buttons should be
  52009. * rotated. The same method is used in Navigator's handles, to rotate them.
  52010. *
  52011. * @function Highcharts.swapXY
  52012. *
  52013. * @param {Highcharts.SVGPathArray} path
  52014. * Path to be rotated.
  52015. *
  52016. * @param {boolean} [vertical]
  52017. * If vertical scrollbar, swap x-y values.
  52018. *
  52019. * @return {Highcharts.SVGPathArray}
  52020. * Rotated path.
  52021. *
  52022. * @requires modules/stock
  52023. */
  52024. var swapXY = H.swapXY = function (path,
  52025. vertical) {
  52026. if (vertical) {
  52027. path.forEach(function (seg) {
  52028. var len = seg.length;
  52029. var temp;
  52030. for (var i = 0; i < len; i += 2) {
  52031. temp = seg[i + 1];
  52032. if (typeof temp === 'number') {
  52033. seg[i + 1] = seg[i + 2];
  52034. seg[i + 2] = temp;
  52035. }
  52036. }
  52037. });
  52038. }
  52039. return path;
  52040. };
  52041. /* eslint-disable no-invalid-this, valid-jsdoc */
  52042. /**
  52043. * A reusable scrollbar, internally used in Highstock's navigator and optionally
  52044. * on individual axes.
  52045. *
  52046. * @private
  52047. * @class
  52048. * @name Highcharts.Scrollbar
  52049. * @param {Highcharts.SVGRenderer} renderer
  52050. * @param {Highcharts.ScrollbarOptions} options
  52051. * @param {Highcharts.Chart} chart
  52052. */
  52053. var Scrollbar = /** @class */ (function () {
  52054. /* *
  52055. *
  52056. * Constructors
  52057. *
  52058. * */
  52059. function Scrollbar(renderer, options, chart) {
  52060. /* *
  52061. *
  52062. * Properties
  52063. *
  52064. * */
  52065. this._events = [];
  52066. this.chartX = 0;
  52067. this.chartY = 0;
  52068. this.from = 0;
  52069. this.group = void 0;
  52070. this.scrollbar = void 0;
  52071. this.scrollbarButtons = [];
  52072. this.scrollbarGroup = void 0;
  52073. this.scrollbarLeft = 0;
  52074. this.scrollbarRifles = void 0;
  52075. this.scrollbarStrokeWidth = 1;
  52076. this.scrollbarTop = 0;
  52077. this.size = 0;
  52078. this.to = 0;
  52079. this.track = void 0;
  52080. this.trackBorderWidth = 1;
  52081. this.userOptions = {};
  52082. this.x = 0;
  52083. this.y = 0;
  52084. this.chart = chart;
  52085. this.options = options;
  52086. this.renderer = chart.renderer;
  52087. this.init(renderer, options, chart);
  52088. }
  52089. /* *
  52090. *
  52091. * Functions
  52092. *
  52093. * */
  52094. /**
  52095. * Set up the mouse and touch events for the Scrollbar
  52096. *
  52097. * @private
  52098. * @function Highcharts.Scrollbar#addEvents
  52099. * @return {void}
  52100. */
  52101. Scrollbar.prototype.addEvents = function () {
  52102. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1],
  52103. buttons = this.scrollbarButtons,
  52104. bar = this.scrollbarGroup.element,
  52105. track = this.track.element,
  52106. mouseDownHandler = this.mouseDownHandler.bind(this),
  52107. mouseMoveHandler = this.mouseMoveHandler.bind(this),
  52108. mouseUpHandler = this.mouseUpHandler.bind(this),
  52109. _events;
  52110. // Mouse events
  52111. _events = [
  52112. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)],
  52113. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)],
  52114. [track, 'click', this.trackClick.bind(this)],
  52115. [bar, 'mousedown', mouseDownHandler],
  52116. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  52117. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  52118. ];
  52119. // Touch events
  52120. if (hasTouch) {
  52121. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  52122. }
  52123. // Add them all
  52124. _events.forEach(function (args) {
  52125. addEvent.apply(null, args);
  52126. });
  52127. this._events = _events;
  52128. };
  52129. Scrollbar.prototype.buttonToMaxClick = function (e) {
  52130. var scroller = this;
  52131. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  52132. scroller.updatePosition(scroller.from + range, scroller.to + range);
  52133. fireEvent(scroller, 'changed', {
  52134. from: scroller.from,
  52135. to: scroller.to,
  52136. trigger: 'scrollbar',
  52137. DOMEvent: e
  52138. });
  52139. };
  52140. Scrollbar.prototype.buttonToMinClick = function (e) {
  52141. var scroller = this;
  52142. var range = correctFloat(scroller.to - scroller.from) *
  52143. pick(scroller.options.step, 0.2);
  52144. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  52145. fireEvent(scroller, 'changed', {
  52146. from: scroller.from,
  52147. to: scroller.to,
  52148. trigger: 'scrollbar',
  52149. DOMEvent: e
  52150. });
  52151. };
  52152. /**
  52153. * Get normalized (0-1) cursor position over the scrollbar
  52154. *
  52155. * @private
  52156. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  52157. *
  52158. * @param {*} normalizedEvent
  52159. * normalized event, with chartX and chartY values
  52160. *
  52161. * @return {Highcharts.Dictionary<number>}
  52162. * Local position {chartX, chartY}
  52163. */
  52164. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  52165. var scroller = this,
  52166. options = scroller.options,
  52167. minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  52168. options.minWidth :
  52169. 0; // minWidth distorts translation
  52170. return {
  52171. chartX: (normalizedEvent.chartX - scroller.x -
  52172. scroller.xOffset) /
  52173. (scroller.barWidth - minWidthDifference),
  52174. chartY: (normalizedEvent.chartY - scroller.y -
  52175. scroller.yOffset) /
  52176. (scroller.barWidth - minWidthDifference)
  52177. };
  52178. };
  52179. /**
  52180. * Destroys allocated elements.
  52181. *
  52182. * @private
  52183. * @function Highcharts.Scrollbar#destroy
  52184. * @return {void}
  52185. */
  52186. Scrollbar.prototype.destroy = function () {
  52187. var scroller = this.chart.scroller;
  52188. // Disconnect events added in addEvents
  52189. this.removeEvents();
  52190. // Destroy properties
  52191. [
  52192. 'track',
  52193. 'scrollbarRifles',
  52194. 'scrollbar',
  52195. 'scrollbarGroup',
  52196. 'group'
  52197. ].forEach(function (prop) {
  52198. if (this[prop] && this[prop].destroy) {
  52199. this[prop] = this[prop].destroy();
  52200. }
  52201. }, this);
  52202. // #6421, chart may have more scrollbars
  52203. if (scroller && this === scroller.scrollbar) {
  52204. scroller.scrollbar = null;
  52205. // Destroy elements in collection
  52206. destroyObjectProperties(scroller.scrollbarButtons);
  52207. }
  52208. };
  52209. /**
  52210. * Draw the scrollbar buttons with arrows
  52211. *
  52212. * @private
  52213. * @function Highcharts.Scrollbar#drawScrollbarButton
  52214. * @param {number} index
  52215. * 0 is left, 1 is right
  52216. * @return {void}
  52217. */
  52218. Scrollbar.prototype.drawScrollbarButton = function (index) {
  52219. var scroller = this,
  52220. renderer = scroller.renderer,
  52221. scrollbarButtons = scroller.scrollbarButtons,
  52222. options = scroller.options,
  52223. size = scroller.size,
  52224. group,
  52225. tempElem;
  52226. group = renderer.g().add(scroller.group);
  52227. scrollbarButtons.push(group);
  52228. // Create a rectangle for the scrollbar button
  52229. tempElem = renderer.rect()
  52230. .addClass('highcharts-scrollbar-button')
  52231. .add(group);
  52232. // Presentational attributes
  52233. if (!this.chart.styledMode) {
  52234. tempElem.attr({
  52235. stroke: options.buttonBorderColor,
  52236. 'stroke-width': options.buttonBorderWidth,
  52237. fill: options.buttonBackgroundColor
  52238. });
  52239. }
  52240. // Place the rectangle based on the rendered stroke width
  52241. tempElem.attr(tempElem.crisp({
  52242. x: -0.5,
  52243. y: -0.5,
  52244. width: size + 1,
  52245. height: size + 1,
  52246. r: options.buttonBorderRadius
  52247. }, tempElem.strokeWidth()));
  52248. // Button arrow
  52249. tempElem = renderer
  52250. .path(swapXY([[
  52251. 'M',
  52252. size / 2 + (index ? -1 : 1),
  52253. size / 2 - 3
  52254. ], [
  52255. 'L',
  52256. size / 2 + (index ? -1 : 1),
  52257. size / 2 + 3
  52258. ], [
  52259. 'L',
  52260. size / 2 + (index ? 2 : -2),
  52261. size / 2
  52262. ]], options.vertical))
  52263. .addClass('highcharts-scrollbar-arrow')
  52264. .add(scrollbarButtons[index]);
  52265. if (!this.chart.styledMode) {
  52266. tempElem.attr({
  52267. fill: options.buttonArrowColor
  52268. });
  52269. }
  52270. };
  52271. /**
  52272. * @private
  52273. * @function Highcharts.Scrollbar#init
  52274. * @param {Highcharts.SVGRenderer} renderer
  52275. * @param {Highcharts.ScrollbarOptions} options
  52276. * @param {Highcharts.Chart} chart
  52277. */
  52278. Scrollbar.prototype.init = function (renderer, options, chart) {
  52279. this.scrollbarButtons = [];
  52280. this.renderer = renderer;
  52281. this.userOptions = options;
  52282. this.options = merge(Scrollbar.defaultOptions, options);
  52283. this.chart = chart;
  52284. // backward compatibility
  52285. this.size = pick(this.options.size, this.options.height);
  52286. // Init
  52287. if (options.enabled) {
  52288. this.render();
  52289. this.addEvents();
  52290. }
  52291. };
  52292. Scrollbar.prototype.mouseDownHandler = function (e) {
  52293. var scroller = this;
  52294. var normalizedEvent = scroller.chart.pointer.normalize(e),
  52295. mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  52296. scroller.chartX = mousePosition.chartX;
  52297. scroller.chartY = mousePosition.chartY;
  52298. scroller.initPositions = [scroller.from, scroller.to];
  52299. scroller.grabbedCenter = true;
  52300. };
  52301. /**
  52302. * Event handler for the mouse move event.
  52303. * @private
  52304. */
  52305. Scrollbar.prototype.mouseMoveHandler = function (e) {
  52306. var scroller = this;
  52307. var normalizedEvent = scroller.chart.pointer.normalize(e),
  52308. options = scroller.options,
  52309. direction = options.vertical ? 'chartY' : 'chartX',
  52310. initPositions = scroller.initPositions || [],
  52311. scrollPosition,
  52312. chartPosition,
  52313. change;
  52314. // In iOS, a mousemove event with e.pageX === 0 is fired when
  52315. // holding the finger down in the center of the scrollbar. This
  52316. // should be ignored.
  52317. if (scroller.grabbedCenter &&
  52318. // #4696, scrollbar failed on Android
  52319. (!e.touches || e.touches[0][direction] !== 0)) {
  52320. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  52321. scrollPosition = scroller[direction];
  52322. change = chartPosition - scrollPosition;
  52323. scroller.hasDragged = true;
  52324. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  52325. if (scroller.hasDragged) {
  52326. fireEvent(scroller, 'changed', {
  52327. from: scroller.from,
  52328. to: scroller.to,
  52329. trigger: 'scrollbar',
  52330. DOMType: e.type,
  52331. DOMEvent: e
  52332. });
  52333. }
  52334. }
  52335. };
  52336. /**
  52337. * Event handler for the mouse up event.
  52338. * @private
  52339. */
  52340. Scrollbar.prototype.mouseUpHandler = function (e) {
  52341. var scroller = this;
  52342. if (scroller.hasDragged) {
  52343. fireEvent(scroller, 'changed', {
  52344. from: scroller.from,
  52345. to: scroller.to,
  52346. trigger: 'scrollbar',
  52347. DOMType: e.type,
  52348. DOMEvent: e
  52349. });
  52350. }
  52351. scroller.grabbedCenter =
  52352. scroller.hasDragged =
  52353. scroller.chartX =
  52354. scroller.chartY = null;
  52355. };
  52356. /**
  52357. * Position the scrollbar, method called from a parent with defined
  52358. * dimensions.
  52359. *
  52360. * @private
  52361. * @function Highcharts.Scrollbar#position
  52362. * @param {number} x
  52363. * x-position on the chart
  52364. * @param {number} y
  52365. * y-position on the chart
  52366. * @param {number} width
  52367. * width of the scrollbar
  52368. * @param {number} height
  52369. * height of the scorllbar
  52370. * @return {void}
  52371. */
  52372. Scrollbar.prototype.position = function (x, y, width, height) {
  52373. var scroller = this,
  52374. options = scroller.options,
  52375. vertical = options.vertical,
  52376. xOffset = height,
  52377. yOffset = 0,
  52378. method = scroller.rendered ? 'animate' : 'attr';
  52379. scroller.x = x;
  52380. scroller.y = y + this.trackBorderWidth;
  52381. scroller.width = width; // width with buttons
  52382. scroller.height = height;
  52383. scroller.xOffset = xOffset;
  52384. scroller.yOffset = yOffset;
  52385. // If Scrollbar is a vertical type, swap options:
  52386. if (vertical) {
  52387. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  52388. scroller.xOffset = xOffset = 0;
  52389. scroller.barWidth = height - width * 2; // width without buttons
  52390. scroller.x = x = x + scroller.options.margin;
  52391. }
  52392. else {
  52393. scroller.height = scroller.xOffset = height = xOffset =
  52394. scroller.size;
  52395. scroller.barWidth = width - height * 2; // width without buttons
  52396. scroller.y = scroller.y + scroller.options.margin;
  52397. }
  52398. // Set general position for a group:
  52399. scroller.group[method]({
  52400. translateX: x,
  52401. translateY: scroller.y
  52402. });
  52403. // Resize background/track:
  52404. scroller.track[method]({
  52405. width: width,
  52406. height: height
  52407. });
  52408. // Move right/bottom button ot it's place:
  52409. scroller.scrollbarButtons[1][method]({
  52410. translateX: vertical ? 0 : width - xOffset,
  52411. translateY: vertical ? height - yOffset : 0
  52412. });
  52413. };
  52414. /**
  52415. * Removes the event handlers attached previously with addEvents.
  52416. *
  52417. * @private
  52418. * @function Highcharts.Scrollbar#removeEvents
  52419. * @return {void}
  52420. */
  52421. Scrollbar.prototype.removeEvents = function () {
  52422. this._events.forEach(function (args) {
  52423. removeEvent.apply(null, args);
  52424. });
  52425. this._events.length = 0;
  52426. };
  52427. /**
  52428. * Render scrollbar with all required items.
  52429. *
  52430. * @private
  52431. * @function Highcharts.Scrollbar#render
  52432. */
  52433. Scrollbar.prototype.render = function () {
  52434. var scroller = this,
  52435. renderer = scroller.renderer,
  52436. options = scroller.options,
  52437. size = scroller.size,
  52438. styledMode = this.chart.styledMode,
  52439. group;
  52440. // Draw the scrollbar group
  52441. scroller.group = group = renderer.g('scrollbar').attr({
  52442. zIndex: options.zIndex,
  52443. translateY: -99999
  52444. }).add();
  52445. // Draw the scrollbar track:
  52446. scroller.track = renderer.rect()
  52447. .addClass('highcharts-scrollbar-track')
  52448. .attr({
  52449. x: 0,
  52450. r: options.trackBorderRadius || 0,
  52451. height: size,
  52452. width: size
  52453. }).add(group);
  52454. if (!styledMode) {
  52455. scroller.track.attr({
  52456. fill: options.trackBackgroundColor,
  52457. stroke: options.trackBorderColor,
  52458. 'stroke-width': options.trackBorderWidth
  52459. });
  52460. }
  52461. this.trackBorderWidth = scroller.track.strokeWidth();
  52462. scroller.track.attr({
  52463. y: -this.trackBorderWidth % 2 / 2
  52464. });
  52465. // Draw the scrollbar itself
  52466. scroller.scrollbarGroup = renderer.g().add(group);
  52467. scroller.scrollbar = renderer.rect()
  52468. .addClass('highcharts-scrollbar-thumb')
  52469. .attr({
  52470. height: size,
  52471. width: size,
  52472. r: options.barBorderRadius || 0
  52473. }).add(scroller.scrollbarGroup);
  52474. scroller.scrollbarRifles = renderer
  52475. .path(swapXY([
  52476. ['M', -3, size / 4],
  52477. ['L', -3, 2 * size / 3],
  52478. ['M', 0, size / 4],
  52479. ['L', 0, 2 * size / 3],
  52480. ['M', 3, size / 4],
  52481. ['L', 3, 2 * size / 3]
  52482. ], options.vertical))
  52483. .addClass('highcharts-scrollbar-rifles')
  52484. .add(scroller.scrollbarGroup);
  52485. if (!styledMode) {
  52486. scroller.scrollbar.attr({
  52487. fill: options.barBackgroundColor,
  52488. stroke: options.barBorderColor,
  52489. 'stroke-width': options.barBorderWidth
  52490. });
  52491. scroller.scrollbarRifles.attr({
  52492. stroke: options.rifleColor,
  52493. 'stroke-width': 1
  52494. });
  52495. }
  52496. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  52497. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  52498. // Draw the buttons:
  52499. scroller.drawScrollbarButton(0);
  52500. scroller.drawScrollbarButton(1);
  52501. };
  52502. /**
  52503. * Set scrollbar size, with a given scale.
  52504. *
  52505. * @private
  52506. * @function Highcharts.Scrollbar#setRange
  52507. * @param {number} from
  52508. * scale (0-1) where bar should start
  52509. * @param {number} to
  52510. * scale (0-1) where bar should end
  52511. * @return {void}
  52512. */
  52513. Scrollbar.prototype.setRange = function (from, to) {
  52514. var scroller = this,
  52515. options = scroller.options,
  52516. vertical = options.vertical,
  52517. minWidth = options.minWidth,
  52518. fullWidth = scroller.barWidth,
  52519. fromPX,
  52520. toPX,
  52521. newPos,
  52522. newSize,
  52523. newRiflesPos,
  52524. method = (this.rendered &&
  52525. !this.hasDragged &&
  52526. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  52527. if (!defined(fullWidth)) {
  52528. return;
  52529. }
  52530. from = Math.max(from, 0);
  52531. fromPX = Math.ceil(fullWidth * from);
  52532. toPX = fullWidth * Math.min(to, 1);
  52533. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  52534. // We need to recalculate position, if minWidth is used
  52535. if (newSize < minWidth) {
  52536. fromPX = (fullWidth - minWidth + newSize) * from;
  52537. newSize = minWidth;
  52538. }
  52539. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  52540. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  52541. // Store current position:
  52542. scroller.from = from;
  52543. scroller.to = to;
  52544. if (!vertical) {
  52545. scroller.scrollbarGroup[method]({
  52546. translateX: newPos
  52547. });
  52548. scroller.scrollbar[method]({
  52549. width: newSize
  52550. });
  52551. scroller.scrollbarRifles[method]({
  52552. translateX: newRiflesPos
  52553. });
  52554. scroller.scrollbarLeft = newPos;
  52555. scroller.scrollbarTop = 0;
  52556. }
  52557. else {
  52558. scroller.scrollbarGroup[method]({
  52559. translateY: newPos
  52560. });
  52561. scroller.scrollbar[method]({
  52562. height: newSize
  52563. });
  52564. scroller.scrollbarRifles[method]({
  52565. translateY: newRiflesPos
  52566. });
  52567. scroller.scrollbarTop = newPos;
  52568. scroller.scrollbarLeft = 0;
  52569. }
  52570. if (newSize <= 12) {
  52571. scroller.scrollbarRifles.hide();
  52572. }
  52573. else {
  52574. scroller.scrollbarRifles.show(true);
  52575. }
  52576. // Show or hide the scrollbar based on the showFull setting
  52577. if (options.showFull === false) {
  52578. if (from <= 0 && to >= 1) {
  52579. scroller.group.hide();
  52580. }
  52581. else {
  52582. scroller.group.show();
  52583. }
  52584. }
  52585. scroller.rendered = true;
  52586. };
  52587. Scrollbar.prototype.trackClick = function (e) {
  52588. var scroller = this;
  52589. var normalizedEvent = scroller.chart.pointer.normalize(e),
  52590. range = scroller.to - scroller.from,
  52591. top = scroller.y + scroller.scrollbarTop,
  52592. left = scroller.x + scroller.scrollbarLeft;
  52593. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  52594. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  52595. // On the top or on the left side of the track:
  52596. scroller.updatePosition(scroller.from + range, scroller.to + range);
  52597. }
  52598. else {
  52599. // On the bottom or the right side of the track:
  52600. scroller.updatePosition(scroller.from - range, scroller.to - range);
  52601. }
  52602. fireEvent(scroller, 'changed', {
  52603. from: scroller.from,
  52604. to: scroller.to,
  52605. trigger: 'scrollbar',
  52606. DOMEvent: e
  52607. });
  52608. };
  52609. /**
  52610. * Update the scrollbar with new options
  52611. *
  52612. * @private
  52613. * @function Highcharts.Scrollbar#update
  52614. * @param {Highcharts.ScrollbarOptions} options
  52615. * @return {void}
  52616. */
  52617. Scrollbar.prototype.update = function (options) {
  52618. this.destroy();
  52619. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  52620. };
  52621. /**
  52622. * Update position option in the Scrollbar, with normalized 0-1 scale
  52623. *
  52624. * @private
  52625. * @function Highcharts.Scrollbar#updatePosition
  52626. * @param {number} from
  52627. * @param {number} to
  52628. * @return {void}
  52629. */
  52630. Scrollbar.prototype.updatePosition = function (from, to) {
  52631. if (to > 1) {
  52632. from = correctFloat(1 - correctFloat(to - from));
  52633. to = 1;
  52634. }
  52635. if (from < 0) {
  52636. to = correctFloat(to - from);
  52637. from = 0;
  52638. }
  52639. this.from = from;
  52640. this.to = to;
  52641. };
  52642. /* *
  52643. *
  52644. * Static Properties
  52645. *
  52646. * */
  52647. /**
  52648. *
  52649. * The scrollbar is a means of panning over the X axis of a stock chart.
  52650. * Scrollbars can also be applied to other types of axes.
  52651. *
  52652. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  52653. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  52654. * is especially suitable for simpler cartesian charts on mobile.
  52655. *
  52656. * In styled mode, all the presentational options for the
  52657. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  52658. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  52659. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  52660. *
  52661. * @sample stock/yaxis/inverted-bar-scrollbar/
  52662. * A scrollbar on a simple bar chart
  52663. *
  52664. * @product highstock gantt
  52665. * @optionparent scrollbar
  52666. *
  52667. * @private
  52668. */
  52669. Scrollbar.defaultOptions = {
  52670. /**
  52671. * The height of the scrollbar. The height also applies to the width
  52672. * of the scroll arrows so that they are always squares. Defaults to
  52673. * 20 for touch devices and 14 for mouse devices.
  52674. *
  52675. * @sample stock/scrollbar/height/
  52676. * A 30px scrollbar
  52677. *
  52678. * @type {number}
  52679. * @default 20/14
  52680. */
  52681. height: isTouchDevice ? 20 : 14,
  52682. /**
  52683. * The border rounding radius of the bar.
  52684. *
  52685. * @sample stock/scrollbar/style/
  52686. * Scrollbar styling
  52687. */
  52688. barBorderRadius: 0,
  52689. /**
  52690. * The corner radius of the scrollbar buttons.
  52691. *
  52692. * @sample stock/scrollbar/style/
  52693. * Scrollbar styling
  52694. */
  52695. buttonBorderRadius: 0,
  52696. /**
  52697. * Enable or disable the scrollbar.
  52698. *
  52699. * @sample stock/scrollbar/enabled/
  52700. * Disable the scrollbar, only use navigator
  52701. *
  52702. * @type {boolean}
  52703. * @default true
  52704. * @apioption scrollbar.enabled
  52705. */
  52706. /**
  52707. * Whether to redraw the main chart as the scrollbar or the navigator
  52708. * zoomed window is moved. Defaults to `true` for modern browsers and
  52709. * `false` for legacy IE browsers as well as mobile devices.
  52710. *
  52711. * @sample stock/scrollbar/liveredraw
  52712. * Setting live redraw to false
  52713. *
  52714. * @type {boolean}
  52715. * @since 1.3
  52716. */
  52717. liveRedraw: void 0,
  52718. /**
  52719. * The margin between the scrollbar and its axis when the scrollbar is
  52720. * applied directly to an axis.
  52721. */
  52722. margin: 10,
  52723. /**
  52724. * The minimum width of the scrollbar.
  52725. *
  52726. * @since 1.2.5
  52727. */
  52728. minWidth: 6,
  52729. /**
  52730. * Whether to show or hide the scrollbar when the scrolled content is
  52731. * zoomed out to it full extent.
  52732. *
  52733. * @type {boolean}
  52734. * @default true
  52735. * @apioption scrollbar.showFull
  52736. */
  52737. step: 0.2,
  52738. /**
  52739. * The z index of the scrollbar group.
  52740. */
  52741. zIndex: 3,
  52742. /**
  52743. * The background color of the scrollbar itself.
  52744. *
  52745. * @sample stock/scrollbar/style/
  52746. * Scrollbar styling
  52747. *
  52748. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52749. */
  52750. barBackgroundColor: '#cccccc',
  52751. /**
  52752. * The width of the bar's border.
  52753. *
  52754. * @sample stock/scrollbar/style/
  52755. * Scrollbar styling
  52756. */
  52757. barBorderWidth: 1,
  52758. /**
  52759. * The color of the scrollbar's border.
  52760. *
  52761. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52762. */
  52763. barBorderColor: '#cccccc',
  52764. /**
  52765. * The color of the small arrow inside the scrollbar buttons.
  52766. *
  52767. * @sample stock/scrollbar/style/
  52768. * Scrollbar styling
  52769. *
  52770. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52771. */
  52772. buttonArrowColor: '#333333',
  52773. /**
  52774. * The color of scrollbar buttons.
  52775. *
  52776. * @sample stock/scrollbar/style/
  52777. * Scrollbar styling
  52778. *
  52779. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52780. */
  52781. buttonBackgroundColor: '#e6e6e6',
  52782. /**
  52783. * The color of the border of the scrollbar buttons.
  52784. *
  52785. * @sample stock/scrollbar/style/
  52786. * Scrollbar styling
  52787. *
  52788. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52789. */
  52790. buttonBorderColor: '#cccccc',
  52791. /**
  52792. * The border width of the scrollbar buttons.
  52793. *
  52794. * @sample stock/scrollbar/style/
  52795. * Scrollbar styling
  52796. */
  52797. buttonBorderWidth: 1,
  52798. /**
  52799. * The color of the small rifles in the middle of the scrollbar.
  52800. *
  52801. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52802. */
  52803. rifleColor: '#333333',
  52804. /**
  52805. * The color of the track background.
  52806. *
  52807. * @sample stock/scrollbar/style/
  52808. * Scrollbar styling
  52809. *
  52810. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52811. */
  52812. trackBackgroundColor: '#f2f2f2',
  52813. /**
  52814. * The color of the border of the scrollbar track.
  52815. *
  52816. * @sample stock/scrollbar/style/
  52817. * Scrollbar styling
  52818. *
  52819. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  52820. */
  52821. trackBorderColor: '#f2f2f2',
  52822. /**
  52823. * The corner radius of the border of the scrollbar track.
  52824. *
  52825. * @sample stock/scrollbar/style/
  52826. * Scrollbar styling
  52827. *
  52828. * @type {number}
  52829. * @default 0
  52830. * @apioption scrollbar.trackBorderRadius
  52831. */
  52832. /**
  52833. * The width of the border of the scrollbar track.
  52834. *
  52835. * @sample stock/scrollbar/style/
  52836. * Scrollbar styling
  52837. */
  52838. trackBorderWidth: 1
  52839. };
  52840. return Scrollbar;
  52841. }());
  52842. if (!H.Scrollbar) {
  52843. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  52844. H.Scrollbar = Scrollbar;
  52845. ScrollbarAxis.compose(Axis, Scrollbar);
  52846. }
  52847. return H.Scrollbar;
  52848. });
  52849. _registerModule(_modules, 'Extensions/RangeSelector.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, O, SVGElement, U) {
  52850. /* *
  52851. *
  52852. * (c) 2010-2020 Torstein Honsi
  52853. *
  52854. * License: www.highcharts.com/license
  52855. *
  52856. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  52857. *
  52858. * */
  52859. var defaultOptions = O.defaultOptions;
  52860. var addEvent = U.addEvent,
  52861. createElement = U.createElement,
  52862. css = U.css,
  52863. defined = U.defined,
  52864. destroyObjectProperties = U.destroyObjectProperties,
  52865. discardElement = U.discardElement,
  52866. extend = U.extend,
  52867. fireEvent = U.fireEvent,
  52868. isNumber = U.isNumber,
  52869. merge = U.merge,
  52870. objectEach = U.objectEach,
  52871. pick = U.pick,
  52872. pInt = U.pInt,
  52873. splat = U.splat;
  52874. /**
  52875. * Define the time span for the button
  52876. *
  52877. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  52878. */
  52879. /**
  52880. * Callback function to react on button clicks.
  52881. *
  52882. * @callback Highcharts.RangeSelectorClickCallbackFunction
  52883. *
  52884. * @param {global.Event} e
  52885. * Event arguments.
  52886. *
  52887. * @param {boolean|undefined}
  52888. * Return false to cancel the default button event.
  52889. */
  52890. /**
  52891. * Callback function to parse values entered in the input boxes and return a
  52892. * valid JavaScript time as milliseconds since 1970.
  52893. *
  52894. * @callback Highcharts.RangeSelectorParseCallbackFunction
  52895. *
  52896. * @param {string} value
  52897. * Input value to parse.
  52898. *
  52899. * @return {number}
  52900. * Parsed JavaScript time value.
  52901. */
  52902. /* ************************************************************************** *
  52903. * Start Range Selector code *
  52904. * ************************************************************************** */
  52905. extend(defaultOptions, {
  52906. /**
  52907. * The range selector is a tool for selecting ranges to display within
  52908. * the chart. It provides buttons to select preconfigured ranges in
  52909. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  52910. * boxes where min and max dates can be manually input.
  52911. *
  52912. * @product highstock gantt
  52913. * @optionparent rangeSelector
  52914. */
  52915. rangeSelector: {
  52916. /**
  52917. * Whether to enable all buttons from the start. By default buttons are
  52918. * only enabled if the corresponding time range exists on the X axis,
  52919. * but enabling all buttons allows for dynamically loading different
  52920. * time ranges.
  52921. *
  52922. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  52923. * All buttons enabled
  52924. *
  52925. * @type {boolean}
  52926. * @default false
  52927. * @since 2.0.3
  52928. * @apioption rangeSelector.allButtonsEnabled
  52929. */
  52930. /**
  52931. * An array of configuration objects for the buttons.
  52932. *
  52933. * Defaults to:
  52934. * ```js
  52935. * buttons: [{
  52936. * type: 'month',
  52937. * count: 1,
  52938. * text: '1m'
  52939. * }, {
  52940. * type: 'month',
  52941. * count: 3,
  52942. * text: '3m'
  52943. * }, {
  52944. * type: 'month',
  52945. * count: 6,
  52946. * text: '6m'
  52947. * }, {
  52948. * type: 'ytd',
  52949. * text: 'YTD'
  52950. * }, {
  52951. * type: 'year',
  52952. * count: 1,
  52953. * text: '1y'
  52954. * }, {
  52955. * type: 'all',
  52956. * text: 'All'
  52957. * }]
  52958. * ```
  52959. *
  52960. * @sample {highstock} stock/rangeselector/datagrouping/
  52961. * Data grouping by buttons
  52962. *
  52963. * @type {Array<*>}
  52964. * @apioption rangeSelector.buttons
  52965. */
  52966. /**
  52967. * How many units of the defined type the button should span. If `type`
  52968. * is "month" and `count` is 3, the button spans three months.
  52969. *
  52970. * @type {number}
  52971. * @default 1
  52972. * @apioption rangeSelector.buttons.count
  52973. */
  52974. /**
  52975. * Fires when clicking on the rangeSelector button. One parameter,
  52976. * event, is passed to the function, containing common event
  52977. * information.
  52978. *
  52979. * ```js
  52980. * click: function(e) {
  52981. * console.log(this);
  52982. * }
  52983. * ```
  52984. *
  52985. * Return false to stop default button's click action.
  52986. *
  52987. * @sample {highstock} stock/rangeselector/button-click/
  52988. * Click event on the button
  52989. *
  52990. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  52991. * @apioption rangeSelector.buttons.events.click
  52992. */
  52993. /**
  52994. * Additional range (in milliseconds) added to the end of the calculated
  52995. * time span.
  52996. *
  52997. * @sample {highstock} stock/rangeselector/min-max-offsets/
  52998. * Button offsets
  52999. *
  53000. * @type {number}
  53001. * @default 0
  53002. * @since 6.0.0
  53003. * @apioption rangeSelector.buttons.offsetMax
  53004. */
  53005. /**
  53006. * Additional range (in milliseconds) added to the start of the
  53007. * calculated time span.
  53008. *
  53009. * @sample {highstock} stock/rangeselector/min-max-offsets/
  53010. * Button offsets
  53011. *
  53012. * @type {number}
  53013. * @default 0
  53014. * @since 6.0.0
  53015. * @apioption rangeSelector.buttons.offsetMin
  53016. */
  53017. /**
  53018. * When buttons apply dataGrouping on a series, by default zooming
  53019. * in/out will deselect buttons and unset dataGrouping. Enable this
  53020. * option to keep buttons selected when extremes change.
  53021. *
  53022. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  53023. * Different preserveDataGrouping settings
  53024. *
  53025. * @type {boolean}
  53026. * @default false
  53027. * @since 6.1.2
  53028. * @apioption rangeSelector.buttons.preserveDataGrouping
  53029. */
  53030. /**
  53031. * A custom data grouping object for each button.
  53032. *
  53033. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  53034. *
  53035. * @sample {highstock} stock/rangeselector/datagrouping/
  53036. * Data grouping by range selector buttons
  53037. *
  53038. * @type {*}
  53039. * @extends plotOptions.series.dataGrouping
  53040. * @apioption rangeSelector.buttons.dataGrouping
  53041. */
  53042. /**
  53043. * The text for the button itself.
  53044. *
  53045. * @type {string}
  53046. * @apioption rangeSelector.buttons.text
  53047. */
  53048. /**
  53049. * Defined the time span for the button. Can be one of `millisecond`,
  53050. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  53051. * and `all`.
  53052. *
  53053. * @type {Highcharts.RangeSelectorButtonTypeValue}
  53054. * @apioption rangeSelector.buttons.type
  53055. */
  53056. /**
  53057. * The space in pixels between the buttons in the range selector.
  53058. *
  53059. * @type {number}
  53060. * @default 0
  53061. * @apioption rangeSelector.buttonSpacing
  53062. */
  53063. /**
  53064. * Enable or disable the range selector.
  53065. *
  53066. * @sample {highstock} stock/rangeselector/enabled/
  53067. * Disable the range selector
  53068. *
  53069. * @type {boolean}
  53070. * @default true
  53071. * @apioption rangeSelector.enabled
  53072. */
  53073. /**
  53074. * The vertical alignment of the rangeselector box. Allowed properties
  53075. * are `top`, `middle`, `bottom`.
  53076. *
  53077. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  53078. * Middle
  53079. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  53080. * Bottom
  53081. *
  53082. * @type {Highcharts.VerticalAlignValue}
  53083. * @since 6.0.0
  53084. */
  53085. verticalAlign: 'top',
  53086. /**
  53087. * A collection of attributes for the buttons. The object takes SVG
  53088. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  53089. * a collection of CSS properties for the text.
  53090. *
  53091. * The object can also be extended with states, so you can set
  53092. * presentational options for `hover`, `select` or `disabled` button
  53093. * states.
  53094. *
  53095. * CSS styles for the text label.
  53096. *
  53097. * In styled mode, the buttons are styled by the
  53098. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  53099. * different states.
  53100. *
  53101. * @sample {highstock} stock/rangeselector/styling/
  53102. * Styling the buttons and inputs
  53103. *
  53104. * @type {Highcharts.SVGAttributes}
  53105. */
  53106. buttonTheme: {
  53107. /** @ignore */
  53108. width: 28,
  53109. /** @ignore */
  53110. height: 18,
  53111. /** @ignore */
  53112. padding: 2,
  53113. /** @ignore */
  53114. zIndex: 7 // #484, #852
  53115. },
  53116. /**
  53117. * When the rangeselector is floating, the plot area does not reserve
  53118. * space for it. This opens for positioning anywhere on the chart.
  53119. *
  53120. * @sample {highstock} stock/rangeselector/floating/
  53121. * Placing the range selector between the plot area and the
  53122. * navigator
  53123. *
  53124. * @since 6.0.0
  53125. */
  53126. floating: false,
  53127. /**
  53128. * The x offset of the range selector relative to its horizontal
  53129. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  53130. *
  53131. * @since 6.0.0
  53132. */
  53133. x: 0,
  53134. /**
  53135. * The y offset of the range selector relative to its horizontal
  53136. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  53137. *
  53138. * @since 6.0.0
  53139. */
  53140. y: 0,
  53141. /**
  53142. * Deprecated. The height of the range selector. Currently it is
  53143. * calculated dynamically.
  53144. *
  53145. * @deprecated
  53146. * @type {number|undefined}
  53147. * @since 2.1.9
  53148. */
  53149. height: void 0,
  53150. /**
  53151. * The border color of the date input boxes.
  53152. *
  53153. * @sample {highstock} stock/rangeselector/styling/
  53154. * Styling the buttons and inputs
  53155. *
  53156. * @type {Highcharts.ColorString}
  53157. * @default #cccccc
  53158. * @since 1.3.7
  53159. * @apioption rangeSelector.inputBoxBorderColor
  53160. */
  53161. /**
  53162. * The pixel height of the date input boxes.
  53163. *
  53164. * @sample {highstock} stock/rangeselector/styling/
  53165. * Styling the buttons and inputs
  53166. *
  53167. * @type {number}
  53168. * @default 17
  53169. * @since 1.3.7
  53170. * @apioption rangeSelector.inputBoxHeight
  53171. */
  53172. /**
  53173. * CSS for the container DIV holding the input boxes. Deprecated as
  53174. * of 1.2.5\. Use [inputPosition](#rangeSelector.inputPosition) instead.
  53175. *
  53176. * @sample {highstock} stock/rangeselector/styling/
  53177. * Styling the buttons and inputs
  53178. *
  53179. * @deprecated
  53180. * @type {Highcharts.CSSObject}
  53181. * @apioption rangeSelector.inputBoxStyle
  53182. */
  53183. /**
  53184. * The pixel width of the date input boxes.
  53185. *
  53186. * @sample {highstock} stock/rangeselector/styling/
  53187. * Styling the buttons and inputs
  53188. *
  53189. * @type {number}
  53190. * @default 90
  53191. * @since 1.3.7
  53192. * @apioption rangeSelector.inputBoxWidth
  53193. */
  53194. /**
  53195. * The date format in the input boxes when not selected for editing.
  53196. * Defaults to `%b %e, %Y`.
  53197. *
  53198. * @sample {highstock} stock/rangeselector/input-format/
  53199. * Milliseconds in the range selector
  53200. *
  53201. * @type {string}
  53202. * @default %b %e, %Y
  53203. * @apioption rangeSelector.inputDateFormat
  53204. */
  53205. /**
  53206. * A custom callback function to parse values entered in the input boxes
  53207. * and return a valid JavaScript time as milliseconds since 1970.
  53208. * The first argument passed is a value to parse,
  53209. * second is a boolean indicating use of the UTC time.
  53210. *
  53211. * @sample {highstock} stock/rangeselector/input-format/
  53212. * Milliseconds in the range selector
  53213. *
  53214. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  53215. * @since 1.3.3
  53216. * @apioption rangeSelector.inputDateParser
  53217. */
  53218. /**
  53219. * The date format in the input boxes when they are selected for
  53220. * editing. This must be a format that is recognized by JavaScript
  53221. * Date.parse.
  53222. *
  53223. * @sample {highstock} stock/rangeselector/input-format/
  53224. * Milliseconds in the range selector
  53225. *
  53226. * @type {string}
  53227. * @default %Y-%m-%d
  53228. * @apioption rangeSelector.inputEditDateFormat
  53229. */
  53230. /**
  53231. * Enable or disable the date input boxes. Defaults to enabled when
  53232. * there is enough space, disabled if not (typically mobile).
  53233. *
  53234. * @sample {highstock} stock/rangeselector/input-datepicker/
  53235. * Extending the input with a jQuery UI datepicker
  53236. *
  53237. * @type {boolean}
  53238. * @default true
  53239. * @apioption rangeSelector.inputEnabled
  53240. */
  53241. /**
  53242. * Positioning for the input boxes. Allowed properties are `align`,
  53243. * `x` and `y`.
  53244. *
  53245. * @since 1.2.4
  53246. */
  53247. inputPosition: {
  53248. /**
  53249. * The alignment of the input box. Allowed properties are `left`,
  53250. * `center`, `right`.
  53251. *
  53252. * @sample {highstock} stock/rangeselector/input-button-position/
  53253. * Alignment
  53254. *
  53255. * @type {Highcharts.AlignValue}
  53256. * @since 6.0.0
  53257. */
  53258. align: 'right',
  53259. /**
  53260. * X offset of the input row.
  53261. */
  53262. x: 0,
  53263. /**
  53264. * Y offset of the input row.
  53265. */
  53266. y: 0
  53267. },
  53268. /**
  53269. * The index of the button to appear pre-selected.
  53270. *
  53271. * @type {number}
  53272. * @apioption rangeSelector.selected
  53273. */
  53274. /**
  53275. * Positioning for the button row.
  53276. *
  53277. * @since 1.2.4
  53278. */
  53279. buttonPosition: {
  53280. /**
  53281. * The alignment of the input box. Allowed properties are `left`,
  53282. * `center`, `right`.
  53283. *
  53284. * @sample {highstock} stock/rangeselector/input-button-position/
  53285. * Alignment
  53286. *
  53287. * @type {Highcharts.AlignValue}
  53288. * @since 6.0.0
  53289. */
  53290. align: 'left',
  53291. /**
  53292. * X offset of the button row.
  53293. */
  53294. x: 0,
  53295. /**
  53296. * Y offset of the button row.
  53297. */
  53298. y: 0
  53299. },
  53300. /**
  53301. * CSS for the HTML inputs in the range selector.
  53302. *
  53303. * In styled mode, the inputs are styled by the
  53304. * `.highcharts-range-input text` rule in SVG mode, and
  53305. * `input.highcharts-range-selector` when active.
  53306. *
  53307. * @sample {highstock} stock/rangeselector/styling/
  53308. * Styling the buttons and inputs
  53309. *
  53310. * @type {Highcharts.CSSObject}
  53311. * @apioption rangeSelector.inputStyle
  53312. */
  53313. /**
  53314. * CSS styles for the labels - the Zoom, From and To texts.
  53315. *
  53316. * In styled mode, the labels are styled by the
  53317. * `.highcharts-range-label` class.
  53318. *
  53319. * @sample {highstock} stock/rangeselector/styling/
  53320. * Styling the buttons and inputs
  53321. *
  53322. * @type {Highcharts.CSSObject}
  53323. */
  53324. labelStyle: {
  53325. /** @ignore */
  53326. color: '#666666'
  53327. }
  53328. }
  53329. });
  53330. defaultOptions.lang = merge(defaultOptions.lang,
  53331. /**
  53332. * Language object. The language object is global and it can't be set
  53333. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  53334. * set it before any chart is initialized.
  53335. *
  53336. * ```js
  53337. * Highcharts.setOptions({
  53338. * lang: {
  53339. * months: [
  53340. * 'Janvier', 'Février', 'Mars', 'Avril',
  53341. * 'Mai', 'Juin', 'Juillet', 'Août',
  53342. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  53343. * ],
  53344. * weekdays: [
  53345. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  53346. * 'Jeudi', 'Vendredi', 'Samedi'
  53347. * ]
  53348. * }
  53349. * });
  53350. * ```
  53351. *
  53352. * @optionparent lang
  53353. */
  53354. {
  53355. /**
  53356. * The text for the label for the range selector buttons.
  53357. *
  53358. * @product highstock gantt
  53359. */
  53360. rangeSelectorZoom: 'Zoom',
  53361. /**
  53362. * The text for the label for the "from" input box in the range
  53363. * selector.
  53364. *
  53365. * @product highstock gantt
  53366. */
  53367. rangeSelectorFrom: 'From',
  53368. /**
  53369. * The text for the label for the "to" input box in the range selector.
  53370. *
  53371. * @product highstock gantt
  53372. */
  53373. rangeSelectorTo: 'To'
  53374. });
  53375. /* eslint-disable no-invalid-this, valid-jsdoc */
  53376. /**
  53377. * The range selector.
  53378. *
  53379. * @private
  53380. * @class
  53381. * @name Highcharts.RangeSelector
  53382. * @param {Highcharts.Chart} chart
  53383. */
  53384. var RangeSelector = /** @class */ (function () {
  53385. function RangeSelector(chart) {
  53386. /* *
  53387. *
  53388. * Properties
  53389. *
  53390. * */
  53391. this.buttons = void 0;
  53392. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  53393. this.options = void 0;
  53394. this.chart = chart;
  53395. // Run RangeSelector
  53396. this.init(chart);
  53397. }
  53398. /**
  53399. * The method to run when one of the buttons in the range selectors is
  53400. * clicked
  53401. *
  53402. * @private
  53403. * @function Highcharts.RangeSelector#clickButton
  53404. * @param {number} i
  53405. * The index of the button
  53406. * @param {boolean} [redraw]
  53407. * @return {void}
  53408. */
  53409. RangeSelector.prototype.clickButton = function (i, redraw) {
  53410. var rangeSelector = this,
  53411. chart = rangeSelector.chart,
  53412. rangeOptions = rangeSelector.buttonOptions[i],
  53413. baseAxis = chart.xAxis[0],
  53414. unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
  53415. dataMin = unionExtremes.dataMin,
  53416. dataMax = unionExtremes.dataMax,
  53417. newMin,
  53418. newMax = baseAxis && Math.round(Math.min(baseAxis.max,
  53419. pick(dataMax,
  53420. baseAxis.max))), // #1568
  53421. type = rangeOptions.type,
  53422. baseXAxisOptions,
  53423. range = rangeOptions._range,
  53424. rangeMin,
  53425. minSetting,
  53426. rangeSetting,
  53427. ctx,
  53428. ytdExtremes,
  53429. dataGrouping = rangeOptions.dataGrouping;
  53430. // chart has no data, base series is removed
  53431. if (dataMin === null || dataMax === null) {
  53432. return;
  53433. }
  53434. // Set the fixed range before range is altered
  53435. chart.fixedRange = range;
  53436. // Apply dataGrouping associated to button
  53437. if (dataGrouping) {
  53438. this.forcedDataGrouping = true;
  53439. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  53440. this.frozenStates = rangeOptions.preserveDataGrouping;
  53441. }
  53442. // Apply range
  53443. if (type === 'month' || type === 'year') {
  53444. if (!baseAxis) {
  53445. // This is set to the user options and picked up later when the
  53446. // axis is instantiated so that we know the min and max.
  53447. range = rangeOptions;
  53448. }
  53449. else {
  53450. ctx = {
  53451. range: rangeOptions,
  53452. max: newMax,
  53453. chart: chart,
  53454. dataMin: dataMin,
  53455. dataMax: dataMax
  53456. };
  53457. newMin = baseAxis.minFromRange.call(ctx);
  53458. if (isNumber(ctx.newMax)) {
  53459. newMax = ctx.newMax;
  53460. }
  53461. }
  53462. // Fixed times like minutes, hours, days
  53463. }
  53464. else if (range) {
  53465. newMin = Math.max(newMax - range, dataMin);
  53466. newMax = Math.min(newMin + range, dataMax);
  53467. }
  53468. else if (type === 'ytd') {
  53469. // On user clicks on the buttons, or a delayed action running from
  53470. // the beforeRender event (below), the baseAxis is defined.
  53471. if (baseAxis) {
  53472. // When "ytd" is the pre-selected button for the initial view,
  53473. // its calculation is delayed and rerun in the beforeRender
  53474. // event (below). When the series are initialized, but before
  53475. // the chart is rendered, we have access to the xData array
  53476. // (#942).
  53477. if (typeof dataMax === 'undefined') {
  53478. dataMin = Number.MAX_VALUE;
  53479. dataMax = Number.MIN_VALUE;
  53480. chart.series.forEach(function (series) {
  53481. // reassign it to the last item
  53482. var xData = series.xData;
  53483. dataMin = Math.min(xData[0], dataMin);
  53484. dataMax = Math.max(xData[xData.length - 1], dataMax);
  53485. });
  53486. redraw = false;
  53487. }
  53488. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  53489. newMin = rangeMin = ytdExtremes.min;
  53490. newMax = ytdExtremes.max;
  53491. // "ytd" is pre-selected. We don't yet have access to processed
  53492. // point and extremes data (things like pointStart and pointInterval
  53493. // are missing), so we delay the process (#942)
  53494. }
  53495. else {
  53496. rangeSelector.deferredYTDClick = i;
  53497. return;
  53498. }
  53499. }
  53500. else if (type === 'all' && baseAxis) {
  53501. newMin = dataMin;
  53502. newMax = dataMax;
  53503. }
  53504. if (defined(newMin)) {
  53505. newMin += rangeOptions._offsetMin;
  53506. }
  53507. if (defined(newMax)) {
  53508. newMax += rangeOptions._offsetMax;
  53509. }
  53510. rangeSelector.setSelected(i);
  53511. // Update the chart
  53512. if (!baseAxis) {
  53513. // Axis not yet instanciated. Temporarily set min and range
  53514. // options and remove them on chart load (#4317).
  53515. baseXAxisOptions = splat(chart.options.xAxis)[0];
  53516. rangeSetting = baseXAxisOptions.range;
  53517. baseXAxisOptions.range = range;
  53518. minSetting = baseXAxisOptions.min;
  53519. baseXAxisOptions.min = rangeMin;
  53520. addEvent(chart, 'load', function resetMinAndRange() {
  53521. baseXAxisOptions.range = rangeSetting;
  53522. baseXAxisOptions.min = minSetting;
  53523. });
  53524. }
  53525. else {
  53526. // Existing axis object. Set extremes after render time.
  53527. baseAxis.setExtremes(newMin, newMax, pick(redraw, 1), null, // auto animation
  53528. {
  53529. trigger: 'rangeSelectorButton',
  53530. rangeSelectorButton: rangeOptions
  53531. });
  53532. }
  53533. };
  53534. /**
  53535. * Set the selected option. This method only sets the internal flag, it
  53536. * doesn't update the buttons or the actual zoomed range.
  53537. *
  53538. * @private
  53539. * @function Highcharts.RangeSelector#setSelected
  53540. * @param {number} [selected]
  53541. * @return {void}
  53542. */
  53543. RangeSelector.prototype.setSelected = function (selected) {
  53544. this.selected = this.options.selected = selected;
  53545. };
  53546. /**
  53547. * Initialize the range selector
  53548. *
  53549. * @private
  53550. * @function Highcharts.RangeSelector#init
  53551. * @param {Highcharts.Chart} chart
  53552. * @return {void}
  53553. */
  53554. RangeSelector.prototype.init = function (chart) {
  53555. var rangeSelector = this,
  53556. options = chart.options.rangeSelector,
  53557. buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(),
  53558. selectedOption = options.selected,
  53559. blurInputs = function () {
  53560. var minInput = rangeSelector.minInput,
  53561. maxInput = rangeSelector.maxInput;
  53562. // #3274 in some case blur is not defined
  53563. if (minInput && minInput.blur) {
  53564. fireEvent(minInput, 'blur');
  53565. }
  53566. if (maxInput && maxInput.blur) {
  53567. fireEvent(maxInput, 'blur');
  53568. }
  53569. };
  53570. rangeSelector.chart = chart;
  53571. rangeSelector.options = options;
  53572. rangeSelector.buttons = [];
  53573. rangeSelector.buttonOptions = buttonOptions;
  53574. this.unMouseDown = addEvent(chart.container, 'mousedown', blurInputs);
  53575. this.unResize = addEvent(chart, 'resize', blurInputs);
  53576. // Extend the buttonOptions with actual range
  53577. buttonOptions.forEach(rangeSelector.computeButtonRange);
  53578. // zoomed range based on a pre-selected button index
  53579. if (typeof selectedOption !== 'undefined' &&
  53580. buttonOptions[selectedOption]) {
  53581. this.clickButton(selectedOption, false);
  53582. }
  53583. addEvent(chart, 'load', function () {
  53584. // If a data grouping is applied to the current button, release it
  53585. // when extremes change
  53586. if (chart.xAxis && chart.xAxis[0]) {
  53587. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  53588. if (this.max - this.min !==
  53589. chart.fixedRange &&
  53590. e.trigger !== 'rangeSelectorButton' &&
  53591. e.trigger !== 'updatedData' &&
  53592. rangeSelector.forcedDataGrouping &&
  53593. !rangeSelector.frozenStates) {
  53594. this.setDataGrouping(false, false);
  53595. }
  53596. });
  53597. }
  53598. });
  53599. };
  53600. /**
  53601. * Dynamically update the range selector buttons after a new range has been
  53602. * set
  53603. *
  53604. * @private
  53605. * @function Highcharts.RangeSelector#updateButtonStates
  53606. * @return {void}
  53607. */
  53608. RangeSelector.prototype.updateButtonStates = function () {
  53609. var rangeSelector = this,
  53610. chart = this.chart,
  53611. baseAxis = chart.xAxis[0],
  53612. actualRange = Math.round(baseAxis.max - baseAxis.min),
  53613. hasNoData = !baseAxis.hasVisibleSeries,
  53614. day = 24 * 36e5, // A single day in milliseconds
  53615. unionExtremes = (chart.scroller &&
  53616. chart.scroller.getUnionExtremes()) || baseAxis,
  53617. dataMin = unionExtremes.dataMin,
  53618. dataMax = unionExtremes.dataMax,
  53619. ytdExtremes = rangeSelector.getYTDExtremes(dataMax,
  53620. dataMin,
  53621. chart.time.useUTC),
  53622. ytdMin = ytdExtremes.min,
  53623. ytdMax = ytdExtremes.max,
  53624. selected = rangeSelector.selected,
  53625. selectedExists = isNumber(selected),
  53626. allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
  53627. buttons = rangeSelector.buttons;
  53628. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  53629. var range = rangeOptions._range,
  53630. type = rangeOptions.type,
  53631. count = rangeOptions.count || 1,
  53632. button = buttons[i],
  53633. state = 0,
  53634. disable,
  53635. select,
  53636. offsetRange = rangeOptions._offsetMax -
  53637. rangeOptions._offsetMin,
  53638. isSelected = i === selected,
  53639. // Disable buttons where the range exceeds what is allowed in
  53640. // the current view
  53641. isTooGreatRange = range >
  53642. dataMax - dataMin,
  53643. // Disable buttons where the range is smaller than the minimum
  53644. // range
  53645. isTooSmallRange = range < baseAxis.minRange,
  53646. // Do not select the YTD button if not explicitly told so
  53647. isYTDButNotSelected = false,
  53648. // Disable the All button if we're already showing all
  53649. isAllButAlreadyShowingAll = false,
  53650. isSameRange = range === actualRange;
  53651. // Months and years have a variable range so we check the extremes
  53652. if ((type === 'month' || type === 'year') &&
  53653. (actualRange + 36e5 >=
  53654. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  53655. (actualRange - 36e5 <=
  53656. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  53657. isSameRange = true;
  53658. }
  53659. else if (type === 'ytd') {
  53660. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  53661. isYTDButNotSelected = !isSelected;
  53662. }
  53663. else if (type === 'all') {
  53664. isSameRange = (baseAxis.max - baseAxis.min >=
  53665. dataMax - dataMin);
  53666. isAllButAlreadyShowingAll = (!isSelected &&
  53667. selectedExists &&
  53668. isSameRange);
  53669. }
  53670. // The new zoom area happens to match the range for a button - mark
  53671. // it selected. This happens when scrolling across an ordinal gap.
  53672. // It can be seen in the intraday demos when selecting 1h and scroll
  53673. // across the night gap.
  53674. disable = (!allButtonsEnabled &&
  53675. (isTooGreatRange ||
  53676. isTooSmallRange ||
  53677. isAllButAlreadyShowingAll ||
  53678. hasNoData));
  53679. select = ((isSelected && isSameRange) ||
  53680. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  53681. (isSelected && rangeSelector.frozenStates));
  53682. if (disable) {
  53683. state = 3;
  53684. }
  53685. else if (select) {
  53686. selectedExists = true; // Only one button can be selected
  53687. state = 2;
  53688. }
  53689. // If state has changed, update the button
  53690. if (button.state !== state) {
  53691. button.setState(state);
  53692. // Reset (#9209)
  53693. if (state === 0 && selected === i) {
  53694. rangeSelector.setSelected(null);
  53695. }
  53696. }
  53697. });
  53698. };
  53699. /**
  53700. * Compute and cache the range for an individual button
  53701. *
  53702. * @private
  53703. * @function Highcharts.RangeSelector#computeButtonRange
  53704. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  53705. * @return {void}
  53706. */
  53707. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  53708. var type = rangeOptions.type,
  53709. count = rangeOptions.count || 1,
  53710. // these time intervals have a fixed number of milliseconds, as
  53711. // opposed to month, ytd and year
  53712. fixedTimes = {
  53713. millisecond: 1,
  53714. second: 1000,
  53715. minute: 60 * 1000,
  53716. hour: 3600 * 1000,
  53717. day: 24 * 3600 * 1000,
  53718. week: 7 * 24 * 3600 * 1000
  53719. };
  53720. // Store the range on the button object
  53721. if (fixedTimes[type]) {
  53722. rangeOptions._range = fixedTimes[type] * count;
  53723. }
  53724. else if (type === 'month' || type === 'year') {
  53725. rangeOptions._range = {
  53726. month: 30,
  53727. year: 365
  53728. }[type] * 24 * 36e5 * count;
  53729. }
  53730. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  53731. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  53732. rangeOptions._range +=
  53733. rangeOptions._offsetMax - rangeOptions._offsetMin;
  53734. };
  53735. /**
  53736. * Set the internal and displayed value of a HTML input for the dates
  53737. *
  53738. * @private
  53739. * @function Highcharts.RangeSelector#setInputValue
  53740. * @param {string} name
  53741. * @param {number} [inputTime]
  53742. * @return {void}
  53743. */
  53744. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  53745. var options = this.chart.options.rangeSelector,
  53746. time = this.chart.time,
  53747. input = this[name + 'Input'];
  53748. if (defined(inputTime)) {
  53749. input.previousValue = input.HCTime;
  53750. input.HCTime = inputTime;
  53751. }
  53752. input.value = time.dateFormat(options.inputEditDateFormat || '%Y-%m-%d', input.HCTime);
  53753. this[name + 'DateBox'].attr({
  53754. text: time.dateFormat(options.inputDateFormat || '%b %e, %Y', input.HCTime)
  53755. });
  53756. };
  53757. /**
  53758. * @private
  53759. * @function Highcharts.RangeSelector#showInput
  53760. * @param {string} name
  53761. * @return {void}
  53762. */
  53763. RangeSelector.prototype.showInput = function (name) {
  53764. var inputGroup = this.inputGroup,
  53765. dateBox = this[name + 'DateBox'];
  53766. css(this[name + 'Input'], {
  53767. left: (inputGroup.translateX + dateBox.x) + 'px',
  53768. top: inputGroup.translateY + 'px',
  53769. width: (dateBox.width - 2) + 'px',
  53770. height: (dateBox.height - 2) + 'px',
  53771. border: '2px solid silver'
  53772. });
  53773. };
  53774. /**
  53775. * @private
  53776. * @function Highcharts.RangeSelector#hideInput
  53777. * @param {string} name
  53778. * @return {void}
  53779. */
  53780. RangeSelector.prototype.hideInput = function (name) {
  53781. css(this[name + 'Input'], {
  53782. border: 0,
  53783. width: '1px',
  53784. height: '1px'
  53785. });
  53786. this.setInputValue(name);
  53787. };
  53788. /**
  53789. * @private
  53790. * @function Highcharts.RangeSelector#defaultInputDateParser
  53791. */
  53792. RangeSelector.prototype.defaultInputDateParser = function (inputDate, useUTC) {
  53793. var date = new Date();
  53794. if (H.isSafari) {
  53795. return Date.parse(inputDate.split(' ').join('T'));
  53796. }
  53797. if (useUTC) {
  53798. return Date.parse(inputDate + 'Z');
  53799. }
  53800. return Date.parse(inputDate) - date.getTimezoneOffset() * 60 * 1000;
  53801. };
  53802. /**
  53803. * Draw either the 'from' or the 'to' HTML input box of the range selector
  53804. *
  53805. * @private
  53806. * @function Highcharts.RangeSelector#drawInput
  53807. * @param {string} name
  53808. * @return {void}
  53809. */
  53810. RangeSelector.prototype.drawInput = function (name) {
  53811. var rangeSelector = this,
  53812. chart = rangeSelector.chart,
  53813. chartStyle = chart.renderer.style || {},
  53814. renderer = chart.renderer,
  53815. options = chart.options.rangeSelector,
  53816. lang = defaultOptions.lang,
  53817. div = rangeSelector.div,
  53818. isMin = name === 'min',
  53819. input,
  53820. label,
  53821. dateBox,
  53822. inputGroup = this.inputGroup,
  53823. defaultInputDateParser = this.defaultInputDateParser;
  53824. /**
  53825. * @private
  53826. */
  53827. function updateExtremes() {
  53828. var inputValue = input.value,
  53829. value,
  53830. chartAxis = chart.xAxis[0],
  53831. dataAxis = chart.scroller && chart.scroller.xAxis ?
  53832. chart.scroller.xAxis :
  53833. chartAxis,
  53834. dataMin = dataAxis.dataMin,
  53835. dataMax = dataAxis.dataMax;
  53836. value = (options.inputDateParser || defaultInputDateParser)(inputValue, chart.time.useUTC);
  53837. if (value !== input.previousValue) {
  53838. input.previousValue = value;
  53839. // If the value isn't parsed directly to a value by the
  53840. // browser's Date.parse method, like YYYY-MM-DD in IE, try
  53841. // parsing it a different way
  53842. if (!isNumber(value)) {
  53843. value = inputValue.split('-');
  53844. value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
  53845. }
  53846. if (isNumber(value)) {
  53847. // Correct for timezone offset (#433)
  53848. if (!chart.time.useUTC) {
  53849. value =
  53850. value + new Date().getTimezoneOffset() * 60 * 1000;
  53851. }
  53852. // Validate the extremes. If it goes beyound the data min or
  53853. // max, use the actual data extreme (#2438).
  53854. if (isMin) {
  53855. if (value > rangeSelector.maxInput.HCTime) {
  53856. value = void 0;
  53857. }
  53858. else if (value < dataMin) {
  53859. value = dataMin;
  53860. }
  53861. }
  53862. else {
  53863. if (value < rangeSelector.minInput.HCTime) {
  53864. value = void 0;
  53865. }
  53866. else if (value > dataMax) {
  53867. value = dataMax;
  53868. }
  53869. }
  53870. // Set the extremes
  53871. if (typeof value !== 'undefined') { // @todo typof undefined
  53872. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  53873. }
  53874. }
  53875. }
  53876. }
  53877. // Create the text label
  53878. this[name + 'Label'] = label = renderer
  53879. .label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset)
  53880. .addClass('highcharts-range-label')
  53881. .attr({
  53882. padding: 2
  53883. })
  53884. .add(inputGroup);
  53885. inputGroup.offset += label.width + 5;
  53886. // Create an SVG label that shows updated date ranges and and records
  53887. // click events that bring in the HTML input.
  53888. this[name + 'DateBox'] = dateBox = renderer
  53889. .label('', inputGroup.offset)
  53890. .addClass('highcharts-range-input')
  53891. .attr({
  53892. padding: 2,
  53893. width: options.inputBoxWidth || 90,
  53894. height: options.inputBoxHeight || 17,
  53895. 'text-align': 'center'
  53896. })
  53897. .on('click', function () {
  53898. // If it is already focused, the onfocus event doesn't fire
  53899. // (#3713)
  53900. rangeSelector.showInput(name);
  53901. rangeSelector[name + 'Input'].focus();
  53902. });
  53903. if (!chart.styledMode) {
  53904. dateBox.attr({
  53905. stroke: options.inputBoxBorderColor || '#cccccc',
  53906. 'stroke-width': 1
  53907. });
  53908. }
  53909. dateBox.add(inputGroup);
  53910. inputGroup.offset += dateBox.width + (isMin ? 10 : 0);
  53911. // Create the HTML input element. This is rendered as 1x1 pixel then set
  53912. // to the right size when focused.
  53913. this[name + 'Input'] = input = createElement('input', {
  53914. name: name,
  53915. className: 'highcharts-range-selector',
  53916. type: 'text'
  53917. }, {
  53918. top: chart.plotTop + 'px' // prevent jump on focus in Firefox
  53919. }, div);
  53920. if (!chart.styledMode) {
  53921. // Styles
  53922. label.css(merge(chartStyle, options.labelStyle));
  53923. dateBox.css(merge({
  53924. color: '#333333'
  53925. }, chartStyle, options.inputStyle));
  53926. css(input, extend({
  53927. position: 'absolute',
  53928. border: 0,
  53929. width: '1px',
  53930. height: '1px',
  53931. padding: 0,
  53932. textAlign: 'center',
  53933. fontSize: chartStyle.fontSize,
  53934. fontFamily: chartStyle.fontFamily,
  53935. top: '-9999em' // #4798
  53936. }, options.inputStyle));
  53937. }
  53938. // Blow up the input box
  53939. input.onfocus = function () {
  53940. rangeSelector.showInput(name);
  53941. };
  53942. // Hide away the input box
  53943. input.onblur = function () {
  53944. // update extermes only when inputs are active
  53945. if (input === H.doc.activeElement) { // Only when focused
  53946. // Update also when no `change` event is triggered, like when
  53947. // clicking inside the SVG (#4710)
  53948. updateExtremes();
  53949. }
  53950. // #10404 - move hide and blur outside focus
  53951. rangeSelector.hideInput(name);
  53952. input.blur(); // #4606
  53953. };
  53954. // handle changes in the input boxes
  53955. input.onchange = updateExtremes;
  53956. input.onkeypress = function (event) {
  53957. // IE does not fire onchange on enter
  53958. if (event.keyCode === 13) {
  53959. updateExtremes();
  53960. }
  53961. };
  53962. };
  53963. /**
  53964. * Get the position of the range selector buttons and inputs. This can be
  53965. * overridden from outside for custom positioning.
  53966. *
  53967. * @private
  53968. * @function Highcharts.RangeSelector#getPosition
  53969. *
  53970. * @return {Highcharts.Dictionary<number>}
  53971. */
  53972. RangeSelector.prototype.getPosition = function () {
  53973. var chart = this.chart,
  53974. options = chart.options.rangeSelector,
  53975. top = options.verticalAlign === 'top' ?
  53976. chart.plotTop - chart.axisOffset[0] :
  53977. 0; // set offset only for varticalAlign top
  53978. return {
  53979. buttonTop: top + options.buttonPosition.y,
  53980. inputTop: top + options.inputPosition.y - 10
  53981. };
  53982. };
  53983. /**
  53984. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  53985. * the current timestamp. Will choose dataMin if its value is higher than
  53986. * the timestamp for the start of current year.
  53987. *
  53988. * @private
  53989. * @function Highcharts.RangeSelector#getYTDExtremes
  53990. *
  53991. * @param {number} dataMax
  53992. *
  53993. * @param {number} dataMin
  53994. *
  53995. * @return {*}
  53996. * Returns min and max for the YTD
  53997. */
  53998. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  53999. var time = this.chart.time,
  54000. min,
  54001. now = new time.Date(dataMax),
  54002. year = time.get('FullYear',
  54003. now),
  54004. startOfYear = useUTC ?
  54005. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  54006. +new time.Date(year, 0, 1);
  54007. min = Math.max(dataMin || 0, startOfYear);
  54008. now = now.getTime();
  54009. return {
  54010. max: Math.min(dataMax || now, now),
  54011. min: min
  54012. };
  54013. };
  54014. /**
  54015. * Render the range selector including the buttons and the inputs. The first
  54016. * time render is called, the elements are created and positioned. On
  54017. * subsequent calls, they are moved and updated.
  54018. *
  54019. * @private
  54020. * @function Highcharts.RangeSelector#render
  54021. * @param {number} [min]
  54022. * X axis minimum
  54023. * @param {number} [max]
  54024. * X axis maximum
  54025. * @return {void}
  54026. */
  54027. RangeSelector.prototype.render = function (min, max) {
  54028. var rangeSelector = this,
  54029. chart = rangeSelector.chart,
  54030. renderer = chart.renderer,
  54031. container = chart.container,
  54032. chartOptions = chart.options,
  54033. navButtonOptions = (chartOptions.exporting &&
  54034. chartOptions.exporting.enabled !== false &&
  54035. chartOptions.navigation &&
  54036. chartOptions.navigation.buttonOptions),
  54037. lang = defaultOptions.lang,
  54038. div = rangeSelector.div,
  54039. options = chartOptions.rangeSelector,
  54040. // Place inputs above the container
  54041. inputsZIndex = pick(chartOptions.chart.style &&
  54042. chartOptions.chart.style.zIndex, 0) + 1,
  54043. floating = options.floating,
  54044. buttons = rangeSelector.buttons,
  54045. inputGroup = rangeSelector.inputGroup,
  54046. buttonTheme = options.buttonTheme,
  54047. buttonPosition = options.buttonPosition,
  54048. inputPosition = options.inputPosition,
  54049. inputEnabled = options.inputEnabled,
  54050. states = buttonTheme && buttonTheme.states,
  54051. plotLeft = chart.plotLeft,
  54052. buttonLeft,
  54053. buttonGroup = rangeSelector.buttonGroup,
  54054. group,
  54055. groupHeight,
  54056. rendered = rangeSelector.rendered,
  54057. verticalAlign = rangeSelector.options.verticalAlign,
  54058. legend = chart.legend,
  54059. legendOptions = legend && legend.options,
  54060. buttonPositionY = buttonPosition.y,
  54061. inputPositionY = inputPosition.y,
  54062. animate = chart.hasLoaded,
  54063. verb = animate ? 'animate' : 'attr',
  54064. exportingX = 0,
  54065. alignTranslateY,
  54066. legendHeight,
  54067. minPosition,
  54068. translateY = 0,
  54069. translateX;
  54070. if (options.enabled === false) {
  54071. return;
  54072. }
  54073. // create the elements
  54074. if (!rendered) {
  54075. rangeSelector.group = group = renderer.g('range-selector-group')
  54076. .attr({
  54077. zIndex: 7
  54078. })
  54079. .add();
  54080. rangeSelector.buttonGroup = buttonGroup =
  54081. renderer.g('range-selector-buttons').add(group);
  54082. rangeSelector.zoomText = renderer
  54083. .text(lang.rangeSelectorZoom, 0, 15)
  54084. .add(buttonGroup);
  54085. if (!chart.styledMode) {
  54086. rangeSelector.zoomText.css(options.labelStyle);
  54087. buttonTheme['stroke-width'] =
  54088. pick(buttonTheme['stroke-width'], 0);
  54089. }
  54090. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  54091. buttons[i] = renderer
  54092. .button(rangeOptions.text, 0, 0, function (e) {
  54093. // extract events from button object and call
  54094. var buttonEvents = (rangeOptions.events &&
  54095. rangeOptions.events.click),
  54096. callDefaultEvent;
  54097. if (buttonEvents) {
  54098. callDefaultEvent =
  54099. buttonEvents.call(rangeOptions, e);
  54100. }
  54101. if (callDefaultEvent !== false) {
  54102. rangeSelector.clickButton(i);
  54103. }
  54104. rangeSelector.isActive = true;
  54105. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  54106. .attr({
  54107. 'text-align': 'center'
  54108. })
  54109. .add(buttonGroup);
  54110. });
  54111. // first create a wrapper outside the container in order to make
  54112. // the inputs work and make export correct
  54113. if (inputEnabled !== false) {
  54114. rangeSelector.div = div = createElement('div', null, {
  54115. position: 'relative',
  54116. height: 0,
  54117. zIndex: inputsZIndex
  54118. });
  54119. container.parentNode.insertBefore(div, container);
  54120. // Create the group to keep the inputs
  54121. rangeSelector.inputGroup = inputGroup =
  54122. renderer.g('input-group').add(group);
  54123. inputGroup.offset = 0;
  54124. rangeSelector.drawInput('min');
  54125. rangeSelector.drawInput('max');
  54126. }
  54127. }
  54128. // #8769, allow dynamically updating margins
  54129. rangeSelector.zoomText[verb]({
  54130. x: pick(plotLeft + buttonPosition.x, plotLeft)
  54131. });
  54132. // button start position
  54133. buttonLeft = pick(plotLeft + buttonPosition.x, plotLeft) +
  54134. rangeSelector.zoomText.getBBox().width + 5;
  54135. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  54136. buttons[i][verb]({ x: buttonLeft });
  54137. // increase button position for the next button
  54138. buttonLeft += buttons[i].width + pick(options.buttonSpacing, 5);
  54139. });
  54140. plotLeft = chart.plotLeft - chart.spacing[3];
  54141. rangeSelector.updateButtonStates();
  54142. // detect collisiton with exporting
  54143. if (navButtonOptions &&
  54144. this.titleCollision(chart) &&
  54145. verticalAlign === 'top' &&
  54146. buttonPosition.align === 'right' && ((buttonPosition.y +
  54147. buttonGroup.getBBox().height - 12) <
  54148. ((navButtonOptions.y || 0) +
  54149. navButtonOptions.height))) {
  54150. exportingX = -40;
  54151. }
  54152. translateX = buttonPosition.x - chart.spacing[3];
  54153. if (buttonPosition.align === 'right') {
  54154. translateX += exportingX - plotLeft; // (#13014)
  54155. }
  54156. else if (buttonPosition.align === 'center') {
  54157. translateX -= plotLeft / 2;
  54158. }
  54159. // align button group
  54160. buttonGroup.align({
  54161. y: buttonPosition.y,
  54162. width: buttonGroup.getBBox().width,
  54163. align: buttonPosition.align,
  54164. x: translateX
  54165. }, true, chart.spacingBox);
  54166. // skip animation
  54167. rangeSelector.group.placed = animate;
  54168. rangeSelector.buttonGroup.placed = animate;
  54169. if (inputEnabled !== false) {
  54170. var inputGroupX,
  54171. inputGroupWidth,
  54172. buttonGroupX,
  54173. buttonGroupWidth;
  54174. // detect collision with exporting
  54175. if (navButtonOptions &&
  54176. this.titleCollision(chart) &&
  54177. verticalAlign === 'top' &&
  54178. inputPosition.align === 'right' && ((inputPosition.y -
  54179. inputGroup.getBBox().height - 12) <
  54180. ((navButtonOptions.y || 0) +
  54181. navButtonOptions.height +
  54182. chart.spacing[0]))) {
  54183. exportingX = -40;
  54184. }
  54185. else {
  54186. exportingX = 0;
  54187. }
  54188. if (inputPosition.align === 'left') {
  54189. translateX = plotLeft;
  54190. }
  54191. else if (inputPosition.align === 'right') {
  54192. translateX = -Math.max(chart.axisOffset[1], -exportingX);
  54193. }
  54194. // Update the alignment to the updated spacing box
  54195. inputGroup.align({
  54196. y: inputPosition.y,
  54197. width: inputGroup.getBBox().width,
  54198. align: inputPosition.align,
  54199. // fix wrong getBBox() value on right align
  54200. x: inputPosition.x + translateX - 2
  54201. }, true, chart.spacingBox);
  54202. // detect collision
  54203. inputGroupX = (inputGroup.alignAttr.translateX +
  54204. inputGroup.alignOptions.x -
  54205. exportingX +
  54206. // getBBox for detecing left margin
  54207. inputGroup.getBBox().x +
  54208. // 2px padding to not overlap input and label
  54209. 2);
  54210. inputGroupWidth = inputGroup.alignOptions.width;
  54211. buttonGroupX = buttonGroup.alignAttr.translateX +
  54212. buttonGroup.getBBox().x;
  54213. // 20 is minimal spacing between elements
  54214. buttonGroupWidth = buttonGroup.getBBox().width + 20;
  54215. if ((inputPosition.align ===
  54216. buttonPosition.align) || ((buttonGroupX + buttonGroupWidth > inputGroupX) &&
  54217. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  54218. (buttonPositionY <
  54219. (inputPositionY +
  54220. inputGroup.getBBox().height)))) {
  54221. inputGroup.attr({
  54222. translateX: inputGroup.alignAttr.translateX +
  54223. (chart.axisOffset[1] >= -exportingX ? 0 : -exportingX),
  54224. translateY: inputGroup.alignAttr.translateY +
  54225. buttonGroup.getBBox().height + 10
  54226. });
  54227. }
  54228. // Set or reset the input values
  54229. rangeSelector.setInputValue('min', min);
  54230. rangeSelector.setInputValue('max', max);
  54231. // skip animation
  54232. rangeSelector.inputGroup.placed = animate;
  54233. }
  54234. // vertical align
  54235. rangeSelector.group.align({
  54236. verticalAlign: verticalAlign
  54237. }, true, chart.spacingBox);
  54238. // set position
  54239. groupHeight =
  54240. rangeSelector.group.getBBox().height + 20; // # 20 padding
  54241. alignTranslateY =
  54242. rangeSelector.group.alignAttr.translateY;
  54243. // calculate bottom position
  54244. if (verticalAlign === 'bottom') {
  54245. legendHeight = (legendOptions &&
  54246. legendOptions.verticalAlign === 'bottom' &&
  54247. legendOptions.enabled &&
  54248. !legendOptions.floating ?
  54249. legend.legendHeight + pick(legendOptions.margin, 10) :
  54250. 0);
  54251. groupHeight = groupHeight + legendHeight - 20;
  54252. translateY = (alignTranslateY -
  54253. groupHeight -
  54254. (floating ? 0 : options.y) -
  54255. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  54256. 10 // 10 spacing
  54257. );
  54258. }
  54259. if (verticalAlign === 'top') {
  54260. if (floating) {
  54261. translateY = 0;
  54262. }
  54263. if (chart.titleOffset && chart.titleOffset[0]) {
  54264. translateY = chart.titleOffset[0];
  54265. }
  54266. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  54267. }
  54268. else if (verticalAlign === 'middle') {
  54269. if (inputPositionY === buttonPositionY) {
  54270. if (inputPositionY < 0) {
  54271. translateY = alignTranslateY + minPosition;
  54272. }
  54273. else {
  54274. translateY = alignTranslateY;
  54275. }
  54276. }
  54277. else if (inputPositionY || buttonPositionY) {
  54278. if (inputPositionY < 0 ||
  54279. buttonPositionY < 0) {
  54280. translateY -= Math.min(inputPositionY, buttonPositionY);
  54281. }
  54282. else {
  54283. translateY =
  54284. alignTranslateY - groupHeight + minPosition;
  54285. }
  54286. }
  54287. }
  54288. rangeSelector.group.translate(options.x, options.y + Math.floor(translateY));
  54289. // translate HTML inputs
  54290. if (inputEnabled !== false) {
  54291. rangeSelector.minInput.style.marginTop =
  54292. rangeSelector.group.translateY + 'px';
  54293. rangeSelector.maxInput.style.marginTop =
  54294. rangeSelector.group.translateY + 'px';
  54295. }
  54296. rangeSelector.rendered = true;
  54297. };
  54298. /**
  54299. * Extracts height of range selector
  54300. *
  54301. * @private
  54302. * @function Highcharts.RangeSelector#getHeight
  54303. * @return {number}
  54304. * Returns rangeSelector height
  54305. */
  54306. RangeSelector.prototype.getHeight = function () {
  54307. var rangeSelector = this,
  54308. options = rangeSelector.options,
  54309. rangeSelectorGroup = rangeSelector.group,
  54310. inputPosition = options.inputPosition,
  54311. buttonPosition = options.buttonPosition,
  54312. yPosition = options.y,
  54313. buttonPositionY = buttonPosition.y,
  54314. inputPositionY = inputPosition.y,
  54315. rangeSelectorHeight = 0,
  54316. minPosition;
  54317. if (options.height) {
  54318. return options.height;
  54319. }
  54320. rangeSelectorHeight = rangeSelectorGroup ?
  54321. // 13px to keep back compatibility
  54322. (rangeSelectorGroup.getBBox(true).height) + 13 +
  54323. yPosition :
  54324. 0;
  54325. minPosition = Math.min(inputPositionY, buttonPositionY);
  54326. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  54327. (inputPositionY > 0 && buttonPositionY > 0)) {
  54328. rangeSelectorHeight += Math.abs(minPosition);
  54329. }
  54330. return rangeSelectorHeight;
  54331. };
  54332. /**
  54333. * Detect collision with title or subtitle
  54334. *
  54335. * @private
  54336. * @function Highcharts.RangeSelector#titleCollision
  54337. *
  54338. * @param {Highcharts.Chart} chart
  54339. *
  54340. * @return {boolean}
  54341. * Returns collision status
  54342. */
  54343. RangeSelector.prototype.titleCollision = function (chart) {
  54344. return !(chart.options.title.text ||
  54345. chart.options.subtitle.text);
  54346. };
  54347. /**
  54348. * Update the range selector with new options
  54349. *
  54350. * @private
  54351. * @function Highcharts.RangeSelector#update
  54352. * @param {Highcharts.RangeSelectorOptions} options
  54353. * @return {void}
  54354. */
  54355. RangeSelector.prototype.update = function (options) {
  54356. var chart = this.chart;
  54357. merge(true, chart.options.rangeSelector, options);
  54358. this.destroy();
  54359. this.init(chart);
  54360. chart.rangeSelector.render();
  54361. };
  54362. /**
  54363. * Destroys allocated elements.
  54364. *
  54365. * @private
  54366. * @function Highcharts.RangeSelector#destroy
  54367. */
  54368. RangeSelector.prototype.destroy = function () {
  54369. var rSelector = this,
  54370. minInput = rSelector.minInput,
  54371. maxInput = rSelector.maxInput;
  54372. rSelector.unMouseDown();
  54373. rSelector.unResize();
  54374. // Destroy elements in collections
  54375. destroyObjectProperties(rSelector.buttons);
  54376. // Clear input element events
  54377. if (minInput) {
  54378. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  54379. }
  54380. if (maxInput) {
  54381. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  54382. }
  54383. // Destroy HTML and SVG elements
  54384. objectEach(rSelector, function (val, key) {
  54385. if (val && key !== 'chart') {
  54386. if (val instanceof SVGElement) {
  54387. // SVGElement
  54388. val.destroy();
  54389. }
  54390. else if (val instanceof window.HTMLElement) {
  54391. // HTML element
  54392. discardElement(val);
  54393. }
  54394. }
  54395. if (val !== RangeSelector.prototype[key]) {
  54396. rSelector[key] = null;
  54397. }
  54398. }, this);
  54399. };
  54400. return RangeSelector;
  54401. }());
  54402. /**
  54403. * The default buttons for pre-selecting time frames
  54404. */
  54405. RangeSelector.prototype.defaultButtons = [{
  54406. type: 'month',
  54407. count: 1,
  54408. text: '1m'
  54409. }, {
  54410. type: 'month',
  54411. count: 3,
  54412. text: '3m'
  54413. }, {
  54414. type: 'month',
  54415. count: 6,
  54416. text: '6m'
  54417. }, {
  54418. type: 'ytd',
  54419. text: 'YTD'
  54420. }, {
  54421. type: 'year',
  54422. count: 1,
  54423. text: '1y'
  54424. }, {
  54425. type: 'all',
  54426. text: 'All'
  54427. }];
  54428. /**
  54429. * Get the axis min value based on the range option and the current max. For
  54430. * stock charts this is extended via the {@link RangeSelector} so that if the
  54431. * selected range is a multiple of months or years, it is compensated for
  54432. * various month lengths.
  54433. *
  54434. * @private
  54435. * @function Highcharts.Axis#minFromRange
  54436. * @return {number|undefined}
  54437. * The new minimum value.
  54438. */
  54439. Axis.prototype.minFromRange = function () {
  54440. var rangeOptions = this.range,
  54441. type = rangeOptions.type,
  54442. min,
  54443. max = this.max,
  54444. dataMin,
  54445. range,
  54446. time = this.chart.time,
  54447. // Get the true range from a start date
  54448. getTrueRange = function (base,
  54449. count) {
  54450. var timeName = type === 'year' ? 'FullYear' : 'Month';
  54451. var date = new time.Date(base);
  54452. var basePeriod = time.get(timeName,
  54453. date);
  54454. time.set(timeName, date, basePeriod + count);
  54455. if (basePeriod === time.get(timeName, date)) {
  54456. time.set('Date', date, 0); // #6537
  54457. }
  54458. return date.getTime() - base;
  54459. };
  54460. if (isNumber(rangeOptions)) {
  54461. min = max - rangeOptions;
  54462. range = rangeOptions;
  54463. }
  54464. else {
  54465. min = max + getTrueRange(max, -rangeOptions.count);
  54466. // Let the fixedRange reflect initial settings (#5930)
  54467. if (this.chart) {
  54468. this.chart.fixedRange = max - min;
  54469. }
  54470. }
  54471. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  54472. if (!isNumber(min)) {
  54473. min = dataMin;
  54474. }
  54475. if (min <= dataMin) {
  54476. min = dataMin;
  54477. if (typeof range === 'undefined') { // #4501
  54478. range = getTrueRange(min, rangeOptions.count);
  54479. }
  54480. this.newMax = Math.min(min + range, this.dataMax);
  54481. }
  54482. if (!isNumber(max)) {
  54483. min = void 0;
  54484. }
  54485. return min;
  54486. };
  54487. if (!H.RangeSelector) {
  54488. // Initialize rangeselector for stock charts
  54489. addEvent(Chart, 'afterGetContainer', function () {
  54490. if (this.options.rangeSelector.enabled) {
  54491. this.rangeSelector = new RangeSelector(this);
  54492. }
  54493. });
  54494. addEvent(Chart, 'beforeRender', function () {
  54495. var chart = this,
  54496. axes = chart.axes,
  54497. rangeSelector = chart.rangeSelector,
  54498. verticalAlign;
  54499. if (rangeSelector) {
  54500. if (isNumber(rangeSelector.deferredYTDClick)) {
  54501. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  54502. delete rangeSelector.deferredYTDClick;
  54503. }
  54504. axes.forEach(function (axis) {
  54505. axis.updateNames();
  54506. axis.setScale();
  54507. });
  54508. chart.getAxisMargins();
  54509. rangeSelector.render();
  54510. verticalAlign = rangeSelector.options.verticalAlign;
  54511. if (!rangeSelector.options.floating) {
  54512. if (verticalAlign === 'bottom') {
  54513. this.extraBottomMargin = true;
  54514. }
  54515. else if (verticalAlign !== 'middle') {
  54516. this.extraTopMargin = true;
  54517. }
  54518. }
  54519. }
  54520. });
  54521. addEvent(Chart, 'update', function (e) {
  54522. var chart = this,
  54523. options = e.options,
  54524. optionsRangeSelector = options.rangeSelector,
  54525. rangeSelector = chart.rangeSelector,
  54526. verticalAlign,
  54527. extraBottomMarginWas = this.extraBottomMargin,
  54528. extraTopMarginWas = this.extraTopMargin;
  54529. if (optionsRangeSelector &&
  54530. optionsRangeSelector.enabled &&
  54531. !defined(rangeSelector)) {
  54532. this.options.rangeSelector.enabled = true;
  54533. this.rangeSelector = new RangeSelector(this);
  54534. }
  54535. this.extraBottomMargin = false;
  54536. this.extraTopMargin = false;
  54537. if (rangeSelector) {
  54538. rangeSelector.render();
  54539. verticalAlign = (optionsRangeSelector &&
  54540. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  54541. if (!rangeSelector.options.floating) {
  54542. if (verticalAlign === 'bottom') {
  54543. this.extraBottomMargin = true;
  54544. }
  54545. else if (verticalAlign !== 'middle') {
  54546. this.extraTopMargin = true;
  54547. }
  54548. }
  54549. if (this.extraBottomMargin !== extraBottomMarginWas ||
  54550. this.extraTopMargin !== extraTopMarginWas) {
  54551. this.isDirtyBox = true;
  54552. }
  54553. }
  54554. });
  54555. addEvent(Chart, 'render', function () {
  54556. var chart = this,
  54557. rangeSelector = chart.rangeSelector,
  54558. verticalAlign;
  54559. if (rangeSelector && !rangeSelector.options.floating) {
  54560. rangeSelector.render();
  54561. verticalAlign = rangeSelector.options.verticalAlign;
  54562. if (verticalAlign === 'bottom') {
  54563. this.extraBottomMargin = true;
  54564. }
  54565. else if (verticalAlign !== 'middle') {
  54566. this.extraTopMargin = true;
  54567. }
  54568. }
  54569. });
  54570. addEvent(Chart, 'getMargins', function () {
  54571. var rangeSelector = this.rangeSelector,
  54572. rangeSelectorHeight;
  54573. if (rangeSelector) {
  54574. rangeSelectorHeight = rangeSelector.getHeight();
  54575. if (this.extraTopMargin) {
  54576. this.plotTop += rangeSelectorHeight;
  54577. }
  54578. if (this.extraBottomMargin) {
  54579. this.marginBottom += rangeSelectorHeight;
  54580. }
  54581. }
  54582. });
  54583. Chart.prototype.callbacks.push(function (chart) {
  54584. var extremes,
  54585. rangeSelector = chart.rangeSelector,
  54586. unbindRender,
  54587. unbindSetExtremes,
  54588. legend,
  54589. alignTo,
  54590. verticalAlign;
  54591. /**
  54592. * @private
  54593. */
  54594. function renderRangeSelector() {
  54595. extremes = chart.xAxis[0].getExtremes();
  54596. legend = chart.legend;
  54597. verticalAlign = rangeSelector === null || rangeSelector === void 0 ? void 0 : rangeSelector.options.verticalAlign;
  54598. if (isNumber(extremes.min)) {
  54599. rangeSelector.render(extremes.min, extremes.max);
  54600. }
  54601. // Re-align the legend so that it's below the rangeselector
  54602. if (rangeSelector && legend.display &&
  54603. verticalAlign === 'top' &&
  54604. verticalAlign === legend.options.verticalAlign) {
  54605. // Create a new alignment box for the legend.
  54606. alignTo = merge(chart.spacingBox);
  54607. if (legend.options.layout === 'vertical') {
  54608. alignTo.y = chart.plotTop;
  54609. }
  54610. else {
  54611. alignTo.y += rangeSelector.getHeight();
  54612. }
  54613. legend.group.placed = false; // Don't animate the alignment.
  54614. legend.align(alignTo);
  54615. }
  54616. }
  54617. if (rangeSelector) {
  54618. // redraw the scroller on setExtremes
  54619. unbindSetExtremes = addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  54620. rangeSelector.render(e.min, e.max);
  54621. });
  54622. // redraw the scroller chart resize
  54623. unbindRender = addEvent(chart, 'redraw', renderRangeSelector);
  54624. // do it now
  54625. renderRangeSelector();
  54626. }
  54627. // Remove resize/afterSetExtremes at chart destroy
  54628. addEvent(chart, 'destroy', function destroyEvents() {
  54629. if (rangeSelector) {
  54630. unbindRender();
  54631. unbindSetExtremes();
  54632. }
  54633. });
  54634. });
  54635. H.RangeSelector = RangeSelector;
  54636. }
  54637. return H.RangeSelector;
  54638. });
  54639. _registerModule(_modules, 'Core/Axis/NavigatorAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  54640. /* *
  54641. *
  54642. * (c) 2010-2020 Torstein Honsi
  54643. *
  54644. * License: www.highcharts.com/license
  54645. *
  54646. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  54647. *
  54648. * */
  54649. var isTouchDevice = H.isTouchDevice;
  54650. var addEvent = U.addEvent,
  54651. correctFloat = U.correctFloat,
  54652. defined = U.defined,
  54653. isNumber = U.isNumber,
  54654. pick = U.pick;
  54655. /* eslint-disable valid-jsdoc */
  54656. /**
  54657. * @private
  54658. * @class
  54659. */
  54660. var NavigatorAxisAdditions = /** @class */ (function () {
  54661. /* *
  54662. *
  54663. * Constructors
  54664. *
  54665. * */
  54666. function NavigatorAxisAdditions(axis) {
  54667. this.axis = axis;
  54668. }
  54669. /* *
  54670. *
  54671. * Functions
  54672. *
  54673. * */
  54674. /**
  54675. * @private
  54676. */
  54677. NavigatorAxisAdditions.prototype.destroy = function () {
  54678. this.axis = void 0;
  54679. };
  54680. /**
  54681. * Add logic to normalize the zoomed range in order to preserve the pressed
  54682. * state of range selector buttons
  54683. *
  54684. * @private
  54685. * @function Highcharts.Axis#toFixedRange
  54686. * @param {number} [pxMin]
  54687. * @param {number} [pxMax]
  54688. * @param {number} [fixedMin]
  54689. * @param {number} [fixedMax]
  54690. * @return {*}
  54691. */
  54692. NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  54693. var navigator = this;
  54694. var axis = navigator.axis;
  54695. var chart = axis.chart;
  54696. var fixedRange = chart && chart.fixedRange,
  54697. halfPointRange = (axis.pointRange || 0) / 2,
  54698. newMin = pick(fixedMin,
  54699. axis.translate(pxMin,
  54700. true, !axis.horiz)),
  54701. newMax = pick(fixedMax,
  54702. axis.translate(pxMax,
  54703. true, !axis.horiz)),
  54704. changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  54705. // Add/remove half point range to/from the extremes (#1172)
  54706. if (!defined(fixedMin)) {
  54707. newMin = correctFloat(newMin + halfPointRange);
  54708. }
  54709. if (!defined(fixedMax)) {
  54710. newMax = correctFloat(newMax - halfPointRange);
  54711. }
  54712. // If the difference between the fixed range and the actual requested
  54713. // range is too great, the user is dragging across an ordinal gap, and
  54714. // we need to release the range selector button.
  54715. if (changeRatio > 0.7 && changeRatio < 1.3) {
  54716. if (fixedMax) {
  54717. newMin = newMax - fixedRange;
  54718. }
  54719. else {
  54720. newMax = newMin + fixedRange;
  54721. }
  54722. }
  54723. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  54724. newMin = newMax = void 0;
  54725. }
  54726. return {
  54727. min: newMin,
  54728. max: newMax
  54729. };
  54730. };
  54731. return NavigatorAxisAdditions;
  54732. }());
  54733. /**
  54734. * @private
  54735. * @class
  54736. */
  54737. var NavigatorAxis = /** @class */ (function () {
  54738. function NavigatorAxis() {
  54739. }
  54740. /* *
  54741. *
  54742. * Static Functions
  54743. *
  54744. * */
  54745. /**
  54746. * @private
  54747. */
  54748. NavigatorAxis.compose = function (AxisClass) {
  54749. AxisClass.keepProps.push('navigatorAxis');
  54750. /* eslint-disable no-invalid-this */
  54751. addEvent(AxisClass, 'init', function () {
  54752. var axis = this;
  54753. if (!axis.navigatorAxis) {
  54754. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  54755. }
  54756. });
  54757. // For Stock charts, override selection zooming with some special
  54758. // features because X axis zooming is already allowed by the Navigator
  54759. // and Range selector.
  54760. addEvent(AxisClass, 'zoom', function (e) {
  54761. var axis = this;
  54762. var chart = axis.chart;
  54763. var chartOptions = chart.options;
  54764. var navigator = chartOptions.navigator;
  54765. var navigatorAxis = axis.navigatorAxis;
  54766. var pinchType = chartOptions.chart.pinchType;
  54767. var rangeSelector = chartOptions.rangeSelector;
  54768. var zoomType = chartOptions.chart.zoomType;
  54769. var previousZoom;
  54770. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  54771. (rangeSelector && rangeSelector.enabled))) {
  54772. // For y only zooming, ignore the X axis completely
  54773. if (zoomType === 'y') {
  54774. e.zoomed = false;
  54775. // For xy zooming, record the state of the zoom before zoom
  54776. // selection, then when the reset button is pressed, revert to
  54777. // this state. This should apply only if the chart is
  54778. // initialized with a range (#6612), otherwise zoom all the way
  54779. // out.
  54780. }
  54781. else if (((!isTouchDevice && zoomType === 'xy') ||
  54782. (isTouchDevice && pinchType === 'xy')) &&
  54783. axis.options.range) {
  54784. previousZoom = navigatorAxis.previousZoom;
  54785. if (defined(e.newMin)) {
  54786. navigatorAxis.previousZoom = [axis.min, axis.max];
  54787. }
  54788. else if (previousZoom) {
  54789. e.newMin = previousZoom[0];
  54790. e.newMax = previousZoom[1];
  54791. navigatorAxis.previousZoom = void 0;
  54792. }
  54793. }
  54794. }
  54795. if (typeof e.zoomed !== 'undefined') {
  54796. e.preventDefault();
  54797. }
  54798. });
  54799. /* eslint-enable no-invalid-this */
  54800. };
  54801. /* *
  54802. *
  54803. * Static Properties
  54804. *
  54805. * */
  54806. /**
  54807. * @private
  54808. */
  54809. NavigatorAxis.AdditionsClass = NavigatorAxisAdditions;
  54810. return NavigatorAxis;
  54811. }());
  54812. return NavigatorAxis;
  54813. });
  54814. _registerModule(_modules, 'Core/Navigator.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Color.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxis.js'], _modules['Core/Options.js'], _modules['Core/Scrollbar.js'], _modules['Core/Utilities.js']], function (Axis, Chart, Color, H, NavigatorAxis, O, Scrollbar, U) {
  54815. /* *
  54816. *
  54817. * (c) 2010-2020 Torstein Honsi
  54818. *
  54819. * License: www.highcharts.com/license
  54820. *
  54821. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  54822. *
  54823. * */
  54824. var color = Color.parse;
  54825. var defaultOptions = O.defaultOptions;
  54826. var addEvent = U.addEvent,
  54827. clamp = U.clamp,
  54828. correctFloat = U.correctFloat,
  54829. defined = U.defined,
  54830. destroyObjectProperties = U.destroyObjectProperties,
  54831. erase = U.erase,
  54832. extend = U.extend,
  54833. find = U.find,
  54834. isArray = U.isArray,
  54835. isNumber = U.isNumber,
  54836. merge = U.merge,
  54837. pick = U.pick,
  54838. removeEvent = U.removeEvent,
  54839. splat = U.splat;
  54840. var hasTouch = H.hasTouch,
  54841. isTouchDevice = H.isTouchDevice,
  54842. Series = H.Series,
  54843. seriesTypes = H.seriesTypes,
  54844. defaultSeriesType,
  54845. // Finding the min or max of a set of variables where we don't know if they
  54846. // are defined, is a pattern that is repeated several places in Highcharts.
  54847. // Consider making this a global utility method.
  54848. numExt = function (extreme) {
  54849. var args = [];
  54850. for (var _i = 1; _i < arguments.length; _i++) {
  54851. args[_i - 1] = arguments[_i];
  54852. }
  54853. var numbers = [].filter.call(args,
  54854. isNumber);
  54855. if (numbers.length) {
  54856. return Math[extreme].apply(0, numbers);
  54857. }
  54858. };
  54859. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  54860. 'line' :
  54861. 'areaspline';
  54862. extend(defaultOptions, {
  54863. /**
  54864. * Maximum range which can be set using the navigator's handles.
  54865. * Opposite of [xAxis.minRange](#xAxis.minRange).
  54866. *
  54867. * @sample {highstock} stock/navigator/maxrange/
  54868. * Defined max and min range
  54869. *
  54870. * @type {number}
  54871. * @since 6.0.0
  54872. * @product highstock gantt
  54873. * @apioption xAxis.maxRange
  54874. */
  54875. /**
  54876. * The navigator is a small series below the main series, displaying
  54877. * a view of the entire data set. It provides tools to zoom in and
  54878. * out on parts of the data as well as panning across the dataset.
  54879. *
  54880. * @product highstock gantt
  54881. * @optionparent navigator
  54882. */
  54883. navigator: {
  54884. /**
  54885. * Whether the navigator and scrollbar should adapt to updated data
  54886. * in the base X axis. When loading data async, as in the demo below,
  54887. * this should be `false`. Otherwise new data will trigger navigator
  54888. * redraw, which will cause unwanted looping. In the demo below, the
  54889. * data in the navigator is set only once. On navigating, only the main
  54890. * chart content is updated.
  54891. *
  54892. * @sample {highstock} stock/demo/lazy-loading/
  54893. * Set to false with async data loading
  54894. *
  54895. * @type {boolean}
  54896. * @default true
  54897. * @apioption navigator.adaptToUpdatedData
  54898. */
  54899. /**
  54900. * An integer identifying the index to use for the base series, or a
  54901. * string representing the id of the series.
  54902. *
  54903. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  54904. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  54905. *
  54906. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  54907. *
  54908. * @deprecated
  54909. * @type {number|string}
  54910. * @default 0
  54911. * @apioption navigator.baseSeries
  54912. */
  54913. /**
  54914. * Enable or disable the navigator.
  54915. *
  54916. * @sample {highstock} stock/navigator/enabled/
  54917. * Disable the navigator
  54918. *
  54919. * @type {boolean}
  54920. * @default true
  54921. * @apioption navigator.enabled
  54922. */
  54923. /**
  54924. * When the chart is inverted, whether to draw the navigator on the
  54925. * opposite side.
  54926. *
  54927. * @type {boolean}
  54928. * @default false
  54929. * @since 5.0.8
  54930. * @apioption navigator.opposite
  54931. */
  54932. /**
  54933. * The height of the navigator.
  54934. *
  54935. * @sample {highstock} stock/navigator/height/
  54936. * A higher navigator
  54937. */
  54938. height: 40,
  54939. /**
  54940. * The distance from the nearest element, the X axis or X axis labels.
  54941. *
  54942. * @sample {highstock} stock/navigator/margin/
  54943. * A margin of 2 draws the navigator closer to the X axis labels
  54944. */
  54945. margin: 25,
  54946. /**
  54947. * Whether the mask should be inside the range marking the zoomed
  54948. * range, or outside. In Highstock 1.x it was always `false`.
  54949. *
  54950. * @sample {highstock} stock/navigator/maskinside-false/
  54951. * False, mask outside
  54952. *
  54953. * @since 2.0
  54954. */
  54955. maskInside: true,
  54956. /**
  54957. * Options for the handles for dragging the zoomed area.
  54958. *
  54959. * @sample {highstock} stock/navigator/handles/
  54960. * Colored handles
  54961. */
  54962. handles: {
  54963. /**
  54964. * Width for handles.
  54965. *
  54966. * @sample {highstock} stock/navigator/styled-handles/
  54967. * Styled handles
  54968. *
  54969. * @since 6.0.0
  54970. */
  54971. width: 7,
  54972. /**
  54973. * Height for handles.
  54974. *
  54975. * @sample {highstock} stock/navigator/styled-handles/
  54976. * Styled handles
  54977. *
  54978. * @since 6.0.0
  54979. */
  54980. height: 15,
  54981. /**
  54982. * Array to define shapes of handles. 0-index for left, 1-index for
  54983. * right.
  54984. *
  54985. * Additionally, the URL to a graphic can be given on this form:
  54986. * `url(graphic.png)`. Note that for the image to be applied to
  54987. * exported charts, its URL needs to be accessible by the export
  54988. * server.
  54989. *
  54990. * Custom callbacks for symbol path generation can also be added to
  54991. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  54992. * used by its method name, as shown in the demo.
  54993. *
  54994. * @sample {highstock} stock/navigator/styled-handles/
  54995. * Styled handles
  54996. *
  54997. * @type {Array<string>}
  54998. * @default ["navigator-handle", "navigator-handle"]
  54999. * @since 6.0.0
  55000. */
  55001. symbols: ['navigator-handle', 'navigator-handle'],
  55002. /**
  55003. * Allows to enable/disable handles.
  55004. *
  55005. * @since 6.0.0
  55006. */
  55007. enabled: true,
  55008. /**
  55009. * The width for the handle border and the stripes inside.
  55010. *
  55011. * @sample {highstock} stock/navigator/styled-handles/
  55012. * Styled handles
  55013. *
  55014. * @since 6.0.0
  55015. * @apioption navigator.handles.lineWidth
  55016. */
  55017. lineWidth: 1,
  55018. /**
  55019. * The fill for the handle.
  55020. *
  55021. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  55022. */
  55023. backgroundColor: '#f2f2f2',
  55024. /**
  55025. * The stroke for the handle border and the stripes inside.
  55026. *
  55027. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  55028. */
  55029. borderColor: '#999999'
  55030. },
  55031. /**
  55032. * The color of the mask covering the areas of the navigator series
  55033. * that are currently not visible in the main series. The default
  55034. * color is bluish with an opacity of 0.3 to see the series below.
  55035. *
  55036. * @see In styled mode, the mask is styled with the
  55037. * `.highcharts-navigator-mask` and
  55038. * `.highcharts-navigator-mask-inside` classes.
  55039. *
  55040. * @sample {highstock} stock/navigator/maskfill/
  55041. * Blue, semi transparent mask
  55042. *
  55043. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  55044. * @default rgba(102,133,194,0.3)
  55045. */
  55046. maskFill: color('#6685c2').setOpacity(0.3).get(),
  55047. /**
  55048. * The color of the line marking the currently zoomed area in the
  55049. * navigator.
  55050. *
  55051. * @sample {highstock} stock/navigator/outline/
  55052. * 2px blue outline
  55053. *
  55054. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  55055. * @default #cccccc
  55056. */
  55057. outlineColor: '#cccccc',
  55058. /**
  55059. * The width of the line marking the currently zoomed area in the
  55060. * navigator.
  55061. *
  55062. * @see In styled mode, the outline stroke width is set with the
  55063. * `.highcharts-navigator-outline` class.
  55064. *
  55065. * @sample {highstock} stock/navigator/outline/
  55066. * 2px blue outline
  55067. *
  55068. * @type {number}
  55069. */
  55070. outlineWidth: 1,
  55071. /**
  55072. * Options for the navigator series. Available options are the same
  55073. * as any series, documented at [plotOptions](#plotOptions.series)
  55074. * and [series](#series).
  55075. *
  55076. * Unless data is explicitly defined on navigator.series, the data
  55077. * is borrowed from the first series in the chart.
  55078. *
  55079. * Default series options for the navigator series are:
  55080. * ```js
  55081. * series: {
  55082. * type: 'areaspline',
  55083. * fillOpacity: 0.05,
  55084. * dataGrouping: {
  55085. * smoothed: true
  55086. * },
  55087. * lineWidth: 1,
  55088. * marker: {
  55089. * enabled: false
  55090. * }
  55091. * }
  55092. * ```
  55093. *
  55094. * @see In styled mode, the navigator series is styled with the
  55095. * `.highcharts-navigator-series` class.
  55096. *
  55097. * @sample {highstock} stock/navigator/series-data/
  55098. * Using a separate data set for the navigator
  55099. * @sample {highstock} stock/navigator/series/
  55100. * A green navigator series
  55101. *
  55102. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  55103. */
  55104. series: {
  55105. /**
  55106. * The type of the navigator series.
  55107. *
  55108. * Heads up:
  55109. * In column-type navigator, zooming is limited to at least one
  55110. * point with its `pointRange`.
  55111. *
  55112. * @sample {highstock} stock/navigator/column/
  55113. * Column type navigator
  55114. *
  55115. * @type {string}
  55116. * @default {highstock} `areaspline` if defined, otherwise `line`
  55117. * @default {gantt} gantt
  55118. */
  55119. type: defaultSeriesType,
  55120. /**
  55121. * The fill opacity of the navigator series.
  55122. */
  55123. fillOpacity: 0.05,
  55124. /**
  55125. * The pixel line width of the navigator series.
  55126. */
  55127. lineWidth: 1,
  55128. /**
  55129. * @ignore-option
  55130. */
  55131. compare: null,
  55132. /**
  55133. * Unless data is explicitly defined, the data is borrowed from the
  55134. * first series in the chart.
  55135. *
  55136. * @type {Array<number|Array<number|string|null>|object|null>}
  55137. * @product highstock
  55138. * @apioption navigator.series.data
  55139. */
  55140. /**
  55141. * Data grouping options for the navigator series.
  55142. *
  55143. * @extends plotOptions.series.dataGrouping
  55144. */
  55145. dataGrouping: {
  55146. approximation: 'average',
  55147. enabled: true,
  55148. groupPixelWidth: 2,
  55149. smoothed: true,
  55150. // Day and week differs from plotOptions.series.dataGrouping
  55151. units: [
  55152. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  55153. ['second', [1, 2, 5, 10, 15, 30]],
  55154. ['minute', [1, 2, 5, 10, 15, 30]],
  55155. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  55156. ['day', [1, 2, 3, 4]],
  55157. ['week', [1, 2, 3]],
  55158. ['month', [1, 3, 6]],
  55159. ['year', null]
  55160. ]
  55161. },
  55162. /**
  55163. * Data label options for the navigator series. Data labels are
  55164. * disabled by default on the navigator series.
  55165. *
  55166. * @extends plotOptions.series.dataLabels
  55167. */
  55168. dataLabels: {
  55169. enabled: false,
  55170. zIndex: 2 // #1839
  55171. },
  55172. id: 'highcharts-navigator-series',
  55173. className: 'highcharts-navigator-series',
  55174. /**
  55175. * Sets the fill color of the navigator series.
  55176. *
  55177. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  55178. * @apioption navigator.series.color
  55179. */
  55180. /**
  55181. * Line color for the navigator series. Allows setting the color
  55182. * while disallowing the default candlestick setting.
  55183. *
  55184. * @type {Highcharts.ColorString|null}
  55185. */
  55186. lineColor: null,
  55187. marker: {
  55188. enabled: false
  55189. },
  55190. /**
  55191. * Since Highstock v8, default value is the same as default
  55192. * `pointRange` defined for a specific type (e.g. `null` for
  55193. * column type).
  55194. *
  55195. * In Highstock version < 8, defaults to 0.
  55196. *
  55197. * @extends plotOptions.series.pointRange
  55198. * @type {number|null}
  55199. * @apioption navigator.series.pointRange
  55200. */
  55201. /**
  55202. * The threshold option. Setting it to 0 will make the default
  55203. * navigator area series draw its area from the 0 value and up.
  55204. *
  55205. * @type {number|null}
  55206. */
  55207. threshold: null
  55208. },
  55209. /**
  55210. * Options for the navigator X axis. Default series options for the
  55211. * navigator xAxis are:
  55212. * ```js
  55213. * xAxis: {
  55214. * tickWidth: 0,
  55215. * lineWidth: 0,
  55216. * gridLineWidth: 1,
  55217. * tickPixelInterval: 200,
  55218. * labels: {
  55219. * align: 'left',
  55220. * style: {
  55221. * color: '#888'
  55222. * },
  55223. * x: 3,
  55224. * y: -4
  55225. * }
  55226. * }
  55227. * ```
  55228. *
  55229. * @extends xAxis
  55230. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  55231. * showEmpty, maxRange
  55232. */
  55233. xAxis: {
  55234. /**
  55235. * Additional range on the right side of the xAxis. Works similar to
  55236. * xAxis.maxPadding, but value is set in milliseconds.
  55237. * Can be set for both, main xAxis and navigator's xAxis.
  55238. *
  55239. * @since 6.0.0
  55240. */
  55241. overscroll: 0,
  55242. className: 'highcharts-navigator-xaxis',
  55243. tickLength: 0,
  55244. lineWidth: 0,
  55245. gridLineColor: '#e6e6e6',
  55246. gridLineWidth: 1,
  55247. tickPixelInterval: 200,
  55248. labels: {
  55249. align: 'left',
  55250. /**
  55251. * @type {Highcharts.CSSObject}
  55252. */
  55253. style: {
  55254. /** @ignore */
  55255. color: '#999999'
  55256. },
  55257. x: 3,
  55258. y: -4
  55259. },
  55260. crosshair: false
  55261. },
  55262. /**
  55263. * Options for the navigator Y axis. Default series options for the
  55264. * navigator yAxis are:
  55265. * ```js
  55266. * yAxis: {
  55267. * gridLineWidth: 0,
  55268. * startOnTick: false,
  55269. * endOnTick: false,
  55270. * minPadding: 0.1,
  55271. * maxPadding: 0.1,
  55272. * labels: {
  55273. * enabled: false
  55274. * },
  55275. * title: {
  55276. * text: null
  55277. * },
  55278. * tickWidth: 0
  55279. * }
  55280. * ```
  55281. *
  55282. * @extends yAxis
  55283. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  55284. * showEmpty, scrollbar, top, units, maxRange, minLength,
  55285. * maxLength, resize
  55286. */
  55287. yAxis: {
  55288. className: 'highcharts-navigator-yaxis',
  55289. gridLineWidth: 0,
  55290. startOnTick: false,
  55291. endOnTick: false,
  55292. minPadding: 0.1,
  55293. maxPadding: 0.1,
  55294. labels: {
  55295. enabled: false
  55296. },
  55297. crosshair: false,
  55298. title: {
  55299. text: null
  55300. },
  55301. tickLength: 0,
  55302. tickWidth: 0
  55303. }
  55304. }
  55305. });
  55306. /* eslint-disable no-invalid-this, valid-jsdoc */
  55307. /**
  55308. * Draw one of the handles on the side of the zoomed range in the navigator
  55309. *
  55310. * @private
  55311. * @function Highcharts.Renderer#symbols.navigator-handle
  55312. * @param {number} x
  55313. * @param {number} y
  55314. * @param {number} w
  55315. * @param {number} h
  55316. * @param {Highcharts.NavigatorHandlesOptions} options
  55317. * @return {Highcharts.SVGPathArray}
  55318. * Path to be used in a handle
  55319. */
  55320. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  55321. var halfWidth = (options && options.width || 0) / 2,
  55322. markerPosition = Math.round(halfWidth / 3) + 0.5,
  55323. height = options && options.height || 0;
  55324. return [
  55325. ['M', -halfWidth - 1, 0.5],
  55326. ['L', halfWidth, 0.5],
  55327. ['L', halfWidth, height + 0.5],
  55328. ['L', -halfWidth - 1, height + 0.5],
  55329. ['L', -halfWidth - 1, 0.5],
  55330. ['M', -markerPosition, 4],
  55331. ['L', -markerPosition, height - 3],
  55332. ['M', markerPosition - 1, 4],
  55333. ['L', markerPosition - 1, height - 3]
  55334. ];
  55335. };
  55336. /**
  55337. * The Navigator class
  55338. *
  55339. * @private
  55340. * @class
  55341. * @name Highcharts.Navigator
  55342. *
  55343. * @param {Highcharts.Chart} chart
  55344. * Chart object
  55345. */
  55346. var Navigator = /** @class */ (function () {
  55347. function Navigator(chart) {
  55348. this.baseSeries = void 0;
  55349. this.chart = void 0;
  55350. this.handles = void 0;
  55351. this.height = void 0;
  55352. this.left = void 0;
  55353. this.navigatorEnabled = void 0;
  55354. this.navigatorGroup = void 0;
  55355. this.navigatorOptions = void 0;
  55356. this.navigatorSeries = void 0;
  55357. this.navigatorSize = void 0;
  55358. this.opposite = void 0;
  55359. this.outline = void 0;
  55360. this.outlineHeight = void 0;
  55361. this.range = void 0;
  55362. this.rendered = void 0;
  55363. this.shades = void 0;
  55364. this.size = void 0;
  55365. this.top = void 0;
  55366. this.xAxis = void 0;
  55367. this.yAxis = void 0;
  55368. this.zoomedMax = void 0;
  55369. this.zoomedMin = void 0;
  55370. this.init(chart);
  55371. }
  55372. /**
  55373. * Draw one of the handles on the side of the zoomed range in the navigator
  55374. *
  55375. * @private
  55376. * @function Highcharts.Navigator#drawHandle
  55377. *
  55378. * @param {number} x
  55379. * The x center for the handle
  55380. *
  55381. * @param {number} index
  55382. * 0 for left and 1 for right
  55383. *
  55384. * @param {boolean|undefined} inverted
  55385. * flag for chart.inverted
  55386. *
  55387. * @param {string} verb
  55388. * use 'animate' or 'attr'
  55389. */
  55390. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  55391. var navigator = this,
  55392. height = navigator.navigatorOptions.handles.height;
  55393. // Place it
  55394. navigator.handles[index][verb](inverted ? {
  55395. translateX: Math.round(navigator.left + navigator.height / 2),
  55396. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  55397. } : {
  55398. translateX: Math.round(navigator.left + parseInt(x, 10)),
  55399. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  55400. });
  55401. };
  55402. /**
  55403. * Render outline around the zoomed range
  55404. *
  55405. * @private
  55406. * @function Highcharts.Navigator#drawOutline
  55407. *
  55408. * @param {number} zoomedMin
  55409. * in pixels position where zoomed range starts
  55410. *
  55411. * @param {number} zoomedMax
  55412. * in pixels position where zoomed range ends
  55413. *
  55414. * @param {boolean|undefined} inverted
  55415. * flag if chart is inverted
  55416. *
  55417. * @param {string} verb
  55418. * use 'animate' or 'attr'
  55419. */
  55420. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  55421. var navigator = this,
  55422. maskInside = navigator.navigatorOptions.maskInside,
  55423. outlineWidth = navigator.outline.strokeWidth(),
  55424. halfOutline = outlineWidth / 2,
  55425. outlineCorrection = (outlineWidth % 2) / 2, // #5800
  55426. outlineHeight = navigator.outlineHeight,
  55427. scrollbarHeight = navigator.scrollbarHeight || 0,
  55428. navigatorSize = navigator.size,
  55429. left = navigator.left - scrollbarHeight,
  55430. navigatorTop = navigator.top,
  55431. verticalMin,
  55432. path;
  55433. if (inverted) {
  55434. left -= halfOutline;
  55435. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  55436. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  55437. path = [
  55438. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  55439. ['L', left + outlineHeight, verticalMin],
  55440. ['L', left, verticalMin],
  55441. ['L', left, zoomedMax],
  55442. ['L', left + outlineHeight, zoomedMax],
  55443. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  55444. ];
  55445. if (maskInside) {
  55446. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  55447. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  55448. );
  55449. }
  55450. }
  55451. else {
  55452. zoomedMin += left + scrollbarHeight - outlineCorrection;
  55453. zoomedMax += left + scrollbarHeight - outlineCorrection;
  55454. navigatorTop += halfOutline;
  55455. path = [
  55456. ['M', left, navigatorTop],
  55457. ['L', zoomedMin, navigatorTop],
  55458. ['L', zoomedMin, navigatorTop + outlineHeight],
  55459. ['L', zoomedMax, navigatorTop + outlineHeight],
  55460. ['L', zoomedMax, navigatorTop],
  55461. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  55462. ];
  55463. if (maskInside) {
  55464. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  55465. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  55466. );
  55467. }
  55468. }
  55469. navigator.outline[verb]({
  55470. d: path
  55471. });
  55472. };
  55473. /**
  55474. * Render outline around the zoomed range
  55475. *
  55476. * @private
  55477. * @function Highcharts.Navigator#drawMasks
  55478. *
  55479. * @param {number} zoomedMin
  55480. * in pixels position where zoomed range starts
  55481. *
  55482. * @param {number} zoomedMax
  55483. * in pixels position where zoomed range ends
  55484. *
  55485. * @param {boolean|undefined} inverted
  55486. * flag if chart is inverted
  55487. *
  55488. * @param {string} verb
  55489. * use 'animate' or 'attr'
  55490. */
  55491. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  55492. var navigator = this,
  55493. left = navigator.left,
  55494. top = navigator.top,
  55495. navigatorHeight = navigator.height,
  55496. height,
  55497. width,
  55498. x,
  55499. y;
  55500. // Determine rectangle position & size
  55501. // According to (non)inverted position:
  55502. if (inverted) {
  55503. x = [left, left, left];
  55504. y = [top, top + zoomedMin, top + zoomedMax];
  55505. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  55506. height = [
  55507. zoomedMin,
  55508. zoomedMax - zoomedMin,
  55509. navigator.size - zoomedMax
  55510. ];
  55511. }
  55512. else {
  55513. x = [left, left + zoomedMin, left + zoomedMax];
  55514. y = [top, top, top];
  55515. width = [
  55516. zoomedMin,
  55517. zoomedMax - zoomedMin,
  55518. navigator.size - zoomedMax
  55519. ];
  55520. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  55521. }
  55522. navigator.shades.forEach(function (shade, i) {
  55523. shade[verb]({
  55524. x: x[i],
  55525. y: y[i],
  55526. width: width[i],
  55527. height: height[i]
  55528. });
  55529. });
  55530. };
  55531. /**
  55532. * Generate DOM elements for a navigator:
  55533. *
  55534. * - main navigator group
  55535. *
  55536. * - all shades
  55537. *
  55538. * - outline
  55539. *
  55540. * - handles
  55541. *
  55542. * @private
  55543. * @function Highcharts.Navigator#renderElements
  55544. */
  55545. Navigator.prototype.renderElements = function () {
  55546. var navigator = this,
  55547. navigatorOptions = navigator.navigatorOptions,
  55548. maskInside = navigatorOptions.maskInside,
  55549. chart = navigator.chart,
  55550. inverted = chart.inverted,
  55551. renderer = chart.renderer,
  55552. navigatorGroup,
  55553. mouseCursor = {
  55554. cursor: inverted ? 'ns-resize' : 'ew-resize'
  55555. };
  55556. // Create the main navigator group
  55557. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  55558. .attr({
  55559. zIndex: 8,
  55560. visibility: 'hidden'
  55561. })
  55562. .add();
  55563. // Create masks, each mask will get events and fill:
  55564. [
  55565. !maskInside,
  55566. maskInside,
  55567. !maskInside
  55568. ].forEach(function (hasMask, index) {
  55569. navigator.shades[index] = renderer.rect()
  55570. .addClass('highcharts-navigator-mask' +
  55571. (index === 1 ? '-inside' : '-outside'))
  55572. .add(navigatorGroup);
  55573. if (!chart.styledMode) {
  55574. navigator.shades[index]
  55575. .attr({
  55576. fill: hasMask ?
  55577. navigatorOptions.maskFill :
  55578. 'rgba(0,0,0,0)'
  55579. })
  55580. .css((index === 1) && mouseCursor);
  55581. }
  55582. });
  55583. // Create the outline:
  55584. navigator.outline = renderer.path()
  55585. .addClass('highcharts-navigator-outline')
  55586. .add(navigatorGroup);
  55587. if (!chart.styledMode) {
  55588. navigator.outline.attr({
  55589. 'stroke-width': navigatorOptions.outlineWidth,
  55590. stroke: navigatorOptions.outlineColor
  55591. });
  55592. }
  55593. // Create the handlers:
  55594. if (navigatorOptions.handles.enabled) {
  55595. [0, 1].forEach(function (index) {
  55596. navigatorOptions.handles.inverted = chart.inverted;
  55597. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  55598. // zIndex = 6 for right handle, 7 for left.
  55599. // Can't be 10, because of the tooltip in inverted chart #2908
  55600. navigator.handles[index].attr({ zIndex: 7 - index })
  55601. .addClass('highcharts-navigator-handle ' +
  55602. 'highcharts-navigator-handle-' +
  55603. ['left', 'right'][index]).add(navigatorGroup);
  55604. if (!chart.styledMode) {
  55605. var handlesOptions = navigatorOptions.handles;
  55606. navigator.handles[index]
  55607. .attr({
  55608. fill: handlesOptions.backgroundColor,
  55609. stroke: handlesOptions.borderColor,
  55610. 'stroke-width': handlesOptions.lineWidth
  55611. })
  55612. .css(mouseCursor);
  55613. }
  55614. });
  55615. }
  55616. };
  55617. /**
  55618. * Update navigator
  55619. *
  55620. * @private
  55621. * @function Highcharts.Navigator#update
  55622. *
  55623. * @param {Highcharts.NavigatorOptions} options
  55624. * Options to merge in when updating navigator
  55625. */
  55626. Navigator.prototype.update = function (options) {
  55627. // Remove references to old navigator series in base series
  55628. (this.series || []).forEach(function (series) {
  55629. if (series.baseSeries) {
  55630. delete series.baseSeries.navigatorSeries;
  55631. }
  55632. });
  55633. // Destroy and rebuild navigator
  55634. this.destroy();
  55635. var chartOptions = this.chart.options;
  55636. merge(true, chartOptions.navigator, this.options, options);
  55637. this.init(this.chart);
  55638. };
  55639. /**
  55640. * Render the navigator
  55641. *
  55642. * @private
  55643. * @function Highcharts.Navigator#render
  55644. * @param {number} min
  55645. * X axis value minimum
  55646. * @param {number} max
  55647. * X axis value maximum
  55648. * @param {number} [pxMin]
  55649. * Pixel value minimum
  55650. * @param {number} [pxMax]
  55651. * Pixel value maximum
  55652. * @return {void}
  55653. */
  55654. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  55655. var navigator = this,
  55656. chart = navigator.chart,
  55657. navigatorWidth,
  55658. scrollbarLeft,
  55659. scrollbarTop,
  55660. scrollbarHeight = navigator.scrollbarHeight,
  55661. navigatorSize,
  55662. xAxis = navigator.xAxis,
  55663. pointRange = xAxis.pointRange || 0,
  55664. scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis,
  55665. navigatorEnabled = navigator.navigatorEnabled,
  55666. zoomedMin,
  55667. zoomedMax,
  55668. rendered = navigator.rendered,
  55669. inverted = chart.inverted,
  55670. verb,
  55671. newMin,
  55672. newMax,
  55673. currentRange,
  55674. minRange = chart.xAxis[0].minRange,
  55675. maxRange = chart.xAxis[0].options.maxRange;
  55676. // Don't redraw while moving the handles (#4703).
  55677. if (this.hasDragged && !defined(pxMin)) {
  55678. return;
  55679. }
  55680. min = correctFloat(min - pointRange / 2);
  55681. max = correctFloat(max + pointRange / 2);
  55682. // Don't render the navigator until we have data (#486, #4202, #5172).
  55683. if (!isNumber(min) || !isNumber(max)) {
  55684. // However, if navigator was already rendered, we may need to resize
  55685. // it. For example hidden series, but visible navigator (#6022).
  55686. if (rendered) {
  55687. pxMin = 0;
  55688. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  55689. }
  55690. else {
  55691. return;
  55692. }
  55693. }
  55694. navigator.left = pick(xAxis.left,
  55695. // in case of scrollbar only, without navigator
  55696. chart.plotLeft + scrollbarHeight +
  55697. (inverted ? chart.plotWidth : 0));
  55698. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  55699. 2 * scrollbarHeight);
  55700. if (inverted) {
  55701. navigatorWidth = scrollbarHeight;
  55702. }
  55703. else {
  55704. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  55705. }
  55706. // Get the pixel position of the handles
  55707. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  55708. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  55709. // Verify (#1851, #2238)
  55710. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  55711. pxMin = 0;
  55712. pxMax = navigatorWidth;
  55713. }
  55714. // Are we below the minRange? (#2618, #6191)
  55715. newMin = xAxis.toValue(pxMin, true);
  55716. newMax = xAxis.toValue(pxMax, true);
  55717. currentRange = Math.abs(correctFloat(newMax - newMin));
  55718. if (currentRange < minRange) {
  55719. if (this.grabbedLeft) {
  55720. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  55721. }
  55722. else if (this.grabbedRight) {
  55723. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  55724. }
  55725. }
  55726. else if (defined(maxRange) &&
  55727. correctFloat(currentRange - pointRange) > maxRange) {
  55728. if (this.grabbedLeft) {
  55729. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  55730. }
  55731. else if (this.grabbedRight) {
  55732. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  55733. }
  55734. }
  55735. // Handles are allowed to cross, but never exceed the plot area
  55736. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  55737. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  55738. navigator.zoomedMax - navigator.fixedWidth :
  55739. Math.min(pxMin, pxMax), 0, zoomedMax);
  55740. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  55741. zoomedMax = Math.round(navigator.zoomedMax);
  55742. zoomedMin = Math.round(navigator.zoomedMin);
  55743. if (navigatorEnabled) {
  55744. navigator.navigatorGroup.attr({
  55745. visibility: 'visible'
  55746. });
  55747. // Place elements
  55748. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  55749. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  55750. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  55751. if (navigator.navigatorOptions.handles.enabled) {
  55752. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  55753. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  55754. }
  55755. }
  55756. if (navigator.scrollbar) {
  55757. if (inverted) {
  55758. scrollbarTop = navigator.top - scrollbarHeight;
  55759. scrollbarLeft = navigator.left - scrollbarHeight +
  55760. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  55761. // Multiple axes has offsets:
  55762. (scrollbarXAxis.titleOffset || 0) +
  55763. // Self margin from the axis.title
  55764. scrollbarXAxis.axisTitleMargin);
  55765. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  55766. }
  55767. else {
  55768. scrollbarTop = navigator.top + (navigatorEnabled ?
  55769. navigator.height :
  55770. -scrollbarHeight);
  55771. scrollbarLeft = navigator.left - scrollbarHeight;
  55772. }
  55773. // Reposition scrollbar
  55774. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  55775. // Keep scale 0-1
  55776. navigator.scrollbar.setRange(
  55777. // Use real value, not rounded because range can be very small
  55778. // (#1716)
  55779. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  55780. }
  55781. navigator.rendered = true;
  55782. };
  55783. /**
  55784. * Set up the mouse and touch events for the navigator
  55785. *
  55786. * @private
  55787. * @function Highcharts.Navigator#addMouseEvents
  55788. */
  55789. Navigator.prototype.addMouseEvents = function () {
  55790. var navigator = this,
  55791. chart = navigator.chart,
  55792. container = chart.container,
  55793. eventsToUnbind = [],
  55794. mouseMoveHandler,
  55795. mouseUpHandler;
  55796. /**
  55797. * Create mouse events' handlers.
  55798. * Make them as separate functions to enable wrapping them:
  55799. */
  55800. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  55801. navigator.onMouseMove(e);
  55802. };
  55803. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  55804. navigator.onMouseUp(e);
  55805. };
  55806. // Add shades and handles mousedown events
  55807. eventsToUnbind = navigator.getPartsEvents('mousedown');
  55808. // Add mouse move and mouseup events. These are bind to doc/container,
  55809. // because Navigator.grabbedSomething flags are stored in mousedown
  55810. // events
  55811. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  55812. // Touch events
  55813. if (hasTouch) {
  55814. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  55815. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  55816. }
  55817. navigator.eventsToUnbind = eventsToUnbind;
  55818. // Data events
  55819. if (navigator.series && navigator.series[0]) {
  55820. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  55821. chart.navigator.modifyNavigatorAxisExtremes();
  55822. }));
  55823. }
  55824. };
  55825. /**
  55826. * Generate events for handles and masks
  55827. *
  55828. * @private
  55829. * @function Highcharts.Navigator#getPartsEvents
  55830. *
  55831. * @param {string} eventName
  55832. * Event name handler, 'mousedown' or 'touchstart'
  55833. *
  55834. * @return {Array<Function>}
  55835. * An array of functions to remove navigator functions from the
  55836. * events again.
  55837. */
  55838. Navigator.prototype.getPartsEvents = function (eventName) {
  55839. var navigator = this,
  55840. events = [];
  55841. ['shades', 'handles'].forEach(function (name) {
  55842. navigator[name].forEach(function (navigatorItem, index) {
  55843. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  55844. navigator[name + 'Mousedown'](e, index);
  55845. }));
  55846. });
  55847. });
  55848. return events;
  55849. };
  55850. /**
  55851. * Mousedown on a shaded mask, either:
  55852. *
  55853. * - will be stored for future drag&drop
  55854. *
  55855. * - will directly shift to a new range
  55856. *
  55857. * @private
  55858. * @function Highcharts.Navigator#shadesMousedown
  55859. *
  55860. * @param {Highcharts.PointerEventObject} e
  55861. * Mouse event
  55862. *
  55863. * @param {number} index
  55864. * Index of a mask in Navigator.shades array
  55865. */
  55866. Navigator.prototype.shadesMousedown = function (e, index) {
  55867. e = this.chart.pointer.normalize(e);
  55868. var navigator = this,
  55869. chart = navigator.chart,
  55870. xAxis = navigator.xAxis,
  55871. zoomedMin = navigator.zoomedMin,
  55872. navigatorPosition = navigator.left,
  55873. navigatorSize = navigator.size,
  55874. range = navigator.range,
  55875. chartX = e.chartX,
  55876. fixedMax,
  55877. fixedMin,
  55878. ext,
  55879. left;
  55880. // For inverted chart, swap some options:
  55881. if (chart.inverted) {
  55882. chartX = e.chartY;
  55883. navigatorPosition = navigator.top;
  55884. }
  55885. if (index === 1) {
  55886. // Store information for drag&drop
  55887. navigator.grabbedCenter = chartX;
  55888. navigator.fixedWidth = range;
  55889. navigator.dragOffset = chartX - zoomedMin;
  55890. }
  55891. else {
  55892. // Shift the range by clicking on shaded areas
  55893. left = chartX - navigatorPosition - range / 2;
  55894. if (index === 0) {
  55895. left = Math.max(0, left);
  55896. }
  55897. else if (index === 2 && left + range >= navigatorSize) {
  55898. left = navigatorSize - range;
  55899. if (navigator.reversedExtremes) {
  55900. // #7713
  55901. left -= range;
  55902. fixedMin = navigator.getUnionExtremes().dataMin;
  55903. }
  55904. else {
  55905. // #2293, #3543
  55906. fixedMax = navigator.getUnionExtremes().dataMax;
  55907. }
  55908. }
  55909. if (left !== zoomedMin) { // it has actually moved
  55910. navigator.fixedWidth = range; // #1370
  55911. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  55912. if (defined(ext.min)) { // #7411
  55913. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  55914. { trigger: 'navigator' });
  55915. }
  55916. }
  55917. }
  55918. };
  55919. /**
  55920. * Mousedown on a handle mask.
  55921. * Will store necessary information for drag&drop.
  55922. *
  55923. * @private
  55924. * @function Highcharts.Navigator#handlesMousedown
  55925. * @param {Highcharts.PointerEventObject} e
  55926. * Mouse event
  55927. * @param {number} index
  55928. * Index of a handle in Navigator.handles array
  55929. * @return {void}
  55930. */
  55931. Navigator.prototype.handlesMousedown = function (e, index) {
  55932. e = this.chart.pointer.normalize(e);
  55933. var navigator = this,
  55934. chart = navigator.chart,
  55935. baseXAxis = chart.xAxis[0],
  55936. // For reversed axes, min and max are changed,
  55937. // so the other extreme should be stored
  55938. reverse = navigator.reversedExtremes;
  55939. if (index === 0) {
  55940. // Grab the left handle
  55941. navigator.grabbedLeft = true;
  55942. navigator.otherHandlePos = navigator.zoomedMax;
  55943. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  55944. }
  55945. else {
  55946. // Grab the right handle
  55947. navigator.grabbedRight = true;
  55948. navigator.otherHandlePos = navigator.zoomedMin;
  55949. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  55950. }
  55951. chart.fixedRange = null;
  55952. };
  55953. /**
  55954. * Mouse move event based on x/y mouse position.
  55955. *
  55956. * @private
  55957. * @function Highcharts.Navigator#onMouseMove
  55958. *
  55959. * @param {Highcharts.PointerEventObject} e
  55960. * Mouse event
  55961. */
  55962. Navigator.prototype.onMouseMove = function (e) {
  55963. var navigator = this,
  55964. chart = navigator.chart,
  55965. left = navigator.left,
  55966. navigatorSize = navigator.navigatorSize,
  55967. range = navigator.range,
  55968. dragOffset = navigator.dragOffset,
  55969. inverted = chart.inverted,
  55970. chartX;
  55971. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  55972. // the finger down in the center of the scrollbar. This should be
  55973. // ignored.
  55974. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  55975. e = chart.pointer.normalize(e);
  55976. chartX = e.chartX;
  55977. // Swap some options for inverted chart
  55978. if (inverted) {
  55979. left = navigator.top;
  55980. chartX = e.chartY;
  55981. }
  55982. // Drag left handle or top handle
  55983. if (navigator.grabbedLeft) {
  55984. navigator.hasDragged = true;
  55985. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  55986. // Drag right handle or bottom handle
  55987. }
  55988. else if (navigator.grabbedRight) {
  55989. navigator.hasDragged = true;
  55990. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  55991. // Drag scrollbar or open area in navigator
  55992. }
  55993. else if (navigator.grabbedCenter) {
  55994. navigator.hasDragged = true;
  55995. if (chartX < dragOffset) { // outside left
  55996. chartX = dragOffset;
  55997. // outside right
  55998. }
  55999. else if (chartX >
  56000. navigatorSize + dragOffset - range) {
  56001. chartX = navigatorSize + dragOffset - range;
  56002. }
  56003. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  56004. }
  56005. if (navigator.hasDragged &&
  56006. navigator.scrollbar &&
  56007. pick(navigator.scrollbar.options.liveRedraw,
  56008. // By default, don't run live redraw on VML, on touch
  56009. // devices or if the chart is in boost.
  56010. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  56011. e.DOMType = e.type; // DOMType is for IE8
  56012. setTimeout(function () {
  56013. navigator.onMouseUp(e);
  56014. }, 0);
  56015. }
  56016. }
  56017. };
  56018. /**
  56019. * Mouse up event based on x/y mouse position.
  56020. *
  56021. * @private
  56022. * @function Highcharts.Navigator#onMouseUp
  56023. * @param {Highcharts.PointerEventObject} e
  56024. * Mouse event
  56025. * @return {void}
  56026. */
  56027. Navigator.prototype.onMouseUp = function (e) {
  56028. var navigator = this,
  56029. chart = navigator.chart,
  56030. xAxis = navigator.xAxis,
  56031. scrollbar = navigator.scrollbar,
  56032. DOMEvent = e.DOMEvent || e,
  56033. inverted = chart.inverted,
  56034. verb = navigator.rendered && !navigator.hasDragged ?
  56035. 'animate' : 'attr',
  56036. zoomedMax,
  56037. zoomedMin,
  56038. unionExtremes,
  56039. fixedMin,
  56040. fixedMax,
  56041. ext;
  56042. if (
  56043. // MouseUp is called for both, navigator and scrollbar (that order),
  56044. // which causes calling afterSetExtremes twice. Prevent first call
  56045. // by checking if scrollbar is going to set new extremes (#6334)
  56046. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  56047. e.trigger === 'scrollbar') {
  56048. unionExtremes = navigator.getUnionExtremes();
  56049. // When dragging one handle, make sure the other one doesn't change
  56050. if (navigator.zoomedMin === navigator.otherHandlePos) {
  56051. fixedMin = navigator.fixedExtreme;
  56052. }
  56053. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  56054. fixedMax = navigator.fixedExtreme;
  56055. }
  56056. // Snap to right edge (#4076)
  56057. if (navigator.zoomedMax === navigator.size) {
  56058. fixedMax = navigator.reversedExtremes ?
  56059. unionExtremes.dataMin :
  56060. unionExtremes.dataMax;
  56061. }
  56062. // Snap to left edge (#7576)
  56063. if (navigator.zoomedMin === 0) {
  56064. fixedMin = navigator.reversedExtremes ?
  56065. unionExtremes.dataMax :
  56066. unionExtremes.dataMin;
  56067. }
  56068. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  56069. if (defined(ext.min)) {
  56070. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  56071. // Run animation when clicking buttons, scrollbar track etc,
  56072. // but not when dragging handles or scrollbar
  56073. navigator.hasDragged ? false : null, {
  56074. trigger: 'navigator',
  56075. triggerOp: 'navigator-drag',
  56076. DOMEvent: DOMEvent // #1838
  56077. });
  56078. }
  56079. }
  56080. if (e.DOMType !== 'mousemove' &&
  56081. e.DOMType !== 'touchmove') {
  56082. navigator.grabbedLeft = navigator.grabbedRight =
  56083. navigator.grabbedCenter = navigator.fixedWidth =
  56084. navigator.fixedExtreme = navigator.otherHandlePos =
  56085. navigator.hasDragged = navigator.dragOffset = null;
  56086. }
  56087. // Update position of navigator shades, outline and handles (#12573)
  56088. if (navigator.navigatorEnabled &&
  56089. isNumber(navigator.zoomedMin) &&
  56090. isNumber(navigator.zoomedMax)) {
  56091. zoomedMin = Math.round(navigator.zoomedMin);
  56092. zoomedMax = Math.round(navigator.zoomedMax);
  56093. if (navigator.shades) {
  56094. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  56095. }
  56096. if (navigator.outline) {
  56097. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  56098. }
  56099. if (navigator.navigatorOptions.handles.enabled &&
  56100. Object.keys(navigator.handles).length ===
  56101. navigator.handles.length) {
  56102. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  56103. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  56104. }
  56105. }
  56106. };
  56107. /**
  56108. * Removes the event handlers attached previously with addEvents.
  56109. *
  56110. * @private
  56111. * @function Highcharts.Navigator#removeEvents
  56112. * @return {void}
  56113. */
  56114. Navigator.prototype.removeEvents = function () {
  56115. if (this.eventsToUnbind) {
  56116. this.eventsToUnbind.forEach(function (unbind) {
  56117. unbind();
  56118. });
  56119. this.eventsToUnbind = void 0;
  56120. }
  56121. this.removeBaseSeriesEvents();
  56122. };
  56123. /**
  56124. * Remove data events.
  56125. *
  56126. * @private
  56127. * @function Highcharts.Navigator#removeBaseSeriesEvents
  56128. * @return {void}
  56129. */
  56130. Navigator.prototype.removeBaseSeriesEvents = function () {
  56131. var baseSeries = this.baseSeries || [];
  56132. if (this.navigatorEnabled && baseSeries[0]) {
  56133. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  56134. baseSeries.forEach(function (series) {
  56135. removeEvent(series, 'updatedData', this.updatedDataHandler);
  56136. }, this);
  56137. }
  56138. // We only listen for extremes-events on the first baseSeries
  56139. if (baseSeries[0].xAxis) {
  56140. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  56141. }
  56142. }
  56143. };
  56144. /**
  56145. * Initialize the Navigator object
  56146. *
  56147. * @private
  56148. * @function Highcharts.Navigator#init
  56149. *
  56150. * @param {Highcharts.Chart} chart
  56151. */
  56152. Navigator.prototype.init = function (chart) {
  56153. var chartOptions = chart.options,
  56154. navigatorOptions = chartOptions.navigator,
  56155. navigatorEnabled = navigatorOptions.enabled,
  56156. scrollbarOptions = chartOptions.scrollbar,
  56157. scrollbarEnabled = scrollbarOptions.enabled,
  56158. height = navigatorEnabled ? navigatorOptions.height : 0,
  56159. scrollbarHeight = scrollbarEnabled ?
  56160. scrollbarOptions.height :
  56161. 0;
  56162. this.handles = [];
  56163. this.shades = [];
  56164. this.chart = chart;
  56165. this.setBaseSeries();
  56166. this.height = height;
  56167. this.scrollbarHeight = scrollbarHeight;
  56168. this.scrollbarEnabled = scrollbarEnabled;
  56169. this.navigatorEnabled = navigatorEnabled;
  56170. this.navigatorOptions = navigatorOptions;
  56171. this.scrollbarOptions = scrollbarOptions;
  56172. this.outlineHeight = height + scrollbarHeight;
  56173. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  56174. var navigator = this,
  56175. baseSeries = navigator.baseSeries,
  56176. xAxisIndex = chart.xAxis.length,
  56177. yAxisIndex = chart.yAxis.length,
  56178. baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  56179. chart.xAxis[0] || { options: {} };
  56180. chart.isDirtyBox = true;
  56181. if (navigator.navigatorEnabled) {
  56182. // an x axis is required for scrollbar also
  56183. navigator.xAxis = new Axis(chart, merge({
  56184. // inherit base xAxis' break and ordinal options
  56185. breaks: baseXaxis.options.breaks,
  56186. ordinal: baseXaxis.options.ordinal
  56187. }, navigatorOptions.xAxis, {
  56188. id: 'navigator-x-axis',
  56189. yAxis: 'navigator-y-axis',
  56190. isX: true,
  56191. type: 'datetime',
  56192. index: xAxisIndex,
  56193. isInternal: true,
  56194. offset: 0,
  56195. keepOrdinalPadding: true,
  56196. startOnTick: false,
  56197. endOnTick: false,
  56198. minPadding: 0,
  56199. maxPadding: 0,
  56200. zoomEnabled: false
  56201. }, chart.inverted ? {
  56202. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  56203. width: height
  56204. } : {
  56205. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  56206. height: height
  56207. }));
  56208. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  56209. id: 'navigator-y-axis',
  56210. alignTicks: false,
  56211. offset: 0,
  56212. index: yAxisIndex,
  56213. isInternal: true,
  56214. zoomEnabled: false
  56215. }, chart.inverted ? {
  56216. width: height
  56217. } : {
  56218. height: height
  56219. }));
  56220. // If we have a base series, initialize the navigator series
  56221. if (baseSeries || navigatorOptions.series.data) {
  56222. navigator.updateNavigatorSeries(false);
  56223. // If not, set up an event to listen for added series
  56224. }
  56225. else if (chart.series.length === 0) {
  56226. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  56227. // We've got one, now add it as base
  56228. if (chart.series.length > 0 && !navigator.series) {
  56229. navigator.setBaseSeries();
  56230. navigator.unbindRedraw(); // reset
  56231. }
  56232. });
  56233. }
  56234. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  56235. // Render items, so we can bind events to them:
  56236. navigator.renderElements();
  56237. // Add mouse events
  56238. navigator.addMouseEvents();
  56239. // in case of scrollbar only, fake an x axis to get translation
  56240. }
  56241. else {
  56242. navigator.xAxis = {
  56243. chart: chart,
  56244. navigatorAxis: {
  56245. fake: true
  56246. },
  56247. translate: function (value, reverse) {
  56248. var axis = chart.xAxis[0], ext = axis.getExtremes(), scrollTrackWidth = axis.len - 2 * scrollbarHeight, min = numExt('min', axis.options.min, ext.dataMin), valueRange = numExt('max', axis.options.max, ext.dataMax) - min;
  56249. return reverse ?
  56250. // from pixel to value
  56251. (value * valueRange / scrollTrackWidth) + min :
  56252. // from value to pixel
  56253. scrollTrackWidth * (value - min) / valueRange;
  56254. },
  56255. toPixels: function (value) {
  56256. return this.translate(value);
  56257. },
  56258. toValue: function (value) {
  56259. return this.translate(value, true);
  56260. }
  56261. };
  56262. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  56263. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  56264. }
  56265. // Initialize the scrollbar
  56266. if (chart.options.scrollbar.enabled) {
  56267. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  56268. margin: navigator.navigatorEnabled ? 0 : 10,
  56269. vertical: chart.inverted
  56270. }), chart);
  56271. addEvent(navigator.scrollbar, 'changed', function (e) {
  56272. var range = navigator.size,
  56273. to = range * this.to,
  56274. from = range * this.from;
  56275. navigator.hasDragged = navigator.scrollbar.hasDragged;
  56276. navigator.render(0, 0, from, to);
  56277. if (chart.options.scrollbar.liveRedraw ||
  56278. (e.DOMType !== 'mousemove' &&
  56279. e.DOMType !== 'touchmove')) {
  56280. setTimeout(function () {
  56281. navigator.onMouseUp(e);
  56282. });
  56283. }
  56284. });
  56285. }
  56286. // Add data events
  56287. navigator.addBaseSeriesEvents();
  56288. // Add redraw events
  56289. navigator.addChartEvents();
  56290. };
  56291. /**
  56292. * Get the union data extremes of the chart - the outer data extremes of the
  56293. * base X axis and the navigator axis.
  56294. *
  56295. * @private
  56296. * @function Highcharts.Navigator#getUnionExtremes
  56297. * @param {boolean} [returnFalseOnNoBaseSeries]
  56298. * as the param says.
  56299. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  56300. */
  56301. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  56302. var baseAxis = this.chart.xAxis[0],
  56303. navAxis = this.xAxis,
  56304. navAxisOptions = navAxis.options,
  56305. baseAxisOptions = baseAxis.options,
  56306. ret;
  56307. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  56308. ret = {
  56309. dataMin: pick(// #4053
  56310. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  56311. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  56312. };
  56313. }
  56314. return ret;
  56315. };
  56316. /**
  56317. * Set the base series and update the navigator series from this. With a bit
  56318. * of modification we should be able to make this an API method to be called
  56319. * from the outside
  56320. *
  56321. * @private
  56322. * @function Highcharts.Navigator#setBaseSeries
  56323. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  56324. * Additional series options for a navigator
  56325. * @param {boolean} [redraw]
  56326. * Whether to redraw after update.
  56327. * @return {void}
  56328. */
  56329. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  56330. var chart = this.chart,
  56331. baseSeries = this.baseSeries = [];
  56332. baseSeriesOptions = (baseSeriesOptions ||
  56333. chart.options && chart.options.navigator.baseSeries ||
  56334. (chart.series.length ?
  56335. // Find the first non-navigator series (#8430)
  56336. find(chart.series, function (s) {
  56337. return !s.options.isInternal;
  56338. }).index :
  56339. 0));
  56340. // Iterate through series and add the ones that should be shown in
  56341. // navigator.
  56342. (chart.series || []).forEach(function (series, i) {
  56343. if (
  56344. // Don't include existing nav series
  56345. !series.options.isInternal &&
  56346. (series.options.showInNavigator ||
  56347. (i === baseSeriesOptions ||
  56348. series.options.id === baseSeriesOptions) &&
  56349. series.options.showInNavigator !== false)) {
  56350. baseSeries.push(series);
  56351. }
  56352. });
  56353. // When run after render, this.xAxis already exists
  56354. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  56355. this.updateNavigatorSeries(true, redraw);
  56356. }
  56357. };
  56358. /**
  56359. * Update series in the navigator from baseSeries, adding new if does not
  56360. * exist.
  56361. *
  56362. * @private
  56363. * @function Highcharts.Navigator.updateNavigatorSeries
  56364. * @param {boolean} addEvents
  56365. * @param {boolean} [redraw]
  56366. * @return {void}
  56367. */
  56368. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  56369. var navigator = this,
  56370. chart = navigator.chart,
  56371. baseSeries = navigator.baseSeries,
  56372. baseOptions,
  56373. mergedNavSeriesOptions,
  56374. chartNavigatorSeriesOptions = navigator.navigatorOptions.series,
  56375. baseNavigatorOptions,
  56376. navSeriesMixin = {
  56377. enableMouseTracking: false,
  56378. index: null,
  56379. linkedTo: null,
  56380. group: 'nav',
  56381. padXAxis: false,
  56382. xAxis: 'navigator-x-axis',
  56383. yAxis: 'navigator-y-axis',
  56384. showInLegend: false,
  56385. stacking: void 0,
  56386. isInternal: true,
  56387. states: {
  56388. inactive: {
  56389. opacity: 1
  56390. }
  56391. }
  56392. },
  56393. // Remove navigator series that are no longer in the baseSeries
  56394. navigatorSeries = navigator.series =
  56395. (navigator.series || []).filter(function (navSeries) {
  56396. var base = navSeries.baseSeries;
  56397. if (baseSeries.indexOf(base) < 0) { // Not in array
  56398. // If there is still a base series connected to this
  56399. // series, remove event handler and reference.
  56400. if (base) {
  56401. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  56402. delete base.navigatorSeries;
  56403. }
  56404. // Kill the nav series. It may already have been
  56405. // destroyed (#8715).
  56406. if (navSeries.chart) {
  56407. navSeries.destroy();
  56408. }
  56409. return false;
  56410. }
  56411. return true;
  56412. });
  56413. // Go through each base series and merge the options to create new
  56414. // series
  56415. if (baseSeries && baseSeries.length) {
  56416. baseSeries.forEach(function eachBaseSeries(base) {
  56417. var linkedNavSeries = base.navigatorSeries,
  56418. userNavOptions = extend(
  56419. // Grab color and visibility from base as default
  56420. {
  56421. color: base.color,
  56422. visible: base.visible
  56423. }, !isArray(chartNavigatorSeriesOptions) ?
  56424. chartNavigatorSeriesOptions :
  56425. defaultOptions.navigator.series);
  56426. // Don't update if the series exists in nav and we have disabled
  56427. // adaptToUpdatedData.
  56428. if (linkedNavSeries &&
  56429. navigator.navigatorOptions.adaptToUpdatedData === false) {
  56430. return;
  56431. }
  56432. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  56433. baseOptions = base.options || {};
  56434. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  56435. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  56436. // Once nav series type is resolved, pick correct pointRange
  56437. mergedNavSeriesOptions.pointRange = pick(
  56438. // Stricte set pointRange in options
  56439. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  56440. // Fallback to default values, e.g. `null` for column
  56441. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  56442. // Merge data separately. Do a slice to avoid mutating the
  56443. // navigator options from base series (#4923).
  56444. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  56445. navigator.hasNavigatorData =
  56446. navigator.hasNavigatorData || !!navigatorSeriesData;
  56447. mergedNavSeriesOptions.data =
  56448. navigatorSeriesData ||
  56449. baseOptions.data && baseOptions.data.slice(0);
  56450. // Update or add the series
  56451. if (linkedNavSeries && linkedNavSeries.options) {
  56452. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  56453. }
  56454. else {
  56455. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  56456. base.navigatorSeries.baseSeries = base; // Store ref
  56457. navigatorSeries.push(base.navigatorSeries);
  56458. }
  56459. });
  56460. }
  56461. // If user has defined data (and no base series) or explicitly defined
  56462. // navigator.series as an array, we create these series on top of any
  56463. // base series.
  56464. if (chartNavigatorSeriesOptions.data &&
  56465. !(baseSeries && baseSeries.length) ||
  56466. isArray(chartNavigatorSeriesOptions)) {
  56467. navigator.hasNavigatorData = false;
  56468. // Allow navigator.series to be an array
  56469. chartNavigatorSeriesOptions =
  56470. splat(chartNavigatorSeriesOptions);
  56471. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  56472. navSeriesMixin.name =
  56473. 'Navigator ' + (navigatorSeries.length + 1);
  56474. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  56475. // Since we don't have a base series to pull color from,
  56476. // try to fake it by using color from series with same
  56477. // index. Otherwise pull from the colors array. We need
  56478. // an explicit color as otherwise updates will increment
  56479. // color counter and we'll get a new color for each
  56480. // update of the nav series.
  56481. color: chart.series[i] &&
  56482. !chart.series[i].options.isInternal &&
  56483. chart.series[i].color ||
  56484. chart.options.colors[i] ||
  56485. chart.options.colors[0]
  56486. }, navSeriesMixin, userSeriesOptions);
  56487. mergedNavSeriesOptions.data = userSeriesOptions.data;
  56488. if (mergedNavSeriesOptions.data) {
  56489. navigator.hasNavigatorData = true;
  56490. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  56491. }
  56492. });
  56493. }
  56494. if (addEvents) {
  56495. this.addBaseSeriesEvents();
  56496. }
  56497. };
  56498. /**
  56499. * Add data events.
  56500. * For example when main series is updated we need to recalculate extremes
  56501. *
  56502. * @private
  56503. * @function Highcharts.Navigator#addBaseSeriesEvent
  56504. * @return {void}
  56505. */
  56506. Navigator.prototype.addBaseSeriesEvents = function () {
  56507. var navigator = this,
  56508. baseSeries = navigator.baseSeries || [];
  56509. // Bind modified extremes event to first base's xAxis only.
  56510. // In event of > 1 base-xAxes, the navigator will ignore those.
  56511. // Adding this multiple times to the same axis is no problem, as
  56512. // duplicates should be discarded by the browser.
  56513. if (baseSeries[0] && baseSeries[0].xAxis) {
  56514. addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  56515. }
  56516. baseSeries.forEach(function (base) {
  56517. // Link base series show/hide to navigator series visibility
  56518. addEvent(base, 'show', function () {
  56519. if (this.navigatorSeries) {
  56520. this.navigatorSeries.setVisible(true, false);
  56521. }
  56522. });
  56523. addEvent(base, 'hide', function () {
  56524. if (this.navigatorSeries) {
  56525. this.navigatorSeries.setVisible(false, false);
  56526. }
  56527. });
  56528. // Respond to updated data in the base series, unless explicitily
  56529. // not adapting to data changes.
  56530. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  56531. if (base.xAxis) {
  56532. addEvent(base, 'updatedData', this.updatedDataHandler);
  56533. }
  56534. }
  56535. // Handle series removal
  56536. addEvent(base, 'remove', function () {
  56537. if (this.navigatorSeries) {
  56538. erase(navigator.series, this.navigatorSeries);
  56539. if (defined(this.navigatorSeries.options)) {
  56540. this.navigatorSeries.remove(false);
  56541. }
  56542. delete this.navigatorSeries;
  56543. }
  56544. });
  56545. }, this);
  56546. };
  56547. /**
  56548. * Get minimum from all base series connected to the navigator
  56549. * @private
  56550. * @param {number} currentSeriesMin
  56551. * Minium from the current series
  56552. * @return {number} Minimum from all series
  56553. */
  56554. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  56555. return this.baseSeries.reduce(function (min, series) {
  56556. // (#10193)
  56557. return Math.min(min, series.xData ? series.xData[0] : min);
  56558. }, currentSeriesMin);
  56559. };
  56560. /**
  56561. * Set the navigator x axis extremes to reflect the total. The navigator
  56562. * extremes should always be the extremes of the union of all series in the
  56563. * chart as well as the navigator series.
  56564. *
  56565. * @private
  56566. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  56567. */
  56568. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  56569. var xAxis = this.xAxis,
  56570. unionExtremes;
  56571. if (typeof xAxis.getExtremes !== 'undefined') {
  56572. unionExtremes = this.getUnionExtremes(true);
  56573. if (unionExtremes &&
  56574. (unionExtremes.dataMin !== xAxis.min ||
  56575. unionExtremes.dataMax !== xAxis.max)) {
  56576. xAxis.min = unionExtremes.dataMin;
  56577. xAxis.max = unionExtremes.dataMax;
  56578. }
  56579. }
  56580. };
  56581. /**
  56582. * Hook to modify the base axis extremes with information from the Navigator
  56583. *
  56584. * @private
  56585. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  56586. */
  56587. Navigator.prototype.modifyBaseAxisExtremes = function () {
  56588. var baseXAxis = this,
  56589. navigator = baseXAxis.chart.navigator,
  56590. baseExtremes = baseXAxis.getExtremes(),
  56591. baseMin = baseExtremes.min,
  56592. baseMax = baseExtremes.max,
  56593. baseDataMin = baseExtremes.dataMin,
  56594. baseDataMax = baseExtremes.dataMax,
  56595. range = baseMax - baseMin,
  56596. stickToMin = navigator.stickToMin,
  56597. stickToMax = navigator.stickToMax,
  56598. overscroll = pick(baseXAxis.options.overscroll, 0),
  56599. newMax,
  56600. newMin,
  56601. navigatorSeries = navigator.series && navigator.series[0],
  56602. hasSetExtremes = !!baseXAxis.setExtremes,
  56603. // When the extremes have been set by range selector button, don't
  56604. // stick to min or max. The range selector buttons will handle the
  56605. // extremes. (#5489)
  56606. unmutable = baseXAxis.eventArgs &&
  56607. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  56608. if (!unmutable) {
  56609. // If the zoomed range is already at the min, move it to the right
  56610. // as new data comes in
  56611. if (stickToMin) {
  56612. newMin = baseDataMin;
  56613. newMax = newMin + range;
  56614. }
  56615. // If the zoomed range is already at the max, move it to the right
  56616. // as new data comes in
  56617. if (stickToMax) {
  56618. newMax = baseDataMax + overscroll;
  56619. // If stickToMin is true, the new min value is set above
  56620. if (!stickToMin) {
  56621. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  56622. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  56623. navigatorSeries.xData[0] :
  56624. -Number.MAX_VALUE));
  56625. }
  56626. }
  56627. // Update the extremes
  56628. if (hasSetExtremes && (stickToMin || stickToMax)) {
  56629. if (isNumber(newMin)) {
  56630. baseXAxis.min = baseXAxis.userMin = newMin;
  56631. baseXAxis.max = baseXAxis.userMax = newMax;
  56632. }
  56633. }
  56634. }
  56635. // Reset
  56636. navigator.stickToMin =
  56637. navigator.stickToMax = null;
  56638. };
  56639. /**
  56640. * Handler for updated data on the base series. When data is modified, the
  56641. * navigator series must reflect it. This is called from the Chart.redraw
  56642. * function before axis and series extremes are computed.
  56643. *
  56644. * @private
  56645. * @function Highcharts.Navigator#updateDataHandler
  56646. */
  56647. Navigator.prototype.updatedDataHandler = function () {
  56648. var navigator = this.chart.navigator,
  56649. baseSeries = this,
  56650. navigatorSeries = this.navigatorSeries,
  56651. xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  56652. // If the scrollbar is scrolled all the way to the right, keep right as
  56653. // new data comes in.
  56654. navigator.stickToMax = navigator.reversedExtremes ?
  56655. Math.round(navigator.zoomedMin) === 0 :
  56656. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  56657. // Detect whether the zoomed area should stick to the minimum or
  56658. // maximum. If the current axis minimum falls outside the new updated
  56659. // dataset, we must adjust.
  56660. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  56661. (baseSeries.xAxis.min <= xDataMin) &&
  56662. (!this.chart.fixedRange || !navigator.stickToMax);
  56663. // Set the navigator series data to the new data of the base series
  56664. if (navigatorSeries && !navigator.hasNavigatorData) {
  56665. navigatorSeries.options.pointStart = baseSeries.xData[0];
  56666. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  56667. }
  56668. };
  56669. /**
  56670. * Add chart events, like redrawing navigator, when chart requires that.
  56671. *
  56672. * @private
  56673. * @function Highcharts.Navigator#addChartEvents
  56674. * @return {void}
  56675. */
  56676. Navigator.prototype.addChartEvents = function () {
  56677. if (!this.eventsToUnbind) {
  56678. this.eventsToUnbind = [];
  56679. }
  56680. this.eventsToUnbind.push(
  56681. // Move the scrollbar after redraw, like after data updata even if
  56682. // axes don't redraw
  56683. addEvent(this.chart, 'redraw', function () {
  56684. var navigator = this.navigator,
  56685. xAxis = navigator && (navigator.baseSeries &&
  56686. navigator.baseSeries[0] &&
  56687. navigator.baseSeries[0].xAxis ||
  56688. this.xAxis[0]); // #5709, #13114
  56689. if (xAxis) {
  56690. navigator.render(xAxis.min,
  56691. xAxis.max);
  56692. }
  56693. }),
  56694. // Make room for the navigator, can be placed around the chart:
  56695. addEvent(this.chart, 'getMargins', function () {
  56696. var chart = this,
  56697. navigator = chart.navigator,
  56698. marginName = navigator.opposite ?
  56699. 'plotTop' : 'marginBottom';
  56700. if (chart.inverted) {
  56701. marginName = navigator.opposite ?
  56702. 'marginRight' : 'plotLeft';
  56703. }
  56704. chart[marginName] =
  56705. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  56706. navigator.outlineHeight :
  56707. 0) + navigator.navigatorOptions.margin;
  56708. }));
  56709. };
  56710. /**
  56711. * Destroys allocated elements.
  56712. *
  56713. * @private
  56714. * @function Highcharts.Navigator#destroy
  56715. */
  56716. Navigator.prototype.destroy = function () {
  56717. // Disconnect events added in addEvents
  56718. this.removeEvents();
  56719. if (this.xAxis) {
  56720. erase(this.chart.xAxis, this.xAxis);
  56721. erase(this.chart.axes, this.xAxis);
  56722. }
  56723. if (this.yAxis) {
  56724. erase(this.chart.yAxis, this.yAxis);
  56725. erase(this.chart.axes, this.yAxis);
  56726. }
  56727. // Destroy series
  56728. (this.series || []).forEach(function (s) {
  56729. if (s.destroy) {
  56730. s.destroy();
  56731. }
  56732. });
  56733. // Destroy properties
  56734. [
  56735. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  56736. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  56737. 'rendered'
  56738. ].forEach(function (prop) {
  56739. if (this[prop] && this[prop].destroy) {
  56740. this[prop].destroy();
  56741. }
  56742. this[prop] = null;
  56743. }, this);
  56744. // Destroy elements in collection
  56745. [this.handles].forEach(function (coll) {
  56746. destroyObjectProperties(coll);
  56747. }, this);
  56748. };
  56749. return Navigator;
  56750. }());
  56751. // End of prototype
  56752. if (!H.Navigator) {
  56753. H.Navigator = Navigator;
  56754. NavigatorAxis.compose(Axis);
  56755. // For Stock charts. For x only zooming, do not to create the zoom button
  56756. // because X axis zooming is already allowed by the Navigator and Range
  56757. // selector. (#9285)
  56758. addEvent(Chart, 'beforeShowResetZoom', function () {
  56759. var chartOptions = this.options,
  56760. navigator = chartOptions.navigator,
  56761. rangeSelector = chartOptions.rangeSelector;
  56762. if (((navigator && navigator.enabled) ||
  56763. (rangeSelector && rangeSelector.enabled)) &&
  56764. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  56765. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  56766. return false;
  56767. }
  56768. });
  56769. // Initialize navigator for stock charts
  56770. addEvent(Chart, 'beforeRender', function () {
  56771. var options = this.options;
  56772. if (options.navigator.enabled ||
  56773. options.scrollbar.enabled) {
  56774. this.scroller = this.navigator = new Navigator(this);
  56775. }
  56776. });
  56777. // For stock charts, extend the Chart.setChartSize method so that we can set
  56778. // the final top position of the navigator once the height of the chart,
  56779. // including the legend, is determined. #367. We can't use Chart.getMargins,
  56780. // because labels offsets are not calculated yet.
  56781. addEvent(Chart, 'afterSetChartSize', function () {
  56782. var legend = this.legend,
  56783. navigator = this.navigator,
  56784. scrollbarHeight,
  56785. legendOptions,
  56786. xAxis,
  56787. yAxis;
  56788. if (navigator) {
  56789. legendOptions = legend && legend.options;
  56790. xAxis = navigator.xAxis;
  56791. yAxis = navigator.yAxis;
  56792. scrollbarHeight = navigator.scrollbarHeight;
  56793. // Compute the top position
  56794. if (this.inverted) {
  56795. navigator.left = navigator.opposite ?
  56796. this.chartWidth - scrollbarHeight -
  56797. navigator.height :
  56798. this.spacing[3] + scrollbarHeight;
  56799. navigator.top = this.plotTop + scrollbarHeight;
  56800. }
  56801. else {
  56802. navigator.left = this.plotLeft + scrollbarHeight;
  56803. navigator.top = navigator.navigatorOptions.top ||
  56804. this.chartHeight -
  56805. navigator.height -
  56806. scrollbarHeight -
  56807. this.spacing[2] -
  56808. (this.rangeSelector && this.extraBottomMargin ?
  56809. this.rangeSelector.getHeight() :
  56810. 0) -
  56811. ((legendOptions &&
  56812. legendOptions.verticalAlign === 'bottom' &&
  56813. legendOptions.layout !== 'proximate' && // #13392
  56814. legendOptions.enabled &&
  56815. !legendOptions.floating) ?
  56816. legend.legendHeight +
  56817. pick(legendOptions.margin, 10) :
  56818. 0) -
  56819. (this.titleOffset ? this.titleOffset[2] : 0);
  56820. }
  56821. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  56822. if (this.inverted) {
  56823. xAxis.options.left = yAxis.options.left = navigator.left;
  56824. }
  56825. else {
  56826. xAxis.options.top = yAxis.options.top = navigator.top;
  56827. }
  56828. xAxis.setAxisSize();
  56829. yAxis.setAxisSize();
  56830. }
  56831. }
  56832. });
  56833. // Merge options, if no scrolling exists yet
  56834. addEvent(Chart, 'update', function (e) {
  56835. var navigatorOptions = (e.options.navigator || {}),
  56836. scrollbarOptions = (e.options.scrollbar || {});
  56837. if (!this.navigator && !this.scroller &&
  56838. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  56839. merge(true, this.options.navigator, navigatorOptions);
  56840. merge(true, this.options.scrollbar, scrollbarOptions);
  56841. delete e.options.navigator;
  56842. delete e.options.scrollbar;
  56843. }
  56844. });
  56845. // Initialize navigator, if no scrolling exists yet
  56846. addEvent(Chart, 'afterUpdate', function (event) {
  56847. if (!this.navigator && !this.scroller &&
  56848. (this.options.navigator.enabled ||
  56849. this.options.scrollbar.enabled)) {
  56850. this.scroller = this.navigator = new Navigator(this);
  56851. if (pick(event.redraw, true)) {
  56852. this.redraw(event.animation); // #7067
  56853. }
  56854. }
  56855. });
  56856. // Handle adding new series
  56857. addEvent(Chart, 'afterAddSeries', function () {
  56858. if (this.navigator) {
  56859. // Recompute which series should be shown in navigator, and add them
  56860. this.navigator.setBaseSeries(null, false);
  56861. }
  56862. });
  56863. // Handle updating series
  56864. addEvent(Series, 'afterUpdate', function () {
  56865. if (this.chart.navigator && !this.options.isInternal) {
  56866. this.chart.navigator.setBaseSeries(null, false);
  56867. }
  56868. });
  56869. Chart.prototype.callbacks.push(function (chart) {
  56870. var extremes,
  56871. navigator = chart.navigator;
  56872. // Initialize the navigator
  56873. if (navigator && chart.xAxis[0]) {
  56874. extremes = chart.xAxis[0].getExtremes();
  56875. navigator.render(extremes.min, extremes.max);
  56876. }
  56877. });
  56878. }
  56879. H.Navigator = Navigator;
  56880. return H.Navigator;
  56881. });
  56882. _registerModule(_modules, 'masters/modules/gantt.src.js', [], function () {
  56883. });
  56884. _registerModule(_modules, 'masters/highcharts-gantt.src.js', [_modules['masters/highcharts.src.js']], function (Highcharts) {
  56885. Highcharts.product = 'Highcharts Gantt';
  56886. return Highcharts;
  56887. });
  56888. _modules['masters/highcharts-gantt.src.js']._modules = _modules;
  56889. return _modules['masters/highcharts-gantt.src.js'];
  56890. }));