highcharts.src.js 1.5 MB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733
  1. /**
  2. * @license Highcharts JS v7.0.2 (2019-01-17)
  3. *
  4. * (c) 2009-2018 Torstein Honsi
  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(function () {
  17. return factory(root);
  18. });
  19. } else {
  20. root.Highcharts = factory(root);
  21. }
  22. }(typeof window !== 'undefined' ? window : this, function (win) {
  23. var Highcharts = (function () {
  24. /**
  25. * (c) 2010-2019 Torstein Honsi
  26. *
  27. * License: www.highcharts.com/license
  28. */
  29. /**
  30. * Reference to the global SVGElement class as a workaround for a name conflict
  31. * in the Highcharts namespace.
  32. *
  33. * @global
  34. * @typedef {global.SVGElement} GlobalSVGElement
  35. *
  36. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  37. */
  38. /* global win, window */
  39. // glob is a temporary fix to allow our es-modules to work.
  40. var glob = typeof win === 'undefined' ? window : win,
  41. doc = glob.document,
  42. SVG_NS = 'http://www.w3.org/2000/svg',
  43. userAgent = (glob.navigator && glob.navigator.userAgent) || '',
  44. svg = (
  45. doc &&
  46. doc.createElementNS &&
  47. !!doc.createElementNS(SVG_NS, 'svg').createSVGRect
  48. ),
  49. isMS = /(edge|msie|trident)/i.test(userAgent) && !glob.opera,
  50. isFirefox = userAgent.indexOf('Firefox') !== -1,
  51. isChrome = userAgent.indexOf('Chrome') !== -1,
  52. hasBidiBug = (
  53. isFirefox &&
  54. parseInt(userAgent.split('Firefox/')[1], 10) < 4 // issue #38
  55. );
  56. var Highcharts = glob.Highcharts ? glob.Highcharts.error(16, true) : {
  57. product: 'Highcharts',
  58. version: '7.0.2',
  59. deg2rad: Math.PI * 2 / 360,
  60. doc: doc,
  61. hasBidiBug: hasBidiBug,
  62. hasTouch: doc && doc.documentElement.ontouchstart !== undefined,
  63. isMS: isMS,
  64. isWebKit: userAgent.indexOf('AppleWebKit') !== -1,
  65. isFirefox: isFirefox,
  66. isChrome: isChrome,
  67. isSafari: !isChrome && userAgent.indexOf('Safari') !== -1,
  68. isTouchDevice: /(Mobile|Android|Windows Phone)/.test(userAgent),
  69. SVG_NS: SVG_NS,
  70. chartCount: 0,
  71. seriesTypes: {},
  72. symbolSizes: {},
  73. svg: svg,
  74. win: glob,
  75. marginNames: ['plotTop', 'marginRight', 'marginBottom', 'plotLeft'],
  76. noop: function () {
  77. return undefined;
  78. },
  79. /**
  80. * An array containing the current chart objects in the page. A chart's
  81. * position in the array is preserved throughout the page's lifetime. When
  82. * a chart is destroyed, the array item becomes `undefined`.
  83. *
  84. * @name Highcharts.charts
  85. * @type {Array<Highcharts.Chart>}
  86. */
  87. charts: []
  88. };
  89. return Highcharts;
  90. }());
  91. (function (H) {
  92. /* *
  93. *
  94. * (c) 2010-2019 Torstein Honsi
  95. *
  96. * License: www.highcharts.com/license
  97. *
  98. * */
  99. /**
  100. * Reference to the global SVGElement class as a workaround for a name conflict
  101. * in the Highcharts namespace.
  102. *
  103. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  104. *
  105. * @global
  106. * @typedef {global.SVGElement} GlobalSVGElement
  107. */
  108. /**
  109. * An animation configuration. Animation configurations can also be defined as
  110. * booleans, where `false` turns off animation and `true` defaults to a duration
  111. * of 500ms.
  112. *
  113. * @interface Highcharts.AnimationOptionsObject
  114. *//**
  115. * A callback function to exectute when the animation finishes.
  116. * @name Highcharts.AnimationOptionsObject#complete
  117. * @type {Function|undefined}
  118. *//**
  119. * The animation duration in milliseconds.
  120. * @name Highcharts.AnimationOptionsObject#duration
  121. * @type {number}
  122. *//**
  123. * The name of an easing function as defined on the `Math` object.
  124. * @name Highcharts.AnimationOptionsObject#easing
  125. * @type {string|undefined}
  126. *//**
  127. * A callback function to execute on each step of each attribute or CSS property
  128. * that's being animated. The first argument contains information about the
  129. * animation and progress.
  130. * @name Highcharts.AnimationOptionsObject#step
  131. * @type {Function|undefined}
  132. */
  133. /**
  134. * A style object with camel case property names to define visual appearance of
  135. * a SVG element or HTML element. The properties can be whatever styles are
  136. * supported on the given SVG or HTML element.
  137. *
  138. * @example
  139. * {
  140. * fontFamily: 'monospace',
  141. * fontSize: '1.2em'
  142. * }
  143. *
  144. * @interface Highcharts.CSSObject
  145. *//**
  146. * @name Highcharts.CSSObject#[key:string]
  147. * @type {boolean|number|string|undefined}
  148. *//**
  149. * Background style for the element.
  150. * @name Highcharts.CSSObject#background
  151. * @type {string|undefined}
  152. *//**
  153. * Background color of the element.
  154. * @name Highcharts.CSSObject#backgroundColor
  155. * @type {Highcharts.ColorString|undefined}
  156. *//**
  157. * Border style for the element.
  158. * @name Highcharts.CSSObject#border
  159. * @type {string|undefined}
  160. *//**
  161. * Radius of the element border.
  162. * @name Highcharts.CSSObject#borderRadius
  163. * @type {number|undefined}
  164. *//**
  165. * Color used in the element. The "contrast" option is a Highcharts custom
  166. * property that results in black or white, depending on the background of the
  167. * element.
  168. * @name Highcharts.CSSObject#color
  169. * @type {"contrast"|Highcharts.ColorString|undefined}
  170. *//**
  171. * Style of the mouse cursor when resting over the element.
  172. * @name Highcharts.CSSObject#cursor
  173. * @type {Highcharts.CursorType|undefined}
  174. *//**
  175. * Font family of the element text. Multiple values have to be in decreasing
  176. * preference order and separated by comma.
  177. * @name Highcharts.CSSObject#fontFamily
  178. * @type {string|undefined}
  179. *//**
  180. * Font size of the element text.
  181. * @name Highcharts.CSSObject#fontSize
  182. * @type {string|undefined}
  183. *//**
  184. * Font weight of the element text.
  185. * @name Highcharts.CSSObject#fontWeight
  186. * @type {string|undefined}
  187. *//**
  188. * Height of the element.
  189. * @name Highcharts.CSSObject#height
  190. * @type {number|undefined}
  191. *//**
  192. * Width of the element border.
  193. * @name Highcharts.CSSObject#lineWidth
  194. * @type {number|undefined}
  195. *//**
  196. * Opacity of the element.
  197. * @name Highcharts.CSSObject#opacity
  198. * @type {number|undefined}
  199. *//**
  200. * Space around the element content.
  201. * @name Highcharts.CSSObject#padding
  202. * @type {string|undefined}
  203. *//**
  204. * Behaviour of the element when the mouse cursor rests over it.
  205. * @name Highcharts.CSSObject#pointerEvents
  206. * @type {string|undefined}
  207. *//**
  208. * Positioning of the element.
  209. * @name Highcharts.CSSObject#position
  210. * @type {string|undefined}
  211. *//**
  212. * Alignment of the element text.
  213. * @name Highcharts.CSSObject#textAlign
  214. * @type {string|undefined}
  215. *//**
  216. * Outline style of the element text.
  217. * @name Highcharts.CSSObject#textOutline
  218. * @type {string|undefined}
  219. *//**
  220. * Additional decoration of the element text.
  221. * @name Highcharts.CSSObject#textDecoration
  222. * @type {string|undefined}
  223. *//**
  224. * Line break style of the element text. Highcharts SVG elements support
  225. * `ellipsis` when a `width` is set.
  226. * @name Highcharts.CSSObject#textOverflow
  227. * @type {string|undefined}
  228. *//**
  229. * Top spacing of the element relative to the parent element.
  230. * @name Highcharts.CSSObject#top
  231. * @type {string|undefined}
  232. *//**
  233. * Animated transition of selected element properties.
  234. * @name Highcharts.CSSObject#transition
  235. * @type {string|undefined}
  236. *//**
  237. * Line break style of the element text.
  238. * @name Highcharts.CSSObject#whiteSpace
  239. * @type {string|undefined}
  240. *//**
  241. * Width of the element.
  242. * @name Highcharts.CSSObject#width
  243. * @type {number|undefined}
  244. */
  245. /**
  246. * All possible cursor styles.
  247. *
  248. * @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.CursorType
  249. */
  250. /**
  251. * All possible dash styles.
  252. *
  253. * @typedef {"Dash"|"DashDot"|"Dot"|"LongDash"|"LongDashDot"|"LongDashDotDot"|"ShortDash"|"ShortDashDot"|"ShortDashDotDot"|"ShortDot"|"Solid"} Highcharts.DashStyleType
  254. */
  255. /**
  256. * Generic dictionary in TypeScript notation.
  257. *
  258. * @interface Highcharts.Dictionary<T>
  259. *//**
  260. * @name Highcharts.Dictionary<T>#[key:string]
  261. * @type {T}
  262. */
  263. /**
  264. * The function callback to execute when the event is fired. The `this` context
  265. * contains the instance, that fired the event.
  266. *
  267. * @callback Highcharts.EventCallbackFunction<T>
  268. *
  269. * @param {T} this
  270. *
  271. * @param {Highcharts.Dictionary<*>} [eventArguments]
  272. * Event arguments.
  273. */
  274. /**
  275. * The event options for adding function callback.
  276. *
  277. * @interface Highcharts.EventOptionsObject
  278. *//**
  279. * The order the event handler should be called. This opens for having one
  280. * handler be called before another, independent of in which order they were
  281. * added.
  282. * @name Highcharts.EventOptionsObject#order
  283. * @type {number}
  284. */
  285. /**
  286. * Formats data as a string. Usually the data is accessible throught the `this`
  287. * keyword.
  288. *
  289. * @callback Highcharts.FormatterCallbackFunction<T>
  290. *
  291. * @param {T} this
  292. *
  293. * @return {string}
  294. */
  295. /**
  296. * An object of key-value pairs for HTML attributes.
  297. *
  298. * @typedef {Highcharts.Dictionary<boolean|number|string>} Highcharts.HTMLAttributes
  299. */
  300. /**
  301. * An HTML DOM element. The type is a reference to the regular HTMLElement in
  302. * the global scope.
  303. *
  304. * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
  305. *
  306. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
  307. */
  308. /**
  309. * The iterator callback.
  310. *
  311. * @callback Highcharts.ObjectEachCallbackFunction
  312. *
  313. * @param {*} value
  314. * The property value.
  315. *
  316. * @param {string} key
  317. * The property key.
  318. *
  319. * @param {*} obj
  320. * The object that objectEach is being applied to.
  321. */
  322. /**
  323. * An object containing `left` and `top` properties for the position in the
  324. * page.
  325. *
  326. * @interface Highcharts.OffsetObject
  327. *//**
  328. * Left distance to the page border.
  329. * @name Highcharts.OffsetObject#left
  330. * @type {number}
  331. *//**
  332. * Top distance to the page border.
  333. * @name Highcharts.OffsetObject#top
  334. * @type {number}
  335. */
  336. /**
  337. * If a number is given, it defines the pixel length. If a percentage string is
  338. * given, like for example `'50%'`, the setting defines a length relative to a
  339. * base size, for example the size of a container.
  340. *
  341. * @typedef {number|string} Highcharts.RelativeSize
  342. */
  343. /**
  344. * An object of key-value pairs for SVG attributes. Attributes in Highcharts
  345. * elements for the most parts correspond to SVG, but some are specific to
  346. * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
  347. * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
  348. * attributes containing a hyphen are _not_ camel-cased, they should be
  349. * quoted to preserve the hyphen.
  350. *
  351. * @example
  352. * {
  353. * 'stroke': '#ff0000', // basic
  354. * 'stroke-width': 2, // hyphenated
  355. * 'rotation': 45 // custom
  356. * 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
  357. * }
  358. *
  359. * @interface Highcharts.SVGAttributes
  360. *//**
  361. * @name Highcharts.SVGAttributes#[key:string]
  362. * @type {boolean|number|string|Array<number|string>|undefined}
  363. *//**
  364. * @name Highcharts.SVGAttributes#d
  365. * @type {string|Highcharts.SVGPathArray|undefined}
  366. *//**
  367. * @name Highcharts.SVGAttributes#inverted
  368. * @type {boolean|undefined}
  369. *//**
  370. * @name Highcharts.SVGAttributes#matrix
  371. * @type {Array<number>|undefined}
  372. *//**
  373. * @name Highcharts.SVGAttributes#stroke
  374. * @type {Highcharts.ColorString|undefined}
  375. *//**
  376. * @name Highcharts.SVGAttributes#rotation
  377. * @type {string|undefined}
  378. *//**
  379. * @name Highcharts.SVGAttributes#rotationOriginX
  380. * @type {number|undefined}
  381. *//**
  382. * @name Highcharts.SVGAttributes#rotationOriginY
  383. * @type {number|undefined}
  384. *//**
  385. * @name Highcharts.SVGAttributes#scaleX
  386. * @type {number|undefined}
  387. *//**
  388. * @name Highcharts.SVGAttributes#scaleY
  389. * @type {number|undefined}
  390. *//**
  391. * @name Highcharts.SVGAttributes#translateX
  392. * @type {number|undefined}
  393. *//**
  394. * @name Highcharts.SVGAttributes#translateY
  395. * @type {number|undefined}
  396. *//**
  397. * @name Highcharts.SVGAttributes#zIndex
  398. * @type {number|undefined}
  399. */
  400. /**
  401. * An SVG DOM element. The type is a reference to the regular SVGElement in the
  402. * global scope.
  403. *
  404. * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
  405. *
  406. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  407. */
  408. /**
  409. * Array of path commands, that will go into the `d` attribute of an SVG
  410. * element.
  411. *
  412. * @typedef {Array<number|Highcharts.SVGPathCommand>} Highcharts.SVGPathArray
  413. */
  414. /**
  415. * Possible path commands in a SVG path array.
  416. *
  417. * @typedef {string} Highcharts.SVGPathCommand
  418. * @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
  419. */
  420. /**
  421. * The Highcharts object is the placeholder for all other members, and various
  422. * utility functions. The most important member of the namespace would be the
  423. * chart constructor.
  424. *
  425. * @example
  426. * var chart = Highcharts.chart('container', { ... });
  427. *
  428. * @namespace Highcharts
  429. */
  430. H.timers = [];
  431. var charts = H.charts,
  432. doc = H.doc,
  433. win = H.win;
  434. /**
  435. * Provide error messages for debugging, with links to online explanation. This
  436. * function can be overridden to provide custom error handling.
  437. *
  438. * @sample highcharts/chart/highcharts-error/
  439. * Custom error handler
  440. *
  441. * @function Highcharts.error
  442. *
  443. * @param {number|string} code
  444. * The error code. See
  445. * [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
  446. * for available codes. If it is a string, the error message is printed
  447. * directly in the console.
  448. *
  449. * @param {boolean} [stop=false]
  450. * Whether to throw an error or just log a warning in the console.
  451. *
  452. * @param {Highcharts.Chart} [chart]
  453. * Reference to the chart that causes the error. Used in 'debugger'
  454. * module to display errors directly on the chart.
  455. * Important note: This argument is undefined for errors that lack
  456. * access to the Chart instance.
  457. */
  458. H.error = function (code, stop, chart) {
  459. var msg = H.isNumber(code) ?
  460. 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code :
  461. code;
  462. if (chart) {
  463. H.fireEvent(chart, 'displayError', { code: code });
  464. }
  465. if (stop) {
  466. throw new Error(msg);
  467. }
  468. // else ...
  469. if (win.console) {
  470. console.log(msg); // eslint-disable-line no-console
  471. }
  472. };
  473. /**
  474. * An animator object used internally. One instance applies to one property
  475. * (attribute or style prop) on one element. Animation is always initiated
  476. * through {@link SVGElement#animate}.
  477. *
  478. * @example
  479. * var rect = renderer.rect(0, 0, 10, 10).add();
  480. * rect.animate({ width: 100 });
  481. *
  482. * @private
  483. * @class Highcharts.Fx
  484. *
  485. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
  486. * The element to animate.
  487. *
  488. * @param {Highcharts.AnimationOptionsObject} options
  489. * Animation options.
  490. *
  491. * @param {string} prop
  492. * The single attribute or CSS property to animate.
  493. */
  494. H.Fx = function (elem, options, prop) {
  495. this.options = options;
  496. this.elem = elem;
  497. this.prop = prop;
  498. };
  499. H.Fx.prototype = {
  500. /**
  501. * Set the current step of a path definition on SVGElement.
  502. *
  503. * @function Highcharts.Fx#dSetter
  504. */
  505. dSetter: function () {
  506. var start = this.paths[0],
  507. end = this.paths[1],
  508. ret = [],
  509. now = this.now,
  510. i = start.length,
  511. startVal;
  512. // Land on the final path without adjustment points appended in the ends
  513. if (now === 1) {
  514. ret = this.toD;
  515. } else if (i === end.length && now < 1) {
  516. while (i--) {
  517. startVal = parseFloat(start[i]);
  518. ret[i] =
  519. isNaN(startVal) ? // a letter instruction like M or L
  520. end[i] :
  521. now * (parseFloat(end[i] - startVal)) + startVal;
  522. }
  523. // If animation is finished or length not matching, land on right value
  524. } else {
  525. ret = end;
  526. }
  527. this.elem.attr('d', ret, null, true);
  528. },
  529. /**
  530. * Update the element with the current animation step.
  531. *
  532. * @function Highcharts.Fx#update
  533. */
  534. update: function () {
  535. var elem = this.elem,
  536. prop = this.prop, // if destroyed, it is null
  537. now = this.now,
  538. step = this.options.step;
  539. // Animation setter defined from outside
  540. if (this[prop + 'Setter']) {
  541. this[prop + 'Setter']();
  542. // Other animations on SVGElement
  543. } else if (elem.attr) {
  544. if (elem.element) {
  545. elem.attr(prop, now, null, true);
  546. }
  547. // HTML styles, raw HTML content like container size
  548. } else {
  549. elem.style[prop] = now + this.unit;
  550. }
  551. if (step) {
  552. step.call(elem, now, this);
  553. }
  554. },
  555. /**
  556. * Run an animation.
  557. *
  558. * @function Highcharts.Fx#run
  559. *
  560. * @param {number} from
  561. * The current value, value to start from.
  562. *
  563. * @param {number} to
  564. * The end value, value to land on.
  565. *
  566. * @param {string} [unit]
  567. * The property unit, for example `px`.
  568. */
  569. run: function (from, to, unit) {
  570. var self = this,
  571. options = self.options,
  572. timer = function (gotoEnd) {
  573. return timer.stopped ? false : self.step(gotoEnd);
  574. },
  575. requestAnimationFrame =
  576. win.requestAnimationFrame ||
  577. function (step) {
  578. setTimeout(step, 13);
  579. },
  580. step = function () {
  581. for (var i = 0; i < H.timers.length; i++) {
  582. if (!H.timers[i]()) {
  583. H.timers.splice(i--, 1);
  584. }
  585. }
  586. if (H.timers.length) {
  587. requestAnimationFrame(step);
  588. }
  589. };
  590. if (from === to && !this.elem['forceAnimate:' + this.prop]) {
  591. delete options.curAnim[this.prop];
  592. if (options.complete && Object.keys(options.curAnim).length === 0) {
  593. options.complete.call(this.elem);
  594. }
  595. } else { // #7166
  596. this.startTime = +new Date();
  597. this.start = from;
  598. this.end = to;
  599. this.unit = unit;
  600. this.now = this.start;
  601. this.pos = 0;
  602. timer.elem = this.elem;
  603. timer.prop = this.prop;
  604. if (timer() && H.timers.push(timer) === 1) {
  605. requestAnimationFrame(step);
  606. }
  607. }
  608. },
  609. /**
  610. * Run a single step in the animation.
  611. *
  612. * @function Highcharts.Fx#step
  613. *
  614. * @param {boolean} [gotoEnd]
  615. * Whether to go to the endpoint of the animation after abort.
  616. *
  617. * @return {boolean}
  618. * Returns `true` if animation continues.
  619. */
  620. step: function (gotoEnd) {
  621. var t = +new Date(),
  622. ret,
  623. done,
  624. options = this.options,
  625. elem = this.elem,
  626. complete = options.complete,
  627. duration = options.duration,
  628. curAnim = options.curAnim;
  629. if (elem.attr && !elem.element) { // #2616, element is destroyed
  630. ret = false;
  631. } else if (gotoEnd || t >= duration + this.startTime) {
  632. this.now = this.end;
  633. this.pos = 1;
  634. this.update();
  635. curAnim[this.prop] = true;
  636. done = true;
  637. H.objectEach(curAnim, function (val) {
  638. if (val !== true) {
  639. done = false;
  640. }
  641. });
  642. if (done && complete) {
  643. complete.call(elem);
  644. }
  645. ret = false;
  646. } else {
  647. this.pos = options.easing((t - this.startTime) / duration);
  648. this.now = this.start + ((this.end - this.start) * this.pos);
  649. this.update();
  650. ret = true;
  651. }
  652. return ret;
  653. },
  654. /**
  655. * Prepare start and end values so that the path can be animated one to one.
  656. *
  657. * @function Highcharts.Fx#initPath
  658. *
  659. * @param {Highcharts.SVGElement} elem
  660. * The SVGElement item.
  661. *
  662. * @param {string} fromD
  663. * Starting path definition.
  664. *
  665. * @param {Highcharts.SVGPathArray} toD
  666. * Ending path definition.
  667. *
  668. * @return {Array<Highcharts.SVGPathArray>}
  669. * An array containing start and end paths in array form so that
  670. * they can be animated in parallel.
  671. */
  672. initPath: function (elem, fromD, toD) {
  673. fromD = fromD || '';
  674. var shift,
  675. startX = elem.startX,
  676. endX = elem.endX,
  677. bezier = fromD.indexOf('C') > -1,
  678. numParams = bezier ? 7 : 3,
  679. fullLength,
  680. slice,
  681. i,
  682. start = fromD.split(' '),
  683. end = toD.slice(), // copy
  684. isArea = elem.isArea,
  685. positionFactor = isArea ? 2 : 1,
  686. reverse;
  687. /**
  688. * In splines make moveTo and lineTo points have six parameters like
  689. * bezier curves, to allow animation one-to-one.
  690. */
  691. function sixify(arr) {
  692. var isOperator,
  693. nextIsOperator;
  694. i = arr.length;
  695. while (i--) {
  696. // Fill in dummy coordinates only if the next operator comes
  697. // three places behind (#5788)
  698. isOperator = arr[i] === 'M' || arr[i] === 'L';
  699. nextIsOperator = /[a-zA-Z]/.test(arr[i + 3]);
  700. if (isOperator && nextIsOperator) {
  701. arr.splice(
  702. i + 1, 0,
  703. arr[i + 1], arr[i + 2],
  704. arr[i + 1], arr[i + 2]
  705. );
  706. }
  707. }
  708. }
  709. /**
  710. * Insert an array at the given position of another array
  711. */
  712. function insertSlice(arr, subArr, index) {
  713. [].splice.apply(
  714. arr,
  715. [index, 0].concat(subArr)
  716. );
  717. }
  718. /**
  719. * If shifting points, prepend a dummy point to the end path.
  720. */
  721. function prepend(arr, other) {
  722. while (arr.length < fullLength) {
  723. // Move to, line to or curve to?
  724. arr[0] = other[fullLength - arr.length];
  725. // Prepend a copy of the first point
  726. insertSlice(arr, arr.slice(0, numParams), 0);
  727. // For areas, the bottom path goes back again to the left, so we
  728. // need to append a copy of the last point.
  729. if (isArea) {
  730. insertSlice(
  731. arr,
  732. arr.slice(arr.length - numParams), arr.length
  733. );
  734. i--;
  735. }
  736. }
  737. arr[0] = 'M';
  738. }
  739. /**
  740. * Copy and append last point until the length matches the end length.
  741. */
  742. function append(arr, other) {
  743. var i = (fullLength - arr.length) / numParams;
  744. while (i > 0 && i--) {
  745. // Pull out the slice that is going to be appended or inserted.
  746. // In a line graph, the positionFactor is 1, and the last point
  747. // is sliced out. In an area graph, the positionFactor is 2,
  748. // causing the middle two points to be sliced out, since an area
  749. // path starts at left, follows the upper path then turns and
  750. // follows the bottom back.
  751. slice = arr.slice().splice(
  752. (arr.length / positionFactor) - numParams,
  753. numParams * positionFactor
  754. );
  755. // Move to, line to or curve to?
  756. slice[0] = other[fullLength - numParams - (i * numParams)];
  757. // Disable first control point
  758. if (bezier) {
  759. slice[numParams - 6] = slice[numParams - 2];
  760. slice[numParams - 5] = slice[numParams - 1];
  761. }
  762. // Now insert the slice, either in the middle (for areas) or at
  763. // the end (for lines)
  764. insertSlice(arr, slice, arr.length / positionFactor);
  765. if (isArea) {
  766. i--;
  767. }
  768. }
  769. }
  770. if (bezier) {
  771. sixify(start);
  772. sixify(end);
  773. }
  774. // For sideways animation, find out how much we need to shift to get the
  775. // start path Xs to match the end path Xs.
  776. if (startX && endX) {
  777. for (i = 0; i < startX.length; i++) {
  778. // Moving left, new points coming in on right
  779. if (startX[i] === endX[0]) {
  780. shift = i;
  781. break;
  782. // Moving right
  783. } else if (startX[0] ===
  784. endX[endX.length - startX.length + i]) {
  785. shift = i;
  786. reverse = true;
  787. break;
  788. }
  789. }
  790. if (shift === undefined) {
  791. start = [];
  792. }
  793. }
  794. if (start.length && H.isNumber(shift)) {
  795. // The common target length for the start and end array, where both
  796. // arrays are padded in opposite ends
  797. fullLength = end.length + shift * positionFactor * numParams;
  798. if (!reverse) {
  799. prepend(end, start);
  800. append(start, end);
  801. } else {
  802. prepend(start, end);
  803. append(end, start);
  804. }
  805. }
  806. return [start, end];
  807. },
  808. /**
  809. * Handle animation of the color attributes directly.
  810. *
  811. * @function Highcharts.Fx#fillSetter
  812. */
  813. fillSetter: function () {
  814. H.Fx.prototype.strokeSetter.apply(this, arguments);
  815. },
  816. /**
  817. * Handle animation of the color attributes directly.
  818. *
  819. * @function Highcharts.Fx#strokeSetter
  820. */
  821. strokeSetter: function () {
  822. this.elem.attr(
  823. this.prop,
  824. H.color(this.start).tweenTo(H.color(this.end), this.pos),
  825. null,
  826. true
  827. );
  828. }
  829. }; // End of Fx prototype
  830. /**
  831. * Utility function to deep merge two or more objects and return a third object.
  832. * The merge function can also be used with a single object argument to create a
  833. * deep copy of an object.
  834. *
  835. * @function Highcharts.merge
  836. *
  837. * @param {*} a
  838. * The first object to extend. When only this is given, the function
  839. * returns a deep copy.
  840. *
  841. * @param {*} [n]
  842. * An object to merge into the previous one.
  843. *
  844. * @return {*}
  845. * The merged object. If the first argument is true, the return is the
  846. * same as the second argument.
  847. *//**
  848. * Utility function to deep merge two or more objects and return a third object.
  849. * If the first argument is true, the contents of the second object is copied
  850. * into the first object. The merge function can also be used with a single
  851. * object argument to create a deep copy of an object.
  852. *
  853. * @function Highcharts.merge
  854. *
  855. * @param {boolean} extend
  856. * Whether to extend the left-side object (a) or return a whole new
  857. * object.
  858. *
  859. * @param {*} a
  860. * The first object to extend. When only this is given, the function
  861. * returns a deep copy.
  862. *
  863. * @param {*} [n]
  864. * An object to merge into the previous one.
  865. *
  866. * @return {*}
  867. * The merged object. If the first argument is true, the return is the
  868. * same as the second argument.
  869. */
  870. H.merge = function () {
  871. var i,
  872. args = arguments,
  873. len,
  874. ret = {},
  875. doCopy = function (copy, original) {
  876. // An object is replacing a primitive
  877. if (typeof copy !== 'object') {
  878. copy = {};
  879. }
  880. H.objectEach(original, function (value, key) {
  881. // Copy the contents of objects, but not arrays or DOM nodes
  882. if (H.isObject(value, true) &&
  883. !H.isClass(value) &&
  884. !H.isDOMElement(value)
  885. ) {
  886. copy[key] = doCopy(copy[key] || {}, value);
  887. // Primitives and arrays are copied over directly
  888. } else {
  889. copy[key] = original[key];
  890. }
  891. });
  892. return copy;
  893. };
  894. // If first argument is true, copy into the existing object. Used in
  895. // setOptions.
  896. if (args[0] === true) {
  897. ret = args[1];
  898. args = Array.prototype.slice.call(args, 2);
  899. }
  900. // For each argument, extend the return
  901. len = args.length;
  902. for (i = 0; i < len; i++) {
  903. ret = doCopy(ret, args[i]);
  904. }
  905. return ret;
  906. };
  907. /**
  908. * Shortcut for parseInt
  909. *
  910. * @private
  911. * @function Highcharts.pInt
  912. *
  913. * @param {*} s
  914. *
  915. * @param {number} mag
  916. * Magnitude
  917. *
  918. * @return {number}
  919. */
  920. H.pInt = function (s, mag) {
  921. return parseInt(s, mag || 10);
  922. };
  923. /**
  924. * Utility function to check for string type.
  925. *
  926. * @function Highcharts.isString
  927. *
  928. * @param {*} s
  929. * The item to check.
  930. *
  931. * @return {boolean}
  932. * True if the argument is a string.
  933. */
  934. H.isString = function (s) {
  935. return typeof s === 'string';
  936. };
  937. /**
  938. * Utility function to check if an item is an array.
  939. *
  940. * @function Highcharts.isArray
  941. *
  942. * @param {*} obj
  943. * The item to check.
  944. *
  945. * @return {boolean}
  946. * True if the argument is an array.
  947. */
  948. H.isArray = function (obj) {
  949. var str = Object.prototype.toString.call(obj);
  950. return str === '[object Array]' || str === '[object Array Iterator]';
  951. };
  952. /**
  953. * Utility function to check if an item is of type object.
  954. *
  955. * @function Highcharts.isObject
  956. *
  957. * @param {*} obj
  958. * The item to check.
  959. *
  960. * @param {boolean} [strict=false]
  961. * Also checks that the object is not an array.
  962. *
  963. * @return {boolean}
  964. * True if the argument is an object.
  965. */
  966. H.isObject = function (obj, strict) {
  967. return !!obj && typeof obj === 'object' && (!strict || !H.isArray(obj));
  968. };
  969. /**
  970. * Utility function to check if an Object is a HTML Element.
  971. *
  972. * @function Highcharts.isDOMElement
  973. *
  974. * @param {*} obj
  975. * The item to check.
  976. *
  977. * @return {boolean}
  978. * True if the argument is a HTML Element.
  979. */
  980. H.isDOMElement = function (obj) {
  981. return H.isObject(obj) && typeof obj.nodeType === 'number';
  982. };
  983. /**
  984. * Utility function to check if an Object is an class.
  985. *
  986. * @function Highcharts.isClass
  987. *
  988. * @param {*} obj
  989. * The item to check.
  990. *
  991. * @return {boolean}
  992. * True if the argument is an class.
  993. */
  994. H.isClass = function (obj) {
  995. var c = obj && obj.constructor;
  996. return !!(
  997. H.isObject(obj, true) &&
  998. !H.isDOMElement(obj) &&
  999. (c && c.name && c.name !== 'Object')
  1000. );
  1001. };
  1002. /**
  1003. * Utility function to check if an item is a number and it is finite (not NaN,
  1004. * Infinity or -Infinity).
  1005. *
  1006. * @function Highcharts.isNumber
  1007. *
  1008. * @param {*} n
  1009. * The item to check.
  1010. *
  1011. * @return {boolean}
  1012. * True if the item is a finite number
  1013. */
  1014. H.isNumber = function (n) {
  1015. return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
  1016. };
  1017. /**
  1018. * Remove the last occurence of an item from an array.
  1019. *
  1020. * @function Highcharts.erase
  1021. *
  1022. * @param {Array} arr
  1023. * The array.
  1024. *
  1025. * @param {*} item
  1026. * The item to remove.
  1027. */
  1028. H.erase = function (arr, item) {
  1029. var i = arr.length;
  1030. while (i--) {
  1031. if (arr[i] === item) {
  1032. arr.splice(i, 1);
  1033. break;
  1034. }
  1035. }
  1036. };
  1037. /**
  1038. * Check if an object is null or undefined.
  1039. *
  1040. * @function Highcharts.defined
  1041. *
  1042. * @param {*} obj
  1043. * The object to check.
  1044. *
  1045. * @return {boolean}
  1046. * False if the object is null or undefined, otherwise true.
  1047. */
  1048. H.defined = function (obj) {
  1049. return obj !== undefined && obj !== null;
  1050. };
  1051. /**
  1052. * Set or get an attribute or an object of attributes. To use as a setter, pass
  1053. * a key and a value, or let the second argument be a collection of keys and
  1054. * values. To use as a getter, pass only a string as the second argument.
  1055. *
  1056. * @function Highcharts.attr
  1057. *
  1058. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
  1059. * The DOM element to receive the attribute(s).
  1060. *
  1061. * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [prop]
  1062. * The property or an object of key-value pairs.
  1063. *
  1064. * @param {string} [value]
  1065. * The value if a single property is set.
  1066. *
  1067. * @return {*}
  1068. * When used as a getter, return the value.
  1069. */
  1070. H.attr = function (elem, prop, value) {
  1071. var ret;
  1072. // if the prop is a string
  1073. if (H.isString(prop)) {
  1074. // set the value
  1075. if (H.defined(value)) {
  1076. elem.setAttribute(prop, value);
  1077. // get the value
  1078. } else if (elem && elem.getAttribute) {
  1079. ret = elem.getAttribute(prop);
  1080. // IE7 and below cannot get class through getAttribute (#7850)
  1081. if (!ret && prop === 'class') {
  1082. ret = elem.getAttribute(prop + 'Name');
  1083. }
  1084. }
  1085. // else if prop is defined, it is a hash of key/value pairs
  1086. } else if (H.defined(prop) && H.isObject(prop)) {
  1087. H.objectEach(prop, function (val, key) {
  1088. elem.setAttribute(key, val);
  1089. });
  1090. }
  1091. return ret;
  1092. };
  1093. /**
  1094. * Check if an element is an array, and if not, make it into an array.
  1095. *
  1096. * @function Highcharts.splat
  1097. *
  1098. * @param {*} obj
  1099. * The object to splat.
  1100. *
  1101. * @return {Array}
  1102. * The produced or original array.
  1103. */
  1104. H.splat = function (obj) {
  1105. return H.isArray(obj) ? obj : [obj];
  1106. };
  1107. /**
  1108. * Set a timeout if the delay is given, otherwise perform the function
  1109. * synchronously.
  1110. *
  1111. * @function Highcharts.syncTimeout
  1112. *
  1113. * @param {Function} fn
  1114. * The function callback.
  1115. *
  1116. * @param {number} delay
  1117. * Delay in milliseconds.
  1118. *
  1119. * @param {*} [parameter]
  1120. * An optional parameter to send to the function callback.
  1121. *
  1122. * @return {number}
  1123. * An identifier for the timeout that can later be cleared with
  1124. * Highcharts.clearTimeout.
  1125. */
  1126. H.syncTimeout = function (fn, delay, context) {
  1127. if (delay) {
  1128. return setTimeout(fn, delay, context);
  1129. }
  1130. fn.call(0, context);
  1131. };
  1132. /**
  1133. * Internal clear timeout. The function checks that the `id` was not removed
  1134. * (e.g. by `chart.destroy()`). For the details see
  1135. * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
  1136. *
  1137. * @function Highcharts.clearTimeout
  1138. *
  1139. * @param {number} id
  1140. * Id of a timeout.
  1141. */
  1142. H.clearTimeout = function (id) {
  1143. if (H.defined(id)) {
  1144. clearTimeout(id);
  1145. }
  1146. };
  1147. /**
  1148. * Utility function to extend an object with the members of another.
  1149. *
  1150. * @function Highcharts.extend
  1151. *
  1152. * @param {Highcharts.Dictionary<*>} a
  1153. * The object to be extended.
  1154. *
  1155. * @param {Highcharts.Dictionary<*>} b
  1156. * The object to add to the first one.
  1157. *
  1158. * @return {Highcharts.Dictionary<*>}
  1159. * Object a, the original object.
  1160. */
  1161. H.extend = function (a, b) {
  1162. var n;
  1163. if (!a) {
  1164. a = {};
  1165. }
  1166. for (n in b) {
  1167. a[n] = b[n];
  1168. }
  1169. return a;
  1170. };
  1171. /**
  1172. * Return the first value that is not null or undefined.
  1173. *
  1174. * @function Highcharts.pick
  1175. *
  1176. * @param {...*} items
  1177. * Variable number of arguments to inspect.
  1178. *
  1179. * @return {*}
  1180. * The value of the first argument that is not null or undefined.
  1181. */
  1182. H.pick = function () {
  1183. var args = arguments,
  1184. i,
  1185. arg,
  1186. length = args.length;
  1187. for (i = 0; i < length; i++) {
  1188. arg = args[i];
  1189. if (arg !== undefined && arg !== null) {
  1190. return arg;
  1191. }
  1192. }
  1193. };
  1194. /**
  1195. * Set CSS on a given element.
  1196. *
  1197. * @function Highcharts.css
  1198. *
  1199. * @param {Highcharts.HTMLDOMElement} el
  1200. * An HTML DOM element.
  1201. *
  1202. * @param {Highcharts.CSSObject} styles
  1203. * Style object with camel case property names.
  1204. */
  1205. H.css = function (el, styles) {
  1206. if (H.isMS && !H.svg) { // #2686
  1207. if (styles && styles.opacity !== undefined) {
  1208. styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')';
  1209. }
  1210. }
  1211. H.extend(el.style, styles);
  1212. };
  1213. /**
  1214. * Utility function to create an HTML element with attributes and styles.
  1215. *
  1216. * @function Highcharts.createElement
  1217. *
  1218. * @param {string} tag
  1219. * The HTML tag.
  1220. *
  1221. * @param {Highcharts.HTMLAttributes} [attribs]
  1222. * Attributes as an object of key-value pairs.
  1223. *
  1224. * @param {Highcharts.CSSObject} [styles]
  1225. * Styles as an object of key-value pairs.
  1226. *
  1227. * @param {Highcharts.HTMLDOMElement} [parent]
  1228. * The parent HTML object.
  1229. *
  1230. * @param {boolean} [nopad=false]
  1231. * If true, remove all padding, border and margin.
  1232. *
  1233. * @return {Highcharts.HTMLDOMElement}
  1234. * The created DOM element.
  1235. */
  1236. H.createElement = function (tag, attribs, styles, parent, nopad) {
  1237. var el = doc.createElement(tag),
  1238. css = H.css;
  1239. if (attribs) {
  1240. H.extend(el, attribs);
  1241. }
  1242. if (nopad) {
  1243. css(el, { padding: 0, border: 'none', margin: 0 });
  1244. }
  1245. if (styles) {
  1246. css(el, styles);
  1247. }
  1248. if (parent) {
  1249. parent.appendChild(el);
  1250. }
  1251. return el;
  1252. };
  1253. /**
  1254. * Extend a prototyped class by new members.
  1255. *
  1256. * @function Highcharts.extendClass
  1257. *
  1258. * @param {*} parent
  1259. * The parent prototype to inherit.
  1260. *
  1261. * @param {Highcharts.Dictionary<*>} members
  1262. * A collection of prototype members to add or override compared to the
  1263. * parent prototype.
  1264. *
  1265. * @return {*}
  1266. * A new prototype.
  1267. */
  1268. H.extendClass = function (parent, members) {
  1269. var object = function () {};
  1270. object.prototype = new parent(); // eslint-disable-line new-cap
  1271. H.extend(object.prototype, members);
  1272. return object;
  1273. };
  1274. /**
  1275. * Left-pad a string to a given length by adding a character repetetively.
  1276. *
  1277. * @function Highcharts.pad
  1278. *
  1279. * @param {number} number
  1280. * The input string or number.
  1281. *
  1282. * @param {number} length
  1283. * The desired string length.
  1284. *
  1285. * @param {string} [padder=0]
  1286. * The character to pad with.
  1287. *
  1288. * @return {string}
  1289. * The padded string.
  1290. */
  1291. H.pad = function (number, length, padder) {
  1292. return new Array(
  1293. (length || 2) +
  1294. 1 -
  1295. String(number)
  1296. .replace('-', '')
  1297. .length
  1298. ).join(padder || 0) + number;
  1299. };
  1300. /**
  1301. * Return a length based on either the integer value, or a percentage of a base.
  1302. *
  1303. * @function Highcharts.relativeLength
  1304. *
  1305. * @param {Highcharts.RelativeSize} value
  1306. * A percentage string or a number.
  1307. *
  1308. * @param {number} base
  1309. * The full length that represents 100%.
  1310. *
  1311. * @param {number} [offset=0]
  1312. * A pixel offset to apply for percentage values. Used internally in
  1313. * axis positioning.
  1314. *
  1315. * @return {number}
  1316. * The computed length.
  1317. */
  1318. H.relativeLength = function (value, base, offset) {
  1319. return (/%$/).test(value) ?
  1320. (base * parseFloat(value) / 100) + (offset || 0) :
  1321. parseFloat(value);
  1322. };
  1323. /**
  1324. * Wrap a method with extended functionality, preserving the original function.
  1325. *
  1326. * @function Highcharts.wrap
  1327. *
  1328. * @param {*} obj
  1329. * The context object that the method belongs to. In real cases, this is
  1330. * often a prototype.
  1331. *
  1332. * @param {string} method
  1333. * The name of the method to extend.
  1334. *
  1335. * @param {Function} func
  1336. * A wrapper function callback. This function is called with the same
  1337. * arguments as the original function, except that the original function
  1338. * is unshifted and passed as the first argument.
  1339. */
  1340. H.wrap = function (obj, method, func) {
  1341. var proceed = obj[method];
  1342. obj[method] = function () {
  1343. var args = Array.prototype.slice.call(arguments),
  1344. outerArgs = arguments,
  1345. ctx = this,
  1346. ret;
  1347. ctx.proceed = function () {
  1348. proceed.apply(ctx, arguments.length ? arguments : outerArgs);
  1349. };
  1350. args.unshift(proceed);
  1351. ret = func.apply(this, args);
  1352. ctx.proceed = null;
  1353. return ret;
  1354. };
  1355. };
  1356. /**
  1357. * Recursively converts all Date properties to timestamps.
  1358. *
  1359. * @param {Object} object - any object to convert properties of
  1360. */
  1361. H.datePropsToTimestamps = function (object) {
  1362. H.objectEach(object, function (val, key) {
  1363. if (H.isObject(val) && typeof val.getTime === 'function') {
  1364. object[key] = val.getTime();
  1365. } else if (H.isObject(val) || H.isArray(val)) {
  1366. H.datePropsToTimestamps(val);
  1367. }
  1368. });
  1369. };
  1370. /**
  1371. * Format a single variable. Similar to sprintf, without the % prefix.
  1372. *
  1373. * @example
  1374. * formatSingle('.2f', 5); // => '5.00'.
  1375. *
  1376. * @function Highcharts.formatSingle
  1377. *
  1378. * @param {string} format
  1379. * The format string.
  1380. *
  1381. * @param {*} val
  1382. * The value.
  1383. *
  1384. * @param {Highcharts.Time} [time]
  1385. * A `Time` instance that determines the date formatting, for example
  1386. * for applying time zone corrections to the formatted date.
  1387. *
  1388. * @return {string}
  1389. * The formatted representation of the value.
  1390. */
  1391. H.formatSingle = function (format, val, time) {
  1392. var floatRegex = /f$/,
  1393. decRegex = /\.([0-9])/,
  1394. lang = H.defaultOptions.lang,
  1395. decimals;
  1396. if (floatRegex.test(format)) { // float
  1397. decimals = format.match(decRegex);
  1398. decimals = decimals ? decimals[1] : -1;
  1399. if (val !== null) {
  1400. val = H.numberFormat(
  1401. val,
  1402. decimals,
  1403. lang.decimalPoint,
  1404. format.indexOf(',') > -1 ? lang.thousandsSep : ''
  1405. );
  1406. }
  1407. } else {
  1408. val = (time || H.time).dateFormat(format, val);
  1409. }
  1410. return val;
  1411. };
  1412. /**
  1413. * Format a string according to a subset of the rules of Python's String.format
  1414. * method.
  1415. *
  1416. * @example
  1417. * var s = Highcharts.format(
  1418. * 'The {color} fox was {len:.2f} feet long',
  1419. * { color: 'red', len: Math.PI }
  1420. * );
  1421. * // => The red fox was 3.14 feet long
  1422. *
  1423. * @function Highcharts.format
  1424. *
  1425. * @param {string} str
  1426. * The string to format.
  1427. *
  1428. * @param {*} ctx
  1429. * The context, a collection of key-value pairs where each key is
  1430. * replaced by its value.
  1431. *
  1432. * @param {Highcharts.Time} [time]
  1433. * A `Time` instance that determines the date formatting, for example
  1434. * for applying time zone corrections to the formatted date.
  1435. *
  1436. * @return {string}
  1437. * The formatted string.
  1438. */
  1439. H.format = function (str, ctx, time) {
  1440. var splitter = '{',
  1441. isInside = false,
  1442. segment,
  1443. valueAndFormat,
  1444. path,
  1445. i,
  1446. len,
  1447. ret = [],
  1448. val,
  1449. index;
  1450. while (str) {
  1451. index = str.indexOf(splitter);
  1452. if (index === -1) {
  1453. break;
  1454. }
  1455. segment = str.slice(0, index);
  1456. if (isInside) { // we're on the closing bracket looking back
  1457. valueAndFormat = segment.split(':');
  1458. path = valueAndFormat.shift().split('.'); // get first and leave
  1459. len = path.length;
  1460. val = ctx;
  1461. // Assign deeper paths
  1462. for (i = 0; i < len; i++) {
  1463. if (val) {
  1464. val = val[path[i]];
  1465. }
  1466. }
  1467. // Format the replacement
  1468. if (valueAndFormat.length) {
  1469. val = H.formatSingle(valueAndFormat.join(':'), val, time);
  1470. }
  1471. // Push the result and advance the cursor
  1472. ret.push(val);
  1473. } else {
  1474. ret.push(segment);
  1475. }
  1476. str = str.slice(index + 1); // the rest
  1477. isInside = !isInside; // toggle
  1478. splitter = isInside ? '}' : '{'; // now look for next matching bracket
  1479. }
  1480. ret.push(str);
  1481. return ret.join('');
  1482. };
  1483. /**
  1484. * Get the magnitude of a number.
  1485. *
  1486. * @function Highcharts.getMagnitude
  1487. *
  1488. * @param {number} number
  1489. * The number.
  1490. *
  1491. * @return {number}
  1492. * The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
  1493. */
  1494. H.getMagnitude = function (num) {
  1495. return Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
  1496. };
  1497. /**
  1498. * Take an interval and normalize it to multiples of round numbers.
  1499. *
  1500. * @deprecated
  1501. * @function Highcharts.normalizeTickInterval
  1502. *
  1503. * @param {number} interval
  1504. * The raw, un-rounded interval.
  1505. *
  1506. * @param {Array} [multiples]
  1507. * Allowed multiples.
  1508. *
  1509. * @param {number} [magnitude]
  1510. * The magnitude of the number.
  1511. *
  1512. * @param {boolean} [allowDecimals]
  1513. * Whether to allow decimals.
  1514. *
  1515. * @param {boolean} [hasTickAmount]
  1516. * If it has tickAmount, avoid landing on tick intervals lower than
  1517. * original.
  1518. *
  1519. * @return {number}
  1520. * The normalized interval.
  1521. *
  1522. * @todo
  1523. * Move this function to the Axis prototype. It is here only for historical
  1524. * reasons.
  1525. */
  1526. H.normalizeTickInterval = function (
  1527. interval,
  1528. multiples,
  1529. magnitude,
  1530. allowDecimals,
  1531. hasTickAmount
  1532. ) {
  1533. var normalized,
  1534. i,
  1535. retInterval = interval;
  1536. // round to a tenfold of 1, 2, 2.5 or 5
  1537. magnitude = H.pick(magnitude, 1);
  1538. normalized = interval / magnitude;
  1539. // multiples for a linear scale
  1540. if (!multiples) {
  1541. multiples = hasTickAmount ?
  1542. // Finer grained ticks when the tick amount is hard set, including
  1543. // when alignTicks is true on multiple axes (#4580).
  1544. [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
  1545. // Else, let ticks fall on rounder numbers
  1546. [1, 2, 2.5, 5, 10];
  1547. // the allowDecimals option
  1548. if (allowDecimals === false) {
  1549. if (magnitude === 1) {
  1550. multiples = multiples.filter(function (num) {
  1551. return num % 1 === 0;
  1552. });
  1553. } else if (magnitude <= 0.1) {
  1554. multiples = [1 / magnitude];
  1555. }
  1556. }
  1557. }
  1558. // normalize the interval to the nearest multiple
  1559. for (i = 0; i < multiples.length; i++) {
  1560. retInterval = multiples[i];
  1561. // only allow tick amounts smaller than natural
  1562. if (
  1563. (
  1564. hasTickAmount &&
  1565. retInterval * magnitude >= interval
  1566. ) ||
  1567. (
  1568. !hasTickAmount &&
  1569. (
  1570. normalized <=
  1571. (
  1572. multiples[i] +
  1573. (multiples[i + 1] || multiples[i])
  1574. ) / 2
  1575. )
  1576. )
  1577. ) {
  1578. break;
  1579. }
  1580. }
  1581. // Multiply back to the correct magnitude. Correct floats to appropriate
  1582. // precision (#6085).
  1583. retInterval = H.correctFloat(
  1584. retInterval * magnitude,
  1585. -Math.round(Math.log(0.001) / Math.LN10)
  1586. );
  1587. return retInterval;
  1588. };
  1589. /**
  1590. * Sort an object array and keep the order of equal items. The ECMAScript
  1591. * standard does not specify the behaviour when items are equal.
  1592. *
  1593. * @function Highcharts.stableSort
  1594. *
  1595. * @param {Array} arr
  1596. * The array to sort.
  1597. *
  1598. * @param {Function} sortFunction
  1599. * The function to sort it with, like with regular Array.prototype.sort.
  1600. */
  1601. H.stableSort = function (arr, sortFunction) {
  1602. var length = arr.length,
  1603. sortValue,
  1604. i;
  1605. // Add index to each item
  1606. for (i = 0; i < length; i++) {
  1607. arr[i].safeI = i; // stable sort index
  1608. }
  1609. arr.sort(function (a, b) {
  1610. sortValue = sortFunction(a, b);
  1611. return sortValue === 0 ? a.safeI - b.safeI : sortValue;
  1612. });
  1613. // Remove index from items
  1614. for (i = 0; i < length; i++) {
  1615. delete arr[i].safeI; // stable sort index
  1616. }
  1617. };
  1618. /**
  1619. * Non-recursive method to find the lowest member of an array. `Math.min` raises
  1620. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1621. * than 150.000 points. This method is slightly slower, but safe.
  1622. *
  1623. * @function Highcharts.arrayMin
  1624. *
  1625. * @param {Array} data
  1626. * An array of numbers.
  1627. *
  1628. * @return {number}
  1629. * The lowest number.
  1630. */
  1631. H.arrayMin = function (data) {
  1632. var i = data.length,
  1633. min = data[0];
  1634. while (i--) {
  1635. if (data[i] < min) {
  1636. min = data[i];
  1637. }
  1638. }
  1639. return min;
  1640. };
  1641. /**
  1642. * Non-recursive method to find the lowest member of an array. `Math.max` raises
  1643. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1644. * than 150.000 points. This method is slightly slower, but safe.
  1645. *
  1646. * @function Highcharts.arrayMax
  1647. *
  1648. * @param {Array} data
  1649. * An array of numbers.
  1650. *
  1651. * @return {number}
  1652. * The highest number.
  1653. */
  1654. H.arrayMax = function (data) {
  1655. var i = data.length,
  1656. max = data[0];
  1657. while (i--) {
  1658. if (data[i] > max) {
  1659. max = data[i];
  1660. }
  1661. }
  1662. return max;
  1663. };
  1664. /**
  1665. * Utility method that destroys any SVGElement instances that are properties on
  1666. * the given object. It loops all properties and invokes destroy if there is a
  1667. * destroy method. The property is then delete.
  1668. *
  1669. * @function Highcharts.destroyObjectProperties
  1670. *
  1671. * @param {*} obj
  1672. * The object to destroy properties on.
  1673. *
  1674. * @param {*} [except]
  1675. * Exception, do not destroy this property, only delete it.
  1676. */
  1677. H.destroyObjectProperties = function (obj, except) {
  1678. H.objectEach(obj, function (val, n) {
  1679. // If the object is non-null and destroy is defined
  1680. if (val && val !== except && val.destroy) {
  1681. // Invoke the destroy
  1682. val.destroy();
  1683. }
  1684. // Delete the property from the object.
  1685. delete obj[n];
  1686. });
  1687. };
  1688. /**
  1689. * Discard a HTML element by moving it to the bin and delete.
  1690. *
  1691. * @function Highcharts.discardElement
  1692. *
  1693. * @param {Highcharts.HTMLDOMElement} element
  1694. * The HTML node to discard.
  1695. */
  1696. H.discardElement = function (element) {
  1697. var garbageBin = H.garbageBin;
  1698. // create a garbage bin element, not part of the DOM
  1699. if (!garbageBin) {
  1700. garbageBin = H.createElement('div');
  1701. }
  1702. // move the node and empty bin
  1703. if (element) {
  1704. garbageBin.appendChild(element);
  1705. }
  1706. garbageBin.innerHTML = '';
  1707. };
  1708. /**
  1709. * Fix JS round off float errors.
  1710. *
  1711. * @function Highcharts.correctFloat
  1712. *
  1713. * @param {number} num
  1714. * A float number to fix.
  1715. *
  1716. * @param {number} [prec=14]
  1717. * The precision.
  1718. *
  1719. * @return {number}
  1720. * The corrected float number.
  1721. */
  1722. H.correctFloat = function (num, prec) {
  1723. return parseFloat(
  1724. num.toPrecision(prec || 14)
  1725. );
  1726. };
  1727. /**
  1728. * Set the global animation to either a given value, or fall back to the given
  1729. * chart's animation option.
  1730. *
  1731. * @function Highcharts.setAnimation
  1732. *
  1733. * @param {boolean|Highcharts.AnimationOptionsObject} animation
  1734. * The animation object.
  1735. *
  1736. * @param {Highcharts.Chart} chart
  1737. * The chart instance.
  1738. *
  1739. * @todo
  1740. * This function always relates to a chart, and sets a property on the renderer,
  1741. * so it should be moved to the SVGRenderer.
  1742. */
  1743. H.setAnimation = function (animation, chart) {
  1744. chart.renderer.globalAnimation = H.pick(
  1745. animation,
  1746. chart.options.chart.animation,
  1747. true
  1748. );
  1749. };
  1750. /**
  1751. * Get the animation in object form, where a disabled animation is always
  1752. * returned as `{ duration: 0 }`.
  1753. *
  1754. * @function Highcharts.animObject
  1755. *
  1756. * @param {boolean|Highcharts.AnimationOptionsObject} animation
  1757. * An animation setting. Can be an object with duration, complete and
  1758. * easing properties, or a boolean to enable or disable.
  1759. *
  1760. * @return {Highcharts.AnimationOptionsObject}
  1761. * An object with at least a duration property.
  1762. */
  1763. H.animObject = function (animation) {
  1764. return H.isObject(animation) ?
  1765. H.merge(animation) :
  1766. { duration: animation ? 500 : 0 };
  1767. };
  1768. /**
  1769. * The time unit lookup
  1770. *
  1771. * @ignore
  1772. */
  1773. H.timeUnits = {
  1774. millisecond: 1,
  1775. second: 1000,
  1776. minute: 60000,
  1777. hour: 3600000,
  1778. day: 24 * 3600000,
  1779. week: 7 * 24 * 3600000,
  1780. month: 28 * 24 * 3600000,
  1781. year: 364 * 24 * 3600000
  1782. };
  1783. /**
  1784. * Format a number and return a string based on input settings.
  1785. *
  1786. * @sample highcharts/members/highcharts-numberformat/
  1787. * Custom number format
  1788. *
  1789. * @function Highcharts.numberFormat
  1790. *
  1791. * @param {number} number
  1792. * The input number to format.
  1793. *
  1794. * @param {number} decimals
  1795. * The amount of decimals. A value of -1 preserves the amount in the
  1796. * input number.
  1797. *
  1798. * @param {string} [decimalPoint]
  1799. * The decimal point, defaults to the one given in the lang options, or
  1800. * a dot.
  1801. *
  1802. * @param {string} [thousandsSep]
  1803. * The thousands separator, defaults to the one given in the lang
  1804. * options, or a space character.
  1805. *
  1806. * @return {string}
  1807. * The formatted number.
  1808. */
  1809. H.numberFormat = function (number, decimals, decimalPoint, thousandsSep) {
  1810. number = +number || 0;
  1811. decimals = +decimals;
  1812. var lang = H.defaultOptions.lang,
  1813. origDec = (number.toString().split('.')[1] || '').split('e')[0].length,
  1814. strinteger,
  1815. thousands,
  1816. ret,
  1817. roundedNumber,
  1818. exponent = number.toString().split('e'),
  1819. fractionDigits;
  1820. if (decimals === -1) {
  1821. // Preserve decimals. Not huge numbers (#3793).
  1822. decimals = Math.min(origDec, 20);
  1823. } else if (!H.isNumber(decimals)) {
  1824. decimals = 2;
  1825. } else if (decimals && exponent[1] && exponent[1] < 0) {
  1826. // Expose decimals from exponential notation (#7042)
  1827. fractionDigits = decimals + +exponent[1];
  1828. if (fractionDigits >= 0) {
  1829. // remove too small part of the number while keeping the notation
  1830. exponent[0] = (+exponent[0]).toExponential(fractionDigits)
  1831. .split('e')[0];
  1832. decimals = fractionDigits;
  1833. } else {
  1834. // fractionDigits < 0
  1835. exponent[0] = exponent[0].split('.')[0] || 0;
  1836. if (decimals < 20) {
  1837. // use number instead of exponential notation (#7405)
  1838. number = (exponent[0] * Math.pow(10, exponent[1]))
  1839. .toFixed(decimals);
  1840. } else {
  1841. // or zero
  1842. number = 0;
  1843. }
  1844. exponent[1] = 0;
  1845. }
  1846. }
  1847. // Add another decimal to avoid rounding errors of float numbers. (#4573)
  1848. // Then use toFixed to handle rounding.
  1849. roundedNumber = (
  1850. Math.abs(exponent[1] ? exponent[0] : number) +
  1851. Math.pow(10, -Math.max(decimals, origDec) - 1)
  1852. ).toFixed(decimals);
  1853. // A string containing the positive integer component of the number
  1854. strinteger = String(H.pInt(roundedNumber));
  1855. // Leftover after grouping into thousands. Can be 0, 1 or 2.
  1856. thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
  1857. // Language
  1858. decimalPoint = H.pick(decimalPoint, lang.decimalPoint);
  1859. thousandsSep = H.pick(thousandsSep, lang.thousandsSep);
  1860. // Start building the return
  1861. ret = number < 0 ? '-' : '';
  1862. // Add the leftover after grouping into thousands. For example, in the
  1863. // number 42 000 000, this line adds 42.
  1864. ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
  1865. // Add the remaining thousands groups, joined by the thousands separator
  1866. ret += strinteger
  1867. .substr(thousands)
  1868. .replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
  1869. // Add the decimal point and the decimal component
  1870. if (decimals) {
  1871. // Get the decimal component
  1872. ret += decimalPoint + roundedNumber.slice(-decimals);
  1873. }
  1874. if (exponent[1] && +ret !== 0) {
  1875. ret += 'e' + exponent[1];
  1876. }
  1877. return ret;
  1878. };
  1879. /**
  1880. * Easing definition
  1881. *
  1882. * @private
  1883. * @function Math.easeInOutSine
  1884. *
  1885. * @param {number} pos
  1886. * Current position, ranging from 0 to 1.
  1887. *
  1888. * @return {number}
  1889. */
  1890. Math.easeInOutSine = function (pos) {
  1891. return -0.5 * (Math.cos(Math.PI * pos) - 1);
  1892. };
  1893. /**
  1894. * Get the computed CSS value for given element and property, only for numerical
  1895. * properties. For width and height, the dimension of the inner box (excluding
  1896. * padding) is returned. Used for fitting the chart within the container.
  1897. *
  1898. * @function Highcharts.getStyle
  1899. *
  1900. * @param {Highcharts.HTMLDOMElement} el
  1901. * An HTML element.
  1902. *
  1903. * @param {string} prop
  1904. * The property name.
  1905. *
  1906. * @param {boolean} [toInt=true]
  1907. * Parse to integer.
  1908. *
  1909. * @return {number}
  1910. * The numeric value.
  1911. */
  1912. H.getStyle = function (el, prop, toInt) {
  1913. var style;
  1914. // For width and height, return the actual inner pixel size (#4913)
  1915. if (prop === 'width') {
  1916. return Math.max(
  1917. 0, // #8377
  1918. (
  1919. Math.min(
  1920. el.offsetWidth,
  1921. el.scrollWidth,
  1922. (
  1923. el.getBoundingClientRect &&
  1924. // #9871, getBoundingClientRect doesn't handle
  1925. // transforms, so avoid that
  1926. H.getStyle(el, 'transform', false) === 'none'
  1927. ) ?
  1928. Math.floor(el.getBoundingClientRect().width) : // #6427
  1929. Infinity
  1930. ) -
  1931. H.getStyle(el, 'padding-left') -
  1932. H.getStyle(el, 'padding-right')
  1933. )
  1934. );
  1935. }
  1936. if (prop === 'height') {
  1937. return Math.max(
  1938. 0, // #8377
  1939. Math.min(el.offsetHeight, el.scrollHeight) -
  1940. H.getStyle(el, 'padding-top') -
  1941. H.getStyle(el, 'padding-bottom')
  1942. );
  1943. }
  1944. if (!win.getComputedStyle) {
  1945. // SVG not supported, forgot to load oldie.js?
  1946. H.error(27, true);
  1947. }
  1948. // Otherwise, get the computed style
  1949. style = win.getComputedStyle(el, undefined);
  1950. if (style) {
  1951. style = style.getPropertyValue(prop);
  1952. if (H.pick(toInt, prop !== 'opacity')) {
  1953. style = H.pInt(style);
  1954. }
  1955. }
  1956. return style;
  1957. };
  1958. /**
  1959. * Search for an item in an array.
  1960. *
  1961. * @function Highcharts.inArray
  1962. *
  1963. * @deprecated
  1964. *
  1965. * @param {*} item
  1966. * The item to search for.
  1967. *
  1968. * @param {Array} arr
  1969. * The array or node collection to search in.
  1970. *
  1971. * @param {number} [fromIndex=0]
  1972. * The index to start searching from.
  1973. *
  1974. * @return {number}
  1975. * The index within the array, or -1 if not found.
  1976. */
  1977. H.inArray = function (item, arr, fromIndex) {
  1978. return arr.indexOf(item, fromIndex);
  1979. };
  1980. /**
  1981. * Return the value of the first element in the array that satisfies the
  1982. * provided testing function.
  1983. *
  1984. * @function Highcharts.find
  1985. *
  1986. * @param {Array} arr
  1987. * The array to test.
  1988. *
  1989. * @param {Function} callback
  1990. * The callback function. The function receives the item as the first
  1991. * argument. Return `true` if this item satisfies the condition.
  1992. *
  1993. * @return {*}
  1994. * The value of the element.
  1995. */
  1996. H.find = Array.prototype.find ?
  1997. function (arr, callback) {
  1998. return arr.find(callback);
  1999. } :
  2000. // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
  2001. function (arr, fn) {
  2002. var i,
  2003. length = arr.length;
  2004. for (i = 0; i < length; i++) {
  2005. if (fn(arr[i], i)) {
  2006. return arr[i];
  2007. }
  2008. }
  2009. };
  2010. /**
  2011. * Returns an array of a given object's own properties.
  2012. *
  2013. * @function Highcharts.keys
  2014. * @deprecated
  2015. *
  2016. * @param {*} obj
  2017. * The object of which the properties are to be returned.
  2018. *
  2019. * @return {Array<string>}
  2020. * An array of strings that represents all the properties.
  2021. */
  2022. H.keys = Object.keys;
  2023. /**
  2024. * Get the element's offset position, corrected for `overflow: auto`.
  2025. *
  2026. * @function Highcharts.offset
  2027. *
  2028. * @param {Highcharts.HTMLDOMElement} el
  2029. * The HTML element.
  2030. *
  2031. * @return {Highcharts.OffsetObject}
  2032. * An object containing `left` and `top` properties for the position in
  2033. * the page.
  2034. */
  2035. H.offset = function (el) {
  2036. var docElem = doc.documentElement,
  2037. box = (el.parentElement || el.parentNode) ?
  2038. el.getBoundingClientRect() :
  2039. { top: 0, left: 0 };
  2040. return {
  2041. top: box.top + (win.pageYOffset || docElem.scrollTop) -
  2042. (docElem.clientTop || 0),
  2043. left: box.left + (win.pageXOffset || docElem.scrollLeft) -
  2044. (docElem.clientLeft || 0)
  2045. };
  2046. };
  2047. /**
  2048. * Stop running animation.
  2049. *
  2050. * @function Highcharts.stop
  2051. *
  2052. * @param {Highcharts.SVGElement} el
  2053. * The SVGElement to stop animation on.
  2054. *
  2055. * @param {string} [prop]
  2056. * The property to stop animating. If given, the stop method will stop a
  2057. * single property from animating, while others continue.
  2058. *
  2059. * @todo
  2060. * A possible extension to this would be to stop a single property, when
  2061. * we want to continue animating others. Then assign the prop to the timer
  2062. * in the Fx.run method, and check for the prop here. This would be an
  2063. * improvement in all cases where we stop the animation from .attr. Instead of
  2064. * stopping everything, we can just stop the actual attributes we're setting.
  2065. */
  2066. H.stop = function (el, prop) {
  2067. var i = H.timers.length;
  2068. // Remove timers related to this element (#4519)
  2069. while (i--) {
  2070. if (H.timers[i].elem === el && (!prop || prop === H.timers[i].prop)) {
  2071. H.timers[i].stopped = true; // #4667
  2072. }
  2073. }
  2074. };
  2075. /**
  2076. * Iterate over object key pairs in an object.
  2077. *
  2078. * @function Highcharts.objectEach
  2079. *
  2080. * @param {*} obj
  2081. * The object to iterate over.
  2082. *
  2083. * @param {Highcharts.ObjectEachCallbackFunction} fn
  2084. * The iterator callback. It passes three arguments:
  2085. * * value - The property value.
  2086. * * key - The property key.
  2087. * * obj - The object that objectEach is being applied to.
  2088. *
  2089. * @param {*} [ctx]
  2090. * The context.
  2091. */
  2092. H.objectEach = function (obj, fn, ctx) {
  2093. for (var key in obj) {
  2094. if (obj.hasOwnProperty(key)) {
  2095. fn.call(ctx || obj[key], obj[key], key, obj);
  2096. }
  2097. }
  2098. };
  2099. /**
  2100. * Iterate over an array.
  2101. *
  2102. * @deprecated
  2103. * @function Highcharts.each
  2104. *
  2105. * @param {Array<*>} arr
  2106. * The array to iterate over.
  2107. *
  2108. * @param {Function} fn
  2109. * The iterator callback. It passes three arguments:
  2110. * - `item`: The array item.
  2111. * - `index`: The item's index in the array.
  2112. * - `arr`: The array that each is being applied to.
  2113. *
  2114. * @param {*} [ctx]
  2115. * The context.
  2116. */
  2117. /**
  2118. * Filter an array by a callback.
  2119. *
  2120. * @deprecated
  2121. * @function Highcharts.grep
  2122. *
  2123. * @param {Array<*>} arr
  2124. * The array to filter.
  2125. *
  2126. * @param {Function} callback
  2127. * The callback function. The function receives the item as the first
  2128. * argument. Return `true` if the item is to be preserved.
  2129. *
  2130. * @return {Array<*>}
  2131. * A new, filtered array.
  2132. */
  2133. /**
  2134. * Map an array by a callback.
  2135. *
  2136. * @deprecated
  2137. * @function Highcharts.map
  2138. *
  2139. * @param {Array<*>} arr
  2140. * The array to map.
  2141. *
  2142. * @param {Function} fn
  2143. * The callback function. Return the new value for the new array.
  2144. *
  2145. * @return {Array<*>}
  2146. * A new array item with modified items.
  2147. */
  2148. /**
  2149. * Reduce an array to a single value.
  2150. *
  2151. * @deprecated
  2152. * @function Highcharts.reduce
  2153. *
  2154. * @param {Array} arr
  2155. * The array to reduce.
  2156. *
  2157. * @param {Function} fn
  2158. * The callback function. Return the reduced value. Receives 4
  2159. * arguments: Accumulated/reduced value, current value, current array
  2160. * index, and the array.
  2161. *
  2162. * @param {*} initialValue
  2163. * The initial value of the accumulator.
  2164. *
  2165. * @return {*}
  2166. * The reduced value.
  2167. */
  2168. /**
  2169. * Test whether at least one element in the array passes the test implemented by
  2170. * the provided function.
  2171. *
  2172. * @deprecated
  2173. * @function Highcharts.some
  2174. *
  2175. * @param {Array<*>} arr
  2176. * The array to test
  2177. *
  2178. * @param {Function} fn
  2179. * The function to run on each item. Return truty to pass the test.
  2180. * Receives arguments `currentValue`, `index` and `array`.
  2181. *
  2182. * @param {*} ctx
  2183. * The context.
  2184. *
  2185. * @return {boolean}
  2186. */
  2187. H.objectEach({
  2188. map: 'map',
  2189. each: 'forEach',
  2190. grep: 'filter',
  2191. reduce: 'reduce',
  2192. some: 'some'
  2193. }, function (val, key) {
  2194. H[key] = function (arr) {
  2195. return Array.prototype[val].apply(
  2196. arr,
  2197. [].slice.call(arguments, 1)
  2198. );
  2199. };
  2200. });
  2201. /**
  2202. * Add an event listener.
  2203. *
  2204. * @function Highcharts.addEvent<T>
  2205. *
  2206. * @param {T} el
  2207. * The element or object to add a listener to. It can be a
  2208. * {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
  2209. *
  2210. * @param {string} type
  2211. * The event type.
  2212. *
  2213. * @param {Highcharts.EventCallbackFunction<T>} fn
  2214. * The function callback to execute when the event is fired.
  2215. *
  2216. * @param {Highcharts.EventOptionsObject} [options]
  2217. * Options for adding the event.
  2218. *
  2219. * @return {Function}
  2220. * A callback function to remove the added event.
  2221. */
  2222. H.addEvent = function (el, type, fn, options) {
  2223. var events,
  2224. addEventListener = el.addEventListener || H.addEventListenerPolyfill;
  2225. // If we're setting events directly on the constructor, use a separate
  2226. // collection, `protoEvents` to distinguish it from the item events in
  2227. // `hcEvents`.
  2228. if (typeof el === 'function' && el.prototype) {
  2229. events = el.prototype.protoEvents = el.prototype.protoEvents || {};
  2230. } else {
  2231. events = el.hcEvents = el.hcEvents || {};
  2232. }
  2233. // Allow click events added to points, otherwise they will be prevented by
  2234. // the TouchPointer.pinch function after a pinch zoom operation (#7091).
  2235. if (H.Point && el instanceof H.Point && el.series && el.series.chart) {
  2236. el.series.chart.runTrackerClick = true;
  2237. }
  2238. // Handle DOM events
  2239. if (addEventListener) {
  2240. addEventListener.call(el, type, fn, false);
  2241. }
  2242. if (!events[type]) {
  2243. events[type] = [];
  2244. }
  2245. events[type].push(fn);
  2246. // Order the calls
  2247. if (options && H.isNumber(options.order)) {
  2248. fn.order = options.order;
  2249. events[type].sort(function (a, b) {
  2250. return a.order - b.order;
  2251. });
  2252. }
  2253. // Return a function that can be called to remove this event.
  2254. return function () {
  2255. H.removeEvent(el, type, fn);
  2256. };
  2257. };
  2258. /**
  2259. * Remove an event that was added with {@link Highcharts#addEvent}.
  2260. *
  2261. * @function Highcharts.removeEvent<T>
  2262. *
  2263. * @param {T} el
  2264. * The element to remove events on.
  2265. *
  2266. * @param {string} [type]
  2267. * The type of events to remove. If undefined, all events are removed
  2268. * from the element.
  2269. *
  2270. * @param {Highcharts.EventCallbackFunction<T>} [fn]
  2271. * The specific callback to remove. If undefined, all events that match
  2272. * the element and optionally the type are removed.
  2273. */
  2274. H.removeEvent = function (el, type, fn) {
  2275. var events,
  2276. index;
  2277. function removeOneEvent(type, fn) {
  2278. var removeEventListener =
  2279. el.removeEventListener || H.removeEventListenerPolyfill;
  2280. if (removeEventListener) {
  2281. removeEventListener.call(el, type, fn, false);
  2282. }
  2283. }
  2284. function removeAllEvents(eventCollection) {
  2285. var types,
  2286. len;
  2287. if (!el.nodeName) {
  2288. return; // break on non-DOM events
  2289. }
  2290. if (type) {
  2291. types = {};
  2292. types[type] = true;
  2293. } else {
  2294. types = eventCollection;
  2295. }
  2296. H.objectEach(types, function (val, n) {
  2297. if (eventCollection[n]) {
  2298. len = eventCollection[n].length;
  2299. while (len--) {
  2300. removeOneEvent(n, eventCollection[n][len]);
  2301. }
  2302. }
  2303. });
  2304. }
  2305. ['protoEvents', 'hcEvents'].forEach(function (coll) {
  2306. var eventCollection = el[coll];
  2307. if (eventCollection) {
  2308. if (type) {
  2309. events = eventCollection[type] || [];
  2310. if (fn) {
  2311. index = events.indexOf(fn);
  2312. if (index > -1) {
  2313. events.splice(index, 1);
  2314. eventCollection[type] = events;
  2315. }
  2316. removeOneEvent(type, fn);
  2317. } else {
  2318. removeAllEvents(eventCollection);
  2319. eventCollection[type] = [];
  2320. }
  2321. } else {
  2322. removeAllEvents(eventCollection);
  2323. el[coll] = {};
  2324. }
  2325. }
  2326. });
  2327. };
  2328. /**
  2329. * Fire an event that was registered with {@link Highcharts#addEvent}.
  2330. *
  2331. * @function Highcharts.fireEvent
  2332. *
  2333. * @param {*} el
  2334. * The object to fire the event on. It can be a {@link HTMLDOMElement},
  2335. * an {@link SVGElement} or any other object.
  2336. *
  2337. * @param {string} type
  2338. * The type of event.
  2339. *
  2340. * @param {Highcharts.Dictionary<*>} [eventArguments]
  2341. * Custom event arguments that are passed on as an argument to the event
  2342. * handler.
  2343. *
  2344. * @param {Function} [defaultFunction]
  2345. * The default function to execute if the other listeners haven't
  2346. * returned false.
  2347. */
  2348. H.fireEvent = function (el, type, eventArguments, defaultFunction) {
  2349. var e,
  2350. events,
  2351. len,
  2352. i,
  2353. fn;
  2354. eventArguments = eventArguments || {};
  2355. if (doc.createEvent && (el.dispatchEvent || el.fireEvent)) {
  2356. e = doc.createEvent('Events');
  2357. e.initEvent(type, true, true);
  2358. H.extend(e, eventArguments);
  2359. if (el.dispatchEvent) {
  2360. el.dispatchEvent(e);
  2361. } else {
  2362. el.fireEvent(type, e);
  2363. }
  2364. } else {
  2365. ['protoEvents', 'hcEvents'].forEach(function (coll) {
  2366. if (el[coll]) {
  2367. events = el[coll][type] || [];
  2368. len = events.length;
  2369. if (!eventArguments.target) { // We're running a custom event
  2370. H.extend(eventArguments, {
  2371. // Attach a simple preventDefault function to skip
  2372. // default handler if called. The built-in
  2373. // defaultPrevented property is not overwritable (#5112)
  2374. preventDefault: function () {
  2375. eventArguments.defaultPrevented = true;
  2376. },
  2377. // Setting target to native events fails with clicking
  2378. // the zoom-out button in Chrome.
  2379. target: el,
  2380. // If the type is not set, we're running a custom event
  2381. // (#2297). If it is set, we're running a browser event,
  2382. // and setting it will cause en error in IE8 (#2465).
  2383. type: type
  2384. });
  2385. }
  2386. for (i = 0; i < len; i++) {
  2387. fn = events[i];
  2388. // If the event handler return false, prevent the default
  2389. // handler from executing
  2390. if (fn && fn.call(el, eventArguments) === false) {
  2391. eventArguments.preventDefault();
  2392. }
  2393. }
  2394. }
  2395. });
  2396. }
  2397. // Run the default if not prevented
  2398. if (defaultFunction && !eventArguments.defaultPrevented) {
  2399. defaultFunction.call(el, eventArguments);
  2400. }
  2401. };
  2402. /**
  2403. * The global animate method, which uses Fx to create individual animators.
  2404. *
  2405. * @function Highcharts.animate
  2406. *
  2407. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
  2408. * The element to animate.
  2409. *
  2410. * @param {Highcharts.HTMLAttributes|Highcharts.SVGAttributes} params
  2411. * An object containing key-value pairs of the properties to animate.
  2412. * Supports numeric as pixel-based CSS properties for HTML objects and
  2413. * attributes for SVGElements.
  2414. *
  2415. * @param {Highcharts.AnimationOptionsObject} [opt]
  2416. * Animation options.
  2417. */
  2418. H.animate = function (el, params, opt) {
  2419. var start,
  2420. unit = '',
  2421. end,
  2422. fx,
  2423. args;
  2424. if (!H.isObject(opt)) { // Number or undefined/null
  2425. args = arguments;
  2426. opt = {
  2427. duration: args[2],
  2428. easing: args[3],
  2429. complete: args[4]
  2430. };
  2431. }
  2432. if (!H.isNumber(opt.duration)) {
  2433. opt.duration = 400;
  2434. }
  2435. opt.easing = typeof opt.easing === 'function' ?
  2436. opt.easing :
  2437. (Math[opt.easing] || Math.easeInOutSine);
  2438. opt.curAnim = H.merge(params);
  2439. H.objectEach(params, function (val, prop) {
  2440. // Stop current running animation of this property
  2441. H.stop(el, prop);
  2442. fx = new H.Fx(el, opt, prop);
  2443. end = null;
  2444. if (prop === 'd') {
  2445. fx.paths = fx.initPath(
  2446. el,
  2447. el.d,
  2448. params.d
  2449. );
  2450. fx.toD = params.d;
  2451. start = 0;
  2452. end = 1;
  2453. } else if (el.attr) {
  2454. start = el.attr(prop);
  2455. } else {
  2456. start = parseFloat(H.getStyle(el, prop)) || 0;
  2457. if (prop !== 'opacity') {
  2458. unit = 'px';
  2459. }
  2460. }
  2461. if (!end) {
  2462. end = val;
  2463. }
  2464. if (end && end.match && end.match('px')) {
  2465. end = end.replace(/px/g, ''); // #4351
  2466. }
  2467. fx.run(start, end, unit);
  2468. });
  2469. };
  2470. /**
  2471. * Factory to create new series prototypes.
  2472. *
  2473. * @function Highcharts.seriesType
  2474. *
  2475. * @param {string} type
  2476. * The series type name.
  2477. *
  2478. * @param {string} parent
  2479. * The parent series type name. Use `line` to inherit from the basic
  2480. * {@link Series} object.
  2481. *
  2482. * @param {*} options
  2483. * The additional default options that is merged with the parent's
  2484. * options.
  2485. *
  2486. * @param {*} props
  2487. * The properties (functions and primitives) to set on the new
  2488. * prototype.
  2489. *
  2490. * @param {*} [pointProps]
  2491. * Members for a series-specific extension of the {@link Point}
  2492. * prototype if needed.
  2493. *
  2494. * @return {Highcharts.Series}
  2495. * The newly created prototype as extended from {@link Series} or its
  2496. * derivatives.
  2497. */
  2498. // docs: add to API + extending Highcharts
  2499. H.seriesType = function (type, parent, options, props, pointProps) {
  2500. var defaultOptions = H.getOptions(),
  2501. seriesTypes = H.seriesTypes;
  2502. // Merge the options
  2503. defaultOptions.plotOptions[type] = H.merge(
  2504. defaultOptions.plotOptions[parent],
  2505. options
  2506. );
  2507. // Create the class
  2508. seriesTypes[type] = H.extendClass(seriesTypes[parent] ||
  2509. function () {}, props);
  2510. seriesTypes[type].prototype.type = type;
  2511. // Create the point class if needed
  2512. if (pointProps) {
  2513. seriesTypes[type].prototype.pointClass =
  2514. H.extendClass(H.Point, pointProps);
  2515. }
  2516. return seriesTypes[type];
  2517. };
  2518. /**
  2519. * Get a unique key for using in internal element id's and pointers. The key is
  2520. * composed of a random hash specific to this Highcharts instance, and a
  2521. * counter.
  2522. *
  2523. * @example
  2524. * var id = H.uniqueKey(); // => 'highcharts-x45f6hp-0'
  2525. *
  2526. * @function Highcharts.uniqueKey
  2527. *
  2528. * @return {string}
  2529. * A unique key.
  2530. */
  2531. H.uniqueKey = (function () {
  2532. var uniqueKeyHash = Math.random().toString(36).substring(2, 9),
  2533. idCounter = 0;
  2534. return function () {
  2535. return 'highcharts-' + uniqueKeyHash + '-' + idCounter++;
  2536. };
  2537. }());
  2538. H.isFunction = function (obj) {
  2539. return typeof obj === 'function';
  2540. };
  2541. // Register Highcharts as a plugin in jQuery
  2542. if (win.jQuery) {
  2543. /**
  2544. * Highcharts-extended JQuery.
  2545. *
  2546. * @external JQuery
  2547. */
  2548. /**
  2549. * Helper function to return the chart of the current JQuery selector
  2550. * element.
  2551. *
  2552. * @function external:JQuery#highcharts
  2553. *
  2554. * @return {Highcharts.Chart}
  2555. * The chart that is linked to the JQuery selector element.
  2556. *//**
  2557. * Factory function to create a chart in the current JQuery selector
  2558. * element.
  2559. *
  2560. * @function external:JQuery#highcharts
  2561. *
  2562. * @param {"Chart"|"Map"|"StockChart"|string} [className]
  2563. * Name of the factory class in the Highcharts namespace.
  2564. *
  2565. * @param {Highcharts.Options} [options]
  2566. * The chart options structure.
  2567. *
  2568. * @param {Highcharts.ChartCallbackFunction} [callback]
  2569. * Function to run when the chart has loaded and and all external
  2570. * images are loaded. Defining a
  2571. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  2572. * handler is equivalent.
  2573. *
  2574. * @return {JQuery}
  2575. * The current JQuery selector.
  2576. */
  2577. win.jQuery.fn.highcharts = function () {
  2578. var args = [].slice.call(arguments);
  2579. if (this[0]) { // this[0] is the renderTo div
  2580. // Create the chart
  2581. if (args[0]) {
  2582. new H[ // eslint-disable-line no-new
  2583. // Constructor defaults to Chart
  2584. H.isString(args[0]) ? args.shift() : 'Chart'
  2585. ](this[0], args[0], args[1]);
  2586. return this;
  2587. }
  2588. // When called without parameters or with the return argument,
  2589. // return an existing chart
  2590. return charts[H.attr(this[0], 'data-highcharts-chart')];
  2591. }
  2592. };
  2593. }
  2594. }(Highcharts));
  2595. (function (H) {
  2596. /**
  2597. * (c) 2010-2019 Torstein Honsi
  2598. *
  2599. * License: www.highcharts.com/license
  2600. */
  2601. /**
  2602. * A valid color to be parsed and handled by Highcharts. Highcharts internally
  2603. * supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
  2604. * rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
  2605. * browsers and displayed correctly, but Highcharts is not able to process them
  2606. * and apply concepts like opacity and brightening.
  2607. *
  2608. * @typedef {string} Highcharts.ColorString
  2609. */
  2610. var isNumber = H.isNumber,
  2611. merge = H.merge,
  2612. pInt = H.pInt;
  2613. /**
  2614. * Handle color operations. The object methods are chainable.
  2615. *
  2616. * @private
  2617. * @class
  2618. * @name Highcharts.Color
  2619. *
  2620. * @param {Highcharts.ColorString} input
  2621. * The input color in either rbga or hex format
  2622. */
  2623. H.Color = function (input) {
  2624. // Backwards compatibility, allow instanciation without new
  2625. if (!(this instanceof H.Color)) {
  2626. return new H.Color(input);
  2627. }
  2628. // Initialize
  2629. this.init(input);
  2630. };
  2631. H.Color.prototype = {
  2632. // Collection of parsers. This can be extended from the outside by pushing
  2633. // parsers to Highcharts.Color.prototype.parsers.
  2634. parsers: [{
  2635. // RGBA color
  2636. 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*\)/, // eslint-disable-line security/detect-unsafe-regex
  2637. parse: function (result) {
  2638. return [
  2639. pInt(result[1]),
  2640. pInt(result[2]),
  2641. pInt(result[3]),
  2642. parseFloat(result[4], 10)
  2643. ];
  2644. }
  2645. }, {
  2646. // RGB color
  2647. regex:
  2648. /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
  2649. parse: function (result) {
  2650. return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
  2651. }
  2652. }],
  2653. // Collection of named colors. Can be extended from the outside by adding
  2654. // colors to Highcharts.Color.prototype.names.
  2655. names: {
  2656. white: '#ffffff',
  2657. black: '#000000'
  2658. },
  2659. /**
  2660. * Parse the input color to rgba array
  2661. *
  2662. * @private
  2663. * @function Highcharts.Color#init
  2664. *
  2665. * @param {Highcharts.ColorString} input
  2666. * The input color in either rbga or hex format
  2667. */
  2668. init: function (input) {
  2669. var result,
  2670. rgba,
  2671. i,
  2672. parser,
  2673. len;
  2674. this.input = input = this.names[
  2675. input && input.toLowerCase ?
  2676. input.toLowerCase() :
  2677. ''
  2678. ] || input;
  2679. // Gradients
  2680. if (input && input.stops) {
  2681. this.stops = input.stops.map(function (stop) {
  2682. return new H.Color(stop[1]);
  2683. });
  2684. // Solid colors
  2685. } else {
  2686. // Bitmasking as input[0] is not working for legacy IE.
  2687. if (input && input.charAt && input.charAt() === '#') {
  2688. len = input.length;
  2689. input = parseInt(input.substr(1), 16);
  2690. // Handle long-form, e.g. #AABBCC
  2691. if (len === 7) {
  2692. rgba = [
  2693. (input & 0xFF0000) >> 16,
  2694. (input & 0xFF00) >> 8,
  2695. (input & 0xFF),
  2696. 1
  2697. ];
  2698. // Handle short-form, e.g. #ABC
  2699. // In short form, the value is assumed to be the same
  2700. // for both nibbles for each component. e.g. #ABC = #AABBCC
  2701. } else if (len === 4) {
  2702. rgba = [
  2703. ((input & 0xF00) >> 4) | (input & 0xF00) >> 8,
  2704. ((input & 0xF0) >> 4) | (input & 0xF0),
  2705. ((input & 0xF) << 4) | (input & 0xF),
  2706. 1
  2707. ];
  2708. }
  2709. }
  2710. // Otherwise, check regex parsers
  2711. if (!rgba) {
  2712. i = this.parsers.length;
  2713. while (i-- && !rgba) {
  2714. parser = this.parsers[i];
  2715. result = parser.regex.exec(input);
  2716. if (result) {
  2717. rgba = parser.parse(result);
  2718. }
  2719. }
  2720. }
  2721. }
  2722. this.rgba = rgba || [];
  2723. },
  2724. /**
  2725. * Return the color in the specified format
  2726. *
  2727. * @function Highcharts.Color#get
  2728. *
  2729. * @param {string} format
  2730. * Possible values are 'a', 'rgb', undefined
  2731. *
  2732. * @return {Highcharts.ColorString}
  2733. * This color as a string.
  2734. */
  2735. get: function (format) {
  2736. var input = this.input,
  2737. rgba = this.rgba,
  2738. ret;
  2739. if (this.stops) {
  2740. ret = merge(input);
  2741. ret.stops = [].concat(ret.stops);
  2742. this.stops.forEach(function (stop, i) {
  2743. ret.stops[i] = [ret.stops[i][0], stop.get(format)];
  2744. });
  2745. // it's NaN if gradient colors on a column chart
  2746. } else if (rgba && isNumber(rgba[0])) {
  2747. if (format === 'rgb' || (!format && rgba[3] === 1)) {
  2748. ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
  2749. } else if (format === 'a') {
  2750. ret = rgba[3];
  2751. } else {
  2752. ret = 'rgba(' + rgba.join(',') + ')';
  2753. }
  2754. } else {
  2755. ret = input;
  2756. }
  2757. return ret;
  2758. },
  2759. /**
  2760. * Brighten the color instance.
  2761. *
  2762. * @function Highcharts.Color#brighten
  2763. *
  2764. * @param {number} alpha
  2765. * The alpha value.
  2766. *
  2767. * @return {Highcharts.ColorString}
  2768. * This color with modifications.
  2769. */
  2770. brighten: function (alpha) {
  2771. var i,
  2772. rgba = this.rgba;
  2773. if (this.stops) {
  2774. this.stops.forEach(function (stop) {
  2775. stop.brighten(alpha);
  2776. });
  2777. } else if (isNumber(alpha) && alpha !== 0) {
  2778. for (i = 0; i < 3; i++) {
  2779. rgba[i] += pInt(alpha * 255);
  2780. if (rgba[i] < 0) {
  2781. rgba[i] = 0;
  2782. }
  2783. if (rgba[i] > 255) {
  2784. rgba[i] = 255;
  2785. }
  2786. }
  2787. }
  2788. return this;
  2789. },
  2790. /**
  2791. * Set the color's opacity to a given alpha value.
  2792. *
  2793. * @function Highcharts.Color#setOpacity
  2794. *
  2795. * @param {number} alpha
  2796. * Opacity between 0 and 1.
  2797. *
  2798. * @return {Highcharts.ColorString}
  2799. * Color with modifications.
  2800. */
  2801. setOpacity: function (alpha) {
  2802. this.rgba[3] = alpha;
  2803. return this;
  2804. },
  2805. /**
  2806. * Return an intermediate color between two colors.
  2807. *
  2808. * @function Highcharts.Color#tweenTo
  2809. *
  2810. * @param {Highcharts.Color} to
  2811. * The color object to tween to.
  2812. *
  2813. * @param {number} pos
  2814. * The intermediate position, where 0 is the from color (current
  2815. * color item), and 1 is the `to` color.
  2816. *
  2817. * @return {Highcharts.ColorString}
  2818. * The intermediate color in rgba notation.
  2819. */
  2820. tweenTo: function (to, pos) {
  2821. // Check for has alpha, because rgba colors perform worse due to lack of
  2822. // support in WebKit.
  2823. var fromRgba = this.rgba,
  2824. toRgba = to.rgba,
  2825. hasAlpha,
  2826. ret;
  2827. // Unsupported color, return to-color (#3920, #7034)
  2828. if (!toRgba.length || !fromRgba || !fromRgba.length) {
  2829. ret = to.input || 'none';
  2830. // Interpolate
  2831. } else {
  2832. hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
  2833. ret = (hasAlpha ? 'rgba(' : 'rgb(') +
  2834. Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
  2835. ',' +
  2836. Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
  2837. ',' +
  2838. Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
  2839. (
  2840. hasAlpha ?
  2841. (
  2842. ',' +
  2843. (toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))
  2844. ) :
  2845. ''
  2846. ) +
  2847. ')';
  2848. }
  2849. return ret;
  2850. }
  2851. };
  2852. /**
  2853. * Creates a color instance out of a color string.
  2854. *
  2855. * @private
  2856. * @function Highcharts.color
  2857. *
  2858. * @param {Highcharts.ColorString} input
  2859. * The input color in either rbga or hex format
  2860. */
  2861. H.color = function (input) {
  2862. return new H.Color(input);
  2863. };
  2864. }(Highcharts));
  2865. (function (H) {
  2866. /* *
  2867. *
  2868. * (c) 2010-2019 Torstein Honsi
  2869. *
  2870. * License: www.highcharts.com/license
  2871. *
  2872. * */
  2873. /**
  2874. * The horizontal alignment of an element.
  2875. *
  2876. * @typedef {"center"|"left"|"right"} Highcharts.AlignType
  2877. */
  2878. /**
  2879. * Options to align the element relative to the chart or another box.
  2880. *
  2881. * @interface Highcharts.AlignObject
  2882. *//**
  2883. * Horizontal alignment. Can be one of `left`, `center` and `right`.
  2884. *
  2885. * @name Highcharts.AlignObject#align
  2886. * @type {Highcharts.AlignType|undefined}
  2887. *
  2888. * @default left
  2889. *//**
  2890. * Vertical alignment. Can be one of `top`, `middle` and `bottom`.
  2891. *
  2892. * @name Highcharts.AlignObject#verticalAlign
  2893. * @type {Highcharts.VerticalAlignType|undefined}
  2894. *
  2895. * @default top
  2896. *//**
  2897. * Horizontal pixel offset from alignment.
  2898. *
  2899. * @name Highcharts.AlignObject#x
  2900. * @type {number|undefined}
  2901. *
  2902. * @default 0
  2903. *//**
  2904. * Vertical pixel offset from alignment.
  2905. *
  2906. * @name Highcharts.AlignObject#y
  2907. * @type {number|undefined}
  2908. *
  2909. * @default 0
  2910. *//**
  2911. * Use the `transform` attribute with translateX and translateY custom
  2912. * attributes to align this elements rather than `x` and `y` attributes.
  2913. *
  2914. * @name Highcharts.AlignObject#alignByTranslate
  2915. * @type {boolean|undefined}
  2916. *
  2917. * @default false
  2918. */
  2919. /**
  2920. * Bounding box of an element.
  2921. *
  2922. * @interface Highcharts.BBoxObject
  2923. *//**
  2924. * Height of the bounding box.
  2925. *
  2926. * @name Highcharts.BBoxObject#height
  2927. * @type {number}
  2928. *//**
  2929. * Width of the bounding box.
  2930. *
  2931. * @name Highcharts.BBoxObject#width
  2932. * @type {number}
  2933. *//**
  2934. * Horizontal position of the bounding box.
  2935. *
  2936. * @name Highcharts.BBoxObject#x
  2937. * @type {number}
  2938. *//**
  2939. * Vertical position of the bounding box.
  2940. *
  2941. * @name Highcharts.BBoxObject#y
  2942. * @type {number}
  2943. */
  2944. /**
  2945. * A clipping rectangle that can be applied to one or more {@link SVGElement}
  2946. * instances. It is instanciated with the {@link SVGRenderer#clipRect} function
  2947. * and applied with the {@link SVGElement#clip} function.
  2948. *
  2949. * @example
  2950. * var circle = renderer.circle(100, 100, 100)
  2951. * .attr({ fill: 'red' })
  2952. * .add();
  2953. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  2954. *
  2955. * // Leave only the lower right quarter visible
  2956. * circle.clip(clipRect);
  2957. *
  2958. * @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
  2959. */
  2960. /**
  2961. * The font metrics.
  2962. *
  2963. * @interface Highcharts.FontMetricsObject
  2964. *//**
  2965. * The baseline relative to the top of the box.
  2966. *
  2967. * @name Highcharts.FontMetricsObject#b
  2968. * @type {number}
  2969. *//**
  2970. * The line height.
  2971. *
  2972. * @name Highcharts.FontMetricsObject#h
  2973. * @type {number}
  2974. *//**
  2975. * The font size.
  2976. *
  2977. * @name Highcharts.FontMetricsObject#f
  2978. * @type {number}
  2979. */
  2980. /**
  2981. * Gradient options instead of a solid color.
  2982. *
  2983. * @example
  2984. * // Linear gradient used as a color option
  2985. * color: {
  2986. * linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
  2987. * stops: [
  2988. * [0, '#003399'], // start
  2989. * [0.5, '#ffffff'], // middle
  2990. * [1, '#3366AA'] // end
  2991. * ]
  2992. * }
  2993. * }
  2994. *
  2995. * @interface Highcharts.GradientColorObject
  2996. *//**
  2997. * Holds an object that defines the start position and the end position relative
  2998. * to the shape.
  2999. * @name Highcharts.GradientColorObject#linearGradient
  3000. * @type {Highcharts.LinearGradientColorObject|undefined}
  3001. *//**
  3002. * Holds an object that defines the center position and the radius.
  3003. * @name Highcharts.GradientColorObject#radialGradient
  3004. * @type {Highcharts.RadialGradientColorObject|undefined}
  3005. *//**
  3006. * The first item in each tuple is the position in the gradient, where 0 is the
  3007. * start of the gradient and 1 is the end of the gradient. Multiple stops can be
  3008. * applied. The second item is the color for each stop. This color can also be
  3009. * given in the rgba format.
  3010. * @name Highcharts.GradientColorObject#stops
  3011. * @type {Array<Array<number,Highcharts.ColorString>>|undefined}
  3012. */
  3013. /**
  3014. * Defines the start position and the end position for a gradient relative
  3015. * to the shape. Start position (x1, y1) and end position (x2, y2) are relative
  3016. * to the shape, where 0 means top/left and 1 is bottom/right.
  3017. *
  3018. * @interface Highcharts.LinearGradientColorObject
  3019. *//**
  3020. * Start horizontal position of the gradient. Float ranges 0-1.
  3021. * @name Highcharts.LinearGradientColorObject#x1
  3022. * @type {number}
  3023. *//**
  3024. * End horizontal position of the gradient. Float ranges 0-1.
  3025. * @name Highcharts.LinearGradientColorObject#x2
  3026. * @type {number}
  3027. *//**
  3028. * Start vertical position of the gradient. Float ranges 0-1.
  3029. * @name Highcharts.LinearGradientColorObject#y1
  3030. * @type {number}
  3031. *//**
  3032. * End vertical position of the gradient. Float ranges 0-1.
  3033. * @name Highcharts.LinearGradientColorObject#y2
  3034. * @type {number}
  3035. */
  3036. /**
  3037. * An object containing `x` and `y` properties for the position of an element.
  3038. *
  3039. * @interface Highcharts.PositionObject
  3040. *//**
  3041. * X position of the element.
  3042. * @name Highcharts.PositionObject#x
  3043. * @type {number}
  3044. *//**
  3045. * Y position of the element.
  3046. * @name Highcharts.PositionObject#y
  3047. * @type {number}
  3048. */
  3049. /**
  3050. * Defines the center position and the radius for a gradient.
  3051. *
  3052. * @interface Highcharts.RadialGradientColorObject
  3053. *//**
  3054. * Center horizontal position relative to the shape. Float ranges 0-1.
  3055. * @name Highcharts.RadialGradientColorObject#cx
  3056. * @type {number}
  3057. *//**
  3058. * Center vertical position relative to the shape. Float ranges 0-1.
  3059. * @name Highcharts.RadialGradientColorObject#cy
  3060. * @type {number}
  3061. *//**
  3062. * Radius relative to the shape. Float ranges 0-1.
  3063. * @name Highcharts.RadialGradientColorObject#r
  3064. * @type {number}
  3065. */
  3066. /**
  3067. * A rectangle.
  3068. *
  3069. * @interface Highcharts.RectangleObject
  3070. *//**
  3071. * Height of the rectangle.
  3072. * @name Highcharts.RectangleObject#height
  3073. * @type {number}
  3074. *//**
  3075. * Width of the rectangle.
  3076. * @name Highcharts.RectangleObject#width
  3077. * @type {number}
  3078. *//**
  3079. * Horizontal position of the rectangle.
  3080. * @name Highcharts.RectangleObject#x
  3081. * @type {number}
  3082. *//**
  3083. * Vertical position of the rectangle.
  3084. * @name Highcharts.RectangleObject#y
  3085. * @type {number}
  3086. */
  3087. /**
  3088. * The shadow options.
  3089. *
  3090. * @interface Highcharts.ShadowOptionsObject
  3091. *//**
  3092. * The shadow color.
  3093. * @name Highcharts.ShadowOptionsObject#color
  3094. * @type {string|undefined}
  3095. * @default #000000
  3096. *//**
  3097. * The horizontal offset from the element.
  3098. *
  3099. * @name Highcharts.ShadowOptionsObject#offsetX
  3100. * @type {number|undefined}
  3101. * @default 1
  3102. *//**
  3103. * The vertical offset from the element.
  3104. * @name Highcharts.ShadowOptionsObject#offsetY
  3105. * @type {number|undefined}
  3106. * @default 1
  3107. *//**
  3108. * The shadow opacity.
  3109. *
  3110. * @name Highcharts.ShadowOptionsObject#opacity
  3111. * @type {number|undefined}
  3112. * @default 0.15
  3113. *//**
  3114. * The shadow width or distance from the element.
  3115. * @name Highcharts.ShadowOptionsObject#width
  3116. * @type {number|undefined}
  3117. * @default 3
  3118. */
  3119. /**
  3120. * Serialized form of an SVG definition, including children. Some key
  3121. * property names are reserved: tagName, textContent, and children.
  3122. *
  3123. * @interface Highcharts.SVGDefinitionObject
  3124. *//**
  3125. * @name Highcharts.SVGDefinitionObject#[key:string]
  3126. * @type {number|string|Array<Highcharts.SVGDefinitionObject>|undefined}
  3127. *//**
  3128. * @name Highcharts.SVGDefinitionObject#children
  3129. * @type {Array<Highcharts.SVGDefinitionObject>|undefined}
  3130. *//**
  3131. * @name Highcharts.SVGDefinitionObject#tagName
  3132. * @type {string|undefined}
  3133. *//**
  3134. * @name Highcharts.SVGDefinitionObject#textContent
  3135. * @type {string|undefined}
  3136. */
  3137. /**
  3138. * An extendable collection of functions for defining symbol paths.
  3139. *
  3140. * @typedef Highcharts.SymbolDictionary
  3141. *
  3142. * @property {Function|undefined} [key:Highcharts.SymbolKey]
  3143. */
  3144. /**
  3145. * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`,
  3146. * `triangle`, `triangle-down`. Symbols are used internally for point
  3147. * markers, button and label borders and backgrounds, or custom shapes.
  3148. * Extendable by adding to {@link SVGRenderer#symbols}.
  3149. *
  3150. * @typedef {string} Highcharts.SymbolKey
  3151. * @validvalue ["arc", "callout", "circle", "diamond", "square", "triangle",
  3152. * "triangle-down"]
  3153. */
  3154. /**
  3155. * Additional options, depending on the actual symbol drawn.
  3156. *
  3157. * @interface Highcharts.SymbolOptionsObject
  3158. *//**
  3159. * The anchor X position for the `callout` symbol. This is where the chevron
  3160. * points to.
  3161. *
  3162. * @name Highcharts.SymbolOptionsObject#anchorX
  3163. * @type {number}
  3164. *//**
  3165. * The anchor Y position for the `callout` symbol. This is where the chevron
  3166. * points to.
  3167. *
  3168. * @name Highcharts.SymbolOptionsObject#anchorY
  3169. * @type {number}
  3170. *//**
  3171. * The end angle of an `arc` symbol.
  3172. *
  3173. * @name Highcharts.SymbolOptionsObject#end
  3174. * @type {number}
  3175. *//**
  3176. * Whether to draw `arc` symbol open or closed.
  3177. *
  3178. * @name Highcharts.SymbolOptionsObject#open
  3179. * @type {boolean}
  3180. *//**
  3181. * The radius of an `arc` symbol, or the border radius for the `callout` symbol.
  3182. *
  3183. * @name Highcharts.SymbolOptionsObject#r
  3184. * @type {number}
  3185. *//**
  3186. * The start angle of an `arc` symbol.
  3187. *
  3188. * @name Highcharts.SymbolOptionsObject#start
  3189. * @type {number}
  3190. */
  3191. /**
  3192. * The vertical alignment of an element.
  3193. *
  3194. * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignType
  3195. */
  3196. var SVGElement,
  3197. SVGRenderer,
  3198. addEvent = H.addEvent,
  3199. animate = H.animate,
  3200. attr = H.attr,
  3201. charts = H.charts,
  3202. color = H.color,
  3203. css = H.css,
  3204. createElement = H.createElement,
  3205. defined = H.defined,
  3206. deg2rad = H.deg2rad,
  3207. destroyObjectProperties = H.destroyObjectProperties,
  3208. doc = H.doc,
  3209. extend = H.extend,
  3210. erase = H.erase,
  3211. hasTouch = H.hasTouch,
  3212. isArray = H.isArray,
  3213. isFirefox = H.isFirefox,
  3214. isMS = H.isMS,
  3215. isObject = H.isObject,
  3216. isString = H.isString,
  3217. isWebKit = H.isWebKit,
  3218. merge = H.merge,
  3219. noop = H.noop,
  3220. objectEach = H.objectEach,
  3221. pick = H.pick,
  3222. pInt = H.pInt,
  3223. removeEvent = H.removeEvent,
  3224. splat = H.splat,
  3225. stop = H.stop,
  3226. svg = H.svg,
  3227. SVG_NS = H.SVG_NS,
  3228. symbolSizes = H.symbolSizes,
  3229. win = H.win;
  3230. /**
  3231. * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
  3232. * rendering layer of Highcharts. Combined with the {@link
  3233. * Highcharts.SVGRenderer} object, these prototypes allow freeform annotation
  3234. * in the charts or even in HTML pages without instanciating a chart. The
  3235. * SVGElement can also wrap HTML labels, when `text` or `label` elements are
  3236. * created with the `useHTML` parameter.
  3237. *
  3238. * The SVGElement instances are created through factory functions on the {@link
  3239. * Highcharts.SVGRenderer} object, like {@link Highcharts.SVGRenderer#rect|
  3240. * rect}, {@link Highcharts.SVGRenderer#path|path}, {@link
  3241. * Highcharts.SVGRenderer#text|text}, {@link Highcharts.SVGRenderer#label|
  3242. * label}, {@link Highcharts.SVGRenderer#g|g} and more.
  3243. *
  3244. * @class
  3245. * @name Highcharts.SVGElement
  3246. */
  3247. SVGElement = H.SVGElement = function () {
  3248. return this;
  3249. };
  3250. extend(SVGElement.prototype, /** @lends Highcharts.SVGElement.prototype */ {
  3251. // Default base for animation
  3252. opacity: 1,
  3253. SVG_NS: SVG_NS,
  3254. /**
  3255. * For labels, these CSS properties are applied to the `text` node directly.
  3256. *
  3257. * @private
  3258. * @name Highcharts.SVGElement#textProps
  3259. * @type {Array<string>}
  3260. */
  3261. textProps: ['direction', 'fontSize', 'fontWeight', 'fontFamily',
  3262. 'fontStyle', 'color', 'lineHeight', 'width', 'textAlign',
  3263. 'textDecoration', 'textOverflow', 'textOutline', 'cursor'],
  3264. /**
  3265. * Initialize the SVG element. This function only exists to make the
  3266. * initiation process overridable. It should not be called directly.
  3267. *
  3268. * @function Highcharts.SVGElement#init
  3269. *
  3270. * @param {Highcharts.SVGRenderer} renderer
  3271. * The SVGRenderer instance to initialize to.
  3272. *
  3273. * @param {string} nodeName
  3274. * The SVG node name.
  3275. */
  3276. init: function (renderer, nodeName) {
  3277. /**
  3278. * The primary DOM node. Each `SVGElement` instance wraps a main DOM
  3279. * node, but may also represent more nodes.
  3280. *
  3281. * @name Highcharts.SVGElement#element
  3282. * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  3283. */
  3284. this.element = nodeName === 'span' ?
  3285. createElement(nodeName) :
  3286. doc.createElementNS(this.SVG_NS, nodeName);
  3287. /**
  3288. * The renderer that the SVGElement belongs to.
  3289. *
  3290. * @name Highcharts.SVGElement#renderer
  3291. * @type {Highcharts.SVGRenderer}
  3292. */
  3293. this.renderer = renderer;
  3294. H.fireEvent(this, 'afterInit');
  3295. },
  3296. /**
  3297. * Animate to given attributes or CSS properties.
  3298. *
  3299. * @sample highcharts/members/element-on/
  3300. * Setting some attributes by animation
  3301. *
  3302. * @function Highcharts.SVGElement#animate
  3303. *
  3304. * @param {Highcharts.SVGAttributes} params
  3305. * SVG attributes or CSS to animate.
  3306. *
  3307. * @param {Highcharts.AnimationOptionsObject} [options]
  3308. * Animation options.
  3309. *
  3310. * @param {Function} [complete]
  3311. * Function to perform at the end of animation.
  3312. *
  3313. * @return {Highcharts.SVGElement}
  3314. * Returns the SVGElement for chaining.
  3315. */
  3316. animate: function (params, options, complete) {
  3317. var animOptions = H.animObject(
  3318. pick(options, this.renderer.globalAnimation, true)
  3319. );
  3320. // When the page is hidden save resources in the background by not
  3321. // running animation at all (#9749).
  3322. if (pick(doc.hidden, doc.msHidden, doc.webkitHidden, false)) {
  3323. animOptions.duration = 0;
  3324. }
  3325. if (animOptions.duration !== 0) {
  3326. // allows using a callback with the global animation without
  3327. // overwriting it
  3328. if (complete) {
  3329. animOptions.complete = complete;
  3330. }
  3331. animate(this, params, animOptions);
  3332. } else {
  3333. this.attr(params, null, complete);
  3334. if (animOptions.step) {
  3335. animOptions.step.call(this);
  3336. }
  3337. }
  3338. return this;
  3339. },
  3340. /**
  3341. * Build and apply an SVG gradient out of a common JavaScript configuration
  3342. * object. This function is called from the attribute setters. An event
  3343. * hook is added for supporting other complex color types.
  3344. *
  3345. * @private
  3346. * @function Highcharts.SVGElement#complexColor
  3347. *
  3348. * @param {Highcharts.GradientColorObject} color
  3349. * The gradient options structure.
  3350. *
  3351. * @param {string} prop
  3352. * The property to apply, can either be `fill` or `stroke`.
  3353. *
  3354. * @param {Highcharts.SVGDOMElement} elem
  3355. * SVG DOM element to apply the gradient on.
  3356. */
  3357. complexColor: function (color, prop, elem) {
  3358. var renderer = this.renderer,
  3359. colorObject,
  3360. gradName,
  3361. gradAttr,
  3362. radAttr,
  3363. gradients,
  3364. gradientObject,
  3365. stops,
  3366. stopColor,
  3367. stopOpacity,
  3368. radialReference,
  3369. id,
  3370. key = [],
  3371. value;
  3372. H.fireEvent(this.renderer, 'complexColor', {
  3373. args: arguments
  3374. }, function () {
  3375. // Apply linear or radial gradients
  3376. if (color.radialGradient) {
  3377. gradName = 'radialGradient';
  3378. } else if (color.linearGradient) {
  3379. gradName = 'linearGradient';
  3380. }
  3381. if (gradName) {
  3382. gradAttr = color[gradName];
  3383. gradients = renderer.gradients;
  3384. stops = color.stops;
  3385. radialReference = elem.radialReference;
  3386. // Keep < 2.2 kompatibility
  3387. if (isArray(gradAttr)) {
  3388. color[gradName] = gradAttr = {
  3389. x1: gradAttr[0],
  3390. y1: gradAttr[1],
  3391. x2: gradAttr[2],
  3392. y2: gradAttr[3],
  3393. gradientUnits: 'userSpaceOnUse'
  3394. };
  3395. }
  3396. // Correct the radial gradient for the radial reference system
  3397. if (
  3398. gradName === 'radialGradient' &&
  3399. radialReference &&
  3400. !defined(gradAttr.gradientUnits)
  3401. ) {
  3402. // Save the radial attributes for updating
  3403. radAttr = gradAttr;
  3404. gradAttr = merge(
  3405. gradAttr,
  3406. renderer.getRadialAttr(radialReference, radAttr),
  3407. { gradientUnits: 'userSpaceOnUse' }
  3408. );
  3409. }
  3410. // Build the unique key to detect whether we need to create a
  3411. // new element (#1282)
  3412. objectEach(gradAttr, function (val, n) {
  3413. if (n !== 'id') {
  3414. key.push(n, val);
  3415. }
  3416. });
  3417. objectEach(stops, function (val) {
  3418. key.push(val);
  3419. });
  3420. key = key.join(',');
  3421. // Check if a gradient object with the same config object is
  3422. // created within this renderer
  3423. if (gradients[key]) {
  3424. id = gradients[key].attr('id');
  3425. } else {
  3426. // Set the id and create the element
  3427. gradAttr.id = id = H.uniqueKey();
  3428. gradients[key] = gradientObject =
  3429. renderer.createElement(gradName)
  3430. .attr(gradAttr)
  3431. .add(renderer.defs);
  3432. gradientObject.radAttr = radAttr;
  3433. // The gradient needs to keep a list of stops to be able to
  3434. // destroy them
  3435. gradientObject.stops = [];
  3436. stops.forEach(function (stop) {
  3437. var stopObject;
  3438. if (stop[1].indexOf('rgba') === 0) {
  3439. colorObject = H.color(stop[1]);
  3440. stopColor = colorObject.get('rgb');
  3441. stopOpacity = colorObject.get('a');
  3442. } else {
  3443. stopColor = stop[1];
  3444. stopOpacity = 1;
  3445. }
  3446. stopObject = renderer.createElement('stop').attr({
  3447. offset: stop[0],
  3448. 'stop-color': stopColor,
  3449. 'stop-opacity': stopOpacity
  3450. }).add(gradientObject);
  3451. // Add the stop element to the gradient
  3452. gradientObject.stops.push(stopObject);
  3453. });
  3454. }
  3455. // Set the reference to the gradient object
  3456. value = 'url(' + renderer.url + '#' + id + ')';
  3457. elem.setAttribute(prop, value);
  3458. elem.gradient = key;
  3459. // Allow the color to be concatenated into tooltips formatters
  3460. // etc. (#2995)
  3461. color.toString = function () {
  3462. return value;
  3463. };
  3464. }
  3465. });
  3466. },
  3467. /**
  3468. * Apply a text outline through a custom CSS property, by copying the text
  3469. * element and apply stroke to the copy. Used internally. Contrast checks at
  3470. * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
  3471. *
  3472. * @example
  3473. * // Specific color
  3474. * text.css({
  3475. * textOutline: '1px black'
  3476. * });
  3477. * // Automatic contrast
  3478. * text.css({
  3479. * color: '#000000', // black text
  3480. * textOutline: '1px contrast' // => white outline
  3481. * });
  3482. *
  3483. * @private
  3484. * @function Highcharts.SVGElement#applyTextOutline
  3485. *
  3486. * @param {string} textOutline
  3487. * A custom CSS `text-outline` setting, defined by `width color`.
  3488. */
  3489. applyTextOutline: function (textOutline) {
  3490. var elem = this.element,
  3491. tspans,
  3492. tspan,
  3493. hasContrast = textOutline.indexOf('contrast') !== -1,
  3494. styles = {},
  3495. color,
  3496. strokeWidth,
  3497. firstRealChild,
  3498. i;
  3499. // When the text shadow is set to contrast, use dark stroke for light
  3500. // text and vice versa.
  3501. if (hasContrast) {
  3502. styles.textOutline = textOutline = textOutline.replace(
  3503. /contrast/g,
  3504. this.renderer.getContrast(elem.style.fill)
  3505. );
  3506. }
  3507. // Extract the stroke width and color
  3508. textOutline = textOutline.split(' ');
  3509. color = textOutline[textOutline.length - 1];
  3510. strokeWidth = textOutline[0];
  3511. if (strokeWidth && strokeWidth !== 'none' && H.svg) {
  3512. this.fakeTS = true; // Fake text shadow
  3513. tspans = [].slice.call(elem.getElementsByTagName('tspan'));
  3514. // In order to get the right y position of the clone,
  3515. // copy over the y setter
  3516. this.ySetter = this.xSetter;
  3517. // Since the stroke is applied on center of the actual outline, we
  3518. // need to double it to get the correct stroke-width outside the
  3519. // glyphs.
  3520. strokeWidth = strokeWidth.replace(
  3521. /(^[\d\.]+)(.*?)$/g,
  3522. function (match, digit, unit) {
  3523. return (2 * digit) + unit;
  3524. }
  3525. );
  3526. // Remove shadows from previous runs. Iterate from the end to
  3527. // support removing items inside the cycle (#6472).
  3528. i = tspans.length;
  3529. while (i--) {
  3530. tspan = tspans[i];
  3531. if (tspan.getAttribute('class') === 'highcharts-text-outline') {
  3532. // Remove then erase
  3533. erase(tspans, elem.removeChild(tspan));
  3534. }
  3535. }
  3536. // For each of the tspans, create a stroked copy behind it.
  3537. firstRealChild = elem.firstChild;
  3538. tspans.forEach(function (tspan, y) {
  3539. var clone;
  3540. // Let the first line start at the correct X position
  3541. if (y === 0) {
  3542. tspan.setAttribute('x', elem.getAttribute('x'));
  3543. y = elem.getAttribute('y');
  3544. tspan.setAttribute('y', y || 0);
  3545. if (y === null) {
  3546. elem.setAttribute('y', 0);
  3547. }
  3548. }
  3549. // Create the clone and apply outline properties
  3550. clone = tspan.cloneNode(1);
  3551. attr(clone, {
  3552. 'class': 'highcharts-text-outline',
  3553. 'fill': color,
  3554. 'stroke': color,
  3555. 'stroke-width': strokeWidth,
  3556. 'stroke-linejoin': 'round'
  3557. });
  3558. elem.insertBefore(clone, firstRealChild);
  3559. });
  3560. }
  3561. },
  3562. // Custom attributes used for symbols, these should be filtered out when
  3563. // setting SVGElement attributes (#9375).
  3564. symbolCustomAttribs: [
  3565. 'x',
  3566. 'y',
  3567. 'width',
  3568. 'height',
  3569. 'r',
  3570. 'start',
  3571. 'end',
  3572. 'innerR',
  3573. 'anchorX',
  3574. 'anchorY',
  3575. 'rounded'
  3576. ],
  3577. /**
  3578. * Apply native and custom attributes to the SVG elements.
  3579. *
  3580. * In order to set the rotation center for rotation, set x and y to 0 and
  3581. * use `translateX` and `translateY` attributes to position the element
  3582. * instead.
  3583. *
  3584. * Attributes frequently used in Highcharts are `fill`, `stroke`,
  3585. * `stroke-width`.
  3586. *
  3587. * @sample highcharts/members/renderer-rect/
  3588. * Setting some attributes
  3589. *
  3590. * @example
  3591. * // Set multiple attributes
  3592. * element.attr({
  3593. * stroke: 'red',
  3594. * fill: 'blue',
  3595. * x: 10,
  3596. * y: 10
  3597. * });
  3598. *
  3599. * // Set a single attribute
  3600. * element.attr('stroke', 'red');
  3601. *
  3602. * // Get an attribute
  3603. * element.attr('stroke'); // => 'red'
  3604. *
  3605. * @function Highcharts.SVGElement#attr
  3606. *
  3607. * @param {string|Highcharts.SVGAttributes} [hash]
  3608. * The native and custom SVG attributes.
  3609. *
  3610. * @param {string} [val]
  3611. * If the type of the first argument is `string`, the second can be a
  3612. * value, which will serve as a single attribute setter. If the first
  3613. * argument is a string and the second is undefined, the function
  3614. * serves as a getter and the current value of the property is
  3615. * returned.
  3616. *
  3617. * @param {Function} [complete]
  3618. * A callback function to execute after setting the attributes. This
  3619. * makes the function compliant and interchangeable with the
  3620. * {@link SVGElement#animate} function.
  3621. *
  3622. * @param {boolean} [continueAnimation=true]
  3623. * Used internally when `.attr` is called as part of an animation
  3624. * step. Otherwise, calling `.attr` for an attribute will stop
  3625. * animation for that attribute.
  3626. *
  3627. * @return {number|string|Highcharts.SVGElement}
  3628. * If used as a setter, it returns the current
  3629. * {@link Highcharts.SVGElement} so the calls can be chained. If
  3630. * used as a getter, the current value of the attribute is returned.
  3631. */
  3632. attr: function (hash, val, complete, continueAnimation) {
  3633. var key,
  3634. element = this.element,
  3635. hasSetSymbolSize,
  3636. ret = this,
  3637. skipAttr,
  3638. setter,
  3639. symbolCustomAttribs = this.symbolCustomAttribs;
  3640. // single key-value pair
  3641. if (typeof hash === 'string' && val !== undefined) {
  3642. key = hash;
  3643. hash = {};
  3644. hash[key] = val;
  3645. }
  3646. // used as a getter: first argument is a string, second is undefined
  3647. if (typeof hash === 'string') {
  3648. ret = (this[hash + 'Getter'] || this._defaultGetter).call(
  3649. this,
  3650. hash,
  3651. element
  3652. );
  3653. // setter
  3654. } else {
  3655. objectEach(hash, function eachAttribute(val, key) {
  3656. skipAttr = false;
  3657. // Unless .attr is from the animator update, stop current
  3658. // running animation of this property
  3659. if (!continueAnimation) {
  3660. stop(this, key);
  3661. }
  3662. // Special handling of symbol attributes
  3663. if (
  3664. this.symbolName &&
  3665. H.inArray(key, symbolCustomAttribs) !== -1
  3666. ) {
  3667. if (!hasSetSymbolSize) {
  3668. this.symbolAttr(hash);
  3669. hasSetSymbolSize = true;
  3670. }
  3671. skipAttr = true;
  3672. }
  3673. if (this.rotation && (key === 'x' || key === 'y')) {
  3674. this.doTransform = true;
  3675. }
  3676. if (!skipAttr) {
  3677. setter = this[key + 'Setter'] || this._defaultSetter;
  3678. setter.call(this, val, key, element);
  3679. // Let the shadow follow the main element
  3680. if (
  3681. !this.styledMode &&
  3682. this.shadows &&
  3683. /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/
  3684. .test(key)
  3685. ) {
  3686. this.updateShadows(key, val, setter);
  3687. }
  3688. }
  3689. }, this);
  3690. this.afterSetters();
  3691. }
  3692. // In accordance with animate, run a complete callback
  3693. if (complete) {
  3694. complete.call(this);
  3695. }
  3696. return ret;
  3697. },
  3698. /**
  3699. * This method is executed in the end of `attr()`, after setting all
  3700. * attributes in the hash. In can be used to efficiently consolidate
  3701. * multiple attributes in one SVG property -- e.g., translate, rotate and
  3702. * scale are merged in one "transform" attribute in the SVG node.
  3703. *
  3704. * @private
  3705. * @function Highcharts.SVGElement#afterSetters
  3706. */
  3707. afterSetters: function () {
  3708. // Update transform. Do this outside the loop to prevent redundant
  3709. // updating for batch setting of attributes.
  3710. if (this.doTransform) {
  3711. this.updateTransform();
  3712. this.doTransform = false;
  3713. }
  3714. },
  3715. /**
  3716. * Update the shadow elements with new attributes.
  3717. *
  3718. * @private
  3719. * @function Highcharts.SVGElement#updateShadows
  3720. *
  3721. * @param {string} key
  3722. * The attribute name.
  3723. *
  3724. * @param {string|number} value
  3725. * The value of the attribute.
  3726. *
  3727. * @param {Function} setter
  3728. * The setter function, inherited from the parent wrapper.
  3729. */
  3730. updateShadows: function (key, value, setter) {
  3731. var shadows = this.shadows,
  3732. i = shadows.length;
  3733. while (i--) {
  3734. setter.call(
  3735. shadows[i],
  3736. key === 'height' ?
  3737. Math.max(value - (shadows[i].cutHeight || 0), 0) :
  3738. key === 'd' ? this.d : value,
  3739. key,
  3740. shadows[i]
  3741. );
  3742. }
  3743. },
  3744. /**
  3745. * Add a class name to an element.
  3746. *
  3747. * @function Highcharts.SVGElement#addClass
  3748. *
  3749. * @param {string} className
  3750. * The new class name to add.
  3751. *
  3752. * @param {boolean} [replace=false]
  3753. * When true, the existing class name(s) will be overwritten with
  3754. * the new one. When false, the new one is added.
  3755. *
  3756. * @return {Highcharts.SVGElement}
  3757. * Return the SVG element for chainability.
  3758. */
  3759. addClass: function (className, replace) {
  3760. var currentClassName = this.attr('class') || '';
  3761. if (currentClassName.indexOf(className) === -1) {
  3762. if (!replace) {
  3763. className =
  3764. (currentClassName + (currentClassName ? ' ' : '') +
  3765. className).replace(' ', ' ');
  3766. }
  3767. this.attr('class', className);
  3768. }
  3769. return this;
  3770. },
  3771. /**
  3772. * Check if an element has the given class name.
  3773. *
  3774. * @function Highcharts.SVGElement#hasClass
  3775. *
  3776. * @param {string} className
  3777. * The class name to check for.
  3778. *
  3779. * @return {boolean}
  3780. * Whether the class name is found.
  3781. */
  3782. hasClass: function (className) {
  3783. return (this.attr('class') || '').split(' ').indexOf(className) !== -1;
  3784. },
  3785. /**
  3786. * Remove a class name from the element.
  3787. *
  3788. * @function Highcharts.SVGElement#removeClass
  3789. *
  3790. * @param {string|RegExp} className
  3791. * The class name to remove.
  3792. *
  3793. * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
  3794. */
  3795. removeClass: function (className) {
  3796. return this.attr(
  3797. 'class',
  3798. (this.attr('class') || '').replace(className, '')
  3799. );
  3800. },
  3801. /**
  3802. * If one of the symbol size affecting parameters are changed,
  3803. * check all the others only once for each call to an element's
  3804. * .attr() method
  3805. *
  3806. * @private
  3807. * @function Highcharts.SVGElement#symbolAttr
  3808. *
  3809. * @param {Highcharts.Dictionary<number|string>} hash
  3810. * The attributes to set.
  3811. */
  3812. symbolAttr: function (hash) {
  3813. var wrapper = this;
  3814. [
  3815. 'x',
  3816. 'y',
  3817. 'r',
  3818. 'start',
  3819. 'end',
  3820. 'width',
  3821. 'height',
  3822. 'innerR',
  3823. 'anchorX',
  3824. 'anchorY'
  3825. ].forEach(function (key) {
  3826. wrapper[key] = pick(hash[key], wrapper[key]);
  3827. });
  3828. wrapper.attr({
  3829. d: wrapper.renderer.symbols[wrapper.symbolName](
  3830. wrapper.x,
  3831. wrapper.y,
  3832. wrapper.width,
  3833. wrapper.height,
  3834. wrapper
  3835. )
  3836. });
  3837. },
  3838. /**
  3839. * Apply a clipping rectangle to this element.
  3840. *
  3841. * @function Highcharts.SVGElement#clip
  3842. *
  3843. * @param {Highcharts.ClipRectElement} [clipRect]
  3844. * The clipping rectangle. If skipped, the current clip is removed.
  3845. *
  3846. * @return {Highcharts.SVGElement}
  3847. * Returns the SVG element to allow chaining.
  3848. */
  3849. clip: function (clipRect) {
  3850. return this.attr(
  3851. 'clip-path',
  3852. clipRect ?
  3853. 'url(' + this.renderer.url + '#' + clipRect.id + ')' :
  3854. 'none'
  3855. );
  3856. },
  3857. /**
  3858. * Calculate the coordinates needed for drawing a rectangle crisply and
  3859. * return the calculated attributes.
  3860. *
  3861. * @function Highcharts.SVGElement#crisp
  3862. *
  3863. * @param {Highcharts.RectangleObject} rect
  3864. * Rectangle to crisp.
  3865. *
  3866. * @param {number} [strokeWidth]
  3867. * The stroke width to consider when computing crisp positioning. It
  3868. * can also be set directly on the rect parameter.
  3869. *
  3870. * @return {Highcharts.RectangleObject}
  3871. * The modified rectangle arguments.
  3872. */
  3873. crisp: function (rect, strokeWidth) {
  3874. var wrapper = this,
  3875. normalizer;
  3876. strokeWidth = strokeWidth || rect.strokeWidth || 0;
  3877. // Math.round because strokeWidth can sometimes have roundoff errors
  3878. normalizer = Math.round(strokeWidth) % 2 / 2;
  3879. // normalize for crisp edges
  3880. rect.x = Math.floor(rect.x || wrapper.x || 0) + normalizer;
  3881. rect.y = Math.floor(rect.y || wrapper.y || 0) + normalizer;
  3882. rect.width = Math.floor(
  3883. (rect.width || wrapper.width || 0) - 2 * normalizer
  3884. );
  3885. rect.height = Math.floor(
  3886. (rect.height || wrapper.height || 0) - 2 * normalizer
  3887. );
  3888. if (defined(rect.strokeWidth)) {
  3889. rect.strokeWidth = strokeWidth;
  3890. }
  3891. return rect;
  3892. },
  3893. /**
  3894. * Set styles for the element. In addition to CSS styles supported by
  3895. * native SVG and HTML elements, there are also some custom made for
  3896. * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
  3897. * elements.
  3898. *
  3899. * @sample highcharts/members/renderer-text-on-chart/
  3900. * Styled text
  3901. *
  3902. * @function Highcharts.SVGElement#css
  3903. *
  3904. * @param {Highcharts.CSSObject} styles
  3905. * The new CSS styles.
  3906. *
  3907. * @return {Highcharts.SVGElement}
  3908. * Return the SVG element for chaining.
  3909. */
  3910. css: function (styles) {
  3911. var oldStyles = this.styles,
  3912. newStyles = {},
  3913. elem = this.element,
  3914. textWidth,
  3915. serializedCss = '',
  3916. hyphenate,
  3917. hasNew = !oldStyles,
  3918. // These CSS properties are interpreted internally by the SVG
  3919. // renderer, but are not supported by SVG and should not be added to
  3920. // the DOM. In styled mode, no CSS should find its way to the DOM
  3921. // whatsoever (#6173, #6474).
  3922. svgPseudoProps = ['textOutline', 'textOverflow', 'width'];
  3923. // convert legacy
  3924. if (styles && styles.color) {
  3925. styles.fill = styles.color;
  3926. }
  3927. // Filter out existing styles to increase performance (#2640)
  3928. if (oldStyles) {
  3929. objectEach(styles, function (style, n) {
  3930. if (style !== oldStyles[n]) {
  3931. newStyles[n] = style;
  3932. hasNew = true;
  3933. }
  3934. });
  3935. }
  3936. if (hasNew) {
  3937. // Merge the new styles with the old ones
  3938. if (oldStyles) {
  3939. styles = extend(
  3940. oldStyles,
  3941. newStyles
  3942. );
  3943. }
  3944. // Get the text width from style
  3945. if (styles) {
  3946. // Previously set, unset it (#8234)
  3947. if (styles.width === null || styles.width === 'auto') {
  3948. delete this.textWidth;
  3949. // Apply new
  3950. } else if (
  3951. elem.nodeName.toLowerCase() === 'text' &&
  3952. styles.width
  3953. ) {
  3954. textWidth = this.textWidth = pInt(styles.width);
  3955. }
  3956. }
  3957. // store object
  3958. this.styles = styles;
  3959. if (textWidth && (!svg && this.renderer.forExport)) {
  3960. delete styles.width;
  3961. }
  3962. // Serialize and set style attribute
  3963. if (elem.namespaceURI === this.SVG_NS) { // #7633
  3964. hyphenate = function (a, b) {
  3965. return '-' + b.toLowerCase();
  3966. };
  3967. objectEach(styles, function (style, n) {
  3968. if (svgPseudoProps.indexOf(n) === -1) {
  3969. serializedCss +=
  3970. n.replace(/([A-Z])/g, hyphenate) + ':' +
  3971. style + ';';
  3972. }
  3973. });
  3974. if (serializedCss) {
  3975. attr(elem, 'style', serializedCss); // #1881
  3976. }
  3977. } else {
  3978. css(elem, styles);
  3979. }
  3980. if (this.added) {
  3981. // Rebuild text after added. Cache mechanisms in the buildText
  3982. // will prevent building if there are no significant changes.
  3983. if (this.element.nodeName === 'text') {
  3984. this.renderer.buildText(this);
  3985. }
  3986. // Apply text outline after added
  3987. if (styles && styles.textOutline) {
  3988. this.applyTextOutline(styles.textOutline);
  3989. }
  3990. }
  3991. }
  3992. return this;
  3993. },
  3994. /**
  3995. * Get the computed style. Only in styled mode.
  3996. *
  3997. * @example
  3998. * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
  3999. *
  4000. * @function Highcharts.SVGElement#getStyle
  4001. *
  4002. * @param {string} prop
  4003. * The property name to check for.
  4004. *
  4005. * @return {string}
  4006. * The current computed value.
  4007. */
  4008. getStyle: function (prop) {
  4009. return win.getComputedStyle(this.element || this, '')
  4010. .getPropertyValue(prop);
  4011. },
  4012. /**
  4013. * Get the computed stroke width in pixel values. This is used extensively
  4014. * when drawing shapes to ensure the shapes are rendered crisp and
  4015. * positioned correctly relative to each other. Using
  4016. * `shape-rendering: crispEdges` leaves us less control over positioning,
  4017. * for example when we want to stack columns next to each other, or position
  4018. * things pixel-perfectly within the plot box.
  4019. *
  4020. * The common pattern when placing a shape is:
  4021. * - Create the SVGElement and add it to the DOM. In styled mode, it will
  4022. * now receive a stroke width from the style sheet. In classic mode we
  4023. * will add the `stroke-width` attribute.
  4024. * - Read the computed `elem.strokeWidth()`.
  4025. * - Place it based on the stroke width.
  4026. *
  4027. * @function Highcharts.SVGElement#strokeWidth
  4028. *
  4029. * @return {number}
  4030. * The stroke width in pixels. Even if the given stroke widtch (in
  4031. * CSS or by attributes) is based on `em` or other units, the pixel
  4032. * size is returned.
  4033. */
  4034. strokeWidth: function () {
  4035. // In non-styled mode, read the stroke width as set by .attr
  4036. if (!this.renderer.styledMode) {
  4037. return this['stroke-width'] || 0;
  4038. }
  4039. // In styled mode, read computed stroke width
  4040. var val = this.getStyle('stroke-width'),
  4041. ret,
  4042. dummy;
  4043. // Read pixel values directly
  4044. if (val.indexOf('px') === val.length - 2) {
  4045. ret = pInt(val);
  4046. // Other values like em, pt etc need to be measured
  4047. } else {
  4048. dummy = doc.createElementNS(SVG_NS, 'rect');
  4049. attr(dummy, {
  4050. 'width': val,
  4051. 'stroke-width': 0
  4052. });
  4053. this.element.parentNode.appendChild(dummy);
  4054. ret = dummy.getBBox().width;
  4055. dummy.parentNode.removeChild(dummy);
  4056. }
  4057. return ret;
  4058. },
  4059. /**
  4060. * Add an event listener. This is a simple setter that replaces all other
  4061. * events of the same type, opposed to the {@link Highcharts#addEvent}
  4062. * function.
  4063. *
  4064. * @sample highcharts/members/element-on/
  4065. * A clickable rectangle
  4066. *
  4067. * @function Highcharts.SVGElement#on
  4068. *
  4069. * @param {string} eventType
  4070. * The event type. If the type is `click`, Highcharts will internally
  4071. * translate it to a `touchstart` event on touch devices, to prevent
  4072. * the browser from waiting for a click event from firing.
  4073. *
  4074. * @param {Function} handler
  4075. * The handler callback.
  4076. *
  4077. * @return {Highcharts.SVGElement}
  4078. * The SVGElement for chaining.
  4079. */
  4080. on: function (eventType, handler) {
  4081. var svgElement = this,
  4082. element = svgElement.element;
  4083. // touch
  4084. if (hasTouch && eventType === 'click') {
  4085. element.ontouchstart = function (e) {
  4086. svgElement.touchEventFired = Date.now(); // #2269
  4087. e.preventDefault();
  4088. handler.call(element, e);
  4089. };
  4090. element.onclick = function (e) {
  4091. if (win.navigator.userAgent.indexOf('Android') === -1 ||
  4092. Date.now() - (svgElement.touchEventFired || 0) > 1100) {
  4093. handler.call(element, e);
  4094. }
  4095. };
  4096. } else {
  4097. // simplest possible event model for internal use
  4098. element['on' + eventType] = handler;
  4099. }
  4100. return this;
  4101. },
  4102. /**
  4103. * Set the coordinates needed to draw a consistent radial gradient across
  4104. * a shape regardless of positioning inside the chart. Used on pie slices
  4105. * to make all the slices have the same radial reference point.
  4106. *
  4107. * @function Highcharts.SVGElement#setRadialReference
  4108. *
  4109. * @param {Array<number>} coordinates
  4110. * The center reference. The format is `[centerX, centerY, diameter]`
  4111. * in pixels.
  4112. *
  4113. * @return {Highcharts.SVGElement}
  4114. * Returns the SVGElement for chaining.
  4115. */
  4116. setRadialReference: function (coordinates) {
  4117. var existingGradient = this.renderer.gradients[this.element.gradient];
  4118. this.element.radialReference = coordinates;
  4119. // On redrawing objects with an existing gradient, the gradient needs
  4120. // to be repositioned (#3801)
  4121. if (existingGradient && existingGradient.radAttr) {
  4122. existingGradient.animate(
  4123. this.renderer.getRadialAttr(
  4124. coordinates,
  4125. existingGradient.radAttr
  4126. )
  4127. );
  4128. }
  4129. return this;
  4130. },
  4131. /**
  4132. * Move an object and its children by x and y values.
  4133. *
  4134. * @function Highcharts.SVGElement#translate
  4135. *
  4136. * @param {number} x
  4137. * The x value.
  4138. *
  4139. * @param {number} y
  4140. * The y value.
  4141. */
  4142. translate: function (x, y) {
  4143. return this.attr({
  4144. translateX: x,
  4145. translateY: y
  4146. });
  4147. },
  4148. /**
  4149. * Invert a group, rotate and flip. This is used internally on inverted
  4150. * charts, where the points and graphs are drawn as if not inverted, then
  4151. * the series group elements are inverted.
  4152. *
  4153. * @function Highcharts.SVGElement#invert
  4154. *
  4155. * @param {boolean} inverted
  4156. * Whether to invert or not. An inverted shape can be un-inverted by
  4157. * setting it to false.
  4158. *
  4159. * @return {Highcharts.SVGElement}
  4160. * Return the SVGElement for chaining.
  4161. */
  4162. invert: function (inverted) {
  4163. var wrapper = this;
  4164. wrapper.inverted = inverted;
  4165. wrapper.updateTransform();
  4166. return wrapper;
  4167. },
  4168. /**
  4169. * Update the transform attribute based on internal properties. Deals with
  4170. * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
  4171. * attributes and updates the SVG `transform` attribute.
  4172. *
  4173. * @private
  4174. * @function Highcharts.SVGElement#updateTransform
  4175. */
  4176. updateTransform: function () {
  4177. var wrapper = this,
  4178. translateX = wrapper.translateX || 0,
  4179. translateY = wrapper.translateY || 0,
  4180. scaleX = wrapper.scaleX,
  4181. scaleY = wrapper.scaleY,
  4182. inverted = wrapper.inverted,
  4183. rotation = wrapper.rotation,
  4184. matrix = wrapper.matrix,
  4185. element = wrapper.element,
  4186. transform;
  4187. // Flipping affects translate as adjustment for flipping around the
  4188. // group's axis
  4189. if (inverted) {
  4190. translateX += wrapper.width;
  4191. translateY += wrapper.height;
  4192. }
  4193. // Apply translate. Nearly all transformed elements have translation,
  4194. // so instead of checking for translate = 0, do it always (#1767,
  4195. // #1846).
  4196. transform = ['translate(' + translateX + ',' + translateY + ')'];
  4197. // apply matrix
  4198. if (defined(matrix)) {
  4199. transform.push(
  4200. 'matrix(' + matrix.join(',') + ')'
  4201. );
  4202. }
  4203. // apply rotation
  4204. if (inverted) {
  4205. transform.push('rotate(90) scale(-1,1)');
  4206. } else if (rotation) { // text rotation
  4207. transform.push(
  4208. 'rotate(' + rotation + ' ' +
  4209. pick(this.rotationOriginX, element.getAttribute('x'), 0) +
  4210. ' ' +
  4211. pick(this.rotationOriginY, element.getAttribute('y') || 0) + ')'
  4212. );
  4213. }
  4214. // apply scale
  4215. if (defined(scaleX) || defined(scaleY)) {
  4216. transform.push(
  4217. 'scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')'
  4218. );
  4219. }
  4220. if (transform.length) {
  4221. element.setAttribute('transform', transform.join(' '));
  4222. }
  4223. },
  4224. /**
  4225. * Bring the element to the front. Alternatively, a new zIndex can be set.
  4226. *
  4227. * @sample highcharts/members/element-tofront/
  4228. * Click an element to bring it to front
  4229. *
  4230. * @function Highcharts.SVGElement#toFront
  4231. *
  4232. * @return {Highcharts.SVGElement}
  4233. * Returns the SVGElement for chaining.
  4234. */
  4235. toFront: function () {
  4236. var element = this.element;
  4237. element.parentNode.appendChild(element);
  4238. return this;
  4239. },
  4240. /**
  4241. * Align the element relative to the chart or another box.
  4242. *
  4243. * @function Highcharts.SVGElement#align
  4244. *
  4245. * @param {Highcharts.AlignObject} [alignOptions]
  4246. * The alignment options. The function can be called without this
  4247. * parameter in order to re-align an element after the box has been
  4248. * updated.
  4249. *
  4250. * @param {boolean} [alignByTranslate]
  4251. * Align element by translation.
  4252. *
  4253. * @param {string|Highcharts.BBoxObject} [box]
  4254. * The box to align to, needs a width and height. When the box is a
  4255. * string, it refers to an object in the Renderer. For example, when
  4256. * box is `spacingBox`, it refers to `Renderer.spacingBox` which
  4257. * holds `width`, `height`, `x` and `y` properties.
  4258. *
  4259. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  4260. */
  4261. align: function (alignOptions, alignByTranslate, box) {
  4262. var align,
  4263. vAlign,
  4264. x,
  4265. y,
  4266. attribs = {},
  4267. alignTo,
  4268. renderer = this.renderer,
  4269. alignedObjects = renderer.alignedObjects,
  4270. alignFactor,
  4271. vAlignFactor;
  4272. // First call on instanciate
  4273. if (alignOptions) {
  4274. this.alignOptions = alignOptions;
  4275. this.alignByTranslate = alignByTranslate;
  4276. if (!box || isString(box)) {
  4277. this.alignTo = alignTo = box || 'renderer';
  4278. // prevent duplicates, like legendGroup after resize
  4279. erase(alignedObjects, this);
  4280. alignedObjects.push(this);
  4281. box = null; // reassign it below
  4282. }
  4283. // When called on resize, no arguments are supplied
  4284. } else {
  4285. alignOptions = this.alignOptions;
  4286. alignByTranslate = this.alignByTranslate;
  4287. alignTo = this.alignTo;
  4288. }
  4289. box = pick(box, renderer[alignTo], renderer);
  4290. // Assign variables
  4291. align = alignOptions.align;
  4292. vAlign = alignOptions.verticalAlign;
  4293. x = (box.x || 0) + (alignOptions.x || 0); // default: left align
  4294. y = (box.y || 0) + (alignOptions.y || 0); // default: top align
  4295. // Align
  4296. if (align === 'right') {
  4297. alignFactor = 1;
  4298. } else if (align === 'center') {
  4299. alignFactor = 2;
  4300. }
  4301. if (alignFactor) {
  4302. x += (box.width - (alignOptions.width || 0)) / alignFactor;
  4303. }
  4304. attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
  4305. // Vertical align
  4306. if (vAlign === 'bottom') {
  4307. vAlignFactor = 1;
  4308. } else if (vAlign === 'middle') {
  4309. vAlignFactor = 2;
  4310. }
  4311. if (vAlignFactor) {
  4312. y += (box.height - (alignOptions.height || 0)) / vAlignFactor;
  4313. }
  4314. attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
  4315. // Animate only if already placed
  4316. this[this.placed ? 'animate' : 'attr'](attribs);
  4317. this.placed = true;
  4318. this.alignAttr = attribs;
  4319. return this;
  4320. },
  4321. /**
  4322. * Get the bounding box (width, height, x and y) for the element. Generally
  4323. * used to get rendered text size. Since this is called a lot in charts,
  4324. * the results are cached based on text properties, in order to save DOM
  4325. * traffic. The returned bounding box includes the rotation, so for example
  4326. * a single text line of rotation 90 will report a greater height, and a
  4327. * width corresponding to the line-height.
  4328. *
  4329. * @sample highcharts/members/renderer-on-chart/
  4330. * Draw a rectangle based on a text's bounding box
  4331. *
  4332. * @function Highcharts.SVGElement#getBBox
  4333. *
  4334. * @param {boolean} [reload]
  4335. * Skip the cache and get the updated DOM bouding box.
  4336. *
  4337. * @param {number} [rot]
  4338. * Override the element's rotation. This is internally used on axis
  4339. * labels with a value of 0 to find out what the bounding box would
  4340. * be have been if it were not rotated.
  4341. *
  4342. * @return {Highcharts.BBoxObject}
  4343. * The bounding box with `x`, `y`, `width` and `height` properties.
  4344. */
  4345. getBBox: function (reload, rot) {
  4346. var wrapper = this,
  4347. bBox, // = wrapper.bBox,
  4348. renderer = wrapper.renderer,
  4349. width,
  4350. height,
  4351. rotation,
  4352. rad,
  4353. element = wrapper.element,
  4354. styles = wrapper.styles,
  4355. fontSize,
  4356. textStr = wrapper.textStr,
  4357. toggleTextShadowShim,
  4358. cache = renderer.cache,
  4359. cacheKeys = renderer.cacheKeys,
  4360. isSVG = element.namespaceURI === wrapper.SVG_NS,
  4361. cacheKey;
  4362. rotation = pick(rot, wrapper.rotation);
  4363. rad = rotation * deg2rad;
  4364. fontSize = renderer.styledMode ? (
  4365. element &&
  4366. SVGElement.prototype.getStyle.call(element, 'font-size')
  4367. ) : (
  4368. styles && styles.fontSize
  4369. );
  4370. // Avoid undefined and null (#7316)
  4371. if (defined(textStr)) {
  4372. cacheKey = textStr.toString();
  4373. // Since numbers are monospaced, and numerical labels appear a lot
  4374. // in a chart, we assume that a label of n characters has the same
  4375. // bounding box as others of the same length. Unless there is inner
  4376. // HTML in the label. In that case, leave the numbers as is (#5899).
  4377. if (cacheKey.indexOf('<') === -1) {
  4378. cacheKey = cacheKey.replace(/[0-9]/g, '0');
  4379. }
  4380. // Properties that affect bounding box
  4381. cacheKey += [
  4382. '',
  4383. rotation || 0,
  4384. fontSize,
  4385. wrapper.textWidth, // #7874, also useHTML
  4386. styles && styles.textOverflow // #5968
  4387. ].join(',');
  4388. }
  4389. if (cacheKey && !reload) {
  4390. bBox = cache[cacheKey];
  4391. }
  4392. // No cache found
  4393. if (!bBox) {
  4394. // SVG elements
  4395. if (isSVG || renderer.forExport) {
  4396. try { // Fails in Firefox if the container has display: none.
  4397. // When the text shadow shim is used, we need to hide the
  4398. // fake shadows to get the correct bounding box (#3872)
  4399. toggleTextShadowShim = this.fakeTS && function (display) {
  4400. [].forEach.call(
  4401. element.querySelectorAll(
  4402. '.highcharts-text-outline'
  4403. ),
  4404. function (tspan) {
  4405. tspan.style.display = display;
  4406. }
  4407. );
  4408. };
  4409. // Workaround for #3842, Firefox reporting wrong bounding
  4410. // box for shadows
  4411. if (toggleTextShadowShim) {
  4412. toggleTextShadowShim('none');
  4413. }
  4414. bBox = element.getBBox ?
  4415. // SVG: use extend because IE9 is not allowed to change
  4416. // width and height in case of rotation (below)
  4417. extend({}, element.getBBox()) : {
  4418. // Legacy IE in export mode
  4419. width: element.offsetWidth,
  4420. height: element.offsetHeight
  4421. };
  4422. // #3842
  4423. if (toggleTextShadowShim) {
  4424. toggleTextShadowShim('');
  4425. }
  4426. } catch (e) {}
  4427. // If the bBox is not set, the try-catch block above failed. The
  4428. // other condition is for Opera that returns a width of
  4429. // -Infinity on hidden elements.
  4430. if (!bBox || bBox.width < 0) {
  4431. bBox = { width: 0, height: 0 };
  4432. }
  4433. // VML Renderer or useHTML within SVG
  4434. } else {
  4435. bBox = wrapper.htmlGetBBox();
  4436. }
  4437. // True SVG elements as well as HTML elements in modern browsers
  4438. // using the .useHTML option need to compensated for rotation
  4439. if (renderer.isSVG) {
  4440. width = bBox.width;
  4441. height = bBox.height;
  4442. // Workaround for wrong bounding box in IE, Edge and Chrome on
  4443. // Windows. With Highcharts' default font, IE and Edge report
  4444. // a box height of 16.899 and Chrome rounds it to 17. If this
  4445. // stands uncorrected, it results in more padding added below
  4446. // the text than above when adding a label border or background.
  4447. // Also vertical positioning is affected.
  4448. // https://jsfiddle.net/highcharts/em37nvuj/
  4449. // (#1101, #1505, #1669, #2568, #6213).
  4450. if (isSVG) {
  4451. bBox.height = height = (
  4452. {
  4453. '11px,17': 14,
  4454. '13px,20': 16
  4455. }[
  4456. styles && styles.fontSize + ',' + Math.round(height)
  4457. ] ||
  4458. height
  4459. );
  4460. }
  4461. // Adjust for rotated text
  4462. if (rotation) {
  4463. bBox.width = Math.abs(height * Math.sin(rad)) +
  4464. Math.abs(width * Math.cos(rad));
  4465. bBox.height = Math.abs(height * Math.cos(rad)) +
  4466. Math.abs(width * Math.sin(rad));
  4467. }
  4468. }
  4469. // Cache it. When loading a chart in a hidden iframe in Firefox and
  4470. // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
  4471. if (cacheKey && bBox.height > 0) {
  4472. // Rotate (#4681)
  4473. while (cacheKeys.length > 250) {
  4474. delete cache[cacheKeys.shift()];
  4475. }
  4476. if (!cache[cacheKey]) {
  4477. cacheKeys.push(cacheKey);
  4478. }
  4479. cache[cacheKey] = bBox;
  4480. }
  4481. }
  4482. return bBox;
  4483. },
  4484. /**
  4485. * Show the element after it has been hidden.
  4486. *
  4487. * @function Highcharts.SVGElement#show
  4488. *
  4489. * @param {boolean} [inherit=false]
  4490. * Set the visibility attribute to `inherit` rather than `visible`.
  4491. * The difference is that an element with `visibility="visible"`
  4492. * will be visible even if the parent is hidden.
  4493. *
  4494. * @return {Highcharts.SVGElement}
  4495. * Returns the SVGElement for chaining.
  4496. */
  4497. show: function (inherit) {
  4498. return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
  4499. },
  4500. /**
  4501. * Hide the element, equivalent to setting the `visibility` attribute to
  4502. * `hidden`.
  4503. *
  4504. * @function Highcharts.SVGElement#hide
  4505. *
  4506. * @return {Highcharts.SVGElement}
  4507. * Returns the SVGElement for chaining.
  4508. */
  4509. hide: function () {
  4510. return this.attr({ visibility: 'hidden' });
  4511. },
  4512. /**
  4513. * Fade out an element by animating its opacity down to 0, and hide it on
  4514. * complete. Used internally for the tooltip.
  4515. *
  4516. * @function Highcharts.SVGElement#fadeOut
  4517. *
  4518. * @param {number} [duration=150]
  4519. * The fade duration in milliseconds.
  4520. */
  4521. fadeOut: function (duration) {
  4522. var elemWrapper = this;
  4523. elemWrapper.animate({
  4524. opacity: 0
  4525. }, {
  4526. duration: duration || 150,
  4527. complete: function () {
  4528. // #3088, assuming we're only using this for tooltips
  4529. elemWrapper.attr({ y: -9999 });
  4530. }
  4531. });
  4532. },
  4533. /**
  4534. * Add the element to the DOM. All elements must be added this way.
  4535. *
  4536. * @sample highcharts/members/renderer-g
  4537. * Elements added to a group
  4538. *
  4539. * @function Highcharts.SVGElement#add
  4540. *
  4541. * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement} [parent]
  4542. * The parent item to add it to. If undefined, the element is added
  4543. * to the {@link Highcharts.SVGRenderer.box}.
  4544. *
  4545. * @return {Highcharts.SVGElement}
  4546. * Returns the SVGElement for chaining.
  4547. */
  4548. add: function (parent) {
  4549. var renderer = this.renderer,
  4550. element = this.element,
  4551. inserted;
  4552. if (parent) {
  4553. this.parentGroup = parent;
  4554. }
  4555. // mark as inverted
  4556. this.parentInverted = parent && parent.inverted;
  4557. // build formatted text
  4558. if (this.textStr !== undefined) {
  4559. renderer.buildText(this);
  4560. }
  4561. // Mark as added
  4562. this.added = true;
  4563. // If we're adding to renderer root, or other elements in the group
  4564. // have a z index, we need to handle it
  4565. if (!parent || parent.handleZ || this.zIndex) {
  4566. inserted = this.zIndexSetter();
  4567. }
  4568. // If zIndex is not handled, append at the end
  4569. if (!inserted) {
  4570. (parent ? parent.element : renderer.box).appendChild(element);
  4571. }
  4572. // fire an event for internal hooks
  4573. if (this.onAdd) {
  4574. this.onAdd();
  4575. }
  4576. return this;
  4577. },
  4578. /**
  4579. * Removes an element from the DOM.
  4580. *
  4581. * @private
  4582. * @function Highcharts.SVGElement#safeRemoveChild
  4583. *
  4584. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  4585. * The DOM node to remove.
  4586. */
  4587. safeRemoveChild: function (element) {
  4588. var parentNode = element.parentNode;
  4589. if (parentNode) {
  4590. parentNode.removeChild(element);
  4591. }
  4592. },
  4593. /**
  4594. * Destroy the element and element wrapper and clear up the DOM and event
  4595. * hooks.
  4596. *
  4597. * @function Highcharts.SVGElement#destroy
  4598. */
  4599. destroy: function () {
  4600. var wrapper = this,
  4601. element = wrapper.element || {},
  4602. renderer = wrapper.renderer,
  4603. parentToClean =
  4604. renderer.isSVG &&
  4605. element.nodeName === 'SPAN' &&
  4606. wrapper.parentGroup,
  4607. grandParent,
  4608. ownerSVGElement = element.ownerSVGElement,
  4609. i,
  4610. clipPath = wrapper.clipPath;
  4611. // remove events
  4612. element.onclick = element.onmouseout = element.onmouseover =
  4613. element.onmousemove = element.point = null;
  4614. stop(wrapper); // stop running animations
  4615. if (clipPath && ownerSVGElement) {
  4616. // Look for existing references to this clipPath and remove them
  4617. // before destroying the element (#6196).
  4618. // The upper case version is for Edge
  4619. [].forEach.call(
  4620. ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'),
  4621. function (el) {
  4622. var clipPathAttr = el.getAttribute('clip-path'),
  4623. clipPathId = clipPath.element.id;
  4624. // Include the closing paranthesis in the test to rule out
  4625. // id's from 10 and above (#6550). Edge puts quotes inside
  4626. // the url, others not.
  4627. if (
  4628. clipPathAttr.indexOf('(#' + clipPathId + ')') > -1 ||
  4629. clipPathAttr.indexOf('("#' + clipPathId + '")') > -1
  4630. ) {
  4631. el.removeAttribute('clip-path');
  4632. }
  4633. }
  4634. );
  4635. wrapper.clipPath = clipPath.destroy();
  4636. }
  4637. // Destroy stops in case this is a gradient object
  4638. if (wrapper.stops) {
  4639. for (i = 0; i < wrapper.stops.length; i++) {
  4640. wrapper.stops[i] = wrapper.stops[i].destroy();
  4641. }
  4642. wrapper.stops = null;
  4643. }
  4644. // remove element
  4645. wrapper.safeRemoveChild(element);
  4646. if (!renderer.styledMode) {
  4647. wrapper.destroyShadows();
  4648. }
  4649. // In case of useHTML, clean up empty containers emulating SVG groups
  4650. // (#1960, #2393, #2697).
  4651. while (
  4652. parentToClean &&
  4653. parentToClean.div &&
  4654. parentToClean.div.childNodes.length === 0
  4655. ) {
  4656. grandParent = parentToClean.parentGroup;
  4657. wrapper.safeRemoveChild(parentToClean.div);
  4658. delete parentToClean.div;
  4659. parentToClean = grandParent;
  4660. }
  4661. // remove from alignObjects
  4662. if (wrapper.alignTo) {
  4663. erase(renderer.alignedObjects, wrapper);
  4664. }
  4665. objectEach(wrapper, function (val, key) {
  4666. delete wrapper[key];
  4667. });
  4668. return null;
  4669. },
  4670. /**
  4671. * Add a shadow to the element. Must be called after the element is added to
  4672. * the DOM. In styled mode, this method is not used, instead use `defs` and
  4673. * filters.
  4674. *
  4675. * @example
  4676. * renderer.rect(10, 100, 100, 100)
  4677. * .attr({ fill: 'red' })
  4678. * .shadow(true);
  4679. *
  4680. * @function Highcharts.SVGElement#shadow
  4681. *
  4682. * @param {boolean|Highcharts.ShadowOptionsObject} shadowOptions
  4683. * The shadow options. If `true`, the default options are applied. If
  4684. * `false`, the current shadow will be removed.
  4685. *
  4686. * @param {Highcharts.SVGElement} [group]
  4687. * The SVG group element where the shadows will be applied. The
  4688. * default is to add it to the same parent as the current element.
  4689. * Internally, this is ised for pie slices, where all the shadows are
  4690. * added to an element behind all the slices.
  4691. *
  4692. * @param {boolean} [cutOff]
  4693. * Used internally for column shadows.
  4694. *
  4695. * @return {Highcharts.SVGElement}
  4696. * Returns the SVGElement for chaining.
  4697. */
  4698. shadow: function (shadowOptions, group, cutOff) {
  4699. var shadows = [],
  4700. i,
  4701. shadow,
  4702. element = this.element,
  4703. strokeWidth,
  4704. shadowWidth,
  4705. shadowElementOpacity,
  4706. // compensate for inverted plot area
  4707. transform;
  4708. if (!shadowOptions) {
  4709. this.destroyShadows();
  4710. } else if (!this.shadows) {
  4711. shadowWidth = pick(shadowOptions.width, 3);
  4712. shadowElementOpacity = (shadowOptions.opacity || 0.15) /
  4713. shadowWidth;
  4714. transform = this.parentInverted ?
  4715. '(-1,-1)' :
  4716. '(' + pick(shadowOptions.offsetX, 1) + ', ' +
  4717. pick(shadowOptions.offsetY, 1) + ')';
  4718. for (i = 1; i <= shadowWidth; i++) {
  4719. shadow = element.cloneNode(0);
  4720. strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
  4721. attr(shadow, {
  4722. 'stroke':
  4723. shadowOptions.color || '#000000',
  4724. 'stroke-opacity': shadowElementOpacity * i,
  4725. 'stroke-width': strokeWidth,
  4726. 'transform': 'translate' + transform,
  4727. 'fill': 'none'
  4728. });
  4729. shadow.setAttribute(
  4730. 'class',
  4731. (shadow.getAttribute('class') || '') + ' highcharts-shadow'
  4732. );
  4733. if (cutOff) {
  4734. attr(
  4735. shadow,
  4736. 'height',
  4737. Math.max(attr(shadow, 'height') - strokeWidth, 0)
  4738. );
  4739. shadow.cutHeight = strokeWidth;
  4740. }
  4741. if (group) {
  4742. group.element.appendChild(shadow);
  4743. } else if (element.parentNode) {
  4744. element.parentNode.insertBefore(shadow, element);
  4745. }
  4746. shadows.push(shadow);
  4747. }
  4748. this.shadows = shadows;
  4749. }
  4750. return this;
  4751. },
  4752. /**
  4753. * Destroy shadows on the element.
  4754. *
  4755. * @private
  4756. * @function Highcharts.SVGElement#destroyShadows
  4757. */
  4758. destroyShadows: function () {
  4759. (this.shadows || []).forEach(function (shadow) {
  4760. this.safeRemoveChild(shadow);
  4761. }, this);
  4762. this.shadows = undefined;
  4763. },
  4764. /**
  4765. * @private
  4766. * @function Highcharts.SVGElement#xGetter
  4767. *
  4768. * @param {string} key
  4769. *
  4770. * @return {number|string|null}
  4771. */
  4772. xGetter: function (key) {
  4773. if (this.element.nodeName === 'circle') {
  4774. if (key === 'x') {
  4775. key = 'cx';
  4776. } else if (key === 'y') {
  4777. key = 'cy';
  4778. }
  4779. }
  4780. return this._defaultGetter(key);
  4781. },
  4782. /**
  4783. * Get the current value of an attribute or pseudo attribute,
  4784. * used mainly for animation. Called internally from
  4785. * the {@link Highcharts.SVGRenderer#attr} function.
  4786. *
  4787. * @private
  4788. * @function Highcharts.SVGElement#_defaultGetter
  4789. *
  4790. * @param {string} key
  4791. * Property key.
  4792. *
  4793. * @return {number|string|null}
  4794. * Property value.
  4795. */
  4796. _defaultGetter: function (key) {
  4797. var ret = pick(
  4798. this[key + 'Value'], // align getter
  4799. this[key],
  4800. this.element ? this.element.getAttribute(key) : null,
  4801. 0
  4802. );
  4803. if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
  4804. ret = parseFloat(ret);
  4805. }
  4806. return ret;
  4807. },
  4808. /**
  4809. * @private
  4810. * @function Highcharts.SVGElement#dSettter
  4811. *
  4812. * @param {number|string|Highcharts.SVGPathArray} value
  4813. *
  4814. * @param {string} key
  4815. *
  4816. * @param {Highcharts.SVGDOMElement} element
  4817. */
  4818. dSetter: function (value, key, element) {
  4819. if (value && value.join) { // join path
  4820. value = value.join(' ');
  4821. }
  4822. if (/(NaN| {2}|^$)/.test(value)) {
  4823. value = 'M 0 0';
  4824. }
  4825. // Check for cache before resetting. Resetting causes disturbance in the
  4826. // DOM, causing flickering in some cases in Edge/IE (#6747). Also
  4827. // possible performance gain.
  4828. if (this[key] !== value) {
  4829. element.setAttribute(key, value);
  4830. this[key] = value;
  4831. }
  4832. },
  4833. /**
  4834. * @private
  4835. * @function Highcharts.SVGElement#dashstyleSetter
  4836. *
  4837. * @param {string} value
  4838. */
  4839. dashstyleSetter: function (value) {
  4840. var i,
  4841. strokeWidth = this['stroke-width'];
  4842. // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
  4843. // strokeWidth function, we should be able to use that instead.
  4844. if (strokeWidth === 'inherit') {
  4845. strokeWidth = 1;
  4846. }
  4847. value = value && value.toLowerCase();
  4848. if (value) {
  4849. value = value
  4850. .replace('shortdashdotdot', '3,1,1,1,1,1,')
  4851. .replace('shortdashdot', '3,1,1,1')
  4852. .replace('shortdot', '1,1,')
  4853. .replace('shortdash', '3,1,')
  4854. .replace('longdash', '8,3,')
  4855. .replace(/dot/g, '1,3,')
  4856. .replace('dash', '4,3,')
  4857. .replace(/,$/, '')
  4858. .split(','); // ending comma
  4859. i = value.length;
  4860. while (i--) {
  4861. value[i] = pInt(value[i]) * strokeWidth;
  4862. }
  4863. value = value.join(',')
  4864. .replace(/NaN/g, 'none'); // #3226
  4865. this.element.setAttribute('stroke-dasharray', value);
  4866. }
  4867. },
  4868. /**
  4869. * @private
  4870. * @function Highcharts.SVGElement#alignSetter
  4871. *
  4872. * @param {"start"|"middle"|"end"} value
  4873. */
  4874. alignSetter: function (value) {
  4875. var convert = { left: 'start', center: 'middle', right: 'end' };
  4876. this.alignValue = value;
  4877. this.element.setAttribute('text-anchor', convert[value]);
  4878. },
  4879. /**
  4880. * @private
  4881. * @function Highcharts.SVGElement#opacitySetter
  4882. *
  4883. * @param {string} value
  4884. *
  4885. * @param {string} key
  4886. *
  4887. * @param {Highcharts.SVGDOMElement} element
  4888. */
  4889. opacitySetter: function (value, key, element) {
  4890. this[key] = value;
  4891. element.setAttribute(key, value);
  4892. },
  4893. /**
  4894. * @private
  4895. * @function Highcharts.SVGElement#titleSetter
  4896. *
  4897. * @param {string} value
  4898. */
  4899. titleSetter: function (value) {
  4900. var titleNode = this.element.getElementsByTagName('title')[0];
  4901. if (!titleNode) {
  4902. titleNode = doc.createElementNS(this.SVG_NS, 'title');
  4903. this.element.appendChild(titleNode);
  4904. }
  4905. // Remove text content if it exists
  4906. if (titleNode.firstChild) {
  4907. titleNode.removeChild(titleNode.firstChild);
  4908. }
  4909. titleNode.appendChild(
  4910. doc.createTextNode(
  4911. // #3276, #3895
  4912. (String(pick(value), ''))
  4913. .replace(/<[^>]*>/g, '')
  4914. .replace(/&lt;/g, '<')
  4915. .replace(/&gt;/g, '>')
  4916. )
  4917. );
  4918. },
  4919. /**
  4920. * @private
  4921. * @function Highcharts.SVGElement#textSetter
  4922. *
  4923. * @param {string} value
  4924. */
  4925. textSetter: function (value) {
  4926. if (value !== this.textStr) {
  4927. // Delete bBox memo when the text changes
  4928. delete this.bBox;
  4929. this.textStr = value;
  4930. if (this.added) {
  4931. this.renderer.buildText(this);
  4932. }
  4933. }
  4934. },
  4935. /**
  4936. * @private
  4937. * @function Highcharts.SVGElement#fillSetter
  4938. *
  4939. * @param {Highcharts.Color|Highcharts.ColorString} value
  4940. *
  4941. * @param {string} key
  4942. *
  4943. * @param {Highcharts.SVGDOMElement} element
  4944. */
  4945. fillSetter: function (value, key, element) {
  4946. if (typeof value === 'string') {
  4947. element.setAttribute(key, value);
  4948. } else if (value) {
  4949. this.complexColor(value, key, element);
  4950. }
  4951. },
  4952. /**
  4953. * @private
  4954. * @function Highcharts.SVGElement#visibilitySetter
  4955. *
  4956. * @param {string} value
  4957. *
  4958. * @param {string} key
  4959. *
  4960. * @param {Highcharts.SVGDOMElement} element
  4961. */
  4962. visibilitySetter: function (value, key, element) {
  4963. // IE9-11 doesn't handle visibilty:inherit well, so we remove the
  4964. // attribute instead (#2881, #3909)
  4965. if (value === 'inherit') {
  4966. element.removeAttribute(key);
  4967. } else if (this[key] !== value) { // #6747
  4968. element.setAttribute(key, value);
  4969. }
  4970. this[key] = value;
  4971. },
  4972. /**
  4973. * @private
  4974. * @function Highcharts.SVGElement#zIndexSetter
  4975. *
  4976. * @param {string} value
  4977. *
  4978. * @param {string} key
  4979. *
  4980. * @return {boolean}
  4981. */
  4982. zIndexSetter: function (value, key) {
  4983. var renderer = this.renderer,
  4984. parentGroup = this.parentGroup,
  4985. parentWrapper = parentGroup || renderer,
  4986. parentNode = parentWrapper.element || renderer.box,
  4987. childNodes,
  4988. otherElement,
  4989. otherZIndex,
  4990. element = this.element,
  4991. inserted,
  4992. undefinedOtherZIndex,
  4993. svgParent = parentNode === renderer.box,
  4994. run = this.added,
  4995. i;
  4996. if (defined(value)) {
  4997. // So we can read it for other elements in the group
  4998. element.setAttribute('data-z-index', value);
  4999. value = +value;
  5000. if (this[key] === value) { // Only update when needed (#3865)
  5001. run = false;
  5002. }
  5003. } else if (defined(this[key])) {
  5004. element.removeAttribute('data-z-index');
  5005. }
  5006. this[key] = value;
  5007. // Insert according to this and other elements' zIndex. Before .add() is
  5008. // called, nothing is done. Then on add, or by later calls to
  5009. // zIndexSetter, the node is placed on the right place in the DOM.
  5010. if (run) {
  5011. value = this.zIndex;
  5012. if (value && parentGroup) {
  5013. parentGroup.handleZ = true;
  5014. }
  5015. childNodes = parentNode.childNodes;
  5016. for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
  5017. otherElement = childNodes[i];
  5018. otherZIndex = otherElement.getAttribute('data-z-index');
  5019. undefinedOtherZIndex = !defined(otherZIndex);
  5020. if (otherElement !== element) {
  5021. if (
  5022. // Negative zIndex versus no zIndex:
  5023. // On all levels except the highest. If the parent is
  5024. // <svg>, then we don't want to put items before <desc>
  5025. // or <defs>
  5026. (value < 0 && undefinedOtherZIndex && !svgParent && !i)
  5027. ) {
  5028. parentNode.insertBefore(element, childNodes[i]);
  5029. inserted = true;
  5030. } else if (
  5031. // Insert after the first element with a lower zIndex
  5032. pInt(otherZIndex) <= value ||
  5033. // If negative zIndex, add this before first undefined
  5034. // zIndex element
  5035. (
  5036. undefinedOtherZIndex &&
  5037. (!defined(value) || value >= 0)
  5038. )
  5039. ) {
  5040. parentNode.insertBefore(
  5041. element,
  5042. childNodes[i + 1] || null // null for oldIE export
  5043. );
  5044. inserted = true;
  5045. }
  5046. }
  5047. }
  5048. if (!inserted) {
  5049. parentNode.insertBefore(
  5050. element,
  5051. childNodes[svgParent ? 3 : 0] || null // null for oldIE
  5052. );
  5053. inserted = true;
  5054. }
  5055. }
  5056. return inserted;
  5057. },
  5058. /**
  5059. * @private
  5060. * @function Highcharts.SVGElement#_defaultSetter
  5061. *
  5062. * @param {string} value
  5063. *
  5064. * @param {string} key
  5065. *
  5066. * @param {Highcharts.SVGDOMElement} element
  5067. */
  5068. _defaultSetter: function (value, key, element) {
  5069. element.setAttribute(key, value);
  5070. }
  5071. });
  5072. // Some shared setters and getters
  5073. SVGElement.prototype.yGetter =
  5074. SVGElement.prototype.xGetter;
  5075. SVGElement.prototype.translateXSetter =
  5076. SVGElement.prototype.translateYSetter =
  5077. SVGElement.prototype.rotationSetter =
  5078. SVGElement.prototype.verticalAlignSetter =
  5079. SVGElement.prototype.rotationOriginXSetter =
  5080. SVGElement.prototype.rotationOriginYSetter =
  5081. SVGElement.prototype.scaleXSetter =
  5082. SVGElement.prototype.scaleYSetter =
  5083. SVGElement.prototype.matrixSetter = function (value, key) {
  5084. this[key] = value;
  5085. this.doTransform = true;
  5086. };
  5087. // WebKit and Batik have problems with a stroke-width of zero, so in this case
  5088. // we remove the stroke attribute altogether. #1270, #1369, #3065, #3072.
  5089. SVGElement.prototype['stroke-widthSetter'] =
  5090. /**
  5091. * @private
  5092. * @function Highcharts.SVGElement#strokeSetter
  5093. *
  5094. * @param {number|string} value
  5095. *
  5096. * @param {string} key
  5097. *
  5098. * @param {Highcharts.SVGDOMElement} element
  5099. */
  5100. SVGElement.prototype.strokeSetter = function (value, key, element) {
  5101. this[key] = value;
  5102. // Only apply the stroke attribute if the stroke width is defined and larger
  5103. // than 0
  5104. if (this.stroke && this['stroke-width']) {
  5105. // Use prototype as instance may be overridden
  5106. SVGElement.prototype.fillSetter.call(
  5107. this,
  5108. this.stroke,
  5109. 'stroke',
  5110. element
  5111. );
  5112. element.setAttribute('stroke-width', this['stroke-width']);
  5113. this.hasStroke = true;
  5114. } else if (key === 'stroke-width' && value === 0 && this.hasStroke) {
  5115. element.removeAttribute('stroke');
  5116. this.hasStroke = false;
  5117. }
  5118. };
  5119. /**
  5120. * Allows direct access to the Highcharts rendering layer in order to draw
  5121. * primitive shapes like circles, rectangles, paths or text directly on a chart,
  5122. * or independent from any chart. The SVGRenderer represents a wrapper object
  5123. * for SVG in modern browsers. Through the VMLRenderer, part of the `oldie.js`
  5124. * module, it also brings vector graphics to IE <= 8.
  5125. *
  5126. * An existing chart's renderer can be accessed through {@link Chart.renderer}.
  5127. * The renderer can also be used completely decoupled from a chart.
  5128. *
  5129. * @sample highcharts/members/renderer-on-chart
  5130. * Annotating a chart programmatically.
  5131. * @sample highcharts/members/renderer-basic
  5132. * Independent SVG drawing.
  5133. *
  5134. * @example
  5135. * // Use directly without a chart object.
  5136. * var renderer = new Highcharts.Renderer(parentNode, 600, 400);
  5137. *
  5138. * @class
  5139. * @name Highcharts.SVGRenderer
  5140. *
  5141. * @param {Highcharts.HTMLDOMElement} container
  5142. * Where to put the SVG in the web page.
  5143. *
  5144. * @param {number} width
  5145. * The width of the SVG.
  5146. *
  5147. * @param {number} height
  5148. * The height of the SVG.
  5149. *
  5150. * @param {boolean} [forExport=false]
  5151. * Whether the rendered content is intended for export.
  5152. *
  5153. * @param {boolean} [allowHTML=true]
  5154. * Whether the renderer is allowed to include HTML text, which will be
  5155. * projected on top of the SVG.
  5156. */
  5157. SVGRenderer = H.SVGRenderer = function () {
  5158. this.init.apply(this, arguments);
  5159. };
  5160. extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ {
  5161. /**
  5162. * A pointer to the renderer's associated Element class. The VMLRenderer
  5163. * will have a pointer to VMLElement here.
  5164. *
  5165. * @name Highcharts.SVGRenderer#Element
  5166. * @type {Highcharts.SVGElement}
  5167. */
  5168. Element: SVGElement,
  5169. SVG_NS: SVG_NS,
  5170. /**
  5171. * Initialize the SVGRenderer. Overridable initiator function that takes
  5172. * the same parameters as the constructor.
  5173. *
  5174. * @function Highcharts.SVGRenderer#init
  5175. *
  5176. * @param {Highcharts.HTMLDOMElement} container
  5177. * Where to put the SVG in the web page.
  5178. *
  5179. * @param {number} width
  5180. * The width of the SVG.
  5181. *
  5182. * @param {number} height
  5183. * The height of the SVG.
  5184. *
  5185. * @param {boolean} [forExport=false]
  5186. * Whether the rendered content is intended for export.
  5187. *
  5188. * @param {boolean} [allowHTML=true]
  5189. * Whether the renderer is allowed to include HTML text, which will
  5190. * be projected on top of the SVG.
  5191. *
  5192. * @param {boolean} [styledMode=false]
  5193. * Whether the renderer belongs to a chart that is in styled mode.
  5194. * If it does, it will avoid setting presentational attributes in
  5195. * some cases, but not when set explicitly through `.attr` and `.css`
  5196. * etc.
  5197. *
  5198. * @return {void}
  5199. */
  5200. init: function (
  5201. container,
  5202. width,
  5203. height,
  5204. style,
  5205. forExport,
  5206. allowHTML,
  5207. styledMode
  5208. ) {
  5209. var renderer = this,
  5210. boxWrapper,
  5211. element,
  5212. desc;
  5213. boxWrapper = renderer.createElement('svg')
  5214. .attr({
  5215. 'version': '1.1',
  5216. 'class': 'highcharts-root'
  5217. });
  5218. if (!styledMode) {
  5219. boxWrapper.css(this.getStyle(style));
  5220. }
  5221. element = boxWrapper.element;
  5222. container.appendChild(element);
  5223. // Always use ltr on the container, otherwise text-anchor will be
  5224. // flipped and text appear outside labels, buttons, tooltip etc (#3482)
  5225. attr(container, 'dir', 'ltr');
  5226. // For browsers other than IE, add the namespace attribute (#1978)
  5227. if (container.innerHTML.indexOf('xmlns') === -1) {
  5228. attr(element, 'xmlns', this.SVG_NS);
  5229. }
  5230. // object properties
  5231. renderer.isSVG = true;
  5232. /**
  5233. * The root `svg` node of the renderer.
  5234. *
  5235. * @name Highcharts.SVGRenderer#box
  5236. * @type {Highcharts.SVGDOMElement}
  5237. */
  5238. this.box = element;
  5239. /**
  5240. * The wrapper for the root `svg` node of the renderer.
  5241. *
  5242. * @name Highcharts.SVGRenderer#boxWrapper
  5243. * @type {Highcharts.SVGElement}
  5244. */
  5245. this.boxWrapper = boxWrapper;
  5246. renderer.alignedObjects = [];
  5247. /**
  5248. * Page url used for internal references.
  5249. *
  5250. * @private
  5251. * @name Highcharts.SVGRenderer#url
  5252. * @type {string}
  5253. */
  5254. // #24, #672, #1070
  5255. this.url = (
  5256. (isFirefox || isWebKit) &&
  5257. doc.getElementsByTagName('base').length
  5258. ) ?
  5259. win.location.href
  5260. .split('#')[0] // remove the hash
  5261. .replace(/<[^>]*>/g, '') // wing cut HTML
  5262. // escape parantheses and quotes
  5263. .replace(/([\('\)])/g, '\\$1')
  5264. // replace spaces (needed for Safari only)
  5265. .replace(/ /g, '%20') :
  5266. '';
  5267. // Add description
  5268. desc = this.createElement('desc').add();
  5269. desc.element.appendChild(
  5270. doc.createTextNode('Created with Highcharts 7.0.2')
  5271. );
  5272. /**
  5273. * A pointer to the `defs` node of the root SVG.
  5274. *
  5275. * @name Highcharts.SVGRenderer#defs
  5276. * @type {Highcharts.SVGElement}
  5277. */
  5278. renderer.defs = this.createElement('defs').add();
  5279. renderer.allowHTML = allowHTML;
  5280. renderer.forExport = forExport;
  5281. renderer.styledMode = styledMode;
  5282. renderer.gradients = {}; // Object where gradient SvgElements are stored
  5283. renderer.cache = {}; // Cache for numerical bounding boxes
  5284. renderer.cacheKeys = [];
  5285. renderer.imgCount = 0;
  5286. renderer.setSize(width, height, false);
  5287. // Issue 110 workaround:
  5288. // In Firefox, if a div is positioned by percentage, its pixel position
  5289. // may land between pixels. The container itself doesn't display this,
  5290. // but an SVG element inside this container will be drawn at subpixel
  5291. // precision. In order to draw sharp lines, this must be compensated
  5292. // for. This doesn't seem to work inside iframes though (like in
  5293. // jsFiddle).
  5294. var subPixelFix, rect;
  5295. if (isFirefox && container.getBoundingClientRect) {
  5296. subPixelFix = function () {
  5297. css(container, { left: 0, top: 0 });
  5298. rect = container.getBoundingClientRect();
  5299. css(container, {
  5300. left: (Math.ceil(rect.left) - rect.left) + 'px',
  5301. top: (Math.ceil(rect.top) - rect.top) + 'px'
  5302. });
  5303. };
  5304. // run the fix now
  5305. subPixelFix();
  5306. // run it on resize
  5307. renderer.unSubPixelFix = addEvent(win, 'resize', subPixelFix);
  5308. }
  5309. },
  5310. /**
  5311. * General method for adding a definition to the SVG `defs` tag. Can be used
  5312. * for gradients, fills, filters etc. Styled mode only. A hook for adding
  5313. * general definitions to the SVG's defs tag. Definitions can be referenced
  5314. * from the CSS by its `id`. Read more in
  5315. * [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  5316. * Styled mode only.
  5317. *
  5318. * @function Highcharts.SVGRenderer#definition
  5319. *
  5320. * @param {Highcharts.SVGDefinitionObject} def
  5321. * A serialized form of an SVG definition, including children.
  5322. *
  5323. * @return {Highcharts.SVGElement}
  5324. * The inserted node.
  5325. */
  5326. definition: function (def) {
  5327. var ren = this;
  5328. function recurse(config, parent) {
  5329. var ret;
  5330. splat(config).forEach(function (item) {
  5331. var node = ren.createElement(item.tagName),
  5332. attr = {};
  5333. // Set attributes
  5334. objectEach(item, function (val, key) {
  5335. if (
  5336. key !== 'tagName' &&
  5337. key !== 'children' &&
  5338. key !== 'textContent'
  5339. ) {
  5340. attr[key] = val;
  5341. }
  5342. });
  5343. node.attr(attr);
  5344. // Add to the tree
  5345. node.add(parent || ren.defs);
  5346. // Add text content
  5347. if (item.textContent) {
  5348. node.element.appendChild(
  5349. doc.createTextNode(item.textContent)
  5350. );
  5351. }
  5352. // Recurse
  5353. recurse(item.children || [], node);
  5354. ret = node;
  5355. });
  5356. // Return last node added (on top level it's the only one)
  5357. return ret;
  5358. }
  5359. return recurse(def);
  5360. },
  5361. /**
  5362. * Get the global style setting for the renderer.
  5363. *
  5364. * @private
  5365. * @function Highcharts.SVGRenderer#getStyle
  5366. *
  5367. * @param {Highcharts.CSSObject} style
  5368. * Style settings.
  5369. *
  5370. * @return {Highcharts.CSSObject}
  5371. * The style settings mixed with defaults.
  5372. */
  5373. getStyle: function (style) {
  5374. this.style = extend({
  5375. fontFamily: '"Lucida Grande", "Lucida Sans Unicode", ' +
  5376. 'Arial, Helvetica, sans-serif',
  5377. fontSize: '12px'
  5378. }, style);
  5379. return this.style;
  5380. },
  5381. /**
  5382. * Apply the global style on the renderer, mixed with the default styles.
  5383. *
  5384. * @function Highcharts.SVGRenderer#setStyle
  5385. *
  5386. * @param {Highcharts.CSSObject} style
  5387. * CSS to apply.
  5388. */
  5389. setStyle: function (style) {
  5390. this.boxWrapper.css(this.getStyle(style));
  5391. },
  5392. /**
  5393. * Detect whether the renderer is hidden. This happens when one of the
  5394. * parent elements has `display: none`. Used internally to detect when we
  5395. * needto render preliminarily in another div to get the text bounding boxes
  5396. * right.
  5397. *
  5398. * @function Highcharts.SVGRenderer#isHidden
  5399. *
  5400. * @return {boolean}
  5401. * True if it is hidden.
  5402. */
  5403. isHidden: function () { // #608
  5404. return !this.boxWrapper.getBBox().width;
  5405. },
  5406. /**
  5407. * Destroys the renderer and its allocated members.
  5408. *
  5409. * @function Highcharts.SVGRenderer#destroy
  5410. */
  5411. destroy: function () {
  5412. var renderer = this,
  5413. rendererDefs = renderer.defs;
  5414. renderer.box = null;
  5415. renderer.boxWrapper = renderer.boxWrapper.destroy();
  5416. // Call destroy on all gradient elements
  5417. destroyObjectProperties(renderer.gradients || {});
  5418. renderer.gradients = null;
  5419. // Defs are null in VMLRenderer
  5420. // Otherwise, destroy them here.
  5421. if (rendererDefs) {
  5422. renderer.defs = rendererDefs.destroy();
  5423. }
  5424. // Remove sub pixel fix handler (#982)
  5425. if (renderer.unSubPixelFix) {
  5426. renderer.unSubPixelFix();
  5427. }
  5428. renderer.alignedObjects = null;
  5429. return null;
  5430. },
  5431. /**
  5432. * Create a wrapper for an SVG element. Serves as a factory for
  5433. * {@link SVGElement}, but this function is itself mostly called from
  5434. * primitive factories like {@link SVGRenderer#path}, {@link
  5435. * SVGRenderer#rect} or {@link SVGRenderer#text}.
  5436. *
  5437. * @function Highcharts.SVGRenderer#createElement
  5438. *
  5439. * @param {string} nodeName
  5440. * The node name, for example `rect`, `g` etc.
  5441. *
  5442. * @return {Highcharts.SVGElement}
  5443. * The generated SVGElement.
  5444. */
  5445. createElement: function (nodeName) {
  5446. var wrapper = new this.Element();
  5447. wrapper.init(this, nodeName);
  5448. return wrapper;
  5449. },
  5450. /**
  5451. * Dummy function for plugins, called every time the renderer is updated.
  5452. * Prior to Highcharts 5, this was used for the canvg renderer.
  5453. *
  5454. * @deprecated
  5455. * @function Highcharts.SVGRenderer#draw
  5456. */
  5457. draw: noop,
  5458. /**
  5459. * Get converted radial gradient attributes according to the radial
  5460. * reference. Used internally from the {@link SVGElement#colorGradient}
  5461. * function.
  5462. *
  5463. * @private
  5464. * @function Highcharts.SVGRenderer#getRadialAttr
  5465. *
  5466. * @param {Array<number>} radialReference
  5467. *
  5468. * @param {Highcharts.SVGAttributes} gradAttr
  5469. *
  5470. * @return {Highcharts.SVGAttributes}
  5471. */
  5472. getRadialAttr: function (radialReference, gradAttr) {
  5473. return {
  5474. cx: (radialReference[0] - radialReference[2] / 2) +
  5475. gradAttr.cx * radialReference[2],
  5476. cy: (radialReference[1] - radialReference[2] / 2) +
  5477. gradAttr.cy * radialReference[2],
  5478. r: gradAttr.r * radialReference[2]
  5479. };
  5480. },
  5481. /**
  5482. * Truncate the text node contents to a given length. Used when the css
  5483. * width is set. If the `textOverflow` is `ellipsis`, the text is truncated
  5484. * character by character to the given length. If not, the text is
  5485. * word-wrapped line by line.
  5486. *
  5487. * @private
  5488. * @function Highcharts.SVGRenderer#truncate
  5489. *
  5490. * @param {Highcharts.SVGElement} wrapper
  5491. *
  5492. * @param {Highcharts.SVGDOMElement} tspan
  5493. *
  5494. * @param {string} text
  5495. *
  5496. * @param {Array.<string>} words
  5497. *
  5498. * @param {number} width
  5499. *
  5500. * @param {Function} getString
  5501. *
  5502. * @return {boolean}
  5503. * True if tspan is too long.
  5504. */
  5505. truncate: function (
  5506. wrapper,
  5507. tspan,
  5508. text,
  5509. words,
  5510. startAt,
  5511. width,
  5512. getString
  5513. ) {
  5514. var renderer = this,
  5515. rotation = wrapper.rotation,
  5516. str,
  5517. // Word wrap can not be truncated to shorter than one word, ellipsis
  5518. // text can be completely blank.
  5519. minIndex = words ? 1 : 0,
  5520. maxIndex = (text || words).length,
  5521. currentIndex = maxIndex,
  5522. // Cache the lengths to avoid checking the same twice
  5523. lengths = [],
  5524. updateTSpan = function (s) {
  5525. if (tspan.firstChild) {
  5526. tspan.removeChild(tspan.firstChild);
  5527. }
  5528. if (s) {
  5529. tspan.appendChild(doc.createTextNode(s));
  5530. }
  5531. },
  5532. getSubStringLength = function (charEnd, concatenatedEnd) {
  5533. // charEnd is useed when finding the character-by-character
  5534. // break for ellipsis, concatenatedEnd is used for word-by-word
  5535. // break for word wrapping.
  5536. var end = concatenatedEnd || charEnd;
  5537. if (lengths[end] === undefined) {
  5538. // Modern browsers
  5539. if (tspan.getSubStringLength) {
  5540. // Fails with DOM exception on unit-tests/legend/members
  5541. // of unknown reason. Desired width is 0, text content
  5542. // is "5" and end is 1.
  5543. try {
  5544. lengths[end] = startAt + tspan.getSubStringLength(
  5545. 0,
  5546. words ? end + 1 : end
  5547. );
  5548. } catch (e) {}
  5549. // Legacy
  5550. } else if (renderer.getSpanWidth) { // #9058 jsdom
  5551. updateTSpan(getString(text || words, charEnd));
  5552. lengths[end] = startAt +
  5553. renderer.getSpanWidth(wrapper, tspan);
  5554. }
  5555. }
  5556. return lengths[end];
  5557. },
  5558. actualWidth,
  5559. truncated;
  5560. wrapper.rotation = 0; // discard rotation when computing box
  5561. actualWidth = getSubStringLength(tspan.textContent.length);
  5562. truncated = startAt + actualWidth > width;
  5563. if (truncated) {
  5564. // Do a binary search for the index where to truncate the text
  5565. while (minIndex <= maxIndex) {
  5566. currentIndex = Math.ceil((minIndex + maxIndex) / 2);
  5567. // When checking words for word-wrap, we need to build the
  5568. // string and measure the subStringLength at the concatenated
  5569. // word length.
  5570. if (words) {
  5571. str = getString(words, currentIndex);
  5572. }
  5573. actualWidth = getSubStringLength(
  5574. currentIndex,
  5575. str && str.length - 1
  5576. );
  5577. if (minIndex === maxIndex) {
  5578. // Complete
  5579. minIndex = maxIndex + 1;
  5580. } else if (actualWidth > width) {
  5581. // Too large. Set max index to current.
  5582. maxIndex = currentIndex - 1;
  5583. } else {
  5584. // Within width. Set min index to current.
  5585. minIndex = currentIndex;
  5586. }
  5587. }
  5588. // If max index was 0 it means the shortest possible text was also
  5589. // too large. For ellipsis that means only the ellipsis, while for
  5590. // word wrap it means the whole first word.
  5591. if (maxIndex === 0) {
  5592. // Remove ellipsis
  5593. updateTSpan('');
  5594. // If the new text length is one less than the original, we don't
  5595. // need the ellipsis
  5596. } else if (!(text && maxIndex === text.length - 1)) {
  5597. updateTSpan(str || getString(text || words, currentIndex));
  5598. }
  5599. }
  5600. // When doing line wrapping, prepare for the next line by removing the
  5601. // items from this line.
  5602. if (words) {
  5603. words.splice(0, currentIndex);
  5604. }
  5605. wrapper.actualWidth = actualWidth;
  5606. wrapper.rotation = rotation; // Apply rotation again.
  5607. return truncated;
  5608. },
  5609. /**
  5610. * A collection of characters mapped to HTML entities. When `useHTML` on an
  5611. * element is true, these entities will be rendered correctly by HTML. In
  5612. * the SVG pseudo-HTML, they need to be unescaped back to simple characters,
  5613. * so for example `&lt;` will render as `<`.
  5614. *
  5615. * @example
  5616. * // Add support for unescaping quotes
  5617. * Highcharts.SVGRenderer.prototype.escapes['"'] = '&quot;';
  5618. *
  5619. * @name Highcharts.SVGRenderer#escapes
  5620. * @type {Highcharts.Dictionary<string>}
  5621. */
  5622. escapes: {
  5623. '&': '&amp;',
  5624. '<': '&lt;',
  5625. '>': '&gt;',
  5626. "'": '&#39;', // eslint-disable-line quotes
  5627. '"': '&quot;'
  5628. },
  5629. /**
  5630. * Parse a simple HTML string into SVG tspans. Called internally when text
  5631. * is set on an SVGElement. The function supports a subset of HTML tags, CSS
  5632. * text features like `width`, `text-overflow`, `white-space`, and also
  5633. * attributes like `href` and `style`.
  5634. *
  5635. * @private
  5636. * @function Highcharts.SVGRenderer#buildText
  5637. *
  5638. * @param {Highcharts.SVGElement} wrapper
  5639. * The parent SVGElement.
  5640. */
  5641. buildText: function (wrapper) {
  5642. var textNode = wrapper.element,
  5643. renderer = this,
  5644. forExport = renderer.forExport,
  5645. textStr = pick(wrapper.textStr, '').toString(),
  5646. hasMarkup = textStr.indexOf('<') !== -1,
  5647. lines,
  5648. childNodes = textNode.childNodes,
  5649. truncated,
  5650. parentX = attr(textNode, 'x'),
  5651. textStyles = wrapper.styles,
  5652. width = wrapper.textWidth,
  5653. textLineHeight = textStyles && textStyles.lineHeight,
  5654. textOutline = textStyles && textStyles.textOutline,
  5655. ellipsis = textStyles && textStyles.textOverflow === 'ellipsis',
  5656. noWrap = textStyles && textStyles.whiteSpace === 'nowrap',
  5657. fontSize = textStyles && textStyles.fontSize,
  5658. textCache,
  5659. isSubsequentLine,
  5660. i = childNodes.length,
  5661. tempParent = width && !wrapper.added && this.box,
  5662. getLineHeight = function (tspan) {
  5663. var fontSizeStyle;
  5664. if (!renderer.styledMode) {
  5665. fontSizeStyle =
  5666. /(px|em)$/.test(tspan && tspan.style.fontSize) ?
  5667. tspan.style.fontSize :
  5668. (fontSize || renderer.style.fontSize || 12);
  5669. }
  5670. return textLineHeight ?
  5671. pInt(textLineHeight) :
  5672. renderer.fontMetrics(
  5673. fontSizeStyle,
  5674. // Get the computed size from parent if not explicit
  5675. tspan.getAttribute('style') ? tspan : textNode
  5676. ).h;
  5677. },
  5678. unescapeEntities = function (inputStr, except) {
  5679. objectEach(renderer.escapes, function (value, key) {
  5680. if (!except || except.indexOf(value) === -1) {
  5681. inputStr = inputStr.toString().replace(
  5682. new RegExp(value, 'g'), // eslint-disable-line security/detect-non-literal-regexp
  5683. key
  5684. );
  5685. }
  5686. });
  5687. return inputStr;
  5688. },
  5689. parseAttribute = function (s, attr) {
  5690. var start,
  5691. delimiter;
  5692. start = s.indexOf('<');
  5693. s = s.substring(start, s.indexOf('>') - start);
  5694. start = s.indexOf(attr + '=');
  5695. if (start !== -1) {
  5696. start = start + attr.length + 1;
  5697. delimiter = s.charAt(start);
  5698. if (delimiter === '"' || delimiter === "'") { // eslint-disable-line quotes
  5699. s = s.substring(start + 1);
  5700. return s.substring(0, s.indexOf(delimiter));
  5701. }
  5702. }
  5703. };
  5704. // The buildText code is quite heavy, so if we're not changing something
  5705. // that affects the text, skip it (#6113).
  5706. textCache = [
  5707. textStr,
  5708. ellipsis,
  5709. noWrap,
  5710. textLineHeight,
  5711. textOutline,
  5712. fontSize,
  5713. width
  5714. ].join(',');
  5715. if (textCache === wrapper.textCache) {
  5716. return;
  5717. }
  5718. wrapper.textCache = textCache;
  5719. // Remove old text
  5720. while (i--) {
  5721. textNode.removeChild(childNodes[i]);
  5722. }
  5723. // Skip tspans, add text directly to text node. The forceTSpan is a hook
  5724. // used in text outline hack.
  5725. if (
  5726. !hasMarkup &&
  5727. !textOutline &&
  5728. !ellipsis &&
  5729. !width &&
  5730. textStr.indexOf(' ') === -1
  5731. ) {
  5732. textNode.appendChild(doc.createTextNode(unescapeEntities(textStr)));
  5733. // Complex strings, add more logic
  5734. } else {
  5735. if (tempParent) {
  5736. // attach it to the DOM to read offset width
  5737. tempParent.appendChild(textNode);
  5738. }
  5739. if (hasMarkup) {
  5740. lines = renderer.styledMode ? (
  5741. textStr
  5742. .replace(
  5743. /<(b|strong)>/g,
  5744. '<span class="highcharts-strong">'
  5745. )
  5746. .replace(
  5747. /<(i|em)>/g,
  5748. '<span class="highcharts-emphasized">'
  5749. )
  5750. ) : (
  5751. textStr
  5752. .replace(
  5753. /<(b|strong)>/g,
  5754. '<span style="font-weight:bold">'
  5755. )
  5756. .replace(
  5757. /<(i|em)>/g,
  5758. '<span style="font-style:italic">'
  5759. )
  5760. );
  5761. lines = lines
  5762. .replace(/<a/g, '<span')
  5763. .replace(/<\/(b|strong|i|em|a)>/g, '</span>')
  5764. .split(/<br.*?>/g);
  5765. } else {
  5766. lines = [textStr];
  5767. }
  5768. // Trim empty lines (#5261)
  5769. lines = lines.filter(function (line) {
  5770. return line !== '';
  5771. });
  5772. // build the lines
  5773. lines.forEach(function buildTextLines(line, lineNo) {
  5774. var spans,
  5775. spanNo = 0,
  5776. lineLength = 0;
  5777. line = line
  5778. // Trim to prevent useless/costly process on the spaces
  5779. // (#5258)
  5780. .replace(/^\s+|\s+$/g, '')
  5781. .replace(/<span/g, '|||<span')
  5782. .replace(/<\/span>/g, '</span>|||');
  5783. spans = line.split('|||');
  5784. spans.forEach(function buildTextSpans(span) {
  5785. if (span !== '' || spans.length === 1) {
  5786. var attributes = {},
  5787. tspan = doc.createElementNS(
  5788. renderer.SVG_NS,
  5789. 'tspan'
  5790. ),
  5791. classAttribute,
  5792. styleAttribute, // #390
  5793. hrefAttribute;
  5794. classAttribute = parseAttribute(span, 'class');
  5795. if (classAttribute) {
  5796. attr(tspan, 'class', classAttribute);
  5797. }
  5798. styleAttribute = parseAttribute(span, 'style');
  5799. if (styleAttribute) {
  5800. styleAttribute = styleAttribute.replace(
  5801. /(;| |^)color([ :])/,
  5802. '$1fill$2'
  5803. );
  5804. attr(tspan, 'style', styleAttribute);
  5805. }
  5806. // Not for export - #1529
  5807. hrefAttribute = parseAttribute(span, 'href');
  5808. if (hrefAttribute && !forExport) {
  5809. attr(
  5810. tspan,
  5811. 'onclick',
  5812. 'location.href=\"' + hrefAttribute + '\"'
  5813. );
  5814. attr(tspan, 'class', 'highcharts-anchor');
  5815. if (!renderer.styledMode) {
  5816. css(tspan, { cursor: 'pointer' });
  5817. }
  5818. }
  5819. // Strip away unsupported HTML tags (#7126)
  5820. span = unescapeEntities(
  5821. span.replace(/<[a-zA-Z\/](.|\n)*?>/g, '') || ' '
  5822. );
  5823. // Nested tags aren't supported, and cause crash in
  5824. // Safari (#1596)
  5825. if (span !== ' ') {
  5826. // add the text node
  5827. tspan.appendChild(doc.createTextNode(span));
  5828. // First span in a line, align it to the left
  5829. if (!spanNo) {
  5830. if (lineNo && parentX !== null) {
  5831. attributes.x = parentX;
  5832. }
  5833. } else {
  5834. attributes.dx = 0; // #16
  5835. }
  5836. // add attributes
  5837. attr(tspan, attributes);
  5838. // Append it
  5839. textNode.appendChild(tspan);
  5840. // first span on subsequent line, add the line
  5841. // height
  5842. if (!spanNo && isSubsequentLine) {
  5843. // allow getting the right offset height in
  5844. // exporting in IE
  5845. if (!svg && forExport) {
  5846. css(tspan, { display: 'block' });
  5847. }
  5848. // Set the line height based on the font size of
  5849. // either the text element or the tspan element
  5850. attr(
  5851. tspan,
  5852. 'dy',
  5853. getLineHeight(tspan)
  5854. );
  5855. }
  5856. // Check width and apply soft breaks or ellipsis
  5857. if (width) {
  5858. var words = span.replace(
  5859. /([^\^])-/g,
  5860. '$1- '
  5861. ).split(' '), // #1273
  5862. hasWhiteSpace = !noWrap && (
  5863. spans.length > 1 ||
  5864. lineNo ||
  5865. words.length > 1
  5866. ),
  5867. wrapLineNo = 0,
  5868. dy = getLineHeight(tspan);
  5869. if (ellipsis) {
  5870. truncated = renderer.truncate(
  5871. wrapper,
  5872. tspan,
  5873. span,
  5874. undefined,
  5875. 0,
  5876. // Target width
  5877. Math.max(
  5878. 0,
  5879. // Substract the font face to make
  5880. // room for the ellipsis itself
  5881. width - parseInt(fontSize || 12, 10)
  5882. ),
  5883. // Build the text to test for
  5884. function (text, currentIndex) {
  5885. return text.substring(
  5886. 0,
  5887. currentIndex
  5888. ) + '\u2026';
  5889. }
  5890. );
  5891. } else if (hasWhiteSpace) {
  5892. while (words.length) {
  5893. // For subsequent lines, create tspans
  5894. // with the same style attributes as the
  5895. // parent text node.
  5896. if (
  5897. words.length &&
  5898. !noWrap &&
  5899. wrapLineNo > 0
  5900. ) {
  5901. tspan = doc.createElementNS(
  5902. SVG_NS,
  5903. 'tspan'
  5904. );
  5905. attr(tspan, {
  5906. dy: dy,
  5907. x: parentX
  5908. });
  5909. if (styleAttribute) { // #390
  5910. attr(
  5911. tspan,
  5912. 'style',
  5913. styleAttribute
  5914. );
  5915. }
  5916. // Start by appending the full
  5917. // remaining text
  5918. tspan.appendChild(
  5919. doc.createTextNode(
  5920. words.join(' ')
  5921. .replace(/- /g, '-')
  5922. )
  5923. );
  5924. textNode.appendChild(tspan);
  5925. }
  5926. // For each line, truncate the remaining
  5927. // words into the line length.
  5928. renderer.truncate(
  5929. wrapper,
  5930. tspan,
  5931. null,
  5932. words,
  5933. wrapLineNo === 0 ? lineLength : 0,
  5934. width,
  5935. // Build the text to test for
  5936. function (text, currentIndex) {
  5937. return words
  5938. .slice(0, currentIndex)
  5939. .join(' ')
  5940. .replace(/- /g, '-');
  5941. }
  5942. );
  5943. lineLength = wrapper.actualWidth;
  5944. wrapLineNo++;
  5945. }
  5946. }
  5947. }
  5948. spanNo++;
  5949. }
  5950. }
  5951. });
  5952. // To avoid beginning lines that doesn't add to the textNode
  5953. // (#6144)
  5954. isSubsequentLine = (
  5955. isSubsequentLine ||
  5956. textNode.childNodes.length
  5957. );
  5958. });
  5959. if (ellipsis && truncated) {
  5960. wrapper.attr(
  5961. 'title',
  5962. unescapeEntities(wrapper.textStr, ['&lt;', '&gt;']) // #7179
  5963. );
  5964. }
  5965. if (tempParent) {
  5966. tempParent.removeChild(textNode);
  5967. }
  5968. // Apply the text outline
  5969. if (textOutline && wrapper.applyTextOutline) {
  5970. wrapper.applyTextOutline(textOutline);
  5971. }
  5972. }
  5973. },
  5974. /**
  5975. * Returns white for dark colors and black for bright colors.
  5976. *
  5977. * @function Highcharts.SVGRenderer#getContrast
  5978. *
  5979. * @param {Highcharts.ColorString} rgba
  5980. * The color to get the contrast for.
  5981. *
  5982. * @return {string}
  5983. * The contrast color, either `#000000` or `#FFFFFF`.
  5984. */
  5985. getContrast: function (rgba) {
  5986. rgba = color(rgba).rgba;
  5987. // The threshold may be discussed. Here's a proposal for adding
  5988. // different weight to the color channels (#6216)
  5989. rgba[0] *= 1; // red
  5990. rgba[1] *= 1.2; // green
  5991. rgba[2] *= 0.5; // blue
  5992. return rgba[0] + rgba[1] + rgba[2] > 1.8 * 255 ? '#000000' : '#FFFFFF';
  5993. },
  5994. /**
  5995. * Create a button with preset states.
  5996. *
  5997. * @function Highcharts.SVGRenderer#button
  5998. *
  5999. * @param {string} text
  6000. * The text or HTML to draw.
  6001. *
  6002. * @param {number} x
  6003. * The x position of the button's left side.
  6004. *
  6005. * @param {number} y
  6006. * The y position of the button's top side.
  6007. *
  6008. * @param {Function} callback
  6009. * The function to execute on button click or touch.
  6010. *
  6011. * @param {Highcharts.SVGAttributes} [normalState]
  6012. * SVG attributes for the normal state.
  6013. *
  6014. * @param {Highcharts.SVGAttributes} [hoverState]
  6015. * SVG attributes for the hover state.
  6016. *
  6017. * @param {Highcharts.SVGAttributes} [pressedState]
  6018. * SVG attributes for the pressed state.
  6019. *
  6020. * @param {Highcharts.SVGAttributes} [disabledState]
  6021. * SVG attributes for the disabled state.
  6022. *
  6023. * @param {Highcharts.SymbolKey} [shape=rect]
  6024. * The shape type.
  6025. *
  6026. * @return {Highcharts.SVGElement}
  6027. * The button element.
  6028. */
  6029. button: function (
  6030. text,
  6031. x,
  6032. y,
  6033. callback,
  6034. normalState,
  6035. hoverState,
  6036. pressedState,
  6037. disabledState,
  6038. shape
  6039. ) {
  6040. var label = this.label(
  6041. text,
  6042. x,
  6043. y,
  6044. shape,
  6045. null,
  6046. null,
  6047. null,
  6048. null,
  6049. 'button'
  6050. ),
  6051. curState = 0,
  6052. styledMode = this.styledMode;
  6053. // Default, non-stylable attributes
  6054. label.attr(merge({
  6055. 'padding': 8,
  6056. 'r': 2
  6057. }, normalState));
  6058. if (!styledMode) {
  6059. // Presentational
  6060. var normalStyle,
  6061. hoverStyle,
  6062. pressedStyle,
  6063. disabledStyle;
  6064. // Normal state - prepare the attributes
  6065. normalState = merge({
  6066. fill: '#f7f7f7',
  6067. stroke: '#cccccc',
  6068. 'stroke-width': 1,
  6069. style: {
  6070. color: '#333333',
  6071. cursor: 'pointer',
  6072. fontWeight: 'normal'
  6073. }
  6074. }, normalState);
  6075. normalStyle = normalState.style;
  6076. delete normalState.style;
  6077. // Hover state
  6078. hoverState = merge(normalState, {
  6079. fill: '#e6e6e6'
  6080. }, hoverState);
  6081. hoverStyle = hoverState.style;
  6082. delete hoverState.style;
  6083. // Pressed state
  6084. pressedState = merge(normalState, {
  6085. fill: '#e6ebf5',
  6086. style: {
  6087. color: '#000000',
  6088. fontWeight: 'bold'
  6089. }
  6090. }, pressedState);
  6091. pressedStyle = pressedState.style;
  6092. delete pressedState.style;
  6093. // Disabled state
  6094. disabledState = merge(normalState, {
  6095. style: {
  6096. color: '#cccccc'
  6097. }
  6098. }, disabledState);
  6099. disabledStyle = disabledState.style;
  6100. delete disabledState.style;
  6101. }
  6102. // Add the events. IE9 and IE10 need mouseover and mouseout to funciton
  6103. // (#667).
  6104. addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
  6105. if (curState !== 3) {
  6106. label.setState(1);
  6107. }
  6108. });
  6109. addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
  6110. if (curState !== 3) {
  6111. label.setState(curState);
  6112. }
  6113. });
  6114. label.setState = function (state) {
  6115. // Hover state is temporary, don't record it
  6116. if (state !== 1) {
  6117. label.state = curState = state;
  6118. }
  6119. // Update visuals
  6120. label
  6121. .removeClass(
  6122. /highcharts-button-(normal|hover|pressed|disabled)/
  6123. )
  6124. .addClass(
  6125. 'highcharts-button-' +
  6126. ['normal', 'hover', 'pressed', 'disabled'][state || 0]
  6127. );
  6128. if (!styledMode) {
  6129. label
  6130. .attr([
  6131. normalState,
  6132. hoverState,
  6133. pressedState,
  6134. disabledState
  6135. ][state || 0])
  6136. .css([
  6137. normalStyle,
  6138. hoverStyle,
  6139. pressedStyle,
  6140. disabledStyle
  6141. ][state || 0]);
  6142. }
  6143. };
  6144. // Presentational attributes
  6145. if (!styledMode) {
  6146. label
  6147. .attr(normalState)
  6148. .css(extend({ cursor: 'default' }, normalStyle));
  6149. }
  6150. return label
  6151. .on('click', function (e) {
  6152. if (curState !== 3) {
  6153. callback.call(label, e);
  6154. }
  6155. });
  6156. },
  6157. /**
  6158. * Make a straight line crisper by not spilling out to neighbour pixels.
  6159. *
  6160. * @function Highcharts.SVGRenderer#crispLine
  6161. *
  6162. * @param {Highcharts.SVGPathArray} points
  6163. * The original points on the format `['M', 0, 0, 'L', 100, 0]`.
  6164. *
  6165. * @param {number} width
  6166. * The width of the line.
  6167. *
  6168. * @return {Highcharts.SVGPathArray}
  6169. * The original points array, but modified to render crisply.
  6170. */
  6171. crispLine: function (points, width) {
  6172. // normalize to a crisp line
  6173. if (points[1] === points[4]) {
  6174. // Substract due to #1129. Now bottom and left axis gridlines behave
  6175. // the same.
  6176. points[1] = points[4] = Math.round(points[1]) - (width % 2 / 2);
  6177. }
  6178. if (points[2] === points[5]) {
  6179. points[2] = points[5] = Math.round(points[2]) + (width % 2 / 2);
  6180. }
  6181. return points;
  6182. },
  6183. /**
  6184. * Draw a path, wraps the SVG `path` element.
  6185. *
  6186. * @sample highcharts/members/renderer-path-on-chart/
  6187. * Draw a path in a chart
  6188. * @sample highcharts/members/renderer-path/
  6189. * Draw a path independent from a chart
  6190. *
  6191. * @example
  6192. * var path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
  6193. * .attr({ stroke: '#ff00ff' })
  6194. * .add();
  6195. *
  6196. * @function Highcharts.SVGRenderer#path
  6197. *
  6198. * @param {Highcharts.SVGPathArray} [path]
  6199. * An SVG path definition in array form.
  6200. *
  6201. * @return {Highcharts.SVGElement}
  6202. * The generated wrapper element.
  6203. *
  6204. *//**
  6205. * Draw a path, wraps the SVG `path` element.
  6206. *
  6207. * @function Highcharts.SVGRenderer#path
  6208. *
  6209. * @param {Highcharts.SVGAttributes} [attribs]
  6210. * The initial attributes.
  6211. *
  6212. * @return {Highcharts.SVGElement}
  6213. * The generated wrapper element.
  6214. */
  6215. path: function (path) {
  6216. var attribs = this.styledMode ? {} : {
  6217. fill: 'none'
  6218. };
  6219. if (isArray(path)) {
  6220. attribs.d = path;
  6221. } else if (isObject(path)) { // attributes
  6222. extend(attribs, path);
  6223. }
  6224. return this.createElement('path').attr(attribs);
  6225. },
  6226. /**
  6227. * Draw a circle, wraps the SVG `circle` element.
  6228. *
  6229. * @sample highcharts/members/renderer-circle/
  6230. * Drawing a circle
  6231. *
  6232. * @function Highcharts.SVGRenderer#circle
  6233. *
  6234. * @param {number} [x]
  6235. * The center x position.
  6236. *
  6237. * @param {number} [y]
  6238. * The center y position.
  6239. *
  6240. * @param {number} [r]
  6241. * The radius.
  6242. *
  6243. * @return {Highcharts.SVGElement}
  6244. * The generated wrapper element.
  6245. *//**
  6246. * Draw a circle, wraps the SVG `circle` element.
  6247. *
  6248. * @function Highcharts.SVGRenderer#circle
  6249. *
  6250. * @param {Highcharts.SVGAttributes} [attribs]
  6251. * The initial attributes.
  6252. *
  6253. * @return {Highcharts.SVGElement}
  6254. * The generated wrapper element.
  6255. */
  6256. circle: function (x, y, r) {
  6257. var attribs = (
  6258. isObject(x) ?
  6259. x :
  6260. x === undefined ? {} : { x: x, y: y, r: r }
  6261. ),
  6262. wrapper = this.createElement('circle');
  6263. // Setting x or y translates to cx and cy
  6264. wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
  6265. element.setAttribute('c' + key, value);
  6266. };
  6267. return wrapper.attr(attribs);
  6268. },
  6269. /**
  6270. * Draw and return an arc.
  6271. *
  6272. * @sample highcharts/members/renderer-arc/
  6273. * Drawing an arc
  6274. *
  6275. * @function Highcharts.SVGRenderer#arc
  6276. *
  6277. * @param {number} [x=0]
  6278. * Center X position.
  6279. *
  6280. * @param {number} [y=0]
  6281. * Center Y position.
  6282. *
  6283. * @param {number} [r=0]
  6284. * The outer radius of the arc.
  6285. *
  6286. * @param {number} [innerR=0]
  6287. * Inner radius like used in donut charts.
  6288. *
  6289. * @param {number} [start=0]
  6290. * The starting angle of the arc in radians, where 0 is to the right
  6291. * and `-Math.PI/2` is up.
  6292. *
  6293. * @param {number} [end=0]
  6294. * The ending angle of the arc in radians, where 0 is to the right
  6295. * and `-Math.PI/2` is up.
  6296. *
  6297. * @return {Highcharts.SVGElement}
  6298. * The generated wrapper element.
  6299. *//**
  6300. * Draw and return an arc. Overloaded function that takes arguments object.
  6301. *
  6302. * @function Highcharts.SVGRenderer#arc
  6303. *
  6304. * @param {Highcharts.SVGAttributes} attribs
  6305. * Initial SVG attributes.
  6306. *
  6307. * @return {Highcharts.SVGElement}
  6308. * The generated wrapper element.
  6309. */
  6310. arc: function (x, y, r, innerR, start, end) {
  6311. var arc,
  6312. options;
  6313. if (isObject(x)) {
  6314. options = x;
  6315. y = options.y;
  6316. r = options.r;
  6317. innerR = options.innerR;
  6318. start = options.start;
  6319. end = options.end;
  6320. x = options.x;
  6321. } else {
  6322. options = {
  6323. innerR: innerR,
  6324. start: start,
  6325. end: end
  6326. };
  6327. }
  6328. // Arcs are defined as symbols for the ability to set
  6329. // attributes in attr and animate
  6330. arc = this.symbol('arc', x, y, r, r, options);
  6331. arc.r = r; // #959
  6332. return arc;
  6333. },
  6334. /**
  6335. * Draw and return a rectangle.
  6336. *
  6337. * @function Highcharts.SVGRenderer#rect
  6338. *
  6339. * @param {number} [x]
  6340. * Left position.
  6341. *
  6342. * @param {number} [y]
  6343. * Top position.
  6344. *
  6345. * @param {number} [width]
  6346. * Width of the rectangle.
  6347. *
  6348. * @param {number} [height]
  6349. * Height of the rectangle.
  6350. *
  6351. * @param {number} [r]
  6352. * Border corner radius.
  6353. *
  6354. * @param {number} [strokeWidth]
  6355. * A stroke width can be supplied to allow crisp drawing.
  6356. *
  6357. * @return {Highcharts.SVGElement}
  6358. * The generated wrapper element.
  6359. *//**
  6360. * Draw and return a rectangle.
  6361. *
  6362. * @sample highcharts/members/renderer-rect-on-chart/
  6363. * Draw a rectangle in a chart
  6364. * @sample highcharts/members/renderer-rect/
  6365. * Draw a rectangle independent from a chart
  6366. *
  6367. * @function Highcharts.SVGRenderer#rect
  6368. *
  6369. * @param {Highcharts.SVGAttributes} [attributes]
  6370. * General SVG attributes for the rectangle.
  6371. *
  6372. * @return {Highcharts.SVGElement}
  6373. * The generated wrapper element.
  6374. */
  6375. rect: function (x, y, width, height, r, strokeWidth) {
  6376. r = isObject(x) ? x.r : r;
  6377. var wrapper = this.createElement('rect'),
  6378. attribs = isObject(x) ? x : x === undefined ? {} : {
  6379. x: x,
  6380. y: y,
  6381. width: Math.max(width, 0),
  6382. height: Math.max(height, 0)
  6383. };
  6384. if (!this.styledMode) {
  6385. if (strokeWidth !== undefined) {
  6386. attribs.strokeWidth = strokeWidth;
  6387. attribs = wrapper.crisp(attribs);
  6388. }
  6389. attribs.fill = 'none';
  6390. }
  6391. if (r) {
  6392. attribs.r = r;
  6393. }
  6394. wrapper.rSetter = function (value, key, element) {
  6395. attr(element, {
  6396. rx: value,
  6397. ry: value
  6398. });
  6399. };
  6400. return wrapper.attr(attribs);
  6401. },
  6402. /**
  6403. * Resize the {@link SVGRenderer#box} and re-align all aligned child
  6404. * elements.
  6405. *
  6406. * @sample highcharts/members/renderer-g/
  6407. * Show and hide grouped objects
  6408. *
  6409. * @function Highcharts.SVGRenderer#setSize
  6410. *
  6411. * @param {number} width
  6412. * The new pixel width.
  6413. *
  6414. * @param {number} height
  6415. * The new pixel height.
  6416. *
  6417. * @param {boolean|Highcharts.AnimationOptionsObject} [animate=true]
  6418. * Whether and how to animate.
  6419. */
  6420. setSize: function (width, height, animate) {
  6421. var renderer = this,
  6422. alignedObjects = renderer.alignedObjects,
  6423. i = alignedObjects.length;
  6424. renderer.width = width;
  6425. renderer.height = height;
  6426. renderer.boxWrapper.animate({
  6427. width: width,
  6428. height: height
  6429. }, {
  6430. step: function () {
  6431. this.attr({
  6432. viewBox: '0 0 ' + this.attr('width') + ' ' +
  6433. this.attr('height')
  6434. });
  6435. },
  6436. duration: pick(animate, true) ? undefined : 0
  6437. });
  6438. while (i--) {
  6439. alignedObjects[i].align();
  6440. }
  6441. },
  6442. /**
  6443. * Create and return an svg group element. Child
  6444. * {@link Highcharts.SVGElement} objects are added to the group by using the
  6445. * group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
  6446. *
  6447. * @function Highcharts.SVGRenderer#g
  6448. *
  6449. * @param {string} [name]
  6450. * The group will be given a class name of `highcharts-{name}`. This
  6451. * can be used for styling and scripting.
  6452. *
  6453. * @return {Highcharts.SVGElement}
  6454. * The generated wrapper element.
  6455. */
  6456. g: function (name) {
  6457. var elem = this.createElement('g');
  6458. return name ? elem.attr({ 'class': 'highcharts-' + name }) : elem;
  6459. },
  6460. /**
  6461. * Display an image.
  6462. *
  6463. * @sample highcharts/members/renderer-image-on-chart/
  6464. * Add an image in a chart
  6465. * @sample highcharts/members/renderer-image/
  6466. * Add an image independent of a chart
  6467. *
  6468. * @function Highcharts.SVGRenderer#image
  6469. *
  6470. * @param {string} src
  6471. * The image source.
  6472. *
  6473. * @param {number} [x]
  6474. * The X position.
  6475. *
  6476. * @param {number} [y]
  6477. * The Y position.
  6478. *
  6479. * @param {number} [width]
  6480. * The image width. If omitted, it defaults to the image file width.
  6481. *
  6482. * @param {number} [height]
  6483. * The image height. If omitted it defaults to the image file
  6484. * height.
  6485. *
  6486. * @param {Function} [onload]
  6487. * Event handler for image load.
  6488. *
  6489. * @return {Highcharts.SVGElement}
  6490. * The generated wrapper element.
  6491. */
  6492. image: function (src, x, y, width, height, onload) {
  6493. var attribs = {
  6494. preserveAspectRatio: 'none'
  6495. },
  6496. elemWrapper,
  6497. dummy,
  6498. setSVGImageSource = function (el, src) {
  6499. // Set the href in the xlink namespace
  6500. if (el.setAttributeNS) {
  6501. el.setAttributeNS(
  6502. 'http://www.w3.org/1999/xlink', 'href', src
  6503. );
  6504. } else {
  6505. // could be exporting in IE
  6506. // using href throws "not supported" in ie7 and under,
  6507. // requries regex shim to fix later
  6508. el.setAttribute('hc-svg-href', src);
  6509. }
  6510. },
  6511. onDummyLoad = function (e) {
  6512. setSVGImageSource(elemWrapper.element, src);
  6513. onload.call(elemWrapper, e);
  6514. };
  6515. // optional properties
  6516. if (arguments.length > 1) {
  6517. extend(attribs, {
  6518. x: x,
  6519. y: y,
  6520. width: width,
  6521. height: height
  6522. });
  6523. }
  6524. elemWrapper = this.createElement('image').attr(attribs);
  6525. // Add load event if supplied
  6526. if (onload) {
  6527. // We have to use a dummy HTML image since IE support for SVG image
  6528. // load events is very buggy. First set a transparent src, wait for
  6529. // dummy to load, and then add the real src to the SVG image.
  6530. setSVGImageSource(
  6531. elemWrapper.element,
  6532. 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' /* eslint-disable-line */
  6533. );
  6534. dummy = new win.Image();
  6535. addEvent(dummy, 'load', onDummyLoad);
  6536. dummy.src = src;
  6537. if (dummy.complete) {
  6538. onDummyLoad({});
  6539. }
  6540. } else {
  6541. setSVGImageSource(elemWrapper.element, src);
  6542. }
  6543. return elemWrapper;
  6544. },
  6545. /**
  6546. * Draw a symbol out of pre-defined shape paths from
  6547. * {@link SVGRenderer#symbols}.
  6548. * It is used in Highcharts for point makers, which cake a `symbol` option,
  6549. * and label and button backgrounds like in the tooltip and stock flags.
  6550. *
  6551. * @function Highcharts.SVGRenderer#symbol
  6552. *
  6553. * @param {symbol} symbol
  6554. * The symbol name.
  6555. *
  6556. * @param {number} x
  6557. * The X coordinate for the top left position.
  6558. *
  6559. * @param {number} y
  6560. * The Y coordinate for the top left position.
  6561. *
  6562. * @param {number} width
  6563. * The pixel width.
  6564. *
  6565. * @param {number} height
  6566. * The pixel height.
  6567. *
  6568. * @param {Highcharts.SymbolOptionsObject} [options]
  6569. * Additional options, depending on the actual symbol drawn.
  6570. *
  6571. * @return {Highcharts.SVGElement}
  6572. */
  6573. symbol: function (symbol, x, y, width, height, options) {
  6574. var ren = this,
  6575. obj,
  6576. imageRegex = /^url\((.*?)\)$/,
  6577. isImage = imageRegex.test(symbol),
  6578. sym = !isImage && (this.symbols[symbol] ? symbol : 'circle'),
  6579. // get the symbol definition function
  6580. symbolFn = sym && this.symbols[sym],
  6581. // check if there's a path defined for this symbol
  6582. path = defined(x) && symbolFn && symbolFn.call(
  6583. this.symbols,
  6584. Math.round(x),
  6585. Math.round(y),
  6586. width,
  6587. height,
  6588. options
  6589. ),
  6590. imageSrc,
  6591. centerImage;
  6592. if (symbolFn) {
  6593. obj = this.path(path);
  6594. if (!ren.styledMode) {
  6595. obj.attr('fill', 'none');
  6596. }
  6597. // expando properties for use in animate and attr
  6598. extend(obj, {
  6599. symbolName: sym,
  6600. x: x,
  6601. y: y,
  6602. width: width,
  6603. height: height
  6604. });
  6605. if (options) {
  6606. extend(obj, options);
  6607. }
  6608. // Image symbols
  6609. } else if (isImage) {
  6610. imageSrc = symbol.match(imageRegex)[1];
  6611. // Create the image synchronously, add attribs async
  6612. obj = this.image(imageSrc);
  6613. // The image width is not always the same as the symbol width. The
  6614. // image may be centered within the symbol, as is the case when
  6615. // image shapes are used as label backgrounds, for example in flags.
  6616. obj.imgwidth = pick(
  6617. symbolSizes[imageSrc] && symbolSizes[imageSrc].width,
  6618. options && options.width
  6619. );
  6620. obj.imgheight = pick(
  6621. symbolSizes[imageSrc] && symbolSizes[imageSrc].height,
  6622. options && options.height
  6623. );
  6624. /**
  6625. * Set the size and position
  6626. */
  6627. centerImage = function () {
  6628. obj.attr({
  6629. width: obj.width,
  6630. height: obj.height
  6631. });
  6632. };
  6633. /**
  6634. * Width and height setters that take both the image's physical size
  6635. * and the label size into consideration, and translates the image
  6636. * to center within the label.
  6637. */
  6638. ['width', 'height'].forEach(function (key) {
  6639. obj[key + 'Setter'] = function (value, key) {
  6640. var attribs = {},
  6641. imgSize = this['img' + key],
  6642. trans = key === 'width' ? 'translateX' : 'translateY';
  6643. this[key] = value;
  6644. if (defined(imgSize)) {
  6645. if (this.element) {
  6646. this.element.setAttribute(key, imgSize);
  6647. }
  6648. if (!this.alignByTranslate) {
  6649. attribs[trans] = ((this[key] || 0) - imgSize) / 2;
  6650. this.attr(attribs);
  6651. }
  6652. }
  6653. };
  6654. });
  6655. if (defined(x)) {
  6656. obj.attr({
  6657. x: x,
  6658. y: y
  6659. });
  6660. }
  6661. obj.isImg = true;
  6662. if (defined(obj.imgwidth) && defined(obj.imgheight)) {
  6663. centerImage();
  6664. } else {
  6665. // Initialize image to be 0 size so export will still function
  6666. // if there's no cached sizes.
  6667. obj.attr({ width: 0, height: 0 });
  6668. // Create a dummy JavaScript image to get the width and height.
  6669. createElement('img', {
  6670. onload: function () {
  6671. var chart = charts[ren.chartIndex];
  6672. // Special case for SVGs on IE11, the width is not
  6673. // accessible until the image is part of the DOM
  6674. // (#2854).
  6675. if (this.width === 0) {
  6676. css(this, {
  6677. position: 'absolute',
  6678. top: '-999em'
  6679. });
  6680. doc.body.appendChild(this);
  6681. }
  6682. // Center the image
  6683. symbolSizes[imageSrc] = { // Cache for next
  6684. width: this.width,
  6685. height: this.height
  6686. };
  6687. obj.imgwidth = this.width;
  6688. obj.imgheight = this.height;
  6689. if (obj.element) {
  6690. centerImage();
  6691. }
  6692. // Clean up after #2854 workaround.
  6693. if (this.parentNode) {
  6694. this.parentNode.removeChild(this);
  6695. }
  6696. // Fire the load event when all external images are
  6697. // loaded
  6698. ren.imgCount--;
  6699. if (!ren.imgCount && chart && chart.onload) {
  6700. chart.onload();
  6701. }
  6702. },
  6703. src: imageSrc
  6704. });
  6705. this.imgCount++;
  6706. }
  6707. }
  6708. return obj;
  6709. },
  6710. /**
  6711. * An extendable collection of functions for defining symbol paths.
  6712. *
  6713. * @name Highcharts.SVGRenderer#symbols
  6714. * @type {Highcharts.SymbolDictionary}
  6715. */
  6716. symbols: {
  6717. 'circle': function (x, y, w, h) {
  6718. // Return a full arc
  6719. return this.arc(x + w / 2, y + h / 2, w / 2, h / 2, {
  6720. start: 0,
  6721. end: Math.PI * 2,
  6722. open: false
  6723. });
  6724. },
  6725. 'square': function (x, y, w, h) {
  6726. return [
  6727. 'M', x, y,
  6728. 'L', x + w, y,
  6729. x + w, y + h,
  6730. x, y + h,
  6731. 'Z'
  6732. ];
  6733. },
  6734. 'triangle': function (x, y, w, h) {
  6735. return [
  6736. 'M', x + w / 2, y,
  6737. 'L', x + w, y + h,
  6738. x, y + h,
  6739. 'Z'
  6740. ];
  6741. },
  6742. 'triangle-down': function (x, y, w, h) {
  6743. return [
  6744. 'M', x, y,
  6745. 'L', x + w, y,
  6746. x + w / 2, y + h,
  6747. 'Z'
  6748. ];
  6749. },
  6750. 'diamond': function (x, y, w, h) {
  6751. return [
  6752. 'M', x + w / 2, y,
  6753. 'L', x + w, y + h / 2,
  6754. x + w / 2, y + h,
  6755. x, y + h / 2,
  6756. 'Z'
  6757. ];
  6758. },
  6759. 'arc': function (x, y, w, h, options) {
  6760. var start = options.start,
  6761. rx = options.r || w,
  6762. ry = options.r || h || w,
  6763. proximity = 0.001,
  6764. fullCircle =
  6765. Math.abs(options.end - options.start - 2 * Math.PI) <
  6766. proximity,
  6767. // Substract a small number to prevent cos and sin of start and
  6768. // end from becoming equal on 360 arcs (related: #1561)
  6769. end = options.end - proximity,
  6770. innerRadius = options.innerR,
  6771. open = pick(options.open, fullCircle),
  6772. cosStart = Math.cos(start),
  6773. sinStart = Math.sin(start),
  6774. cosEnd = Math.cos(end),
  6775. sinEnd = Math.sin(end),
  6776. // Proximity takes care of rounding errors around PI (#6971)
  6777. longArc = options.end - start - Math.PI < proximity ? 0 : 1,
  6778. arc;
  6779. arc = [
  6780. 'M',
  6781. x + rx * cosStart,
  6782. y + ry * sinStart,
  6783. 'A', // arcTo
  6784. rx, // x radius
  6785. ry, // y radius
  6786. 0, // slanting
  6787. longArc, // long or short arc
  6788. 1, // clockwise
  6789. x + rx * cosEnd,
  6790. y + ry * sinEnd
  6791. ];
  6792. if (defined(innerRadius)) {
  6793. arc.push(
  6794. open ? 'M' : 'L',
  6795. x + innerRadius * cosEnd,
  6796. y + innerRadius * sinEnd,
  6797. 'A', // arcTo
  6798. innerRadius, // x radius
  6799. innerRadius, // y radius
  6800. 0, // slanting
  6801. longArc, // long or short arc
  6802. 0, // clockwise
  6803. x + innerRadius * cosStart,
  6804. y + innerRadius * sinStart
  6805. );
  6806. }
  6807. arc.push(open ? '' : 'Z'); // close
  6808. return arc;
  6809. },
  6810. /**
  6811. * Callout shape used for default tooltips, also used for rounded
  6812. * rectangles in VML
  6813. */
  6814. 'callout': function (x, y, w, h, options) {
  6815. var arrowLength = 6,
  6816. halfDistance = 6,
  6817. r = Math.min((options && options.r) || 0, w, h),
  6818. safeDistance = r + halfDistance,
  6819. anchorX = options && options.anchorX,
  6820. anchorY = options && options.anchorY,
  6821. path;
  6822. path = [
  6823. 'M', x + r, y,
  6824. 'L', x + w - r, y, // top side
  6825. 'C', x + w, y, x + w, y, x + w, y + r, // top-right corner
  6826. 'L', x + w, y + h - r, // right side
  6827. 'C', x + w, y + h, x + w, y + h, x + w - r, y + h, // bottom-rgt
  6828. 'L', x + r, y + h, // bottom side
  6829. 'C', x, y + h, x, y + h, x, y + h - r, // bottom-left corner
  6830. 'L', x, y + r, // left side
  6831. 'C', x, y, x, y, x + r, y // top-left corner
  6832. ];
  6833. // Anchor on right side
  6834. if (anchorX && anchorX > w) {
  6835. // Chevron
  6836. if (
  6837. anchorY > y + safeDistance &&
  6838. anchorY < y + h - safeDistance
  6839. ) {
  6840. path.splice(
  6841. 13,
  6842. 3,
  6843. 'L', x + w, anchorY - halfDistance,
  6844. x + w + arrowLength, anchorY,
  6845. x + w, anchorY + halfDistance,
  6846. x + w, y + h - r
  6847. );
  6848. // Simple connector
  6849. } else {
  6850. path.splice(
  6851. 13,
  6852. 3,
  6853. 'L', x + w, h / 2,
  6854. anchorX, anchorY,
  6855. x + w, h / 2,
  6856. x + w, y + h - r
  6857. );
  6858. }
  6859. // Anchor on left side
  6860. } else if (anchorX && anchorX < 0) {
  6861. // Chevron
  6862. if (
  6863. anchorY > y + safeDistance &&
  6864. anchorY < y + h - safeDistance
  6865. ) {
  6866. path.splice(
  6867. 33,
  6868. 3,
  6869. 'L', x, anchorY + halfDistance,
  6870. x - arrowLength, anchorY,
  6871. x, anchorY - halfDistance,
  6872. x, y + r
  6873. );
  6874. // Simple connector
  6875. } else {
  6876. path.splice(
  6877. 33,
  6878. 3,
  6879. 'L', x, h / 2,
  6880. anchorX, anchorY,
  6881. x, h / 2,
  6882. x, y + r
  6883. );
  6884. }
  6885. } else if ( // replace bottom
  6886. anchorY &&
  6887. anchorY > h &&
  6888. anchorX > x + safeDistance &&
  6889. anchorX < x + w - safeDistance
  6890. ) {
  6891. path.splice(
  6892. 23,
  6893. 3,
  6894. 'L', anchorX + halfDistance, y + h,
  6895. anchorX, y + h + arrowLength,
  6896. anchorX - halfDistance, y + h,
  6897. x + r, y + h
  6898. );
  6899. } else if ( // replace top
  6900. anchorY &&
  6901. anchorY < 0 &&
  6902. anchorX > x + safeDistance &&
  6903. anchorX < x + w - safeDistance
  6904. ) {
  6905. path.splice(
  6906. 3,
  6907. 3,
  6908. 'L', anchorX - halfDistance, y,
  6909. anchorX, y - arrowLength,
  6910. anchorX + halfDistance, y,
  6911. w - r, y
  6912. );
  6913. }
  6914. return path;
  6915. }
  6916. },
  6917. /**
  6918. * Define a clipping rectangle. The clipping rectangle is later applied
  6919. * to {@link SVGElement} objects through the {@link SVGElement#clip}
  6920. * function.
  6921. *
  6922. * @example
  6923. * var circle = renderer.circle(100, 100, 100)
  6924. * .attr({ fill: 'red' })
  6925. * .add();
  6926. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  6927. *
  6928. * // Leave only the lower right quarter visible
  6929. * circle.clip(clipRect);
  6930. *
  6931. * @function Highcharts.SVGRenderer#clipRect
  6932. *
  6933. * @param {string} id
  6934. *
  6935. * @param {number} x
  6936. *
  6937. * @param {number} y
  6938. *
  6939. * @param {number} width
  6940. *
  6941. * @param {number} height
  6942. *
  6943. * @return {Highcharts.ClipRectElement}
  6944. * A clipping rectangle.
  6945. */
  6946. clipRect: function (x, y, width, height) {
  6947. var wrapper,
  6948. id = H.uniqueKey(),
  6949. clipPath = this.createElement('clipPath').attr({
  6950. id: id
  6951. }).add(this.defs);
  6952. wrapper = this.rect(x, y, width, height, 0).add(clipPath);
  6953. wrapper.id = id;
  6954. wrapper.clipPath = clipPath;
  6955. wrapper.count = 0;
  6956. return wrapper;
  6957. },
  6958. /**
  6959. * Draw text. The text can contain a subset of HTML, like spans and anchors
  6960. * and some basic text styling of these. For more advanced features like
  6961. * border and background, use {@link Highcharts.SVGRenderer#label} instead.
  6962. * To update the text after render, run `text.attr({ text: 'New text' })`.
  6963. *
  6964. * @sample highcharts/members/renderer-text-on-chart/
  6965. * Annotate the chart freely
  6966. * @sample highcharts/members/renderer-on-chart/
  6967. * Annotate with a border and in response to the data
  6968. * @sample highcharts/members/renderer-text/
  6969. * Formatted text
  6970. *
  6971. * @function Highcharts.SVGRenderer#text
  6972. *
  6973. * @param {string} str
  6974. * The text of (subset) HTML to draw.
  6975. *
  6976. * @param {number} x
  6977. * The x position of the text's lower left corner.
  6978. *
  6979. * @param {number} y
  6980. * The y position of the text's lower left corner.
  6981. *
  6982. * @param {boolean} [useHTML=false]
  6983. * Use HTML to render the text.
  6984. *
  6985. * @return {Highcharts.SVGElement}
  6986. * The text object.
  6987. */
  6988. text: function (str, x, y, useHTML) {
  6989. // declare variables
  6990. var renderer = this,
  6991. wrapper,
  6992. attribs = {};
  6993. if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
  6994. return renderer.html(str, x, y);
  6995. }
  6996. attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
  6997. if (y) {
  6998. attribs.y = Math.round(y);
  6999. }
  7000. if (defined(str)) {
  7001. attribs.text = str;
  7002. }
  7003. wrapper = renderer.createElement('text')
  7004. .attr(attribs);
  7005. if (!useHTML) {
  7006. wrapper.xSetter = function (value, key, element) {
  7007. var tspans = element.getElementsByTagName('tspan'),
  7008. tspan,
  7009. parentVal = element.getAttribute(key),
  7010. i;
  7011. for (i = 0; i < tspans.length; i++) {
  7012. tspan = tspans[i];
  7013. // If the x values are equal, the tspan represents a
  7014. // linebreak
  7015. if (tspan.getAttribute(key) === parentVal) {
  7016. tspan.setAttribute(key, value);
  7017. }
  7018. }
  7019. element.setAttribute(key, value);
  7020. };
  7021. }
  7022. return wrapper;
  7023. },
  7024. /**
  7025. * Utility to return the baseline offset and total line height from the font
  7026. * size.
  7027. *
  7028. * @function Highcharts.SVGRenderer#fontMetrics
  7029. *
  7030. * @param {string} [fontSize]
  7031. * The current font size to inspect. If not given, the font size
  7032. * will be found from the DOM element.
  7033. *
  7034. * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement} [elem]
  7035. * The element to inspect for a current font size.
  7036. *
  7037. * @return {Highcharts.FontMetricsObject}
  7038. * The font metrics.
  7039. */
  7040. fontMetrics: function (fontSize, elem) {
  7041. var lineHeight,
  7042. baseline;
  7043. if (
  7044. (this.styledMode || !/px/.test(fontSize)) &&
  7045. win.getComputedStyle // old IE doesn't support it
  7046. ) {
  7047. fontSize = elem && SVGElement.prototype.getStyle.call(
  7048. elem,
  7049. 'font-size'
  7050. );
  7051. } else {
  7052. fontSize = fontSize ||
  7053. // When the elem is a DOM element (#5932)
  7054. (elem && elem.style && elem.style.fontSize) ||
  7055. // Fall back on the renderer style default
  7056. (this.style && this.style.fontSize);
  7057. }
  7058. // Handle different units
  7059. if (/px/.test(fontSize)) {
  7060. fontSize = pInt(fontSize);
  7061. } else {
  7062. fontSize = 12;
  7063. }
  7064. // Empirical values found by comparing font size and bounding box
  7065. // height. Applies to the default font family.
  7066. // https://jsfiddle.net/highcharts/7xvn7/
  7067. lineHeight = fontSize < 24 ? fontSize + 3 : Math.round(fontSize * 1.2);
  7068. baseline = Math.round(lineHeight * 0.8);
  7069. return {
  7070. h: lineHeight,
  7071. b: baseline,
  7072. f: fontSize
  7073. };
  7074. },
  7075. /**
  7076. * Correct X and Y positioning of a label for rotation (#1764).
  7077. *
  7078. * @private
  7079. * @function Highcharts.SVGRenderer#rotCorr
  7080. *
  7081. * @param {number} baseline
  7082. *
  7083. * @param {number} rotation
  7084. *
  7085. * @param {boolean} alterY
  7086. */
  7087. rotCorr: function (baseline, rotation, alterY) {
  7088. var y = baseline;
  7089. if (rotation && alterY) {
  7090. y = Math.max(y * Math.cos(rotation * deg2rad), 4);
  7091. }
  7092. return {
  7093. x: (-baseline / 3) * Math.sin(rotation * deg2rad),
  7094. y: y
  7095. };
  7096. },
  7097. /**
  7098. * Draw a label, which is an extended text element with support for border
  7099. * and background. Highcharts creates a `g` element with a text and a `path`
  7100. * or `rect` inside, to make it behave somewhat like a HTML div. Border and
  7101. * background are set through `stroke`, `stroke-width` and `fill` attributes
  7102. * using the {@link Highcharts.SVGElement#attr|attr} method. To update the
  7103. * text after render, run `label.attr({ text: 'New text' })`.
  7104. *
  7105. * @sample highcharts/members/renderer-label-on-chart/
  7106. * A label on the chart
  7107. *
  7108. * @function Highcharts.SVGRenderer#label
  7109. *
  7110. * @param {string} str
  7111. * The initial text string or (subset) HTML to render.
  7112. *
  7113. * @param {number} x
  7114. * The x position of the label's left side.
  7115. *
  7116. * @param {number} y
  7117. * The y position of the label's top side or baseline, depending on
  7118. * the `baseline` parameter.
  7119. *
  7120. * @param {string} [shape='rect']
  7121. * The shape of the label's border/background, if any. Defaults to
  7122. * `rect`. Other possible values are `callout` or other shapes
  7123. * defined in {@link Highcharts.SVGRenderer#symbols}.
  7124. *
  7125. * @param {string} [shape='rect']
  7126. * The shape of the label's border/background, if any. Defaults to
  7127. * `rect`. Other possible values are `callout` or other shapes
  7128. * defined in {@link Highcharts.SVGRenderer#symbols}.
  7129. *
  7130. * @param {number} [anchorX]
  7131. * In case the `shape` has a pointer, like a flag, this is the
  7132. * coordinates it should be pinned to.
  7133. *
  7134. * @param {number} [anchorY]
  7135. * In case the `shape` has a pointer, like a flag, this is the
  7136. * coordinates it should be pinned to.
  7137. *
  7138. * @param {boolean} [useHTML=false]
  7139. * Wether to use HTML to render the label.
  7140. *
  7141. * @param {boolean} [baseline=false]
  7142. * Whether to position the label relative to the text baseline,
  7143. * like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
  7144. * upper border of the rectangle.
  7145. *
  7146. * @param {string} [className]
  7147. * Class name for the group.
  7148. *
  7149. * @return {Highcharts.SVGElement}
  7150. * The generated label.
  7151. */
  7152. label: function (
  7153. str,
  7154. x,
  7155. y,
  7156. shape,
  7157. anchorX,
  7158. anchorY,
  7159. useHTML,
  7160. baseline,
  7161. className
  7162. ) {
  7163. var renderer = this,
  7164. styledMode = renderer.styledMode,
  7165. wrapper = renderer.g(className !== 'button' && 'label'),
  7166. text = wrapper.text = renderer.text('', 0, 0, useHTML)
  7167. .attr({
  7168. zIndex: 1
  7169. }),
  7170. box,
  7171. bBox,
  7172. alignFactor = 0,
  7173. padding = 3,
  7174. paddingLeft = 0,
  7175. width,
  7176. height,
  7177. wrapperX,
  7178. wrapperY,
  7179. textAlign,
  7180. deferredAttr = {},
  7181. strokeWidth,
  7182. baselineOffset,
  7183. hasBGImage = /^url\((.*?)\)$/.test(shape),
  7184. needsBox = styledMode || hasBGImage,
  7185. getCrispAdjust = function () {
  7186. return styledMode ?
  7187. box.strokeWidth() % 2 / 2 :
  7188. (strokeWidth ? parseInt(strokeWidth, 10) : 0) % 2 / 2;
  7189. },
  7190. updateBoxSize,
  7191. updateTextPadding,
  7192. boxAttr;
  7193. if (className) {
  7194. wrapper.addClass('highcharts-' + className);
  7195. }
  7196. /* This function runs after the label is added to the DOM (when the
  7197. bounding box is available), and after the text of the label is
  7198. updated to detect the new bounding box and reflect it in the border
  7199. box. */
  7200. updateBoxSize = function () {
  7201. var style = text.element.style,
  7202. crispAdjust,
  7203. attribs = {};
  7204. bBox = (
  7205. (width === undefined || height === undefined || textAlign) &&
  7206. defined(text.textStr) &&
  7207. text.getBBox()
  7208. ); // #3295 && 3514 box failure when string equals 0
  7209. wrapper.width = (
  7210. (width || bBox.width || 0) +
  7211. 2 * padding +
  7212. paddingLeft
  7213. );
  7214. wrapper.height = (height || bBox.height || 0) + 2 * padding;
  7215. // Update the label-scoped y offset
  7216. baselineOffset = padding + Math.min(
  7217. renderer.fontMetrics(style && style.fontSize, text).b,
  7218. // Math.min because of inline style (#9400)
  7219. bBox ? bBox.height : Infinity
  7220. );
  7221. if (needsBox) {
  7222. // Create the border box if it is not already present
  7223. if (!box) {
  7224. // Symbol definition exists (#5324)
  7225. wrapper.box = box = renderer.symbols[shape] || hasBGImage ?
  7226. renderer.symbol(shape) :
  7227. renderer.rect();
  7228. box.addClass( // Don't use label className for buttons
  7229. (className === 'button' ? '' : 'highcharts-label-box') +
  7230. (className ? ' highcharts-' + className + '-box' : '')
  7231. );
  7232. box.add(wrapper);
  7233. crispAdjust = getCrispAdjust();
  7234. attribs.x = crispAdjust;
  7235. attribs.y = (baseline ? -baselineOffset : 0) + crispAdjust;
  7236. }
  7237. // Apply the box attributes
  7238. attribs.width = Math.round(wrapper.width);
  7239. attribs.height = Math.round(wrapper.height);
  7240. box.attr(extend(attribs, deferredAttr));
  7241. deferredAttr = {};
  7242. }
  7243. };
  7244. /*
  7245. * This function runs after setting text or padding, but only if padding
  7246. * is changed.
  7247. */
  7248. updateTextPadding = function () {
  7249. var textX = paddingLeft + padding,
  7250. textY;
  7251. // determin y based on the baseline
  7252. textY = baseline ? 0 : baselineOffset;
  7253. // compensate for alignment
  7254. if (
  7255. defined(width) &&
  7256. bBox &&
  7257. (textAlign === 'center' || textAlign === 'right')
  7258. ) {
  7259. textX += { center: 0.5, right: 1 }[textAlign] *
  7260. (width - bBox.width);
  7261. }
  7262. // update if anything changed
  7263. if (textX !== text.x || textY !== text.y) {
  7264. text.attr('x', textX);
  7265. // #8159 - prevent misplaced data labels in treemap
  7266. // (useHTML: true)
  7267. if (text.hasBoxWidthChanged) {
  7268. bBox = text.getBBox(true);
  7269. updateBoxSize();
  7270. }
  7271. if (textY !== undefined) {
  7272. text.attr('y', textY);
  7273. }
  7274. }
  7275. // record current values
  7276. text.x = textX;
  7277. text.y = textY;
  7278. };
  7279. /*
  7280. * Set a box attribute, or defer it if the box is not yet created
  7281. */
  7282. boxAttr = function (key, value) {
  7283. if (box) {
  7284. box.attr(key, value);
  7285. } else {
  7286. deferredAttr[key] = value;
  7287. }
  7288. };
  7289. /*
  7290. * After the text element is added, get the desired size of the border
  7291. * box and add it before the text in the DOM.
  7292. */
  7293. wrapper.onAdd = function () {
  7294. text.add(wrapper);
  7295. wrapper.attr({
  7296. // Alignment is available now (#3295, 0 not rendered if given
  7297. // as a value)
  7298. text: (str || str === 0) ? str : '',
  7299. x: x,
  7300. y: y
  7301. });
  7302. if (box && defined(anchorX)) {
  7303. wrapper.attr({
  7304. anchorX: anchorX,
  7305. anchorY: anchorY
  7306. });
  7307. }
  7308. };
  7309. /*
  7310. * Add specific attribute setters.
  7311. */
  7312. // only change local variables
  7313. wrapper.widthSetter = function (value) {
  7314. width = H.isNumber(value) ? value : null; // width:auto => null
  7315. };
  7316. wrapper.heightSetter = function (value) {
  7317. height = value;
  7318. };
  7319. wrapper['text-alignSetter'] = function (value) {
  7320. textAlign = value;
  7321. };
  7322. wrapper.paddingSetter = function (value) {
  7323. if (defined(value) && value !== padding) {
  7324. padding = wrapper.padding = value;
  7325. updateTextPadding();
  7326. }
  7327. };
  7328. wrapper.paddingLeftSetter = function (value) {
  7329. if (defined(value) && value !== paddingLeft) {
  7330. paddingLeft = value;
  7331. updateTextPadding();
  7332. }
  7333. };
  7334. // change local variable and prevent setting attribute on the group
  7335. wrapper.alignSetter = function (value) {
  7336. value = { left: 0, center: 0.5, right: 1 }[value];
  7337. if (value !== alignFactor) {
  7338. alignFactor = value;
  7339. // Bounding box exists, means we're dynamically changing
  7340. if (bBox) {
  7341. wrapper.attr({ x: wrapperX }); // #5134
  7342. }
  7343. }
  7344. };
  7345. // apply these to the box and the text alike
  7346. wrapper.textSetter = function (value) {
  7347. if (value !== undefined) {
  7348. text.textSetter(value);
  7349. }
  7350. updateBoxSize();
  7351. updateTextPadding();
  7352. };
  7353. // apply these to the box but not to the text
  7354. wrapper['stroke-widthSetter'] = function (value, key) {
  7355. if (value) {
  7356. needsBox = true;
  7357. }
  7358. strokeWidth = this['stroke-width'] = value;
  7359. boxAttr(key, value);
  7360. };
  7361. if (styledMode) {
  7362. wrapper.rSetter = function (value, key) {
  7363. boxAttr(key, value);
  7364. };
  7365. } else {
  7366. wrapper.strokeSetter =
  7367. wrapper.fillSetter =
  7368. wrapper.rSetter = function (value, key) {
  7369. if (key !== 'r') {
  7370. if (key === 'fill' && value) {
  7371. needsBox = true;
  7372. }
  7373. // for animation getter (#6776)
  7374. wrapper[key] = value;
  7375. }
  7376. boxAttr(key, value);
  7377. };
  7378. }
  7379. wrapper.anchorXSetter = function (value, key) {
  7380. anchorX = wrapper.anchorX = value;
  7381. boxAttr(key, Math.round(value) - getCrispAdjust() - wrapperX);
  7382. };
  7383. wrapper.anchorYSetter = function (value, key) {
  7384. anchorY = wrapper.anchorY = value;
  7385. boxAttr(key, value - wrapperY);
  7386. };
  7387. // rename attributes
  7388. wrapper.xSetter = function (value) {
  7389. wrapper.x = value; // for animation getter
  7390. if (alignFactor) {
  7391. value -= alignFactor * ((width || bBox.width) + 2 * padding);
  7392. // Force animation even when setting to the same value (#7898)
  7393. wrapper['forceAnimate:x'] = true;
  7394. }
  7395. wrapperX = Math.round(value);
  7396. wrapper.attr('translateX', wrapperX);
  7397. };
  7398. wrapper.ySetter = function (value) {
  7399. wrapperY = wrapper.y = Math.round(value);
  7400. wrapper.attr('translateY', wrapperY);
  7401. };
  7402. // Redirect certain methods to either the box or the text
  7403. var baseCss = wrapper.css;
  7404. var wrapperExtension = {
  7405. /**
  7406. * Pick up some properties and apply them to the text instead of the
  7407. * wrapper.
  7408. */
  7409. css: function (styles) {
  7410. if (styles) {
  7411. var textStyles = {};
  7412. // Create a copy to avoid altering the original object
  7413. // (#537)
  7414. styles = merge(styles);
  7415. wrapper.textProps.forEach(function (prop) {
  7416. if (styles[prop] !== undefined) {
  7417. textStyles[prop] = styles[prop];
  7418. delete styles[prop];
  7419. }
  7420. });
  7421. text.css(textStyles);
  7422. // Update existing text and box
  7423. if ('width' in textStyles) {
  7424. updateBoxSize();
  7425. }
  7426. // Keep updated (#9400)
  7427. if ('fontSize' in textStyles) {
  7428. updateBoxSize();
  7429. updateTextPadding();
  7430. }
  7431. }
  7432. return baseCss.call(wrapper, styles);
  7433. },
  7434. /*
  7435. * Return the bounding box of the box, not the group.
  7436. */
  7437. getBBox: function () {
  7438. return {
  7439. width: bBox.width + 2 * padding,
  7440. height: bBox.height + 2 * padding,
  7441. x: bBox.x - padding,
  7442. y: bBox.y - padding
  7443. };
  7444. },
  7445. /**
  7446. * Destroy and release memory.
  7447. */
  7448. destroy: function () {
  7449. // Added by button implementation
  7450. removeEvent(wrapper.element, 'mouseenter');
  7451. removeEvent(wrapper.element, 'mouseleave');
  7452. if (text) {
  7453. text = text.destroy();
  7454. }
  7455. if (box) {
  7456. box = box.destroy();
  7457. }
  7458. // Call base implementation to destroy the rest
  7459. SVGElement.prototype.destroy.call(wrapper);
  7460. // Release local pointers (#1298)
  7461. wrapper =
  7462. renderer =
  7463. updateBoxSize =
  7464. updateTextPadding =
  7465. boxAttr = null;
  7466. }
  7467. };
  7468. if (!styledMode) {
  7469. /**
  7470. * Apply the shadow to the box.
  7471. *
  7472. * @ignore
  7473. * @function Highcharts.SVGElement#shadow
  7474. *
  7475. * @return {Highcharts.SVGElement}
  7476. */
  7477. wrapperExtension.shadow = function (b) {
  7478. if (b) {
  7479. updateBoxSize();
  7480. if (box) {
  7481. box.shadow(b);
  7482. }
  7483. }
  7484. return wrapper;
  7485. };
  7486. }
  7487. return extend(wrapper, wrapperExtension);
  7488. }
  7489. }); // end SVGRenderer
  7490. // general renderer
  7491. H.Renderer = SVGRenderer;
  7492. }(Highcharts));
  7493. (function (H) {
  7494. /**
  7495. * (c) 2010-2019 Torstein Honsi
  7496. *
  7497. * License: www.highcharts.com/license
  7498. */
  7499. var attr = H.attr,
  7500. createElement = H.createElement,
  7501. css = H.css,
  7502. defined = H.defined,
  7503. extend = H.extend,
  7504. isFirefox = H.isFirefox,
  7505. isMS = H.isMS,
  7506. isWebKit = H.isWebKit,
  7507. pick = H.pick,
  7508. pInt = H.pInt,
  7509. SVGElement = H.SVGElement,
  7510. SVGRenderer = H.SVGRenderer,
  7511. win = H.win;
  7512. // Extend SvgElement for useHTML option.
  7513. extend(SVGElement.prototype, /** @lends SVGElement.prototype */ {
  7514. /**
  7515. * Apply CSS to HTML elements. This is used in text within SVG rendering and
  7516. * by the VML renderer
  7517. *
  7518. * @private
  7519. * @function Highcharts.SVGElement#htmlCss
  7520. *
  7521. * @param {Highcharts.CSSObject} styles
  7522. *
  7523. * @return {Highcharts.SVGElement}
  7524. */
  7525. htmlCss: function (styles) {
  7526. var wrapper = this,
  7527. element = wrapper.element,
  7528. // When setting or unsetting the width style, we need to update
  7529. // transform (#8809)
  7530. isSettingWidth = (
  7531. element.tagName === 'SPAN' &&
  7532. styles &&
  7533. 'width' in styles
  7534. ),
  7535. textWidth = pick(
  7536. isSettingWidth && styles.width,
  7537. undefined
  7538. ),
  7539. doTransform;
  7540. if (isSettingWidth) {
  7541. delete styles.width;
  7542. wrapper.textWidth = textWidth;
  7543. doTransform = true;
  7544. }
  7545. if (styles && styles.textOverflow === 'ellipsis') {
  7546. styles.whiteSpace = 'nowrap';
  7547. styles.overflow = 'hidden';
  7548. }
  7549. wrapper.styles = extend(wrapper.styles, styles);
  7550. css(wrapper.element, styles);
  7551. // Now that all styles are applied, to the transform
  7552. if (doTransform) {
  7553. wrapper.htmlUpdateTransform();
  7554. }
  7555. return wrapper;
  7556. },
  7557. /**
  7558. * VML and useHTML method for calculating the bounding box based on offsets.
  7559. *
  7560. * @private
  7561. * @function Highcharts.SVGElement#htmlGetBBox
  7562. *
  7563. * @param {boolean} refresh
  7564. * Whether to force a fresh value from the DOM or to use the cached
  7565. * value.
  7566. *
  7567. * @return {Highcharts.BBoxObject}
  7568. * A hash containing values for x, y, width and height.
  7569. */
  7570. htmlGetBBox: function () {
  7571. var wrapper = this,
  7572. element = wrapper.element;
  7573. return {
  7574. x: element.offsetLeft,
  7575. y: element.offsetTop,
  7576. width: element.offsetWidth,
  7577. height: element.offsetHeight
  7578. };
  7579. },
  7580. /**
  7581. * VML override private method to update elements based on internal
  7582. * properties based on SVG transform.
  7583. *
  7584. * @private
  7585. * @function Highcharts.SVGElement#htmlUpdateTransform
  7586. */
  7587. htmlUpdateTransform: function () {
  7588. // aligning non added elements is expensive
  7589. if (!this.added) {
  7590. this.alignOnAdd = true;
  7591. return;
  7592. }
  7593. var wrapper = this,
  7594. renderer = wrapper.renderer,
  7595. elem = wrapper.element,
  7596. translateX = wrapper.translateX || 0,
  7597. translateY = wrapper.translateY || 0,
  7598. x = wrapper.x || 0,
  7599. y = wrapper.y || 0,
  7600. align = wrapper.textAlign || 'left',
  7601. alignCorrection = { left: 0, center: 0.5, right: 1 }[align],
  7602. styles = wrapper.styles,
  7603. whiteSpace = styles && styles.whiteSpace;
  7604. function getTextPxLength() {
  7605. // Reset multiline/ellipsis in order to read width (#4928,
  7606. // #5417)
  7607. css(elem, {
  7608. width: '',
  7609. whiteSpace: whiteSpace || 'nowrap'
  7610. });
  7611. return elem.offsetWidth;
  7612. }
  7613. // apply translate
  7614. css(elem, {
  7615. marginLeft: translateX,
  7616. marginTop: translateY
  7617. });
  7618. if (!renderer.styledMode && wrapper.shadows) { // used in labels/tooltip
  7619. wrapper.shadows.forEach(function (shadow) {
  7620. css(shadow, {
  7621. marginLeft: translateX + 1,
  7622. marginTop: translateY + 1
  7623. });
  7624. });
  7625. }
  7626. // apply inversion
  7627. if (wrapper.inverted) { // wrapper is a group
  7628. elem.childNodes.forEach(function (child) {
  7629. renderer.invertChild(child, elem);
  7630. });
  7631. }
  7632. if (elem.tagName === 'SPAN') {
  7633. var rotation = wrapper.rotation,
  7634. baseline,
  7635. textWidth = wrapper.textWidth && pInt(wrapper.textWidth),
  7636. currentTextTransform = [
  7637. rotation,
  7638. align,
  7639. elem.innerHTML,
  7640. wrapper.textWidth,
  7641. wrapper.textAlign
  7642. ].join(',');
  7643. // Update textWidth. Use the memoized textPxLength if possible, to
  7644. // avoid the getTextPxLength function using elem.offsetWidth.
  7645. // Calling offsetWidth affects rendering time as it forces layout
  7646. // (#7656).
  7647. if (
  7648. textWidth !== wrapper.oldTextWidth &&
  7649. (
  7650. (textWidth > wrapper.oldTextWidth) ||
  7651. (wrapper.textPxLength || getTextPxLength()) > textWidth
  7652. ) && (
  7653. // Only set the width if the text is able to word-wrap, or
  7654. // text-overflow is ellipsis (#9537)
  7655. /[ \-]/.test(elem.textContent || elem.innerText) ||
  7656. elem.style.textOverflow === 'ellipsis'
  7657. )
  7658. ) { // #983, #1254
  7659. css(elem, {
  7660. width: textWidth + 'px',
  7661. display: 'block',
  7662. whiteSpace: whiteSpace || 'normal' // #3331
  7663. });
  7664. wrapper.oldTextWidth = textWidth;
  7665. wrapper.hasBoxWidthChanged = true; // #8159
  7666. } else {
  7667. wrapper.hasBoxWidthChanged = false; // #8159
  7668. }
  7669. // Do the calculations and DOM access only if properties changed
  7670. if (currentTextTransform !== wrapper.cTT) {
  7671. baseline = renderer.fontMetrics(elem.style.fontSize, elem).b;
  7672. // Renderer specific handling of span rotation, but only if we
  7673. // have something to update.
  7674. if (
  7675. defined(rotation) &&
  7676. (
  7677. (rotation !== (wrapper.oldRotation || 0)) ||
  7678. (align !== wrapper.oldAlign)
  7679. )
  7680. ) {
  7681. wrapper.setSpanRotation(
  7682. rotation,
  7683. alignCorrection,
  7684. baseline
  7685. );
  7686. }
  7687. wrapper.getSpanCorrection(
  7688. // Avoid elem.offsetWidth if we can, it affects rendering
  7689. // time heavily (#7656)
  7690. (
  7691. (!defined(rotation) && wrapper.textPxLength) || // #7920
  7692. elem.offsetWidth
  7693. ),
  7694. baseline,
  7695. alignCorrection,
  7696. rotation,
  7697. align
  7698. );
  7699. }
  7700. // apply position with correction
  7701. css(elem, {
  7702. left: (x + (wrapper.xCorr || 0)) + 'px',
  7703. top: (y + (wrapper.yCorr || 0)) + 'px'
  7704. });
  7705. // record current text transform
  7706. wrapper.cTT = currentTextTransform;
  7707. wrapper.oldRotation = rotation;
  7708. wrapper.oldAlign = align;
  7709. }
  7710. },
  7711. /**
  7712. * Set the rotation of an individual HTML span.
  7713. *
  7714. * @private
  7715. * @function Highcharts.SVGElement#setSpanRotation
  7716. *
  7717. * @param {number} rotation
  7718. *
  7719. * @param {number} alignCorrection
  7720. *
  7721. * @param {number} baseline
  7722. */
  7723. setSpanRotation: function (rotation, alignCorrection, baseline) {
  7724. var rotationStyle = {},
  7725. cssTransformKey = this.renderer.getTransformKey();
  7726. rotationStyle[cssTransformKey] = rotationStyle.transform =
  7727. 'rotate(' + rotation + 'deg)';
  7728. rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] =
  7729. rotationStyle.transformOrigin =
  7730. (alignCorrection * 100) + '% ' + baseline + 'px';
  7731. css(this.element, rotationStyle);
  7732. },
  7733. /**
  7734. * Get the correction in X and Y positioning as the element is rotated.
  7735. *
  7736. * @private
  7737. * @function Highcharts.SVGElement#getSpanCorrection
  7738. *
  7739. * @param {number} width
  7740. *
  7741. * @param {number} baseline
  7742. *
  7743. * @param {number} alignCorrection
  7744. */
  7745. getSpanCorrection: function (width, baseline, alignCorrection) {
  7746. this.xCorr = -width * alignCorrection;
  7747. this.yCorr = -baseline;
  7748. }
  7749. });
  7750. // Extend SvgRenderer for useHTML option.
  7751. extend(SVGRenderer.prototype, /** @lends SVGRenderer.prototype */ {
  7752. /**
  7753. * @private
  7754. * @function Highcharts.SVGRenderer#getTransformKey
  7755. *
  7756. * @return {string}
  7757. */
  7758. getTransformKey: function () {
  7759. return isMS && !/Edge/.test(win.navigator.userAgent) ?
  7760. '-ms-transform' :
  7761. isWebKit ?
  7762. '-webkit-transform' :
  7763. isFirefox ?
  7764. 'MozTransform' :
  7765. win.opera ?
  7766. '-o-transform' :
  7767. '';
  7768. },
  7769. /**
  7770. * Create HTML text node. This is used by the VML renderer as well as the
  7771. * SVG renderer through the useHTML option.
  7772. *
  7773. * @private
  7774. * @function Highcharts.SVGRenderer#html
  7775. *
  7776. * @param {string} str
  7777. * The text of (subset) HTML to draw.
  7778. *
  7779. * @param {number} x
  7780. * The x position of the text's lower left corner.
  7781. *
  7782. * @param {number} y
  7783. * The y position of the text's lower left corner.
  7784. *
  7785. * @return {Highcharts.HTMLDOMElement}
  7786. */
  7787. html: function (str, x, y) {
  7788. var wrapper = this.createElement('span'),
  7789. element = wrapper.element,
  7790. renderer = wrapper.renderer,
  7791. isSVG = renderer.isSVG,
  7792. addSetters = function (element, style) {
  7793. // These properties are set as attributes on the SVG group, and
  7794. // as identical CSS properties on the div. (#3542)
  7795. ['opacity', 'visibility'].forEach(function (prop) {
  7796. element[prop + 'Setter'] = function (
  7797. value,
  7798. key,
  7799. elem
  7800. ) {
  7801. SVGElement.prototype[prop + 'Setter']
  7802. .call(this, value, key, elem);
  7803. style[key] = value;
  7804. };
  7805. });
  7806. element.addedSetters = true;
  7807. },
  7808. chart = H.charts[renderer.chartIndex],
  7809. styledMode = chart && chart.styledMode;
  7810. // Text setter
  7811. wrapper.textSetter = function (value) {
  7812. if (value !== element.innerHTML) {
  7813. delete this.bBox;
  7814. }
  7815. this.textStr = value;
  7816. element.innerHTML = pick(value, '');
  7817. wrapper.doTransform = true;
  7818. };
  7819. // Add setters for the element itself (#4938)
  7820. if (isSVG) { // #4938, only for HTML within SVG
  7821. addSetters(wrapper, wrapper.element.style);
  7822. }
  7823. // Various setters which rely on update transform
  7824. wrapper.xSetter =
  7825. wrapper.ySetter =
  7826. wrapper.alignSetter =
  7827. wrapper.rotationSetter =
  7828. function (value, key) {
  7829. if (key === 'align') {
  7830. // Do not overwrite the SVGElement.align method. Same as VML.
  7831. key = 'textAlign';
  7832. }
  7833. wrapper[key] = value;
  7834. wrapper.doTransform = true;
  7835. };
  7836. // Runs at the end of .attr()
  7837. wrapper.afterSetters = function () {
  7838. // Update transform. Do this outside the loop to prevent redundant
  7839. // updating for batch setting of attributes.
  7840. if (this.doTransform) {
  7841. this.htmlUpdateTransform();
  7842. this.doTransform = false;
  7843. }
  7844. };
  7845. // Set the default attributes
  7846. wrapper
  7847. .attr({
  7848. text: str,
  7849. x: Math.round(x),
  7850. y: Math.round(y)
  7851. })
  7852. .css({
  7853. position: 'absolute'
  7854. });
  7855. if (!styledMode) {
  7856. wrapper.css({
  7857. fontFamily: this.style.fontFamily,
  7858. fontSize: this.style.fontSize
  7859. });
  7860. }
  7861. // Keep the whiteSpace style outside the wrapper.styles collection
  7862. element.style.whiteSpace = 'nowrap';
  7863. // Use the HTML specific .css method
  7864. wrapper.css = wrapper.htmlCss;
  7865. // This is specific for HTML within SVG
  7866. if (isSVG) {
  7867. wrapper.add = function (svgGroupWrapper) {
  7868. var htmlGroup,
  7869. container = renderer.box.parentNode,
  7870. parentGroup,
  7871. parents = [];
  7872. this.parentGroup = svgGroupWrapper;
  7873. // Create a mock group to hold the HTML elements
  7874. if (svgGroupWrapper) {
  7875. htmlGroup = svgGroupWrapper.div;
  7876. if (!htmlGroup) {
  7877. // Read the parent chain into an array and read from top
  7878. // down
  7879. parentGroup = svgGroupWrapper;
  7880. while (parentGroup) {
  7881. parents.push(parentGroup);
  7882. // Move up to the next parent group
  7883. parentGroup = parentGroup.parentGroup;
  7884. }
  7885. // Ensure dynamically updating position when any parent
  7886. // is translated
  7887. parents.reverse().forEach(function (parentGroup) {
  7888. var htmlGroupStyle,
  7889. cls = attr(parentGroup.element, 'class');
  7890. // Common translate setter for X and Y on the HTML
  7891. // group. Reverted the fix for #6957 du to
  7892. // positioning problems and offline export (#7254,
  7893. // #7280, #7529)
  7894. function translateSetter(value, key) {
  7895. parentGroup[key] = value;
  7896. if (key === 'translateX') {
  7897. htmlGroupStyle.left = value + 'px';
  7898. } else {
  7899. htmlGroupStyle.top = value + 'px';
  7900. }
  7901. parentGroup.doTransform = true;
  7902. }
  7903. if (cls) {
  7904. cls = { className: cls };
  7905. } // else null
  7906. // Create a HTML div and append it to the parent div
  7907. // to emulate the SVG group structure
  7908. htmlGroup =
  7909. parentGroup.div =
  7910. parentGroup.div || createElement('div', cls, {
  7911. position: 'absolute',
  7912. left: (parentGroup.translateX || 0) + 'px',
  7913. top: (parentGroup.translateY || 0) + 'px',
  7914. display: parentGroup.display,
  7915. opacity: parentGroup.opacity, // #5075
  7916. pointerEvents: (
  7917. parentGroup.styles &&
  7918. parentGroup.styles.pointerEvents
  7919. ) // #5595
  7920. // the top group is appended to container
  7921. }, htmlGroup || container);
  7922. // Shortcut
  7923. htmlGroupStyle = htmlGroup.style;
  7924. // Set listeners to update the HTML div's position
  7925. // whenever the SVG group position is changed.
  7926. extend(parentGroup, {
  7927. // (#7287) Pass htmlGroup to use
  7928. // the related group
  7929. classSetter: (function (htmlGroup) {
  7930. return function (value) {
  7931. this.element.setAttribute(
  7932. 'class',
  7933. value
  7934. );
  7935. htmlGroup.className = value;
  7936. };
  7937. }(htmlGroup)),
  7938. on: function () {
  7939. if (parents[0].div) { // #6418
  7940. wrapper.on.apply(
  7941. { element: parents[0].div },
  7942. arguments
  7943. );
  7944. }
  7945. return parentGroup;
  7946. },
  7947. translateXSetter: translateSetter,
  7948. translateYSetter: translateSetter
  7949. });
  7950. if (!parentGroup.addedSetters) {
  7951. addSetters(parentGroup, htmlGroupStyle);
  7952. }
  7953. });
  7954. }
  7955. } else {
  7956. htmlGroup = container;
  7957. }
  7958. htmlGroup.appendChild(element);
  7959. // Shared with VML:
  7960. wrapper.added = true;
  7961. if (wrapper.alignOnAdd) {
  7962. wrapper.htmlUpdateTransform();
  7963. }
  7964. return wrapper;
  7965. };
  7966. }
  7967. return wrapper;
  7968. }
  7969. });
  7970. }(Highcharts));
  7971. (function (Highcharts) {
  7972. /**
  7973. * (c) 2010-2019 Torstein Honsi
  7974. *
  7975. * License: www.highcharts.com/license
  7976. */
  7977. /**
  7978. * Normalized interval.
  7979. *
  7980. * @interface Highcharts.NormalizedIntervalObject
  7981. *//**
  7982. * The interval in axis values (ms).
  7983. *
  7984. * @name Highcharts.NormalizedIntervalObject#unitRange
  7985. * @type {number}
  7986. *//**
  7987. * The count.
  7988. *
  7989. * @name Highcharts.NormalizedIntervalObject#count
  7990. * @type {number}
  7991. */
  7992. /**
  7993. * Function of an additional date format specifier.
  7994. *
  7995. * @callback Highcharts.TimeFormatCallbackFunction
  7996. *
  7997. * @param {number} timestamp
  7998. * The time to format.
  7999. *
  8000. * @return {string}
  8001. * The formatted portion of the date.
  8002. */
  8003. /**
  8004. * Additonal time tick information.
  8005. *
  8006. * @interface Highcharts.TimeTicksInfoObject
  8007. * @augments Highcharts.NormalizedIntervalObject
  8008. *//**
  8009. * @name Highcharts.TimeTicksInfoObject#higherRanks
  8010. * @type {Array<string>}
  8011. *//**
  8012. * @name Highcharts.TimeTicksInfoObject#totalRange
  8013. * @type {number}
  8014. */
  8015. /**
  8016. * Time ticks.
  8017. *
  8018. * @interface Highcharts.TimeTicksObject
  8019. * @augments Array<number>
  8020. *//**
  8021. * @name Highcharts.TimeTicksObject#info
  8022. * @type {Highcharts.TimeTicksInfoObject}
  8023. */
  8024. var H = Highcharts,
  8025. defined = H.defined,
  8026. extend = H.extend,
  8027. merge = H.merge,
  8028. pick = H.pick,
  8029. timeUnits = H.timeUnits,
  8030. win = H.win;
  8031. /**
  8032. * The Time class. Time settings are applied in general for each page using
  8033. * `Highcharts.setOptions`, or individually for each Chart item through the
  8034. * [time](https://api.highcharts.com/highcharts/time) options set.
  8035. *
  8036. * The Time object is available from {@link Highcharts.Chart#time},
  8037. * which refers to `Highcharts.time` if no individual time settings are
  8038. * applied.
  8039. *
  8040. * @example
  8041. * // Apply time settings globally
  8042. * Highcharts.setOptions({
  8043. * time: {
  8044. * timezone: 'Europe/London'
  8045. * }
  8046. * });
  8047. *
  8048. * // Apply time settings by instance
  8049. * var chart = Highcharts.chart('container', {
  8050. * time: {
  8051. * timezone: 'America/New_York'
  8052. * },
  8053. * series: [{
  8054. * data: [1, 4, 3, 5]
  8055. * }]
  8056. * });
  8057. *
  8058. * // Use the Time object
  8059. * console.log(
  8060. * 'Current time in New York',
  8061. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  8062. * );
  8063. *
  8064. * @class
  8065. * @name Highcharts.Time
  8066. *
  8067. * @param {Highcharts.TimeOptions} options
  8068. * Time options as defined in [chart.options.time](/highcharts/time).
  8069. *
  8070. * @since 6.0.5
  8071. */
  8072. Highcharts.Time = function (options) {
  8073. this.update(options, false);
  8074. };
  8075. Highcharts.Time.prototype = {
  8076. /**
  8077. * Time options that can apply globally or to individual charts. These
  8078. * settings affect how `datetime` axes are laid out, how tooltips are
  8079. * formatted, how series
  8080. * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
  8081. * the Highstock range selector handles time.
  8082. *
  8083. * The common use case is that all charts in the same Highcharts object
  8084. * share the same time settings, in which case the global settings are set
  8085. * using `setOptions`.
  8086. *
  8087. * ```js
  8088. * // Apply time settings globally
  8089. * Highcharts.setOptions({
  8090. * time: {
  8091. * timezone: 'Europe/London'
  8092. * }
  8093. * });
  8094. * // Apply time settings by instance
  8095. * var chart = Highcharts.chart('container', {
  8096. * time: {
  8097. * timezone: 'America/New_York'
  8098. * },
  8099. * series: [{
  8100. * data: [1, 4, 3, 5]
  8101. * }]
  8102. * });
  8103. *
  8104. * // Use the Time object
  8105. * console.log(
  8106. * 'Current time in New York',
  8107. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  8108. * );
  8109. * ```
  8110. *
  8111. * Since v6.0.5, the time options were moved from the `global` obect to the
  8112. * `time` object, and time options can be set on each individual chart.
  8113. *
  8114. * @sample {highcharts|highstock}
  8115. * highcharts/time/timezone/
  8116. * Set the timezone globally
  8117. * @sample {highcharts}
  8118. * highcharts/time/individual/
  8119. * Set the timezone per chart instance
  8120. * @sample {highstock}
  8121. * stock/time/individual/
  8122. * Set the timezone per chart instance
  8123. *
  8124. * @since 6.0.5
  8125. * @apioption time
  8126. */
  8127. /**
  8128. * Whether to use UTC time for axis scaling, tickmark placement and
  8129. * time display in `Highcharts.dateFormat`. Advantages of using UTC
  8130. * is that the time displays equally regardless of the user agent's
  8131. * time zone settings. Local time can be used when the data is loaded
  8132. * in real time or when correct Daylight Saving Time transitions are
  8133. * required.
  8134. *
  8135. * @sample {highcharts} highcharts/time/useutc-true/
  8136. * True by default
  8137. * @sample {highcharts} highcharts/time/useutc-false/
  8138. * False
  8139. *
  8140. * @type {boolean}
  8141. * @default true
  8142. * @apioption time.useUTC
  8143. */
  8144. /**
  8145. * A custom `Date` class for advanced date handling. For example,
  8146. * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
  8147. * handle Jalali dates.
  8148. *
  8149. * @type {*}
  8150. * @since 4.0.4
  8151. * @product highcharts highstock gantt
  8152. * @apioption time.Date
  8153. */
  8154. /**
  8155. * A callback to return the time zone offset for a given datetime. It
  8156. * takes the timestamp in terms of milliseconds since January 1 1970,
  8157. * and returns the timezone offset in minutes. This provides a hook
  8158. * for drawing time based charts in specific time zones using their
  8159. * local DST crossover dates, with the help of external libraries.
  8160. *
  8161. * @see [global.timezoneOffset](#global.timezoneOffset)
  8162. *
  8163. * @sample {highcharts|highstock} highcharts/time/gettimezoneoffset/
  8164. * Use moment.js to draw Oslo time regardless of browser locale
  8165. *
  8166. * @type {Function}
  8167. * @since 4.1.0
  8168. * @product highcharts highstock gantt
  8169. * @apioption time.getTimezoneOffset
  8170. */
  8171. /**
  8172. * Requires [moment.js](http://momentjs.com/). If the timezone option
  8173. * is specified, it creates a default
  8174. * [getTimezoneOffset](#time.getTimezoneOffset) function that looks
  8175. * up the specified timezone in moment.js. If moment.js is not included,
  8176. * this throws a Highcharts error in the console, but does not crash the
  8177. * chart.
  8178. *
  8179. * @see [getTimezoneOffset](#time.getTimezoneOffset)
  8180. *
  8181. * @sample {highcharts|highstock} highcharts/time/timezone/
  8182. * Europe/Oslo
  8183. *
  8184. * @type {string}
  8185. * @since 5.0.7
  8186. * @product highcharts highstock gantt
  8187. * @apioption time.timezone
  8188. */
  8189. /**
  8190. * The timezone offset in minutes. Positive values are west, negative
  8191. * values are east of UTC, as in the ECMAScript
  8192. * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
  8193. * method. Use this to display UTC based data in a predefined time zone.
  8194. *
  8195. * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
  8196. *
  8197. * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
  8198. * Timezone offset
  8199. *
  8200. * @type {number}
  8201. * @default 0
  8202. * @since 3.0.8
  8203. * @product highcharts highstock gantt
  8204. * @apioption time.timezoneOffset
  8205. */
  8206. defaultOptions: {},
  8207. /**
  8208. * Update the Time object with current options. It is called internally on
  8209. * initiating Highcharts, after running `Highcharts.setOptions` and on
  8210. * `Chart.update`.
  8211. *
  8212. * @private
  8213. * @function Highcharts.Time#update
  8214. *
  8215. * @param {Highcharts.TimeOptions} options
  8216. */
  8217. update: function (options) {
  8218. var useUTC = pick(options && options.useUTC, true),
  8219. time = this;
  8220. this.options = options = merge(true, this.options || {}, options);
  8221. // Allow using a different Date class
  8222. this.Date = options.Date || win.Date;
  8223. this.useUTC = useUTC;
  8224. this.timezoneOffset = useUTC && options.timezoneOffset;
  8225. /**
  8226. * Get the time zone offset based on the current timezone information as
  8227. * set in the global options.
  8228. *
  8229. * @function Highcharts.Time#getTimezoneOffset
  8230. *
  8231. * @param {number} timestamp
  8232. * The JavaScript timestamp to inspect.
  8233. *
  8234. * @return {number}
  8235. * The timezone offset in minutes compared to UTC.
  8236. */
  8237. this.getTimezoneOffset = this.timezoneOffsetFunction();
  8238. /*
  8239. * The time object has options allowing for variable time zones, meaning
  8240. * the axis ticks or series data needs to consider this.
  8241. */
  8242. this.variableTimezone = !!(
  8243. !useUTC ||
  8244. options.getTimezoneOffset ||
  8245. options.timezone
  8246. );
  8247. // UTC time with timezone handling
  8248. if (this.variableTimezone || this.timezoneOffset) {
  8249. this.get = function (unit, date) {
  8250. var realMs = date.getTime(),
  8251. ms = realMs - time.getTimezoneOffset(date),
  8252. ret;
  8253. date.setTime(ms); // Temporary adjust to timezone
  8254. ret = date['getUTC' + unit]();
  8255. date.setTime(realMs); // Reset
  8256. return ret;
  8257. };
  8258. this.set = function (unit, date, value) {
  8259. var ms, offset, newOffset;
  8260. // For lower order time units, just set it directly using local
  8261. // time
  8262. if (
  8263. unit === 'Milliseconds' ||
  8264. unit === 'Seconds' ||
  8265. // If we're dealting with minutes, we only need to
  8266. // consider timezone if we're in Indian time zones with
  8267. // half-hour offsets (#8768).
  8268. (
  8269. unit === 'Minutes' &&
  8270. date.getTimezoneOffset() % 60 === 0
  8271. )
  8272. ) {
  8273. date['set' + unit](value);
  8274. // Higher order time units need to take the time zone into
  8275. // account
  8276. } else {
  8277. // Adjust by timezone
  8278. offset = time.getTimezoneOffset(date);
  8279. ms = date.getTime() - offset;
  8280. date.setTime(ms);
  8281. date['setUTC' + unit](value);
  8282. newOffset = time.getTimezoneOffset(date);
  8283. ms = date.getTime() + newOffset;
  8284. date.setTime(ms);
  8285. }
  8286. };
  8287. // UTC time with no timezone handling
  8288. } else if (useUTC) {
  8289. this.get = function (unit, date) {
  8290. return date['getUTC' + unit]();
  8291. };
  8292. this.set = function (unit, date, value) {
  8293. return date['setUTC' + unit](value);
  8294. };
  8295. // Local time
  8296. } else {
  8297. this.get = function (unit, date) {
  8298. return date['get' + unit]();
  8299. };
  8300. this.set = function (unit, date, value) {
  8301. return date['set' + unit](value);
  8302. };
  8303. }
  8304. },
  8305. /**
  8306. * Make a time and returns milliseconds. Interprets the inputs as UTC time,
  8307. * local time or a specific timezone time depending on the current time
  8308. * settings.
  8309. *
  8310. * @function Highcharts.Time#makeTime
  8311. *
  8312. * @param {number} year
  8313. * The year
  8314. *
  8315. * @param {number} month
  8316. * The month. Zero-based, so January is 0.
  8317. *
  8318. * @param {number} [date=1]
  8319. * The day of the month
  8320. *
  8321. * @param {number} [hours=0]
  8322. * The hour of the day, 0-23.
  8323. *
  8324. * @param {number} [minutes=0]
  8325. * The minutes
  8326. *
  8327. * @param {number} [seconds=0]
  8328. * The seconds
  8329. *
  8330. * @return {number}
  8331. * The time in milliseconds since January 1st 1970.
  8332. */
  8333. makeTime: function (year, month, date, hours, minutes, seconds) {
  8334. var d, offset, newOffset;
  8335. if (this.useUTC) {
  8336. d = this.Date.UTC.apply(0, arguments);
  8337. offset = this.getTimezoneOffset(d);
  8338. d += offset;
  8339. newOffset = this.getTimezoneOffset(d);
  8340. if (offset !== newOffset) {
  8341. d += newOffset - offset;
  8342. // A special case for transitioning from summer time to winter time.
  8343. // When the clock is set back, the same time is repeated twice, i.e.
  8344. // 02:30 am is repeated since the clock is set back from 3 am to
  8345. // 2 am. We need to make the same time as local Date does.
  8346. } else if (
  8347. offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
  8348. !H.isSafari
  8349. ) {
  8350. d -= 36e5;
  8351. }
  8352. } else {
  8353. d = new this.Date(
  8354. year,
  8355. month,
  8356. pick(date, 1),
  8357. pick(hours, 0),
  8358. pick(minutes, 0),
  8359. pick(seconds, 0)
  8360. ).getTime();
  8361. }
  8362. return d;
  8363. },
  8364. /**
  8365. * Sets the getTimezoneOffset function. If the `timezone` option is set, a
  8366. * default getTimezoneOffset function with that timezone is returned. If
  8367. * a `getTimezoneOffset` option is defined, it is returned. If neither are
  8368. * specified, the function using the `timezoneOffset` option or 0 offset is
  8369. * returned.
  8370. *
  8371. * @private
  8372. * @function Highcharts.Time#timezoneOffsetFunction
  8373. *
  8374. * @return {Function}
  8375. * A getTimezoneOffset function
  8376. */
  8377. timezoneOffsetFunction: function () {
  8378. var time = this,
  8379. options = this.options,
  8380. moment = win.moment;
  8381. if (!this.useUTC) {
  8382. return function (timestamp) {
  8383. return new Date(timestamp).getTimezoneOffset() * 60000;
  8384. };
  8385. }
  8386. if (options.timezone) {
  8387. if (!moment) {
  8388. // getTimezoneOffset-function stays undefined because it depends
  8389. // on Moment.js
  8390. H.error(25);
  8391. } else {
  8392. return function (timestamp) {
  8393. return -moment.tz(
  8394. timestamp,
  8395. options.timezone
  8396. ).utcOffset() * 60000;
  8397. };
  8398. }
  8399. }
  8400. // If not timezone is set, look for the getTimezoneOffset callback
  8401. if (this.useUTC && options.getTimezoneOffset) {
  8402. return function (timestamp) {
  8403. return options.getTimezoneOffset(timestamp) * 60000;
  8404. };
  8405. }
  8406. // Last, use the `timezoneOffset` option if set
  8407. return function () {
  8408. return (time.timezoneOffset || 0) * 60000;
  8409. };
  8410. },
  8411. /**
  8412. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970)
  8413. * into a human readable date string. The format is a subset of the formats
  8414. * for PHP's [strftime](http://www.php.net/manual/en/function.strftime.php)
  8415. * function. Additional formats can be given in the
  8416. * {@link Highcharts.dateFormats} hook.
  8417. *
  8418. * @function Highcharts.Time#dateFormat
  8419. *
  8420. * @param {string} [format]
  8421. * The desired format where various time representations are
  8422. * prefixed with %.
  8423. *
  8424. * @param {number} timestamp
  8425. * The JavaScript timestamp.
  8426. *
  8427. * @param {boolean} [capitalize=false]
  8428. * Upper case first letter in the return.
  8429. *
  8430. * @return {string}
  8431. * The formatted date.
  8432. */
  8433. dateFormat: function (format, timestamp, capitalize) {
  8434. if (!H.defined(timestamp) || isNaN(timestamp)) {
  8435. return H.defaultOptions.lang.invalidDate || '';
  8436. }
  8437. format = H.pick(format, '%Y-%m-%d %H:%M:%S');
  8438. var time = this,
  8439. date = new this.Date(timestamp),
  8440. // get the basic time values
  8441. hours = this.get('Hours', date),
  8442. day = this.get('Day', date),
  8443. dayOfMonth = this.get('Date', date),
  8444. month = this.get('Month', date),
  8445. fullYear = this.get('FullYear', date),
  8446. lang = H.defaultOptions.lang,
  8447. langWeekdays = lang.weekdays,
  8448. shortWeekdays = lang.shortWeekdays,
  8449. pad = H.pad,
  8450. // List all format keys. Custom formats can be added from the
  8451. // outside.
  8452. replacements = H.extend(
  8453. {
  8454. // Day
  8455. // Short weekday, like 'Mon'
  8456. 'a': shortWeekdays ?
  8457. shortWeekdays[day] :
  8458. langWeekdays[day].substr(0, 3),
  8459. // Long weekday, like 'Monday'
  8460. 'A': langWeekdays[day],
  8461. // Two digit day of the month, 01 to 31
  8462. 'd': pad(dayOfMonth),
  8463. // Day of the month, 1 through 31
  8464. 'e': pad(dayOfMonth, 2, ' '),
  8465. 'w': day,
  8466. // Week (none implemented)
  8467. // 'W': weekNumber(),
  8468. // Month
  8469. // Short month, like 'Jan'
  8470. 'b': lang.shortMonths[month],
  8471. // Long month, like 'January'
  8472. 'B': lang.months[month],
  8473. // Two digit month number, 01 through 12
  8474. 'm': pad(month + 1),
  8475. // Month number, 1 through 12 (#8150)
  8476. 'o': month + 1,
  8477. // Year
  8478. // Two digits year, like 09 for 2009
  8479. 'y': fullYear.toString().substr(2, 2),
  8480. // Four digits year, like 2009
  8481. 'Y': fullYear,
  8482. // Time
  8483. // Two digits hours in 24h format, 00 through 23
  8484. 'H': pad(hours),
  8485. // Hours in 24h format, 0 through 23
  8486. 'k': hours,
  8487. // Two digits hours in 12h format, 00 through 11
  8488. 'I': pad((hours % 12) || 12),
  8489. // Hours in 12h format, 1 through 12
  8490. 'l': (hours % 12) || 12,
  8491. // Two digits minutes, 00 through 59
  8492. 'M': pad(time.get('Minutes', date)),
  8493. // Upper case AM or PM
  8494. 'p': hours < 12 ? 'AM' : 'PM',
  8495. // Lower case AM or PM
  8496. 'P': hours < 12 ? 'am' : 'pm',
  8497. // Two digits seconds, 00 through 59
  8498. 'S': pad(date.getSeconds()),
  8499. // Milliseconds (naming from Ruby)
  8500. 'L': pad(Math.floor(timestamp % 1000), 3)
  8501. },
  8502. /**
  8503. * A hook for defining additional date format specifiers. New
  8504. * specifiers are defined as key-value pairs by using the
  8505. * specifier as key, and a function which takes the timestamp as
  8506. * value. This function returns the formatted portion of the
  8507. * date.
  8508. *
  8509. * @sample highcharts/global/dateformats/
  8510. * Adding support for week number
  8511. *
  8512. * @name Highcharts.dateFormats
  8513. * @type {Highcharts.Dictionary<Highcharts.TimeFormatCallbackFunction>}
  8514. */
  8515. H.dateFormats
  8516. );
  8517. // Do the replaces
  8518. H.objectEach(replacements, function (val, key) {
  8519. // Regex would do it in one line, but this is faster
  8520. while (format.indexOf('%' + key) !== -1) {
  8521. format = format.replace(
  8522. '%' + key,
  8523. typeof val === 'function' ? val.call(time, timestamp) : val
  8524. );
  8525. }
  8526. });
  8527. // Optionally capitalize the string and return
  8528. return capitalize ?
  8529. format.substr(0, 1).toUpperCase() + format.substr(1) :
  8530. format;
  8531. },
  8532. /**
  8533. * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
  8534. * an object.
  8535. * @param {String|Array|Object} f General format description
  8536. * @return {Object} The object definition
  8537. */
  8538. resolveDTLFormat: function (f) {
  8539. if (!H.isObject(f, true)) {
  8540. f = H.splat(f);
  8541. return {
  8542. main: f[0],
  8543. from: f[1],
  8544. to: f[2]
  8545. };
  8546. }
  8547. return f;
  8548. },
  8549. /**
  8550. * Return an array with time positions distributed on round time values
  8551. * right and right after min and max. Used in datetime axes as well as for
  8552. * grouping data on a datetime axis.
  8553. *
  8554. * @function Highcharts.Time#getTimeTicks
  8555. *
  8556. * @param {Highcharts.NormalizedIntervalObject} normalizedInterval
  8557. * The interval in axis values (ms) and the count
  8558. *
  8559. * @param {number} [min]
  8560. * The minimum in axis values
  8561. *
  8562. * @param {number} [max]
  8563. * The maximum in axis values
  8564. *
  8565. * @param {number} [startOfWeek=1]
  8566. *
  8567. * @return {Highcharts.TimeTicksObject}
  8568. */
  8569. getTimeTicks: function (
  8570. normalizedInterval,
  8571. min,
  8572. max,
  8573. startOfWeek
  8574. ) {
  8575. var time = this,
  8576. Date = time.Date,
  8577. tickPositions = [],
  8578. i,
  8579. higherRanks = {},
  8580. minYear, // used in months and years as a basis for Date.UTC()
  8581. // When crossing DST, use the max. Resolves #6278.
  8582. minDate = new Date(min),
  8583. interval = normalizedInterval.unitRange,
  8584. count = normalizedInterval.count || 1,
  8585. variableDayLength,
  8586. minDay;
  8587. startOfWeek = pick(startOfWeek, 1);
  8588. if (defined(min)) { // #1300
  8589. time.set(
  8590. 'Milliseconds',
  8591. minDate,
  8592. interval >= timeUnits.second ?
  8593. 0 : // #3935
  8594. count * Math.floor(
  8595. time.get('Milliseconds', minDate) / count
  8596. )
  8597. ); // #3652, #3654
  8598. if (interval >= timeUnits.second) { // second
  8599. time.set(
  8600. 'Seconds',
  8601. minDate,
  8602. interval >= timeUnits.minute ?
  8603. 0 : // #3935
  8604. count * Math.floor(time.get('Seconds', minDate) / count)
  8605. );
  8606. }
  8607. if (interval >= timeUnits.minute) { // minute
  8608. time.set(
  8609. 'Minutes',
  8610. minDate,
  8611. interval >= timeUnits.hour ?
  8612. 0 :
  8613. count * Math.floor(time.get('Minutes', minDate) / count)
  8614. );
  8615. }
  8616. if (interval >= timeUnits.hour) { // hour
  8617. time.set(
  8618. 'Hours',
  8619. minDate,
  8620. interval >= timeUnits.day ?
  8621. 0 :
  8622. count * Math.floor(
  8623. time.get('Hours', minDate) / count
  8624. )
  8625. );
  8626. }
  8627. if (interval >= timeUnits.day) { // day
  8628. time.set(
  8629. 'Date',
  8630. minDate,
  8631. interval >= timeUnits.month ?
  8632. 1 :
  8633. Math.max(
  8634. 1,
  8635. count * Math.floor(
  8636. time.get('Date', minDate) / count
  8637. )
  8638. )
  8639. );
  8640. }
  8641. if (interval >= timeUnits.month) { // month
  8642. time.set(
  8643. 'Month',
  8644. minDate,
  8645. interval >= timeUnits.year ? 0 :
  8646. count * Math.floor(time.get('Month', minDate) / count)
  8647. );
  8648. minYear = time.get('FullYear', minDate);
  8649. }
  8650. if (interval >= timeUnits.year) { // year
  8651. minYear -= minYear % count;
  8652. time.set('FullYear', minDate, minYear);
  8653. }
  8654. // week is a special case that runs outside the hierarchy
  8655. if (interval === timeUnits.week) {
  8656. // get start of current week, independent of count
  8657. minDay = time.get('Day', minDate);
  8658. time.set(
  8659. 'Date',
  8660. minDate,
  8661. (
  8662. time.get('Date', minDate) -
  8663. minDay + startOfWeek +
  8664. // We don't want to skip days that are before
  8665. // startOfWeek (#7051)
  8666. (minDay < startOfWeek ? -7 : 0)
  8667. )
  8668. );
  8669. }
  8670. // Get basics for variable time spans
  8671. minYear = time.get('FullYear', minDate);
  8672. var minMonth = time.get('Month', minDate),
  8673. minDateDate = time.get('Date', minDate),
  8674. minHours = time.get('Hours', minDate);
  8675. // Redefine min to the floored/rounded minimum time (#7432)
  8676. min = minDate.getTime();
  8677. // Handle local timezone offset
  8678. if (time.variableTimezone) {
  8679. // Detect whether we need to take the DST crossover into
  8680. // consideration. If we're crossing over DST, the day length may
  8681. // be 23h or 25h and we need to compute the exact clock time for
  8682. // each tick instead of just adding hours. This comes at a cost,
  8683. // so first we find out if it is needed (#4951).
  8684. variableDayLength = (
  8685. // Long range, assume we're crossing over.
  8686. max - min > 4 * timeUnits.month ||
  8687. // Short range, check if min and max are in different time
  8688. // zones.
  8689. time.getTimezoneOffset(min) !== time.getTimezoneOffset(max)
  8690. );
  8691. }
  8692. // Iterate and add tick positions at appropriate values
  8693. var t = minDate.getTime();
  8694. i = 1;
  8695. while (t < max) {
  8696. tickPositions.push(t);
  8697. // if the interval is years, use Date.UTC to increase years
  8698. if (interval === timeUnits.year) {
  8699. t = time.makeTime(minYear + i * count, 0);
  8700. // if the interval is months, use Date.UTC to increase months
  8701. } else if (interval === timeUnits.month) {
  8702. t = time.makeTime(minYear, minMonth + i * count);
  8703. // if we're using global time, the interval is not fixed as it
  8704. // jumps one hour at the DST crossover
  8705. } else if (
  8706. variableDayLength &&
  8707. (interval === timeUnits.day || interval === timeUnits.week)
  8708. ) {
  8709. t = time.makeTime(
  8710. minYear,
  8711. minMonth,
  8712. minDateDate +
  8713. i * count * (interval === timeUnits.day ? 1 : 7)
  8714. );
  8715. } else if (
  8716. variableDayLength &&
  8717. interval === timeUnits.hour &&
  8718. count > 1
  8719. ) {
  8720. // make sure higher ranks are preserved across DST (#6797,
  8721. // #7621)
  8722. t = time.makeTime(
  8723. minYear,
  8724. minMonth,
  8725. minDateDate,
  8726. minHours + i * count
  8727. );
  8728. // else, the interval is fixed and we use simple addition
  8729. } else {
  8730. t += interval * count;
  8731. }
  8732. i++;
  8733. }
  8734. // push the last time
  8735. tickPositions.push(t);
  8736. // Handle higher ranks. Mark new days if the time is on midnight
  8737. // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
  8738. // to prevent looping over dense data grouping (#6156).
  8739. if (interval <= timeUnits.hour && tickPositions.length < 10000) {
  8740. tickPositions.forEach(function (t) {
  8741. if (
  8742. // Speed optimization, no need to run dateFormat unless
  8743. // we're on a full or half hour
  8744. t % 1800000 === 0 &&
  8745. // Check for local or global midnight
  8746. time.dateFormat('%H%M%S%L', t) === '000000000'
  8747. ) {
  8748. higherRanks[t] = 'day';
  8749. }
  8750. });
  8751. }
  8752. }
  8753. // record information on the chosen unit - for dynamic label formatter
  8754. tickPositions.info = extend(normalizedInterval, {
  8755. higherRanks: higherRanks,
  8756. totalRange: interval * count
  8757. });
  8758. return tickPositions;
  8759. }
  8760. }; // end of Time
  8761. }(Highcharts));
  8762. (function (H) {
  8763. /**
  8764. * (c) 2010-2019 Torstein Honsi
  8765. *
  8766. * License: www.highcharts.com/license
  8767. */
  8768. /**
  8769. * Gets fired when a series is added to the chart after load time, using the
  8770. * `addSeries` method. Returning `false` prevents the series from being added.
  8771. *
  8772. * @callback Highcharts.ChartAddSeriesCallbackFunction
  8773. *
  8774. * @param {Highcharts.Chart} this
  8775. * The chart on which the event occured.
  8776. *
  8777. * @param {Highcharts.ChartAddSeriesEventObject} event
  8778. * The event that occured.
  8779. */
  8780. /**
  8781. * Conaints common event information. Through the `options` property you can
  8782. * access the series options that were passed to the `addSeries` method.
  8783. *
  8784. * @interface Highcharts.ChartAddSeriesEventObject
  8785. *//**
  8786. * The series options that were passed to the `addSeries` method.
  8787. * @name Highcharts.ChartAddSeriesEventObject#options
  8788. * @type {Highcharts.SeriesOptionsType}
  8789. *//**
  8790. * Prevents the default behaviour of the event.
  8791. * @name Highcharts.DrilldownEventObject#preventDefault
  8792. * @type {Function}
  8793. *//**
  8794. * The event target.
  8795. * @name Highcharts.DrilldownEventObject#target
  8796. * @type {Highcharts.Chart}
  8797. *//**
  8798. * The event type.
  8799. * @name Highcharts.DrilldownEventObject#type
  8800. * @type {"drilldown"}
  8801. */
  8802. /**
  8803. * Gets fired when clicking on the plot background.
  8804. *
  8805. * @callback Highcharts.ChartClickCallbackFunction
  8806. *
  8807. * @param {Highcharts.Chart} this
  8808. * The chart on which the event occured.
  8809. *
  8810. * @param {Highcharts.PointerEventObject} event
  8811. * The event that occured.
  8812. */
  8813. /**
  8814. * Contains an axes of the clicked spot.
  8815. *
  8816. * @interface Highcharts.ChartClickEventAxisObject
  8817. *//**
  8818. * Axis at the clicked spot.
  8819. * @name Highcharts.ChartClickEventAxisObject#axis
  8820. * @type {Highcharts.Axis}
  8821. *//**
  8822. * Axis value at the clicked spot.
  8823. * @name Highcharts.ChartClickEventAxisObject#value
  8824. * @type {number}
  8825. */
  8826. /**
  8827. * Contains information about the clicked spot on the chart. Remember the unit
  8828. * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
  8829. *
  8830. * @interface Highcharts.ChartClickEventObject
  8831. * @extends Highcharts.PointerEventObject
  8832. *//**
  8833. * Information about the x-axis on the clicked spot.
  8834. * @name Highcharts.ChartClickEventObject#xAxis
  8835. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  8836. *//**
  8837. * Information about the y-axis on the clicked spot.
  8838. * @name Highcharts.ChartClickEventObject#yAxis
  8839. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  8840. *//**
  8841. * Information about the z-axis on the clicked spot.
  8842. * @name Highcharts.ChartClickEventObject#zAxis
  8843. * @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
  8844. */
  8845. /**
  8846. * Gets fired when the chart is finished loading.
  8847. *
  8848. * @callback Highcharts.ChartLoadCallbackFunction
  8849. *
  8850. * @param {Highcharts.Chart} this
  8851. * The chart on which the event occured.
  8852. *
  8853. * @param {global.Event} event
  8854. * The event that occured.
  8855. */
  8856. /**
  8857. * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
  8858. * after an axis, series or point is modified with the `redraw` option set to
  8859. * `true`.
  8860. *
  8861. * @callback Highcharts.ChartRedrawCallbackFunction
  8862. *
  8863. * @param {Highcharts.Chart} this
  8864. * The chart on which the event occured.
  8865. *
  8866. * @param {global.Event} event
  8867. * The event that occured.
  8868. */
  8869. /**
  8870. * Gets fired after initial load of the chart (directly after the `load` event),
  8871. * and after each redraw (directly after the `redraw` event).
  8872. *
  8873. * @callback Highcharts.ChartRenderCallbackFunction
  8874. *
  8875. * @param {Highcharts.Chart} this
  8876. * The chart on which the event occured.
  8877. *
  8878. * @param {global.Event} event
  8879. * The event that occured.
  8880. */
  8881. /**
  8882. * Gets fired when an area of the chart has been selected. The default action
  8883. * for the selection event is to zoom the chart to the selected area. It can be
  8884. * prevented by calling `event.preventDefault()` or return false.
  8885. *
  8886. * @callback Highcharts.ChartSelectionCallbackFunction
  8887. *
  8888. * @param {Highcharts.Chart} this
  8889. * The chart on which the event occured.
  8890. *
  8891. * @param {global.Event} event
  8892. * Event informations
  8893. *
  8894. * @return {boolean|undefined}
  8895. * Return false to prevent the default action, usually zoom.
  8896. */
  8897. /**
  8898. * @interface Highcharts.TooltipFormatterContextObject
  8899. *//**
  8900. * @name Highcharts.TooltipFormatterContextObject#color
  8901. * @type {Highcharts.ColorString}
  8902. *//**
  8903. * @name Highcharts.TooltipFormatterContextObject#colorIndex
  8904. * @type {number|undefined}
  8905. *//**
  8906. * @name Highcharts.TooltipFormatterContextObject#key
  8907. * @type {number}
  8908. *//**
  8909. * @name Highcharts.TooltipFormatterContextObject#percentage
  8910. * @type {number|undefined}
  8911. *//**
  8912. * @name Highcharts.TooltipFormatterContextObject#point
  8913. * @type {Highcharts.Point}
  8914. *//**
  8915. * @name Highcharts.TooltipFormatterContextObject#series
  8916. * @type {Highcharts.Series}
  8917. *//**
  8918. * @name Highcharts.TooltipFormatterContextObject#total
  8919. * @type {number|undefined}
  8920. *//**
  8921. * @name Highcharts.TooltipFormatterContextObject#x
  8922. * @type {number}
  8923. *//**
  8924. * @name Highcharts.TooltipFormatterContextObject#y
  8925. * @type {number}
  8926. */
  8927. var color = H.color,
  8928. isTouchDevice = H.isTouchDevice,
  8929. merge = H.merge,
  8930. svg = H.svg;
  8931. /* ****************************************************************************
  8932. * Handle the options *
  8933. *****************************************************************************/
  8934. /**
  8935. * Global default settings.
  8936. *
  8937. * @name Highcharts.defaultOptions
  8938. * @type {Highcharts.Options}
  8939. *//**
  8940. * @optionparent
  8941. */
  8942. H.defaultOptions = {
  8943. /**
  8944. * An array containing the default colors for the chart's series. When
  8945. * all colors are used, new colors are pulled from the start again.
  8946. *
  8947. * Default colors can also be set on a series or series.type basis,
  8948. * see [column.colors](#plotOptions.column.colors),
  8949. * [pie.colors](#plotOptions.pie.colors).
  8950. *
  8951. * In styled mode, the colors option doesn't exist. Instead, colors
  8952. * are defined in CSS and applied either through series or point class
  8953. * names, or through the [chart.colorCount](#chart.colorCount) option.
  8954. *
  8955. *
  8956. * ### Legacy
  8957. *
  8958. * In Highcharts 3.x, the default colors were:
  8959. *
  8960. * <pre>colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce',
  8961. * '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']</pre>
  8962. *
  8963. * In Highcharts 2.x, the default colors were:
  8964. *
  8965. * <pre>colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE',
  8966. * '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92']</pre>
  8967. *
  8968. * @sample {highcharts} highcharts/chart/colors/
  8969. * Assign a global color theme
  8970. *
  8971. * @type {Array<Highcharts.ColorString>}
  8972. * @default ["#7cb5ec", "#434348", "#90ed7d", "#f7a35c", "#8085e9",
  8973. * "#f15c80", "#e4d354", "#2b908f", "#f45b5b", "#91e8e1"]
  8974. */
  8975. colors: '#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1'.split(' '),
  8976. /**
  8977. * Styled mode only. Configuration object for adding SVG definitions for
  8978. * reusable elements. See [gradients, shadows and
  8979. * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
  8980. * for more information and code examples.
  8981. *
  8982. * @type {*}
  8983. * @since 5.0.0
  8984. * @apioption defs
  8985. */
  8986. /**
  8987. * @ignore-option
  8988. */
  8989. symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
  8990. /**
  8991. * The language object is global and it can't be set on each chart
  8992. * initiation. Instead, use `Highcharts.setOptions` to set it before any
  8993. * chart is initialized.
  8994. *
  8995. * <pre>Highcharts.setOptions({
  8996. * lang: {
  8997. * months: [
  8998. * 'Janvier', 'Février', 'Mars', 'Avril',
  8999. * 'Mai', 'Juin', 'Juillet', 'Août',
  9000. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  9001. * ],
  9002. * weekdays: [
  9003. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  9004. * 'Jeudi', 'Vendredi', 'Samedi'
  9005. * ]
  9006. * }
  9007. * });</pre>
  9008. */
  9009. lang: {
  9010. /**
  9011. * The loading text that appears when the chart is set into the loading
  9012. * state following a call to `chart.showLoading`.
  9013. */
  9014. loading: 'Loading...',
  9015. /**
  9016. * An array containing the months names. Corresponds to the `%B` format
  9017. * in `Highcharts.dateFormat()`.
  9018. *
  9019. * @type {Array<string>}
  9020. * @default ["January", "February", "March", "April", "May", "June",
  9021. * "July", "August", "September", "October", "November",
  9022. * "December"]
  9023. */
  9024. months: [
  9025. 'January', 'February', 'March', 'April', 'May', 'June', 'July',
  9026. 'August', 'September', 'October', 'November', 'December'
  9027. ],
  9028. /**
  9029. * An array containing the months names in abbreviated form. Corresponds
  9030. * to the `%b` format in `Highcharts.dateFormat()`.
  9031. *
  9032. * @type {Array<string>}
  9033. * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  9034. * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  9035. */
  9036. shortMonths: [
  9037. 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
  9038. 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
  9039. ],
  9040. /**
  9041. * An array containing the weekday names.
  9042. *
  9043. * @type {Array<string>}
  9044. * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  9045. * "Friday", "Saturday"]
  9046. */
  9047. weekdays: [
  9048. 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
  9049. 'Thursday', 'Friday', 'Saturday'
  9050. ],
  9051. /**
  9052. * Short week days, starting Sunday. If not specified, Highcharts uses
  9053. * the first three letters of the `lang.weekdays` option.
  9054. *
  9055. * @sample highcharts/lang/shortweekdays/
  9056. * Finnish two-letter abbreviations
  9057. *
  9058. * @type {Array<string>}
  9059. * @since 4.2.4
  9060. * @apioption lang.shortWeekdays
  9061. */
  9062. /**
  9063. * What to show in a date field for invalid dates. Defaults to an empty
  9064. * string.
  9065. *
  9066. * @type {string}
  9067. * @since 4.1.8
  9068. * @product highcharts highstock
  9069. * @apioption lang.invalidDate
  9070. */
  9071. /**
  9072. * The title appearing on hovering the zoom in button. The text itself
  9073. * defaults to "+" and can be changed in the button options.
  9074. *
  9075. * @type {string}
  9076. * @default Zoom in
  9077. * @product highmaps
  9078. * @apioption lang.zoomIn
  9079. */
  9080. /**
  9081. * The title appearing on hovering the zoom out button. The text itself
  9082. * defaults to "-" and can be changed in the button options.
  9083. *
  9084. * @type {string}
  9085. * @default Zoom out
  9086. * @product highmaps
  9087. * @apioption lang.zoomOut
  9088. */
  9089. /**
  9090. * The default decimal point used in the `Highcharts.numberFormat`
  9091. * method unless otherwise specified in the function arguments.
  9092. *
  9093. * @since 1.2.2
  9094. */
  9095. decimalPoint: '.',
  9096. /**
  9097. * [Metric prefixes](http://en.wikipedia.org/wiki/Metric_prefix) used
  9098. * to shorten high numbers in axis labels. Replacing any of the
  9099. * positions with `null` causes the full number to be written. Setting
  9100. * `numericSymbols` to `null` disables shortening altogether.
  9101. *
  9102. * @sample {highcharts} highcharts/lang/numericsymbols/
  9103. * Replacing the symbols with text
  9104. * @sample {highstock} highcharts/lang/numericsymbols/
  9105. * Replacing the symbols with text
  9106. *
  9107. * @type {Array<string>}
  9108. * @default ["k", "M", "G", "T", "P", "E"]
  9109. * @since 2.3.0
  9110. */
  9111. numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
  9112. /**
  9113. * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
  9114. * Use 10000 for Japanese, Korean and various Chinese locales, which
  9115. * use symbols for 10^4, 10^8 and 10^12.
  9116. *
  9117. * @sample highcharts/lang/numericsymbolmagnitude/
  9118. * 10000 magnitude for Japanese
  9119. *
  9120. * @type {number}
  9121. * @default 1000
  9122. * @since 5.0.3
  9123. * @apioption lang.numericSymbolMagnitude
  9124. */
  9125. /**
  9126. * The text for the label appearing when a chart is zoomed.
  9127. *
  9128. * @since 1.2.4
  9129. */
  9130. resetZoom: 'Reset zoom',
  9131. /**
  9132. * The tooltip title for the label appearing when a chart is zoomed.
  9133. *
  9134. * @since 1.2.4
  9135. */
  9136. resetZoomTitle: 'Reset zoom level 1:1',
  9137. /**
  9138. * The default thousands separator used in the `Highcharts.numberFormat`
  9139. * method unless otherwise specified in the function arguments. Defaults
  9140. * to a single space character, which is recommended in
  9141. * [ISO 31-0](https://en.wikipedia.org/wiki/ISO_31-0#Numbers) and works
  9142. * across Anglo-American and continental European languages.
  9143. *
  9144. * @default \u0020
  9145. * @since 1.2.2
  9146. */
  9147. thousandsSep: ' '
  9148. },
  9149. /**
  9150. * Global options that don't apply to each chart. These options, like
  9151. * the `lang` options, must be set using the `Highcharts.setOptions`
  9152. * method.
  9153. *
  9154. * <pre>Highcharts.setOptions({
  9155. * global: {
  9156. * useUTC: false
  9157. * }
  9158. * });</pre>
  9159. *
  9160. */
  9161. /**
  9162. * _Canvg rendering for Android 2.x is removed as of Highcharts 5.0\.
  9163. * Use the [libURL](#exporting.libURL) option to configure exporting._
  9164. *
  9165. * The URL to the additional file to lazy load for Android 2.x devices.
  9166. * These devices don't support SVG, so we download a helper file that
  9167. * contains [canvg](http://code.google.com/p/canvg/), its dependency
  9168. * rbcolor, and our own CanVG Renderer class. To avoid hotlinking to
  9169. * our site, you can install canvas-tools.js on your own server and
  9170. * change this option accordingly.
  9171. *
  9172. * @deprecated
  9173. *
  9174. * @type {string}
  9175. * @default http://code.highcharts.com/{version}/modules/canvas-tools.js
  9176. * @product highcharts highmaps
  9177. * @apioption global.canvasToolsURL
  9178. */
  9179. /**
  9180. * This option is deprecated since v6.0.5. Instead, use
  9181. * [time.useUTC](#time.useUTC) that supports individual time settings
  9182. * per chart.
  9183. *
  9184. * @deprecated
  9185. *
  9186. * @type {boolean}
  9187. * @apioption global.useUTC
  9188. */
  9189. /**
  9190. * This option is deprecated since v6.0.5. Instead, use
  9191. * [time.Date](#time.Date) that supports individual time settings
  9192. * per chart.
  9193. *
  9194. * @deprecated
  9195. *
  9196. * @type {Function}
  9197. * @product highcharts highstock
  9198. * @apioption global.Date
  9199. */
  9200. /**
  9201. * This option is deprecated since v6.0.5. Instead, use
  9202. * [time.getTimezoneOffset](#time.getTimezoneOffset) that supports
  9203. * individual time settings per chart.
  9204. *
  9205. * @deprecated
  9206. *
  9207. * @type {Function}
  9208. * @product highcharts highstock
  9209. * @apioption global.getTimezoneOffset
  9210. */
  9211. /**
  9212. * This option is deprecated since v6.0.5. Instead, use
  9213. * [time.timezone](#time.timezone) that supports individual time
  9214. * settings per chart.
  9215. *
  9216. * @deprecated
  9217. *
  9218. * @type {string}
  9219. * @product highcharts highstock
  9220. * @apioption global.timezone
  9221. */
  9222. /**
  9223. * This option is deprecated since v6.0.5. Instead, use
  9224. * [time.timezoneOffset](#time.timezoneOffset) that supports individual
  9225. * time settings per chart.
  9226. *
  9227. * @deprecated
  9228. *
  9229. * @type {number}
  9230. * @product highcharts highstock
  9231. * @apioption global.timezoneOffset
  9232. */
  9233. global: {},
  9234. time: H.Time.prototype.defaultOptions,
  9235. /**
  9236. * General options for the chart.
  9237. */
  9238. chart: {
  9239. /**
  9240. * Default `mapData` for all series. If set to a string, it functions
  9241. * as an index into the `Highcharts.maps` array. Otherwise it is
  9242. * interpreted s map data.
  9243. *
  9244. * @see [mapData](#series.map.mapData)
  9245. *
  9246. * @type {string|Array<*>}
  9247. * @since 5.0.0
  9248. * @product highmaps
  9249. * @apioption chart.map
  9250. */
  9251. /**
  9252. * Set lat/lon transformation definitions for the chart. If not defined,
  9253. * these are extracted from the map data.
  9254. *
  9255. * @type {*}
  9256. * @since 5.0.0
  9257. * @product highmaps
  9258. * @apioption chart.mapTransforms
  9259. */
  9260. /**
  9261. * When using multiple axis, the ticks of two or more opposite axes
  9262. * will automatically be aligned by adding ticks to the axis or axes
  9263. * with the least ticks, as if `tickAmount` were specified.
  9264. *
  9265. * This can be prevented by setting `alignTicks` to false. If the grid
  9266. * lines look messy, it's a good idea to hide them for the secondary
  9267. * axis by setting `gridLineWidth` to 0.
  9268. *
  9269. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  9270. * then the `alignTicks ` will be disabled for the Axis.
  9271. *
  9272. * Disabled for logarithmic axes.
  9273. *
  9274. * @sample {highcharts} highcharts/chart/alignticks-true/
  9275. * True by default
  9276. * @sample {highcharts} highcharts/chart/alignticks-false/
  9277. * False
  9278. * @sample {highstock} stock/chart/alignticks-true/
  9279. * True by default
  9280. * @sample {highstock} stock/chart/alignticks-false/
  9281. * False
  9282. *
  9283. * @type {boolean}
  9284. * @default true
  9285. * @product highcharts highstock gantt
  9286. * @apioption chart.alignTicks
  9287. */
  9288. /**
  9289. * Set the overall animation for all chart updating. Animation can be
  9290. * disabled throughout the chart by setting it to false here. It can
  9291. * be overridden for each individual API method as a function parameter.
  9292. * The only animation not affected by this option is the initial series
  9293. * animation, see [plotOptions.series.animation](
  9294. * #plotOptions.series.animation).
  9295. *
  9296. * The animation can either be set as a boolean or a configuration
  9297. * object. If `true`, it will use the 'swing' jQuery easing and a
  9298. * duration of 500 ms. If used as a configuration object, the following
  9299. * properties are supported:
  9300. *
  9301. * <dl>
  9302. *
  9303. * <dt>duration</dt>
  9304. *
  9305. * <dd>The duration of the animation in milliseconds.</dd>
  9306. *
  9307. * <dt>easing</dt>
  9308. *
  9309. * <dd>A string reference to an easing function set on the `Math`
  9310. * object. See [the easing
  9311. * demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
  9312. * </dd>
  9313. *
  9314. * </dl>
  9315. *
  9316. * @sample {highcharts} highcharts/chart/animation-none/
  9317. * Updating with no animation
  9318. * @sample {highcharts} highcharts/chart/animation-duration/
  9319. * With a longer duration
  9320. * @sample {highcharts} highcharts/chart/animation-easing/
  9321. * With a jQuery UI easing
  9322. * @sample {highmaps} maps/chart/animation-none/
  9323. * Updating with no animation
  9324. * @sample {highmaps} maps/chart/animation-duration/
  9325. * With a longer duration
  9326. *
  9327. * @type {boolean|Highcharts.AnimationOptionsObject}
  9328. * @default true
  9329. * @apioption chart.animation
  9330. */
  9331. /**
  9332. * A CSS class name to apply to the charts container `div`, allowing
  9333. * unique CSS styling for each chart.
  9334. *
  9335. * @type {string}
  9336. * @apioption chart.className
  9337. */
  9338. /**
  9339. * Event listeners for the chart.
  9340. *
  9341. * @apioption chart.events
  9342. */
  9343. /**
  9344. * Fires when a series is added to the chart after load time, using the
  9345. * `addSeries` method. One parameter, `event`, is passed to the
  9346. * function, containing common event information. Through
  9347. * `event.options` you can access the series options that were passed to
  9348. * the `addSeries` method. Returning false prevents the series from
  9349. * being added.
  9350. *
  9351. * @sample {highcharts} highcharts/chart/events-addseries/
  9352. * Alert on add series
  9353. * @sample {highstock} stock/chart/events-addseries/
  9354. * Alert on add series
  9355. *
  9356. * @type {Highcharts.ChartAddSeriesCallbackFunction}
  9357. * @since 1.2.0
  9358. * @context Highcharts.Chart
  9359. * @apioption chart.events.addSeries
  9360. */
  9361. /**
  9362. * Fires when clicking on the plot background. One parameter, `event`,
  9363. * is passed to the function, containing common event information.
  9364. *
  9365. * Information on the clicked spot can be found through `event.xAxis`
  9366. * and `event.yAxis`, which are arrays containing the axes of each
  9367. * dimension and each axis' value at the clicked spot. The primary axes
  9368. * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  9369. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  9370. *
  9371. * <pre>click: function(e) {
  9372. * console.log(
  9373. * Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
  9374. * e.yAxis[0].value
  9375. * )
  9376. * }</pre>
  9377. *
  9378. * @sample {highcharts} highcharts/chart/events-click/
  9379. * Alert coordinates on click
  9380. * @sample {highcharts} highcharts/chart/events-container/
  9381. * Alternatively, attach event to container
  9382. * @sample {highstock} stock/chart/events-click/
  9383. * Alert coordinates on click
  9384. * @sample {highstock} highcharts/chart/events-container/
  9385. * Alternatively, attach event to container
  9386. * @sample {highmaps} maps/chart/events-click/
  9387. * Record coordinates on click
  9388. * @sample {highmaps} highcharts/chart/events-container/
  9389. * Alternatively, attach event to container
  9390. *
  9391. * @type {Highcharts.ChartClickCallbackFunction}
  9392. * @since 1.2.0
  9393. * @context Highcharts.Chart
  9394. * @apioption chart.events.click
  9395. */
  9396. /**
  9397. * Fires when the chart is finished loading. Since v4.2.2, it also waits
  9398. * for images to be loaded, for example from point markers. One
  9399. * parameter, `event`, is passed to the function, containing common
  9400. * event information.
  9401. *
  9402. * There is also a second parameter to the chart constructor where a
  9403. * callback function can be passed to be executed on chart.load.
  9404. *
  9405. * @sample {highcharts} highcharts/chart/events-load/
  9406. * Alert on chart load
  9407. * @sample {highstock} stock/chart/events-load/
  9408. * Alert on chart load
  9409. * @sample {highmaps} maps/chart/events-load/
  9410. * Add series on chart load
  9411. *
  9412. * @type {Highcharts.ChartLoadCallbackFunction}
  9413. * @context Highcharts.Chart
  9414. * @apioption chart.events.load
  9415. */
  9416. /**
  9417. * Fires when the chart is redrawn, either after a call to
  9418. * `chart.redraw()` or after an axis, series or point is modified with
  9419. * the `redraw` option set to `true`. One parameter, `event`, is passed
  9420. * to the function, containing common event information.
  9421. *
  9422. * @sample {highcharts} highcharts/chart/events-redraw/
  9423. * Alert on chart redraw
  9424. * @sample {highstock} stock/chart/events-redraw/
  9425. * Alert on chart redraw when adding a series or moving the
  9426. * zoomed range
  9427. * @sample {highmaps} maps/chart/events-redraw/
  9428. * Set subtitle on chart redraw
  9429. *
  9430. * @type {Highcharts.ChartRedrawCallbackFunction}
  9431. * @since 1.2.0
  9432. * @context Highcharts.Chart
  9433. * @apioption chart.events.redraw
  9434. */
  9435. /**
  9436. * Fires after initial load of the chart (directly after the `load`
  9437. * event), and after each redraw (directly after the `redraw` event).
  9438. *
  9439. * @type {Highcharts.ChartRenderCallbackFunction}
  9440. * @since 5.0.7
  9441. * @context Highcharts.Chart
  9442. * @apioption chart.events.render
  9443. */
  9444. /**
  9445. * Fires when an area of the chart has been selected. Selection is
  9446. * enabled by setting the chart's zoomType. One parameter, `event`, is
  9447. * passed to the function, containing common event information. The
  9448. * default action for the selection event is to zoom the chart to the
  9449. * selected area. It can be prevented by calling
  9450. * `event.preventDefault()` or return false.
  9451. *
  9452. * Information on the selected area can be found through `event.xAxis`
  9453. * and `event.yAxis`, which are arrays containing the axes of each
  9454. * dimension and each axis' min and max values. The primary axes are
  9455. * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  9456. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  9457. *
  9458. * <pre>selection: function(event) {
  9459. * // log the min and max of the primary, datetime x-axis
  9460. * console.log(
  9461. * Highcharts.dateFormat(
  9462. * '%Y-%m-%d %H:%M:%S',
  9463. * event.xAxis[0].min
  9464. * ),
  9465. * Highcharts.dateFormat(
  9466. * '%Y-%m-%d %H:%M:%S',
  9467. * event.xAxis[0].max
  9468. * )
  9469. * );
  9470. * // log the min and max of the y axis
  9471. * console.log(event.yAxis[0].min, event.yAxis[0].max);
  9472. * }</pre>
  9473. *
  9474. * @sample {highcharts} highcharts/chart/events-selection/
  9475. * Report on selection and reset
  9476. * @sample {highcharts} highcharts/chart/events-selection-points/
  9477. * Select a range of points through a drag selection
  9478. * @sample {highstock} stock/chart/events-selection/
  9479. * Report on selection and reset
  9480. * @sample {highstock} highcharts/chart/events-selection-points/
  9481. * Select a range of points through a drag selection
  9482. * (Highcharts)
  9483. *
  9484. * @type {Highcharts.ChartSelectionCallbackFunction}
  9485. * @apioption chart.events.selection
  9486. */
  9487. /**
  9488. * The margin between the outer edge of the chart and the plot area.
  9489. * The numbers in the array designate top, right, bottom and left
  9490. * respectively. Use the options `marginTop`, `marginRight`,
  9491. * `marginBottom` and `marginLeft` for shorthand setting of one option.
  9492. *
  9493. * By default there is no margin. The actual space is dynamically
  9494. * calculated from the offset of axis labels, axis title, title,
  9495. * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
  9496. * `spacingBottom` and `spacingLeft` options.
  9497. *
  9498. * @sample {highcharts} highcharts/chart/margins-zero/
  9499. * Zero margins
  9500. * @sample {highstock} stock/chart/margin-zero/
  9501. * Zero margins
  9502. *
  9503. * @type {number|Array<number>}
  9504. * @apioption chart.margin
  9505. */
  9506. /**
  9507. * The margin between the bottom outer edge of the chart and the plot
  9508. * area. Use this to set a fixed pixel value for the margin as opposed
  9509. * to the default dynamic margin. See also `spacingBottom`.
  9510. *
  9511. * @sample {highcharts} highcharts/chart/marginbottom/
  9512. * 100px bottom margin
  9513. * @sample {highstock} stock/chart/marginbottom/
  9514. * 100px bottom margin
  9515. * @sample {highmaps} maps/chart/margin/
  9516. * 100px margins
  9517. *
  9518. * @type {number}
  9519. * @since 2.0
  9520. * @apioption chart.marginBottom
  9521. */
  9522. /**
  9523. * The margin between the left outer edge of the chart and the plot
  9524. * area. Use this to set a fixed pixel value for the margin as opposed
  9525. * to the default dynamic margin. See also `spacingLeft`.
  9526. *
  9527. * @sample {highcharts} highcharts/chart/marginleft/
  9528. * 150px left margin
  9529. * @sample {highstock} stock/chart/marginleft/
  9530. * 150px left margin
  9531. * @sample {highmaps} maps/chart/margin/
  9532. * 100px margins
  9533. *
  9534. * @type {number}
  9535. * @since 2.0
  9536. * @apioption chart.marginLeft
  9537. */
  9538. /**
  9539. * The margin between the right outer edge of the chart and the plot
  9540. * area. Use this to set a fixed pixel value for the margin as opposed
  9541. * to the default dynamic margin. See also `spacingRight`.
  9542. *
  9543. * @sample {highcharts} highcharts/chart/marginright/
  9544. * 100px right margin
  9545. * @sample {highstock} stock/chart/marginright/
  9546. * 100px right margin
  9547. * @sample {highmaps} maps/chart/margin/
  9548. * 100px margins
  9549. *
  9550. * @type {number}
  9551. * @since 2.0
  9552. * @apioption chart.marginRight
  9553. */
  9554. /**
  9555. * The margin between the top outer edge of the chart and the plot area.
  9556. * Use this to set a fixed pixel value for the margin as opposed to
  9557. * the default dynamic margin. See also `spacingTop`.
  9558. *
  9559. * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
  9560. * @sample {highstock} stock/chart/margintop/
  9561. * 100px top margin
  9562. * @sample {highmaps} maps/chart/margin/
  9563. * 100px margins
  9564. *
  9565. * @type {number}
  9566. * @since 2.0
  9567. * @apioption chart.marginTop
  9568. */
  9569. /**
  9570. * Allows setting a key to switch between zooming and panning. Can be
  9571. * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
  9572. * key on Windows) or `shift`. The keys are mapped directly to the key
  9573. * properties of the click event argument (`event.altKey`,
  9574. * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
  9575. *
  9576. * @type {string}
  9577. * @since 4.0.3
  9578. * @product highcharts gantt
  9579. * @validvalue ["alt", "ctrl", "meta", "shift"]
  9580. * @apioption chart.panKey
  9581. */
  9582. /**
  9583. * Allow panning in a chart. Best used with [panKey](#chart.panKey)
  9584. * to combine zooming and panning.
  9585. *
  9586. * On touch devices, when the [tooltip.followTouchMove](
  9587. * #tooltip.followTouchMove) option is `true` (default), panning
  9588. * requires two fingers. To allow panning with one finger, set
  9589. * `followTouchMove` to `false`.
  9590. *
  9591. * @sample {highcharts} highcharts/chart/pankey/ Zooming and panning
  9592. *
  9593. * @type {boolean}
  9594. * @default {highcharts} false
  9595. * @default {highstock} true
  9596. * @since 4.0.3
  9597. * @product highcharts highstock gantt
  9598. * @apioption chart.panning
  9599. */
  9600. /**
  9601. * Equivalent to [zoomType](#chart.zoomType), but for multitouch
  9602. * gestures only. By default, the `pinchType` is the same as the
  9603. * `zoomType` setting. However, pinching can be enabled separately in
  9604. * some cases, for example in stock charts where a mouse drag pans the
  9605. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  9606. * #tooltip.followTouchMove) is true, pinchType only applies to
  9607. * two-finger touches.
  9608. *
  9609. * @type {string}
  9610. * @default {highcharts} undefined
  9611. * @default {highstock} x
  9612. * @since 3.0
  9613. * @product highcharts highstock gantt
  9614. * @validvalue ["x", "y", "xy"]
  9615. * @apioption chart.pinchType
  9616. */
  9617. /**
  9618. * Whether to apply styled mode. When in styled mode, no presentational
  9619. * attributes or CSS are applied to the chart SVG. Instead, CSS rules
  9620. * are required to style the chart. The default style sheet is
  9621. * available from `https://code.highcharts.com/css/highcharts.css`.
  9622. *
  9623. * @type {boolean}
  9624. * @default false
  9625. * @since 7.0
  9626. * @apioption chart.styledMode
  9627. */
  9628. styledMode: false,
  9629. /**
  9630. * The corner radius of the outer chart border.
  9631. *
  9632. * @sample {highcharts} highcharts/chart/borderradius/
  9633. * 20px radius
  9634. * @sample {highstock} stock/chart/border/
  9635. * 10px radius
  9636. * @sample {highmaps} maps/chart/border/
  9637. * Border options
  9638. *
  9639. */
  9640. borderRadius: 0,
  9641. /**
  9642. * In styled mode, this sets how many colors the class names
  9643. * should rotate between. With ten colors, series (or points) are
  9644. * given class names like `highcharts-color-0`, `highcharts-color-0`
  9645. * [...] `highcharts-color-9`. The equivalent in non-styled mode
  9646. * is to set colors using the [colors](#colors) setting.
  9647. *
  9648. * @since 5.0.0
  9649. */
  9650. colorCount: 10,
  9651. /**
  9652. * Alias of `type`.
  9653. *
  9654. * @sample {highcharts} highcharts/chart/defaultseriestype/
  9655. * Bar
  9656. *
  9657. * @deprecated
  9658. *
  9659. * @product highcharts
  9660. */
  9661. defaultSeriesType: 'line',
  9662. /**
  9663. * If true, the axes will scale to the remaining visible series once
  9664. * one series is hidden. If false, hiding and showing a series will
  9665. * not affect the axes or the other series. For stacks, once one series
  9666. * within the stack is hidden, the rest of the stack will close in
  9667. * around it even if the axis is not affected.
  9668. *
  9669. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
  9670. * True by default
  9671. * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
  9672. * False
  9673. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
  9674. * True with stack
  9675. * @sample {highstock} stock/chart/ignorehiddenseries-true/
  9676. * True by default
  9677. * @sample {highstock} stock/chart/ignorehiddenseries-false/
  9678. * False
  9679. *
  9680. * @since 1.2.0
  9681. * @product highcharts highstock gantt
  9682. */
  9683. ignoreHiddenSeries: true,
  9684. /**
  9685. * Whether to invert the axes so that the x axis is vertical and y axis
  9686. * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
  9687. * by default.
  9688. *
  9689. * @productdesc {highcharts}
  9690. * If a bar series is present in the chart, it will be inverted
  9691. * automatically. Inverting the chart doesn't have an effect if there
  9692. * are no cartesian series in the chart, or if the chart is
  9693. * [polar](#chart.polar).
  9694. *
  9695. * @sample {highcharts} highcharts/chart/inverted/
  9696. * Inverted line
  9697. * @sample {highstock} stock/navigator/inverted/
  9698. * Inverted stock chart
  9699. *
  9700. * @type {boolean}
  9701. * @default false
  9702. * @product highcharts highstock gantt
  9703. * @apioption chart.inverted
  9704. */
  9705. /**
  9706. * The distance between the outer edge of the chart and the content,
  9707. * like title or legend, or axis title and labels if present. The
  9708. * numbers in the array designate top, right, bottom and left
  9709. * respectively. Use the options spacingTop, spacingRight, spacingBottom
  9710. * and spacingLeft options for shorthand setting of one option.
  9711. *
  9712. * @type {Array<number>}
  9713. * @see [chart.margin](#chart.margin)
  9714. * @default [10, 10, 15, 10]
  9715. * @since 3.0.6
  9716. */
  9717. spacing: [10, 10, 15, 10],
  9718. /**
  9719. * The button that appears after a selection zoom, allowing the user
  9720. * to reset zoom.
  9721. */
  9722. resetZoomButton: {
  9723. /**
  9724. * What frame the button should be placed related to. Can be either
  9725. * `plot` or `chart`
  9726. *
  9727. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  9728. * Relative to the chart
  9729. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  9730. * Relative to the chart
  9731. *
  9732. * @type {string}
  9733. * @default plot
  9734. * @since 2.2
  9735. * @validvalue ["plot", "chart"]
  9736. * @apioption chart.resetZoomButton.relativeTo
  9737. */
  9738. /**
  9739. * A collection of attributes for the button. The object takes SVG
  9740. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  9741. * border radius. The theme also supports `style`, a collection of
  9742. * CSS properties for the text. Equivalent attributes for the hover
  9743. * state are given in `theme.states.hover`.
  9744. *
  9745. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  9746. * Theming the button
  9747. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  9748. * Theming the button
  9749. *
  9750. * @since 2.2
  9751. */
  9752. theme: {
  9753. /**
  9754. * The Z index for the reset zoom button. The default value
  9755. * places it below the tooltip that has Z index 7.
  9756. */
  9757. zIndex: 6
  9758. },
  9759. /**
  9760. * The position of the button.
  9761. *
  9762. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  9763. * Above the plot area
  9764. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  9765. * Above the plot area
  9766. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  9767. * Above the plot area
  9768. *
  9769. * @type {Highcharts.AlignObject}
  9770. * @since 2.2
  9771. */
  9772. position: {
  9773. /**
  9774. * The horizontal alignment of the button.
  9775. */
  9776. align: 'right',
  9777. /**
  9778. * The horizontal offset of the button.
  9779. */
  9780. x: -10,
  9781. /**
  9782. * The vertical alignment of the button.
  9783. *
  9784. * @type {Highcharts.VerticalAlignType}
  9785. * @default top
  9786. * @apioption chart.resetZoomButton.position.verticalAlign
  9787. */
  9788. /**
  9789. * The vertical offset of the button.
  9790. */
  9791. y: 10
  9792. }
  9793. },
  9794. /**
  9795. * The pixel width of the plot area border.
  9796. *
  9797. * @sample {highcharts} highcharts/chart/plotborderwidth/
  9798. * 1px border
  9799. * @sample {highstock} stock/chart/plotborder/
  9800. * 2px border
  9801. * @sample {highmaps} maps/chart/plotborder/
  9802. * Plot border options
  9803. *
  9804. * @type {number}
  9805. * @default 0
  9806. * @apioption chart.plotBorderWidth
  9807. */
  9808. /**
  9809. * Whether to apply a drop shadow to the plot area. Requires that
  9810. * plotBackgroundColor be set. The shadow can be an object configuration
  9811. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  9812. *
  9813. * @sample {highcharts} highcharts/chart/plotshadow/
  9814. * Plot shadow
  9815. * @sample {highstock} stock/chart/plotshadow/
  9816. * Plot shadow
  9817. * @sample {highmaps} maps/chart/plotborder/
  9818. * Plot border options
  9819. *
  9820. * @type {boolean|Highcharts.CSSObject}
  9821. * @default false
  9822. * @apioption chart.plotShadow
  9823. */
  9824. /**
  9825. * When true, cartesian charts like line, spline, area and column are
  9826. * transformed into the polar coordinate system. This produces _polar
  9827. * charts_, also known as _radar charts_. Requires
  9828. * `highcharts-more.js`.
  9829. *
  9830. * @sample {highcharts} highcharts/demo/polar/
  9831. * Polar chart
  9832. * @sample {highcharts} highcharts/demo/polar-wind-rose/
  9833. * Wind rose, stacked polar column chart
  9834. * @sample {highcharts} highcharts/demo/polar-spider/
  9835. * Spider web chart
  9836. * @sample {highcharts} highcharts/parallel-coordinates/polar/
  9837. * Star plot, multivariate data in a polar chart
  9838. *
  9839. * @type {boolean}
  9840. * @default false
  9841. * @since 2.3.0
  9842. * @product highcharts
  9843. * @apioption chart.polar
  9844. */
  9845. /**
  9846. * Whether to reflow the chart to fit the width of the container div
  9847. * on resizing the window.
  9848. *
  9849. * @sample {highcharts} highcharts/chart/reflow-true/
  9850. * True by default
  9851. * @sample {highcharts} highcharts/chart/reflow-false/
  9852. * False
  9853. * @sample {highstock} stock/chart/reflow-true/
  9854. * True by default
  9855. * @sample {highstock} stock/chart/reflow-false/
  9856. * False
  9857. * @sample {highmaps} maps/chart/reflow-true/
  9858. * True by default
  9859. * @sample {highmaps} maps/chart/reflow-false/
  9860. * False
  9861. *
  9862. * @type {boolean}
  9863. * @default true
  9864. * @since 2.1
  9865. * @apioption chart.reflow
  9866. */
  9867. /**
  9868. * The HTML element where the chart will be rendered. If it is a string,
  9869. * the element by that id is used. The HTML element can also be passed
  9870. * by direct reference, or as the first argument of the chart
  9871. * constructor, in which case the option is not needed.
  9872. *
  9873. * @sample {highcharts} highcharts/chart/reflow-true/
  9874. * String
  9875. * @sample {highcharts} highcharts/chart/renderto-object/
  9876. * Object reference
  9877. * @sample {highcharts} highcharts/chart/renderto-jquery/
  9878. * Object reference through jQuery
  9879. * @sample {highstock} stock/chart/renderto-string/
  9880. * String
  9881. * @sample {highstock} stock/chart/renderto-object/
  9882. * Object reference
  9883. * @sample {highstock} stock/chart/renderto-jquery/
  9884. * Object reference through jQuery
  9885. *
  9886. * @type {string|Highcharts.HTMLDOMElement}
  9887. * @apioption chart.renderTo
  9888. */
  9889. /**
  9890. * The background color of the marker square when selecting (zooming
  9891. * in on) an area of the chart.
  9892. *
  9893. * @see In styled mode, the selection marker fill is set with the
  9894. * `.highcharts-selection-marker` class.
  9895. *
  9896. * @type {Highcharts.ColorString}
  9897. * @default rgba(51,92,173,0.25)
  9898. * @since 2.1.7
  9899. * @apioption chart.selectionMarkerFill
  9900. */
  9901. /**
  9902. * Whether to apply a drop shadow to the outer chart area. Requires
  9903. * that backgroundColor be set. The shadow can be an object
  9904. * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
  9905. * `width`.
  9906. *
  9907. * @sample {highcharts} highcharts/chart/shadow/
  9908. * Shadow
  9909. * @sample {highstock} stock/chart/shadow/
  9910. * Shadow
  9911. * @sample {highmaps} maps/chart/border/
  9912. * Chart border and shadow
  9913. *
  9914. * @type {boolean|Highcharts.CSSObject}
  9915. * @default false
  9916. * @apioption chart.shadow
  9917. */
  9918. /**
  9919. * Whether to show the axes initially. This only applies to empty charts
  9920. * where series are added dynamically, as axes are automatically added
  9921. * to cartesian series.
  9922. *
  9923. * @sample {highcharts} highcharts/chart/showaxes-false/
  9924. * False by default
  9925. * @sample {highcharts} highcharts/chart/showaxes-true/
  9926. * True
  9927. *
  9928. * @type {boolean}
  9929. * @since 1.2.5
  9930. * @product highcharts gantt
  9931. * @apioption chart.showAxes
  9932. */
  9933. /**
  9934. * The space between the bottom edge of the chart and the content (plot
  9935. * area, axis title and labels, title, subtitle or legend in top
  9936. * position).
  9937. *
  9938. * @sample {highcharts} highcharts/chart/spacingbottom/
  9939. * Spacing bottom set to 100
  9940. * @sample {highstock} stock/chart/spacingbottom/
  9941. * Spacing bottom set to 100
  9942. * @sample {highmaps} maps/chart/spacing/
  9943. * Spacing 100 all around
  9944. *
  9945. * @type {number}
  9946. * @default 15
  9947. * @since 2.1
  9948. * @apioption chart.spacingBottom
  9949. */
  9950. /**
  9951. * The space between the left edge of the chart and the content (plot
  9952. * area, axis title and labels, title, subtitle or legend in top
  9953. * position).
  9954. *
  9955. * @sample {highcharts} highcharts/chart/spacingleft/
  9956. * Spacing left set to 100
  9957. * @sample {highstock} stock/chart/spacingleft/
  9958. * Spacing left set to 100
  9959. * @sample {highmaps} maps/chart/spacing/
  9960. * Spacing 100 all around
  9961. *
  9962. * @type {number}
  9963. * @default 10
  9964. * @since 2.1
  9965. * @apioption chart.spacingLeft
  9966. */
  9967. /**
  9968. * The space between the right edge of the chart and the content (plot
  9969. * area, axis title and labels, title, subtitle or legend in top
  9970. * position).
  9971. *
  9972. * @sample {highcharts} highcharts/chart/spacingright-100/
  9973. * Spacing set to 100
  9974. * @sample {highcharts} highcharts/chart/spacingright-legend/
  9975. * Legend in right position with default spacing
  9976. * @sample {highstock} stock/chart/spacingright/
  9977. * Spacing set to 100
  9978. * @sample {highmaps} maps/chart/spacing/
  9979. * Spacing 100 all around
  9980. *
  9981. * @type {number}
  9982. * @default 10
  9983. * @since 2.1
  9984. * @apioption chart.spacingRight
  9985. */
  9986. /**
  9987. * The space between the top edge of the chart and the content (plot
  9988. * area, axis title and labels, title, subtitle or legend in top
  9989. * position).
  9990. *
  9991. * @sample {highcharts} highcharts/chart/spacingtop-100/
  9992. * A top spacing of 100
  9993. * @sample {highcharts} highcharts/chart/spacingtop-10/
  9994. * Floating chart title makes the plot area align to the default
  9995. * spacingTop of 10.
  9996. * @sample {highstock} stock/chart/spacingtop/
  9997. * A top spacing of 100
  9998. * @sample {highmaps} maps/chart/spacing/
  9999. * Spacing 100 all around
  10000. *
  10001. * @type {number}
  10002. * @default 10
  10003. * @since 2.1
  10004. * @apioption chart.spacingTop
  10005. */
  10006. /**
  10007. * Additional CSS styles to apply inline to the container `div`. Note
  10008. * that since the default font styles are applied in the renderer, it
  10009. * is ignorant of the individual chart options and must be set globally.
  10010. *
  10011. * @see In styled mode, general chart styles can be set with the
  10012. * `.highcharts-root` class.
  10013. * @sample {highcharts} highcharts/chart/style-serif-font/
  10014. * Using a serif type font
  10015. * @sample {highcharts} highcharts/css/em/
  10016. * Styled mode with relative font sizes
  10017. * @sample {highstock} stock/chart/style/
  10018. * Using a serif type font
  10019. * @sample {highmaps} maps/chart/style-serif-font/
  10020. * Using a serif type font
  10021. *
  10022. * @type {Highcharts.CSSObject}
  10023. * @default {"fontFamily": "\"Lucida Grande\", \"Lucida Sans Unicode\", Verdana, Arial, Helvetica, sans-serif","fontSize":"12px"}
  10024. * @apioption chart.style
  10025. */
  10026. /**
  10027. * The default series type for the chart. Can be any of the chart types
  10028. * listed under [plotOptions](#plotOptions).
  10029. *
  10030. * In TypeScript instead the `type` option must always be set in the
  10031. * series.
  10032. *
  10033. * @sample {highcharts} highcharts/chart/type-bar/
  10034. * Bar
  10035. * @sample {highstock} stock/chart/type/
  10036. * Areaspline
  10037. * @sample {highmaps} maps/chart/type-mapline/
  10038. * Mapline
  10039. *
  10040. * @type {string}
  10041. * @default {highcharts} line
  10042. * @default {highstock} line
  10043. * @default {highmaps} map
  10044. * @since 2.1.0
  10045. * @validvalue ["line", "spline", "column", "bar", "area", "areaspline",
  10046. * "pie", "arearange", "areasplinerange", "boxplot",
  10047. * "bubble", "columnrange", "errorbar", "funnel", "gauge",
  10048. * "heatmap", "polygon", "pyramid", "scatter", "solidgauge",
  10049. * "treemap", "waterfall"]
  10050. * @apioption chart.type
  10051. */
  10052. /**
  10053. * Decides in what dimensions the user can zoom by dragging the mouse.
  10054. * Can be one of `x`, `y` or `xy`.
  10055. *
  10056. * @see [panKey](#chart.panKey)
  10057. *
  10058. * @sample {highcharts} highcharts/chart/zoomtype-none/
  10059. * None by default
  10060. * @sample {highcharts} highcharts/chart/zoomtype-x/
  10061. * X
  10062. * @sample {highcharts} highcharts/chart/zoomtype-y/
  10063. * Y
  10064. * @sample {highcharts} highcharts/chart/zoomtype-xy/
  10065. * Xy
  10066. * @sample {highstock} stock/demo/basic-line/
  10067. * None by default
  10068. * @sample {highstock} stock/chart/zoomtype-x/
  10069. * X
  10070. * @sample {highstock} stock/chart/zoomtype-y/
  10071. * Y
  10072. * @sample {highstock} stock/chart/zoomtype-xy/
  10073. * Xy
  10074. *
  10075. * @type {string}
  10076. * @product highcharts highstock gantt
  10077. * @validvalue ["x", "y", "xy"]
  10078. * @apioption chart.zoomType
  10079. */
  10080. /**
  10081. * An explicit width for the chart. By default (when `null`) the width
  10082. * is calculated from the offset width of the containing element.
  10083. *
  10084. * @sample {highcharts} highcharts/chart/width/
  10085. * 800px wide
  10086. * @sample {highstock} stock/chart/width/
  10087. * 800px wide
  10088. * @sample {highmaps} maps/chart/size/
  10089. * Chart with explicit size
  10090. *
  10091. * @type {number|null}
  10092. */
  10093. width: null,
  10094. /**
  10095. * An explicit height for the chart. If a _number_, the height is
  10096. * given in pixels. If given a _percentage string_ (for example
  10097. * `'56%'`), the height is given as the percentage of the actual chart
  10098. * width. This allows for preserving the aspect ratio across responsive
  10099. * sizes.
  10100. *
  10101. * By default (when `null`) the height is calculated from the offset
  10102. * height of the containing element, or 400 pixels if the containing
  10103. * element's height is 0.
  10104. *
  10105. * @sample {highcharts} highcharts/chart/height/
  10106. * 500px height
  10107. * @sample {highstock} stock/chart/height/
  10108. * 300px height
  10109. * @sample {highmaps} maps/chart/size/
  10110. * Chart with explicit size
  10111. * @sample highcharts/chart/height-percent/
  10112. * Highcharts with percentage height
  10113. *
  10114. * @type {number|string|null}
  10115. */
  10116. height: null,
  10117. /**
  10118. * The color of the outer chart border.
  10119. *
  10120. * @see In styled mode, the stroke is set with the
  10121. * `.highcharts-background` class.
  10122. *
  10123. * @sample {highcharts} highcharts/chart/bordercolor/
  10124. * Brown border
  10125. * @sample {highstock} stock/chart/border/
  10126. * Brown border
  10127. * @sample {highmaps} maps/chart/border/
  10128. * Border options
  10129. *
  10130. * @type {Highcharts.ColorString}
  10131. */
  10132. borderColor: '#335cad',
  10133. /**
  10134. * The pixel width of the outer chart border.
  10135. *
  10136. * @see In styled mode, the stroke is set with the
  10137. * `.highcharts-background` class.
  10138. *
  10139. * @sample {highcharts} highcharts/chart/borderwidth/
  10140. * 5px border
  10141. * @sample {highstock} stock/chart/border/
  10142. * 2px border
  10143. * @sample {highmaps} maps/chart/border/
  10144. * Border options
  10145. *
  10146. * @type {number}
  10147. * @default 0
  10148. * @apioption chart.borderWidth
  10149. */
  10150. /**
  10151. * The background color or gradient for the outer chart area.
  10152. *
  10153. * @see In styled mode, the background is set with the
  10154. * `.highcharts-background` class.
  10155. *
  10156. * @sample {highcharts} highcharts/chart/backgroundcolor-color/
  10157. * Color
  10158. * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
  10159. * Gradient
  10160. * @sample {highstock} stock/chart/backgroundcolor-color/
  10161. * Color
  10162. * @sample {highstock} stock/chart/backgroundcolor-gradient/
  10163. * Gradient
  10164. * @sample {highmaps} maps/chart/backgroundcolor-color/
  10165. * Color
  10166. * @sample {highmaps} maps/chart/backgroundcolor-gradient/
  10167. * Gradient
  10168. *
  10169. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  10170. */
  10171. backgroundColor: '#ffffff',
  10172. /**
  10173. * The background color or gradient for the plot area.
  10174. *
  10175. * @see In styled mode, the plot background is set with the
  10176. * `.highcharts-plot-background` class.
  10177. *
  10178. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
  10179. * Color
  10180. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
  10181. * Gradient
  10182. * @sample {highstock} stock/chart/plotbackgroundcolor-color/
  10183. * Color
  10184. * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
  10185. * Gradient
  10186. * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
  10187. * Color
  10188. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  10189. * Gradient
  10190. *
  10191. * @type {Highcharts.ColorString}
  10192. * @apioption chart.plotBackgroundColor
  10193. */
  10194. /**
  10195. * The URL for an image to use as the plot background. To set an image
  10196. * as the background for the entire chart, set a CSS background image
  10197. * to the container element. Note that for the image to be applied to
  10198. * exported charts, its URL needs to be accessible by the export server.
  10199. *
  10200. * @see In styled mode, a plot background image can be set with the
  10201. * `.highcharts-plot-background` class and a [custom pattern](
  10202. * https://www.highcharts.com/docs/chart-design-and-style/
  10203. * gradients-shadows-and-patterns).
  10204. *
  10205. * @sample {highcharts} highcharts/chart/plotbackgroundimage/
  10206. * Skies
  10207. * @sample {highstock} stock/chart/plotbackgroundimage/
  10208. * Skies
  10209. *
  10210. * @type {string}
  10211. * @apioption chart.plotBackgroundImage
  10212. */
  10213. /**
  10214. * The color of the inner chart or plot area border.
  10215. *
  10216. * @see In styled mode, a plot border stroke can be set with the
  10217. * `.highcharts-plot-border` class.
  10218. *
  10219. * @sample {highcharts} highcharts/chart/plotbordercolor/
  10220. * Blue border
  10221. * @sample {highstock} stock/chart/plotborder/
  10222. * Blue border
  10223. * @sample {highmaps} maps/chart/plotborder/
  10224. * Plot border options
  10225. *
  10226. * @type {Highcharts.ColorString}
  10227. */
  10228. plotBorderColor: '#cccccc'
  10229. },
  10230. /**
  10231. * The chart's main title.
  10232. *
  10233. * @sample {highmaps} maps/title/title/
  10234. * Title options demonstrated
  10235. */
  10236. title: {
  10237. /**
  10238. * When the title is floating, the plot area will not move to make space
  10239. * for it.
  10240. *
  10241. * @sample {highcharts} highcharts/chart/zoomtype-none/
  10242. * False by default
  10243. * @sample {highcharts} highcharts/title/floating/
  10244. * True - title on top of the plot area
  10245. * @sample {highstock} stock/chart/title-floating/
  10246. * True - title on top of the plot area
  10247. *
  10248. * @type {boolean}
  10249. * @default false
  10250. * @since 2.1
  10251. * @apioption title.floating
  10252. */
  10253. /**
  10254. * CSS styles for the title. Use this for font styling, but use `align`,
  10255. * `x` and `y` for text alignment.
  10256. *
  10257. * In styled mode, the title style is given in the `.highcharts-title`
  10258. * class.
  10259. *
  10260. * @sample {highcharts} highcharts/title/style/
  10261. * Custom color and weight
  10262. * @sample {highstock} stock/chart/title-style/
  10263. * Custom color and weight
  10264. * @sample highcharts/css/titles/
  10265. * Styled mode
  10266. *
  10267. * @type {Highcharts.CSSObject}
  10268. * @default {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
  10269. * @default {highstock} { "color": "#333333", "fontSize": "16px" }
  10270. * @apioption title.style
  10271. */
  10272. /**
  10273. * Whether to
  10274. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  10275. * to render the text.
  10276. *
  10277. * @type {boolean}
  10278. * @default false
  10279. * @apioption title.useHTML
  10280. */
  10281. /**
  10282. * The vertical alignment of the title. Can be one of `"top"`,
  10283. * `"middle"` and `"bottom"`. When a value is given, the title behaves
  10284. * as if [floating](#title.floating) were `true`.
  10285. *
  10286. * @sample {highcharts} highcharts/title/verticalalign/
  10287. * Chart title in bottom right corner
  10288. * @sample {highstock} stock/chart/title-verticalalign/
  10289. * Chart title in bottom right corner
  10290. *
  10291. * @type {Highcharts.VerticalAlignType}
  10292. * @since 2.1
  10293. * @apioption title.verticalAlign
  10294. */
  10295. /**
  10296. * The x position of the title relative to the alignment within
  10297. * `chart.spacingLeft` and `chart.spacingRight`.
  10298. *
  10299. * @sample {highcharts} highcharts/title/align/
  10300. * Aligned to the plot area (x = 70px = margin left - spacing
  10301. * left)
  10302. * @sample {highstock} stock/chart/title-align/
  10303. * Aligned to the plot area (x = 50px = margin left - spacing
  10304. * left)
  10305. *
  10306. * @type {number}
  10307. * @default 0
  10308. * @since 2.0
  10309. * @apioption title.x
  10310. */
  10311. /**
  10312. * The y position of the title relative to the alignment within
  10313. * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
  10314. * #chart.spacingBottom). By default it depends on the font size.
  10315. *
  10316. * @sample {highcharts} highcharts/title/y/
  10317. * Title inside the plot area
  10318. * @sample {highstock} stock/chart/title-verticalalign/
  10319. * Chart title in bottom right corner
  10320. *
  10321. * @type {number}
  10322. * @since 2.0
  10323. * @apioption title.y
  10324. */
  10325. /**
  10326. * The title of the chart. To disable the title, set the `text` to
  10327. * `undefined`.
  10328. *
  10329. * @sample {highcharts} highcharts/title/text/
  10330. * Custom title
  10331. * @sample {highstock} stock/chart/title-text/
  10332. * Custom title
  10333. *
  10334. * @default {highcharts|highmaps} Chart title
  10335. * @default {highstock} undefined
  10336. */
  10337. text: 'Chart title',
  10338. /**
  10339. * The horizontal alignment of the title. Can be one of "left", "center"
  10340. * and "right".
  10341. *
  10342. * @sample {highcharts} highcharts/title/align/
  10343. * Aligned to the plot area (x = 70px = margin left - spacing
  10344. * left)
  10345. * @sample {highstock} stock/chart/title-align/
  10346. * Aligned to the plot area (x = 50px = margin left - spacing
  10347. * left)
  10348. *
  10349. * @type {Highcharts.AlignType}
  10350. * @since 2.0
  10351. */
  10352. align: 'center',
  10353. /**
  10354. * The margin between the title and the plot area, or if a subtitle
  10355. * is present, the margin between the subtitle and the plot area.
  10356. *
  10357. * @sample {highcharts} highcharts/title/margin-50/
  10358. * A chart title margin of 50
  10359. * @sample {highcharts} highcharts/title/margin-subtitle/
  10360. * The same margin applied with a subtitle
  10361. * @sample {highstock} stock/chart/title-margin/
  10362. * A chart title margin of 50
  10363. *
  10364. * @since 2.1
  10365. */
  10366. margin: 15,
  10367. /**
  10368. * Adjustment made to the title width, normally to reserve space for
  10369. * the exporting burger menu.
  10370. *
  10371. * @sample highcharts/title/widthadjust/
  10372. * Wider menu, greater padding
  10373. *
  10374. * @since 4.2.5
  10375. */
  10376. widthAdjust: -44
  10377. },
  10378. /**
  10379. * The chart's subtitle. This can be used both to display a subtitle below
  10380. * the main title, and to display random text anywhere in the chart. The
  10381. * subtitle can be updated after chart initialization through the
  10382. * `Chart.setTitle` method.
  10383. *
  10384. * @sample {highmaps} maps/title/subtitle/
  10385. * Subtitle options demonstrated
  10386. */
  10387. subtitle: {
  10388. /**
  10389. * When the subtitle is floating, the plot area will not move to make
  10390. * space for it.
  10391. *
  10392. * @sample {highcharts} highcharts/subtitle/floating/
  10393. * Floating title and subtitle
  10394. * @sample {highstock} stock/chart/subtitle-footnote
  10395. * Footnote floating at bottom right of plot area
  10396. *
  10397. * @type {boolean}
  10398. * @default false
  10399. * @since 2.1
  10400. * @apioption subtitle.floating
  10401. */
  10402. /**
  10403. * CSS styles for the title.
  10404. *
  10405. * In styled mode, the subtitle style is given in the
  10406. * `.highcharts-subtitle` class.
  10407. *
  10408. * @sample {highcharts} highcharts/subtitle/style/
  10409. * Custom color and weight
  10410. * @sample {highcharts} highcharts/css/titles/
  10411. * Styled mode
  10412. * @sample {highstock} stock/chart/subtitle-style
  10413. * Custom color and weight
  10414. * @sample {highstock} highcharts/css/titles/
  10415. * Styled mode
  10416. * @sample {highmaps} highcharts/css/titles/
  10417. * Styled mode
  10418. *
  10419. * @type {Highcharts.CSSObject}
  10420. * @default {"color": "#666666"}
  10421. * @apioption subtitle.style
  10422. */
  10423. /**
  10424. * Whether to
  10425. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  10426. * to render the text.
  10427. *
  10428. * @type {boolean}
  10429. * @default false
  10430. * @apioption subtitle.useHTML
  10431. */
  10432. /**
  10433. * The vertical alignment of the title. Can be one of "top", "middle"
  10434. * and "bottom". When a value is given, the title behaves as floating.
  10435. *
  10436. * @sample {highcharts} highcharts/subtitle/verticalalign/
  10437. * Footnote at the bottom right of plot area
  10438. * @sample {highstock} stock/chart/subtitle-footnote
  10439. * Footnote at the bottom right of plot area
  10440. *
  10441. * @type {Highcharts.VerticalAlignType}
  10442. * @since 2.1
  10443. * @apioption subtitle.verticalAlign
  10444. */
  10445. /**
  10446. * The x position of the subtitle relative to the alignment within
  10447. * `chart.spacingLeft` and `chart.spacingRight`.
  10448. *
  10449. * @sample {highcharts} highcharts/subtitle/align/
  10450. * Footnote at right of plot area
  10451. * @sample {highstock} stock/chart/subtitle-footnote
  10452. * Footnote at the bottom right of plot area
  10453. *
  10454. * @type {number}
  10455. * @default 0
  10456. * @since 2.0
  10457. * @apioption subtitle.x
  10458. */
  10459. /**
  10460. * The y position of the subtitle relative to the alignment within
  10461. * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
  10462. * is laid out below the title unless the title is floating.
  10463. *
  10464. * @sample {highcharts} highcharts/subtitle/verticalalign/
  10465. * Footnote at the bottom right of plot area
  10466. * @sample {highstock} stock/chart/subtitle-footnote
  10467. * Footnote at the bottom right of plot area
  10468. *
  10469. * @type {number}
  10470. * @since 2.0
  10471. * @apioption subtitle.y
  10472. */
  10473. /**
  10474. * The subtitle of the chart.
  10475. *
  10476. * @sample {highcharts|highstock} highcharts/subtitle/text/
  10477. * Custom subtitle
  10478. * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
  10479. * Formatted and linked text.
  10480. */
  10481. text: '',
  10482. /**
  10483. * The horizontal alignment of the subtitle. Can be one of "left",
  10484. * "center" and "right".
  10485. *
  10486. * @sample {highcharts} highcharts/subtitle/align/
  10487. * Footnote at right of plot area
  10488. * @sample {highstock} stock/chart/subtitle-footnote
  10489. * Footnote at bottom right of plot area
  10490. *
  10491. * @type {Highcharts.AlignType}
  10492. * @since 2.0
  10493. */
  10494. align: 'center',
  10495. /**
  10496. * Adjustment made to the subtitle width, normally to reserve space
  10497. * for the exporting burger menu.
  10498. *
  10499. * @see [title.widthAdjust](#title.widthAdjust)
  10500. *
  10501. * @sample highcharts/title/widthadjust/
  10502. * Wider menu, greater padding
  10503. *
  10504. * @since 4.2.5
  10505. */
  10506. widthAdjust: -44
  10507. },
  10508. /**
  10509. * The plotOptions is a wrapper object for config objects for each series
  10510. * type. The config objects for each series can also be overridden for
  10511. * each series item as given in the series array.
  10512. *
  10513. * Configuration options for the series are given in three levels. Options
  10514. * for all series in a chart are given in the [plotOptions.series](
  10515. * #plotOptions.series) object. Then options for all series of a specific
  10516. * type are given in the plotOptions of that type, for example
  10517. * `plotOptions.line`. Next, options for one single series are given in
  10518. * [the series array](#series).
  10519. */
  10520. plotOptions: {},
  10521. /**
  10522. * HTML labels that can be positioned anywhere in the chart area.
  10523. */
  10524. labels: {
  10525. /**
  10526. * An HTML label that can be positioned anywhere in the chart area.
  10527. *
  10528. * @type {Array<*>}
  10529. * @apioption labels.items
  10530. */
  10531. /**
  10532. * Inner HTML or text for the label.
  10533. *
  10534. * @type {string}
  10535. * @apioption labels.items.html
  10536. */
  10537. /**
  10538. * CSS styles for each label. To position the label, use left and top
  10539. * like this:
  10540. *
  10541. * <pre>style: {
  10542. * left: '100px',
  10543. * top: '100px'
  10544. * }</pre>
  10545. *
  10546. * @type {Highcharts.CSSObject}
  10547. * @apioption labels.items.style
  10548. */
  10549. /**
  10550. * Shared CSS styles for all labels.
  10551. *
  10552. * @type {Highcharts.CSSObject}
  10553. * @default {"color": "#333333", "position": "absolute"}
  10554. */
  10555. style: {
  10556. /**
  10557. * @ignore
  10558. */
  10559. position: 'absolute',
  10560. /**
  10561. * @ignore
  10562. */
  10563. color: '#333333'
  10564. }
  10565. },
  10566. /**
  10567. * The legend is a box containing a symbol and name for each series
  10568. * item or point item in the chart. Each series (or points in case
  10569. * of pie charts) is represented by a symbol and its name in the legend.
  10570. *
  10571. * It is possible to override the symbol creator function and create
  10572. * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
  10573. *
  10574. * @productdesc {highmaps}
  10575. * A Highmaps legend by default contains one legend item per series, but if
  10576. * a `colorAxis` is defined, the axis will be displayed in the legend.
  10577. * Either as a gradient, or as multiple legend items for `dataClasses`.
  10578. */
  10579. legend: {
  10580. /**
  10581. * The background color of the legend.
  10582. *
  10583. * @see In styled mode, the legend background fill can be applied with
  10584. * the `.highcharts-legend-box` class.
  10585. *
  10586. * @sample {highcharts} highcharts/legend/backgroundcolor/
  10587. * Yellowish background
  10588. * @sample {highstock} stock/legend/align/
  10589. * Various legend options
  10590. * @sample {highmaps} maps/legend/border-background/
  10591. * Border and background options
  10592. *
  10593. * @type {Highcharts.ColorString}
  10594. * @apioption legend.backgroundColor
  10595. */
  10596. /**
  10597. * The width of the drawn border around the legend.
  10598. *
  10599. * @see In styled mode, the legend border stroke width can be applied
  10600. * with the `.highcharts-legend-box` class.
  10601. *
  10602. * @sample {highcharts} highcharts/legend/borderwidth/
  10603. * 2px border width
  10604. * @sample {highstock} stock/legend/align/
  10605. * Various legend options
  10606. * @sample {highmaps} maps/legend/border-background/
  10607. * Border and background options
  10608. *
  10609. * @type {number}
  10610. * @default 0
  10611. * @apioption legend.borderWidth
  10612. */
  10613. /**
  10614. * Enable or disable the legend. There is also a series-specific option,
  10615. * [showInLegend](#plotOptions.series.showInLegend), that can hide the
  10616. * series from the legend. In some series types this is `false` by
  10617. * default, so it must set to `true` in order to show the legend for the
  10618. * series.
  10619. *
  10620. * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
  10621. * @sample {highstock} stock/legend/align/ Various legend options
  10622. * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
  10623. *
  10624. * @default {highstock} false
  10625. * @default {highmaps} true
  10626. * @default {gantt} false
  10627. */
  10628. enabled: true,
  10629. /**
  10630. * The horizontal alignment of the legend box within the chart area.
  10631. * Valid values are `left`, `center` and `right`.
  10632. *
  10633. * In the case that the legend is aligned in a corner position, the
  10634. * `layout` option will determine whether to place it above/below
  10635. * or on the side of the plot area.
  10636. *
  10637. * @sample {highcharts} highcharts/legend/align/
  10638. * Legend at the right of the chart
  10639. * @sample {highstock} stock/legend/align/
  10640. * Various legend options
  10641. * @sample {highmaps} maps/legend/alignment/
  10642. * Legend alignment
  10643. *
  10644. * @type {Highcharts.AlignType}
  10645. * @since 2.0
  10646. */
  10647. align: 'center',
  10648. /**
  10649. * If the [layout](legend.layout) is `horizontal` and the legend items
  10650. * span over two lines or more, whether to align the items into vertical
  10651. * columns. Setting this to `false` makes room for more items, but will
  10652. * look more messy.
  10653. *
  10654. * @since 6.1.0
  10655. */
  10656. alignColumns: true,
  10657. /**
  10658. * When the legend is floating, the plot area ignores it and is allowed
  10659. * to be placed below it.
  10660. *
  10661. * @sample {highcharts} highcharts/legend/floating-false/
  10662. * False by default
  10663. * @sample {highcharts} highcharts/legend/floating-true/
  10664. * True
  10665. * @sample {highmaps} maps/legend/alignment/
  10666. * Floating legend
  10667. *
  10668. * @type {boolean}
  10669. * @default false
  10670. * @since 2.1
  10671. * @apioption legend.floating
  10672. */
  10673. /**
  10674. * The layout of the legend items. Can be one of `horizontal` or
  10675. * `vertical` or `proximate`. When `proximate`, the legend items will be
  10676. * placed as close as possible to the graphs they're representing,
  10677. * except in inverted charts or when the legend position doesn't allow
  10678. * it.
  10679. *
  10680. * @sample {highcharts} highcharts/legend/layout-horizontal/
  10681. * Horizontal by default
  10682. * @sample {highcharts} highcharts/legend/layout-vertical/
  10683. * Vertical
  10684. * @sample highcharts/legend/layout-proximate
  10685. * Labels proximate to the data
  10686. * @sample {highstock} stock/legend/layout-horizontal/
  10687. * Horizontal by default
  10688. * @sample {highmaps} maps/legend/padding-itemmargin/
  10689. * Vertical with data classes
  10690. * @sample {highmaps} maps/legend/layout-vertical/
  10691. * Vertical with color axis gradient
  10692. *
  10693. * @validvalue ["horizontal", "vertical", "proximate"]
  10694. */
  10695. layout: 'horizontal',
  10696. /**
  10697. * In a legend with horizontal layout, the itemDistance defines the
  10698. * pixel distance between each item.
  10699. *
  10700. * @sample {highcharts} highcharts/legend/layout-horizontal/
  10701. * 50px item distance
  10702. * @sample {highstock} highcharts/legend/layout-horizontal/
  10703. * 50px item distance
  10704. *
  10705. * @type {number}
  10706. * @default {highcharts} 20
  10707. * @default {highstock} 20
  10708. * @default {highmaps} 8
  10709. * @since 3.0.3
  10710. * @apioption legend.itemDistance
  10711. */
  10712. /**
  10713. * The pixel bottom margin for each legend item.
  10714. *
  10715. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  10716. * Padding and item margins demonstrated
  10717. * @sample {highmaps} maps/legend/padding-itemmargin/
  10718. * Padding and item margins demonstrated
  10719. *
  10720. * @type {number}
  10721. * @default 0
  10722. * @since 2.2.0
  10723. * @apioption legend.itemMarginBottom
  10724. */
  10725. /**
  10726. * The pixel top margin for each legend item.
  10727. *
  10728. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  10729. * Padding and item margins demonstrated
  10730. * @sample {highmaps} maps/legend/padding-itemmargin/
  10731. * Padding and item margins demonstrated
  10732. *
  10733. * @type {number}
  10734. * @default 0
  10735. * @since 2.2.0
  10736. * @apioption legend.itemMarginTop
  10737. */
  10738. /**
  10739. * The width for each legend item. By default the items are laid out
  10740. * successively. In a [horizontal layout](legend.layout), if the items
  10741. * are laid out across two rows or more, they will be vertically aligned
  10742. * depending on the [legend.alignColumns](legend.alignColumns) option.
  10743. *
  10744. * @sample {highcharts} highcharts/legend/itemwidth-default/
  10745. * Undefined by default
  10746. * @sample {highcharts} highcharts/legend/itemwidth-80/
  10747. * 80 for aligned legend items
  10748. *
  10749. * @type {number}
  10750. * @since 2.0
  10751. * @apioption legend.itemWidth
  10752. */
  10753. /**
  10754. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  10755. * for each legend label. Available variables relates to properties on
  10756. * the series, or the point in case of pies.
  10757. *
  10758. * @type {string}
  10759. * @default {name}
  10760. * @since 1.3
  10761. * @apioption legend.labelFormat
  10762. */
  10763. /**
  10764. * Callback function to format each of the series' labels. The `this`
  10765. * keyword refers to the series object, or the point object in case
  10766. * of pie charts. By default the series or point name is printed.
  10767. *
  10768. * @productdesc {highmaps}
  10769. * In Highmaps the context can also be a data class in case of a
  10770. * `colorAxis`.
  10771. *
  10772. * @sample {highcharts} highcharts/legend/labelformatter/
  10773. * Add text
  10774. * @sample {highmaps} maps/legend/labelformatter/
  10775. * Data classes with label formatter
  10776. *
  10777. * @context {Highcharts.Series|Highcharts.Point}
  10778. */
  10779. labelFormatter: function () {
  10780. return this.name;
  10781. },
  10782. /**
  10783. * Line height for the legend items. Deprecated as of 2.1\. Instead,
  10784. * the line height for each item can be set using itemStyle.lineHeight,
  10785. * and the padding between items using `itemMarginTop` and
  10786. * `itemMarginBottom`.
  10787. *
  10788. * @sample {highcharts} highcharts/legend/lineheight/
  10789. * Setting padding
  10790. *
  10791. * @deprecated
  10792. *
  10793. * @type {number}
  10794. * @default 16
  10795. * @since 2.0
  10796. * @product highcharts gantt
  10797. * @apioption legend.lineHeight
  10798. */
  10799. /**
  10800. * If the plot area sized is calculated automatically and the legend
  10801. * is not floating, the legend margin is the space between the legend
  10802. * and the axis labels or plot area.
  10803. *
  10804. * @sample {highcharts} highcharts/legend/margin-default/
  10805. * 12 pixels by default
  10806. * @sample {highcharts} highcharts/legend/margin-30/
  10807. * 30 pixels
  10808. *
  10809. * @type {number}
  10810. * @default 12
  10811. * @since 2.1
  10812. * @apioption legend.margin
  10813. */
  10814. /**
  10815. * Maximum pixel height for the legend. When the maximum height is
  10816. * extended, navigation will show.
  10817. *
  10818. * @type {number}
  10819. * @since 2.3.0
  10820. * @apioption legend.maxHeight
  10821. */
  10822. /**
  10823. * The color of the drawn border around the legend.
  10824. *
  10825. * @see In styled mode, the legend border stroke can be applied with the
  10826. * `.highcharts-legend-box` class.
  10827. *
  10828. * @sample {highcharts} highcharts/legend/bordercolor/
  10829. * Brown border
  10830. * @sample {highstock} stock/legend/align/
  10831. * Various legend options
  10832. * @sample {highmaps} maps/legend/border-background/
  10833. * Border and background options
  10834. *
  10835. * @type {Highcharts.ColorString}
  10836. */
  10837. borderColor: '#999999',
  10838. /**
  10839. * The border corner radius of the legend.
  10840. *
  10841. * @sample {highcharts} highcharts/legend/borderradius-default/
  10842. * Square by default
  10843. * @sample {highcharts} highcharts/legend/borderradius-round/
  10844. * 5px rounded
  10845. * @sample {highmaps} maps/legend/border-background/
  10846. * Border and background options
  10847. */
  10848. borderRadius: 0,
  10849. /**
  10850. * Options for the paging or navigation appearing when the legend
  10851. * is overflown. Navigation works well on screen, but not in static
  10852. * exported images. One way of working around that is to
  10853. * [increase the chart height in
  10854. * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
  10855. */
  10856. navigation: {
  10857. /**
  10858. * How to animate the pages when navigating up or down. A value of
  10859. * `true` applies the default navigation given in the
  10860. * `chart.animation` option. Additional options can be given as an
  10861. * object containing values for easing and duration.
  10862. *
  10863. * @sample {highcharts} highcharts/legend/navigation/
  10864. * Legend page navigation demonstrated
  10865. * @sample {highstock} highcharts/legend/navigation/
  10866. * Legend page navigation demonstrated
  10867. *
  10868. * @type {boolean|Highcharts.AnimationOptionsObject}
  10869. * @default true
  10870. * @since 2.2.4
  10871. * @apioption legend.navigation.animation
  10872. */
  10873. /**
  10874. * The pixel size of the up and down arrows in the legend paging
  10875. * navigation.
  10876. *
  10877. * @sample {highcharts} highcharts/legend/navigation/
  10878. * Legend page navigation demonstrated
  10879. * @sample {highstock} highcharts/legend/navigation/
  10880. * Legend page navigation demonstrated
  10881. *
  10882. * @type {number}
  10883. * @default 12
  10884. * @since 2.2.4
  10885. * @apioption legend.navigation.arrowSize
  10886. */
  10887. /**
  10888. * Whether to enable the legend navigation. In most cases, disabling
  10889. * the navigation results in an unwanted overflow.
  10890. *
  10891. * See also the [adapt chart to legend](
  10892. * https://www.highcharts.com/products/plugin-registry/single/8/Adapt-Chart-To-Legend)
  10893. * plugin for a solution to extend the chart height to make room for
  10894. * the legend, optionally in exported charts only.
  10895. *
  10896. * @type {boolean}
  10897. * @default true
  10898. * @since 4.2.4
  10899. * @apioption legend.navigation.enabled
  10900. */
  10901. /**
  10902. * Text styles for the legend page navigation.
  10903. *
  10904. * @see In styled mode, the navigation items are styled with the
  10905. * `.highcharts-legend-navigation` class.
  10906. *
  10907. * @sample {highcharts} highcharts/legend/navigation/
  10908. * Legend page navigation demonstrated
  10909. * @sample {highstock} highcharts/legend/navigation/
  10910. * Legend page navigation demonstrated
  10911. *
  10912. * @type {Highcharts.CSSObject}
  10913. * @since 2.2.4
  10914. * @apioption legend.navigation.style
  10915. */
  10916. /**
  10917. * The color for the active up or down arrow in the legend page
  10918. * navigation.
  10919. *
  10920. * @see In styled mode, the active arrow be styled with the
  10921. * `.highcharts-legend-nav-active` class.
  10922. *
  10923. * @sample {highcharts} highcharts/legend/navigation/
  10924. * Legend page navigation demonstrated
  10925. * @sample {highstock} highcharts/legend/navigation/
  10926. * Legend page navigation demonstrated
  10927. *
  10928. * @type {Highcharts.ColorString}
  10929. * @since 2.2.4
  10930. */
  10931. activeColor: '#003399',
  10932. /**
  10933. * The color of the inactive up or down arrow in the legend page
  10934. * navigation. .
  10935. *
  10936. * @see In styled mode, the inactive arrow be styled with the
  10937. * `.highcharts-legend-nav-inactive` class.
  10938. *
  10939. * @sample {highcharts} highcharts/legend/navigation/
  10940. * Legend page navigation demonstrated
  10941. * @sample {highstock} highcharts/legend/navigation/
  10942. * Legend page navigation demonstrated
  10943. *
  10944. * @type {Highcharts.ColorString}
  10945. * @since 2.2.4
  10946. */
  10947. inactiveColor: '#cccccc'
  10948. },
  10949. /**
  10950. * The inner padding of the legend box.
  10951. *
  10952. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  10953. * Padding and item margins demonstrated
  10954. * @sample {highmaps} maps/legend/padding-itemmargin/
  10955. * Padding and item margins demonstrated
  10956. *
  10957. * @type {number}
  10958. * @default 8
  10959. * @since 2.2.0
  10960. * @apioption legend.padding
  10961. */
  10962. /**
  10963. * Whether to reverse the order of the legend items compared to the
  10964. * order of the series or points as defined in the configuration object.
  10965. *
  10966. * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
  10967. * [series.legendIndex](#series.legendIndex)
  10968. *
  10969. * @sample {highcharts} highcharts/legend/reversed/
  10970. * Stacked bar with reversed legend
  10971. *
  10972. * @type {boolean}
  10973. * @default false
  10974. * @since 1.2.5
  10975. * @apioption legend.reversed
  10976. */
  10977. /**
  10978. * Whether to show the symbol on the right side of the text rather than
  10979. * the left side. This is common in Arabic and Hebraic.
  10980. *
  10981. * @sample {highcharts} highcharts/legend/rtl/
  10982. * Symbol to the right
  10983. *
  10984. * @type {boolean}
  10985. * @default false
  10986. * @since 2.2
  10987. * @apioption legend.rtl
  10988. */
  10989. /**
  10990. * CSS styles for the legend area. In the 1.x versions the position
  10991. * of the legend area was determined by CSS. In 2.x, the position is
  10992. * determined by properties like `align`, `verticalAlign`, `x` and `y`,
  10993. * but the styles are still parsed for backwards compatibility.
  10994. *
  10995. * @deprecated
  10996. *
  10997. * @type {Highcharts.CSSObject}
  10998. * @product highcharts highstock
  10999. * @apioption legend.style
  11000. */
  11001. /**
  11002. * CSS styles for each legend item. Only a subset of CSS is supported,
  11003. * notably those options related to text. The default `textOverflow`
  11004. * property makes long texts truncate. Set it to `undefined` to wrap
  11005. * text instead. A `width` property can be added to control the text
  11006. * width.
  11007. *
  11008. * @see In styled mode, the legend items can be styled with the
  11009. * `.highcharts-legend-item` class.
  11010. *
  11011. * @sample {highcharts} highcharts/legend/itemstyle/
  11012. * Bold black text
  11013. * @sample {highmaps} maps/legend/itemstyle/
  11014. * Item text styles
  11015. *
  11016. * @type {Highcharts.CSSObject}
  11017. * @default {"color": "#333333", "cursor": "pointer", "fontSize": "12px", "fontWeight": "bold", "textOverflow": "ellipsis"}
  11018. */
  11019. itemStyle: {
  11020. /**
  11021. * @ignore
  11022. */
  11023. color: '#333333',
  11024. /**
  11025. * @ignore
  11026. */
  11027. cursor: 'pointer',
  11028. /**
  11029. * @ignore
  11030. */
  11031. fontSize: '12px',
  11032. /**
  11033. * @ignore
  11034. */
  11035. fontWeight: 'bold',
  11036. /**
  11037. * @ignore
  11038. */
  11039. textOverflow: 'ellipsis'
  11040. },
  11041. /**
  11042. * CSS styles for each legend item in hover mode. Only a subset of
  11043. * CSS is supported, notably those options related to text. Properties
  11044. * are inherited from `style` unless overridden here.
  11045. *
  11046. * @see In styled mode, the hovered legend items can be styled with
  11047. * the `.highcharts-legend-item:hover` pesudo-class.
  11048. *
  11049. * @sample {highcharts} highcharts/legend/itemhoverstyle/
  11050. * Red on hover
  11051. * @sample {highmaps} maps/legend/itemstyle/
  11052. * Item text styles
  11053. *
  11054. * @type {Highcharts.CSSObject}
  11055. * @default {"color": "#000000"}
  11056. */
  11057. itemHoverStyle: {
  11058. /**
  11059. * @ignore
  11060. */
  11061. color: '#000000'
  11062. },
  11063. /**
  11064. * CSS styles for each legend item when the corresponding series or
  11065. * point is hidden. Only a subset of CSS is supported, notably those
  11066. * options related to text. Properties are inherited from `style`
  11067. * unless overridden here.
  11068. *
  11069. * @see In styled mode, the hidden legend items can be styled with
  11070. * the `.highcharts-legend-item-hidden` class.
  11071. *
  11072. * @sample {highcharts} highcharts/legend/itemhiddenstyle/
  11073. * Darker gray color
  11074. *
  11075. * @type {Highcharts.CSSObject}
  11076. * @default {"color": "#cccccc"}
  11077. */
  11078. itemHiddenStyle: {
  11079. /**
  11080. * @ignore
  11081. */
  11082. color: '#cccccc'
  11083. },
  11084. /**
  11085. * Whether to apply a drop shadow to the legend. A `backgroundColor`
  11086. * also needs to be applied for this to take effect. The shadow can be
  11087. * an object configuration containing `color`, `offsetX`, `offsetY`,
  11088. * `opacity` and `width`.
  11089. *
  11090. * @sample {highcharts} highcharts/legend/shadow/
  11091. * White background and drop shadow
  11092. * @sample {highstock} stock/legend/align/
  11093. * Various legend options
  11094. * @sample {highmaps} maps/legend/border-background/
  11095. * Border and background options
  11096. *
  11097. * @type {boolean|Highcharts.CSSObject}
  11098. */
  11099. shadow: false,
  11100. /**
  11101. * Default styling for the checkbox next to a legend item when
  11102. * `showCheckbox` is true.
  11103. *
  11104. * @type {Highcharts.CSSObject}
  11105. * @default {"width": "13px", "height": "13px", "position":"absolute"}
  11106. */
  11107. itemCheckboxStyle: {
  11108. /**
  11109. * @ignore
  11110. */
  11111. position: 'absolute',
  11112. /**
  11113. * @ignore
  11114. */
  11115. width: '13px', // for IE precision
  11116. /**
  11117. * @ignore
  11118. */
  11119. height: '13px'
  11120. },
  11121. // itemWidth: undefined,
  11122. /**
  11123. * When this is true, the legend symbol width will be the same as
  11124. * the symbol height, which in turn defaults to the font size of the
  11125. * legend items.
  11126. *
  11127. * @since 5.0.0
  11128. */
  11129. squareSymbol: true,
  11130. /**
  11131. * The pixel height of the symbol for series types that use a rectangle
  11132. * in the legend. Defaults to the font size of legend items.
  11133. *
  11134. * @productdesc {highmaps}
  11135. * In Highmaps, when the symbol is the gradient of a vertical color
  11136. * axis, the height defaults to 200.
  11137. *
  11138. * @sample {highmaps} maps/legend/layout-vertical-sized/
  11139. * Sized vertical gradient
  11140. * @sample {highmaps} maps/legend/padding-itemmargin/
  11141. * No distance between data classes
  11142. *
  11143. * @type {number}
  11144. * @since 3.0.8
  11145. * @apioption legend.symbolHeight
  11146. */
  11147. /**
  11148. * The border radius of the symbol for series types that use a rectangle
  11149. * in the legend. Defaults to half the `symbolHeight`.
  11150. *
  11151. * @sample {highcharts} highcharts/legend/symbolradius/
  11152. * Round symbols
  11153. * @sample {highstock} highcharts/legend/symbolradius/
  11154. * Round symbols
  11155. * @sample {highmaps} highcharts/legend/symbolradius/
  11156. * Round symbols
  11157. *
  11158. * @type {number}
  11159. * @since 3.0.8
  11160. * @apioption legend.symbolRadius
  11161. */
  11162. /**
  11163. * The pixel width of the legend item symbol. When the `squareSymbol`
  11164. * option is set, this defaults to the `symbolHeight`, otherwise 16.
  11165. *
  11166. * @productdesc {highmaps}
  11167. * In Highmaps, when the symbol is the gradient of a horizontal color
  11168. * axis, the width defaults to 200.
  11169. *
  11170. * @sample {highcharts} highcharts/legend/symbolwidth/
  11171. * Greater symbol width and padding
  11172. * @sample {highmaps} maps/legend/padding-itemmargin/
  11173. * Padding and item margins demonstrated
  11174. * @sample {highmaps} maps/legend/layout-vertical-sized/
  11175. * Sized vertical gradient
  11176. *
  11177. * @type {number}
  11178. * @apioption legend.symbolWidth
  11179. */
  11180. /**
  11181. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  11182. * to render the legend item texts.
  11183. *
  11184. * Prior to 4.1.7, when using HTML, [legend.navigation](
  11185. * #legend.navigation) was disabled.
  11186. *
  11187. * @type {boolean}
  11188. * @default false
  11189. * @apioption legend.useHTML
  11190. */
  11191. /**
  11192. * The width of the legend box. If a number is set, it translates to
  11193. * pixels. Since v7.0.2 it allows setting a percent string of the full
  11194. * chart width, for example `40%`.
  11195. *
  11196. * Defaults to the full chart width from legends below or above the
  11197. * chart, half the chart width for legends to the left and right.
  11198. *
  11199. * @sample {highcharts} highcharts/legend/width/
  11200. * Aligned to the plot area
  11201. * @sample {highcharts} highcharts/legend/width-percent/
  11202. * A percent of the chart width
  11203. *
  11204. * @type {number|string}
  11205. * @since 2.0
  11206. * @apioption legend.width
  11207. */
  11208. /**
  11209. * The pixel padding between the legend item symbol and the legend
  11210. * item text.
  11211. *
  11212. * @sample {highcharts} highcharts/legend/symbolpadding/
  11213. * Greater symbol width and padding
  11214. */
  11215. symbolPadding: 5,
  11216. /**
  11217. * The vertical alignment of the legend box. Can be one of `top`,
  11218. * `middle` or `bottom`. Vertical position can be further determined
  11219. * by the `y` option.
  11220. *
  11221. * In the case that the legend is aligned in a corner position, the
  11222. * `layout` option will determine whether to place it above/below
  11223. * or on the side of the plot area.
  11224. *
  11225. * When the [layout](#legend.layout) option is `proximate`, the
  11226. * `verticalAlign` option doesn't apply.
  11227. *
  11228. * @sample {highcharts} highcharts/legend/verticalalign/
  11229. * Legend 100px from the top of the chart
  11230. * @sample {highstock} stock/legend/align/
  11231. * Various legend options
  11232. * @sample {highmaps} maps/legend/alignment/
  11233. * Legend alignment
  11234. *
  11235. * @type {Highcharts.VerticalAlignType}
  11236. * @since 2.0
  11237. */
  11238. verticalAlign: 'bottom',
  11239. // width: undefined,
  11240. /**
  11241. * The x offset of the legend relative to its horizontal alignment
  11242. * `align` within chart.spacingLeft and chart.spacingRight. Negative
  11243. * x moves it to the left, positive x moves it to the right.
  11244. *
  11245. * @sample {highcharts} highcharts/legend/width/
  11246. * Aligned to the plot area
  11247. *
  11248. * @since 2.0
  11249. */
  11250. x: 0,
  11251. /**
  11252. * The vertical offset of the legend relative to it's vertical alignment
  11253. * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
  11254. * Negative y moves it up, positive y moves it down.
  11255. *
  11256. * @sample {highcharts} highcharts/legend/verticalalign/
  11257. * Legend 100px from the top of the chart
  11258. * @sample {highstock} stock/legend/align/
  11259. * Various legend options
  11260. * @sample {highmaps} maps/legend/alignment/
  11261. * Legend alignment
  11262. *
  11263. * @since 2.0
  11264. */
  11265. y: 0,
  11266. /**
  11267. * A title to be added on top of the legend.
  11268. *
  11269. * @sample {highcharts} highcharts/legend/title/
  11270. * Legend title
  11271. * @sample {highmaps} maps/legend/alignment/
  11272. * Legend with title
  11273. *
  11274. * @since 3.0
  11275. */
  11276. title: {
  11277. /**
  11278. * A text or HTML string for the title.
  11279. *
  11280. * @type {string}
  11281. * @since 3.0
  11282. * @apioption legend.title.text
  11283. */
  11284. /**
  11285. * Generic CSS styles for the legend title.
  11286. *
  11287. * @see In styled mode, the legend title is styled with the
  11288. * `.highcharts-legend-title` class.
  11289. *
  11290. * @type {Highcharts.CSSObject}
  11291. * @default {"fontWeight": "bold"}
  11292. * @since 3.0
  11293. */
  11294. style: {
  11295. /**
  11296. * @ignore
  11297. */
  11298. fontWeight: 'bold'
  11299. }
  11300. }
  11301. },
  11302. /**
  11303. * The loading options control the appearance of the loading screen
  11304. * that covers the plot area on chart operations. This screen only
  11305. * appears after an explicit call to `chart.showLoading()`. It is a
  11306. * utility for developers to communicate to the end user that something
  11307. * is going on, for example while retrieving new data via an XHR connection.
  11308. * The "Loading..." text itself is not part of this configuration
  11309. * object, but part of the `lang` object.
  11310. */
  11311. loading: {
  11312. /**
  11313. * The duration in milliseconds of the fade out effect.
  11314. *
  11315. * @sample highcharts/loading/hideduration/
  11316. * Fade in and out over a second
  11317. *
  11318. * @type {number}
  11319. * @default 100
  11320. * @since 1.2.0
  11321. * @apioption loading.hideDuration
  11322. */
  11323. /**
  11324. * The duration in milliseconds of the fade in effect.
  11325. *
  11326. * @sample highcharts/loading/hideduration/
  11327. * Fade in and out over a second
  11328. *
  11329. * @type {number}
  11330. * @default 100
  11331. * @since 1.2.0
  11332. * @apioption loading.showDuration
  11333. */
  11334. /**
  11335. * CSS styles for the loading label `span`.
  11336. *
  11337. * @see In styled mode, the loading label is styled with the
  11338. * `.highcharts-loading-inner` class.
  11339. *
  11340. * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
  11341. * Vertically centered
  11342. * @sample {highstock} stock/loading/general/
  11343. * Label styles
  11344. *
  11345. * @type {Highcharts.CSSObject}
  11346. * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
  11347. * @since 1.2.0
  11348. */
  11349. labelStyle: {
  11350. /**
  11351. * @ignore
  11352. */
  11353. fontWeight: 'bold',
  11354. /**
  11355. * @ignore
  11356. */
  11357. position: 'relative',
  11358. /**
  11359. * @ignore
  11360. */
  11361. top: '45%'
  11362. },
  11363. /**
  11364. * CSS styles for the loading screen that covers the plot area.
  11365. *
  11366. * In styled mode, the loading label is styled with the
  11367. * `.highcharts-loading` class.
  11368. *
  11369. * @sample {highcharts|highmaps} highcharts/loading/style/
  11370. * Gray plot area, white text
  11371. * @sample {highstock} stock/loading/general/
  11372. * Gray plot area, white text
  11373. *
  11374. * @type {Highcharts.CSSObject}
  11375. * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
  11376. * @since 1.2.0
  11377. */
  11378. style: {
  11379. /**
  11380. * @ignore
  11381. */
  11382. position: 'absolute',
  11383. /**
  11384. * @ignore
  11385. */
  11386. backgroundColor: '#ffffff',
  11387. /**
  11388. * @ignore
  11389. */
  11390. opacity: 0.5,
  11391. /**
  11392. * @ignore
  11393. */
  11394. textAlign: 'center'
  11395. }
  11396. },
  11397. /**
  11398. * Options for the tooltip that appears when the user hovers over a
  11399. * series or point.
  11400. */
  11401. tooltip: {
  11402. /**
  11403. * The color of the tooltip border. When `undefined`, the border takes
  11404. * the color of the corresponding series or point.
  11405. *
  11406. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11407. * Follow series by default
  11408. * @sample {highcharts} highcharts/tooltip/bordercolor-black/
  11409. * Black border
  11410. * @sample {highstock} stock/tooltip/general/
  11411. * Styled tooltip
  11412. * @sample {highmaps} maps/tooltip/background-border/
  11413. * Background and border demo
  11414. *
  11415. * @type {Highcharts.ColorString}
  11416. * @apioption tooltip.borderColor
  11417. */
  11418. /**
  11419. * Since 4.1, the crosshair definitions are moved to the Axis object
  11420. * in order for a better separation from the tooltip. See
  11421. * [xAxis.crosshair](#xAxis.crosshair)<a>.</a>
  11422. *
  11423. * @sample {highcharts} highcharts/tooltip/crosshairs-x/
  11424. * Enable a crosshair for the x value
  11425. *
  11426. * @deprecated
  11427. *
  11428. * @type {*}
  11429. * @default true
  11430. * @apioption tooltip.crosshairs
  11431. */
  11432. /**
  11433. * Whether the tooltip should follow the mouse as it moves across
  11434. * columns, pie slices and other point types with an extent. By default
  11435. * it behaves this way for scatter, bubble and pie series by override
  11436. * in the `plotOptions` for those series types.
  11437. *
  11438. * For touch moves to behave the same way, [followTouchMove](
  11439. * #tooltip.followTouchMove) must be `true` also.
  11440. *
  11441. * @type {boolean}
  11442. * @default {highcharts} false
  11443. * @default {highstock} false
  11444. * @default {highmaps} true
  11445. * @since 3.0
  11446. * @apioption tooltip.followPointer
  11447. */
  11448. /**
  11449. * Whether the tooltip should update as the finger moves on a touch
  11450. * device. If this is `true` and [chart.panning](#chart.panning) is
  11451. * set,`followTouchMove` will take over one-finger touches, so the user
  11452. * needs to use two fingers for zooming and panning.
  11453. *
  11454. * Note the difference to [followPointer](#tooltip.followPointer) that
  11455. * only defines the _position_ of the tooltip. If `followPointer` is
  11456. * false in for example a column series, the tooltip will show above or
  11457. * below the column, but as `followTouchMove` is true, the tooltip will
  11458. * jump from column to column as the user swipes across the plot area.
  11459. *
  11460. * @type {boolean}
  11461. * @default {highcharts} true
  11462. * @default {highstock} true
  11463. * @default {highmaps} false
  11464. * @since 3.0.1
  11465. * @apioption tooltip.followTouchMove
  11466. */
  11467. /**
  11468. * Callback function to format the text of the tooltip from scratch.
  11469. * Return `false` to disable tooltip for a specific point on series.
  11470. *
  11471. * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
  11472. * the tooltip is parsed and converted to SVG, therefore this isn't a
  11473. * complete HTML renderer. The following tags are supported: `<b>`,
  11474. * `<strong>`, `<i>`, `<em>`, `<br/>`, `<span>`. Spans can be styled
  11475. * with a `style` attribute, but only text-related CSS that is shared
  11476. * with SVG is handled.
  11477. *
  11478. * Since version 2.1 the tooltip can be shared between multiple series
  11479. * through the `shared` option. The available data in the formatter
  11480. * differ a bit depending on whether the tooltip is shared or not. In
  11481. * a shared tooltip, all properties except `x`, which is common for
  11482. * all points, are kept in an array, `this.points`.
  11483. *
  11484. * Available data are:
  11485. *
  11486. * <dl>
  11487. *
  11488. * <dt>this.percentage (not shared) / this.points[i].percentage (shared)
  11489. * </dt>
  11490. *
  11491. * <dd>Stacked series and pies only. The point's percentage of the
  11492. * total.
  11493. * </dd>
  11494. *
  11495. * <dt>this.point (not shared) / this.points[i].point (shared)</dt>
  11496. *
  11497. * <dd>The point object. The point name, if defined, is available
  11498. * through `this.point.name`.</dd>
  11499. *
  11500. * <dt>this.points</dt>
  11501. *
  11502. * <dd>In a shared tooltip, this is an array containing all other
  11503. * properties for each point.</dd>
  11504. *
  11505. * <dt>this.series (not shared) / this.points[i].series (shared)</dt>
  11506. *
  11507. * <dd>The series object. The series name is available through
  11508. * `this.series.name`.</dd>
  11509. *
  11510. * <dt>this.total (not shared) / this.points[i].total (shared)</dt>
  11511. *
  11512. * <dd>Stacked series only. The total value at this point's x value.
  11513. * </dd>
  11514. *
  11515. * <dt>this.x</dt>
  11516. *
  11517. * <dd>The x value. This property is the same regardless of the tooltip
  11518. * being shared or not.</dd>
  11519. *
  11520. * <dt>this.y (not shared) / this.points[i].y (shared)</dt>
  11521. *
  11522. * <dd>The y value.</dd>
  11523. *
  11524. * </dl>
  11525. *
  11526. * @sample {highcharts} highcharts/tooltip/formatter-simple/
  11527. * Simple string formatting
  11528. * @sample {highcharts} highcharts/tooltip/formatter-shared/
  11529. * Formatting with shared tooltip
  11530. * @sample {highstock} stock/tooltip/formatter/
  11531. * Formatting with shared tooltip
  11532. * @sample {highmaps} maps/tooltip/formatter/
  11533. * String formatting
  11534. *
  11535. * @type {Highcharts.FormatterCallbackFunction<Highcharts.TooltipFormatterContextObject>}
  11536. * @apioption tooltip.formatter
  11537. */
  11538. /**
  11539. * The number of milliseconds to wait until the tooltip is hidden when
  11540. * mouse out from a point or chart.
  11541. *
  11542. * @type {number}
  11543. * @default 500
  11544. * @since 3.0
  11545. * @apioption tooltip.hideDelay
  11546. */
  11547. /**
  11548. * Whether to allow the tooltip to render outside the chart's SVG
  11549. * element box. By default (`false`), the tooltip is rendered within the
  11550. * chart's SVG element, which results in the tooltip being aligned
  11551. * inside the chart area. For small charts, this may result in clipping
  11552. * or overlapping. When `true`, a separate SVG element is created and
  11553. * overlaid on the page, allowing the tooltip to be aligned inside the
  11554. * page itself.
  11555. *
  11556. * @sample highcharts/tooltip/outside
  11557. * Small charts with tooltips outside
  11558. *
  11559. * @type {boolean}
  11560. * @default false
  11561. * @since 6.1.1
  11562. * @apioption tooltip.outside
  11563. */
  11564. /**
  11565. * A callback function for formatting the HTML output for a single point
  11566. * in the tooltip. Like the `pointFormat` string, but with more
  11567. * flexibility.
  11568. *
  11569. * @type {Function}
  11570. * @since 4.1.0
  11571. * @context Highcharts.Point
  11572. * @apioption tooltip.pointFormatter
  11573. */
  11574. /**
  11575. * A callback function to place the tooltip in a default position. The
  11576. * callback receives three parameters: `labelWidth`, `labelHeight` and
  11577. * `point`, where point contains values for `plotX` and `plotY` telling
  11578. * where the reference point is in the plot area. Add `chart.plotLeft`
  11579. * and `chart.plotTop` to get the full coordinates.
  11580. *
  11581. * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
  11582. * positioner is called for each of the boxes separately, including
  11583. * xAxis header. xAxis header is not a point, instead `point` argument
  11584. * contains info:
  11585. * `{ plotX: Number, plotY: Number, isHeader: Boolean }`
  11586. *
  11587. *
  11588. * The return should be an object containing x and y values, for example
  11589. * `{ x: 100, y: 100 }`.
  11590. *
  11591. * @sample {highcharts} highcharts/tooltip/positioner/
  11592. * A fixed tooltip position
  11593. * @sample {highstock} stock/tooltip/positioner/
  11594. * A fixed tooltip position on top of the chart
  11595. * @sample {highmaps} maps/tooltip/positioner/
  11596. * A fixed tooltip position
  11597. * @sample {highstock} stock/tooltip/split-positioner/
  11598. * Split tooltip with fixed positions
  11599. *
  11600. * @type {Highcharts.TooltipPositionerCallbackFunction}
  11601. * @since 2.2.4
  11602. * @apioption tooltip.positioner
  11603. */
  11604. /**
  11605. * The name of a symbol to use for the border around the tooltip. Can
  11606. * be one of: `"callout"`, `"circle"` or `"square"`. When
  11607. * [tooltip.split](#tooltip.split) option is enabled, shape is applied
  11608. * to all boxes except header, which is controlled by
  11609. * [tooltip.headerShape](#tooltip.headerShape).
  11610. *
  11611. * Custom callbacks for symbol path generation can also be added to
  11612. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  11613. * [series.marker.symbol](plotOptions.line.marker.symbol).
  11614. *
  11615. * @type {string}
  11616. * @default callout
  11617. * @since 4.0
  11618. * @validvalue ["callout", "square"]
  11619. * @apioption tooltip.shape
  11620. */
  11621. /**
  11622. * The name of a symbol to use for the border around the tooltip
  11623. * header. Applies only when [tooltip.split](#tooltip.split) is
  11624. * enabled.
  11625. *
  11626. * Custom callbacks for symbol path generation can also be added to
  11627. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  11628. * [series.marker.symbol](plotOptions.line.marker.symbol).
  11629. *
  11630. * @see [tooltip.shape](#tooltip.shape)
  11631. * @type {String}
  11632. * @default callout
  11633. * @sample {highstock} stock/tooltip/split-positioner/
  11634. * Different shapes for header and split boxes
  11635. * @validvalue ["callout", "square"]
  11636. * @since 7.0
  11637. * @apioption tooltip.headerShape
  11638. */
  11639. /**
  11640. * When the tooltip is shared, the entire plot area will capture mouse
  11641. * movement or touch events. Tooltip texts for series types with ordered
  11642. * data (not pie, scatter, flags etc) will be shown in a single bubble.
  11643. * This is recommended for single series charts and for tablet/mobile
  11644. * optimized charts.
  11645. *
  11646. * See also [tooltip.split](#tooltip.split), that is better suited for
  11647. * charts with many series, especially line-type series. The
  11648. * `tooltip.split` option takes precedence over `tooltip.shared`.
  11649. *
  11650. * @sample {highcharts} highcharts/tooltip/shared-false/
  11651. * False by default
  11652. * @sample {highcharts} highcharts/tooltip/shared-true/
  11653. * True
  11654. * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
  11655. * True with x axis crosshair
  11656. * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
  11657. * True with mixed series types
  11658. *
  11659. * @type {boolean}
  11660. * @default false
  11661. * @since 2.1
  11662. * @product highcharts highstock
  11663. * @apioption tooltip.shared
  11664. */
  11665. /**
  11666. * Split the tooltip into one label per series, with the header close
  11667. * to the axis. This is recommended over [shared](#tooltip.shared)
  11668. * tooltips for charts with multiple line series, generally making them
  11669. * easier to read. This option takes precedence over `tooltip.shared`.
  11670. *
  11671. * @productdesc {highstock} In Highstock, tooltips are split by default
  11672. * since v6.0.0. Stock charts typically contain multi-dimension points
  11673. * and multiple panes, making split tooltips the preferred layout over
  11674. * the previous `shared` tooltip.
  11675. *
  11676. * @sample highcharts/tooltip/split/
  11677. * Split tooltip
  11678. *
  11679. * @type {boolean}
  11680. * @default {highcharts} false
  11681. * @default {highstock} true
  11682. * @since 5.0.0
  11683. * @product highcharts highstock
  11684. * @apioption tooltip.split
  11685. */
  11686. /**
  11687. * Use HTML to render the contents of the tooltip instead of SVG. Using
  11688. * HTML allows advanced formatting like tables and images in the
  11689. * tooltip. It is also recommended for rtl languages as it works around
  11690. * rtl bugs in early Firefox.
  11691. *
  11692. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  11693. * A table for value alignment
  11694. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  11695. * Full HTML tooltip
  11696. * @sample {highmaps} maps/tooltip/usehtml/
  11697. * Pure HTML tooltip
  11698. *
  11699. * @type {boolean}
  11700. * @default false
  11701. * @since 2.2
  11702. * @apioption tooltip.useHTML
  11703. */
  11704. /**
  11705. * How many decimals to show in each series' y value. This is
  11706. * overridable in each series' tooltip options object. The default is to
  11707. * preserve all decimals.
  11708. *
  11709. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  11710. * Set decimals, prefix and suffix for the value
  11711. * @sample {highmaps} maps/tooltip/valuedecimals/
  11712. * Set decimals, prefix and suffix for the value
  11713. *
  11714. * @type {number}
  11715. * @since 2.2
  11716. * @apioption tooltip.valueDecimals
  11717. */
  11718. /**
  11719. * A string to prepend to each series' y value. Overridable in each
  11720. * series' tooltip options object.
  11721. *
  11722. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  11723. * Set decimals, prefix and suffix for the value
  11724. * @sample {highmaps} maps/tooltip/valuedecimals/
  11725. * Set decimals, prefix and suffix for the value
  11726. *
  11727. * @type {string}
  11728. * @since 2.2
  11729. * @apioption tooltip.valuePrefix
  11730. */
  11731. /**
  11732. * A string to append to each series' y value. Overridable in each
  11733. * series' tooltip options object.
  11734. *
  11735. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  11736. * Set decimals, prefix and suffix for the value
  11737. * @sample {highmaps} maps/tooltip/valuedecimals/
  11738. * Set decimals, prefix and suffix for the value
  11739. *
  11740. * @type {string}
  11741. * @since 2.2
  11742. * @apioption tooltip.valueSuffix
  11743. */
  11744. /**
  11745. * The format for the date in the tooltip header if the X axis is a
  11746. * datetime axis. The default is a best guess based on the smallest
  11747. * distance between points in the chart.
  11748. *
  11749. * @sample {highcharts} highcharts/tooltip/xdateformat/
  11750. * A different format
  11751. *
  11752. * @type {string}
  11753. * @product highcharts highstock gantt
  11754. * @apioption tooltip.xDateFormat
  11755. */
  11756. /**
  11757. * How many decimals to show for the `point.change` value when the
  11758. * `series.compare` option is set. This is overridable in each series'
  11759. * tooltip options object. The default is to preserve all decimals.
  11760. *
  11761. * @type {number}
  11762. * @since 1.0.1
  11763. * @product highstock
  11764. * @apioption tooltip.changeDecimals
  11765. */
  11766. /**
  11767. * Enable or disable the tooltip.
  11768. *
  11769. * @sample {highcharts} highcharts/tooltip/enabled/
  11770. * Disabled
  11771. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  11772. * Disable tooltip and show values on chart instead
  11773. */
  11774. enabled: true,
  11775. /**
  11776. * Enable or disable animation of the tooltip.
  11777. *
  11778. * @type {boolean}
  11779. * @default true
  11780. * @since 2.3.0
  11781. */
  11782. animation: svg,
  11783. /**
  11784. * The radius of the rounded border corners.
  11785. *
  11786. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11787. * 5px by default
  11788. * @sample {highcharts} highcharts/tooltip/borderradius-0/
  11789. * Square borders
  11790. * @sample {highmaps} maps/tooltip/background-border/
  11791. * Background and border demo
  11792. */
  11793. borderRadius: 3,
  11794. /**
  11795. * For series on a datetime axes, the date format in the tooltip's
  11796. * header will by default be guessed based on the closest data points.
  11797. * This member gives the default string representations used for
  11798. * each unit. For an overview of the replacement codes, see
  11799. * [dateFormat](/class-reference/Highcharts#dateFormat).
  11800. *
  11801. * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
  11802. *
  11803. * @type {Highcharts.Dictionary<string>}
  11804. * @product highcharts highstock gantt
  11805. */
  11806. dateTimeLabelFormats: {
  11807. millisecond: '%A, %b %e, %H:%M:%S.%L',
  11808. second: '%A, %b %e, %H:%M:%S',
  11809. minute: '%A, %b %e, %H:%M',
  11810. hour: '%A, %b %e, %H:%M',
  11811. day: '%A, %b %e, %Y',
  11812. week: 'Week from %A, %b %e, %Y',
  11813. month: '%B %Y',
  11814. year: '%Y'
  11815. },
  11816. /**
  11817. * A string to append to the tooltip format.
  11818. *
  11819. * @sample {highcharts} highcharts/tooltip/footerformat/
  11820. * A table for value alignment
  11821. * @sample {highmaps} maps/tooltip/format/
  11822. * Format demo
  11823. *
  11824. * @since 2.2
  11825. */
  11826. footerFormat: '',
  11827. /**
  11828. * Padding inside the tooltip, in pixels.
  11829. *
  11830. * @since 5.0.0
  11831. */
  11832. padding: 8,
  11833. /**
  11834. * Proximity snap for graphs or single points. It defaults to 10 for
  11835. * mouse-powered devices and 25 for touch devices.
  11836. *
  11837. * Note that in most cases the whole plot area captures the mouse
  11838. * movement, and in these cases `tooltip.snap` doesn't make sense. This
  11839. * applies when [stickyTracking](#plotOptions.series.stickyTracking)
  11840. * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
  11841. * or [split](#tooltip.split).
  11842. *
  11843. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11844. * 10 px by default
  11845. * @sample {highcharts} highcharts/tooltip/snap-50/
  11846. * 50 px on graph
  11847. *
  11848. * @type {number}
  11849. * @default 10/25
  11850. * @since 1.2.0
  11851. * @product highcharts highstock
  11852. */
  11853. snap: isTouchDevice ? 25 : 10,
  11854. /**
  11855. * The HTML of the tooltip header line. Variables are enclosed by
  11856. * curly brackets. Available variables are `point.key`, `series.name`,
  11857. * `series.color` and other members from the `point` and `series`
  11858. * objects. The `point.key` variable contains the category name, x
  11859. * value or datetime string depending on the type of axis. For datetime
  11860. * axes, the `point.key` date format can be set using
  11861. * `tooltip.xDateFormat`.
  11862. *
  11863. * @sample {highcharts} highcharts/tooltip/footerformat/
  11864. * An HTML table in the tooltip
  11865. * @sample {highstock} highcharts/tooltip/footerformat/
  11866. * An HTML table in the tooltip
  11867. * @sample {highmaps} maps/tooltip/format/
  11868. * Format demo
  11869. *
  11870. * @type {string}
  11871. * @apioption tooltip.headerFormat
  11872. */
  11873. headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
  11874. /**
  11875. * The HTML of the point's line in the tooltip. Variables are enclosed
  11876. * by curly brackets. Available variables are point.x, point.y, series.
  11877. * name and series.color and other properties on the same form.
  11878. * Furthermore, `point.y` can be extended by the `tooltip.valuePrefix`
  11879. * and `tooltip.valueSuffix` variables. This can also be overridden for
  11880. * each series, which makes it a good hook for displaying units.
  11881. *
  11882. * In styled mode, the dot is colored by a class name rather
  11883. * than the point color.
  11884. *
  11885. * @sample {highcharts} highcharts/tooltip/pointformat/
  11886. * A different point format with value suffix
  11887. * @sample {highmaps} maps/tooltip/format/
  11888. * Format demo
  11889. *
  11890. * @type {string}
  11891. * @default <span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>
  11892. * @since 2.2
  11893. * @apioption tooltip.pointFormat
  11894. */
  11895. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
  11896. /**
  11897. * The background color or gradient for the tooltip.
  11898. *
  11899. * In styled mode, the stroke width is set in the
  11900. * `.highcharts-tooltip-box` class.
  11901. *
  11902. * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
  11903. * Yellowish background
  11904. * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
  11905. * Gradient
  11906. * @sample {highcharts} highcharts/css/tooltip-border-background/
  11907. * Tooltip in styled mode
  11908. * @sample {highstock} stock/tooltip/general/
  11909. * Custom tooltip
  11910. * @sample {highstock} highcharts/css/tooltip-border-background/
  11911. * Tooltip in styled mode
  11912. * @sample {highmaps} maps/tooltip/background-border/
  11913. * Background and border demo
  11914. * @sample {highmaps} highcharts/css/tooltip-border-background/
  11915. * Tooltip in styled mode
  11916. *
  11917. * @type {Highcharts.ColorString}
  11918. */
  11919. backgroundColor: color('#f7f7f7')
  11920. .setOpacity(0.85).get(),
  11921. /**
  11922. * The pixel width of the tooltip border.
  11923. *
  11924. * In styled mode, the stroke width is set in the
  11925. * `.highcharts-tooltip-box` class.
  11926. *
  11927. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11928. * 2px by default
  11929. * @sample {highcharts} highcharts/tooltip/borderwidth/
  11930. * No border (shadow only)
  11931. * @sample {highcharts} highcharts/css/tooltip-border-background/
  11932. * Tooltip in styled mode
  11933. * @sample {highstock} stock/tooltip/general/
  11934. * Custom tooltip
  11935. * @sample {highstock} highcharts/css/tooltip-border-background/
  11936. * Tooltip in styled mode
  11937. * @sample {highmaps} maps/tooltip/background-border/
  11938. * Background and border demo
  11939. * @sample {highmaps} highcharts/css/tooltip-border-background/
  11940. * Tooltip in styled mode
  11941. */
  11942. borderWidth: 1,
  11943. /**
  11944. * Whether to apply a drop shadow to the tooltip.
  11945. *
  11946. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  11947. * True by default
  11948. * @sample {highcharts} highcharts/tooltip/shadow/
  11949. * False
  11950. * @sample {highmaps} maps/tooltip/positioner/
  11951. * Fixed tooltip position, border and shadow disabled
  11952. */
  11953. shadow: true,
  11954. /**
  11955. * CSS styles for the tooltip. The tooltip can also be styled through
  11956. * the CSS class `.highcharts-tooltip`.
  11957. *
  11958. * @sample {highcharts} highcharts/tooltip/style/
  11959. * Greater padding, bold text
  11960. *
  11961. * @type {Highcharts.CSSObject}
  11962. * @default {"color": "#333333", "cursor": "default", "fontSize": "12px", "pointerEvents": "none", "whiteSpace": "nowrap"}
  11963. */
  11964. style: {
  11965. /**
  11966. * @ignore
  11967. */
  11968. color: '#333333',
  11969. /**
  11970. * @ignore
  11971. */
  11972. cursor: 'default',
  11973. /**
  11974. * @ignore
  11975. */
  11976. fontSize: '12px',
  11977. /**
  11978. * @ignore
  11979. */
  11980. pointerEvents: 'none',
  11981. // #1686 http://caniuse.com/#feat=pointer-events
  11982. /**
  11983. * @ignore
  11984. */
  11985. whiteSpace: 'nowrap'
  11986. }
  11987. },
  11988. /**
  11989. * Highchart by default puts a credits label in the lower right corner
  11990. * of the chart. This can be changed using these options.
  11991. */
  11992. credits: {
  11993. /**
  11994. * Credits for map source to be concatenated with conventional credit
  11995. * text. By default this is a format string that collects copyright
  11996. * information from the map if available.
  11997. *
  11998. * @see [mapTextFull](#credits.mapTextFull)
  11999. * @see [text](#credits.text)
  12000. *
  12001. * @type {string}
  12002. * @default \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
  12003. * @since 4.2.2
  12004. * @product highmaps
  12005. * @apioption credits.mapText
  12006. */
  12007. /**
  12008. * Detailed credits for map source to be displayed on hover of credits
  12009. * text. By default this is a format string that collects copyright
  12010. * information from the map if available.
  12011. *
  12012. * @see [mapText](#credits.mapText)
  12013. * @see [text](#credits.text)
  12014. *
  12015. * @type {string}
  12016. * @default {geojson.copyright}
  12017. * @since 4.2.2
  12018. * @product highmaps
  12019. * @apioption credits.mapTextFull
  12020. */
  12021. /**
  12022. * Whether to show the credits text.
  12023. *
  12024. * @sample {highcharts} highcharts/credits/enabled-false/
  12025. * Credits disabled
  12026. * @sample {highstock} stock/credits/enabled/
  12027. * Credits disabled
  12028. * @sample {highmaps} maps/credits/enabled-false/
  12029. * Credits disabled
  12030. */
  12031. enabled: true,
  12032. /**
  12033. * The URL for the credits label.
  12034. *
  12035. * @sample {highcharts} highcharts/credits/href/
  12036. * Custom URL and text
  12037. * @sample {highmaps} maps/credits/customized/
  12038. * Custom URL and text
  12039. */
  12040. href: 'https://www.highcharts.com?credits',
  12041. /**
  12042. * Position configuration for the credits label.
  12043. *
  12044. * @sample {highcharts} highcharts/credits/position-left/
  12045. * Left aligned
  12046. * @sample {highcharts} highcharts/credits/position-left/
  12047. * Left aligned
  12048. * @sample {highmaps} maps/credits/customized/
  12049. * Left aligned
  12050. * @sample {highmaps} maps/credits/customized/
  12051. * Left aligned
  12052. *
  12053. * @type {Highcharts.AlignObject}
  12054. * @since 2.1
  12055. */
  12056. position: {
  12057. /**
  12058. * Horizontal alignment of the credits.
  12059. *
  12060. * @type {Highcharts.AlignType}
  12061. */
  12062. align: 'right',
  12063. /**
  12064. * Horizontal pixel offset of the credits.
  12065. */
  12066. x: -10,
  12067. /**
  12068. * Vertical alignment of the credits.
  12069. *
  12070. * @type {Highcharts.VerticalAlignType}
  12071. */
  12072. verticalAlign: 'bottom',
  12073. /**
  12074. * Vertical pixel offset of the credits.
  12075. */
  12076. y: -5
  12077. },
  12078. /**
  12079. * CSS styles for the credits label.
  12080. *
  12081. * @see In styled mode, credits styles can be set with the
  12082. * `.highcharts-credits` class.
  12083. *
  12084. * @type {Highcharts.CSSObject}
  12085. * @default {"cursor": "pointer", "color": "#999999", "fontSize": "10px"}
  12086. */
  12087. style: {
  12088. /**
  12089. * @ignore
  12090. */
  12091. cursor: 'pointer',
  12092. /**
  12093. * @ignore
  12094. */
  12095. color: '#999999',
  12096. /**
  12097. * @ignore
  12098. */
  12099. fontSize: '9px'
  12100. },
  12101. /**
  12102. * The text for the credits label.
  12103. *
  12104. * @productdesc {highmaps}
  12105. * If a map is loaded as GeoJSON, the text defaults to
  12106. * `Highcharts @ {map-credits}`. Otherwise, it defaults to
  12107. * `Highcharts.com`.
  12108. *
  12109. * @sample {highcharts} highcharts/credits/href/
  12110. * Custom URL and text
  12111. * @sample {highmaps} maps/credits/customized/
  12112. * Custom URL and text
  12113. */
  12114. text: 'Highcharts.com'
  12115. }
  12116. };
  12117. /**
  12118. * Merge the default options with custom options and return the new options
  12119. * structure. Commonly used for defining reusable templates.
  12120. *
  12121. * @sample highcharts/global/useutc-false Setting a global option
  12122. * @sample highcharts/members/setoptions Applying a global theme
  12123. *
  12124. * @function Highcharts.setOptions
  12125. *
  12126. * @param {Highcharts.Options} options
  12127. * The new custom chart options.
  12128. *
  12129. * @return {Highcharts.Options}
  12130. * Updated options.
  12131. */
  12132. H.setOptions = function (options) {
  12133. // Copy in the default options
  12134. H.defaultOptions = merge(true, H.defaultOptions, options);
  12135. // Update the time object
  12136. H.time.update(
  12137. merge(H.defaultOptions.global, H.defaultOptions.time),
  12138. false
  12139. );
  12140. return H.defaultOptions;
  12141. };
  12142. /**
  12143. * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
  12144. * for outside modules wasn't enough because the setOptions method created a new
  12145. * object.
  12146. *
  12147. * @function Highcharts.getOptions
  12148. *
  12149. * @return {Highcharts.Options}
  12150. */
  12151. H.getOptions = function () {
  12152. return H.defaultOptions;
  12153. };
  12154. // Series defaults
  12155. H.defaultPlotOptions = H.defaultOptions.plotOptions;
  12156. /**
  12157. * Global `Time` object with default options. Since v6.0.5, time settings can be
  12158. * applied individually for each chart. If no individual settings apply, this
  12159. * `Time` object is shared by all instances.
  12160. *
  12161. * @name Highcharts.time
  12162. * @type {Highcharts.Time}
  12163. */
  12164. H.time = new H.Time(merge(H.defaultOptions.global, H.defaultOptions.time));
  12165. /**
  12166. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
  12167. * human readable date string. The format is a subset of the formats for PHP's
  12168. * [strftime](http://www.php.net/manual/en/function.strftime.php) function.
  12169. * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
  12170. *
  12171. * Since v6.0.5, all internal dates are formatted through the
  12172. * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
  12173. * The `Highcharts.dateFormat` function only reflects global time settings set
  12174. * with `setOptions`.
  12175. *
  12176. * @function Highcharts.dateFormat
  12177. *
  12178. * @param {string} format
  12179. * The desired format where various time representations are prefixed
  12180. * with `%`.
  12181. *
  12182. * @param {number} timestamp
  12183. * The JavaScript timestamp.
  12184. *
  12185. * @param {boolean} [capitalize=false]
  12186. * Upper case first letter in the return.
  12187. *
  12188. * @return {string}
  12189. * The formatted date.
  12190. */
  12191. H.dateFormat = function (format, timestamp, capitalize) {
  12192. return H.time.dateFormat(format, timestamp, capitalize);
  12193. };
  12194. }(Highcharts));
  12195. (function (H) {
  12196. /**
  12197. * (c) 2010-2019 Torstein Honsi
  12198. *
  12199. * License: www.highcharts.com/license
  12200. */
  12201. var correctFloat = H.correctFloat,
  12202. defined = H.defined,
  12203. destroyObjectProperties = H.destroyObjectProperties,
  12204. fireEvent = H.fireEvent,
  12205. isNumber = H.isNumber,
  12206. merge = H.merge,
  12207. pick = H.pick,
  12208. deg2rad = H.deg2rad;
  12209. /**
  12210. * The Tick class.
  12211. *
  12212. * @private
  12213. * @class
  12214. * @name Highcharts.Tick
  12215. *
  12216. * @param {Highcharts.Axis} axis
  12217. *
  12218. * @param {number} pos The position of the tick on the axis.
  12219. *
  12220. * @param {string} [type] The type of tick.
  12221. *
  12222. * @param {boolean} [noLabel=false] Wether to disable the label or not. Defaults to
  12223. * false.
  12224. *
  12225. * @param {object} [parameters] Optional parameters for the tick.
  12226. *
  12227. * @param {object} [parameters.tickmarkOffset] Set tickmarkOffset for the tick.
  12228. *
  12229. * @param {object} [parameters.category] Set category for the tick.
  12230. */
  12231. H.Tick = function (axis, pos, type, noLabel, parameters) {
  12232. this.axis = axis;
  12233. this.pos = pos;
  12234. this.type = type || '';
  12235. this.isNew = true;
  12236. this.isNewLabel = true;
  12237. this.parameters = parameters || {};
  12238. // Usually undefined, numeric for grid axes
  12239. this.tickmarkOffset = this.parameters.tickmarkOffset;
  12240. this.options = this.parameters.options;
  12241. if (!type && !noLabel) {
  12242. this.addLabel();
  12243. }
  12244. };
  12245. /** @lends Highcharts.Tick.prototype */
  12246. H.Tick.prototype = {
  12247. /**
  12248. * Write the tick label.
  12249. *
  12250. * @private
  12251. * @function Highcharts.Tick#addLabel
  12252. */
  12253. addLabel: function () {
  12254. var tick = this,
  12255. axis = tick.axis,
  12256. options = axis.options,
  12257. chart = axis.chart,
  12258. categories = axis.categories,
  12259. names = axis.names,
  12260. pos = tick.pos,
  12261. labelOptions = pick(
  12262. tick.options && tick.options.labels,
  12263. options.labels
  12264. ),
  12265. str,
  12266. tickPositions = axis.tickPositions,
  12267. isFirst = pos === tickPositions[0],
  12268. isLast = pos === tickPositions[tickPositions.length - 1],
  12269. value = this.parameters.category || (
  12270. categories ?
  12271. pick(categories[pos], names[pos], pos) :
  12272. pos
  12273. ),
  12274. label = tick.label,
  12275. tickPositionInfo = tickPositions.info,
  12276. dateTimeLabelFormat,
  12277. dateTimeLabelFormats,
  12278. i,
  12279. list;
  12280. // Set the datetime label format. If a higher rank is set for this
  12281. // position, use that. If not, use the general format.
  12282. if (axis.isDatetimeAxis && tickPositionInfo) {
  12283. dateTimeLabelFormats = chart.time.resolveDTLFormat(
  12284. options.dateTimeLabelFormats[
  12285. (
  12286. !options.grid &&
  12287. tickPositionInfo.higherRanks[pos]
  12288. ) ||
  12289. tickPositionInfo.unitName
  12290. ]
  12291. );
  12292. dateTimeLabelFormat = dateTimeLabelFormats.main;
  12293. }
  12294. // set properties for access in render method
  12295. tick.isFirst = isFirst;
  12296. tick.isLast = isLast;
  12297. // Get the string
  12298. tick.formatCtx = {
  12299. axis: axis,
  12300. chart: chart,
  12301. isFirst: isFirst,
  12302. isLast: isLast,
  12303. dateTimeLabelFormat: dateTimeLabelFormat,
  12304. tickPositionInfo: tickPositionInfo,
  12305. value: axis.isLog ? correctFloat(axis.lin2log(value)) : value,
  12306. pos: pos
  12307. };
  12308. str = axis.labelFormatter.call(tick.formatCtx, this.formatCtx);
  12309. // Set up conditional formatting based on the format list if existing.
  12310. list = dateTimeLabelFormats && dateTimeLabelFormats.list;
  12311. if (list) {
  12312. tick.shortenLabel = function () {
  12313. for (i = 0; i < list.length; i++) {
  12314. label.attr({
  12315. text: axis.labelFormatter.call(H.extend(
  12316. tick.formatCtx,
  12317. { dateTimeLabelFormat: list[i] }
  12318. ))
  12319. });
  12320. if (
  12321. label.getBBox().width <
  12322. axis.getSlotWidth(tick) - 2 *
  12323. pick(labelOptions.padding, 5)
  12324. ) {
  12325. return;
  12326. }
  12327. }
  12328. label.attr({
  12329. text: ''
  12330. });
  12331. };
  12332. }
  12333. // first call
  12334. if (!defined(label)) {
  12335. tick.label = label =
  12336. defined(str) && labelOptions.enabled ?
  12337. chart.renderer
  12338. .text(
  12339. str,
  12340. 0,
  12341. 0,
  12342. labelOptions.useHTML
  12343. )
  12344. .add(axis.labelGroup) :
  12345. null;
  12346. // Un-rotated length
  12347. if (label) {
  12348. // Without position absolute, IE export sometimes is wrong
  12349. if (!chart.styledMode) {
  12350. label.css(merge(labelOptions.style));
  12351. }
  12352. label.textPxLength = label.getBBox().width;
  12353. }
  12354. // Base value to detect change for new calls to getBBox
  12355. tick.rotation = 0;
  12356. // update
  12357. } else if (label && label.textStr !== str) {
  12358. // When resetting text, also reset the width if dynamically set
  12359. // (#8809)
  12360. if (
  12361. label.textWidth &&
  12362. !(labelOptions.style && labelOptions.style.width) &&
  12363. !label.styles.width
  12364. ) {
  12365. label.css({ width: null });
  12366. }
  12367. label.attr({ text: str });
  12368. }
  12369. },
  12370. /**
  12371. * Get the offset height or width of the label
  12372. *
  12373. * @private
  12374. * @function Highcharts.Tick#getLabelSize
  12375. *
  12376. * @return {number}
  12377. */
  12378. getLabelSize: function () {
  12379. return this.label ?
  12380. this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
  12381. 0;
  12382. },
  12383. /**
  12384. * Handle the label overflow by adjusting the labels to the left and right
  12385. * edge, or hide them if they collide into the neighbour label.
  12386. *
  12387. * @private
  12388. * @function Highcharts.Tick#handleOverflow
  12389. *
  12390. * @param {Highcharts.PositionObject} xy
  12391. */
  12392. handleOverflow: function (xy) {
  12393. var tick = this,
  12394. axis = this.axis,
  12395. labelOptions = axis.options.labels,
  12396. pxPos = xy.x,
  12397. chartWidth = axis.chart.chartWidth,
  12398. spacing = axis.chart.spacing,
  12399. leftBound = pick(axis.labelLeft, Math.min(axis.pos, spacing[3])),
  12400. rightBound = pick(
  12401. axis.labelRight,
  12402. Math.max(
  12403. !axis.isRadial ? axis.pos + axis.len : 0,
  12404. chartWidth - spacing[1]
  12405. )
  12406. ),
  12407. label = this.label,
  12408. rotation = this.rotation,
  12409. factor = { left: 0, center: 0.5, right: 1 }[
  12410. axis.labelAlign || label.attr('align')
  12411. ],
  12412. labelWidth = label.getBBox().width,
  12413. slotWidth = axis.getSlotWidth(tick),
  12414. modifiedSlotWidth = slotWidth,
  12415. xCorrection = factor,
  12416. goRight = 1,
  12417. leftPos,
  12418. rightPos,
  12419. textWidth,
  12420. css = {};
  12421. // Check if the label overshoots the chart spacing box. If it does, move
  12422. // it. If it now overshoots the slotWidth, add ellipsis.
  12423. if (!rotation && pick(labelOptions.overflow, 'justify') === 'justify') {
  12424. leftPos = pxPos - factor * labelWidth;
  12425. rightPos = pxPos + (1 - factor) * labelWidth;
  12426. if (leftPos < leftBound) {
  12427. modifiedSlotWidth =
  12428. xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
  12429. } else if (rightPos > rightBound) {
  12430. modifiedSlotWidth =
  12431. rightBound - xy.x + modifiedSlotWidth * factor;
  12432. goRight = -1;
  12433. }
  12434. modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
  12435. if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
  12436. xy.x += (
  12437. goRight *
  12438. (
  12439. slotWidth -
  12440. modifiedSlotWidth -
  12441. xCorrection * (
  12442. slotWidth - Math.min(labelWidth, modifiedSlotWidth)
  12443. )
  12444. )
  12445. );
  12446. }
  12447. // If the label width exceeds the available space, set a text width
  12448. // to be picked up below. Also, if a width has been set before, we
  12449. // need to set a new one because the reported labelWidth will be
  12450. // limited by the box (#3938).
  12451. if (
  12452. labelWidth > modifiedSlotWidth ||
  12453. (axis.autoRotation && (label.styles || {}).width)
  12454. ) {
  12455. textWidth = modifiedSlotWidth;
  12456. }
  12457. // Add ellipsis to prevent rotated labels to be clipped against the edge
  12458. // of the chart
  12459. } else if (rotation < 0 && pxPos - factor * labelWidth < leftBound) {
  12460. textWidth = Math.round(
  12461. pxPos / Math.cos(rotation * deg2rad) - leftBound
  12462. );
  12463. } else if (rotation > 0 && pxPos + factor * labelWidth > rightBound) {
  12464. textWidth = Math.round(
  12465. (chartWidth - pxPos) / Math.cos(rotation * deg2rad)
  12466. );
  12467. }
  12468. if (textWidth) {
  12469. if (tick.shortenLabel) {
  12470. tick.shortenLabel();
  12471. } else {
  12472. css.width = Math.floor(textWidth);
  12473. if (!(labelOptions.style || {}).textOverflow) {
  12474. css.textOverflow = 'ellipsis';
  12475. }
  12476. label.css(css);
  12477. }
  12478. }
  12479. },
  12480. /**
  12481. * Get the x and y position for ticks and labels
  12482. *
  12483. * @private
  12484. * @function Highcharts.Tick#getPosition
  12485. *
  12486. * @param {boolean} horiz
  12487. *
  12488. * @param {number} tickPos
  12489. *
  12490. * @param {number} tickmarkOffset
  12491. *
  12492. * @param {boolean} [old]
  12493. *
  12494. * @return {number}
  12495. *
  12496. * @fires Highcharts.Tick#event:afterGetPosition
  12497. */
  12498. getPosition: function (horiz, tickPos, tickmarkOffset, old) {
  12499. var axis = this.axis,
  12500. chart = axis.chart,
  12501. cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
  12502. pos;
  12503. pos = {
  12504. x: horiz ?
  12505. H.correctFloat(
  12506. axis.translate(tickPos + tickmarkOffset, null, null, old) +
  12507. axis.transB
  12508. ) :
  12509. (
  12510. axis.left +
  12511. axis.offset +
  12512. (
  12513. axis.opposite ?
  12514. (
  12515. (
  12516. (old && chart.oldChartWidth) ||
  12517. chart.chartWidth
  12518. ) -
  12519. axis.right -
  12520. axis.left
  12521. ) :
  12522. 0
  12523. )
  12524. ),
  12525. y: horiz ?
  12526. (
  12527. cHeight -
  12528. axis.bottom +
  12529. axis.offset -
  12530. (axis.opposite ? axis.height : 0)
  12531. ) :
  12532. H.correctFloat(
  12533. cHeight -
  12534. axis.translate(tickPos + tickmarkOffset, null, null, old) -
  12535. axis.transB
  12536. )
  12537. };
  12538. fireEvent(this, 'afterGetPosition', { pos: pos });
  12539. return pos;
  12540. },
  12541. /**
  12542. * Get the x, y position of the tick label
  12543. *
  12544. * @private
  12545. *
  12546. */
  12547. getLabelPosition: function (
  12548. x,
  12549. y,
  12550. label,
  12551. horiz,
  12552. labelOptions,
  12553. tickmarkOffset,
  12554. index,
  12555. step
  12556. ) {
  12557. var axis = this.axis,
  12558. transA = axis.transA,
  12559. reversed = axis.reversed,
  12560. staggerLines = axis.staggerLines,
  12561. rotCorr = axis.tickRotCorr || { x: 0, y: 0 },
  12562. yOffset = labelOptions.y,
  12563. // Adjust for label alignment if we use reserveSpace: true (#5286)
  12564. labelOffsetCorrection = (
  12565. !horiz && !axis.reserveSpaceDefault ?
  12566. -axis.labelOffset * (
  12567. axis.labelAlign === 'center' ? 0.5 : 1
  12568. ) :
  12569. 0
  12570. ),
  12571. line,
  12572. pos = {};
  12573. if (!defined(yOffset)) {
  12574. if (axis.side === 0) {
  12575. yOffset = label.rotation ? -8 : -label.getBBox().height;
  12576. } else if (axis.side === 2) {
  12577. yOffset = rotCorr.y + 8;
  12578. } else {
  12579. // #3140, #3140
  12580. yOffset = Math.cos(label.rotation * deg2rad) *
  12581. (rotCorr.y - label.getBBox(false, 0).height / 2);
  12582. }
  12583. }
  12584. x = x +
  12585. labelOptions.x +
  12586. labelOffsetCorrection +
  12587. rotCorr.x -
  12588. (
  12589. tickmarkOffset && horiz ?
  12590. tickmarkOffset * transA * (reversed ? -1 : 1) :
  12591. 0
  12592. );
  12593. y = y + yOffset - (tickmarkOffset && !horiz ?
  12594. tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
  12595. // Correct for staggered labels
  12596. if (staggerLines) {
  12597. line = (index / (step || 1) % staggerLines);
  12598. if (axis.opposite) {
  12599. line = staggerLines - line - 1;
  12600. }
  12601. y += line * (axis.labelOffset / staggerLines);
  12602. }
  12603. pos.x = x;
  12604. pos.y = Math.round(y);
  12605. fireEvent(
  12606. this,
  12607. 'afterGetLabelPosition',
  12608. { pos: pos, tickmarkOffset: tickmarkOffset, index: index }
  12609. );
  12610. return pos;
  12611. },
  12612. /**
  12613. * Extendible method to return the path of the marker
  12614. *
  12615. * @private
  12616. *
  12617. */
  12618. getMarkPath: function (x, y, tickLength, tickWidth, horiz, renderer) {
  12619. return renderer.crispLine([
  12620. 'M',
  12621. x,
  12622. y,
  12623. 'L',
  12624. x + (horiz ? 0 : -tickLength),
  12625. y + (horiz ? tickLength : 0)
  12626. ], tickWidth);
  12627. },
  12628. /**
  12629. * Renders the gridLine.
  12630. *
  12631. * @private
  12632. *
  12633. * @param {Boolean} old Whether or not the tick is old
  12634. * @param {number} opacity The opacity of the grid line
  12635. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  12636. * @return {undefined}
  12637. */
  12638. renderGridLine: function (old, opacity, reverseCrisp) {
  12639. var tick = this,
  12640. axis = tick.axis,
  12641. options = axis.options,
  12642. gridLine = tick.gridLine,
  12643. gridLinePath,
  12644. attribs = {},
  12645. pos = tick.pos,
  12646. type = tick.type,
  12647. tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset),
  12648. renderer = axis.chart.renderer,
  12649. gridPrefix = type ? type + 'Grid' : 'grid',
  12650. gridLineWidth = options[gridPrefix + 'LineWidth'],
  12651. gridLineColor = options[gridPrefix + 'LineColor'],
  12652. dashStyle = options[gridPrefix + 'LineDashStyle'];
  12653. if (!gridLine) {
  12654. if (!axis.chart.styledMode) {
  12655. attribs.stroke = gridLineColor;
  12656. attribs['stroke-width'] = gridLineWidth;
  12657. if (dashStyle) {
  12658. attribs.dashstyle = dashStyle;
  12659. }
  12660. }
  12661. if (!type) {
  12662. attribs.zIndex = 1;
  12663. }
  12664. if (old) {
  12665. opacity = 0;
  12666. }
  12667. tick.gridLine = gridLine = renderer.path()
  12668. .attr(attribs)
  12669. .addClass(
  12670. 'highcharts-' + (type ? type + '-' : '') + 'grid-line'
  12671. )
  12672. .add(axis.gridGroup);
  12673. }
  12674. if (gridLine) {
  12675. gridLinePath = axis.getPlotLinePath(
  12676. pos + tickmarkOffset,
  12677. gridLine.strokeWidth() * reverseCrisp,
  12678. old,
  12679. 'pass'
  12680. );
  12681. // If the parameter 'old' is set, the current call will be followed
  12682. // by another call, therefore do not do any animations this time
  12683. if (gridLinePath) {
  12684. gridLine[old || tick.isNew ? 'attr' : 'animate']({
  12685. d: gridLinePath,
  12686. opacity: opacity
  12687. });
  12688. }
  12689. }
  12690. },
  12691. /**
  12692. * Renders the tick mark.
  12693. *
  12694. * @private
  12695. *
  12696. * @param {Object} xy The position vector of the mark
  12697. * @param {number} xy.x The x position of the mark
  12698. * @param {number} xy.y The y position of the mark
  12699. * @param {number} opacity The opacity of the mark
  12700. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  12701. * @return {undefined}
  12702. */
  12703. renderMark: function (xy, opacity, reverseCrisp) {
  12704. var tick = this,
  12705. axis = tick.axis,
  12706. options = axis.options,
  12707. renderer = axis.chart.renderer,
  12708. type = tick.type,
  12709. tickPrefix = type ? type + 'Tick' : 'tick',
  12710. tickSize = axis.tickSize(tickPrefix),
  12711. mark = tick.mark,
  12712. isNewMark = !mark,
  12713. x = xy.x,
  12714. y = xy.y,
  12715. tickWidth = pick(
  12716. options[tickPrefix + 'Width'],
  12717. !type && axis.isXAxis ? 1 : 0
  12718. ), // X axis defaults to 1
  12719. tickColor = options[tickPrefix + 'Color'];
  12720. if (tickSize) {
  12721. // negate the length
  12722. if (axis.opposite) {
  12723. tickSize[0] = -tickSize[0];
  12724. }
  12725. // First time, create it
  12726. if (isNewMark) {
  12727. tick.mark = mark = renderer.path()
  12728. .addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
  12729. .add(axis.axisGroup);
  12730. if (!axis.chart.styledMode) {
  12731. mark.attr({
  12732. stroke: tickColor,
  12733. 'stroke-width': tickWidth
  12734. });
  12735. }
  12736. }
  12737. mark[isNewMark ? 'attr' : 'animate']({
  12738. d: tick.getMarkPath(
  12739. x,
  12740. y,
  12741. tickSize[0],
  12742. mark.strokeWidth() * reverseCrisp,
  12743. axis.horiz,
  12744. renderer
  12745. ),
  12746. opacity: opacity
  12747. });
  12748. }
  12749. },
  12750. /**
  12751. * Renders the tick label.
  12752. * Note: The label should already be created in init(), so it should only
  12753. * have to be moved into place.
  12754. *
  12755. * @private
  12756. *
  12757. * @param {Object} xy The position vector of the label
  12758. * @param {number} xy.x The x position of the label
  12759. * @param {number} xy.y The y position of the label
  12760. * @param {Boolean} old Whether or not the tick is old
  12761. * @param {number} opacity The opacity of the label
  12762. * @param {number} index The index of the tick
  12763. * @return {undefined}
  12764. */
  12765. renderLabel: function (xy, old, opacity, index) {
  12766. var tick = this,
  12767. axis = tick.axis,
  12768. horiz = axis.horiz,
  12769. options = axis.options,
  12770. label = tick.label,
  12771. labelOptions = options.labels,
  12772. step = labelOptions.step,
  12773. tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset),
  12774. show = true,
  12775. x = xy.x,
  12776. y = xy.y;
  12777. if (label && isNumber(x)) {
  12778. label.xy = xy = tick.getLabelPosition(
  12779. x,
  12780. y,
  12781. label,
  12782. horiz,
  12783. labelOptions,
  12784. tickmarkOffset,
  12785. index,
  12786. step
  12787. );
  12788. // Apply show first and show last. If the tick is both first and
  12789. // last, it is a single centered tick, in which case we show the
  12790. // label anyway (#2100).
  12791. if (
  12792. (
  12793. tick.isFirst &&
  12794. !tick.isLast &&
  12795. !pick(options.showFirstLabel, 1)
  12796. ) ||
  12797. (
  12798. tick.isLast &&
  12799. !tick.isFirst &&
  12800. !pick(options.showLastLabel, 1)
  12801. )
  12802. ) {
  12803. show = false;
  12804. // Handle label overflow and show or hide accordingly
  12805. } else if (
  12806. horiz &&
  12807. !labelOptions.step &&
  12808. !labelOptions.rotation &&
  12809. !old &&
  12810. opacity !== 0
  12811. ) {
  12812. tick.handleOverflow(xy);
  12813. }
  12814. // apply step
  12815. if (step && index % step) {
  12816. // show those indices dividable by step
  12817. show = false;
  12818. }
  12819. // Set the new position, and show or hide
  12820. if (show && isNumber(xy.y)) {
  12821. xy.opacity = opacity;
  12822. label[tick.isNewLabel ? 'attr' : 'animate'](xy);
  12823. tick.isNewLabel = false;
  12824. } else {
  12825. label.attr('y', -9999); // #1338
  12826. tick.isNewLabel = true;
  12827. }
  12828. }
  12829. },
  12830. /**
  12831. * Put everything in place
  12832. *
  12833. * @private
  12834. *
  12835. * @param index {Number}
  12836. * @param old {Boolean} Use old coordinates to prepare an animation into new
  12837. * position
  12838. */
  12839. render: function (index, old, opacity) {
  12840. var tick = this,
  12841. axis = tick.axis,
  12842. horiz = axis.horiz,
  12843. pos = tick.pos,
  12844. tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset),
  12845. xy = tick.getPosition(horiz, pos, tickmarkOffset, old),
  12846. x = xy.x,
  12847. y = xy.y,
  12848. reverseCrisp = ((horiz && x === axis.pos + axis.len) ||
  12849. (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
  12850. opacity = pick(opacity, 1);
  12851. this.isActive = true;
  12852. // Create the grid line
  12853. this.renderGridLine(old, opacity, reverseCrisp);
  12854. // create the tick mark
  12855. this.renderMark(xy, opacity, reverseCrisp);
  12856. // the label is created on init - now move it into place
  12857. this.renderLabel(xy, old, opacity, index);
  12858. tick.isNew = false;
  12859. H.fireEvent(this, 'afterRender');
  12860. },
  12861. /**
  12862. * Destructor for the tick prototype
  12863. *
  12864. * @private
  12865. * @function Highcharts.Tick#destroy
  12866. */
  12867. destroy: function () {
  12868. destroyObjectProperties(this, this.axis);
  12869. }
  12870. };
  12871. }(Highcharts));
  12872. var Axis = (function (H) {
  12873. /* *
  12874. * (c) 2010-2019 Torstein Honsi
  12875. *
  12876. * License: www.highcharts.com/license
  12877. */
  12878. /**
  12879. * @callback Highcharts.AxisEventCallbackFunction
  12880. *
  12881. * @param {Highcharts.Axis} this
  12882. */
  12883. /**
  12884. * Options for crosshairs on axes.
  12885. *
  12886. * @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
  12887. */
  12888. /**
  12889. * @interface Highcharts.AxisLabelsFormatterContextObject
  12890. *//**
  12891. * @name Highcharts.AxisLabelsFormatterContextObject#axis
  12892. * @type {Highcharts.Axis}
  12893. *//**
  12894. * @name Highcharts.AxisLabelsFormatterContextObject#chart
  12895. * @type {Highcharts.Chart}
  12896. *//**
  12897. * @name Highcharts.AxisLabelsFormatterContextObject#isFirst
  12898. * @type {boolean}
  12899. *//**
  12900. * @name Highcharts.AxisLabelsFormatterContextObject#isLast
  12901. * @type {boolean}
  12902. *//**
  12903. * @name Highcharts.AxisLabelsFormatterContextObject#value
  12904. * @type {number}
  12905. */
  12906. /**
  12907. * Options for axes.
  12908. *
  12909. * @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
  12910. */
  12911. /**
  12912. * The returned object literal from the {@link Highcharts.Axis#getExtremes}
  12913. * function.
  12914. *
  12915. * @interface Highcharts.ExtremesObject
  12916. *//**
  12917. * The maximum value of the axis' associated series.
  12918. * @name Highcharts.ExtremesObject#dataMax
  12919. * @type {number}
  12920. *//**
  12921. * The minimum value of the axis' associated series.
  12922. * @name Highcharts.ExtremesObject#dataMin
  12923. * @type {number}
  12924. *//**
  12925. * The maximum axis value, either automatic or set manually. If the `max` option
  12926. * is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
  12927. * the same as `dataMax`.
  12928. * @name Highcharts.ExtremesObject#max
  12929. * @type {number}
  12930. *//**
  12931. * The minimum axis value, either automatic or set manually. If the `min` option
  12932. * is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
  12933. * the same as `dataMin`.
  12934. * @name Highcharts.ExtremesObject#min
  12935. * @type {number}
  12936. *//**
  12937. * The user defined maximum, either from the `max` option or from a zoom or
  12938. * `setExtremes` action.
  12939. * @name Highcharts.ExtremesObject#userMax
  12940. * @type {number}
  12941. *//**
  12942. * The user defined minimum, either from the `min` option or from a zoom or
  12943. * `setExtremes` action.
  12944. * @name Highcharts.ExtremesObject#userMin
  12945. * @type {number}
  12946. */
  12947. /**
  12948. * @callback Highcharts.AxisPointBreakEventCallbackFunction
  12949. *
  12950. * @param {Highcharts.Axis} this
  12951. *
  12952. * @param {Highcharts.AxisPointBreakEventObject} event
  12953. */
  12954. /**
  12955. * @interface Highcharts.AxisPointBreakEventObject
  12956. *//**
  12957. * @name Highcharts.AxisPointBreakEventObject#brk
  12958. * @type {Highcharts.Dictionary<number>}
  12959. *//**
  12960. * @name Highcharts.AxisPointBreakEventObject#point
  12961. * @type {Highcharts.Point}
  12962. *//**
  12963. * @name Highcharts.AxisPointBreakEventObject#preventDefault
  12964. * @type {Function}
  12965. *//**
  12966. * @name Highcharts.AxisPointBreakEventObject#target
  12967. * @type {Highcharts.SVGElement}
  12968. *//**
  12969. * @name Highcharts.AxisPointBreakEventObject#type
  12970. * @type {"pointBreak"|"pointInBreak"}
  12971. */
  12972. /**
  12973. * @callback Highcharts.AxisSetExtremesEventCallbackFunction
  12974. *
  12975. * @param {Highcharts.Axis} this
  12976. *
  12977. * @param {Highcharts.AxisSetExtremesEventObject} event
  12978. */
  12979. /**
  12980. * @interface Highcharts.AxisSetExtremesEventObject
  12981. *//**
  12982. * @name Highcharts.AxisSetExtremesEventObject#dataMax
  12983. * @type {number}
  12984. *//**
  12985. * @name Highcharts.AxisSetExtremesEventObject#dataMin
  12986. * @type {number}
  12987. *//**
  12988. * @name Highcharts.AxisSetExtremesEventObject#max
  12989. * @type {number}
  12990. *//**
  12991. * @name Highcharts.AxisSetExtremesEventObject#min
  12992. * @type {number}
  12993. *//**
  12994. * @name Highcharts.AxisSetExtremesEventObject#preventDefault
  12995. * @type {Function}
  12996. *//**
  12997. * @name Highcharts.AxisSetExtremesEventObject#target
  12998. * @type {Highcharts.SVGElement}
  12999. *//**
  13000. * @name Highcharts.AxisSetExtremesEventObject#trigger
  13001. * @type {string}
  13002. *//**
  13003. * @name Highcharts.AxisSetExtremesEventObject#type
  13004. * @type {"setExtremes"}
  13005. *//**
  13006. * @name Highcharts.AxisSetExtremesEventObject#userMax
  13007. * @type {number}
  13008. *//**
  13009. * @name Highcharts.AxisSetExtremesEventObject#userMin
  13010. * @type {number}
  13011. */
  13012. /**
  13013. * @callback Highcharts.AxisTickPositionerCallbackFunction
  13014. *
  13015. * @param {Highcharts.Axis} this
  13016. *
  13017. * @return {Array<number>}
  13018. */
  13019. var addEvent = H.addEvent,
  13020. animObject = H.animObject,
  13021. arrayMax = H.arrayMax,
  13022. arrayMin = H.arrayMin,
  13023. color = H.color,
  13024. correctFloat = H.correctFloat,
  13025. defaultOptions = H.defaultOptions,
  13026. defined = H.defined,
  13027. deg2rad = H.deg2rad,
  13028. destroyObjectProperties = H.destroyObjectProperties,
  13029. extend = H.extend,
  13030. fireEvent = H.fireEvent,
  13031. format = H.format,
  13032. getMagnitude = H.getMagnitude,
  13033. isArray = H.isArray,
  13034. isNumber = H.isNumber,
  13035. isString = H.isString,
  13036. merge = H.merge,
  13037. normalizeTickInterval = H.normalizeTickInterval,
  13038. objectEach = H.objectEach,
  13039. pick = H.pick,
  13040. removeEvent = H.removeEvent,
  13041. splat = H.splat,
  13042. syncTimeout = H.syncTimeout,
  13043. Tick = H.Tick;
  13044. /**
  13045. * Create a new axis object. Called internally when instanciating a new chart or
  13046. * adding axes by {@link Highcharts.Chart#addAxis}.
  13047. *
  13048. * A chart can have from 0 axes (pie chart) to multiples. In a normal, single
  13049. * series cartesian chart, there is one X axis and one Y axis.
  13050. *
  13051. * The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
  13052. * an array of Axis objects. If there is only one axis, it can be referenced
  13053. * through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
  13054. * pattern goes for Y axes.
  13055. *
  13056. * If you need to get the axes from a series object, use the `series.xAxis` and
  13057. * `series.yAxis` properties. These are not arrays, as one series can only be
  13058. * associated to one X and one Y axis.
  13059. *
  13060. * A third way to reference the axis programmatically is by `id`. Add an `id` in
  13061. * the axis configuration options, and get the axis by
  13062. * {@link Highcharts.Chart#get}.
  13063. *
  13064. * Configuration options for the axes are given in options.xAxis and
  13065. * options.yAxis.
  13066. *
  13067. * @class
  13068. * @name Highcharts.Axis
  13069. *
  13070. * @param {Highcharts.Chart} chart
  13071. * The Chart instance to apply the axis on.
  13072. *
  13073. * @param {Highcharts.AxisOptions} options
  13074. * Axis options.
  13075. */
  13076. var Axis = function () {
  13077. this.init.apply(this, arguments);
  13078. };
  13079. H.extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */{
  13080. /**
  13081. * The X axis or category axis. Normally this is the horizontal axis,
  13082. * though if the chart is inverted this is the vertical axis. In case of
  13083. * multiple axes, the xAxis node is an array of configuration objects.
  13084. *
  13085. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  13086. * access to the axis.
  13087. *
  13088. * @productdesc {highmaps}
  13089. * In Highmaps, the axis is hidden, but it is used behind the scenes to
  13090. * control features like zooming and panning. Zooming is in effect the same
  13091. * as setting the extremes of one of the exes.
  13092. *
  13093. * @type {*|Array<*>}
  13094. * @optionparent xAxis
  13095. */
  13096. defaultOptions: {
  13097. /**
  13098. * When using multiple axis, the ticks of two or more opposite axes
  13099. * will automatically be aligned by adding ticks to the axis or axes
  13100. * with the least ticks, as if `tickAmount` were specified.
  13101. *
  13102. * This can be prevented by setting `alignTicks` to false. If the grid
  13103. * lines look messy, it's a good idea to hide them for the secondary
  13104. * axis by setting `gridLineWidth` to 0.
  13105. *
  13106. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  13107. * then the `alignTicks ` will be disabled for the Axis.
  13108. *
  13109. * Disabled for logarithmic axes.
  13110. *
  13111. * @type {boolean}
  13112. * @default true
  13113. * @product highcharts highstock gantt
  13114. * @apioption xAxis.alignTicks
  13115. */
  13116. /**
  13117. * Whether to allow decimals in this axis' ticks. When counting
  13118. * integers, like persons or hits on a web page, decimals should
  13119. * be avoided in the labels.
  13120. *
  13121. * @see [minTickInterval](#xAxis.minTickInterval)
  13122. *
  13123. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
  13124. * True by default
  13125. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
  13126. * False
  13127. *
  13128. * @type {boolean}
  13129. * @default true
  13130. * @since 2.0
  13131. * @apioption xAxis.allowDecimals
  13132. */
  13133. /**
  13134. * When using an alternate grid color, a band is painted across the
  13135. * plot area between every other grid line.
  13136. *
  13137. * @sample {highcharts} highcharts/yaxis/alternategridcolor/
  13138. * Alternate grid color on the Y axis
  13139. * @sample {highstock} stock/xaxis/alternategridcolor/
  13140. * Alternate grid color on the Y axis
  13141. *
  13142. * @type {Highcharts.ColorString}
  13143. * @apioption xAxis.alternateGridColor
  13144. */
  13145. /**
  13146. * An array defining breaks in the axis, the sections defined will be
  13147. * left out and all the points shifted closer to each other.
  13148. *
  13149. * @productdesc {highcharts}
  13150. * Requires that the broken-axis.js module is loaded.
  13151. *
  13152. * @sample {highcharts} highcharts/axisbreak/break-simple/
  13153. * Simple break
  13154. * @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
  13155. * Advanced with callback
  13156. * @sample {highstock} stock/demo/intraday-breaks/
  13157. * Break on nights and weekends
  13158. *
  13159. * @type {Array<*>}
  13160. * @since 4.1.0
  13161. * @product highcharts highstock gantt
  13162. * @apioption xAxis.breaks
  13163. */
  13164. /**
  13165. * A number indicating how much space should be left between the start
  13166. * and the end of the break. The break size is given in axis units,
  13167. * so for instance on a `datetime` axis, a break size of 3600000 would
  13168. * indicate the equivalent of an hour.
  13169. *
  13170. * @type {number}
  13171. * @default 0
  13172. * @since 4.1.0
  13173. * @product highcharts highstock gantt
  13174. * @apioption xAxis.breaks.breakSize
  13175. */
  13176. /**
  13177. * The point where the break starts.
  13178. *
  13179. * @type {number}
  13180. * @since 4.1.0
  13181. * @product highcharts highstock gantt
  13182. * @apioption xAxis.breaks.from
  13183. */
  13184. /**
  13185. * Defines an interval after which the break appears again. By default
  13186. * the breaks do not repeat.
  13187. *
  13188. * @type {number}
  13189. * @default 0
  13190. * @since 4.1.0
  13191. * @product highcharts highstock gantt
  13192. * @apioption xAxis.breaks.repeat
  13193. */
  13194. /**
  13195. * The point where the break ends.
  13196. *
  13197. * @type {number}
  13198. * @since 4.1.0
  13199. * @product highcharts highstock gantt
  13200. * @apioption xAxis.breaks.to
  13201. */
  13202. /**
  13203. * If categories are present for the xAxis, names are used instead of
  13204. * numbers for that axis. Since Highcharts 3.0, categories can also
  13205. * be extracted by giving each point a [name](#series.data) and setting
  13206. * axis [type](#xAxis.type) to `category`. However, if you have multiple
  13207. * series, best practice remains defining the `categories` array.
  13208. *
  13209. * Example:
  13210. *
  13211. * <pre>categories: ['Apples', 'Bananas', 'Oranges']</pre>
  13212. *
  13213. * @sample {highcharts} highcharts/demo/line-labels/
  13214. * With
  13215. * @sample {highcharts} highcharts/xaxis/categories/
  13216. * Without
  13217. *
  13218. * @type {Array<string>}
  13219. * @product highcharts gantt
  13220. * @apioption xAxis.categories
  13221. */
  13222. /**
  13223. * The highest allowed value for automatically computed axis extremes.
  13224. *
  13225. * @see [floor](#xAxis.floor)
  13226. *
  13227. * @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
  13228. * Floor and ceiling
  13229. *
  13230. * @type {number}
  13231. * @since 4.0
  13232. * @product highcharts highstock gantt
  13233. * @apioption xAxis.ceiling
  13234. */
  13235. /**
  13236. * A class name that opens for styling the axis by CSS, especially in
  13237. * Highcharts styled mode. The class name is applied to group elements
  13238. * for the grid, axis elements and labels.
  13239. *
  13240. * @sample {highcharts|highstock|highmaps} highcharts/css/axis/
  13241. * Multiple axes with separate styling
  13242. *
  13243. * @type {string}
  13244. * @since 5.0.0
  13245. * @apioption xAxis.className
  13246. */
  13247. /**
  13248. * Configure a crosshair that follows either the mouse pointer or the
  13249. * hovered point.
  13250. *
  13251. * In styled mode, the crosshairs are styled in the
  13252. * `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
  13253. * `.highcharts-xaxis-category` classes.
  13254. *
  13255. * @productdesc {highstock}
  13256. * In Highstock, by default, the crosshair is enabled on the X axis and
  13257. * disabled on the Y axis.
  13258. *
  13259. * @sample {highcharts} highcharts/xaxis/crosshair-both/
  13260. * Crosshair on both axes
  13261. * @sample {highstock} stock/xaxis/crosshairs-xy/
  13262. * Crosshair on both axes
  13263. * @sample {highmaps} highcharts/xaxis/crosshair-both/
  13264. * Crosshair on both axes
  13265. *
  13266. * @type {boolean|*}
  13267. * @default false
  13268. * @since 4.1
  13269. * @apioption xAxis.crosshair
  13270. */
  13271. /**
  13272. * A class name for the crosshair, especially as a hook for styling.
  13273. *
  13274. * @type {string}
  13275. * @since 5.0.0
  13276. * @apioption xAxis.crosshair.className
  13277. */
  13278. /**
  13279. * The color of the crosshair. Defaults to `#cccccc` for numeric and
  13280. * datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
  13281. * the crosshair by default highlights the whole category.
  13282. *
  13283. * @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
  13284. * Customized crosshairs
  13285. *
  13286. * @type {Highcharts.ColorString}
  13287. * @default #cccccc
  13288. * @since 4.1
  13289. * @apioption xAxis.crosshair.color
  13290. */
  13291. /**
  13292. * The dash style for the crosshair. See
  13293. * [series.dashStyle](#plotOptions.series.dashStyle)
  13294. * for possible values.
  13295. *
  13296. * @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
  13297. * Dotted crosshair
  13298. * @sample {highstock} stock/xaxis/crosshair-dashed/
  13299. * Dashed X axis crosshair
  13300. *
  13301. * @type {Highcharts.DashStyleType}
  13302. * @default Solid
  13303. * @since 4.1
  13304. * @apioption xAxis.crosshair.dashStyle
  13305. */
  13306. /**
  13307. * A label on the axis next to the crosshair.
  13308. *
  13309. * In styled mode, the label is styled with the
  13310. * `.highcharts-crosshair-label` class.
  13311. *
  13312. * @sample {highstock} stock/xaxis/crosshair-label/
  13313. * Crosshair labels
  13314. * @sample {highstock} highcharts/css/crosshair-label/
  13315. * Style mode
  13316. *
  13317. * @since 2.1
  13318. * @product highstock
  13319. * @apioption xAxis.crosshair.label
  13320. */
  13321. /**
  13322. * Alignment of the label compared to the axis. Defaults to `left` for
  13323. * right-side axes, `right` for left-side axes and `center` for
  13324. * horizontal axes.
  13325. *
  13326. * @type {string}
  13327. * @since 2.1
  13328. * @product highstock
  13329. * @apioption xAxis.crosshair.label.align
  13330. */
  13331. /**
  13332. * The background color for the label. Defaults to the related series
  13333. * color, or `#666666` if that is not available.
  13334. *
  13335. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13336. * @since 2.1
  13337. * @product highstock
  13338. * @apioption xAxis.crosshair.label.backgroundColor
  13339. */
  13340. /**
  13341. * The border color for the crosshair label
  13342. *
  13343. * @type {Highcharts.ColorString}
  13344. * @since 2.1
  13345. * @product highstock
  13346. * @apioption xAxis.crosshair.label.borderColor
  13347. */
  13348. /**
  13349. * The border corner radius of the crosshair label.
  13350. *
  13351. * @type {number}
  13352. * @default 3
  13353. * @since 2.1.10
  13354. * @product highstock
  13355. * @apioption xAxis.crosshair.label.borderRadius
  13356. */
  13357. /**
  13358. * The border width for the crosshair label.
  13359. *
  13360. * @type {number}
  13361. * @default 0
  13362. * @since 2.1
  13363. * @product highstock
  13364. * @apioption xAxis.crosshair.label.borderWidth
  13365. */
  13366. /**
  13367. * A format string for the crosshair label. Defaults to `{value}` for
  13368. * numeric axes and `{value:%b %d, %Y}` for datetime axes.
  13369. *
  13370. * @type {string}
  13371. * @since 2.1
  13372. * @product highstock
  13373. * @apioption xAxis.crosshair.label.format
  13374. */
  13375. /**
  13376. * Formatter function for the label text.
  13377. *
  13378. * @type {Highcharts.FormatterCallbackFunction<object>}
  13379. * @since 2.1
  13380. * @product highstock
  13381. * @apioption xAxis.crosshair.label.formatter
  13382. */
  13383. /**
  13384. * Padding inside the crosshair label.
  13385. *
  13386. * @type {number}
  13387. * @default 8
  13388. * @since 2.1
  13389. * @product highstock
  13390. * @apioption xAxis.crosshair.label.padding
  13391. */
  13392. /**
  13393. * The shape to use for the label box.
  13394. *
  13395. * @type {string}
  13396. * @default callout
  13397. * @since 2.1
  13398. * @product highstock
  13399. * @apioption xAxis.crosshair.label.shape
  13400. */
  13401. /**
  13402. * Text styles for the crosshair label.
  13403. *
  13404. * @type {Highcharts.CSSObject}
  13405. * @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
  13406. * @since 2.1
  13407. * @product highstock
  13408. * @apioption xAxis.crosshair.label.style
  13409. */
  13410. /**
  13411. * Whether the crosshair should snap to the point or follow the pointer
  13412. * independent of points.
  13413. *
  13414. * @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
  13415. * True by default
  13416. * @sample {highmaps} maps/demo/latlon-advanced/
  13417. * Snap is false
  13418. *
  13419. * @type {boolean}
  13420. * @default true
  13421. * @since 4.1
  13422. * @apioption xAxis.crosshair.snap
  13423. */
  13424. /**
  13425. * The pixel width of the crosshair. Defaults to 1 for numeric or
  13426. * datetime axes, and for one category width for category axes.
  13427. *
  13428. * @sample {highcharts} highcharts/xaxis/crosshair-customized/
  13429. * Customized crosshairs
  13430. * @sample {highstock} highcharts/xaxis/crosshair-customized/
  13431. * Customized crosshairs
  13432. * @sample {highmaps} highcharts/xaxis/crosshair-customized/
  13433. * Customized crosshairs
  13434. *
  13435. * @type {number}
  13436. * @default 1
  13437. * @since 4.1
  13438. * @apioption xAxis.crosshair.width
  13439. */
  13440. /**
  13441. * The Z index of the crosshair. Higher Z indices allow drawing the
  13442. * crosshair on top of the series or behind the grid lines.
  13443. *
  13444. * @type {number}
  13445. * @default 2
  13446. * @since 4.1
  13447. * @apioption xAxis.crosshair.zIndex
  13448. */
  13449. /**
  13450. * For a datetime axis, the scale will automatically adjust to the
  13451. * appropriate unit. This member gives the default string
  13452. * representations used for each unit. For intermediate values,
  13453. * different units may be used, for example the `day` unit can be used
  13454. * on midnight and `hour` unit be used for intermediate values on the
  13455. * same axis. For an overview of the replacement codes, see
  13456. * [dateFormat](/class-reference/Highcharts#dateFormat). Defaults to:
  13457. *
  13458. * <pre>{
  13459. * millisecond: '%H:%M:%S.%L',
  13460. * second: '%H:%M:%S',
  13461. * minute: '%H:%M',
  13462. * hour: '%H:%M',
  13463. * day: '%e. %b',
  13464. * week: '%e. %b',
  13465. * month: '%b \'%y',
  13466. * year: '%Y'
  13467. * }</pre>
  13468. *
  13469. * @sample {highcharts} highcharts/xaxis/datetimelabelformats/
  13470. * Different day format on X axis
  13471. * @sample {highstock} stock/xaxis/datetimelabelformats/
  13472. * More information in x axis labels
  13473. *
  13474. * @product highcharts highstock gantt
  13475. */
  13476. dateTimeLabelFormats: {
  13477. millisecond: {
  13478. main: '%H:%M:%S.%L',
  13479. range: false
  13480. },
  13481. second: {
  13482. main: '%H:%M:%S',
  13483. range: false
  13484. },
  13485. minute: {
  13486. main: '%H:%M',
  13487. range: false
  13488. },
  13489. hour: {
  13490. main: '%H:%M',
  13491. range: false
  13492. },
  13493. day: {
  13494. main: '%e. %b'
  13495. },
  13496. week: {
  13497. main: '%e. %b'
  13498. },
  13499. month: {
  13500. main: '%b \'%y'
  13501. },
  13502. year: {
  13503. main: '%Y'
  13504. }
  13505. },
  13506. /**
  13507. * _Requires Accessibility module_
  13508. *
  13509. * Description of the axis to screen reader users.
  13510. *
  13511. * @type {string}
  13512. * @since 5.0.0
  13513. * @apioption xAxis.description
  13514. */
  13515. /**
  13516. * Whether to force the axis to end on a tick. Use this option with
  13517. * the `maxPadding` option to control the axis end.
  13518. *
  13519. * @productdesc {highstock}
  13520. * In Highstock, `endOnTick` is always false when the navigator is
  13521. * enabled, to prevent jumpy scrolling.
  13522. *
  13523. * @sample {highcharts} highcharts/chart/reflow-true/
  13524. * True by default
  13525. * @sample {highcharts} highcharts/yaxis/endontick/
  13526. * False
  13527. * @sample {highstock} stock/demo/basic-line/
  13528. * True by default
  13529. * @sample {highstock} stock/xaxis/endontick/
  13530. * False
  13531. *
  13532. * @since 1.2.0
  13533. */
  13534. endOnTick: false,
  13535. /**
  13536. * Event handlers for the axis.
  13537. *
  13538. * @type {*}
  13539. * @apioption xAxis.events
  13540. */
  13541. /**
  13542. * An event fired after the breaks have rendered.
  13543. *
  13544. * @see [breaks](#xAxis.breaks)
  13545. *
  13546. * @sample {highcharts} highcharts/axisbreak/break-event/
  13547. * AfterBreak Event
  13548. *
  13549. * @type {Highcharts.AxisEventCallbackFunction}
  13550. * @since 4.1.0
  13551. * @product highcharts gantt
  13552. * @apioption xAxis.events.afterBreaks
  13553. */
  13554. /**
  13555. * As opposed to the `setExtremes` event, this event fires after the
  13556. * final min and max values are computed and corrected for `minRange`.
  13557. *
  13558. * Fires when the minimum and maximum is set for the axis, either by
  13559. * calling the `.setExtremes()` method or by selecting an area in the
  13560. * chart. One parameter, `event`, is passed to the function, containing
  13561. * common event information.
  13562. *
  13563. * The new user set minimum and maximum values can be found by
  13564. * `event.min` and `event.max`. These reflect the axis minimum and
  13565. * maximum in axis values. The actual data extremes are found in
  13566. * `event.dataMin` and `event.dataMax`.
  13567. *
  13568. * @type {Highcharts.AxisEventCallbackFunction}
  13569. * @since 2.3
  13570. * @context Axis
  13571. * @apioption xAxis.events.afterSetExtremes
  13572. */
  13573. /**
  13574. * An event fired when a break from this axis occurs on a point.
  13575. *
  13576. * @see [breaks](#xAxis.breaks)
  13577. *
  13578. * @sample {highcharts} highcharts/axisbreak/break-visualized/
  13579. * Visualization of a Break
  13580. *
  13581. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  13582. * @since 4.1.0
  13583. * @product highcharts gantt
  13584. * @context Axis
  13585. * @apioption xAxis.events.pointBreak
  13586. */
  13587. /**
  13588. * An event fired when a point falls inside a break from this axis.
  13589. *
  13590. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  13591. * @product highcharts highstock gantt
  13592. * @context Axis
  13593. * @apioption xAxis.events.pointInBreak
  13594. */
  13595. /**
  13596. * Fires when the minimum and maximum is set for the axis, either by
  13597. * calling the `.setExtremes()` method or by selecting an area in the
  13598. * chart. One parameter, `event`, is passed to the function,
  13599. * containing common event information.
  13600. *
  13601. * The new user set minimum and maximum values can be found by
  13602. * `event.min` and `event.max`. These reflect the axis minimum and
  13603. * maximum in data values. When an axis is zoomed all the way out from
  13604. * the "Reset zoom" button, `event.min` and `event.max` are null, and
  13605. * the new extremes are set based on `this.dataMin` and `this.dataMax`.
  13606. *
  13607. * @sample {highstock} stock/xaxis/events-setextremes/
  13608. * Log new extremes on x axis
  13609. *
  13610. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  13611. * @since 1.2.0
  13612. * @context Axis
  13613. * @apioption xAxis.events.setExtremes
  13614. */
  13615. /**
  13616. * The lowest allowed value for automatically computed axis extremes.
  13617. *
  13618. * @see [ceiling](#yAxis.ceiling)
  13619. *
  13620. * @sample {highcharts} highcharts/yaxis/floor-ceiling/
  13621. * Floor and ceiling
  13622. * @sample {highstock} stock/demo/lazy-loading/
  13623. * Prevent negative stock price on Y axis
  13624. *
  13625. * @type {number}
  13626. * @since 4.0
  13627. * @product highcharts highstock gantt
  13628. * @apioption xAxis.floor
  13629. */
  13630. /**
  13631. * The dash or dot style of the grid lines. For possible values, see
  13632. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  13633. *
  13634. * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
  13635. * Long dashes
  13636. * @sample {highstock} stock/xaxis/gridlinedashstyle/
  13637. * Long dashes
  13638. *
  13639. * @type {Highcharts.DashStyleType}
  13640. * @default Solid
  13641. * @since 1.2
  13642. * @apioption xAxis.gridLineDashStyle
  13643. */
  13644. /**
  13645. * The Z index of the grid lines.
  13646. *
  13647. * @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
  13648. * A Z index of 4 renders the grid above the graph
  13649. *
  13650. * @type {number}
  13651. * @default 1
  13652. * @product highcharts highstock gantt
  13653. * @apioption xAxis.gridZIndex
  13654. */
  13655. /**
  13656. * An id for the axis. This can be used after render time to get
  13657. * a pointer to the axis object through `chart.get()`.
  13658. *
  13659. * @sample {highcharts} highcharts/xaxis/id/
  13660. * Get the object
  13661. * @sample {highstock} stock/xaxis/id/
  13662. * Get the object
  13663. *
  13664. * @type {string}
  13665. * @since 1.2.0
  13666. * @apioption xAxis.id
  13667. */
  13668. /**
  13669. * The axis labels show the number or category for each tick.
  13670. *
  13671. * @productdesc {highmaps}
  13672. * X and Y axis labels are by default disabled in Highmaps, but the
  13673. * functionality is inherited from Highcharts and used on `colorAxis`,
  13674. * and can be enabled on X and Y axes too.
  13675. */
  13676. labels: {
  13677. /**
  13678. * What part of the string the given position is anchored to.
  13679. * If `left`, the left side of the string is at the axis position.
  13680. * Can be one of `"left"`, `"center"` or `"right"`. Defaults to
  13681. * an intelligent guess based on which side of the chart the axis
  13682. * is on and the rotation of the label.
  13683. *
  13684. * @see [reserveSpace](#xAxis.labels.reserveSpace)
  13685. *
  13686. * @sample {highcharts} highcharts/xaxis/labels-align-left/
  13687. * Left
  13688. * @sample {highcharts} highcharts/xaxis/labels-align-right/
  13689. * Right
  13690. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  13691. * Left-aligned labels on a vertical category axis
  13692. *
  13693. * @type {string}
  13694. * @validvalue ["left", "center", "right"]
  13695. * @apioption xAxis.labels.align
  13696. */
  13697. /**
  13698. * For horizontal axes, the allowed degrees of label rotation
  13699. * to prevent overlapping labels. If there is enough space,
  13700. * labels are not rotated. As the chart gets narrower, it
  13701. * will start rotating the labels -45 degrees, then remove
  13702. * every second label and try again with rotations 0 and -45 etc.
  13703. * Set it to `false` to disable rotation, which will
  13704. * cause the labels to word-wrap if possible.
  13705. *
  13706. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
  13707. * Default auto rotation of 0 or -45
  13708. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
  13709. * Custom graded auto rotation
  13710. *
  13711. * @type {Array<number>}
  13712. * @default [-45]
  13713. * @since 4.1.0
  13714. * @product highcharts highstock gantt
  13715. * @apioption xAxis.labels.autoRotation
  13716. */
  13717. /**
  13718. * When each category width is more than this many pixels, we don't
  13719. * apply auto rotation. Instead, we lay out the axis label with word
  13720. * wrap. A lower limit makes sense when the label contains multiple
  13721. * short words that don't extend the available horizontal space for
  13722. * each label.
  13723. *
  13724. * @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
  13725. * Lower limit
  13726. *
  13727. * @type {number}
  13728. * @default 80
  13729. * @since 4.1.5
  13730. * @product highcharts gantt
  13731. * @apioption xAxis.labels.autoRotationLimit
  13732. */
  13733. /**
  13734. * Polar charts only. The label's pixel distance from the perimeter
  13735. * of the plot area.
  13736. *
  13737. * @type {number}
  13738. * @default 15
  13739. * @product highcharts gantt
  13740. * @apioption xAxis.labels.distance
  13741. */
  13742. /**
  13743. * Enable or disable the axis labels.
  13744. *
  13745. * @sample {highcharts} highcharts/xaxis/labels-enabled/
  13746. * X axis labels disabled
  13747. * @sample {highstock} stock/xaxis/labels-enabled/
  13748. * X axis labels disabled
  13749. *
  13750. * @default {highcharts|highstock|gantt} true
  13751. * @default {highmaps} false
  13752. */
  13753. enabled: true,
  13754. /**
  13755. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  13756. * for the axis label.
  13757. *
  13758. * @sample {highcharts|highstock} highcharts/yaxis/labels-format/
  13759. * Add units to Y axis label
  13760. *
  13761. * @type {string}
  13762. * @default {value}
  13763. * @since 3.0
  13764. * @apioption xAxis.labels.format
  13765. */
  13766. /**
  13767. * Callback JavaScript function to format the label. The value
  13768. * is given by `this.value`. Additional properties for `this` are
  13769. * `axis`, `chart`, `isFirst` and `isLast`. The value of the default
  13770. * label formatter can be retrieved by calling
  13771. * `this.axis.defaultLabelFormatter.call(this)` within the function.
  13772. *
  13773. * Defaults to:
  13774. *
  13775. * <pre>function() {
  13776. * return this.value;
  13777. * }</pre>
  13778. *
  13779. * @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
  13780. * Linked category names
  13781. * @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
  13782. * Modified numeric labels
  13783. * @sample {highstock} stock/xaxis/labels-formatter/
  13784. * Added units on Y axis
  13785. *
  13786. * @type {Highcharts.FormatterCallbackFunction<Highcharts.AxisLabelsFormatterContextObject>}
  13787. * @apioption xAxis.labels.formatter
  13788. */
  13789. /**
  13790. * The number of pixels to indent the labels per level in a treegrid
  13791. * axis.
  13792. *
  13793. * @sample gantt/treegrid-axis/demo
  13794. * Indentation 10px by default.
  13795. * @sample gantt/treegrid-axis/indentation-0px
  13796. * Indentation set to 0px.
  13797. *
  13798. * @product gantt
  13799. */
  13800. indentation: 10,
  13801. /**
  13802. * Horizontal axis only. When `staggerLines` is not set,
  13803. * `maxStaggerLines` defines how many lines the axis is allowed to
  13804. * add to automatically avoid overlapping X labels. Set to `1` to
  13805. * disable overlap detection.
  13806. *
  13807. * @deprecated
  13808. * @type {number}
  13809. * @default 5
  13810. * @since 1.3.3
  13811. * @apioption xAxis.labels.maxStaggerLines
  13812. */
  13813. /**
  13814. * How to handle overflowing labels on horizontal axis. If set to
  13815. * `"allow"`, it will not be aligned at all. By default it
  13816. * `"justify"` labels inside the chart area. If there is room to
  13817. * move it, it will be aligned to the edge, else it will be removed.
  13818. *
  13819. * @type {boolean|string}
  13820. * @default justify
  13821. * @since 2.2.5
  13822. * @validvalue ["allow", "justify"]
  13823. * @apioption xAxis.labels.overflow
  13824. */
  13825. /**
  13826. * The pixel padding for axis labels, to ensure white space between
  13827. * them.
  13828. *
  13829. * @type {number}
  13830. * @default 5
  13831. * @product highcharts gantt
  13832. * @apioption xAxis.labels.padding
  13833. */
  13834. /**
  13835. * Whether to reserve space for the labels. By default, space is
  13836. * reserved for the labels in these cases:
  13837. *
  13838. * * On all horizontal axes.
  13839. * * On vertical axes if `label.align` is `right` on a left-side
  13840. * axis or `left` on a right-side axis.
  13841. * * On vertical axes if `label.align` is `center`.
  13842. *
  13843. * This can be turned off when for example the labels are rendered
  13844. * inside the plot area instead of outside.
  13845. *
  13846. * @see [labels.align](#xAxis.labels.align)
  13847. *
  13848. * @sample {highcharts} highcharts/xaxis/labels-reservespace/
  13849. * No reserved space, labels inside plot
  13850. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  13851. * Left-aligned labels on a vertical category axis
  13852. *
  13853. * @type {boolean}
  13854. * @since 4.1.10
  13855. * @product highcharts gantt
  13856. * @apioption xAxis.labels.reserveSpace
  13857. */
  13858. /**
  13859. * Rotation of the labels in degrees.
  13860. *
  13861. * @sample {highcharts} highcharts/xaxis/labels-rotation/
  13862. * X axis labels rotated 90°
  13863. *
  13864. * @type {number}
  13865. * @default 0
  13866. * @apioption xAxis.labels.rotation
  13867. */
  13868. /**
  13869. * Horizontal axes only. The number of lines to spread the labels
  13870. * over to make room or tighter labels.
  13871. *
  13872. * @sample {highcharts} highcharts/xaxis/labels-staggerlines/
  13873. * Show labels over two lines
  13874. * @sample {highstock} stock/xaxis/labels-staggerlines/
  13875. * Show labels over two lines
  13876. *
  13877. * @type {number}
  13878. * @since 2.1
  13879. * @apioption xAxis.labels.staggerLines
  13880. */
  13881. /**
  13882. * To show only every _n_'th label on the axis, set the step to _n_.
  13883. * Setting the step to 2 shows every other label.
  13884. *
  13885. * By default, the step is calculated automatically to avoid
  13886. * overlap. To prevent this, set it to 1\. This usually only
  13887. * happens on a category axis, and is often a sign that you have
  13888. * chosen the wrong axis type.
  13889. *
  13890. * Read more at
  13891. * [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
  13892. * => What axis should I use?
  13893. *
  13894. * @sample {highcharts} highcharts/xaxis/labels-step/
  13895. * Showing only every other axis label on a categorized
  13896. * x-axis
  13897. * @sample {highcharts} highcharts/xaxis/labels-step-auto/
  13898. * Auto steps on a category axis
  13899. *
  13900. * @type {number}
  13901. * @since 2.1
  13902. * @apioption xAxis.labels.step
  13903. */
  13904. /**
  13905. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  13906. * to render the labels.
  13907. *
  13908. * @type {boolean}
  13909. * @default false
  13910. * @apioption xAxis.labels.useHTML
  13911. */
  13912. /**
  13913. * The x position offset of the label relative to the tick position
  13914. * on the axis.
  13915. *
  13916. * @sample {highcharts} highcharts/xaxis/labels-x/
  13917. * Y axis labels placed on grid lines
  13918. */
  13919. x: 0,
  13920. /**
  13921. * The y position offset of the label relative to the tick position
  13922. * on the axis. The default makes it adapt to the font size on
  13923. * bottom axis.
  13924. *
  13925. * @sample {highcharts} highcharts/xaxis/labels-x/
  13926. * Y axis labels placed on grid lines
  13927. *
  13928. * @type {number}
  13929. * @apioption xAxis.labels.y
  13930. */
  13931. /**
  13932. * The Z index for the axis labels.
  13933. *
  13934. * @type {number}
  13935. * @default 7
  13936. * @apioption xAxis.labels.zIndex
  13937. */
  13938. /**
  13939. * CSS styles for the label. Use `whiteSpace: 'nowrap'` to prevent
  13940. * wrapping of category labels. Use `textOverflow: 'none'` to
  13941. * prevent ellipsis (dots).
  13942. *
  13943. * In styled mode, the labels are styled with the
  13944. * `.highcharts-axis-labels` class.
  13945. *
  13946. * @sample {highcharts} highcharts/xaxis/labels-style/
  13947. * Red X axis labels
  13948. *
  13949. * @type {Highcharts.CSSObject}
  13950. * @default {"color": "#666666", "cursor": "default", "fontSize": "11px"}
  13951. */
  13952. style: {
  13953. /** @ignore-option */
  13954. color: '#666666',
  13955. /** @ignore-option */
  13956. cursor: 'default',
  13957. /** @ignore-option */
  13958. fontSize: '11px'
  13959. }
  13960. },
  13961. /**
  13962. * Index of another axis that this axis is linked to. When an axis is
  13963. * linked to a master axis, it will take the same extremes as
  13964. * the master, but as assigned by min or max or by setExtremes.
  13965. * It can be used to show additional info, or to ease reading the
  13966. * chart by duplicating the scales.
  13967. *
  13968. * @sample {highcharts} highcharts/xaxis/linkedto/
  13969. * Different string formats of the same date
  13970. * @sample {highcharts} highcharts/yaxis/linkedto/
  13971. * Y values on both sides
  13972. *
  13973. * @type {number}
  13974. * @since 2.0.2
  13975. * @product highcharts highstock gantt
  13976. * @apioption xAxis.linkedTo
  13977. */
  13978. /**
  13979. * The maximum value of the axis. If `null`, the max value is
  13980. * automatically calculated.
  13981. *
  13982. * If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
  13983. * might be rounded up.
  13984. *
  13985. * If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
  13986. * beyond the set max in order to reach the given number of ticks. The
  13987. * same may happen in a chart with multiple axes, determined by [chart.
  13988. * alignTicks](#chart), where a `tickAmount` is applied internally.
  13989. *
  13990. * @sample {highcharts} highcharts/yaxis/max-200/
  13991. * Y axis max of 200
  13992. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  13993. * Y axis max on logarithmic axis
  13994. * @sample {highstock} stock/xaxis/min-max/
  13995. * Fixed min and max on X axis
  13996. * @sample {highmaps} maps/axis/min-max/
  13997. * Pre-zoomed to a specific area
  13998. *
  13999. * @type {number}
  14000. * @apioption xAxis.max
  14001. */
  14002. /**
  14003. * Padding of the max value relative to the length of the axis. A
  14004. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  14005. * when you don't want the highest data value to appear on the edge
  14006. * of the plot area. When the axis' `max` option is set or a max extreme
  14007. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  14008. *
  14009. * @sample {highcharts} highcharts/yaxis/maxpadding/
  14010. * Max padding of 0.25 on y axis
  14011. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  14012. * Greater min- and maxPadding
  14013. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  14014. * Add some padding
  14015. *
  14016. * @default {highcharts} 0.01
  14017. * @default {highstock|highmaps} 0
  14018. * @since 1.2.0
  14019. */
  14020. maxPadding: 0.01,
  14021. /**
  14022. * Deprecated. Use `minRange` instead.
  14023. *
  14024. * @deprecated
  14025. * @type {number}
  14026. * @product highcharts highstock
  14027. * @apioption xAxis.maxZoom
  14028. */
  14029. /**
  14030. * The minimum value of the axis. If `null` the min value is
  14031. * automatically calculated.
  14032. *
  14033. * If the [startOnTick](#yAxis.startOnTick) option is true (default),
  14034. * the `min` value might be rounded down.
  14035. *
  14036. * The automatically calculated minimum value is also affected by
  14037. * [floor](#yAxis.floor), [softMin](#yAxis.softMin),
  14038. * [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
  14039. * as well as [series.threshold](#plotOptions.series.threshold)
  14040. * and [series.softThreshold](#plotOptions.series.softThreshold).
  14041. *
  14042. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  14043. * -50 with startOnTick to false
  14044. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  14045. * -50 with startOnTick true by default
  14046. * @sample {highstock} stock/xaxis/min-max/
  14047. * Set min and max on X axis
  14048. * @sample {highmaps} maps/axis/min-max/
  14049. * Pre-zoomed to a specific area
  14050. *
  14051. * @type {number}
  14052. * @apioption xAxis.min
  14053. */
  14054. /**
  14055. * The dash or dot style of the minor grid lines. For possible values,
  14056. * see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  14057. *
  14058. * @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
  14059. * Long dashes on minor grid lines
  14060. * @sample {highstock} stock/xaxis/minorgridlinedashstyle/
  14061. * Long dashes on minor grid lines
  14062. *
  14063. * @type {Highcharts.DashStyleType}
  14064. * @default Solid
  14065. * @since 1.2
  14066. * @apioption xAxis.minorGridLineDashStyle
  14067. */
  14068. /**
  14069. * Specific tick interval in axis units for the minor ticks. On a linear
  14070. * axis, if `"auto"`, the minor tick interval is calculated as a fifth
  14071. * of the tickInterval. If `null` or `undefined`, minor ticks are not
  14072. * shown.
  14073. *
  14074. * On logarithmic axes, the unit is the power of the value. For example,
  14075. * setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
  14076. * 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
  14077. * between 1 and 10, 10 and 100 etc.
  14078. *
  14079. * If user settings dictate minor ticks to become too dense, they don't
  14080. * make sense, and will be ignored to prevent performance problems.
  14081. *
  14082. * @sample {highcharts} highcharts/yaxis/minortickinterval-null/
  14083. * Null by default
  14084. * @sample {highcharts} highcharts/yaxis/minortickinterval-5/
  14085. * 5 units
  14086. * @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
  14087. * "auto"
  14088. * @sample {highcharts} highcharts/yaxis/minortickinterval-log/
  14089. * 0.1
  14090. * @sample {highstock} stock/demo/basic-line/
  14091. * Null by default
  14092. * @sample {highstock} stock/xaxis/minortickinterval-auto/
  14093. * "auto"
  14094. *
  14095. * @type {number|string|null}
  14096. * @apioption xAxis.minorTickInterval
  14097. */
  14098. /**
  14099. * The pixel length of the minor tick marks.
  14100. *
  14101. * @sample {highcharts} highcharts/yaxis/minorticklength/
  14102. * 10px on Y axis
  14103. * @sample {highstock} stock/xaxis/minorticks/
  14104. * 10px on Y axis
  14105. */
  14106. minorTickLength: 2,
  14107. /**
  14108. * The position of the minor tick marks relative to the axis line.
  14109. * Can be one of `inside` and `outside`.
  14110. *
  14111. * @sample {highcharts} highcharts/yaxis/minortickposition-outside/
  14112. * Outside by default
  14113. * @sample {highcharts} highcharts/yaxis/minortickposition-inside/
  14114. * Inside
  14115. * @sample {highstock} stock/xaxis/minorticks/
  14116. * Inside
  14117. *
  14118. * @validvalue ["inside", "outside"]
  14119. */
  14120. minorTickPosition: 'outside',
  14121. /**
  14122. * Enable or disable minor ticks. Unless
  14123. * [minorTickInterval](#xAxis.minorTickInterval) is set, the tick
  14124. * interval is calculated as a fifth of the `tickInterval`.
  14125. *
  14126. * On a logarithmic axis, minor ticks are laid out based on a best
  14127. * guess, attempting to enter approximately 5 minor ticks between
  14128. * each major tick.
  14129. *
  14130. * Prior to v6.0.0, ticks were unabled in auto layout by setting
  14131. * `minorTickInterval` to `"auto"`.
  14132. *
  14133. * @productdesc {highcharts}
  14134. * On axes using [categories](#xAxis.categories), minor ticks are not
  14135. * supported.
  14136. *
  14137. * @sample {highcharts} highcharts/yaxis/minorticks-true/
  14138. * Enabled on linear Y axis
  14139. *
  14140. * @type {boolean}
  14141. * @default false
  14142. * @since 6.0.0
  14143. * @apioption xAxis.minorTicks
  14144. */
  14145. /**
  14146. * The pixel width of the minor tick mark.
  14147. *
  14148. * @sample {highcharts} highcharts/yaxis/minortickwidth/
  14149. * 3px width
  14150. * @sample {highstock} stock/xaxis/minorticks/
  14151. * 1px width
  14152. *
  14153. * @type {number}
  14154. * @default 0
  14155. * @apioption xAxis.minorTickWidth
  14156. */
  14157. /**
  14158. * Padding of the min value relative to the length of the axis. A
  14159. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  14160. * when you don't want the lowest data value to appear on the edge
  14161. * of the plot area. When the axis' `min` option is set or a min extreme
  14162. * is set using `axis.setExtremes()`, the minPadding will be ignored.
  14163. *
  14164. * @sample {highcharts} highcharts/yaxis/minpadding/
  14165. * Min padding of 0.2
  14166. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  14167. * Greater min- and maxPadding
  14168. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  14169. * Add some padding
  14170. *
  14171. * @default {highcharts} 0.01
  14172. * @default {highstock|highmaps} 0
  14173. * @since 1.2.0
  14174. * @product highcharts highstock gantt
  14175. */
  14176. minPadding: 0.01,
  14177. /**
  14178. * The minimum range to display on this axis. The entire axis will not
  14179. * be allowed to span over a smaller interval than this. For example,
  14180. * for a datetime axis the main unit is milliseconds. If minRange is
  14181. * set to 3600000, you can't zoom in more than to one hour.
  14182. *
  14183. * The default minRange for the x axis is five times the smallest
  14184. * interval between any of the data points.
  14185. *
  14186. * On a logarithmic axis, the unit for the minimum range is the power.
  14187. * So a minRange of 1 means that the axis can be zoomed to 10-100,
  14188. * 100-1000, 1000-10000 etc.
  14189. *
  14190. * Note that the `minPadding`, `maxPadding`, `startOnTick` and
  14191. * `endOnTick` settings also affect how the extremes of the axis
  14192. * are computed.
  14193. *
  14194. * @sample {highcharts} highcharts/xaxis/minrange/
  14195. * Minimum range of 5
  14196. * @sample {highstock} stock/xaxis/minrange/
  14197. * Max zoom of 6 months overrides user selections
  14198. * @sample {highmaps} maps/axis/minrange/
  14199. * Minimum range of 1000
  14200. *
  14201. * @type {number}
  14202. * @apioption xAxis.minRange
  14203. */
  14204. /**
  14205. * The minimum tick interval allowed in axis values. For example on
  14206. * zooming in on an axis with daily data, this can be used to prevent
  14207. * the axis from showing hours. Defaults to the closest distance between
  14208. * two points on the axis.
  14209. *
  14210. * @type {number}
  14211. * @since 2.3.0
  14212. * @apioption xAxis.minTickInterval
  14213. */
  14214. /**
  14215. * The distance in pixels from the plot area to the axis line.
  14216. * A positive offset moves the axis with it's line, labels and ticks
  14217. * away from the plot area. This is typically used when two or more
  14218. * axes are displayed on the same side of the plot. With multiple
  14219. * axes the offset is dynamically adjusted to avoid collision, this
  14220. * can be overridden by setting offset explicitly.
  14221. *
  14222. * @sample {highcharts} highcharts/yaxis/offset/
  14223. * Y axis offset of 70
  14224. * @sample {highcharts} highcharts/yaxis/offset-centered/
  14225. * Axes positioned in the center of the plot
  14226. * @sample {highstock} stock/xaxis/offset/
  14227. * Y axis offset by 70 px
  14228. *
  14229. * @type {number}
  14230. * @default 0
  14231. * @apioption xAxis.offset
  14232. */
  14233. /**
  14234. * Whether to display the axis on the opposite side of the normal. The
  14235. * normal is on the left side for vertical axes and bottom for
  14236. * horizontal, so the opposite sides will be right and top respectively.
  14237. * This is typically used with dual or multiple axes.
  14238. *
  14239. * @sample {highcharts} highcharts/yaxis/opposite/
  14240. * Secondary Y axis opposite
  14241. * @sample {highstock} stock/xaxis/opposite/
  14242. * Y axis on left side
  14243. *
  14244. * @type {boolean}
  14245. * @default false
  14246. * @apioption xAxis.opposite
  14247. */
  14248. /**
  14249. * In an ordinal axis, the points are equally spaced in the chart
  14250. * regardless of the actual time or x distance between them. This means
  14251. * that missing data periods (e.g. nights or weekends for a stock chart)
  14252. * will not take up space in the chart.
  14253. * Having `ordinal: false` will show any gaps created by the `gapSize`
  14254. * setting proportionate to their duration.
  14255. *
  14256. * In stock charts the X axis is ordinal by default, unless
  14257. * the boost module is used and at least one of the series' data length
  14258. * exceeds the [boostThreshold](#series.line.boostThreshold).
  14259. *
  14260. * @sample {highstock} stock/xaxis/ordinal-true/
  14261. * True by default
  14262. * @sample {highstock} stock/xaxis/ordinal-false/
  14263. * False
  14264. *
  14265. * @type {boolean}
  14266. * @default true
  14267. * @since 1.1
  14268. * @product highstock
  14269. * @apioption xAxis.ordinal
  14270. */
  14271. /**
  14272. * Additional range on the right side of the xAxis. Works similar to
  14273. * `xAxis.maxPadding`, but value is set in milliseconds. Can be set for
  14274. * both main `xAxis` and the navigator's `xAxis`.
  14275. *
  14276. * @sample {highstock} stock/xaxis/overscroll/
  14277. * One minute overscroll with live data
  14278. *
  14279. * @type {number}
  14280. * @default 0
  14281. * @since 6.0.0
  14282. * @product highstock
  14283. * @apioption xAxis.overscroll
  14284. */
  14285. /**
  14286. * Refers to the index in the [panes](#panes) array. Used for circular
  14287. * gauges and polar charts. When the option is not set then first pane
  14288. * will be used.
  14289. *
  14290. * @sample highcharts/demo/gauge-vu-meter
  14291. * Two gauges with different center
  14292. *
  14293. * @type {number}
  14294. * @product highcharts
  14295. * @apioption xAxis.pane
  14296. */
  14297. /**
  14298. * The zoomed range to display when only defining one or none of `min`
  14299. * or `max`. For example, to show the latest month, a range of one month
  14300. * can be set.
  14301. *
  14302. * @sample {highstock} stock/xaxis/range/
  14303. * Setting a zoomed range when the rangeSelector is disabled
  14304. *
  14305. * @type {number}
  14306. * @product highstock
  14307. * @apioption xAxis.range
  14308. */
  14309. /**
  14310. * Whether to reverse the axis so that the highest number is closest
  14311. * to the origin. If the chart is inverted, the x axis is reversed by
  14312. * default.
  14313. *
  14314. * @sample {highcharts} highcharts/yaxis/reversed/
  14315. * Reversed Y axis
  14316. * @sample {highstock} stock/xaxis/reversed/
  14317. * Reversed Y axis
  14318. *
  14319. * @type {boolean}
  14320. * @default false
  14321. * @apioption xAxis.reversed
  14322. */
  14323. // reversed: false,
  14324. /**
  14325. * This option determines how stacks should be ordered within a group.
  14326. * For example reversed xAxis also reverses stacks, so first series
  14327. * comes last in a group. To keep order like for non-reversed xAxis
  14328. * enable this option.
  14329. *
  14330. * @sample {highcharts} highcharts/xaxis/reversedstacks/
  14331. * Reversed stacks comparison
  14332. * @sample {highstock} highcharts/xaxis/reversedstacks/
  14333. * Reversed stacks comparison
  14334. *
  14335. * @type {boolean}
  14336. * @default false
  14337. * @since 6.1.1
  14338. * @product highcharts highstock
  14339. * @apioption xAxis.reversedStacks
  14340. */
  14341. /**
  14342. * An optional scrollbar to display on the X axis in response to
  14343. * limiting the minimum and maximum of the axis values.
  14344. *
  14345. * In styled mode, all the presentational options for the scrollbar are
  14346. * replaced by the classes `.highcharts-scrollbar-thumb`,
  14347. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  14348. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  14349. *
  14350. * @sample {highstock} stock/yaxis/heatmap-scrollbars/
  14351. * Heatmap with both scrollbars
  14352. *
  14353. * @extends scrollbar
  14354. * @since 4.2.6
  14355. * @product highstock
  14356. * @apioption xAxis.scrollbar
  14357. */
  14358. /**
  14359. * Whether to show the axis line and title when the axis has no data.
  14360. *
  14361. * @sample {highcharts} highcharts/yaxis/showempty/
  14362. * When clicking the legend to hide series, one axis preserves
  14363. * line and title, the other doesn't
  14364. * @sample {highstock} highcharts/yaxis/showempty/
  14365. * When clicking the legend to hide series, one axis preserves
  14366. * line and title, the other doesn't
  14367. *
  14368. * @type {boolean}
  14369. * @default true
  14370. * @since 1.1
  14371. * @apioption xAxis.showEmpty
  14372. */
  14373. /**
  14374. * Whether to show the first tick label.
  14375. *
  14376. * @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
  14377. * Set to false on X axis
  14378. * @sample {highstock} stock/xaxis/showfirstlabel/
  14379. * Labels below plot lines on Y axis
  14380. *
  14381. * @type {boolean}
  14382. * @default true
  14383. * @apioption xAxis.showFirstLabel
  14384. */
  14385. /**
  14386. * Whether to show the last tick label. Defaults to `true` on cartesian
  14387. * charts, and `false` on polar charts.
  14388. *
  14389. * @sample {highcharts} highcharts/xaxis/showlastlabel-true/
  14390. * Set to true on X axis
  14391. * @sample {highstock} stock/xaxis/showfirstlabel/
  14392. * Labels below plot lines on Y axis
  14393. *
  14394. * @type {boolean}
  14395. * @default true
  14396. * @product highcharts highstock gantt
  14397. * @apioption xAxis.showLastLabel
  14398. */
  14399. /**
  14400. * A soft maximum for the axis. If the series data maximum is less than
  14401. * this, the axis will stay at this maximum, but if the series data
  14402. * maximum is higher, the axis will flex to show all data.
  14403. *
  14404. * @sample highcharts/yaxis/softmin-softmax/
  14405. * Soft min and max
  14406. *
  14407. * @type {number}
  14408. * @since 5.0.1
  14409. * @product highcharts highstock gantt
  14410. * @apioption xAxis.softMax
  14411. */
  14412. /**
  14413. * A soft minimum for the axis. If the series data minimum is greater
  14414. * than this, the axis will stay at this minimum, but if the series
  14415. * data minimum is lower, the axis will flex to show all data.
  14416. *
  14417. * @sample highcharts/yaxis/softmin-softmax/
  14418. * Soft min and max
  14419. *
  14420. * @type {number}
  14421. * @since 5.0.1
  14422. * @product highcharts highstock gantt
  14423. * @apioption xAxis.softMin
  14424. */
  14425. /**
  14426. * For datetime axes, this decides where to put the tick between weeks.
  14427. * 0 = Sunday, 1 = Monday.
  14428. *
  14429. * @sample {highcharts} highcharts/xaxis/startofweek-monday/
  14430. * Monday by default
  14431. * @sample {highcharts} highcharts/xaxis/startofweek-sunday/
  14432. * Sunday
  14433. * @sample {highstock} stock/xaxis/startofweek-1
  14434. * Monday by default
  14435. * @sample {highstock} stock/xaxis/startofweek-0
  14436. * Sunday
  14437. *
  14438. * @product highcharts highstock gantt
  14439. */
  14440. startOfWeek: 1,
  14441. /**
  14442. * Whether to force the axis to start on a tick. Use this option with
  14443. * the `minPadding` option to control the axis start.
  14444. *
  14445. * @productdesc {highstock}
  14446. * In Highstock, `startOnTick` is always false when the navigator is
  14447. * enabled, to prevent jumpy scrolling.
  14448. *
  14449. * @sample {highcharts} highcharts/xaxis/startontick-false/
  14450. * False by default
  14451. * @sample {highcharts} highcharts/xaxis/startontick-true/
  14452. * True
  14453. * @sample {highstock} stock/xaxis/endontick/
  14454. * False for Y axis
  14455. *
  14456. * @since 1.2.0
  14457. */
  14458. startOnTick: false,
  14459. /**
  14460. * The amount of ticks to draw on the axis. This opens up for aligning
  14461. * the ticks of multiple charts or panes within a chart. This option
  14462. * overrides the `tickPixelInterval` option.
  14463. *
  14464. * This option only has an effect on linear axes. Datetime, logarithmic
  14465. * or category axes are not affected.
  14466. *
  14467. * @sample {highcharts} highcharts/yaxis/tickamount/
  14468. * 8 ticks on Y axis
  14469. * @sample {highstock} highcharts/yaxis/tickamount/
  14470. * 8 ticks on Y axis
  14471. *
  14472. * @type {number}
  14473. * @since 4.1.0
  14474. * @product highcharts highstock gantt
  14475. * @apioption xAxis.tickAmount
  14476. */
  14477. /**
  14478. * The interval of the tick marks in axis units. When `undefined`, the
  14479. * tick interval is computed to approximately follow the
  14480. * [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
  14481. * axes. On categorized axes, a `undefined` tickInterval will default to
  14482. * 1, one category. Note that datetime axes are based on milliseconds,
  14483. * so for example an interval of one day is expressed as
  14484. * `24 * 3600 * 1000`.
  14485. *
  14486. * On logarithmic axes, the tickInterval is based on powers, so a
  14487. * tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
  14488. * tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
  14489. * of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
  14490. * 40 etc.
  14491. *
  14492. *
  14493. * If the tickInterval is too dense for labels to be drawn, Highcharts
  14494. * may remove ticks.
  14495. *
  14496. * If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
  14497. * option may interfere with the `tickInterval` setting.
  14498. *
  14499. * @see [tickPixelInterval](#xAxis.tickPixelInterval)
  14500. * @see [tickPositions](#xAxis.tickPositions)
  14501. * @see [tickPositioner](#xAxis.tickPositioner)
  14502. *
  14503. * @sample {highcharts} highcharts/xaxis/tickinterval-5/
  14504. * Tick interval of 5 on a linear axis
  14505. * @sample {highstock} stock/xaxis/tickinterval/
  14506. * Tick interval of 0.01 on Y axis
  14507. *
  14508. * @type {number}
  14509. * @apioption xAxis.tickInterval
  14510. */
  14511. /**
  14512. * The pixel length of the main tick marks.
  14513. *
  14514. * @sample {highcharts} highcharts/xaxis/ticklength/
  14515. * 20 px tick length on the X axis
  14516. * @sample {highstock} stock/xaxis/ticks/
  14517. * Formatted ticks on X axis
  14518. */
  14519. tickLength: 10,
  14520. /**
  14521. * If tickInterval is `null` this option sets the approximate pixel
  14522. * interval of the tick marks. Not applicable to categorized axis.
  14523. *
  14524. * The tick interval is also influenced by the [minTickInterval](
  14525. * #xAxis.minTickInterval) option, that, by default prevents ticks from
  14526. * being denser than the data points.
  14527. *
  14528. * @see [tickInterval](#xAxis.tickInterval)
  14529. * @see [tickPositioner](#xAxis.tickPositioner)
  14530. * @see [tickPositions](#xAxis.tickPositions)
  14531. *
  14532. * @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
  14533. * 50 px on X axis
  14534. * @sample {highstock} stock/xaxis/tickpixelinterval/
  14535. * 200 px on X axis
  14536. */
  14537. tickPixelInterval: 100,
  14538. /**
  14539. * For categorized axes only. If `on` the tick mark is placed in the
  14540. * center of the category, if `between` the tick mark is placed between
  14541. * categories. The default is `between` if the `tickInterval` is 1, else
  14542. * `on`.
  14543. *
  14544. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
  14545. * "between" by default
  14546. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
  14547. * "on"
  14548. *
  14549. * @product highcharts gantt
  14550. * @validvalue ["on", "between"]
  14551. */
  14552. tickmarkPlacement: 'between',
  14553. /**
  14554. * The position of the major tick marks relative to the axis line.
  14555. * Can be one of `inside` and `outside`.
  14556. *
  14557. * @sample {highcharts} highcharts/xaxis/tickposition-outside/
  14558. * "outside" by default
  14559. * @sample {highcharts} highcharts/xaxis/tickposition-inside/
  14560. * "inside"
  14561. * @sample {highstock} stock/xaxis/ticks/
  14562. * Formatted ticks on X axis
  14563. *
  14564. * @validvalue ["inside", "outside"]
  14565. */
  14566. tickPosition: 'outside',
  14567. /**
  14568. * A callback function returning array defining where the ticks are
  14569. * laid out on the axis. This overrides the default behaviour of
  14570. * [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
  14571. * #xAxis.tickInterval). The automatic tick positions are accessible
  14572. * through `this.tickPositions` and can be modified by the callback.
  14573. *
  14574. * @see [tickPositions](#xAxis.tickPositions)
  14575. *
  14576. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  14577. * Demo of tickPositions and tickPositioner
  14578. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  14579. * Demo of tickPositions and tickPositioner
  14580. *
  14581. * @type {Highcharts.AxisTickPositionerCallbackFunction}
  14582. * @apioption xAxis.tickPositioner
  14583. */
  14584. /**
  14585. * An array defining where the ticks are laid out on the axis. This
  14586. * overrides the default behaviour of [tickPixelInterval](
  14587. * #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
  14588. *
  14589. * @see [tickPositioner](#xAxis.tickPositioner)
  14590. *
  14591. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  14592. * Demo of tickPositions and tickPositioner
  14593. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  14594. * Demo of tickPositions and tickPositioner
  14595. *
  14596. * @type {Array<number>}
  14597. * @apioption xAxis.tickPositions
  14598. */
  14599. /**
  14600. * The pixel width of the major tick marks.
  14601. *
  14602. * In styled mode, the stroke width is given in the `.highcharts-tick`
  14603. * class.
  14604. *
  14605. * @sample {highcharts} highcharts/xaxis/tickwidth/
  14606. * 10 px width
  14607. * @sample {highcharts} highcharts/css/axis-grid/
  14608. * Styled mode
  14609. * @sample {highstock} stock/xaxis/ticks/
  14610. * Formatted ticks on X axis
  14611. * @sample {highstock} highcharts/css/axis-grid/
  14612. * Styled mode
  14613. *
  14614. * @type {number}
  14615. * @default {highcharts} 1
  14616. * @default {highstock} 1
  14617. * @default {highmaps} 0
  14618. * @apioption xAxis.tickWidth
  14619. */
  14620. /**
  14621. * The axis title, showing next to the axis line.
  14622. *
  14623. * @productdesc {highmaps}
  14624. * In Highmaps, the axis is hidden by default, but adding an axis title
  14625. * is still possible. X axis and Y axis titles will appear at the bottom
  14626. * and left by default.
  14627. */
  14628. title: {
  14629. /**
  14630. * Deprecated. Set the `text` to `null` to disable the title.
  14631. *
  14632. * @deprecated
  14633. * @type {string}
  14634. * @default middle
  14635. * @product highcharts
  14636. * @apioption xAxis.title.enabled
  14637. */
  14638. /**
  14639. * The pixel distance between the axis labels or line and the title.
  14640. * Defaults to 0 for horizontal axes, 10 for vertical
  14641. *
  14642. * @sample {highcharts} highcharts/xaxis/title-margin/
  14643. * Y axis title margin of 60
  14644. *
  14645. * @type {number}
  14646. * @apioption xAxis.title.margin
  14647. */
  14648. /**
  14649. * The distance of the axis title from the axis line. By default,
  14650. * this distance is computed from the offset width of the labels,
  14651. * the labels' distance from the axis and the title's margin.
  14652. * However when the offset option is set, it overrides all this.
  14653. *
  14654. * @sample {highcharts} highcharts/yaxis/title-offset/
  14655. * Place the axis title on top of the axis
  14656. * @sample {highstock} highcharts/yaxis/title-offset/
  14657. * Place the axis title on top of the Y axis
  14658. *
  14659. * @type {number}
  14660. * @since 2.2.0
  14661. * @apioption xAxis.title.offset
  14662. */
  14663. /**
  14664. * Whether to reserve space for the title when laying out the axis.
  14665. *
  14666. * @type {boolean}
  14667. * @default true
  14668. * @since 5.0.11
  14669. * @product highcharts highstock gantt
  14670. * @apioption xAxis.title.reserveSpace
  14671. */
  14672. /**
  14673. * The rotation of the text in degrees. 0 is horizontal, 270 is
  14674. * vertical reading from bottom to top.
  14675. *
  14676. * @sample {highcharts} highcharts/yaxis/title-offset/
  14677. * Horizontal
  14678. *
  14679. * @type {number}
  14680. * @default 0
  14681. * @apioption xAxis.title.rotation
  14682. */
  14683. /**
  14684. * The actual text of the axis title. It can contain basic HTML text
  14685. * markup like <b>, <i> and spans with style.
  14686. *
  14687. * @sample {highcharts} highcharts/xaxis/title-text/
  14688. * Custom HTML
  14689. * @sample {highstock} stock/xaxis/title-text/
  14690. * Titles for both axes
  14691. *
  14692. * @type {string|null}
  14693. * @apioption xAxis.title.text
  14694. */
  14695. /**
  14696. * Alignment of the text, can be `"left"`, `"right"` or `"center"`.
  14697. * Default alignment depends on the
  14698. * [title.align](xAxis.title.align):
  14699. *
  14700. * Horizontal axes:
  14701. * - for `align` = `"low"`, `textAlign` is set to `left`
  14702. * - for `align` = `"middle"`, `textAlign` is set to `center`
  14703. * - for `align` = `"high"`, `textAlign` is set to `right`
  14704. *
  14705. * Vertical axes:
  14706. * - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
  14707. * set to `right`
  14708. * - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
  14709. * set to `left`
  14710. * - for `align` = `"middle"`, `textAlign` is set to `center`
  14711. * - for `align` = `"high"` and `opposite` = `true` `textAlign` is
  14712. * set to `left`
  14713. * - for `align` = `"high"` and `opposite` = `false` `textAlign` is
  14714. * set to `right`
  14715. *
  14716. * @type {string}
  14717. * @apioption xAxis.title.textAlign
  14718. */
  14719. /**
  14720. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  14721. * to render the axis title.
  14722. *
  14723. * @type {boolean}
  14724. * @default false
  14725. * @product highcharts highstock gantt
  14726. * @apioption xAxis.title.useHTML
  14727. */
  14728. /**
  14729. * Horizontal pixel offset of the title position.
  14730. *
  14731. * @type {number}
  14732. * @default 0
  14733. * @since 4.1.6
  14734. * @product highcharts highstock gantt
  14735. * @apioption xAxis.title.x
  14736. */
  14737. /**
  14738. * Vertical pixel offset of the title position.
  14739. *
  14740. * @type {number}
  14741. * @product highcharts highstock gantt
  14742. * @apioption xAxis.title.y
  14743. */
  14744. /**
  14745. * Alignment of the title relative to the axis values. Possible
  14746. * values are "low", "middle" or "high".
  14747. *
  14748. * @sample {highcharts} highcharts/xaxis/title-align-low/
  14749. * "low"
  14750. * @sample {highcharts} highcharts/xaxis/title-align-center/
  14751. * "middle" by default
  14752. * @sample {highcharts} highcharts/xaxis/title-align-high/
  14753. * "high"
  14754. * @sample {highcharts} highcharts/yaxis/title-offset/
  14755. * Place the Y axis title on top of the axis
  14756. * @sample {highstock} stock/xaxis/title-align/
  14757. * Aligned to "high" value
  14758. *
  14759. * @validvalue ["low", "middle", "high"]
  14760. */
  14761. align: 'middle',
  14762. /**
  14763. * CSS styles for the title. If the title text is longer than the
  14764. * axis length, it will wrap to multiple lines by default. This can
  14765. * be customized by setting `textOverflow: 'ellipsis'`, by
  14766. * setting a specific `width` or by setting `whiteSpace: 'nowrap'`.
  14767. *
  14768. * In styled mode, the stroke width is given in the
  14769. * `.highcharts-axis-title` class.
  14770. *
  14771. * @sample {highcharts} highcharts/xaxis/title-style/
  14772. * Red
  14773. * @sample {highcharts} highcharts/css/axis/
  14774. * Styled mode
  14775. *
  14776. * @type {Highcharts.CSSObject}
  14777. * @default {"color": "#666666"}
  14778. */
  14779. style: {
  14780. /** @ignore-option */
  14781. color: '#666666'
  14782. }
  14783. },
  14784. /**
  14785. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
  14786. * or `category`. In a datetime axis, the numbers are given in
  14787. * milliseconds, and tick marks are placed on appropriate values like
  14788. * full hours or days. In a category axis, the
  14789. * [point names](#series.line.data.name) of the chart's series are used
  14790. * for categories, if not a [categories](#xAxis.categories) array is
  14791. * defined.
  14792. *
  14793. * @sample {highcharts} highcharts/xaxis/type-linear/
  14794. * Linear
  14795. * @sample {highcharts} highcharts/yaxis/type-log/
  14796. * Logarithmic
  14797. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  14798. * Logarithmic with minor grid lines
  14799. * @sample {highcharts} highcharts/xaxis/type-log-both/
  14800. * Logarithmic on two axes
  14801. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  14802. * Logarithmic with extension to emulate negative values
  14803. *
  14804. * @product highcharts gantt
  14805. * @validvalue ["linear", "logarithmic", "datetime", "category"]
  14806. */
  14807. type: 'linear',
  14808. /**
  14809. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
  14810. * `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
  14811. * `linear` for other chart types.
  14812. *
  14813. * In a datetime axis, the numbers are given in milliseconds, and tick
  14814. * marks are placed on appropriate values, like full hours or days. In a
  14815. * category or treegrid axis, the [point names](#series.line.data.name)
  14816. * of the chart's series are used for categories, if a
  14817. * [categories](#xAxis.categories) array is not defined.
  14818. *
  14819. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  14820. * Logarithmic with minor grid lines
  14821. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  14822. * Logarithmic with extension to emulate negative values
  14823. * @sample {gantt} gantt/treegrid-axis/demo
  14824. * Treegrid axis
  14825. *
  14826. * @default {highcharts} linear
  14827. * @default {gantt} treegrid
  14828. * @product highcharts gantt
  14829. * @validvalue ["linear", "logarithmic", "datetime", "category",
  14830. * "treegrid"]
  14831. * @apioption yAxis.type
  14832. */
  14833. /**
  14834. * Applies only when the axis `type` is `category`. When `uniqueNames`
  14835. * is true, points are placed on the X axis according to their names.
  14836. * If the same point name is repeated in the same or another series,
  14837. * the point is placed on the same X position as other points of the
  14838. * same name. When `uniqueNames` is false, the points are laid out in
  14839. * increasing X positions regardless of their names, and the X axis
  14840. * category will take the name of the last point in each position.
  14841. *
  14842. * @sample {highcharts} highcharts/xaxis/uniquenames-true/
  14843. * True by default
  14844. * @sample {highcharts} highcharts/xaxis/uniquenames-false/
  14845. * False
  14846. *
  14847. * @type {boolean}
  14848. * @default true
  14849. * @since 4.2.7
  14850. * @product highcharts gantt
  14851. * @apioption xAxis.uniqueNames
  14852. */
  14853. /**
  14854. * Datetime axis only. An array determining what time intervals the
  14855. * ticks are allowed to fall on. Each array item is an array where the
  14856. * first value is the time unit and the second value another array of
  14857. * allowed multiples. Defaults to:
  14858. *
  14859. * <pre>units: [[
  14860. * 'millisecond', // unit name
  14861. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  14862. * ], [
  14863. * 'second',
  14864. * [1, 2, 5, 10, 15, 30]
  14865. * ], [
  14866. * 'minute',
  14867. * [1, 2, 5, 10, 15, 30]
  14868. * ], [
  14869. * 'hour',
  14870. * [1, 2, 3, 4, 6, 8, 12]
  14871. * ], [
  14872. * 'day',
  14873. * [1]
  14874. * ], [
  14875. * 'week',
  14876. * [1]
  14877. * ], [
  14878. * 'month',
  14879. * [1, 3, 6]
  14880. * ], [
  14881. * 'year',
  14882. * null
  14883. * ]]</pre>
  14884. *
  14885. * @type {Array<Array<string,(Array<number>|null)>>}
  14886. * @product highcharts highstock gantt
  14887. * @apioption xAxis.units
  14888. */
  14889. /**
  14890. * Whether axis, including axis title, line, ticks and labels, should
  14891. * be visible.
  14892. *
  14893. * @type {boolean}
  14894. * @default true
  14895. * @since 4.1.9
  14896. * @product highcharts highstock gantt
  14897. * @apioption xAxis.visible
  14898. */
  14899. /**
  14900. * Color of the minor, secondary grid lines.
  14901. *
  14902. * In styled mode, the stroke width is given in the
  14903. * `.highcharts-minor-grid-line` class.
  14904. *
  14905. * @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
  14906. * Bright grey lines from Y axis
  14907. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  14908. * Styled mode
  14909. * @sample {highstock} stock/xaxis/minorgridlinecolor/
  14910. * Bright grey lines from Y axis
  14911. *
  14912. * @type {Highcharts.ColorString}
  14913. * @default #f2f2f2
  14914. */
  14915. minorGridLineColor: '#f2f2f2',
  14916. /**
  14917. * Width of the minor, secondary grid lines.
  14918. *
  14919. * In styled mode, the stroke width is given in the
  14920. * `.highcharts-grid-line` class.
  14921. *
  14922. * @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
  14923. * 2px lines from Y axis
  14924. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  14925. * Styled mode
  14926. * @sample {highstock} stock/xaxis/minorgridlinewidth/
  14927. * 2px lines from Y axis
  14928. */
  14929. minorGridLineWidth: 1,
  14930. /**
  14931. * Color for the minor tick marks.
  14932. *
  14933. * @sample {highcharts} highcharts/yaxis/minortickcolor/
  14934. * Black tick marks on Y axis
  14935. * @sample {highstock} stock/xaxis/minorticks/
  14936. * Black tick marks on Y axis
  14937. *
  14938. * @type {Highcharts.ColorString}
  14939. * @default #999999
  14940. */
  14941. minorTickColor: '#999999',
  14942. /**
  14943. * The color of the line marking the axis itself.
  14944. *
  14945. * In styled mode, the line stroke is given in the
  14946. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  14947. *
  14948. * @productdesc {highmaps}
  14949. * In Highmaps, the axis line is hidden by default, because the axis is
  14950. * not visible by default.
  14951. *
  14952. * @sample {highcharts} highcharts/yaxis/linecolor/
  14953. * A red line on Y axis
  14954. * @sample {highcharts|highstock} highcharts/css/axis/
  14955. * Axes in styled mode
  14956. * @sample {highstock} stock/xaxis/linecolor/
  14957. * A red line on X axis
  14958. *
  14959. * @type {Highcharts.ColorString}
  14960. * @default #ccd6eb
  14961. */
  14962. lineColor: '#ccd6eb',
  14963. /**
  14964. * The width of the line marking the axis itself.
  14965. *
  14966. * In styled mode, the stroke width is given in the
  14967. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  14968. *
  14969. * @sample {highcharts} highcharts/yaxis/linecolor/
  14970. * A 1px line on Y axis
  14971. * @sample {highcharts|highstock} highcharts/css/axis/
  14972. * Axes in styled mode
  14973. * @sample {highstock} stock/xaxis/linewidth/
  14974. * A 2px line on X axis
  14975. *
  14976. * @default {highcharts|highstock} 1
  14977. * @default {highmaps} 0
  14978. */
  14979. lineWidth: 1,
  14980. /**
  14981. * Color of the grid lines extending the ticks across the plot area.
  14982. *
  14983. * In styled mode, the stroke is given in the `.highcharts-grid-line`
  14984. * class.
  14985. *
  14986. * @productdesc {highmaps}
  14987. * In Highmaps, the grid lines are hidden by default.
  14988. *
  14989. * @sample {highcharts} highcharts/yaxis/gridlinecolor/
  14990. * Green lines
  14991. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  14992. * Styled mode
  14993. * @sample {highstock} stock/xaxis/gridlinecolor/
  14994. * Green lines
  14995. *
  14996. * @type {Highcharts.ColorString}
  14997. * @default #e6e6e6
  14998. */
  14999. gridLineColor: '#e6e6e6',
  15000. // gridLineDashStyle: 'solid',
  15001. /**
  15002. * The width of the grid lines extending the ticks across the plot area.
  15003. *
  15004. * In styled mode, the stroke width is given in the
  15005. * `.highcharts-grid-line` class.
  15006. *
  15007. * @sample {highcharts} highcharts/yaxis/gridlinewidth/
  15008. * 2px lines
  15009. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  15010. * Styled mode
  15011. * @sample {highstock} stock/xaxis/gridlinewidth/
  15012. * 2px lines
  15013. *
  15014. * @type {number}
  15015. * @default 0
  15016. * @apioption xAxis.gridLineWidth
  15017. */
  15018. // gridLineWidth: 0,
  15019. /**
  15020. * Color for the main tick marks.
  15021. *
  15022. * In styled mode, the stroke is given in the `.highcharts-tick`
  15023. * class.
  15024. *
  15025. * @sample {highcharts} highcharts/xaxis/tickcolor/
  15026. * Red ticks on X axis
  15027. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  15028. * Styled mode
  15029. * @sample {highstock} stock/xaxis/ticks/
  15030. * Formatted ticks on X axis
  15031. *
  15032. * @type {Highcharts.ColorString}
  15033. * @default #ccd6eb
  15034. */
  15035. tickColor: '#ccd6eb'
  15036. // tickWidth: 1
  15037. },
  15038. /**
  15039. * The Y axis or value axis. Normally this is the vertical axis,
  15040. * though if the chart is inverted this is the horizontal axis.
  15041. * In case of multiple axes, the yAxis node is an array of
  15042. * configuration objects.
  15043. *
  15044. * See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
  15045. * access to the axis.
  15046. *
  15047. * @type {*|Array<*>}
  15048. * @extends xAxis
  15049. * @excluding ordinal,overscroll,currentDateIndicator
  15050. * @optionparent yAxis
  15051. */
  15052. defaultYAxisOptions: {
  15053. /**
  15054. * In a polar chart, this is the angle of the Y axis in degrees, where
  15055. * 0 is up and 90 is right. The angle determines the position of the
  15056. * axis line and the labels, though the coordinate system is unaffected.
  15057. *
  15058. * @sample {highcharts} highcharts/yaxis/angle/
  15059. * Dual axis polar chart
  15060. *
  15061. * @type {number}
  15062. * @default 0
  15063. * @since 4.2.7
  15064. * @product highcharts
  15065. * @apioption yAxis.angle
  15066. */
  15067. /**
  15068. * Polar charts only. Whether the grid lines should draw as a polygon
  15069. * with straight lines between categories, or as circles. Can be either
  15070. * `circle` or `polygon`.
  15071. *
  15072. * @sample {highcharts} highcharts/demo/polar-spider/
  15073. * Polygon grid lines
  15074. * @sample {highcharts} highcharts/yaxis/gridlineinterpolation/
  15075. * Circle and polygon
  15076. *
  15077. * @type {string}
  15078. * @product highcharts
  15079. * @validvalue ["circle", "polygon"]
  15080. * @apioption yAxis.gridLineInterpolation
  15081. */
  15082. /**
  15083. * The height of the Y axis. If it's a number, it is interpreted as
  15084. * pixels.
  15085. *
  15086. * Since Highstock 2: If it's a percentage string, it is interpreted
  15087. * as percentages of the total plot height.
  15088. *
  15089. * @see [yAxis.top](#yAxis.top)
  15090. *
  15091. * @sample {highstock} stock/demo/candlestick-and-volume/
  15092. * Percentage height panes
  15093. *
  15094. * @type {number|string}
  15095. * @product highstock
  15096. * @apioption yAxis.height
  15097. */
  15098. /**
  15099. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  15100. * to represent the maximum value of the Y axis.
  15101. *
  15102. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  15103. * Min and max colors
  15104. *
  15105. * @type {Highcharts.ColorString}
  15106. * @default #003399
  15107. * @since 4.0
  15108. * @product highcharts
  15109. * @apioption yAxis.maxColor
  15110. */
  15111. /**
  15112. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  15113. * to represent the minimum value of the Y axis.
  15114. *
  15115. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  15116. * Min and max color
  15117. *
  15118. * @type {Highcharts.ColorString}
  15119. * @default #e6ebf5
  15120. * @since 4.0
  15121. * @product highcharts
  15122. * @apioption yAxis.minColor
  15123. */
  15124. /**
  15125. * Whether to reverse the axis so that the highest number is closest
  15126. * to the origin.
  15127. *
  15128. * @sample {highcharts} highcharts/yaxis/reversed/
  15129. * Reversed Y axis
  15130. * @sample {highstock} stock/xaxis/reversed/
  15131. * Reversed Y axis
  15132. *
  15133. * @type {boolean}
  15134. * @default {highcharts} false
  15135. * @default {highstock} false
  15136. * @default {highmaps} true
  15137. * @default {gantt} true
  15138. * @apioption yAxis.reversed
  15139. */
  15140. /**
  15141. * If `true`, the first series in a stack will be drawn on top in a
  15142. * positive, non-reversed Y axis. If `false`, the first series is in
  15143. * the base of the stack.
  15144. *
  15145. * @sample {highcharts} highcharts/yaxis/reversedstacks-false/
  15146. * Non-reversed stacks
  15147. * @sample {highstock} highcharts/yaxis/reversedstacks-false/
  15148. * Non-reversed stacks
  15149. *
  15150. * @type {boolean}
  15151. * @default true
  15152. * @since 3.0.10
  15153. * @product highcharts highstock
  15154. * @apioption yAxis.reversedStacks
  15155. */
  15156. /**
  15157. * Solid gauge series only. Color stops for the solid gauge. Use this
  15158. * in cases where a linear gradient between a `minColor` and `maxColor`
  15159. * is not sufficient. The stops is an array of tuples, where the first
  15160. * item is a float between 0 and 1 assigning the relative position in
  15161. * the gradient, and the second item is the color.
  15162. *
  15163. * For solid gauges, the Y axis also inherits the concept of
  15164. * [data classes](http://api.highcharts.com/highmaps#colorAxis.dataClasses)
  15165. * from the Highmaps color axis.
  15166. *
  15167. * @see [minColor](#yAxis.minColor)
  15168. * @see [maxColor](#yAxis.maxColor)
  15169. *
  15170. * @sample {highcharts} highcharts/demo/gauge-solid/
  15171. * True by default
  15172. *
  15173. * @type {Array<Array<number,Highcharts.ColorString>>}
  15174. * @since 4.0
  15175. * @product highcharts
  15176. * @apioption yAxis.stops
  15177. */
  15178. /**
  15179. * The pixel width of the major tick marks.
  15180. *
  15181. * @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
  15182. * @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
  15183. *
  15184. * @type {number}
  15185. * @default 0
  15186. * @product highcharts highstock gantt
  15187. * @apioption yAxis.tickWidth
  15188. */
  15189. /**
  15190. * Angular gauges and solid gauges only. The label's pixel distance
  15191. * from the perimeter of the plot area.
  15192. *
  15193. * @type {number}
  15194. * @default -25
  15195. * @product highcharts
  15196. * @apioption yAxis.labels.distance
  15197. */
  15198. /**
  15199. * The y position offset of the label relative to the tick position
  15200. * on the axis.
  15201. *
  15202. * @sample {highcharts} highcharts/xaxis/labels-x/
  15203. * Y axis labels placed on grid lines
  15204. *
  15205. * @type {number}
  15206. * @default {highcharts} 3
  15207. * @default {highstock} -2
  15208. * @default {highmaps} 3
  15209. * @apioption yAxis.labels.y
  15210. */
  15211. /**
  15212. * @productdesc {highstock}
  15213. * In Highstock, `endOnTick` is always false when the navigator is
  15214. * enabled, to prevent jumpy scrolling.
  15215. */
  15216. endOnTick: true,
  15217. /**
  15218. * Padding of the max value relative to the length of the axis. A
  15219. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15220. * when you don't want the highest data value to appear on the edge
  15221. * of the plot area. When the axis' `max` option is set or a max extreme
  15222. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  15223. *
  15224. * @sample {highcharts} highcharts/yaxis/maxpadding-02/
  15225. * Max padding of 0.2
  15226. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15227. * Greater min- and maxPadding
  15228. *
  15229. * @since 1.2.0
  15230. * @product highcharts highstock gantt
  15231. */
  15232. maxPadding: 0.05,
  15233. /**
  15234. * Padding of the min value relative to the length of the axis. A
  15235. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15236. * when you don't want the lowest data value to appear on the edge
  15237. * of the plot area. When the axis' `min` option is set or a max extreme
  15238. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  15239. *
  15240. * @sample {highcharts} highcharts/yaxis/minpadding/
  15241. * Min padding of 0.2
  15242. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15243. * Greater min- and maxPadding
  15244. *
  15245. * @since 1.2.0
  15246. * @product highcharts highstock gantt
  15247. */
  15248. minPadding: 0.05,
  15249. /**
  15250. * @productdesc {highstock}
  15251. * In Highstock 1.x, the Y axis was placed on the left side by default.
  15252. *
  15253. * @sample {highcharts} highcharts/yaxis/opposite/
  15254. * Secondary Y axis opposite
  15255. * @sample {highstock} stock/xaxis/opposite/
  15256. * Y axis on left side
  15257. *
  15258. * @type {boolean}
  15259. * @default {highstock} true
  15260. * @default {highcharts} false
  15261. * @product highstock highcharts gantt
  15262. * @apioption yAxis.opposite
  15263. */
  15264. /**
  15265. * @see [tickInterval](#xAxis.tickInterval)
  15266. * @see [tickPositioner](#xAxis.tickPositioner)
  15267. * @see [tickPositions](#xAxis.tickPositions)
  15268. */
  15269. tickPixelInterval: 72,
  15270. showLastLabel: true,
  15271. /**
  15272. * @extends xAxis.labels
  15273. */
  15274. labels: {
  15275. /**
  15276. * What part of the string the given position is anchored to. Can
  15277. * be one of `"left"`, `"center"` or `"right"`. The exact position
  15278. * also depends on the `labels.x` setting.
  15279. *
  15280. * Angular gauges and solid gauges defaults to `center`.
  15281. *
  15282. * @sample {highcharts} highcharts/yaxis/labels-align-left/
  15283. * Left
  15284. *
  15285. * @type {string}
  15286. * @default {highcharts|highmaps} right
  15287. * @default {highstock} left
  15288. * @validvalue ["left", "center", "right"]
  15289. * @apioption yAxis.labels.align
  15290. */
  15291. /**
  15292. * The x position offset of the label relative to the tick position
  15293. * on the axis. Defaults to -15 for left axis, 15 for right axis.
  15294. *
  15295. * @sample {highcharts} highcharts/xaxis/labels-x/
  15296. * Y axis labels placed on grid lines
  15297. */
  15298. x: -8
  15299. },
  15300. /**
  15301. * @productdesc {highmaps}
  15302. * In Highmaps, the axis line is hidden by default, because the axis is
  15303. * not visible by default.
  15304. *
  15305. * @type {Highcharts.ColorString}
  15306. * @apioption yAxis.lineColor
  15307. */
  15308. /**
  15309. * @sample {highcharts} highcharts/yaxis/max-200/
  15310. * Y axis max of 200
  15311. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  15312. * Y axis max on logarithmic axis
  15313. * @sample {highstock} stock/yaxis/min-max/
  15314. * Fixed min and max on Y axis
  15315. * @sample {highmaps} maps/axis/min-max/
  15316. * Pre-zoomed to a specific area
  15317. *
  15318. * @type {number}
  15319. * @apioption yAxis.max
  15320. */
  15321. /**
  15322. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  15323. * -50 with startOnTick to false
  15324. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  15325. * -50 with startOnTick true by default
  15326. * @sample {highstock} stock/yaxis/min-max/
  15327. * Fixed min and max on Y axis
  15328. * @sample {highmaps} maps/axis/min-max/
  15329. * Pre-zoomed to a specific area
  15330. *
  15331. * @type {number}
  15332. * @apioption yAxis.min
  15333. */
  15334. /**
  15335. * An optional scrollbar to display on the Y axis in response to
  15336. * limiting the minimum an maximum of the axis values.
  15337. *
  15338. * In styled mode, all the presentational options for the scrollbar
  15339. * are replaced by the classes `.highcharts-scrollbar-thumb`,
  15340. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  15341. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  15342. *
  15343. * @sample {highstock} stock/yaxis/scrollbar/
  15344. * Scrollbar on the Y axis
  15345. *
  15346. * @extends scrollbar
  15347. * @since 4.2.6
  15348. * @product highstock
  15349. * @excluding height
  15350. * @apioption yAxis.scrollbar
  15351. */
  15352. /**
  15353. * Enable the scrollbar on the Y axis.
  15354. *
  15355. * @sample {highstock} stock/yaxis/scrollbar/
  15356. * Enabled on Y axis
  15357. *
  15358. * @type {boolean}
  15359. * @default false
  15360. * @since 4.2.6
  15361. * @product highstock
  15362. * @apioption yAxis.scrollbar.enabled
  15363. */
  15364. /**
  15365. * Pixel margin between the scrollbar and the axis elements.
  15366. *
  15367. * @type {number}
  15368. * @default 10
  15369. * @since 4.2.6
  15370. * @product highstock
  15371. * @apioption yAxis.scrollbar.margin
  15372. */
  15373. /**
  15374. * Whether to show the scrollbar when it is fully zoomed out at max
  15375. * range. Setting it to `false` on the Y axis makes the scrollbar stay
  15376. * hidden until the user zooms in, like common in browsers.
  15377. *
  15378. * @type {boolean}
  15379. * @default true
  15380. * @since 4.2.6
  15381. * @product highstock
  15382. * @apioption yAxis.scrollbar.showFull
  15383. */
  15384. /**
  15385. * The width of a vertical scrollbar or height of a horizontal
  15386. * scrollbar. Defaults to 20 on touch devices.
  15387. *
  15388. * @type {number}
  15389. * @default 14
  15390. * @since 4.2.6
  15391. * @product highstock
  15392. * @apioption yAxis.scrollbar.size
  15393. */
  15394. /**
  15395. * Z index of the scrollbar elements.
  15396. *
  15397. * @type {number}
  15398. * @default 3
  15399. * @since 4.2.6
  15400. * @product highstock
  15401. * @apioption yAxis.scrollbar.zIndex
  15402. */
  15403. /**
  15404. * A soft maximum for the axis. If the series data maximum is less
  15405. * than this, the axis will stay at this maximum, but if the series
  15406. * data maximum is higher, the axis will flex to show all data.
  15407. *
  15408. * **Note**: The [series.softThreshold](
  15409. * #plotOptions.series.softThreshold) option takes precedence over this
  15410. * option.
  15411. *
  15412. * @sample highcharts/yaxis/softmin-softmax/
  15413. * Soft min and max
  15414. *
  15415. * @type {number}
  15416. * @since 5.0.1
  15417. * @product highcharts highstock gantt
  15418. * @apioption yAxis.softMax
  15419. */
  15420. /**
  15421. * A soft minimum for the axis. If the series data minimum is greater
  15422. * than this, the axis will stay at this minimum, but if the series
  15423. * data minimum is lower, the axis will flex to show all data.
  15424. *
  15425. * **Note**: The [series.softThreshold](
  15426. * #plotOptions.series.softThreshold) option takes precedence over this
  15427. * option.
  15428. *
  15429. * @sample highcharts/yaxis/softmin-softmax/
  15430. * Soft min and max
  15431. *
  15432. * @type {number}
  15433. * @since 5.0.1
  15434. * @product highcharts highstock gantt
  15435. * @apioption yAxis.softMin
  15436. */
  15437. /**
  15438. * Defines the horizontal alignment of the stack total label. Can be one
  15439. * of `"left"`, `"center"` or `"right"`. The default value is calculated
  15440. * at runtime and depends on orientation and whether the stack is
  15441. * positive or negative.
  15442. *
  15443. * @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
  15444. * Aligned to the left
  15445. * @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
  15446. * Aligned in center
  15447. * @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
  15448. * Aligned to the right
  15449. *
  15450. * @type {Highcharts.AlignType}
  15451. * @since 2.1.5
  15452. * @product highcharts
  15453. * @apioption yAxis.stackLabels.align
  15454. */
  15455. /**
  15456. * A [format string](http://docs.highcharts.com/#formatting) for the
  15457. * data label. Available variables are the same as for `formatter`.
  15458. *
  15459. * @type {string}
  15460. * @default {total}
  15461. * @since 3.0.2
  15462. * @product highcharts highstock
  15463. * @apioption yAxis.stackLabels.format
  15464. */
  15465. /**
  15466. * Rotation of the labels in degrees.
  15467. *
  15468. * @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
  15469. * Labels rotated 45°
  15470. *
  15471. * @type {number}
  15472. * @default 0
  15473. * @since 2.1.5
  15474. * @product highcharts
  15475. * @apioption yAxis.stackLabels.rotation
  15476. */
  15477. /**
  15478. * The text alignment for the label. While `align` determines where the
  15479. * texts anchor point is placed with regards to the stack, `textAlign`
  15480. * determines how the text is aligned against its anchor point. Possible
  15481. * values are `"left"`, `"center"` and `"right"`. The default value is
  15482. * calculated at runtime and depends on orientation and whether the
  15483. * stack is positive or negative.
  15484. *
  15485. * @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
  15486. * Label in center position but text-aligned left
  15487. *
  15488. * @type {Highcharts.AlignType}
  15489. * @since 2.1.5
  15490. * @product highcharts
  15491. * @apioption yAxis.stackLabels.textAlign
  15492. */
  15493. /**
  15494. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  15495. * to render the labels.
  15496. *
  15497. * @type {boolean}
  15498. * @default false
  15499. * @since 3.0
  15500. * @product highcharts highstock
  15501. * @apioption yAxis.stackLabels.useHTML
  15502. */
  15503. /**
  15504. * Defines the vertical alignment of the stack total label. Can be one
  15505. * of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
  15506. * at runtime and depends on orientation and whether the stack is
  15507. * positive or negative.
  15508. *
  15509. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
  15510. * Vertically aligned top
  15511. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
  15512. * Vertically aligned middle
  15513. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
  15514. * Vertically aligned bottom
  15515. *
  15516. * @type {Highcharts.VerticalAlignType}
  15517. * @since 2.1.5
  15518. * @product highcharts
  15519. * @apioption yAxis.stackLabels.verticalAlign
  15520. */
  15521. /**
  15522. * The x position offset of the label relative to the left of the
  15523. * stacked bar. The default value is calculated at runtime and depends
  15524. * on orientation and whether the stack is positive or negative.
  15525. *
  15526. * @sample {highcharts} highcharts/yaxis/stacklabels-x/
  15527. * Stack total labels with x offset
  15528. *
  15529. * @type {number}
  15530. * @since 2.1.5
  15531. * @product highcharts
  15532. * @apioption yAxis.stackLabels.x
  15533. */
  15534. /**
  15535. * The y position offset of the label relative to the tick position
  15536. * on the axis. The default value is calculated at runtime and depends
  15537. * on orientation and whether the stack is positive or negative.
  15538. *
  15539. * @sample {highcharts} highcharts/yaxis/stacklabels-y/
  15540. * Stack total labels with y offset
  15541. *
  15542. * @type {number}
  15543. * @since 2.1.5
  15544. * @product highcharts
  15545. * @apioption yAxis.stackLabels.y
  15546. */
  15547. /**
  15548. * Whether to force the axis to start on a tick. Use this option with
  15549. * the `maxPadding` option to control the axis start.
  15550. *
  15551. * @sample {highcharts} highcharts/xaxis/startontick-false/
  15552. * False by default
  15553. * @sample {highcharts} highcharts/xaxis/startontick-true/
  15554. * True
  15555. * @sample {highstock} stock/xaxis/endontick/
  15556. * False for Y axis
  15557. *
  15558. * @since 1.2.0
  15559. * @product highcharts highstock gantt
  15560. */
  15561. startOnTick: true,
  15562. title: {
  15563. /**
  15564. * The pixel distance between the axis labels and the title.
  15565. * Positive values are outside the axis line, negative are inside.
  15566. *
  15567. * @sample {highcharts} highcharts/xaxis/title-margin/
  15568. * Y axis title margin of 60
  15569. *
  15570. * @type {number}
  15571. * @default 40
  15572. * @apioption yAxis.title.margin
  15573. */
  15574. /**
  15575. * The rotation of the text in degrees. 0 is horizontal, 270 is
  15576. * vertical reading from bottom to top.
  15577. *
  15578. * @sample {highcharts} highcharts/yaxis/title-offset/
  15579. * Horizontal
  15580. */
  15581. rotation: 270,
  15582. /**
  15583. * The actual text of the axis title. Horizontal texts can contain
  15584. * HTML, but rotated texts are painted using vector techniques and
  15585. * must be clean text. The Y axis title is disabled by setting the
  15586. * `text` option to `undefined`.
  15587. *
  15588. * @sample {highcharts} highcharts/xaxis/title-text/
  15589. * Custom HTML
  15590. *
  15591. * @type {string|null}
  15592. * @default {highcharts} Values
  15593. * @default {highstock} undefined
  15594. * @product highcharts highstock gantt
  15595. */
  15596. text: 'Values'
  15597. },
  15598. /**
  15599. * The top position of the Y axis. If it's a number, it is interpreted
  15600. * as pixel position relative to the chart.
  15601. *
  15602. * Since Highstock 2: If it's a percentage string, it is interpreted
  15603. * as percentages of the plot height, offset from plot area top.
  15604. *
  15605. * @see [yAxis.height](#yAxis.height)
  15606. *
  15607. * @sample {highstock} stock/demo/candlestick-and-volume/
  15608. * Percentage height panes
  15609. *
  15610. * @type {number|string}
  15611. * @product highstock
  15612. * @apioption yAxis.top
  15613. */
  15614. /**
  15615. * The stack labels show the total value for each bar in a stacked
  15616. * column or bar chart. The label will be placed on top of positive
  15617. * columns and below negative columns. In case of an inverted column
  15618. * chart or a bar chart the label is placed to the right of positive
  15619. * bars and to the left of negative bars.
  15620. *
  15621. * @product highcharts
  15622. */
  15623. stackLabels: {
  15624. /**
  15625. * Allow the stack labels to overlap.
  15626. *
  15627. * @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
  15628. * Default false
  15629. *
  15630. * @since 5.0.13
  15631. * @product highcharts
  15632. */
  15633. allowOverlap: false,
  15634. /**
  15635. * Enable or disable the stack total labels.
  15636. *
  15637. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
  15638. * Enabled stack total labels
  15639. *
  15640. * @since 2.1.5
  15641. * @product highcharts
  15642. */
  15643. enabled: false,
  15644. /**
  15645. * Callback JavaScript function to format the label. The value is
  15646. * given by `this.total`.
  15647. *
  15648. * @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
  15649. * Added units to stack total value
  15650. *
  15651. * @type {Highcharts.FormatterCallbackFunction<object>}
  15652. * @since 2.1.5
  15653. * @product highcharts
  15654. */
  15655. formatter: function () {
  15656. return H.numberFormat(this.total, -1);
  15657. },
  15658. /**
  15659. * CSS styles for the label.
  15660. *
  15661. * In styled mode, the styles are set in the
  15662. * `.highcharts-stack-label` class.
  15663. *
  15664. * @sample {highcharts} highcharts/yaxis/stacklabels-style/
  15665. * Red stack total labels
  15666. *
  15667. * @type {Highcharts.CSSObject}
  15668. * @default {"color": "#666666", "fontSize": "11px", "fontWeight": "bold", "textOutline": "1px contrast"}
  15669. * @since 2.1.5
  15670. * @product highcharts
  15671. */
  15672. style: {
  15673. /** @ignore-option */
  15674. color: '#000000',
  15675. /** @ignore-option */
  15676. fontSize: '11px',
  15677. /** @ignore-option */
  15678. fontWeight: 'bold',
  15679. /** @ignore-option */
  15680. textOutline: '1px contrast'
  15681. }
  15682. },
  15683. gridLineWidth: 1,
  15684. lineWidth: 0
  15685. // tickWidth: 0
  15686. },
  15687. /**
  15688. * The Z axis or depth axis for 3D plots.
  15689. *
  15690. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  15691. * access to the axis.
  15692. *
  15693. * @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
  15694. * Z-Axis with Categories
  15695. * @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
  15696. * Z-Axis with styling
  15697. *
  15698. * @type {*|Array<*>}
  15699. * @extends xAxis
  15700. * @since 5.0.0
  15701. * @product highcharts
  15702. * @excluding breaks, crosshair, lineColor, lineWidth, nameToX, showEmpty
  15703. * @apioption zAxis
  15704. */
  15705. // This variable extends the defaultOptions for left axes.
  15706. defaultLeftAxisOptions: {
  15707. labels: {
  15708. x: -15
  15709. },
  15710. title: {
  15711. rotation: 270
  15712. }
  15713. },
  15714. // This variable extends the defaultOptions for right axes.
  15715. defaultRightAxisOptions: {
  15716. labels: {
  15717. x: 15
  15718. },
  15719. title: {
  15720. rotation: 90
  15721. }
  15722. },
  15723. // This variable extends the defaultOptions for bottom axes.
  15724. defaultBottomAxisOptions: {
  15725. labels: {
  15726. autoRotation: [-45],
  15727. x: 0
  15728. // overflow: undefined,
  15729. // staggerLines: null
  15730. },
  15731. title: {
  15732. rotation: 0
  15733. }
  15734. },
  15735. // This variable extends the defaultOptions for top axes.
  15736. defaultTopAxisOptions: {
  15737. labels: {
  15738. autoRotation: [-45],
  15739. x: 0
  15740. // overflow: undefined
  15741. // staggerLines: null
  15742. },
  15743. title: {
  15744. rotation: 0
  15745. }
  15746. },
  15747. /**
  15748. * Overrideable function to initialize the axis.
  15749. *
  15750. * @see {@link Axis}
  15751. *
  15752. * @function Highcharts.Axis#init
  15753. *
  15754. * @param {Highcharts.Chart} chart
  15755. *
  15756. * @param {Highcharts.Options} userOptions
  15757. *
  15758. * @fires Highcharts.Axis#event:afterInit
  15759. * @fires Highcharts.Axis#event:init
  15760. */
  15761. init: function (chart, userOptions) {
  15762. var isXAxis = userOptions.isX,
  15763. axis = this;
  15764. /**
  15765. * The Chart that the axis belongs to.
  15766. *
  15767. * @name Highcharts.Axis#chart
  15768. * @type {Highcharts.Chart}
  15769. */
  15770. axis.chart = chart;
  15771. /**
  15772. * Whether the axis is horizontal.
  15773. *
  15774. * @name Highcharts.Axis#horiz
  15775. * @type {boolean}
  15776. */
  15777. axis.horiz = chart.inverted && !axis.isZAxis ? !isXAxis : isXAxis;
  15778. /**
  15779. * Whether the axis is the x-axis.
  15780. *
  15781. * @name Highcharts.Axis#isXAxis
  15782. * @type {boolean|undefined}
  15783. */
  15784. axis.isXAxis = isXAxis;
  15785. /**
  15786. * The collection where the axis belongs, for example `xAxis`, `yAxis`
  15787. * or `colorAxis`. Corresponds to properties on Chart, for example
  15788. * {@link Chart.xAxis}.
  15789. *
  15790. * @name Highcharts.Axis#coll
  15791. * @type {string}
  15792. */
  15793. axis.coll = axis.coll || (isXAxis ? 'xAxis' : 'yAxis');
  15794. fireEvent(this, 'init', { userOptions: userOptions });
  15795. axis.opposite = userOptions.opposite; // needed in setOptions
  15796. /**
  15797. * The side on which the axis is rendered. 0 is top, 1 is right, 2
  15798. * is bottom and 3 is left.
  15799. *
  15800. * @name Highcharts.Axis#side
  15801. * @type {number}
  15802. */
  15803. axis.side = userOptions.side || (axis.horiz ?
  15804. (axis.opposite ? 0 : 2) : // top : bottom
  15805. (axis.opposite ? 1 : 3)); // right : left
  15806. axis.setOptions(userOptions);
  15807. var options = this.options,
  15808. type = options.type,
  15809. isDatetimeAxis = type === 'datetime';
  15810. axis.labelFormatter = options.labels.formatter ||
  15811. // can be overwritten by dynamic format
  15812. axis.defaultLabelFormatter;
  15813. // Flag, stagger lines or not
  15814. axis.userOptions = userOptions;
  15815. axis.minPixelPadding = 0;
  15816. /**
  15817. * Whether the axis is reversed. Based on the `axis.reversed`,
  15818. * option, but inverted charts have reversed xAxis by default.
  15819. *
  15820. * @name Highcharts.Axis#reversed
  15821. * @type {boolean}
  15822. */
  15823. axis.reversed = options.reversed;
  15824. axis.visible = options.visible !== false;
  15825. axis.zoomEnabled = options.zoomEnabled !== false;
  15826. // Initial categories
  15827. axis.hasNames = type === 'category' || options.categories === true;
  15828. axis.categories = options.categories || axis.hasNames;
  15829. if (!axis.names) { // Preserve on update (#3830)
  15830. axis.names = [];
  15831. axis.names.keys = {};
  15832. }
  15833. // Placeholder for plotlines and plotbands groups
  15834. axis.plotLinesAndBandsGroups = {};
  15835. // Shorthand types
  15836. axis.isLog = type === 'logarithmic';
  15837. axis.isDatetimeAxis = isDatetimeAxis;
  15838. axis.positiveValuesOnly = axis.isLog && !axis.allowNegativeLog;
  15839. // Flag, if axis is linked to another axis
  15840. axis.isLinked = defined(options.linkedTo);
  15841. /**
  15842. * List of major ticks mapped by postition on axis.
  15843. *
  15844. * @see {@link Highcharts.Tick}
  15845. *
  15846. * @private
  15847. * @name Highcharts.Axis#ticks
  15848. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  15849. */
  15850. axis.ticks = {};
  15851. axis.labelEdge = [];
  15852. /**
  15853. * List of minor ticks mapped by position on the axis.
  15854. *
  15855. * @see {@link Highcharts.Tick}
  15856. *
  15857. * @private
  15858. * @name Highcharts.Axis#minorTicks
  15859. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  15860. */
  15861. axis.minorTicks = {};
  15862. // List of plotLines/Bands
  15863. axis.plotLinesAndBands = [];
  15864. // Alternate bands
  15865. axis.alternateBands = {};
  15866. // Axis metrics
  15867. axis.len = 0;
  15868. axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
  15869. axis.range = options.range;
  15870. axis.offset = options.offset || 0;
  15871. // Dictionary for stacks
  15872. axis.stacks = {};
  15873. axis.oldStacks = {};
  15874. axis.stacksTouched = 0;
  15875. /**
  15876. * The maximum value of the axis. In a logarithmic axis, this is the
  15877. * logarithm of the real value, and the real value can be obtained from
  15878. * {@link Axis#getExtremes}.
  15879. *
  15880. * @name Highcharts.Axis#max
  15881. * @type {number|null}
  15882. */
  15883. axis.max = null;
  15884. /**
  15885. * The minimum value of the axis. In a logarithmic axis, this is the
  15886. * logarithm of the real value, and the real value can be obtained from
  15887. * {@link Axis#getExtremes}.
  15888. *
  15889. * @name Highcharts.Axis#min
  15890. * @type {number|null}
  15891. */
  15892. axis.min = null;
  15893. /**
  15894. * The processed crosshair options.
  15895. *
  15896. * @name Highcharts.Axis#crosshair
  15897. * @type {boolean|Highcharts.AxisCrosshairOptions}
  15898. */
  15899. axis.crosshair = pick(
  15900. options.crosshair,
  15901. splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1],
  15902. false
  15903. );
  15904. var events = axis.options.events;
  15905. // Register. Don't add it again on Axis.update().
  15906. if (chart.axes.indexOf(axis) === -1) { //
  15907. if (isXAxis) { // #2713
  15908. chart.axes.splice(chart.xAxis.length, 0, axis);
  15909. } else {
  15910. chart.axes.push(axis);
  15911. }
  15912. chart[axis.coll].push(axis);
  15913. }
  15914. /**
  15915. * All series associated to the axis.
  15916. *
  15917. * @name Highcharts.Axis#series
  15918. * @type {Array<Highcharts.Series>}
  15919. */
  15920. axis.series = axis.series || []; // populated by Series
  15921. // Reversed axis
  15922. if (
  15923. chart.inverted &&
  15924. !axis.isZAxis &&
  15925. isXAxis &&
  15926. axis.reversed === undefined
  15927. ) {
  15928. axis.reversed = true;
  15929. }
  15930. // register event listeners
  15931. objectEach(events, function (event, eventType) {
  15932. addEvent(axis, eventType, event);
  15933. });
  15934. // extend logarithmic axis
  15935. axis.lin2log = options.linearToLogConverter || axis.lin2log;
  15936. if (axis.isLog) {
  15937. axis.val2lin = axis.log2lin;
  15938. axis.lin2val = axis.lin2log;
  15939. }
  15940. fireEvent(this, 'afterInit');
  15941. },
  15942. /**
  15943. * Merge and set options.
  15944. *
  15945. * @private
  15946. * @function Highcharts.Axis#setOptions
  15947. *
  15948. * @param {Highcharts.AxisOptions} userOptions
  15949. *
  15950. * @fires Highcharts.Axis#event:afterSetOptions
  15951. */
  15952. setOptions: function (userOptions) {
  15953. this.options = merge(
  15954. this.defaultOptions,
  15955. this.coll === 'yAxis' && this.defaultYAxisOptions,
  15956. [
  15957. this.defaultTopAxisOptions,
  15958. this.defaultRightAxisOptions,
  15959. this.defaultBottomAxisOptions,
  15960. this.defaultLeftAxisOptions
  15961. ][this.side],
  15962. merge(
  15963. defaultOptions[this.coll], // if set in setOptions (#1053)
  15964. userOptions
  15965. )
  15966. );
  15967. fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
  15968. },
  15969. /**
  15970. * The default label formatter. The context is a special config object for
  15971. * the label. In apps, use the
  15972. * [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
  15973. * instead except when a modification is needed.
  15974. * @private
  15975. */
  15976. defaultLabelFormatter: function () {
  15977. var axis = this.axis,
  15978. value = this.value,
  15979. time = axis.chart.time,
  15980. categories = axis.categories,
  15981. dateTimeLabelFormat = this.dateTimeLabelFormat,
  15982. lang = defaultOptions.lang,
  15983. numericSymbols = lang.numericSymbols,
  15984. numSymMagnitude = lang.numericSymbolMagnitude || 1000,
  15985. i = numericSymbols && numericSymbols.length,
  15986. multi,
  15987. ret,
  15988. formatOption = axis.options.labels.format,
  15989. // make sure the same symbol is added for all labels on a linear
  15990. // axis
  15991. numericSymbolDetector = axis.isLog ?
  15992. Math.abs(value) :
  15993. axis.tickInterval;
  15994. if (formatOption) {
  15995. ret = format(formatOption, this, time);
  15996. } else if (categories) {
  15997. ret = value;
  15998. } else if (dateTimeLabelFormat) { // datetime axis
  15999. ret = time.dateFormat(dateTimeLabelFormat, value);
  16000. } else if (i && numericSymbolDetector >= 1000) {
  16001. // Decide whether we should add a numeric symbol like k (thousands)
  16002. // or M (millions). If we are to enable this in tooltip or other
  16003. // places as well, we can move this logic to the numberFormatter and
  16004. // enable it by a parameter.
  16005. while (i-- && ret === undefined) {
  16006. multi = Math.pow(numSymMagnitude, i + 1);
  16007. if (
  16008. // Only accept a numeric symbol when the distance is more
  16009. // than a full unit. So for example if the symbol is k, we
  16010. // don't accept numbers like 0.5k.
  16011. numericSymbolDetector >= multi &&
  16012. // Accept one decimal before the symbol. Accepts 0.5k but
  16013. // not 0.25k. How does this work with the previous?
  16014. (value * 10) % multi === 0 &&
  16015. numericSymbols[i] !== null &&
  16016. value !== 0
  16017. ) { // #5480
  16018. ret = H.numberFormat(value / multi, -1) + numericSymbols[i];
  16019. }
  16020. }
  16021. }
  16022. if (ret === undefined) {
  16023. if (Math.abs(value) >= 10000) { // add thousands separators
  16024. ret = H.numberFormat(value, -1);
  16025. } else { // small numbers
  16026. ret = H.numberFormat(value, -1, undefined, ''); // #2466
  16027. }
  16028. }
  16029. return ret;
  16030. },
  16031. /**
  16032. * Get the minimum and maximum for the series of each axis. The function
  16033. * analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
  16034. * @private
  16035. * @fires Highcharts.Axis#event:afterGetSeriesExtremes
  16036. * @fires Highcharts.Axis#event:getSeriesExtremes
  16037. */
  16038. getSeriesExtremes: function () {
  16039. var axis = this,
  16040. chart = axis.chart;
  16041. fireEvent(this, 'getSeriesExtremes', null, function () {
  16042. axis.hasVisibleSeries = false;
  16043. // Reset properties in case we're redrawing (#3353)
  16044. axis.dataMin = axis.dataMax = axis.threshold = null;
  16045. axis.softThreshold = !axis.isXAxis;
  16046. if (axis.buildStacks) {
  16047. axis.buildStacks();
  16048. }
  16049. // loop through this axis' series
  16050. axis.series.forEach(function (series) {
  16051. if (series.visible || !chart.options.chart.ignoreHiddenSeries) {
  16052. var seriesOptions = series.options,
  16053. xData,
  16054. threshold = seriesOptions.threshold,
  16055. seriesDataMin,
  16056. seriesDataMax;
  16057. axis.hasVisibleSeries = true;
  16058. // Validate threshold in logarithmic axes
  16059. if (axis.positiveValuesOnly && threshold <= 0) {
  16060. threshold = null;
  16061. }
  16062. // Get dataMin and dataMax for X axes
  16063. if (axis.isXAxis) {
  16064. xData = series.xData;
  16065. if (xData.length) {
  16066. // If xData contains values which is not numbers,
  16067. // then filter them out. To prevent performance hit,
  16068. // we only do this after we have already found
  16069. // seriesDataMin because in most cases all data is
  16070. // valid. #5234.
  16071. seriesDataMin = arrayMin(xData);
  16072. seriesDataMax = arrayMax(xData);
  16073. if (
  16074. !isNumber(seriesDataMin) &&
  16075. !(seriesDataMin instanceof Date) // #5010
  16076. ) {
  16077. xData = xData.filter(isNumber);
  16078. // Do it again with valid data
  16079. seriesDataMin = arrayMin(xData);
  16080. seriesDataMax = arrayMax(xData);
  16081. }
  16082. if (xData.length) {
  16083. axis.dataMin = Math.min(
  16084. pick(axis.dataMin, xData[0], seriesDataMin),
  16085. seriesDataMin
  16086. );
  16087. axis.dataMax = Math.max(
  16088. pick(axis.dataMax, xData[0], seriesDataMax),
  16089. seriesDataMax
  16090. );
  16091. }
  16092. }
  16093. // Get dataMin and dataMax for Y axes, as well as handle
  16094. // stacking and processed data
  16095. } else {
  16096. // Get this particular series extremes
  16097. series.getExtremes();
  16098. seriesDataMax = series.dataMax;
  16099. seriesDataMin = series.dataMin;
  16100. // Get the dataMin and dataMax so far. If percentage is
  16101. // used, the min and max are always 0 and 100. If
  16102. // seriesDataMin and seriesDataMax is null, then series
  16103. // doesn't have active y data, we continue with nulls
  16104. if (defined(seriesDataMin) && defined(seriesDataMax)) {
  16105. axis.dataMin = Math.min(
  16106. pick(axis.dataMin, seriesDataMin),
  16107. seriesDataMin
  16108. );
  16109. axis.dataMax = Math.max(
  16110. pick(axis.dataMax, seriesDataMax),
  16111. seriesDataMax
  16112. );
  16113. }
  16114. // Adjust to threshold
  16115. if (defined(threshold)) {
  16116. axis.threshold = threshold;
  16117. }
  16118. // If any series has a hard threshold, it takes
  16119. // precedence
  16120. if (
  16121. !seriesOptions.softThreshold ||
  16122. axis.positiveValuesOnly
  16123. ) {
  16124. axis.softThreshold = false;
  16125. }
  16126. }
  16127. }
  16128. });
  16129. });
  16130. fireEvent(this, 'afterGetSeriesExtremes');
  16131. },
  16132. /**
  16133. * Translate from axis value to pixel position on the chart, or back. Use
  16134. * the `toPixels` and `toValue` functions in applications.
  16135. * @private
  16136. */
  16137. translate: function (
  16138. val,
  16139. backwards,
  16140. cvsCoord,
  16141. old,
  16142. handleLog,
  16143. pointPlacement
  16144. ) {
  16145. var axis = this.linkedParent || this, // #1417
  16146. sign = 1,
  16147. cvsOffset = 0,
  16148. localA = old ? axis.oldTransA : axis.transA,
  16149. localMin = old ? axis.oldMin : axis.min,
  16150. returnValue,
  16151. minPixelPadding = axis.minPixelPadding,
  16152. doPostTranslate = (
  16153. axis.isOrdinal ||
  16154. axis.isBroken ||
  16155. (axis.isLog && handleLog)
  16156. ) && axis.lin2val;
  16157. if (!localA) {
  16158. localA = axis.transA;
  16159. }
  16160. // In vertical axes, the canvas coordinates start from 0 at the top like
  16161. // in SVG.
  16162. if (cvsCoord) {
  16163. sign *= -1; // canvas coordinates inverts the value
  16164. cvsOffset = axis.len;
  16165. }
  16166. // Handle reversed axis
  16167. if (axis.reversed) {
  16168. sign *= -1;
  16169. cvsOffset -= sign * (axis.sector || axis.len);
  16170. }
  16171. // From pixels to value
  16172. if (backwards) { // reverse translation
  16173. val = val * sign + cvsOffset;
  16174. val -= minPixelPadding;
  16175. returnValue = val / localA + localMin; // from chart pixel to value
  16176. if (doPostTranslate) { // log and ordinal axes
  16177. returnValue = axis.lin2val(returnValue);
  16178. }
  16179. // From value to pixels
  16180. } else {
  16181. if (doPostTranslate) { // log and ordinal axes
  16182. val = axis.val2lin(val);
  16183. }
  16184. returnValue = isNumber(localMin) ?
  16185. (
  16186. sign * (val - localMin) * localA +
  16187. cvsOffset +
  16188. (sign * minPixelPadding) +
  16189. (isNumber(pointPlacement) ? localA * pointPlacement : 0)
  16190. ) :
  16191. undefined;
  16192. }
  16193. return returnValue;
  16194. },
  16195. /**
  16196. * Translate a value in terms of axis units into pixels within the chart.
  16197. *
  16198. * @function Highcharts.Axis#toPixels
  16199. *
  16200. * @param {number} value
  16201. * A value in terms of axis units.
  16202. *
  16203. * @param {boolean} paneCoordinates
  16204. * Whether to return the pixel coordinate relative to the chart or
  16205. * just the axis/pane itself.
  16206. *
  16207. * @return {number}
  16208. * Pixel position of the value on the chart or axis.
  16209. */
  16210. toPixels: function (value, paneCoordinates) {
  16211. return this.translate(value, false, !this.horiz, null, true) +
  16212. (paneCoordinates ? 0 : this.pos);
  16213. },
  16214. /**
  16215. * Translate a pixel position along the axis to a value in terms of axis
  16216. * units.
  16217. *
  16218. * @function Highcharts.Axis#toValue
  16219. *
  16220. * @param {number} pixel
  16221. * The pixel value coordinate.
  16222. *
  16223. * @param {boolean} paneCoordiantes
  16224. * Whether the input pixel is relative to the chart or just the
  16225. * axis/pane itself.
  16226. *
  16227. * @return {number}
  16228. * The axis value.
  16229. */
  16230. toValue: function (pixel, paneCoordinates) {
  16231. return this.translate(
  16232. pixel - (paneCoordinates ? 0 : this.pos),
  16233. true,
  16234. !this.horiz,
  16235. null,
  16236. true
  16237. );
  16238. },
  16239. /**
  16240. * Create the path for a plot line that goes from the given value on
  16241. * this axis, across the plot to the opposite side. Also used internally for
  16242. * grid lines and crosshairs.
  16243. *
  16244. * @function Highcharts.Axis#getPlotLinePath
  16245. *
  16246. * @param {number} value
  16247. * Axis value.
  16248. *
  16249. * @param {number} [lineWidth=1]
  16250. * Used for calculation crisp line coordinates.
  16251. *
  16252. * @param {boolean} [old=false]
  16253. * Use old coordinates (for resizing and rescaling).
  16254. *
  16255. * @param {boolean|string} [force=false]
  16256. * If `false`, the function will return null when it falls outside
  16257. * the axis bounds. If `true`, the function will return a path
  16258. * aligned to the plot area sides if it falls outside. If `pass`, it
  16259. * will return a path outside.
  16260. *
  16261. * @param {number} [translatedValue]
  16262. * If given, return the plot line path of a pixel position on the
  16263. * axis.
  16264. *
  16265. * @return {Array<string|number>}
  16266. * The SVG path definition for the plot line.
  16267. */
  16268. getPlotLinePath: function (value, lineWidth, old, force, translatedValue) {
  16269. var axis = this,
  16270. chart = axis.chart,
  16271. axisLeft = axis.left,
  16272. axisTop = axis.top,
  16273. x1,
  16274. y1,
  16275. x2,
  16276. y2,
  16277. cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
  16278. cWidth = (old && chart.oldChartWidth) || chart.chartWidth,
  16279. skip,
  16280. transB = axis.transB,
  16281. evt,
  16282. /**
  16283. * Check if x is between a and b. If not, either move to a/b
  16284. * or skip, depending on the force parameter.
  16285. */
  16286. between = function (x, a, b) {
  16287. if (force !== 'pass' && x < a || x > b) {
  16288. if (force) {
  16289. x = Math.min(Math.max(a, x), b);
  16290. } else {
  16291. skip = true;
  16292. }
  16293. }
  16294. return x;
  16295. };
  16296. evt = {
  16297. value: value,
  16298. lineWidth: lineWidth,
  16299. old: old,
  16300. force: force,
  16301. translatedValue: translatedValue
  16302. };
  16303. fireEvent(this, 'getPlotLinePath', evt, function (e) {
  16304. translatedValue = pick(
  16305. translatedValue,
  16306. axis.translate(value, null, null, old)
  16307. );
  16308. // Keep the translated value within sane bounds, and avoid Infinity
  16309. // to fail the isNumber test (#7709).
  16310. translatedValue = Math.min(Math.max(-1e5, translatedValue), 1e5);
  16311. x1 = x2 = Math.round(translatedValue + transB);
  16312. y1 = y2 = Math.round(cHeight - translatedValue - transB);
  16313. if (!isNumber(translatedValue)) { // no min or max
  16314. skip = true;
  16315. force = false; // #7175, don't force it when path is invalid
  16316. } else if (axis.horiz) {
  16317. y1 = axisTop;
  16318. y2 = cHeight - axis.bottom;
  16319. x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
  16320. } else {
  16321. x1 = axisLeft;
  16322. x2 = cWidth - axis.right;
  16323. y1 = y2 = between(y1, axisTop, axisTop + axis.height);
  16324. }
  16325. e.path = skip && !force ?
  16326. null :
  16327. chart.renderer.crispLine(
  16328. ['M', x1, y1, 'L', x2, y2],
  16329. lineWidth || 1
  16330. );
  16331. });
  16332. return evt.path;
  16333. },
  16334. /**
  16335. * Internal function to et the tick positions of a linear axis to round
  16336. * values like whole tens or every five.
  16337. *
  16338. * @function Highcharts.Axis#getLinearTickPositions
  16339. *
  16340. * @param {number} tickInterval
  16341. * The normalized tick interval.
  16342. *
  16343. * @param {number} min
  16344. * Axis minimum.
  16345. *
  16346. * @param {number} max
  16347. * Axis maximum.
  16348. *
  16349. * @return {Array<number>}
  16350. * An array of axis values where ticks should be placed.
  16351. */
  16352. getLinearTickPositions: function (tickInterval, min, max) {
  16353. var pos,
  16354. lastPos,
  16355. roundedMin =
  16356. correctFloat(Math.floor(min / tickInterval) * tickInterval),
  16357. roundedMax =
  16358. correctFloat(Math.ceil(max / tickInterval) * tickInterval),
  16359. tickPositions = [],
  16360. precision;
  16361. // When the precision is higher than what we filter out in
  16362. // correctFloat, skip it (#6183).
  16363. if (correctFloat(roundedMin + tickInterval) === roundedMin) {
  16364. precision = 20;
  16365. }
  16366. // For single points, add a tick regardless of the relative position
  16367. // (#2662, #6274)
  16368. if (this.single) {
  16369. return [min];
  16370. }
  16371. // Populate the intermediate values
  16372. pos = roundedMin;
  16373. while (pos <= roundedMax) {
  16374. // Place the tick on the rounded value
  16375. tickPositions.push(pos);
  16376. // Always add the raw tickInterval, not the corrected one.
  16377. pos = correctFloat(
  16378. pos + tickInterval,
  16379. precision
  16380. );
  16381. // If the interval is not big enough in the current min - max range
  16382. // to actually increase the loop variable, we need to break out to
  16383. // prevent endless loop. Issue #619
  16384. if (pos === lastPos) {
  16385. break;
  16386. }
  16387. // Record the last value
  16388. lastPos = pos;
  16389. }
  16390. return tickPositions;
  16391. },
  16392. /**
  16393. * Resolve the new minorTicks/minorTickInterval options into the legacy
  16394. * loosely typed minorTickInterval option.
  16395. *
  16396. * @function Highcharts.Axis#getMinorTickInterval
  16397. *
  16398. * @return {number|"auto"|null}
  16399. */
  16400. getMinorTickInterval: function () {
  16401. var options = this.options;
  16402. if (options.minorTicks === true) {
  16403. return pick(options.minorTickInterval, 'auto');
  16404. }
  16405. if (options.minorTicks === false) {
  16406. return null;
  16407. }
  16408. return options.minorTickInterval;
  16409. },
  16410. /**
  16411. * Internal function to return the minor tick positions. For logarithmic
  16412. * axes, the same logic as for major ticks is reused.
  16413. *
  16414. * @function Highcharts.Axis#getMinorTickPositions
  16415. *
  16416. * @return {Array<number>}
  16417. * An array of axis values where ticks should be placed.
  16418. */
  16419. getMinorTickPositions: function () {
  16420. var axis = this,
  16421. options = axis.options,
  16422. tickPositions = axis.tickPositions,
  16423. minorTickInterval = axis.minorTickInterval,
  16424. minorTickPositions = [],
  16425. pos,
  16426. pointRangePadding = axis.pointRangePadding || 0,
  16427. min = axis.min - pointRangePadding, // #1498
  16428. max = axis.max + pointRangePadding, // #1498
  16429. range = max - min;
  16430. // If minor ticks get too dense, they are hard to read, and may cause
  16431. // long running script. So we don't draw them.
  16432. if (range && range / minorTickInterval < axis.len / 3) { // #3875
  16433. if (axis.isLog) {
  16434. // For each interval in the major ticks, compute the minor ticks
  16435. // separately.
  16436. this.paddedTicks.forEach(function (pos, i, paddedTicks) {
  16437. if (i) {
  16438. minorTickPositions.push.apply(
  16439. minorTickPositions,
  16440. axis.getLogTickPositions(
  16441. minorTickInterval,
  16442. paddedTicks[i - 1],
  16443. paddedTicks[i],
  16444. true
  16445. )
  16446. );
  16447. }
  16448. });
  16449. } else if (
  16450. axis.isDatetimeAxis &&
  16451. this.getMinorTickInterval() === 'auto'
  16452. ) { // #1314
  16453. minorTickPositions = minorTickPositions.concat(
  16454. axis.getTimeTicks(
  16455. axis.normalizeTimeTickInterval(minorTickInterval),
  16456. min,
  16457. max,
  16458. options.startOfWeek
  16459. )
  16460. );
  16461. } else {
  16462. for (
  16463. pos = min + (tickPositions[0] - min) % minorTickInterval;
  16464. pos <= max;
  16465. pos += minorTickInterval
  16466. ) {
  16467. // Very, very, tight grid lines (#5771)
  16468. if (pos === minorTickPositions[0]) {
  16469. break;
  16470. }
  16471. minorTickPositions.push(pos);
  16472. }
  16473. }
  16474. }
  16475. if (minorTickPositions.length !== 0) {
  16476. axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
  16477. }
  16478. return minorTickPositions;
  16479. },
  16480. /**
  16481. * Adjust the min and max for the minimum range. Keep in mind that the
  16482. * series data is not yet processed, so we don't have information on data
  16483. * cropping and grouping, or updated `axis.pointRange` or
  16484. * `series.pointRange`. The data can't be processed until we have finally
  16485. * established min and max.
  16486. * @private
  16487. */
  16488. adjustForMinRange: function () {
  16489. var axis = this,
  16490. options = axis.options,
  16491. min = axis.min,
  16492. max = axis.max,
  16493. zoomOffset,
  16494. spaceAvailable,
  16495. closestDataRange,
  16496. i,
  16497. distance,
  16498. xData,
  16499. loopLength,
  16500. minArgs,
  16501. maxArgs,
  16502. minRange;
  16503. // Set the automatic minimum range based on the closest point distance
  16504. if (axis.isXAxis && axis.minRange === undefined && !axis.isLog) {
  16505. if (defined(options.min) || defined(options.max)) {
  16506. axis.minRange = null; // don't do this again
  16507. } else {
  16508. // Find the closest distance between raw data points, as opposed
  16509. // to closestPointRange that applies to processed points
  16510. // (cropped and grouped)
  16511. axis.series.forEach(function (series) {
  16512. xData = series.xData;
  16513. loopLength = series.xIncrement ? 1 : xData.length - 1;
  16514. for (i = loopLength; i > 0; i--) {
  16515. distance = xData[i] - xData[i - 1];
  16516. if (
  16517. closestDataRange === undefined ||
  16518. distance < closestDataRange
  16519. ) {
  16520. closestDataRange = distance;
  16521. }
  16522. }
  16523. });
  16524. axis.minRange = Math.min(
  16525. closestDataRange * 5,
  16526. axis.dataMax - axis.dataMin
  16527. );
  16528. }
  16529. }
  16530. // if minRange is exceeded, adjust
  16531. if (max - min < axis.minRange) {
  16532. spaceAvailable = axis.dataMax - axis.dataMin >= axis.minRange;
  16533. minRange = axis.minRange;
  16534. zoomOffset = (minRange - max + min) / 2;
  16535. // if min and max options have been set, don't go beyond it
  16536. minArgs = [min - zoomOffset, pick(options.min, min - zoomOffset)];
  16537. // If space is available, stay within the data range
  16538. if (spaceAvailable) {
  16539. minArgs[2] = axis.isLog ?
  16540. axis.log2lin(axis.dataMin) :
  16541. axis.dataMin;
  16542. }
  16543. min = arrayMax(minArgs);
  16544. maxArgs = [min + minRange, pick(options.max, min + minRange)];
  16545. // If space is availabe, stay within the data range
  16546. if (spaceAvailable) {
  16547. maxArgs[2] = axis.isLog ?
  16548. axis.log2lin(axis.dataMax) :
  16549. axis.dataMax;
  16550. }
  16551. max = arrayMin(maxArgs);
  16552. // now if the max is adjusted, adjust the min back
  16553. if (max - min < minRange) {
  16554. minArgs[0] = max - minRange;
  16555. minArgs[1] = pick(options.min, max - minRange);
  16556. min = arrayMax(minArgs);
  16557. }
  16558. }
  16559. // Record modified extremes
  16560. axis.min = min;
  16561. axis.max = max;
  16562. },
  16563. // Find the closestPointRange across all series.
  16564. getClosest: function () {
  16565. var ret;
  16566. if (this.categories) {
  16567. ret = 1;
  16568. } else {
  16569. this.series.forEach(function (series) {
  16570. var seriesClosest = series.closestPointRange,
  16571. visible = series.visible ||
  16572. !series.chart.options.chart.ignoreHiddenSeries;
  16573. if (
  16574. !series.noSharedTooltip &&
  16575. defined(seriesClosest) &&
  16576. visible
  16577. ) {
  16578. ret = defined(ret) ?
  16579. Math.min(ret, seriesClosest) :
  16580. seriesClosest;
  16581. }
  16582. });
  16583. }
  16584. return ret;
  16585. },
  16586. /**
  16587. * When a point name is given and no x, search for the name in the existing
  16588. * categories, or if categories aren't provided, search names or create a
  16589. * new category (#2522).
  16590. * @private
  16591. * @param {Highcharts.Point} point The point to inspect.
  16592. * @return {number} The X value that the point is given.
  16593. */
  16594. nameToX: function (point) {
  16595. var explicitCategories = isArray(this.categories),
  16596. names = explicitCategories ? this.categories : this.names,
  16597. nameX = point.options.x,
  16598. x;
  16599. point.series.requireSorting = false;
  16600. if (!defined(nameX)) {
  16601. nameX = this.options.uniqueNames === false ?
  16602. point.series.autoIncrement() :
  16603. (
  16604. explicitCategories ?
  16605. names.indexOf(point.name) :
  16606. pick(names.keys[point.name], -1)
  16607. );
  16608. }
  16609. if (nameX === -1) { // Not found in currenct categories
  16610. if (!explicitCategories) {
  16611. x = names.length;
  16612. }
  16613. } else {
  16614. x = nameX;
  16615. }
  16616. // Write the last point's name to the names array
  16617. if (x !== undefined) {
  16618. this.names[x] = point.name;
  16619. // Backwards mapping is much faster than array searching (#7725)
  16620. this.names.keys[point.name] = x;
  16621. }
  16622. return x;
  16623. },
  16624. // When changes have been done to series data, update the axis.names.
  16625. updateNames: function () {
  16626. var axis = this,
  16627. names = this.names,
  16628. i = names.length;
  16629. if (i > 0) {
  16630. Object.keys(names.keys).forEach(function (key) {
  16631. delete names.keys[key];
  16632. });
  16633. names.length = 0;
  16634. this.minRange = this.userMinRange; // Reset
  16635. (this.series || []).forEach(function (series) {
  16636. // Reset incrementer (#5928)
  16637. series.xIncrement = null;
  16638. // When adding a series, points are not yet generated
  16639. if (!series.points || series.isDirtyData) {
  16640. // When we're updating the series with data that is longer
  16641. // than it was, and cropThreshold is passed, we need to make
  16642. // sure that the axis.max is increased _before_ running the
  16643. // premature processData. Otherwise this early iteration of
  16644. // processData will crop the points to axis.max, and the
  16645. // names array will be too short (#5857).
  16646. axis.max = Math.max(axis.max, series.xData.length - 1);
  16647. series.processData();
  16648. series.generatePoints();
  16649. }
  16650. series.data.forEach(function (point, i) { // #9487
  16651. var x;
  16652. if (
  16653. point &&
  16654. point.options &&
  16655. point.name !== undefined // #9562
  16656. ) {
  16657. x = axis.nameToX(point);
  16658. if (x !== undefined && x !== point.x) {
  16659. point.x = x;
  16660. series.xData[i] = x;
  16661. }
  16662. }
  16663. });
  16664. });
  16665. }
  16666. },
  16667. /**
  16668. * Update translation information.
  16669. * @private
  16670. * @param {boolean} saveOld
  16671. * @fires Highcharts.Axis#event:afterSetAxisTranslation
  16672. */
  16673. setAxisTranslation: function (saveOld) {
  16674. var axis = this,
  16675. range = axis.max - axis.min,
  16676. pointRange = axis.axisPointRange || 0,
  16677. closestPointRange,
  16678. minPointOffset = 0,
  16679. pointRangePadding = 0,
  16680. linkedParent = axis.linkedParent,
  16681. ordinalCorrection,
  16682. hasCategories = !!axis.categories,
  16683. transA = axis.transA,
  16684. isXAxis = axis.isXAxis;
  16685. // Adjust translation for padding. Y axis with categories need to go
  16686. // through the same (#1784).
  16687. if (isXAxis || hasCategories || pointRange) {
  16688. // Get the closest points
  16689. closestPointRange = axis.getClosest();
  16690. if (linkedParent) {
  16691. minPointOffset = linkedParent.minPointOffset;
  16692. pointRangePadding = linkedParent.pointRangePadding;
  16693. } else {
  16694. axis.series.forEach(function (series) {
  16695. var seriesPointRange = hasCategories ?
  16696. 1 :
  16697. (
  16698. isXAxis ?
  16699. pick(
  16700. series.options.pointRange,
  16701. closestPointRange,
  16702. 0
  16703. ) :
  16704. (axis.axisPointRange || 0)
  16705. ), // #2806
  16706. pointPlacement = series.options.pointPlacement;
  16707. pointRange = Math.max(pointRange, seriesPointRange);
  16708. if (!axis.single) {
  16709. // minPointOffset is the value padding to the left of
  16710. // the axis in order to make room for points with a
  16711. // pointRange, typically columns. When the
  16712. // pointPlacement option is 'between' or 'on', this
  16713. // padding does not apply.
  16714. minPointOffset = Math.max(
  16715. minPointOffset,
  16716. isXAxis && isString(pointPlacement) ?
  16717. 0 : seriesPointRange / 2
  16718. );
  16719. // Determine the total padding needed to the length of
  16720. // the axis to make room for the pointRange. If the
  16721. // series' pointPlacement is 'on', no padding is added.
  16722. pointRangePadding = Math.max(
  16723. pointRangePadding,
  16724. isXAxis && pointPlacement === 'on' ?
  16725. 0 : seriesPointRange
  16726. );
  16727. }
  16728. });
  16729. }
  16730. // Record minPointOffset and pointRangePadding
  16731. ordinalCorrection = axis.ordinalSlope && closestPointRange ?
  16732. axis.ordinalSlope / closestPointRange :
  16733. 1; // #988, #1853
  16734. axis.minPointOffset = minPointOffset =
  16735. minPointOffset * ordinalCorrection;
  16736. axis.pointRangePadding =
  16737. pointRangePadding = pointRangePadding * ordinalCorrection;
  16738. // pointRange means the width reserved for each point, like in a
  16739. // column chart
  16740. axis.pointRange = Math.min(pointRange, range);
  16741. // closestPointRange means the closest distance between points. In
  16742. // columns it is mostly equal to pointRange, but in lines pointRange
  16743. // is 0 while closestPointRange is some other value
  16744. if (isXAxis) {
  16745. axis.closestPointRange = closestPointRange;
  16746. }
  16747. }
  16748. // Secondary values
  16749. if (saveOld) {
  16750. axis.oldTransA = transA;
  16751. }
  16752. axis.translationSlope = axis.transA = transA =
  16753. axis.staticScale ||
  16754. axis.len / ((range + pointRangePadding) || 1);
  16755. // Translation addend
  16756. axis.transB = axis.horiz ? axis.left : axis.bottom;
  16757. axis.minPixelPadding = transA * minPointOffset;
  16758. fireEvent(this, 'afterSetAxisTranslation');
  16759. },
  16760. minFromRange: function () {
  16761. return this.max - this.range;
  16762. },
  16763. /**
  16764. * Set the tick positions to round values and optionally extend the extremes
  16765. * to the nearest tick.
  16766. * @private
  16767. * @param {boolean} secondPass
  16768. * @fires Highcharts.Axis#event:foundExtremes
  16769. */
  16770. setTickInterval: function (secondPass) {
  16771. var axis = this,
  16772. chart = axis.chart,
  16773. options = axis.options,
  16774. isLog = axis.isLog,
  16775. isDatetimeAxis = axis.isDatetimeAxis,
  16776. isXAxis = axis.isXAxis,
  16777. isLinked = axis.isLinked,
  16778. maxPadding = options.maxPadding,
  16779. minPadding = options.minPadding,
  16780. length,
  16781. linkedParentExtremes,
  16782. tickIntervalOption = options.tickInterval,
  16783. minTickInterval,
  16784. tickPixelIntervalOption = options.tickPixelInterval,
  16785. categories = axis.categories,
  16786. threshold = isNumber(axis.threshold) ? axis.threshold : null,
  16787. softThreshold = axis.softThreshold,
  16788. thresholdMin,
  16789. thresholdMax,
  16790. hardMin,
  16791. hardMax;
  16792. if (!isDatetimeAxis && !categories && !isLinked) {
  16793. this.getTickAmount();
  16794. }
  16795. // Min or max set either by zooming/setExtremes or initial options
  16796. hardMin = pick(axis.userMin, options.min);
  16797. hardMax = pick(axis.userMax, options.max);
  16798. // Linked axis gets the extremes from the parent axis
  16799. if (isLinked) {
  16800. axis.linkedParent = chart[axis.coll][options.linkedTo];
  16801. linkedParentExtremes = axis.linkedParent.getExtremes();
  16802. axis.min = pick(
  16803. linkedParentExtremes.min,
  16804. linkedParentExtremes.dataMin
  16805. );
  16806. axis.max = pick(
  16807. linkedParentExtremes.max,
  16808. linkedParentExtremes.dataMax
  16809. );
  16810. if (options.type !== axis.linkedParent.options.type) {
  16811. H.error(11, 1, chart); // Can't link axes of different type
  16812. }
  16813. // Initial min and max from the extreme data values
  16814. } else {
  16815. // Adjust to hard threshold
  16816. if (!softThreshold && defined(threshold)) {
  16817. if (axis.dataMin >= threshold) {
  16818. thresholdMin = threshold;
  16819. minPadding = 0;
  16820. } else if (axis.dataMax <= threshold) {
  16821. thresholdMax = threshold;
  16822. maxPadding = 0;
  16823. }
  16824. }
  16825. axis.min = pick(hardMin, thresholdMin, axis.dataMin);
  16826. axis.max = pick(hardMax, thresholdMax, axis.dataMax);
  16827. }
  16828. if (isLog) {
  16829. if (
  16830. axis.positiveValuesOnly &&
  16831. !secondPass &&
  16832. Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0
  16833. ) { // #978
  16834. H.error(10, 1, chart); // Can't plot negative values on log axis
  16835. }
  16836. // The correctFloat cures #934, float errors on full tens. But it
  16837. // was too aggressive for #4360 because of conversion back to lin,
  16838. // therefore use precision 15.
  16839. axis.min = correctFloat(axis.log2lin(axis.min), 15);
  16840. axis.max = correctFloat(axis.log2lin(axis.max), 15);
  16841. }
  16842. // handle zoomed range
  16843. if (axis.range && defined(axis.max)) {
  16844. axis.userMin = axis.min = hardMin =
  16845. Math.max(axis.dataMin, axis.minFromRange()); // #618, #6773
  16846. axis.userMax = hardMax = axis.max;
  16847. axis.range = null; // don't use it when running setExtremes
  16848. }
  16849. // Hook for Highstock Scroller. Consider combining with beforePadding.
  16850. fireEvent(axis, 'foundExtremes');
  16851. // Hook for adjusting this.min and this.max. Used by bubble series.
  16852. if (axis.beforePadding) {
  16853. axis.beforePadding();
  16854. }
  16855. // adjust min and max for the minimum range
  16856. axis.adjustForMinRange();
  16857. // Pad the values to get clear of the chart's edges. To avoid
  16858. // tickInterval taking the padding into account, we do this after
  16859. // computing tick interval (#1337).
  16860. if (
  16861. !categories &&
  16862. !axis.axisPointRange &&
  16863. !axis.usePercentage &&
  16864. !isLinked &&
  16865. defined(axis.min) &&
  16866. defined(axis.max)
  16867. ) {
  16868. length = axis.max - axis.min;
  16869. if (length) {
  16870. if (!defined(hardMin) && minPadding) {
  16871. axis.min -= length * minPadding;
  16872. }
  16873. if (!defined(hardMax) && maxPadding) {
  16874. axis.max += length * maxPadding;
  16875. }
  16876. }
  16877. }
  16878. // Handle options for floor, ceiling, softMin and softMax (#6359)
  16879. if (isNumber(options.softMin) && !isNumber(axis.userMin)) {
  16880. axis.min = Math.min(axis.min, options.softMin);
  16881. }
  16882. if (isNumber(options.softMax) && !isNumber(axis.userMax)) {
  16883. axis.max = Math.max(axis.max, options.softMax);
  16884. }
  16885. if (isNumber(options.floor)) {
  16886. axis.min = Math.min(
  16887. Math.max(axis.min, options.floor),
  16888. Number.MAX_VALUE
  16889. );
  16890. }
  16891. if (isNumber(options.ceiling)) {
  16892. axis.max = Math.max(
  16893. Math.min(axis.max, options.ceiling),
  16894. pick(axis.userMax, -Number.MAX_VALUE)
  16895. );
  16896. }
  16897. // When the threshold is soft, adjust the extreme value only if the data
  16898. // extreme and the padded extreme land on either side of the threshold.
  16899. // For example, a series of [0, 1, 2, 3] would make the yAxis add a tick
  16900. // for -1 because of the default minPadding and startOnTick options.
  16901. // This is prevented by the softThreshold option.
  16902. if (softThreshold && defined(axis.dataMin)) {
  16903. threshold = threshold || 0;
  16904. if (
  16905. !defined(hardMin) &&
  16906. axis.min < threshold &&
  16907. axis.dataMin >= threshold
  16908. ) {
  16909. axis.min = threshold;
  16910. } else if (
  16911. !defined(hardMax) &&
  16912. axis.max > threshold &&
  16913. axis.dataMax <= threshold
  16914. ) {
  16915. axis.max = threshold;
  16916. }
  16917. }
  16918. // get tickInterval
  16919. if (
  16920. axis.min === axis.max ||
  16921. axis.min === undefined ||
  16922. axis.max === undefined
  16923. ) {
  16924. axis.tickInterval = 1;
  16925. } else if (
  16926. isLinked &&
  16927. !tickIntervalOption &&
  16928. tickPixelIntervalOption ===
  16929. axis.linkedParent.options.tickPixelInterval
  16930. ) {
  16931. axis.tickInterval = tickIntervalOption =
  16932. axis.linkedParent.tickInterval;
  16933. } else {
  16934. axis.tickInterval = pick(
  16935. tickIntervalOption,
  16936. this.tickAmount ?
  16937. ((axis.max - axis.min) / Math.max(this.tickAmount - 1, 1)) :
  16938. undefined,
  16939. // For categoried axis, 1 is default, for linear axis use
  16940. // tickPix
  16941. categories ?
  16942. 1 :
  16943. // don't let it be more than the data range
  16944. (axis.max - axis.min) * tickPixelIntervalOption /
  16945. Math.max(axis.len, tickPixelIntervalOption)
  16946. );
  16947. }
  16948. // Now we're finished detecting min and max, crop and group series data.
  16949. // This is in turn needed in order to find tick positions in ordinal
  16950. // axes.
  16951. if (isXAxis && !secondPass) {
  16952. axis.series.forEach(function (series) {
  16953. series.processData(
  16954. axis.min !== axis.oldMin || axis.max !== axis.oldMax
  16955. );
  16956. });
  16957. }
  16958. // set the translation factor used in translate function
  16959. axis.setAxisTranslation(true);
  16960. // hook for ordinal axes and radial axes
  16961. if (axis.beforeSetTickPositions) {
  16962. axis.beforeSetTickPositions();
  16963. }
  16964. // hook for extensions, used in Highstock ordinal axes
  16965. if (axis.postProcessTickInterval) {
  16966. axis.tickInterval = axis.postProcessTickInterval(axis.tickInterval);
  16967. }
  16968. // In column-like charts, don't cramp in more ticks than there are
  16969. // points (#1943, #4184)
  16970. if (axis.pointRange && !tickIntervalOption) {
  16971. axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
  16972. }
  16973. // Before normalizing the tick interval, handle minimum tick interval.
  16974. // This applies only if tickInterval is not defined.
  16975. minTickInterval = pick(
  16976. options.minTickInterval,
  16977. axis.isDatetimeAxis && axis.closestPointRange
  16978. );
  16979. if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
  16980. axis.tickInterval = minTickInterval;
  16981. }
  16982. // for linear axes, get magnitude and normalize the interval
  16983. if (!isDatetimeAxis && !isLog && !tickIntervalOption) {
  16984. axis.tickInterval = normalizeTickInterval(
  16985. axis.tickInterval,
  16986. null,
  16987. getMagnitude(axis.tickInterval),
  16988. // If the tick interval is between 0.5 and 5 and the axis max is
  16989. // in the order of thousands, chances are we are dealing with
  16990. // years. Don't allow decimals. #3363.
  16991. pick(
  16992. options.allowDecimals,
  16993. !(
  16994. axis.tickInterval > 0.5 &&
  16995. axis.tickInterval < 5 &&
  16996. axis.max > 1000 &&
  16997. axis.max < 9999
  16998. )
  16999. ),
  17000. !!this.tickAmount
  17001. );
  17002. }
  17003. // Prevent ticks from getting so close that we can't draw the labels
  17004. if (!this.tickAmount) {
  17005. axis.tickInterval = axis.unsquish();
  17006. }
  17007. this.setTickPositions();
  17008. },
  17009. /**
  17010. * Now we have computed the normalized tickInterval, get the tick positions
  17011. *
  17012. * @function Highcharts.Axis#setTickPositions
  17013. *
  17014. * @fires Highcharts.Axis#event:afterSetTickPositions
  17015. */
  17016. setTickPositions: function () {
  17017. var options = this.options,
  17018. tickPositions,
  17019. tickPositionsOption = options.tickPositions,
  17020. minorTickIntervalOption = this.getMinorTickInterval(),
  17021. tickPositioner = options.tickPositioner,
  17022. startOnTick = options.startOnTick,
  17023. endOnTick = options.endOnTick;
  17024. // Set the tickmarkOffset
  17025. this.tickmarkOffset = (
  17026. this.categories &&
  17027. options.tickmarkPlacement === 'between' &&
  17028. this.tickInterval === 1
  17029. ) ? 0.5 : 0; // #3202
  17030. // get minorTickInterval
  17031. this.minorTickInterval =
  17032. minorTickIntervalOption === 'auto' &&
  17033. this.tickInterval ?
  17034. this.tickInterval / 5 :
  17035. minorTickIntervalOption;
  17036. // When there is only one point, or all points have the same value on
  17037. // this axis, then min and max are equal and tickPositions.length is 0
  17038. // or 1. In this case, add some padding in order to center the point,
  17039. // but leave it with one tick. #1337.
  17040. this.single =
  17041. this.min === this.max &&
  17042. defined(this.min) &&
  17043. !this.tickAmount &&
  17044. (
  17045. // Data is on integer (#6563)
  17046. parseInt(this.min, 10) === this.min ||
  17047. // Between integers and decimals are not allowed (#6274)
  17048. options.allowDecimals !== false
  17049. );
  17050. // Find the tick positions. Work on a copy (#1565)
  17051. this.tickPositions = tickPositions =
  17052. tickPositionsOption && tickPositionsOption.slice();
  17053. if (!tickPositions) {
  17054. // Too many ticks (#6405). Create a friendly warning and provide two
  17055. // ticks so at least we can show the data series.
  17056. if (
  17057. !this.ordinalPositions &&
  17058. (
  17059. (this.max - this.min) / this.tickInterval >
  17060. Math.max(2 * this.len, 200)
  17061. )
  17062. ) {
  17063. tickPositions = [this.min, this.max];
  17064. H.error(19, false, this.chart);
  17065. } else if (this.isDatetimeAxis) {
  17066. tickPositions = this.getTimeTicks(
  17067. this.normalizeTimeTickInterval(
  17068. this.tickInterval,
  17069. options.units
  17070. ),
  17071. this.min,
  17072. this.max,
  17073. options.startOfWeek,
  17074. this.ordinalPositions,
  17075. this.closestPointRange,
  17076. true
  17077. );
  17078. } else if (this.isLog) {
  17079. tickPositions = this.getLogTickPositions(
  17080. this.tickInterval,
  17081. this.min,
  17082. this.max
  17083. );
  17084. } else {
  17085. tickPositions = this.getLinearTickPositions(
  17086. this.tickInterval,
  17087. this.min,
  17088. this.max
  17089. );
  17090. }
  17091. // Too dense ticks, keep only the first and last (#4477)
  17092. if (tickPositions.length > this.len) {
  17093. tickPositions = [tickPositions[0], tickPositions.pop()];
  17094. // Reduce doubled value (#7339)
  17095. if (tickPositions[0] === tickPositions[1]) {
  17096. tickPositions.length = 1;
  17097. }
  17098. }
  17099. this.tickPositions = tickPositions;
  17100. // Run the tick positioner callback, that allows modifying auto tick
  17101. // positions.
  17102. if (tickPositioner) {
  17103. tickPositioner = tickPositioner.apply(
  17104. this,
  17105. [this.min, this.max]
  17106. );
  17107. if (tickPositioner) {
  17108. this.tickPositions = tickPositions = tickPositioner;
  17109. }
  17110. }
  17111. }
  17112. // Reset min/max or remove extremes based on start/end on tick
  17113. this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
  17114. this.trimTicks(tickPositions, startOnTick, endOnTick);
  17115. if (!this.isLinked) {
  17116. // Substract half a unit (#2619, #2846, #2515, #3390),
  17117. // but not in case of multiple ticks (#6897)
  17118. if (this.single && tickPositions.length < 2) {
  17119. this.min -= 0.5;
  17120. this.max += 0.5;
  17121. }
  17122. if (!tickPositionsOption && !tickPositioner) {
  17123. this.adjustTickAmount();
  17124. }
  17125. }
  17126. fireEvent(this, 'afterSetTickPositions');
  17127. },
  17128. // Handle startOnTick and endOnTick by either adapting to padding min/max or
  17129. // rounded min/max. Also handle single data points.
  17130. trimTicks: function (tickPositions, startOnTick, endOnTick) {
  17131. var roundedMin = tickPositions[0],
  17132. roundedMax = tickPositions[tickPositions.length - 1],
  17133. minPointOffset = this.minPointOffset || 0;
  17134. fireEvent(this, 'trimTicks');
  17135. if (!this.isLinked) {
  17136. if (startOnTick && roundedMin !== -Infinity) { // #6502
  17137. this.min = roundedMin;
  17138. } else {
  17139. while (this.min - minPointOffset > tickPositions[0]) {
  17140. tickPositions.shift();
  17141. }
  17142. }
  17143. if (endOnTick) {
  17144. this.max = roundedMax;
  17145. } else {
  17146. while (this.max + minPointOffset <
  17147. tickPositions[tickPositions.length - 1]) {
  17148. tickPositions.pop();
  17149. }
  17150. }
  17151. // If no tick are left, set one tick in the middle (#3195)
  17152. if (
  17153. tickPositions.length === 0 &&
  17154. defined(roundedMin) &&
  17155. !this.options.tickPositions
  17156. ) {
  17157. tickPositions.push((roundedMax + roundedMin) / 2);
  17158. }
  17159. }
  17160. },
  17161. /**
  17162. * Check if there are multiple axes in the same pane.
  17163. * @private
  17164. * @return {boolean} True if there are other axes.
  17165. */
  17166. alignToOthers: function () {
  17167. var others = {}, // Whether there is another axis to pair with this one
  17168. hasOther,
  17169. options = this.options;
  17170. if (
  17171. // Only if alignTicks is true
  17172. this.chart.options.chart.alignTicks !== false &&
  17173. options.alignTicks !== false &&
  17174. // Disabled when startOnTick or endOnTick are false (#7604)
  17175. options.startOnTick !== false &&
  17176. options.endOnTick !== false &&
  17177. // Don't try to align ticks on a log axis, they are not evenly
  17178. // spaced (#6021)
  17179. !this.isLog
  17180. ) {
  17181. this.chart[this.coll].forEach(function (axis) {
  17182. var otherOptions = axis.options,
  17183. horiz = axis.horiz,
  17184. key = [
  17185. horiz ? otherOptions.left : otherOptions.top,
  17186. otherOptions.width,
  17187. otherOptions.height,
  17188. otherOptions.pane
  17189. ].join(',');
  17190. if (axis.series.length) { // #4442
  17191. if (others[key]) {
  17192. hasOther = true; // #4201
  17193. } else {
  17194. others[key] = 1;
  17195. }
  17196. }
  17197. });
  17198. }
  17199. return hasOther;
  17200. },
  17201. /**
  17202. * Find the max ticks of either the x and y axis collection, and record it
  17203. * in `this.tickAmount`.
  17204. * @private
  17205. */
  17206. getTickAmount: function () {
  17207. var options = this.options,
  17208. tickAmount = options.tickAmount,
  17209. tickPixelInterval = options.tickPixelInterval;
  17210. if (
  17211. !defined(options.tickInterval) &&
  17212. this.len < tickPixelInterval &&
  17213. !this.isRadial &&
  17214. !this.isLog &&
  17215. options.startOnTick &&
  17216. options.endOnTick
  17217. ) {
  17218. tickAmount = 2;
  17219. }
  17220. if (!tickAmount && this.alignToOthers()) {
  17221. // Add 1 because 4 tick intervals require 5 ticks (including first
  17222. // and last)
  17223. tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
  17224. }
  17225. // For tick amounts of 2 and 3, compute five ticks and remove the
  17226. // intermediate ones. This prevents the axis from adding ticks that are
  17227. // too far away from the data extremes.
  17228. if (tickAmount < 4) {
  17229. this.finalTickAmt = tickAmount;
  17230. tickAmount = 5;
  17231. }
  17232. this.tickAmount = tickAmount;
  17233. },
  17234. /**
  17235. * When using multiple axes, adjust the number of ticks to match the highest
  17236. * number of ticks in that group.
  17237. * @private
  17238. */
  17239. adjustTickAmount: function () {
  17240. var axis = this,
  17241. axisOptions = axis.options,
  17242. tickInterval = axis.tickInterval,
  17243. tickPositions = axis.tickPositions,
  17244. tickAmount = axis.tickAmount,
  17245. finalTickAmt = axis.finalTickAmt,
  17246. currentTickAmount = tickPositions && tickPositions.length,
  17247. threshold = pick(axis.threshold, axis.softThreshold ? 0 : null),
  17248. min,
  17249. len,
  17250. i;
  17251. if (axis.hasData()) {
  17252. if (currentTickAmount < tickAmount) {
  17253. min = axis.min;
  17254. while (tickPositions.length < tickAmount) {
  17255. // Extend evenly for both sides unless we're on the
  17256. // threshold (#3965)
  17257. if (
  17258. tickPositions.length % 2 ||
  17259. min === threshold
  17260. ) {
  17261. // to the end
  17262. tickPositions.push(correctFloat(
  17263. tickPositions[tickPositions.length - 1] +
  17264. tickInterval
  17265. ));
  17266. } else {
  17267. // to the start
  17268. tickPositions.unshift(correctFloat(
  17269. tickPositions[0] - tickInterval
  17270. ));
  17271. }
  17272. }
  17273. axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
  17274. // Do not crop when ticks are not extremes (#9841)
  17275. axis.min = axisOptions.startOnTick ?
  17276. tickPositions[0] :
  17277. Math.min(axis.min, tickPositions[0]);
  17278. axis.max = axisOptions.endOnTick ?
  17279. tickPositions[tickPositions.length - 1] :
  17280. Math.max(axis.max, tickPositions[tickPositions.length - 1]);
  17281. // We have too many ticks, run second pass to try to reduce ticks
  17282. } else if (currentTickAmount > tickAmount) {
  17283. axis.tickInterval *= 2;
  17284. axis.setTickPositions();
  17285. }
  17286. // The finalTickAmt property is set in getTickAmount
  17287. if (defined(finalTickAmt)) {
  17288. i = len = tickPositions.length;
  17289. while (i--) {
  17290. if (
  17291. // Remove every other tick
  17292. (finalTickAmt === 3 && i % 2 === 1) ||
  17293. // Remove all but first and last
  17294. (finalTickAmt <= 2 && i > 0 && i < len - 1)
  17295. ) {
  17296. tickPositions.splice(i, 1);
  17297. }
  17298. }
  17299. axis.finalTickAmt = undefined;
  17300. }
  17301. }
  17302. },
  17303. /**
  17304. * Set the scale based on data min and max, user set min and max or options.
  17305. * @private
  17306. * @fires Highcharts.Axis#event:afterSetScale
  17307. */
  17308. setScale: function () {
  17309. var axis = this,
  17310. isDirtyData,
  17311. isDirtyAxisLength;
  17312. axis.oldMin = axis.min;
  17313. axis.oldMax = axis.max;
  17314. axis.oldAxisLength = axis.len;
  17315. // set the new axisLength
  17316. axis.setAxisSize();
  17317. isDirtyAxisLength = axis.len !== axis.oldAxisLength;
  17318. // is there new data?
  17319. axis.series.forEach(function (series) {
  17320. if (
  17321. series.isDirtyData ||
  17322. series.isDirty ||
  17323. // When x axis is dirty, we need new data extremes for y as well
  17324. series.xAxis.isDirty
  17325. ) {
  17326. isDirtyData = true;
  17327. }
  17328. });
  17329. // do we really need to go through all this?
  17330. if (
  17331. isDirtyAxisLength ||
  17332. isDirtyData ||
  17333. axis.isLinked ||
  17334. axis.forceRedraw ||
  17335. axis.userMin !== axis.oldUserMin ||
  17336. axis.userMax !== axis.oldUserMax ||
  17337. axis.alignToOthers()
  17338. ) {
  17339. if (axis.resetStacks) {
  17340. axis.resetStacks();
  17341. }
  17342. axis.forceRedraw = false;
  17343. // get data extremes if needed
  17344. axis.getSeriesExtremes();
  17345. // get fixed positions based on tickInterval
  17346. axis.setTickInterval();
  17347. // record old values to decide whether a rescale is necessary later
  17348. // on (#540)
  17349. axis.oldUserMin = axis.userMin;
  17350. axis.oldUserMax = axis.userMax;
  17351. // Mark as dirty if it is not already set to dirty and extremes have
  17352. // changed. #595.
  17353. if (!axis.isDirty) {
  17354. axis.isDirty =
  17355. isDirtyAxisLength ||
  17356. axis.min !== axis.oldMin ||
  17357. axis.max !== axis.oldMax;
  17358. }
  17359. } else if (axis.cleanStacks) {
  17360. axis.cleanStacks();
  17361. }
  17362. fireEvent(this, 'afterSetScale');
  17363. },
  17364. /**
  17365. * Set the minimum and maximum of the axes after render time. If the
  17366. * `startOnTick` and `endOnTick` options are true, the minimum and maximum
  17367. * values are rounded off to the nearest tick. To prevent this, these
  17368. * options can be set to false before calling setExtremes. Also, setExtremes
  17369. * will not allow a range lower than the `minRange` option, which by default
  17370. * is the range of five points.
  17371. *
  17372. * @sample highcharts/members/axis-setextremes/
  17373. * Set extremes from a button
  17374. * @sample highcharts/members/axis-setextremes-datetime/
  17375. * Set extremes on a datetime axis
  17376. * @sample highcharts/members/axis-setextremes-off-ticks/
  17377. * Set extremes off ticks
  17378. * @sample stock/members/axis-setextremes/
  17379. * Set extremes in Highstock
  17380. * @sample maps/members/axis-setextremes/
  17381. * Set extremes in Highmaps
  17382. *
  17383. * @function Highcharts.Axis#setExtremes
  17384. *
  17385. * @param {number} [newMin]
  17386. * The new minimum value.
  17387. *
  17388. * @param {number} [newMax]
  17389. * The new maximum value.
  17390. *
  17391. * @param {boolean} [redraw=true]
  17392. * Whether to redraw the chart or wait for an explicit call to
  17393. * {@link Highcharts.Chart#redraw}
  17394. *
  17395. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  17396. * Enable or modify animations.
  17397. *
  17398. * @param {*} [eventArguments]
  17399. * Arguments to be accessed in event handler.
  17400. *
  17401. * @fires Highcharts.Axis#event:setExtremes
  17402. */
  17403. setExtremes: function (newMin, newMax, redraw, animation, eventArguments) {
  17404. var axis = this,
  17405. chart = axis.chart;
  17406. redraw = pick(redraw, true); // defaults to true
  17407. axis.series.forEach(function (serie) {
  17408. delete serie.kdTree;
  17409. });
  17410. // Extend the arguments with min and max
  17411. eventArguments = extend(eventArguments, {
  17412. min: newMin,
  17413. max: newMax
  17414. });
  17415. // Fire the event
  17416. fireEvent(axis, 'setExtremes', eventArguments, function () {
  17417. axis.userMin = newMin;
  17418. axis.userMax = newMax;
  17419. axis.eventArgs = eventArguments;
  17420. if (redraw) {
  17421. chart.redraw(animation);
  17422. }
  17423. });
  17424. },
  17425. // Overridable method for zooming chart. Pulled out in a separate method to
  17426. // allow overriding in stock charts.
  17427. zoom: function (newMin, newMax) {
  17428. var dataMin = this.dataMin,
  17429. dataMax = this.dataMax,
  17430. options = this.options,
  17431. min = Math.min(dataMin, pick(options.min, dataMin)),
  17432. max = Math.max(dataMax, pick(options.max, dataMax)),
  17433. evt = { newMin: newMin, newMax: newMax };
  17434. fireEvent(this, 'zoom', evt, function (e) {
  17435. // Use e.newMin and e.newMax - event handlers may have altered them
  17436. var newMin = e.newMin,
  17437. newMax = e.newMax;
  17438. if (newMin !== this.min || newMax !== this.max) { // #5790
  17439. // Prevent pinch zooming out of range. Check for defined is for
  17440. // #1946. #1734.
  17441. if (!this.allowZoomOutside) {
  17442. // #6014, sometimes newMax will be smaller than min (or
  17443. // newMin will be larger than max).
  17444. if (defined(dataMin)) {
  17445. if (newMin < min) {
  17446. newMin = min;
  17447. }
  17448. if (newMin > max) {
  17449. newMin = max;
  17450. }
  17451. }
  17452. if (defined(dataMax)) {
  17453. if (newMax < min) {
  17454. newMax = min;
  17455. }
  17456. if (newMax > max) {
  17457. newMax = max;
  17458. }
  17459. }
  17460. }
  17461. // In full view, displaying the reset zoom button is not
  17462. // required
  17463. this.displayBtn = newMin !== undefined || newMax !== undefined;
  17464. // Do it
  17465. this.setExtremes(
  17466. newMin,
  17467. newMax,
  17468. false,
  17469. undefined,
  17470. { trigger: 'zoom' }
  17471. );
  17472. }
  17473. e.zoomed = true;
  17474. });
  17475. return evt.zoomed;
  17476. },
  17477. // Update the axis metrics.
  17478. setAxisSize: function () {
  17479. var chart = this.chart,
  17480. options = this.options,
  17481. // [top, right, bottom, left]
  17482. offsets = options.offsets || [0, 0, 0, 0],
  17483. horiz = this.horiz,
  17484. // Check for percentage based input values. Rounding fixes problems
  17485. // with column overflow and plot line filtering (#4898, #4899)
  17486. width = this.width = Math.round(H.relativeLength(
  17487. pick(
  17488. options.width,
  17489. chart.plotWidth - offsets[3] + offsets[1]
  17490. ),
  17491. chart.plotWidth
  17492. )),
  17493. height = this.height = Math.round(H.relativeLength(
  17494. pick(
  17495. options.height,
  17496. chart.plotHeight - offsets[0] + offsets[2]
  17497. ),
  17498. chart.plotHeight
  17499. )),
  17500. top = this.top = Math.round(H.relativeLength(
  17501. pick(options.top, chart.plotTop + offsets[0]),
  17502. chart.plotHeight,
  17503. chart.plotTop
  17504. )),
  17505. left = this.left = Math.round(H.relativeLength(
  17506. pick(options.left, chart.plotLeft + offsets[3]),
  17507. chart.plotWidth,
  17508. chart.plotLeft
  17509. ));
  17510. // Expose basic values to use in Series object and navigator
  17511. this.bottom = chart.chartHeight - height - top;
  17512. this.right = chart.chartWidth - width - left;
  17513. // Direction agnostic properties
  17514. this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
  17515. this.pos = horiz ? left : top; // distance from SVG origin
  17516. },
  17517. /**
  17518. * Get the current extremes for the axis.
  17519. *
  17520. * @sample highcharts/members/axis-getextremes/
  17521. * Report extremes by click on a button
  17522. * @sample maps/members/axis-getextremes/
  17523. * Get extremes in Highmaps
  17524. *
  17525. * @function Highcharts.Axis#getExtremes
  17526. *
  17527. * @returns {Highcharts.ExtremesObject}
  17528. * An object containing extremes information.
  17529. */
  17530. getExtremes: function () {
  17531. var axis = this,
  17532. isLog = axis.isLog;
  17533. return {
  17534. min: isLog ? correctFloat(axis.lin2log(axis.min)) : axis.min,
  17535. max: isLog ? correctFloat(axis.lin2log(axis.max)) : axis.max,
  17536. dataMin: axis.dataMin,
  17537. dataMax: axis.dataMax,
  17538. userMin: axis.userMin,
  17539. userMax: axis.userMax
  17540. };
  17541. },
  17542. /**
  17543. * Get the zero plane either based on zero or on the min or max value.
  17544. * Used in bar and area plots.
  17545. *
  17546. * @function Highcharts.Axis#getThreshold
  17547. *
  17548. * @param {number} threshold
  17549. * The threshold in axis values.
  17550. *
  17551. * @return {number}
  17552. * The translated threshold position in terms of pixels, and
  17553. * corrected to stay within the axis bounds.
  17554. */
  17555. getThreshold: function (threshold) {
  17556. var axis = this,
  17557. isLog = axis.isLog,
  17558. realMin = isLog ? axis.lin2log(axis.min) : axis.min,
  17559. realMax = isLog ? axis.lin2log(axis.max) : axis.max;
  17560. if (threshold === null || threshold === -Infinity) {
  17561. threshold = realMin;
  17562. } else if (threshold === Infinity) {
  17563. threshold = realMax;
  17564. } else if (realMin > threshold) {
  17565. threshold = realMin;
  17566. } else if (realMax < threshold) {
  17567. threshold = realMax;
  17568. }
  17569. return axis.translate(threshold, 0, 1, 0, 1);
  17570. },
  17571. /**
  17572. * Compute auto alignment for the axis label based on which side the axis is
  17573. * on and the given rotation for the label.
  17574. * @private
  17575. * @param {number} rotation The rotation in degrees as set by either the
  17576. * `rotation` or `autoRotation` options.
  17577. * @return {string} Can be `center`, `left` or `right`.
  17578. */
  17579. autoLabelAlign: function (rotation) {
  17580. var angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360,
  17581. evt = { align: 'center' };
  17582. fireEvent(this, 'autoLabelAlign', evt, function (e) {
  17583. if (angle > 15 && angle < 165) {
  17584. e.align = 'right';
  17585. } else if (angle > 195 && angle < 345) {
  17586. e.align = 'left';
  17587. }
  17588. });
  17589. return evt.align;
  17590. },
  17591. /**
  17592. * Get the tick length and width for the axis based on axis options.
  17593. * @private
  17594. * @param {string} prefix 'tick' or 'minorTick'
  17595. * @return {Array<number>} An array of tickLength and tickWidth
  17596. */
  17597. tickSize: function (prefix) {
  17598. var options = this.options,
  17599. tickLength = options[prefix + 'Length'],
  17600. tickWidth = pick(
  17601. options[prefix + 'Width'],
  17602. prefix === 'tick' && this.isXAxis ? 1 : 0 // X axis default 1
  17603. ),
  17604. e,
  17605. tickSize;
  17606. if (tickWidth && tickLength) {
  17607. // Negate the length
  17608. if (options[prefix + 'Position'] === 'inside') {
  17609. tickLength = -tickLength;
  17610. }
  17611. tickSize = [tickLength, tickWidth];
  17612. }
  17613. e = { tickSize: tickSize };
  17614. fireEvent(this, 'afterTickSize', e);
  17615. return e.tickSize;
  17616. },
  17617. // Return the size of the labels.
  17618. labelMetrics: function () {
  17619. var index = this.tickPositions && this.tickPositions[0] || 0;
  17620. return this.chart.renderer.fontMetrics(
  17621. this.options.labels.style && this.options.labels.style.fontSize,
  17622. this.ticks[index] && this.ticks[index].label
  17623. );
  17624. },
  17625. // Prevent the ticks from getting so close we can't draw the labels. On a
  17626. // horizontal axis, this is handled by rotating the labels, removing ticks
  17627. // and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
  17628. unsquish: function () {
  17629. var labelOptions = this.options.labels,
  17630. horiz = this.horiz,
  17631. tickInterval = this.tickInterval,
  17632. newTickInterval = tickInterval,
  17633. slotSize = this.len / (
  17634. ((this.categories ? 1 : 0) + this.max - this.min) / tickInterval
  17635. ),
  17636. rotation,
  17637. rotationOption = labelOptions.rotation,
  17638. labelMetrics = this.labelMetrics(),
  17639. step,
  17640. bestScore = Number.MAX_VALUE,
  17641. autoRotation,
  17642. range = this.max - this.min,
  17643. // Return the multiple of tickInterval that is needed to avoid
  17644. // collision
  17645. getStep = function (spaceNeeded) {
  17646. var step = spaceNeeded / (slotSize || 1);
  17647. step = step > 1 ? Math.ceil(step) : 1;
  17648. // Guard for very small or negative angles (#9835)
  17649. if (
  17650. step * tickInterval > range &&
  17651. spaceNeeded !== Infinity &&
  17652. slotSize !== Infinity
  17653. ) {
  17654. step = Math.ceil(range / tickInterval);
  17655. }
  17656. return correctFloat(step * tickInterval);
  17657. };
  17658. if (horiz) {
  17659. autoRotation = !labelOptions.staggerLines &&
  17660. !labelOptions.step &&
  17661. ( // #3971
  17662. defined(rotationOption) ?
  17663. [rotationOption] :
  17664. slotSize < pick(labelOptions.autoRotationLimit, 80) &&
  17665. labelOptions.autoRotation
  17666. );
  17667. if (autoRotation) {
  17668. // Loop over the given autoRotation options, and determine
  17669. // which gives the best score. The best score is that with
  17670. // the lowest number of steps and a rotation closest
  17671. // to horizontal.
  17672. autoRotation.forEach(function (rot) {
  17673. var score;
  17674. if (
  17675. rot === rotationOption ||
  17676. (rot && rot >= -90 && rot <= 90)
  17677. ) { // #3891
  17678. step = getStep(
  17679. Math.abs(labelMetrics.h / Math.sin(deg2rad * rot))
  17680. );
  17681. score = step + Math.abs(rot / 360);
  17682. if (score < bestScore) {
  17683. bestScore = score;
  17684. rotation = rot;
  17685. newTickInterval = step;
  17686. }
  17687. }
  17688. });
  17689. }
  17690. } else if (!labelOptions.step) { // #4411
  17691. newTickInterval = getStep(labelMetrics.h);
  17692. }
  17693. this.autoRotation = autoRotation;
  17694. this.labelRotation = pick(rotation, rotationOption);
  17695. return newTickInterval;
  17696. },
  17697. /**
  17698. * Get the general slot width for labels/categories on this axis. This may
  17699. * change between the pre-render (from Axis.getOffset) and the final tick
  17700. * rendering and placement.
  17701. * @private
  17702. * @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
  17703. * basing on tick label. It is used in highcharts-3d module, where the slots
  17704. * has different widths depending on perspective angles.
  17705. * @return {number} The pixel width allocated to each axis label.
  17706. */
  17707. getSlotWidth: function (tick) {
  17708. // #5086, #1580, #1931
  17709. var chart = this.chart,
  17710. horiz = this.horiz,
  17711. labelOptions = this.options.labels,
  17712. slotCount = Math.max(
  17713. this.tickPositions.length - (this.categories ? 0 : 1),
  17714. 1
  17715. ),
  17716. marginLeft = chart.margin[3];
  17717. return (
  17718. tick &&
  17719. tick.slotWidth // Used by grid axis
  17720. ) || (
  17721. horiz &&
  17722. (labelOptions.step || 0) < 2 &&
  17723. !labelOptions.rotation && // #4415
  17724. ((this.staggerLines || 1) * this.len) / slotCount
  17725. ) || (
  17726. !horiz && (
  17727. // #7028
  17728. (
  17729. labelOptions.style &&
  17730. parseInt(labelOptions.style.width, 10)
  17731. ) ||
  17732. (
  17733. marginLeft &&
  17734. (marginLeft - chart.spacing[3])
  17735. ) ||
  17736. chart.chartWidth * 0.33
  17737. )
  17738. );
  17739. },
  17740. // Render the axis labels and determine whether ellipsis or rotation need to
  17741. // be applied.
  17742. renderUnsquish: function () {
  17743. var chart = this.chart,
  17744. renderer = chart.renderer,
  17745. tickPositions = this.tickPositions,
  17746. ticks = this.ticks,
  17747. labelOptions = this.options.labels,
  17748. labelStyleOptions = (labelOptions && labelOptions.style || {}),
  17749. horiz = this.horiz,
  17750. slotWidth = this.getSlotWidth(),
  17751. innerWidth = Math.max(
  17752. 1,
  17753. Math.round(slotWidth - 2 * (labelOptions.padding || 5))
  17754. ),
  17755. attr = {},
  17756. labelMetrics = this.labelMetrics(),
  17757. textOverflowOption = labelOptions.style &&
  17758. labelOptions.style.textOverflow,
  17759. commonWidth,
  17760. commonTextOverflow,
  17761. maxLabelLength = 0,
  17762. label,
  17763. i,
  17764. pos;
  17765. // Set rotation option unless it is "auto", like in gauges
  17766. if (!isString(labelOptions.rotation)) {
  17767. attr.rotation = labelOptions.rotation || 0; // #4443
  17768. }
  17769. // Get the longest label length
  17770. tickPositions.forEach(function (tick) {
  17771. tick = ticks[tick];
  17772. if (
  17773. tick &&
  17774. tick.label &&
  17775. tick.label.textPxLength > maxLabelLength
  17776. ) {
  17777. maxLabelLength = tick.label.textPxLength;
  17778. }
  17779. });
  17780. this.maxLabelLength = maxLabelLength;
  17781. // Handle auto rotation on horizontal axis
  17782. if (this.autoRotation) {
  17783. // Apply rotation only if the label is too wide for the slot, and
  17784. // the label is wider than its height.
  17785. if (
  17786. maxLabelLength > innerWidth &&
  17787. maxLabelLength > labelMetrics.h
  17788. ) {
  17789. attr.rotation = this.labelRotation;
  17790. } else {
  17791. this.labelRotation = 0;
  17792. }
  17793. // Handle word-wrap or ellipsis on vertical axis
  17794. } else if (slotWidth) {
  17795. // For word-wrap or ellipsis
  17796. commonWidth = innerWidth;
  17797. if (!textOverflowOption) {
  17798. commonTextOverflow = 'clip';
  17799. // On vertical axis, only allow word wrap if there is room
  17800. // for more lines.
  17801. i = tickPositions.length;
  17802. while (!horiz && i--) {
  17803. pos = tickPositions[i];
  17804. label = ticks[pos].label;
  17805. if (label) {
  17806. // Reset ellipsis in order to get the correct
  17807. // bounding box (#4070)
  17808. if (
  17809. label.styles &&
  17810. label.styles.textOverflow === 'ellipsis'
  17811. ) {
  17812. label.css({ textOverflow: 'clip' });
  17813. // Set the correct width in order to read
  17814. // the bounding box height (#4678, #5034)
  17815. } else if (label.textPxLength > slotWidth) {
  17816. label.css({ width: slotWidth + 'px' });
  17817. }
  17818. if (
  17819. label.getBBox().height > (
  17820. this.len / tickPositions.length -
  17821. (labelMetrics.h - labelMetrics.f)
  17822. )
  17823. ) {
  17824. label.specificTextOverflow = 'ellipsis';
  17825. }
  17826. }
  17827. }
  17828. }
  17829. }
  17830. // Add ellipsis if the label length is significantly longer than ideal
  17831. if (attr.rotation) {
  17832. commonWidth = (
  17833. maxLabelLength > chart.chartHeight * 0.5 ?
  17834. chart.chartHeight * 0.33 :
  17835. maxLabelLength
  17836. );
  17837. if (!textOverflowOption) {
  17838. commonTextOverflow = 'ellipsis';
  17839. }
  17840. }
  17841. // Set the explicit or automatic label alignment
  17842. this.labelAlign = labelOptions.align ||
  17843. this.autoLabelAlign(this.labelRotation);
  17844. if (this.labelAlign) {
  17845. attr.align = this.labelAlign;
  17846. }
  17847. // Apply general and specific CSS
  17848. tickPositions.forEach(function (pos) {
  17849. var tick = ticks[pos],
  17850. label = tick && tick.label,
  17851. widthOption = labelStyleOptions.width,
  17852. css = {};
  17853. if (label) {
  17854. // This needs to go before the CSS in old IE (#4502)
  17855. label.attr(attr);
  17856. if (tick.shortenLabel) {
  17857. tick.shortenLabel();
  17858. } else if (
  17859. commonWidth &&
  17860. !widthOption &&
  17861. // Setting width in this case messes with the bounding box
  17862. // (#7975)
  17863. labelStyleOptions.whiteSpace !== 'nowrap' &&
  17864. (
  17865. // Speed optimizing, #7656
  17866. commonWidth < label.textPxLength ||
  17867. // Resetting CSS, #4928
  17868. label.element.tagName === 'SPAN'
  17869. )
  17870. ) {
  17871. css.width = commonWidth;
  17872. if (!textOverflowOption) {
  17873. css.textOverflow = (
  17874. label.specificTextOverflow ||
  17875. commonTextOverflow
  17876. );
  17877. }
  17878. label.css(css);
  17879. // Reset previously shortened label (#8210)
  17880. } else if (
  17881. label.styles &&
  17882. label.styles.width &&
  17883. !css.width &&
  17884. !widthOption
  17885. ) {
  17886. label.css({ width: null });
  17887. }
  17888. delete label.specificTextOverflow;
  17889. tick.rotation = attr.rotation;
  17890. }
  17891. }, this);
  17892. // Note: Why is this not part of getLabelPosition?
  17893. this.tickRotCorr = renderer.rotCorr(
  17894. labelMetrics.b,
  17895. this.labelRotation || 0,
  17896. this.side !== 0
  17897. );
  17898. },
  17899. /**
  17900. * Return true if the axis has associated data.
  17901. *
  17902. * @function Highcharts.Axis#hasData
  17903. *
  17904. * @return {boolean}
  17905. * True if the axis has associated visible series and those series
  17906. * have either valid data points or explicit `min` and `max`
  17907. * settings.
  17908. */
  17909. hasData: function () {
  17910. return (
  17911. this.hasVisibleSeries ||
  17912. (
  17913. defined(this.min) &&
  17914. defined(this.max) &&
  17915. this.tickPositions &&
  17916. this.tickPositions.length > 0
  17917. )
  17918. );
  17919. },
  17920. /**
  17921. * Adds the title defined in axis.options.title.
  17922. *
  17923. * @function Highcharts.Axis#addTitle
  17924. *
  17925. * @param {boolean} display
  17926. * Whether or not to display the title.
  17927. */
  17928. addTitle: function (display) {
  17929. var axis = this,
  17930. renderer = axis.chart.renderer,
  17931. horiz = axis.horiz,
  17932. opposite = axis.opposite,
  17933. options = axis.options,
  17934. axisTitleOptions = options.title,
  17935. textAlign,
  17936. styledMode = axis.chart.styledMode;
  17937. if (!axis.axisTitle) {
  17938. textAlign = axisTitleOptions.textAlign;
  17939. if (!textAlign) {
  17940. textAlign = (horiz ? {
  17941. low: 'left',
  17942. middle: 'center',
  17943. high: 'right'
  17944. } : {
  17945. low: opposite ? 'right' : 'left',
  17946. middle: 'center',
  17947. high: opposite ? 'left' : 'right'
  17948. })[axisTitleOptions.align];
  17949. }
  17950. axis.axisTitle = renderer.text(
  17951. axisTitleOptions.text,
  17952. 0,
  17953. 0,
  17954. axisTitleOptions.useHTML
  17955. )
  17956. .attr({
  17957. zIndex: 7,
  17958. rotation: axisTitleOptions.rotation || 0,
  17959. align: textAlign
  17960. })
  17961. .addClass('highcharts-axis-title');
  17962. // #7814, don't mutate style option
  17963. if (!styledMode) {
  17964. axis.axisTitle.css(merge(axisTitleOptions.style));
  17965. }
  17966. axis.axisTitle.add(axis.axisGroup);
  17967. axis.axisTitle.isNew = true;
  17968. }
  17969. // Max width defaults to the length of the axis
  17970. if (!styledMode && !axisTitleOptions.style.width && !axis.isRadial) {
  17971. axis.axisTitle.css({
  17972. width: axis.len
  17973. });
  17974. }
  17975. // hide or show the title depending on whether showEmpty is set
  17976. axis.axisTitle[display ? 'show' : 'hide'](true);
  17977. },
  17978. /**
  17979. * Generates a tick for initial positioning.
  17980. * @private
  17981. * @param {number} pos The tick position in axis values.
  17982. * @param {number} i The index of the tick in {@link Axis.tickPositions}.
  17983. */
  17984. generateTick: function (pos) {
  17985. var ticks = this.ticks;
  17986. if (!ticks[pos]) {
  17987. ticks[pos] = new Tick(this, pos);
  17988. } else {
  17989. ticks[pos].addLabel(); // update labels depending on tick interval
  17990. }
  17991. },
  17992. /**
  17993. * Render the tick labels to a preliminary position to get their sizes
  17994. * @private
  17995. * @fires Highcharts.Axis#event:afterGetOffset
  17996. */
  17997. getOffset: function () {
  17998. var axis = this,
  17999. chart = axis.chart,
  18000. renderer = chart.renderer,
  18001. options = axis.options,
  18002. tickPositions = axis.tickPositions,
  18003. ticks = axis.ticks,
  18004. horiz = axis.horiz,
  18005. side = axis.side,
  18006. invertedSide = chart.inverted &&
  18007. !axis.isZAxis ? [1, 0, 3, 2][side] : side,
  18008. hasData,
  18009. showAxis,
  18010. titleOffset = 0,
  18011. titleOffsetOption,
  18012. titleMargin = 0,
  18013. axisTitleOptions = options.title,
  18014. labelOptions = options.labels,
  18015. labelOffset = 0, // reset
  18016. labelOffsetPadded,
  18017. axisOffset = chart.axisOffset,
  18018. clipOffset = chart.clipOffset,
  18019. clip,
  18020. directionFactor = [-1, 1, 1, -1][side],
  18021. className = options.className,
  18022. axisParent = axis.axisParent, // Used in color axis
  18023. lineHeightCorrection,
  18024. tickSize;
  18025. // For reuse in Axis.render
  18026. hasData = axis.hasData();
  18027. axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
  18028. // Set/reset staggerLines
  18029. axis.staggerLines = axis.horiz && labelOptions.staggerLines;
  18030. // Create the axisGroup and gridGroup elements on first iteration
  18031. if (!axis.axisGroup) {
  18032. axis.gridGroup = renderer.g('grid')
  18033. .attr({ zIndex: options.gridZIndex || 1 })
  18034. .addClass(
  18035. 'highcharts-' + this.coll.toLowerCase() + '-grid ' +
  18036. (className || '')
  18037. )
  18038. .add(axisParent);
  18039. axis.axisGroup = renderer.g('axis')
  18040. .attr({ zIndex: options.zIndex || 2 })
  18041. .addClass(
  18042. 'highcharts-' + this.coll.toLowerCase() + ' ' +
  18043. (className || '')
  18044. )
  18045. .add(axisParent);
  18046. axis.labelGroup = renderer.g('axis-labels')
  18047. .attr({ zIndex: labelOptions.zIndex || 7 })
  18048. .addClass(
  18049. 'highcharts-' + axis.coll.toLowerCase() + '-labels ' +
  18050. (className || '')
  18051. )
  18052. .add(axisParent);
  18053. }
  18054. if (hasData || axis.isLinked) {
  18055. // Generate ticks
  18056. tickPositions.forEach(function (pos, i) {
  18057. // i is not used here, but may be used in overrides
  18058. axis.generateTick(pos, i);
  18059. });
  18060. axis.renderUnsquish();
  18061. // Left side must be align: right and right side must
  18062. // have align: left for labels
  18063. axis.reserveSpaceDefault = (
  18064. side === 0 ||
  18065. side === 2 ||
  18066. { 1: 'left', 3: 'right' }[side] === axis.labelAlign
  18067. );
  18068. if (pick(
  18069. labelOptions.reserveSpace,
  18070. axis.labelAlign === 'center' ? true : null,
  18071. axis.reserveSpaceDefault
  18072. )) {
  18073. tickPositions.forEach(function (pos) {
  18074. // get the highest offset
  18075. labelOffset = Math.max(
  18076. ticks[pos].getLabelSize(),
  18077. labelOffset
  18078. );
  18079. });
  18080. }
  18081. if (axis.staggerLines) {
  18082. labelOffset *= axis.staggerLines;
  18083. }
  18084. axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
  18085. } else { // doesn't have data
  18086. objectEach(ticks, function (tick, n) {
  18087. tick.destroy();
  18088. delete ticks[n];
  18089. });
  18090. }
  18091. if (
  18092. axisTitleOptions &&
  18093. axisTitleOptions.text &&
  18094. axisTitleOptions.enabled !== false
  18095. ) {
  18096. axis.addTitle(showAxis);
  18097. if (showAxis && axisTitleOptions.reserveSpace !== false) {
  18098. axis.titleOffset = titleOffset =
  18099. axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
  18100. titleOffsetOption = axisTitleOptions.offset;
  18101. titleMargin = defined(titleOffsetOption) ?
  18102. 0 :
  18103. pick(axisTitleOptions.margin, horiz ? 5 : 10);
  18104. }
  18105. }
  18106. // Render the axis line
  18107. axis.renderLine();
  18108. // handle automatic or user set offset
  18109. axis.offset = directionFactor * pick(options.offset, axisOffset[side]);
  18110. axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
  18111. if (side === 0) {
  18112. lineHeightCorrection = -axis.labelMetrics().h;
  18113. } else if (side === 2) {
  18114. lineHeightCorrection = axis.tickRotCorr.y;
  18115. } else {
  18116. lineHeightCorrection = 0;
  18117. }
  18118. // Find the padded label offset
  18119. labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
  18120. if (labelOffset) {
  18121. labelOffsetPadded -= lineHeightCorrection;
  18122. labelOffsetPadded += directionFactor * (
  18123. horiz ?
  18124. pick(
  18125. labelOptions.y,
  18126. axis.tickRotCorr.y + directionFactor * 8
  18127. ) :
  18128. labelOptions.x
  18129. );
  18130. }
  18131. axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
  18132. if (axis.getMaxLabelDimensions) {
  18133. axis.maxLabelDimensions = axis.getMaxLabelDimensions(
  18134. ticks,
  18135. tickPositions
  18136. );
  18137. }
  18138. // Due to GridAxis.tickSize, tickSize should be calculated after ticks
  18139. // has rendered.
  18140. tickSize = this.tickSize('tick');
  18141. axisOffset[side] = Math.max(
  18142. axisOffset[side],
  18143. axis.axisTitleMargin + titleOffset + directionFactor * axis.offset,
  18144. labelOffsetPadded, // #3027
  18145. hasData && tickPositions.length && tickSize ?
  18146. tickSize[0] + directionFactor * axis.offset :
  18147. 0 // #4866
  18148. );
  18149. // Decide the clipping needed to keep the graph inside
  18150. // the plot area and axis lines
  18151. clip = options.offset ?
  18152. 0 :
  18153. Math.floor(axis.axisLine.strokeWidth() / 2) * 2; // #4308, #4371
  18154. clipOffset[invertedSide] = Math.max(clipOffset[invertedSide], clip);
  18155. fireEvent(this, 'afterGetOffset');
  18156. },
  18157. /**
  18158. * Internal function to get the path for the axis line. Extended for polar
  18159. * charts.
  18160. *
  18161. * @function Highcharts.Axis#getLinePath
  18162. *
  18163. * @param {number} lineWidth
  18164. * The line width in pixels.
  18165. *
  18166. * @return {Highcharts.SVGPathArray}
  18167. * The SVG path definition in array form.
  18168. */
  18169. getLinePath: function (lineWidth) {
  18170. var chart = this.chart,
  18171. opposite = this.opposite,
  18172. offset = this.offset,
  18173. horiz = this.horiz,
  18174. lineLeft = this.left + (opposite ? this.width : 0) + offset,
  18175. lineTop = chart.chartHeight - this.bottom -
  18176. (opposite ? this.height : 0) + offset;
  18177. if (opposite) {
  18178. lineWidth *= -1; // crispify the other way - #1480, #1687
  18179. }
  18180. return chart.renderer
  18181. .crispLine([
  18182. 'M',
  18183. horiz ?
  18184. this.left :
  18185. lineLeft,
  18186. horiz ?
  18187. lineTop :
  18188. this.top,
  18189. 'L',
  18190. horiz ?
  18191. chart.chartWidth - this.right :
  18192. lineLeft,
  18193. horiz ?
  18194. lineTop :
  18195. chart.chartHeight - this.bottom
  18196. ], lineWidth);
  18197. },
  18198. /**
  18199. * Render the axis line. Called internally when rendering and redrawing the
  18200. * axis.
  18201. *
  18202. * @function Highcharts.Axis#renderLine
  18203. */
  18204. renderLine: function () {
  18205. if (!this.axisLine) {
  18206. this.axisLine = this.chart.renderer.path()
  18207. .addClass('highcharts-axis-line')
  18208. .add(this.axisGroup);
  18209. if (!this.chart.styledMode) {
  18210. this.axisLine.attr({
  18211. stroke: this.options.lineColor,
  18212. 'stroke-width': this.options.lineWidth,
  18213. zIndex: 7
  18214. });
  18215. }
  18216. }
  18217. },
  18218. /**
  18219. * Position the axis title.
  18220. * @private
  18221. * @return {Highcharts.PositionObject} X and Y positions for the title.
  18222. */
  18223. getTitlePosition: function () {
  18224. // compute anchor points for each of the title align options
  18225. var horiz = this.horiz,
  18226. axisLeft = this.left,
  18227. axisTop = this.top,
  18228. axisLength = this.len,
  18229. axisTitleOptions = this.options.title,
  18230. margin = horiz ? axisLeft : axisTop,
  18231. opposite = this.opposite,
  18232. offset = this.offset,
  18233. xOption = axisTitleOptions.x || 0,
  18234. yOption = axisTitleOptions.y || 0,
  18235. axisTitle = this.axisTitle,
  18236. fontMetrics = this.chart.renderer.fontMetrics(
  18237. axisTitleOptions.style && axisTitleOptions.style.fontSize,
  18238. axisTitle
  18239. ),
  18240. // The part of a multiline text that is below the baseline of the
  18241. // first line. Subtract 1 to preserve pixel-perfectness from the
  18242. // old behaviour (v5.0.12), where only one line was allowed.
  18243. textHeightOvershoot = Math.max(
  18244. axisTitle.getBBox(null, 0).height - fontMetrics.h - 1,
  18245. 0
  18246. ),
  18247. // the position in the length direction of the axis
  18248. alongAxis = {
  18249. low: margin + (horiz ? 0 : axisLength),
  18250. middle: margin + axisLength / 2,
  18251. high: margin + (horiz ? axisLength : 0)
  18252. }[axisTitleOptions.align],
  18253. // the position in the perpendicular direction of the axis
  18254. offAxis = (horiz ? axisTop + this.height : axisLeft) +
  18255. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  18256. (opposite ? -1 : 1) * // so does opposite axes
  18257. this.axisTitleMargin +
  18258. [
  18259. -textHeightOvershoot, // top
  18260. textHeightOvershoot, // right
  18261. fontMetrics.f, // bottom
  18262. -textHeightOvershoot // left
  18263. ][this.side],
  18264. titlePosition = {
  18265. x: horiz ?
  18266. alongAxis + xOption :
  18267. offAxis + (opposite ? this.width : 0) + offset + xOption,
  18268. y: horiz ?
  18269. offAxis + yOption - (opposite ? this.height : 0) + offset :
  18270. alongAxis + yOption
  18271. };
  18272. fireEvent(
  18273. this,
  18274. 'afterGetTitlePosition',
  18275. { titlePosition: titlePosition }
  18276. );
  18277. return titlePosition;
  18278. },
  18279. /**
  18280. * Render a minor tick into the given position. If a minor tick already
  18281. * exists in this position, move it.
  18282. *
  18283. * @function Highcharts.Axis#renderMinorTick
  18284. *
  18285. * @param {number} pos
  18286. * The position in axis values.
  18287. */
  18288. renderMinorTick: function (pos) {
  18289. var slideInTicks = this.chart.hasRendered && isNumber(this.oldMin),
  18290. minorTicks = this.minorTicks;
  18291. if (!minorTicks[pos]) {
  18292. minorTicks[pos] = new Tick(this, pos, 'minor');
  18293. }
  18294. // Render new ticks in old position
  18295. if (slideInTicks && minorTicks[pos].isNew) {
  18296. minorTicks[pos].render(null, true);
  18297. }
  18298. minorTicks[pos].render(null, false, 1);
  18299. },
  18300. /**
  18301. * Render a major tick into the given position. If a tick already exists
  18302. * in this position, move it.
  18303. *
  18304. * @function Highcharts.Axis#renderTick
  18305. *
  18306. * @param {number} pos
  18307. * The position in axis values.
  18308. *
  18309. * @param {number} i
  18310. * The tick index.
  18311. */
  18312. renderTick: function (pos, i) {
  18313. var isLinked = this.isLinked,
  18314. ticks = this.ticks,
  18315. slideInTicks = this.chart.hasRendered && isNumber(this.oldMin);
  18316. // Linked axes need an extra check to find out if
  18317. if (!isLinked || (pos >= this.min && pos <= this.max)) {
  18318. if (!ticks[pos]) {
  18319. ticks[pos] = new Tick(this, pos);
  18320. }
  18321. // NOTE this seems like overkill. Could be handled in tick.render by
  18322. // setting old position in attr, then set new position in animate.
  18323. // render new ticks in old position
  18324. if (slideInTicks && ticks[pos].isNew) {
  18325. // Start with negative opacity so that it is visible from
  18326. // halfway into the animation
  18327. ticks[pos].render(i, true, -1);
  18328. }
  18329. ticks[pos].render(i);
  18330. }
  18331. },
  18332. /**
  18333. * Render the axis.
  18334. * @private
  18335. * @fires Highcharts.Axis#event:afterRender
  18336. */
  18337. render: function () {
  18338. var axis = this,
  18339. chart = axis.chart,
  18340. renderer = chart.renderer,
  18341. options = axis.options,
  18342. isLog = axis.isLog,
  18343. isLinked = axis.isLinked,
  18344. tickPositions = axis.tickPositions,
  18345. axisTitle = axis.axisTitle,
  18346. ticks = axis.ticks,
  18347. minorTicks = axis.minorTicks,
  18348. alternateBands = axis.alternateBands,
  18349. stackLabelOptions = options.stackLabels,
  18350. alternateGridColor = options.alternateGridColor,
  18351. tickmarkOffset = axis.tickmarkOffset,
  18352. axisLine = axis.axisLine,
  18353. showAxis = axis.showAxis,
  18354. animation = animObject(renderer.globalAnimation),
  18355. from,
  18356. to;
  18357. // Reset
  18358. axis.labelEdge.length = 0;
  18359. axis.overlap = false;
  18360. // Mark all elements inActive before we go over and mark the active ones
  18361. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  18362. objectEach(coll, function (tick) {
  18363. tick.isActive = false;
  18364. });
  18365. });
  18366. // If the series has data draw the ticks. Else only the line and title
  18367. if (axis.hasData() || isLinked) {
  18368. // minor ticks
  18369. if (axis.minorTickInterval && !axis.categories) {
  18370. axis.getMinorTickPositions().forEach(function (pos) {
  18371. axis.renderMinorTick(pos);
  18372. });
  18373. }
  18374. // Major ticks. Pull out the first item and render it last so that
  18375. // we can get the position of the neighbour label. #808.
  18376. if (tickPositions.length) { // #1300
  18377. tickPositions.forEach(function (pos, i) {
  18378. axis.renderTick(pos, i);
  18379. });
  18380. // In a categorized axis, the tick marks are displayed
  18381. // between labels. So we need to add a tick mark and
  18382. // grid line at the left edge of the X axis.
  18383. if (tickmarkOffset && (axis.min === 0 || axis.single)) {
  18384. if (!ticks[-1]) {
  18385. ticks[-1] = new Tick(axis, -1, null, true);
  18386. }
  18387. ticks[-1].render(-1);
  18388. }
  18389. }
  18390. // alternate grid color
  18391. if (alternateGridColor) {
  18392. tickPositions.forEach(function (pos, i) {
  18393. to = tickPositions[i + 1] !== undefined ?
  18394. tickPositions[i + 1] + tickmarkOffset :
  18395. axis.max - tickmarkOffset;
  18396. if (
  18397. i % 2 === 0 &&
  18398. pos < axis.max &&
  18399. to <= axis.max + (
  18400. chart.polar ?
  18401. -tickmarkOffset :
  18402. tickmarkOffset
  18403. )
  18404. ) { // #2248, #4660
  18405. if (!alternateBands[pos]) {
  18406. alternateBands[pos] = new H.PlotLineOrBand(axis);
  18407. }
  18408. from = pos + tickmarkOffset; // #949
  18409. alternateBands[pos].options = {
  18410. from: isLog ? axis.lin2log(from) : from,
  18411. to: isLog ? axis.lin2log(to) : to,
  18412. color: alternateGridColor
  18413. };
  18414. alternateBands[pos].render();
  18415. alternateBands[pos].isActive = true;
  18416. }
  18417. });
  18418. }
  18419. // custom plot lines and bands
  18420. if (!axis._addedPlotLB) { // only first time
  18421. (
  18422. (options.plotLines || []).concat(options.plotBands || [])
  18423. ).forEach(
  18424. function (plotLineOptions) {
  18425. axis.addPlotBandOrLine(plotLineOptions);
  18426. }
  18427. );
  18428. axis._addedPlotLB = true;
  18429. }
  18430. } // end if hasData
  18431. // Remove inactive ticks
  18432. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  18433. var i,
  18434. forDestruction = [],
  18435. delay = animation.duration,
  18436. destroyInactiveItems = function () {
  18437. i = forDestruction.length;
  18438. while (i--) {
  18439. // When resizing rapidly, the same items
  18440. // may be destroyed in different timeouts,
  18441. // or the may be reactivated
  18442. if (
  18443. coll[forDestruction[i]] &&
  18444. !coll[forDestruction[i]].isActive
  18445. ) {
  18446. coll[forDestruction[i]].destroy();
  18447. delete coll[forDestruction[i]];
  18448. }
  18449. }
  18450. };
  18451. objectEach(coll, function (tick, pos) {
  18452. if (!tick.isActive) {
  18453. // Render to zero opacity
  18454. tick.render(pos, false, 0);
  18455. tick.isActive = false;
  18456. forDestruction.push(pos);
  18457. }
  18458. });
  18459. // When the objects are finished fading out, destroy them
  18460. syncTimeout(
  18461. destroyInactiveItems,
  18462. coll === alternateBands ||
  18463. !chart.hasRendered ||
  18464. !delay ?
  18465. 0 :
  18466. delay
  18467. );
  18468. });
  18469. // Set the axis line path
  18470. if (axisLine) {
  18471. axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
  18472. d: this.getLinePath(axisLine.strokeWidth())
  18473. });
  18474. axisLine.isPlaced = true;
  18475. // Show or hide the line depending on options.showEmpty
  18476. axisLine[showAxis ? 'show' : 'hide'](true);
  18477. }
  18478. if (axisTitle && showAxis) {
  18479. var titleXy = axis.getTitlePosition();
  18480. if (isNumber(titleXy.y)) {
  18481. axisTitle[axisTitle.isNew ? 'attr' : 'animate'](titleXy);
  18482. axisTitle.isNew = false;
  18483. } else {
  18484. axisTitle.attr('y', -9999);
  18485. axisTitle.isNew = true;
  18486. }
  18487. }
  18488. // Stacked totals:
  18489. if (stackLabelOptions && stackLabelOptions.enabled) {
  18490. axis.renderStackTotals();
  18491. }
  18492. // End stacked totals
  18493. axis.isDirty = false;
  18494. fireEvent(this, 'afterRender');
  18495. },
  18496. // Redraw the axis to reflect changes in the data or axis extremes. Called
  18497. // internally from Highcharts.Chart#redraw.
  18498. redraw: function () {
  18499. if (this.visible) {
  18500. // render the axis
  18501. this.render();
  18502. // move plot lines and bands
  18503. this.plotLinesAndBands.forEach(function (plotLine) {
  18504. plotLine.render();
  18505. });
  18506. }
  18507. // mark associated series as dirty and ready for redraw
  18508. this.series.forEach(function (series) {
  18509. series.isDirty = true;
  18510. });
  18511. },
  18512. // Properties to survive after destroy, needed for Axis.update (#4317,
  18513. // #5773, #5881).
  18514. keepProps: ['extKey', 'hcEvents', 'names', 'series', 'userMax', 'userMin'],
  18515. /**
  18516. * Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
  18517. * to fully remove the axis.
  18518. * @private
  18519. * @param {boolean} keepEvents Whether to preserve events, used internally
  18520. * in Axis.update.
  18521. */
  18522. destroy: function (keepEvents) {
  18523. var axis = this,
  18524. stacks = axis.stacks,
  18525. plotLinesAndBands = axis.plotLinesAndBands,
  18526. plotGroup,
  18527. i;
  18528. fireEvent(this, 'destroy', { keepEvents: keepEvents });
  18529. // Remove the events
  18530. if (!keepEvents) {
  18531. removeEvent(axis);
  18532. }
  18533. // Destroy each stack total
  18534. objectEach(stacks, function (stack, stackKey) {
  18535. destroyObjectProperties(stack);
  18536. stacks[stackKey] = null;
  18537. });
  18538. // Destroy collections
  18539. [axis.ticks, axis.minorTicks, axis.alternateBands].forEach(
  18540. function (coll) {
  18541. destroyObjectProperties(coll);
  18542. }
  18543. );
  18544. if (plotLinesAndBands) {
  18545. i = plotLinesAndBands.length;
  18546. while (i--) { // #1975
  18547. plotLinesAndBands[i].destroy();
  18548. }
  18549. }
  18550. // Destroy elements
  18551. ['stackTotalGroup', 'axisLine', 'axisTitle', 'axisGroup',
  18552. 'gridGroup', 'labelGroup', 'cross', 'scrollbar'].forEach(
  18553. function (prop) {
  18554. if (axis[prop]) {
  18555. axis[prop] = axis[prop].destroy();
  18556. }
  18557. }
  18558. );
  18559. // Destroy each generated group for plotlines and plotbands
  18560. for (plotGroup in axis.plotLinesAndBandsGroups) {
  18561. axis.plotLinesAndBandsGroups[plotGroup] =
  18562. axis.plotLinesAndBandsGroups[plotGroup].destroy();
  18563. }
  18564. // Delete all properties and fall back to the prototype.
  18565. objectEach(axis, function (val, key) {
  18566. if (axis.keepProps.indexOf(key) === -1) {
  18567. delete axis[key];
  18568. }
  18569. });
  18570. },
  18571. /**
  18572. * Internal function to draw a crosshair.
  18573. *
  18574. * @function Highcharts.Axis#drawCrosshair
  18575. *
  18576. * @param {Highcharts.PointerEventObject} [e]
  18577. * The event arguments from the modified pointer event, extended with
  18578. * `chartX` and `chartY`
  18579. *
  18580. * @param {Highcharts.Point} [point]
  18581. * The Point object if the crosshair snaps to points.
  18582. *
  18583. * @fires Highcharts.Axis#event:afterDrawCrosshair
  18584. * @fires Highcharts.Axis#event:drawCrosshair
  18585. */
  18586. drawCrosshair: function (e, point) {
  18587. var path,
  18588. options = this.crosshair,
  18589. snap = pick(options.snap, true),
  18590. pos,
  18591. categorized,
  18592. graphic = this.cross;
  18593. fireEvent(this, 'drawCrosshair', { e: e, point: point });
  18594. // Use last available event when updating non-snapped crosshairs without
  18595. // mouse interaction (#5287)
  18596. if (!e) {
  18597. e = this.cross && this.cross.e;
  18598. }
  18599. if (
  18600. // Disabled in options
  18601. !this.crosshair ||
  18602. // Snap
  18603. ((defined(point) || !snap) === false)
  18604. ) {
  18605. this.hideCrosshair();
  18606. } else {
  18607. // Get the path
  18608. if (!snap) {
  18609. pos = e &&
  18610. (
  18611. this.horiz ?
  18612. e.chartX - this.pos :
  18613. this.len - e.chartY + this.pos
  18614. );
  18615. } else if (defined(point)) {
  18616. // #3834
  18617. pos = pick(
  18618. point.crosshairPos, // 3D axis extension
  18619. this.isXAxis ? point.plotX : this.len - point.plotY
  18620. );
  18621. }
  18622. if (defined(pos)) {
  18623. path = this.getPlotLinePath(
  18624. // First argument, value, only used on radial
  18625. point && (this.isXAxis ?
  18626. point.x :
  18627. pick(point.stackY, point.y)
  18628. ),
  18629. null,
  18630. null,
  18631. null,
  18632. pos // Translated position
  18633. ) || null; // #3189
  18634. }
  18635. if (!defined(path)) {
  18636. this.hideCrosshair();
  18637. return;
  18638. }
  18639. categorized = this.categories && !this.isRadial;
  18640. // Draw the cross
  18641. if (!graphic) {
  18642. this.cross = graphic = this.chart.renderer
  18643. .path()
  18644. .addClass(
  18645. 'highcharts-crosshair highcharts-crosshair-' +
  18646. (categorized ? 'category ' : 'thin ') +
  18647. options.className
  18648. )
  18649. .attr({
  18650. zIndex: pick(options.zIndex, 2)
  18651. })
  18652. .add();
  18653. // Presentational attributes
  18654. if (!this.chart.styledMode) {
  18655. graphic.attr({
  18656. 'stroke': options.color ||
  18657. (
  18658. categorized ?
  18659. color('#ccd6eb')
  18660. .setOpacity(0.25).get() :
  18661. '#cccccc'
  18662. ),
  18663. 'stroke-width': pick(options.width, 1)
  18664. }).css({
  18665. 'pointer-events': 'none'
  18666. });
  18667. if (options.dashStyle) {
  18668. graphic.attr({
  18669. dashstyle: options.dashStyle
  18670. });
  18671. }
  18672. }
  18673. }
  18674. graphic.show().attr({
  18675. d: path
  18676. });
  18677. if (categorized && !options.width) {
  18678. graphic.attr({
  18679. 'stroke-width': this.transA
  18680. });
  18681. }
  18682. this.cross.e = e;
  18683. }
  18684. fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
  18685. },
  18686. /**
  18687. * Hide the crosshair if visible.
  18688. *
  18689. * @function Highcharts.Axis#hideCrosshair
  18690. */
  18691. hideCrosshair: function () {
  18692. if (this.cross) {
  18693. this.cross.hide();
  18694. }
  18695. fireEvent(this, 'afterHideCrosshair');
  18696. }
  18697. }); // end Axis
  18698. H.Axis = Axis;
  18699. return Axis;
  18700. }(Highcharts));
  18701. (function (H) {
  18702. /**
  18703. * (c) 2010-2019 Torstein Honsi
  18704. *
  18705. * License: www.highcharts.com/license
  18706. */
  18707. var Axis = H.Axis,
  18708. getMagnitude = H.getMagnitude,
  18709. normalizeTickInterval = H.normalizeTickInterval,
  18710. timeUnits = H.timeUnits;
  18711. /**
  18712. * Set the tick positions to a time unit that makes sense, for example
  18713. * on the first of each month or on every Monday. Return an array
  18714. * with the time positions. Used in datetime axes as well as for grouping
  18715. * data on a datetime axis.
  18716. *
  18717. * @private
  18718. * @function Highcharts.Axis#getTimeTicks
  18719. *
  18720. * @param {*} normalizedInterval
  18721. * The interval in axis values (ms) and thecount
  18722. *
  18723. * @param {number} min
  18724. * The minimum in axis values
  18725. *
  18726. * @param {number} max
  18727. * The maximum in axis values
  18728. *
  18729. * @param {number} startOfWeek
  18730. *
  18731. * @return {number}
  18732. */
  18733. Axis.prototype.getTimeTicks = function () {
  18734. return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
  18735. };
  18736. /**
  18737. * Get a normalized tick interval for dates. Returns a configuration object with
  18738. * unit range (interval), count and name. Used to prepare data for getTimeTicks.
  18739. * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
  18740. * of segments in stock charts, the normalizing logic was extracted in order to
  18741. * prevent it for running over again for each segment having the same interval.
  18742. * #662, #697.
  18743. *
  18744. * @private
  18745. * @function Highcharts.Axis#normalizeTimeTickInterval
  18746. *
  18747. * @param {number} tickInterval
  18748. *
  18749. * @param {Array<Array<number|string>>} [unitsOption]
  18750. *
  18751. * @return {*}
  18752. */
  18753. Axis.prototype.normalizeTimeTickInterval = function (
  18754. tickInterval,
  18755. unitsOption
  18756. ) {
  18757. var units = unitsOption || [[
  18758. 'millisecond', // unit name
  18759. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  18760. ], [
  18761. 'second',
  18762. [1, 2, 5, 10, 15, 30]
  18763. ], [
  18764. 'minute',
  18765. [1, 2, 5, 10, 15, 30]
  18766. ], [
  18767. 'hour',
  18768. [1, 2, 3, 4, 6, 8, 12]
  18769. ], [
  18770. 'day',
  18771. [1, 2]
  18772. ], [
  18773. 'week',
  18774. [1, 2]
  18775. ], [
  18776. 'month',
  18777. [1, 2, 3, 4, 6]
  18778. ], [
  18779. 'year',
  18780. null
  18781. ]],
  18782. unit = units[units.length - 1], // default unit is years
  18783. interval = timeUnits[unit[0]],
  18784. multiples = unit[1],
  18785. count,
  18786. i;
  18787. // loop through the units to find the one that best fits the tickInterval
  18788. for (i = 0; i < units.length; i++) {
  18789. unit = units[i];
  18790. interval = timeUnits[unit[0]];
  18791. multiples = unit[1];
  18792. if (units[i + 1]) {
  18793. // lessThan is in the middle between the highest multiple and the
  18794. // next unit.
  18795. var lessThan = (interval * multiples[multiples.length - 1] +
  18796. timeUnits[units[i + 1][0]]) / 2;
  18797. // break and keep the current unit
  18798. if (tickInterval <= lessThan) {
  18799. break;
  18800. }
  18801. }
  18802. }
  18803. // prevent 2.5 years intervals, though 25, 250 etc. are allowed
  18804. if (interval === timeUnits.year && tickInterval < 5 * interval) {
  18805. multiples = [1, 2, 5];
  18806. }
  18807. // get the count
  18808. count = normalizeTickInterval(
  18809. tickInterval / interval,
  18810. multiples,
  18811. unit[0] === 'year' ?
  18812. Math.max(getMagnitude(tickInterval / interval), 1) : // #1913, #2360
  18813. 1
  18814. );
  18815. return {
  18816. unitRange: interval,
  18817. count: count,
  18818. unitName: unit[0]
  18819. };
  18820. };
  18821. }(Highcharts));
  18822. (function (H) {
  18823. /**
  18824. * (c) 2010-2019 Torstein Honsi
  18825. *
  18826. * License: www.highcharts.com/license
  18827. */
  18828. var Axis = H.Axis,
  18829. getMagnitude = H.getMagnitude,
  18830. normalizeTickInterval = H.normalizeTickInterval,
  18831. pick = H.pick;
  18832. /*
  18833. * Methods defined on the Axis prototype
  18834. */
  18835. /**
  18836. * Set the tick positions of a logarithmic axis.
  18837. *
  18838. * @private
  18839. * @function Highcharts.Axis#getLogTickPositions
  18840. *
  18841. * @param {number} interval
  18842. *
  18843. * @param {number} min
  18844. *
  18845. * @param {number} max
  18846. *
  18847. * @param {number} minor
  18848. *
  18849. * @return {Array<number>}
  18850. */
  18851. Axis.prototype.getLogTickPositions = function (interval, min, max, minor) {
  18852. var axis = this,
  18853. options = axis.options,
  18854. axisLength = axis.len,
  18855. // Since we use this method for both major and minor ticks,
  18856. // use a local variable and return the result
  18857. positions = [];
  18858. // Reset
  18859. if (!minor) {
  18860. axis._minorAutoInterval = null;
  18861. }
  18862. // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
  18863. if (interval >= 0.5) {
  18864. interval = Math.round(interval);
  18865. positions = axis.getLinearTickPositions(interval, min, max);
  18866. // Second case: We need intermediary ticks. For example
  18867. // 1, 2, 4, 6, 8, 10, 20, 40 etc.
  18868. } else if (interval >= 0.08) {
  18869. var roundedMin = Math.floor(min),
  18870. intermediate,
  18871. i,
  18872. j,
  18873. len,
  18874. pos,
  18875. lastPos,
  18876. break2;
  18877. if (interval > 0.3) {
  18878. intermediate = [1, 2, 4];
  18879. // 0.2 equals five minor ticks per 1, 10, 100 etc
  18880. } else if (interval > 0.15) {
  18881. intermediate = [1, 2, 4, 6, 8];
  18882. } else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
  18883. intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  18884. }
  18885. for (i = roundedMin; i < max + 1 && !break2; i++) {
  18886. len = intermediate.length;
  18887. for (j = 0; j < len && !break2; j++) {
  18888. pos = axis.log2lin(axis.lin2log(i) * intermediate[j]);
  18889. // #1670, lastPos is #3113
  18890. if (
  18891. pos > min &&
  18892. (!minor || lastPos <= max) &&
  18893. lastPos !== undefined
  18894. ) {
  18895. positions.push(lastPos);
  18896. }
  18897. if (lastPos > max) {
  18898. break2 = true;
  18899. }
  18900. lastPos = pos;
  18901. }
  18902. }
  18903. // Third case: We are so deep in between whole logarithmic values that
  18904. // we might as well handle the tick positions like a linear axis. For
  18905. // example 1.01, 1.02, 1.03, 1.04.
  18906. } else {
  18907. var realMin = axis.lin2log(min),
  18908. realMax = axis.lin2log(max),
  18909. tickIntervalOption = minor ?
  18910. this.getMinorTickInterval() :
  18911. options.tickInterval,
  18912. filteredTickIntervalOption = tickIntervalOption === 'auto' ?
  18913. null :
  18914. tickIntervalOption,
  18915. tickPixelIntervalOption =
  18916. options.tickPixelInterval / (minor ? 5 : 1),
  18917. totalPixelLength = minor ?
  18918. axisLength / axis.tickPositions.length :
  18919. axisLength;
  18920. interval = pick(
  18921. filteredTickIntervalOption,
  18922. axis._minorAutoInterval,
  18923. (realMax - realMin) *
  18924. tickPixelIntervalOption / (totalPixelLength || 1)
  18925. );
  18926. interval = normalizeTickInterval(
  18927. interval,
  18928. null,
  18929. getMagnitude(interval)
  18930. );
  18931. positions = axis.getLinearTickPositions(
  18932. interval,
  18933. realMin,
  18934. realMax
  18935. ).map(axis.log2lin);
  18936. if (!minor) {
  18937. axis._minorAutoInterval = interval / 5;
  18938. }
  18939. }
  18940. // Set the axis-level tickInterval variable
  18941. if (!minor) {
  18942. axis.tickInterval = interval;
  18943. }
  18944. return positions;
  18945. };
  18946. /**
  18947. * @private
  18948. * @function Highcharts.Axis#log2lin
  18949. *
  18950. * @param {number} num
  18951. *
  18952. * @return {number}
  18953. */
  18954. Axis.prototype.log2lin = function (num) {
  18955. return Math.log(num) / Math.LN10;
  18956. };
  18957. /**
  18958. * @private
  18959. * @function Highcharts.Axis#lin2log
  18960. *
  18961. * @param {number} num
  18962. *
  18963. * @return {number}
  18964. */
  18965. Axis.prototype.lin2log = function (num) {
  18966. return Math.pow(10, num);
  18967. };
  18968. }(Highcharts));
  18969. (function (H, Axis) {
  18970. /* *
  18971. * (c) 2010-2019 Torstein Honsi
  18972. *
  18973. * License: www.highcharts.com/license
  18974. */
  18975. /**
  18976. * Options for plot bands on axes.
  18977. *
  18978. * @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
  18979. */
  18980. /**
  18981. * Options for plot band labels on axes.
  18982. *
  18983. * @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
  18984. */
  18985. /**
  18986. * Options for plot lines on axes.
  18987. *
  18988. * @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
  18989. */
  18990. /**
  18991. * Options for plot line labels on axes.
  18992. *
  18993. * @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
  18994. */
  18995. var arrayMax = H.arrayMax,
  18996. arrayMin = H.arrayMin,
  18997. defined = H.defined,
  18998. destroyObjectProperties = H.destroyObjectProperties,
  18999. erase = H.erase,
  19000. merge = H.merge,
  19001. pick = H.pick;
  19002. /**
  19003. * The object wrapper for plot lines and plot bands
  19004. *
  19005. * @class
  19006. * @name Highcharts.PlotLineOrBand
  19007. *
  19008. * @param {Highcharts.Axis} axis
  19009. *
  19010. * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} options
  19011. */
  19012. H.PlotLineOrBand = function (axis, options) {
  19013. this.axis = axis;
  19014. if (options) {
  19015. this.options = options;
  19016. this.id = options.id;
  19017. }
  19018. };
  19019. H.PlotLineOrBand.prototype = {
  19020. /**
  19021. * Render the plot line or plot band. If it is already existing,
  19022. * move it.
  19023. *
  19024. * @private
  19025. * @function Highcharts.PlotLineOrBand#render
  19026. *
  19027. * @return {Highcharts.PlotLineOrBand|undefined}
  19028. */
  19029. render: function () {
  19030. H.fireEvent(this, 'render');
  19031. var plotLine = this,
  19032. axis = plotLine.axis,
  19033. horiz = axis.horiz,
  19034. options = plotLine.options,
  19035. optionsLabel = options.label,
  19036. label = plotLine.label,
  19037. to = options.to,
  19038. from = options.from,
  19039. value = options.value,
  19040. isBand = defined(from) && defined(to),
  19041. isLine = defined(value),
  19042. svgElem = plotLine.svgElem,
  19043. isNew = !svgElem,
  19044. path = [],
  19045. color = options.color,
  19046. zIndex = pick(options.zIndex, 0),
  19047. events = options.events,
  19048. attribs = {
  19049. 'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
  19050. (options.className || '')
  19051. },
  19052. groupAttribs = {},
  19053. renderer = axis.chart.renderer,
  19054. groupName = isBand ? 'bands' : 'lines',
  19055. group;
  19056. // logarithmic conversion
  19057. if (axis.isLog) {
  19058. from = axis.log2lin(from);
  19059. to = axis.log2lin(to);
  19060. value = axis.log2lin(value);
  19061. }
  19062. // Set the presentational attributes
  19063. if (!axis.chart.styledMode) {
  19064. if (isLine) {
  19065. attribs.stroke = color;
  19066. attribs['stroke-width'] = options.width;
  19067. if (options.dashStyle) {
  19068. attribs.dashstyle = options.dashStyle;
  19069. }
  19070. } else if (isBand) { // plot band
  19071. if (color) {
  19072. attribs.fill = color;
  19073. }
  19074. if (options.borderWidth) {
  19075. attribs.stroke = options.borderColor;
  19076. attribs['stroke-width'] = options.borderWidth;
  19077. }
  19078. }
  19079. }
  19080. // Grouping and zIndex
  19081. groupAttribs.zIndex = zIndex;
  19082. groupName += '-' + zIndex;
  19083. group = axis.plotLinesAndBandsGroups[groupName];
  19084. if (!group) {
  19085. axis.plotLinesAndBandsGroups[groupName] = group =
  19086. renderer.g('plot-' + groupName)
  19087. .attr(groupAttribs).add();
  19088. }
  19089. // Create the path
  19090. if (isNew) {
  19091. /**
  19092. * SVG element of the plot line or band.
  19093. *
  19094. * @name Highcharts.PlotLineOrBand#svgElement
  19095. * @type {Highcharts.SVGElement}
  19096. */
  19097. plotLine.svgElem = svgElem =
  19098. renderer
  19099. .path()
  19100. .attr(attribs).add(group);
  19101. }
  19102. // Set the path or return
  19103. if (isLine) {
  19104. path = axis.getPlotLinePath(value, svgElem.strokeWidth());
  19105. } else if (isBand) { // plot band
  19106. path = axis.getPlotBandPath(from, to, options);
  19107. } else {
  19108. return;
  19109. }
  19110. // common for lines and bands
  19111. if (isNew && path && path.length) {
  19112. svgElem.attr({ d: path });
  19113. // events
  19114. if (events) {
  19115. H.objectEach(events, function (event, eventType) {
  19116. svgElem.on(eventType, function (e) {
  19117. events[eventType].apply(plotLine, [e]);
  19118. });
  19119. });
  19120. }
  19121. } else if (svgElem) {
  19122. if (path) {
  19123. svgElem.show();
  19124. svgElem.animate({ d: path });
  19125. } else {
  19126. svgElem.hide();
  19127. if (label) {
  19128. plotLine.label = label = label.destroy();
  19129. }
  19130. }
  19131. }
  19132. // the plot band/line label
  19133. if (
  19134. optionsLabel &&
  19135. defined(optionsLabel.text) &&
  19136. path &&
  19137. path.length &&
  19138. axis.width > 0 &&
  19139. axis.height > 0 &&
  19140. !path.isFlat
  19141. ) {
  19142. // apply defaults
  19143. optionsLabel = merge({
  19144. align: horiz && isBand && 'center',
  19145. x: horiz ? !isBand && 4 : 10,
  19146. verticalAlign: !horiz && isBand && 'middle',
  19147. y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
  19148. rotation: horiz && !isBand && 90
  19149. }, optionsLabel);
  19150. this.renderLabel(optionsLabel, path, isBand, zIndex);
  19151. } else if (label) { // move out of sight
  19152. label.hide();
  19153. }
  19154. // chainable
  19155. return plotLine;
  19156. },
  19157. /**
  19158. * Render and align label for plot line or band.
  19159. *
  19160. * @private
  19161. * @function Highcharts.PlotLineOrBand#renderLabel
  19162. *
  19163. * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
  19164. *
  19165. * @param {Highcharts.SVGPathArray} path
  19166. *
  19167. * @param {boolean} [isBand]
  19168. *
  19169. * @param {number} [zIndex]
  19170. */
  19171. renderLabel: function (optionsLabel, path, isBand, zIndex) {
  19172. var plotLine = this,
  19173. label = plotLine.label,
  19174. renderer = plotLine.axis.chart.renderer,
  19175. attribs,
  19176. xBounds,
  19177. yBounds,
  19178. x,
  19179. y;
  19180. // add the SVG element
  19181. if (!label) {
  19182. attribs = {
  19183. align: optionsLabel.textAlign || optionsLabel.align,
  19184. rotation: optionsLabel.rotation,
  19185. 'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
  19186. '-label ' + (optionsLabel.className || '')
  19187. };
  19188. attribs.zIndex = zIndex;
  19189. /**
  19190. * SVG element of the label.
  19191. *
  19192. * @name Highcharts.PlotLineOrBand#label
  19193. * @type {Highcharts.SVGElement}
  19194. */
  19195. plotLine.label = label = renderer.text(
  19196. optionsLabel.text,
  19197. 0,
  19198. 0,
  19199. optionsLabel.useHTML
  19200. )
  19201. .attr(attribs)
  19202. .add();
  19203. if (!this.axis.chart.styledMode) {
  19204. label.css(optionsLabel.style);
  19205. }
  19206. }
  19207. // get the bounding box and align the label
  19208. // #3000 changed to better handle choice between plotband or plotline
  19209. xBounds = path.xBounds ||
  19210. [path[1], path[4], (isBand ? path[6] : path[1])];
  19211. yBounds = path.yBounds ||
  19212. [path[2], path[5], (isBand ? path[7] : path[2])];
  19213. x = arrayMin(xBounds);
  19214. y = arrayMin(yBounds);
  19215. label.align(optionsLabel, false, {
  19216. x: x,
  19217. y: y,
  19218. width: arrayMax(xBounds) - x,
  19219. height: arrayMax(yBounds) - y
  19220. });
  19221. label.show();
  19222. },
  19223. /**
  19224. * Remove the plot line or band.
  19225. *
  19226. * @function Highcharts.PlotLineOrBand#destroy
  19227. */
  19228. destroy: function () {
  19229. // remove it from the lookup
  19230. erase(this.axis.plotLinesAndBands, this);
  19231. delete this.axis;
  19232. destroyObjectProperties(this);
  19233. }
  19234. };
  19235. // Object with members for extending the Axis prototype
  19236. H.extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  19237. /**
  19238. * An array of colored bands stretching across the plot area marking an
  19239. * interval on the axis.
  19240. *
  19241. * In styled mode, the plot bands are styled by the `.highcharts-plot-band`
  19242. * class in addition to the `className` option.
  19243. *
  19244. * @productdesc {highcharts}
  19245. * In a gauge, a plot band on the Y axis (value axis) will stretch along the
  19246. * perimeter of the gauge.
  19247. *
  19248. * @type {Array<*>}
  19249. * @product highcharts highstock gantt
  19250. * @apioption xAxis.plotBands
  19251. */
  19252. /**
  19253. * Border color for the plot band. Also requires `borderWidth` to be set.
  19254. *
  19255. * @type {Highcharts.ColorString}
  19256. * @apioption xAxis.plotBands.borderColor
  19257. */
  19258. /**
  19259. * Border width for the plot band. Also requires `borderColor` to be set.
  19260. *
  19261. * @type {number}
  19262. * @default 0
  19263. * @apioption xAxis.plotBands.borderWidth
  19264. */
  19265. /**
  19266. * A custom class name, in addition to the default `highcharts-plot-band`,
  19267. * to apply to each individual band.
  19268. *
  19269. * @type {string}
  19270. * @since 5.0.0
  19271. * @apioption xAxis.plotBands.className
  19272. */
  19273. /**
  19274. * The color of the plot band.
  19275. *
  19276. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19277. * Color band
  19278. * @sample {highstock} stock/xaxis/plotbands/
  19279. * Plot band on Y axis
  19280. *
  19281. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  19282. * @apioption xAxis.plotBands.color
  19283. */
  19284. /**
  19285. * An object defining mouse events for the plot band. Supported properties
  19286. * are `click`, `mouseover`, `mouseout`, `mousemove`.
  19287. *
  19288. * @sample {highcharts} highcharts/xaxis/plotbands-events/
  19289. * Mouse events demonstrated
  19290. *
  19291. * @since 1.2
  19292. * @context PlotLineOrBand
  19293. * @apioption xAxis.plotBands.events
  19294. */
  19295. /**
  19296. * The start position of the plot band in axis units.
  19297. *
  19298. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19299. * Datetime axis
  19300. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  19301. * Categorized axis
  19302. * @sample {highstock} stock/xaxis/plotbands/
  19303. * Plot band on Y axis
  19304. *
  19305. * @type {number}
  19306. * @apioption xAxis.plotBands.from
  19307. */
  19308. /**
  19309. * An id used for identifying the plot band in Axis.removePlotBand.
  19310. *
  19311. * @sample {highcharts} highcharts/xaxis/plotbands-id/
  19312. * Remove plot band by id
  19313. * @sample {highstock} highcharts/xaxis/plotbands-id/
  19314. * Remove plot band by id
  19315. *
  19316. * @type {string}
  19317. * @apioption xAxis.plotBands.id
  19318. */
  19319. /**
  19320. * The end position of the plot band in axis units.
  19321. *
  19322. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19323. * Datetime axis
  19324. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  19325. * Categorized axis
  19326. * @sample {highstock} stock/xaxis/plotbands/
  19327. * Plot band on Y axis
  19328. *
  19329. * @type {number}
  19330. * @apioption xAxis.plotBands.to
  19331. */
  19332. /**
  19333. * The z index of the plot band within the chart, relative to other
  19334. * elements. Using the same z index as another element may give
  19335. * unpredictable results, as the last rendered element will be on top.
  19336. * Values from 0 to 20 make sense.
  19337. *
  19338. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  19339. * Behind plot lines by default
  19340. * @sample {highcharts} highcharts/xaxis/plotbands-zindex/
  19341. * Above plot lines
  19342. * @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
  19343. * Above plot lines and series
  19344. *
  19345. * @type {number}
  19346. * @since 1.2
  19347. * @apioption xAxis.plotBands.zIndex
  19348. */
  19349. /**
  19350. * Text labels for the plot bands
  19351. *
  19352. * @product highcharts highstock gantt
  19353. * @apioption xAxis.plotBands.label
  19354. */
  19355. /**
  19356. * Horizontal alignment of the label. Can be one of "left", "center" or
  19357. * "right".
  19358. *
  19359. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  19360. * Aligned to the right
  19361. * @sample {highstock} stock/xaxis/plotbands-label/
  19362. * Plot band with labels
  19363. *
  19364. * @type {Highcharts.AlignType}
  19365. * @default center
  19366. * @since 2.1
  19367. * @apioption xAxis.plotBands.label.align
  19368. */
  19369. /**
  19370. * Rotation of the text label in degrees .
  19371. *
  19372. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  19373. * Vertical text
  19374. *
  19375. * @type {number}
  19376. * @default 0
  19377. * @since 2.1
  19378. * @apioption xAxis.plotBands.label.rotation
  19379. */
  19380. /**
  19381. * CSS styles for the text label.
  19382. *
  19383. * In styled mode, the labels are styled by the
  19384. * `.highcharts-plot-band-label` class.
  19385. *
  19386. * @sample {highcharts} highcharts/xaxis/plotbands-label-style/
  19387. * Blue and bold label
  19388. *
  19389. * @type {Highcharts.CSSObject}
  19390. * @since 2.1
  19391. * @apioption xAxis.plotBands.label.style
  19392. */
  19393. /**
  19394. * The string text itself. A subset of HTML is supported.
  19395. *
  19396. * @type {string}
  19397. * @since 2.1
  19398. * @apioption xAxis.plotBands.label.text
  19399. */
  19400. /**
  19401. * The text alignment for the label. While `align` determines where the
  19402. * texts anchor point is placed within the plot band, `textAlign` determines
  19403. * how the text is aligned against its anchor point. Possible values are
  19404. * "left", "center" and "right". Defaults to the same as the `align` option.
  19405. *
  19406. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  19407. * Vertical text in center position but text-aligned left
  19408. *
  19409. * @type {Highcharts.AlignType}
  19410. * @since 2.1
  19411. * @apioption xAxis.plotBands.label.textAlign
  19412. */
  19413. /**
  19414. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  19415. * to render the labels.
  19416. *
  19417. * @type {boolean}
  19418. * @default false
  19419. * @since 3.0.3
  19420. * @apioption xAxis.plotBands.label.useHTML
  19421. */
  19422. /**
  19423. * Vertical alignment of the label relative to the plot band. Can be one of
  19424. * "top", "middle" or "bottom".
  19425. *
  19426. * @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
  19427. * Vertically centered label
  19428. * @sample {highstock} stock/xaxis/plotbands-label/
  19429. * Plot band with labels
  19430. *
  19431. * @type {Highcharts.VerticalAlignType}
  19432. * @default top
  19433. * @since 2.1
  19434. * @apioption xAxis.plotBands.label.verticalAlign
  19435. */
  19436. /**
  19437. * Horizontal position relative the alignment. Default varies by
  19438. * orientation.
  19439. *
  19440. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  19441. * Aligned 10px from the right edge
  19442. * @sample {highstock} stock/xaxis/plotbands-label/
  19443. * Plot band with labels
  19444. *
  19445. * @type {number}
  19446. * @since 2.1
  19447. * @apioption xAxis.plotBands.label.x
  19448. */
  19449. /**
  19450. * Vertical position of the text baseline relative to the alignment. Default
  19451. * varies by orientation.
  19452. *
  19453. * @sample {highcharts} highcharts/xaxis/plotbands-label-y/
  19454. * Label on x axis
  19455. * @sample {highstock} stock/xaxis/plotbands-label/
  19456. * Plot band with labels
  19457. *
  19458. * @type {number}
  19459. * @since 2.1
  19460. * @apioption xAxis.plotBands.label.y
  19461. */
  19462. /**
  19463. * An array of lines stretching across the plot area, marking a specific
  19464. * value on one of the axes.
  19465. *
  19466. * In styled mode, the plot lines are styled by the
  19467. * `.highcharts-plot-line` class in addition to the `className` option.
  19468. *
  19469. * @type {Array<*>}
  19470. * @product highcharts highstock gantt
  19471. * @apioption xAxis.plotLines
  19472. */
  19473. /**
  19474. * A custom class name, in addition to the default `highcharts-plot-line`,
  19475. * to apply to each individual line.
  19476. *
  19477. * @type {string}
  19478. * @since 5.0.0
  19479. * @apioption xAxis.plotLines.className
  19480. */
  19481. /**
  19482. * The color of the line.
  19483. *
  19484. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  19485. * A red line from X axis
  19486. * @sample {highstock} stock/xaxis/plotlines/
  19487. * Plot line on Y axis
  19488. *
  19489. * @type {Highcharts.ColorString}
  19490. * @apioption xAxis.plotLines.color
  19491. */
  19492. /**
  19493. * The dashing or dot style for the plot line. For possible values see
  19494. * [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  19495. *
  19496. * @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
  19497. * Dash and dot pattern
  19498. * @sample {highstock} stock/xaxis/plotlines/
  19499. * Plot line on Y axis
  19500. *
  19501. * @type {Highcharts.DashStyleType}
  19502. * @default Solid
  19503. * @since 1.2
  19504. * @apioption xAxis.plotLines.dashStyle
  19505. */
  19506. /**
  19507. * An object defining mouse events for the plot line. Supported
  19508. * properties are `click`, `mouseover`, `mouseout`, `mousemove`.
  19509. *
  19510. * @sample {highcharts} highcharts/xaxis/plotlines-events/
  19511. * Mouse events demonstrated
  19512. *
  19513. * @type {*}
  19514. * @since 1.2
  19515. * @context PlotLineOrBand
  19516. * @apioption xAxis.plotLines.events
  19517. */
  19518. /**
  19519. * An id used for identifying the plot line in Axis.removePlotLine.
  19520. *
  19521. * @sample {highcharts} highcharts/xaxis/plotlines-id/
  19522. * Remove plot line by id
  19523. *
  19524. * @type {string}
  19525. * @apioption xAxis.plotLines.id
  19526. */
  19527. /**
  19528. * The position of the line in axis units.
  19529. *
  19530. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  19531. * Between two categories on X axis
  19532. * @sample {highstock} stock/xaxis/plotlines/
  19533. * Plot line on Y axis
  19534. *
  19535. * @type {number}
  19536. * @apioption xAxis.plotLines.value
  19537. */
  19538. /**
  19539. * The width or thickness of the plot line.
  19540. *
  19541. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  19542. * 2px wide line from X axis
  19543. * @sample {highstock} stock/xaxis/plotlines/
  19544. * Plot line on Y axis
  19545. *
  19546. * @type {number}
  19547. * @apioption xAxis.plotLines.width
  19548. */
  19549. /**
  19550. * The z index of the plot line within the chart.
  19551. *
  19552. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
  19553. * Behind plot lines by default
  19554. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
  19555. * Above plot lines
  19556. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
  19557. * Above plot lines and series
  19558. *
  19559. * @type {number}
  19560. * @since 1.2
  19561. * @apioption xAxis.plotLines.zIndex
  19562. */
  19563. /**
  19564. * Text labels for the plot bands
  19565. *
  19566. * @apioption xAxis.plotLines.label
  19567. */
  19568. /**
  19569. * Horizontal alignment of the label. Can be one of "left", "center" or
  19570. * "right".
  19571. *
  19572. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  19573. * Aligned to the right
  19574. * @sample {highstock} stock/xaxis/plotlines/
  19575. * Plot line on Y axis
  19576. *
  19577. * @type {Highcharts.AlignType}
  19578. * @default left
  19579. * @since 2.1
  19580. * @apioption xAxis.plotLines.label.align
  19581. */
  19582. /**
  19583. * Rotation of the text label in degrees. Defaults to 0 for horizontal plot
  19584. * lines and 90 for vertical lines.
  19585. *
  19586. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  19587. * Slanted text
  19588. *
  19589. * @type {number}
  19590. * @since 2.1
  19591. * @apioption xAxis.plotLines.label.rotation
  19592. */
  19593. /**
  19594. * CSS styles for the text label.
  19595. *
  19596. * In styled mode, the labels are styled by the
  19597. * `.highcharts-plot-line-label` class.
  19598. *
  19599. * @sample {highcharts} highcharts/xaxis/plotlines-label-style/
  19600. * Blue and bold label
  19601. *
  19602. * @type {Highcharts.CSSObject}
  19603. * @since 2.1
  19604. * @apioption xAxis.plotLines.label.style
  19605. */
  19606. /**
  19607. * The text itself. A subset of HTML is supported.
  19608. *
  19609. * @type {string}
  19610. * @since 2.1
  19611. * @apioption xAxis.plotLines.label.text
  19612. */
  19613. /**
  19614. * The text alignment for the label. While `align` determines where the
  19615. * texts anchor point is placed within the plot band, `textAlign` determines
  19616. * how the text is aligned against its anchor point. Possible values are
  19617. * "left", "center" and "right". Defaults to the same as the `align` option.
  19618. *
  19619. * @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
  19620. * Text label in bottom position
  19621. *
  19622. * @type {Highcharts.AlignType}
  19623. * @since 2.1
  19624. * @apioption xAxis.plotLines.label.textAlign
  19625. */
  19626. /**
  19627. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  19628. * to render the labels.
  19629. *
  19630. * @type {boolean}
  19631. * @default false
  19632. * @since 3.0.3
  19633. * @apioption xAxis.plotLines.label.useHTML
  19634. */
  19635. /**
  19636. * Vertical alignment of the label relative to the plot line. Can be
  19637. * one of "top", "middle" or "bottom".
  19638. *
  19639. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  19640. * Vertically centered label
  19641. *
  19642. * @type {Highcharts.VerticalAlignType}
  19643. * @default {highcharts} top
  19644. * @default {highstock} top
  19645. * @since 2.1
  19646. * @validvalue ["top", "middle", "bottom"]
  19647. * @apioption xAxis.plotLines.label.verticalAlign
  19648. */
  19649. /**
  19650. * Horizontal position relative the alignment. Default varies by
  19651. * orientation.
  19652. *
  19653. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  19654. * Aligned 10px from the right edge
  19655. * @sample {highstock} stock/xaxis/plotlines/
  19656. * Plot line on Y axis
  19657. *
  19658. * @type {number}
  19659. * @since 2.1
  19660. * @apioption xAxis.plotLines.label.x
  19661. */
  19662. /**
  19663. * Vertical position of the text baseline relative to the alignment. Default
  19664. * varies by orientation.
  19665. *
  19666. * @sample {highcharts} highcharts/xaxis/plotlines-label-y/
  19667. * Label below the plot line
  19668. * @sample {highstock} stock/xaxis/plotlines/
  19669. * Plot line on Y axis
  19670. *
  19671. * @type {number}
  19672. * @since 2.1
  19673. * @apioption xAxis.plotLines.label.y
  19674. */
  19675. /**
  19676. * An array of objects defining plot bands on the Y axis.
  19677. *
  19678. * @type {Array<*>}
  19679. * @extends xAxis.plotBands
  19680. * @apioption yAxis.plotBands
  19681. */
  19682. /**
  19683. * In a gauge chart, this option determines the inner radius of the
  19684. * plot band that stretches along the perimeter. It can be given as
  19685. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  19686. * By default, the inner radius is controlled by the [thickness](
  19687. * #yAxis.plotBands.thickness) option.
  19688. *
  19689. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  19690. * Gauge plot band
  19691. *
  19692. * @type {number|string}
  19693. * @since 2.3
  19694. * @product highcharts
  19695. * @apioption yAxis.plotBands.innerRadius
  19696. */
  19697. /**
  19698. * In a gauge chart, this option determines the outer radius of the
  19699. * plot band that stretches along the perimeter. It can be given as
  19700. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  19701. *
  19702. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  19703. * Gauge plot band
  19704. *
  19705. * @type {number|string}
  19706. * @default 100%
  19707. * @since 2.3
  19708. * @product highcharts
  19709. * @apioption yAxis.plotBands.outerRadius
  19710. */
  19711. /**
  19712. * In a gauge chart, this option sets the width of the plot band
  19713. * stretching along the perimeter. It can be given as a percentage
  19714. * string, like `"10%"`, or as a pixel number, like `10`. The default
  19715. * value 10 is the same as the default [tickLength](#yAxis.tickLength),
  19716. * thus making the plot band act as a background for the tick markers.
  19717. *
  19718. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  19719. * Gauge plot band
  19720. *
  19721. * @type {number|string}
  19722. * @default 10
  19723. * @since 2.3
  19724. * @product highcharts
  19725. * @apioption yAxis.plotBands.thickness
  19726. */
  19727. /**
  19728. * An array of objects representing plot lines on the X axis
  19729. *
  19730. * @type {Array<*>}
  19731. * @extends xAxis.plotLines
  19732. * @apioption yAxis.plotLines
  19733. */
  19734. /**
  19735. * Internal function to create the SVG path definition for a plot band.
  19736. *
  19737. * @function Highcharts.Axis#getPlotBandPath
  19738. *
  19739. * @param {number} from
  19740. * The axis value to start from.
  19741. *
  19742. * @param {number} to
  19743. * The axis value to end on.
  19744. *
  19745. * @return {Highcharts.SVGPathArray}
  19746. * The SVG path definition in array form.
  19747. */
  19748. getPlotBandPath: function (from, to) {
  19749. var toPath = this.getPlotLinePath(to, null, null, true),
  19750. path = this.getPlotLinePath(from, null, null, true),
  19751. result = [],
  19752. i,
  19753. // #4964 check if chart is inverted or plotband is on yAxis
  19754. horiz = this.horiz,
  19755. plus = 1,
  19756. isFlat,
  19757. outside =
  19758. (from < this.min && to < this.min) ||
  19759. (from > this.max && to > this.max);
  19760. if (path && toPath) {
  19761. // Flat paths don't need labels (#3836)
  19762. if (outside) {
  19763. isFlat = path.toString() === toPath.toString();
  19764. plus = 0;
  19765. }
  19766. // Go over each subpath - for panes in Highstock
  19767. for (i = 0; i < path.length; i += 6) {
  19768. // Add 1 pixel when coordinates are the same
  19769. if (horiz && toPath[i + 1] === path[i + 1]) {
  19770. toPath[i + 1] += plus;
  19771. toPath[i + 4] += plus;
  19772. } else if (!horiz && toPath[i + 2] === path[i + 2]) {
  19773. toPath[i + 2] += plus;
  19774. toPath[i + 5] += plus;
  19775. }
  19776. result.push(
  19777. 'M',
  19778. path[i + 1],
  19779. path[i + 2],
  19780. 'L',
  19781. path[i + 4],
  19782. path[i + 5],
  19783. toPath[i + 4],
  19784. toPath[i + 5],
  19785. toPath[i + 1],
  19786. toPath[i + 2],
  19787. 'z'
  19788. );
  19789. result.isFlat = isFlat;
  19790. }
  19791. } else { // outside the axis area
  19792. path = null;
  19793. }
  19794. return result;
  19795. },
  19796. /**
  19797. * Add a plot band after render time.
  19798. *
  19799. * @sample highcharts/members/axis-addplotband/
  19800. * Toggle the plot band from a button
  19801. *
  19802. * @function Highcharts.Axis#addPlotBand
  19803. *
  19804. * @param {Highcharts.AxisPlotBandsOptions} options
  19805. * A configuration object for the plot band, as defined in
  19806. * [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
  19807. *
  19808. * @return {Highcharts.PlotLineOrBand|undefined}
  19809. * The added plot band.
  19810. */
  19811. addPlotBand: function (options) {
  19812. return this.addPlotBandOrLine(options, 'plotBands');
  19813. },
  19814. /**
  19815. * Add a plot line after render time.
  19816. *
  19817. * @sample highcharts/members/axis-addplotline/
  19818. * Toggle the plot line from a button
  19819. *
  19820. * @function Highcharts.Axis#addPlotLine
  19821. *
  19822. * @param {Highcharts.AxisPlotLinesOptions} options
  19823. * A configuration object for the plot line, as defined in
  19824. * [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
  19825. *
  19826. * @return {Highcharts.PlotLineOrBand|undefined}
  19827. * The added plot line.
  19828. */
  19829. addPlotLine: function (options) {
  19830. return this.addPlotBandOrLine(options, 'plotLines');
  19831. },
  19832. /**
  19833. * Add a plot band or plot line after render time. Called from addPlotBand
  19834. * and addPlotLine internally.
  19835. *
  19836. * @private
  19837. * @function Highcharts.Axis#addPlotBandOrLine
  19838. *
  19839. * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} options
  19840. * The plotBand or plotLine configuration object.
  19841. *
  19842. * @param {"plotBands"|"plotLines"} [coll]
  19843. *
  19844. * @return {Highcharts.PlotLineOrBand|undefined}
  19845. */
  19846. addPlotBandOrLine: function (options, coll) {
  19847. var obj = new H.PlotLineOrBand(this, options).render(),
  19848. userOptions = this.userOptions;
  19849. if (obj) { // #2189
  19850. // Add it to the user options for exporting and Axis.update
  19851. if (coll) {
  19852. userOptions[coll] = userOptions[coll] || [];
  19853. userOptions[coll].push(options);
  19854. }
  19855. this.plotLinesAndBands.push(obj);
  19856. }
  19857. return obj;
  19858. },
  19859. /**
  19860. * Remove a plot band or plot line from the chart by id. Called internally
  19861. * from `removePlotBand` and `removePlotLine`.
  19862. *
  19863. * @private
  19864. * @function Highcharts.Axis#removePlotBandOrLine
  19865. *
  19866. * @param {string} id
  19867. */
  19868. removePlotBandOrLine: function (id) {
  19869. var plotLinesAndBands = this.plotLinesAndBands,
  19870. options = this.options,
  19871. userOptions = this.userOptions,
  19872. i = plotLinesAndBands.length;
  19873. while (i--) {
  19874. if (plotLinesAndBands[i].id === id) {
  19875. plotLinesAndBands[i].destroy();
  19876. }
  19877. }
  19878. ([
  19879. options.plotLines || [],
  19880. userOptions.plotLines || [],
  19881. options.plotBands || [],
  19882. userOptions.plotBands || []
  19883. ]).forEach(function (arr) {
  19884. i = arr.length;
  19885. while (i--) {
  19886. if (arr[i].id === id) {
  19887. erase(arr, arr[i]);
  19888. }
  19889. }
  19890. });
  19891. },
  19892. /**
  19893. * Remove a plot band by its id.
  19894. *
  19895. * @sample highcharts/members/axis-removeplotband/
  19896. * Remove plot band by id
  19897. * @sample highcharts/members/axis-addplotband/
  19898. * Toggle the plot band from a button
  19899. *
  19900. * @function Highcharts.Axis#removePlotBand
  19901. *
  19902. * @param {string} id
  19903. * The plot band's `id` as given in the original configuration
  19904. * object or in the `addPlotBand` option.
  19905. */
  19906. removePlotBand: function (id) {
  19907. this.removePlotBandOrLine(id);
  19908. },
  19909. /**
  19910. * Remove a plot line by its id.
  19911. *
  19912. * @sample highcharts/xaxis/plotlines-id/
  19913. * Remove plot line by id
  19914. * @sample highcharts/members/axis-addplotline/
  19915. * Toggle the plot line from a button
  19916. *
  19917. * @function Highcharts.Axis#removePlotLine
  19918. *
  19919. * @param {string} id
  19920. * The plot line's `id` as given in the original configuration
  19921. * object or in the `addPlotLine` option.
  19922. */
  19923. removePlotLine: function (id) {
  19924. this.removePlotBandOrLine(id);
  19925. }
  19926. });
  19927. }(Highcharts, Axis));
  19928. (function (H) {
  19929. /**
  19930. * (c) 2010-2019 Torstein Honsi
  19931. *
  19932. * License: www.highcharts.com/license
  19933. */
  19934. /**
  19935. * A callback function to place the tooltip in a specific position.
  19936. *
  19937. * @callback Highcharts.TooltipPositionerCallbackFunction
  19938. *
  19939. * @param {number} labelWidth
  19940. * Width of the tooltip.
  19941. *
  19942. * @param {number} labelHeight
  19943. * Height of the tooltip.
  19944. *
  19945. * @param {Highcharts.TooltipPositionerPointObject} point
  19946. * Point information for positioning a tooltip.
  19947. *
  19948. * @return {Highcharts.PositionObject}
  19949. * New position for the tooltip.
  19950. */
  19951. /**
  19952. * Point information for positioning a tooltip.
  19953. *
  19954. * @interface Highcharts.TooltipPositionerPointObject
  19955. *//**
  19956. * If `tooltip.split` option is enabled and positioner is called for each of the
  19957. * boxes separately, this property indicates the call on the xAxis header, which
  19958. * is not a point itself.
  19959. * @name Highcharts.TooltipPositionerPointObject#isHeader
  19960. * @type {boolean}
  19961. *//**
  19962. * @name Highcharts.TooltipPositionerPointObject#negative
  19963. * @type {boolean}
  19964. *//**
  19965. * The reference point relative to the plot area. Add chart.plotLeft to get the
  19966. * full coordinates.
  19967. * @name Highcharts.TooltipPositionerPointObject#plotX
  19968. * @type {number}
  19969. *//**
  19970. * The reference point relative to the plot area. Add chart.plotTop to get the
  19971. * full coordinates.
  19972. * @name Highcharts.TooltipPositionerPointObject#plotY
  19973. * @type {number}
  19974. */
  19975. var doc = H.doc,
  19976. extend = H.extend,
  19977. format = H.format,
  19978. isNumber = H.isNumber,
  19979. merge = H.merge,
  19980. pick = H.pick,
  19981. splat = H.splat,
  19982. syncTimeout = H.syncTimeout,
  19983. timeUnits = H.timeUnits;
  19984. /**
  19985. * Tooltip of a chart.
  19986. *
  19987. * @class
  19988. * @name Highcharts.Tooltip
  19989. *
  19990. * @param {Highcharts.Chart} chart
  19991. * The chart instance.
  19992. *
  19993. * @param {Highcharts.TooltipOptions} options
  19994. * Tooltip options.
  19995. */
  19996. H.Tooltip = function () {
  19997. this.init.apply(this, arguments);
  19998. };
  19999. H.Tooltip.prototype = {
  20000. /**
  20001. * @private
  20002. * @function Highcharts.Tooltip#init
  20003. *
  20004. * @param {Highcharts.Chart} chart
  20005. * The chart instance.
  20006. *
  20007. * @param {Highcharts.TooltipOptions} options
  20008. * Tooltip options.
  20009. */
  20010. init: function (chart, options) {
  20011. /**
  20012. * Chart of the tooltip.
  20013. *
  20014. * @readonly
  20015. * @name Highcharts.Tooltip#chart
  20016. * @type {Highcharts.Chart}
  20017. */
  20018. this.chart = chart;
  20019. /**
  20020. * Used tooltip options.
  20021. *
  20022. * @readonly
  20023. * @name Highcharts.Tooltip#options
  20024. * @type {Highcharts.TooltipOptions}
  20025. */
  20026. this.options = options;
  20027. /**
  20028. * List of crosshairs.
  20029. *
  20030. * @private
  20031. * @readonly
  20032. * @name Highcharts.Tooltip#crosshairs
  20033. * @type {Array<*>}
  20034. */
  20035. this.crosshairs = [];
  20036. /**
  20037. * Current values of x and y when animating.
  20038. *
  20039. * @private
  20040. * @readonly
  20041. * @name Highcharts.Tooltip#now
  20042. * @type {*}
  20043. */
  20044. this.now = { x: 0, y: 0 };
  20045. /**
  20046. * Tooltips are initially hidden.
  20047. *
  20048. * @readonly
  20049. * @name Highcharts.Tooltip#isHidden
  20050. * @type {boolean}
  20051. */
  20052. this.isHidden = true;
  20053. /**
  20054. * True, if the tooltip is splitted into one label per series, with the
  20055. * header close to the axis.
  20056. *
  20057. * @readonly
  20058. * @name Highcharts.Tooltip#split
  20059. * @type {boolean}
  20060. */
  20061. this.split = options.split && !chart.inverted;
  20062. /**
  20063. * When the tooltip is shared, the entire plot area will capture mouse
  20064. * movement or touch events.
  20065. *
  20066. * @readonly
  20067. * @name Highcharts.Tooltip#shared
  20068. * @type {boolean}
  20069. */
  20070. this.shared = options.shared || this.split;
  20071. /**
  20072. * Whether to allow the tooltip to render outside the chart's SVG
  20073. * element box. By default (false), the tooltip is rendered within the
  20074. * chart's SVG element, which results in the tooltip being aligned
  20075. * inside the chart area.
  20076. *
  20077. * @readonly
  20078. * @name Highcharts.Tooltip#outside
  20079. * @type {boolean}
  20080. *
  20081. * @todo
  20082. * Split tooltip does not support outside in the first iteration. Should
  20083. * not be too complicated to implement.
  20084. */
  20085. this.outside = options.outside && !this.split;
  20086. },
  20087. /**
  20088. * Destroy the single tooltips in a split tooltip.
  20089. * If the tooltip is active then it is not destroyed, unless forced to.
  20090. *
  20091. * @private
  20092. * @function Highcharts.Tooltip#cleanSplit
  20093. *
  20094. * @param {boolean} force
  20095. * Force destroy all tooltips.
  20096. */
  20097. cleanSplit: function (force) {
  20098. this.chart.series.forEach(function (series) {
  20099. var tt = series && series.tt;
  20100. if (tt) {
  20101. if (!tt.isActive || force) {
  20102. series.tt = tt.destroy();
  20103. } else {
  20104. tt.isActive = false;
  20105. }
  20106. }
  20107. });
  20108. },
  20109. /**
  20110. * In styled mode, apply the default filter for the tooltip drop-shadow. It
  20111. * needs to have an id specific to the chart, otherwise there will be issues
  20112. * when one tooltip adopts the filter of a different chart, specifically one
  20113. * where the container is hidden.
  20114. *
  20115. * @private
  20116. * @function Highcharts.Tooltip#applyFilter
  20117. */
  20118. applyFilter: function () {
  20119. var chart = this.chart;
  20120. chart.renderer.definition({
  20121. tagName: 'filter',
  20122. id: 'drop-shadow-' + chart.index,
  20123. opacity: 0.5,
  20124. children: [{
  20125. tagName: 'feGaussianBlur',
  20126. 'in': 'SourceAlpha',
  20127. stdDeviation: 1
  20128. }, {
  20129. tagName: 'feOffset',
  20130. dx: 1,
  20131. dy: 1
  20132. }, {
  20133. tagName: 'feComponentTransfer',
  20134. children: [{
  20135. tagName: 'feFuncA',
  20136. type: 'linear',
  20137. slope: 0.3
  20138. }]
  20139. }, {
  20140. tagName: 'feMerge',
  20141. children: [{
  20142. tagName: 'feMergeNode'
  20143. }, {
  20144. tagName: 'feMergeNode',
  20145. 'in': 'SourceGraphic'
  20146. }]
  20147. }]
  20148. });
  20149. chart.renderer.definition({
  20150. tagName: 'style',
  20151. textContent: '.highcharts-tooltip-' + chart.index + '{' +
  20152. 'filter:url(#drop-shadow-' + chart.index + ')' +
  20153. '}'
  20154. });
  20155. },
  20156. /**
  20157. * Creates the Tooltip label element if it does not exist, then returns it.
  20158. *
  20159. * @function Highcharts.Tooltip#getLabel
  20160. *
  20161. * @return {Highcharts.SVGElement}
  20162. */
  20163. getLabel: function () {
  20164. var tooltip = this,
  20165. renderer = this.chart.renderer,
  20166. styledMode = this.chart.styledMode,
  20167. options = this.options,
  20168. container,
  20169. set;
  20170. if (!this.label) {
  20171. if (this.outside) {
  20172. this.container = container = H.doc.createElement('div');
  20173. container.className = 'highcharts-tooltip-container';
  20174. H.css(container, {
  20175. position: 'absolute',
  20176. top: '1px',
  20177. pointerEvents: options.style && options.style.pointerEvents
  20178. });
  20179. H.doc.body.appendChild(container);
  20180. this.renderer = renderer = new H.Renderer(container, 0, 0);
  20181. }
  20182. // Create the label
  20183. if (this.split) {
  20184. this.label = renderer.g('tooltip');
  20185. } else {
  20186. this.label = renderer
  20187. .label(
  20188. '',
  20189. 0,
  20190. 0,
  20191. options.shape || 'callout',
  20192. null,
  20193. null,
  20194. options.useHTML,
  20195. null,
  20196. 'tooltip'
  20197. )
  20198. .attr({
  20199. padding: options.padding,
  20200. r: options.borderRadius
  20201. });
  20202. if (!styledMode) {
  20203. this.label
  20204. .attr({
  20205. 'fill': options.backgroundColor,
  20206. 'stroke-width': options.borderWidth
  20207. })
  20208. // #2301, #2657
  20209. .css(options.style)
  20210. .shadow(options.shadow);
  20211. }
  20212. }
  20213. if (styledMode) {
  20214. // Apply the drop-shadow filter
  20215. this.applyFilter();
  20216. this.label.addClass('highcharts-tooltip-' + this.chart.index);
  20217. }
  20218. if (this.outside) {
  20219. set = {
  20220. x: this.label.xSetter,
  20221. y: this.label.ySetter
  20222. };
  20223. this.label.xSetter = function (value, key) {
  20224. set[key].call(this.label, tooltip.distance);
  20225. container.style.left = value + 'px';
  20226. };
  20227. this.label.ySetter = function (value, key) {
  20228. set[key].call(this.label, tooltip.distance);
  20229. container.style.top = value + 'px';
  20230. };
  20231. }
  20232. this.label
  20233. .attr({
  20234. zIndex: 8
  20235. })
  20236. .add();
  20237. }
  20238. return this.label;
  20239. },
  20240. /**
  20241. * Updates the tooltip with the provided tooltip options.
  20242. *
  20243. * @function Highcharts.Tooltip#update
  20244. *
  20245. * @param {Highcharts.TooltipOptions} options
  20246. * The tooltip options to update.
  20247. */
  20248. update: function (options) {
  20249. this.destroy();
  20250. // Update user options (#6218)
  20251. merge(true, this.chart.options.tooltip.userOptions, options);
  20252. this.init(this.chart, merge(true, this.options, options));
  20253. },
  20254. /**
  20255. * Removes and destroys the tooltip and its elements.
  20256. *
  20257. * @function Highcharts.Tooltip#destroy
  20258. */
  20259. destroy: function () {
  20260. // Destroy and clear local variables
  20261. if (this.label) {
  20262. this.label = this.label.destroy();
  20263. }
  20264. if (this.split && this.tt) {
  20265. this.cleanSplit(this.chart, true);
  20266. this.tt = this.tt.destroy();
  20267. }
  20268. if (this.renderer) {
  20269. this.renderer = this.renderer.destroy();
  20270. H.discardElement(this.container);
  20271. }
  20272. H.clearTimeout(this.hideTimer);
  20273. H.clearTimeout(this.tooltipTimeout);
  20274. },
  20275. /**
  20276. * Moves the tooltip with a soft animation to a new position.
  20277. *
  20278. * @function Highcharts.Tooltip#move
  20279. *
  20280. * @param {number} x
  20281. *
  20282. * @param {number} y
  20283. *
  20284. * @param {number} anchorX
  20285. *
  20286. * @param {number} anchorY
  20287. */
  20288. move: function (x, y, anchorX, anchorY) {
  20289. var tooltip = this,
  20290. now = tooltip.now,
  20291. animate = tooltip.options.animation !== false &&
  20292. !tooltip.isHidden &&
  20293. // When we get close to the target position, abort animation and
  20294. // land on the right place (#3056)
  20295. (Math.abs(x - now.x) > 1 || Math.abs(y - now.y) > 1),
  20296. skipAnchor = tooltip.followPointer || tooltip.len > 1;
  20297. // Get intermediate values for animation
  20298. extend(now, {
  20299. x: animate ? (2 * now.x + x) / 3 : x,
  20300. y: animate ? (now.y + y) / 2 : y,
  20301. anchorX: skipAnchor ?
  20302. undefined :
  20303. animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
  20304. anchorY: skipAnchor ?
  20305. undefined :
  20306. animate ? (now.anchorY + anchorY) / 2 : anchorY
  20307. });
  20308. // Move to the intermediate value
  20309. tooltip.getLabel().attr(now);
  20310. // Run on next tick of the mouse tracker
  20311. if (animate) {
  20312. // Never allow two timeouts
  20313. H.clearTimeout(this.tooltipTimeout);
  20314. // Set the fixed interval ticking for the smooth tooltip
  20315. this.tooltipTimeout = setTimeout(function () {
  20316. // The interval function may still be running during destroy,
  20317. // so check that the chart is really there before calling.
  20318. if (tooltip) {
  20319. tooltip.move(x, y, anchorX, anchorY);
  20320. }
  20321. }, 32);
  20322. }
  20323. },
  20324. /**
  20325. * Hides the tooltip with a fade out animation.
  20326. *
  20327. * @function Highcharts.Tooltip#hide
  20328. *
  20329. * @param {number} [delay]
  20330. * The fade out in milliseconds. If no value is provided the value
  20331. * of the tooltip.hideDelay option is used. A value of 0 disables
  20332. * the fade out animation.
  20333. */
  20334. hide: function (delay) {
  20335. var tooltip = this;
  20336. // disallow duplicate timers (#1728, #1766)
  20337. H.clearTimeout(this.hideTimer);
  20338. delay = pick(delay, this.options.hideDelay, 500);
  20339. if (!this.isHidden) {
  20340. this.hideTimer = syncTimeout(function () {
  20341. tooltip.getLabel()[delay ? 'fadeOut' : 'hide']();
  20342. tooltip.isHidden = true;
  20343. }, delay);
  20344. }
  20345. },
  20346. /**
  20347. * Extendable method to get the anchor position of the tooltip
  20348. * from a point or set of points
  20349. *
  20350. * @private
  20351. * @function Highcharts.Tooltip#getAnchor
  20352. *
  20353. * @param {Array<Highchart.Points>} points
  20354. *
  20355. * @param {global.Event} [mouseEvent]
  20356. */
  20357. getAnchor: function (points, mouseEvent) {
  20358. var ret,
  20359. chart = this.chart,
  20360. pointer = chart.pointer,
  20361. inverted = chart.inverted,
  20362. plotTop = chart.plotTop,
  20363. plotLeft = chart.plotLeft,
  20364. plotX = 0,
  20365. plotY = 0,
  20366. yAxis,
  20367. xAxis;
  20368. points = splat(points);
  20369. // When tooltip follows mouse, relate the position to the mouse
  20370. if (this.followPointer && mouseEvent) {
  20371. if (mouseEvent.chartX === undefined) {
  20372. mouseEvent = pointer.normalize(mouseEvent);
  20373. }
  20374. ret = [
  20375. mouseEvent.chartX - chart.plotLeft,
  20376. mouseEvent.chartY - plotTop
  20377. ];
  20378. // Pie uses a special tooltipPos
  20379. } else if (points[0].tooltipPos) {
  20380. ret = points[0].tooltipPos;
  20381. // When shared, use the average position
  20382. } else {
  20383. points.forEach(function (point) {
  20384. yAxis = point.series.yAxis;
  20385. xAxis = point.series.xAxis;
  20386. plotX += point.plotX +
  20387. (!inverted && xAxis ? xAxis.left - plotLeft : 0);
  20388. plotY +=
  20389. (
  20390. point.plotLow ?
  20391. (point.plotLow + point.plotHigh) / 2 :
  20392. point.plotY
  20393. ) +
  20394. (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
  20395. });
  20396. plotX /= points.length;
  20397. plotY /= points.length;
  20398. ret = [
  20399. inverted ? chart.plotWidth - plotY : plotX,
  20400. this.shared && !inverted && points.length > 1 && mouseEvent ?
  20401. // place shared tooltip next to the mouse (#424)
  20402. mouseEvent.chartY - plotTop :
  20403. inverted ? chart.plotHeight - plotX : plotY
  20404. ];
  20405. }
  20406. return ret.map(Math.round);
  20407. },
  20408. /**
  20409. * Place the tooltip in a chart without spilling over
  20410. * and not covering the point it self.
  20411. *
  20412. * @private
  20413. * @function Highcharts.Tooltip#getPosition
  20414. *
  20415. * @param {number} boxWidth
  20416. *
  20417. * @param {number} boxHeight
  20418. *
  20419. * @param {Highcharts.Point} point
  20420. *
  20421. * @return {*}
  20422. */
  20423. getPosition: function (boxWidth, boxHeight, point) {
  20424. var chart = this.chart,
  20425. distance = this.distance,
  20426. ret = {},
  20427. // Don't use h if chart isn't inverted (#7242)
  20428. h = (chart.inverted && point.h) || 0, // #4117
  20429. swapped,
  20430. outside = this.outside,
  20431. outerWidth = outside ?
  20432. // substract distance to prevent scrollbars
  20433. doc.documentElement.clientWidth - 2 * distance :
  20434. chart.chartWidth,
  20435. outerHeight = outside ?
  20436. Math.max(
  20437. doc.body.scrollHeight,
  20438. doc.documentElement.scrollHeight,
  20439. doc.body.offsetHeight,
  20440. doc.documentElement.offsetHeight,
  20441. doc.documentElement.clientHeight
  20442. ) :
  20443. chart.chartHeight,
  20444. chartPosition = chart.pointer.chartPosition,
  20445. first = [
  20446. 'y',
  20447. outerHeight,
  20448. boxHeight,
  20449. (outside ? chartPosition.top - distance : 0) +
  20450. point.plotY + chart.plotTop,
  20451. outside ? 0 : chart.plotTop,
  20452. outside ? outerHeight : chart.plotTop + chart.plotHeight
  20453. ],
  20454. second = [
  20455. 'x',
  20456. outerWidth,
  20457. boxWidth,
  20458. (outside ? chartPosition.left - distance : 0) +
  20459. point.plotX + chart.plotLeft,
  20460. outside ? 0 : chart.plotLeft,
  20461. outside ? outerWidth : chart.plotLeft + chart.plotWidth
  20462. ],
  20463. // The far side is right or bottom
  20464. preferFarSide = !this.followPointer && pick(
  20465. point.ttBelow,
  20466. !chart.inverted === !!point.negative
  20467. ), // #4984
  20468. /*
  20469. * Handle the preferred dimension. When the preferred dimension is
  20470. * tooltip on top or bottom of the point, it will look for space
  20471. * there.
  20472. *
  20473. * @private
  20474. */
  20475. firstDimension = function (
  20476. dim,
  20477. outerSize,
  20478. innerSize,
  20479. point,
  20480. min,
  20481. max
  20482. ) {
  20483. var roomLeft = innerSize < point - distance,
  20484. roomRight = point + distance + innerSize < outerSize,
  20485. alignedLeft = point - distance - innerSize,
  20486. alignedRight = point + distance;
  20487. if (preferFarSide && roomRight) {
  20488. ret[dim] = alignedRight;
  20489. } else if (!preferFarSide && roomLeft) {
  20490. ret[dim] = alignedLeft;
  20491. } else if (roomLeft) {
  20492. ret[dim] = Math.min(
  20493. max - innerSize,
  20494. alignedLeft - h < 0 ? alignedLeft : alignedLeft - h
  20495. );
  20496. } else if (roomRight) {
  20497. ret[dim] = Math.max(
  20498. min,
  20499. alignedRight + h + innerSize > outerSize ?
  20500. alignedRight :
  20501. alignedRight + h
  20502. );
  20503. } else {
  20504. return false;
  20505. }
  20506. },
  20507. /*
  20508. * Handle the secondary dimension. If the preferred dimension is
  20509. * tooltip on top or bottom of the point, the second dimension is to
  20510. * align the tooltip above the point, trying to align center but
  20511. * allowing left or right align within the chart box.
  20512. *
  20513. * @private
  20514. */
  20515. secondDimension = function (dim, outerSize, innerSize, point) {
  20516. var retVal;
  20517. // Too close to the edge, return false and swap dimensions
  20518. if (point < distance || point > outerSize - distance) {
  20519. retVal = false;
  20520. // Align left/top
  20521. } else if (point < innerSize / 2) {
  20522. ret[dim] = 1;
  20523. // Align right/bottom
  20524. } else if (point > outerSize - innerSize / 2) {
  20525. ret[dim] = outerSize - innerSize - 2;
  20526. // Align center
  20527. } else {
  20528. ret[dim] = point - innerSize / 2;
  20529. }
  20530. return retVal;
  20531. },
  20532. /*
  20533. * Swap the dimensions
  20534. */
  20535. swap = function (count) {
  20536. var temp = first;
  20537. first = second;
  20538. second = temp;
  20539. swapped = count;
  20540. },
  20541. run = function () {
  20542. if (firstDimension.apply(0, first) !== false) {
  20543. if (
  20544. secondDimension.apply(0, second) === false &&
  20545. !swapped
  20546. ) {
  20547. swap(true);
  20548. run();
  20549. }
  20550. } else if (!swapped) {
  20551. swap(true);
  20552. run();
  20553. } else {
  20554. ret.x = ret.y = 0;
  20555. }
  20556. };
  20557. // Under these conditions, prefer the tooltip on the side of the point
  20558. if (chart.inverted || this.len > 1) {
  20559. swap();
  20560. }
  20561. run();
  20562. return ret;
  20563. },
  20564. /**
  20565. * In case no user defined formatter is given, this will be used. Note that
  20566. * the context here is an object holding point, series, x, y etc.
  20567. *
  20568. * @private
  20569. * @function Highcharts.Tooltip#defaultFormatter
  20570. *
  20571. * @param {Highcharts.Tooltip} tooltip
  20572. *
  20573. * @return {Array<string>}
  20574. */
  20575. defaultFormatter: function (tooltip) {
  20576. var items = this.points || splat(this),
  20577. s;
  20578. // Build the header
  20579. s = [tooltip.tooltipFooterHeaderFormatter(items[0])];
  20580. // build the values
  20581. s = s.concat(tooltip.bodyFormatter(items));
  20582. // footer
  20583. s.push(tooltip.tooltipFooterHeaderFormatter(items[0], true));
  20584. return s;
  20585. },
  20586. /**
  20587. * Refresh the tooltip's text and position.
  20588. *
  20589. * @function Highcharts.Tooltip#refresh
  20590. *
  20591. * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
  20592. * Either a point or an array of points.
  20593. *
  20594. * @param {global.Event} [mouseEvent]
  20595. * Mouse event, that is responsible for the refresh and should be
  20596. * used for the tooltip update.
  20597. */
  20598. refresh: function (pointOrPoints, mouseEvent) {
  20599. var tooltip = this,
  20600. label,
  20601. options = tooltip.options,
  20602. x,
  20603. y,
  20604. point = pointOrPoints,
  20605. anchor,
  20606. textConfig = {},
  20607. text,
  20608. pointConfig = [],
  20609. formatter = options.formatter || tooltip.defaultFormatter,
  20610. shared = tooltip.shared,
  20611. currentSeries,
  20612. styledMode = this.chart.styledMode;
  20613. if (!options.enabled) {
  20614. return;
  20615. }
  20616. H.clearTimeout(this.hideTimer);
  20617. // get the reference point coordinates (pie charts use tooltipPos)
  20618. tooltip.followPointer = splat(point)[0].series.tooltipOptions
  20619. .followPointer;
  20620. anchor = tooltip.getAnchor(point, mouseEvent);
  20621. x = anchor[0];
  20622. y = anchor[1];
  20623. // shared tooltip, array is sent over
  20624. if (shared && !(point.series && point.series.noSharedTooltip)) {
  20625. point.forEach(function (item) {
  20626. item.setState('hover');
  20627. pointConfig.push(item.getLabelConfig());
  20628. });
  20629. textConfig = {
  20630. x: point[0].category,
  20631. y: point[0].y
  20632. };
  20633. textConfig.points = pointConfig;
  20634. point = point[0];
  20635. // single point tooltip
  20636. } else {
  20637. textConfig = point.getLabelConfig();
  20638. }
  20639. this.len = pointConfig.length; // #6128
  20640. text = formatter.call(textConfig, tooltip);
  20641. // register the current series
  20642. currentSeries = point.series;
  20643. this.distance = pick(currentSeries.tooltipOptions.distance, 16);
  20644. // update the inner HTML
  20645. if (text === false) {
  20646. this.hide();
  20647. } else {
  20648. label = tooltip.getLabel();
  20649. // show it
  20650. if (tooltip.isHidden) {
  20651. label.attr({
  20652. opacity: 1
  20653. }).show();
  20654. }
  20655. // update text
  20656. if (tooltip.split) {
  20657. this.renderSplit(text, splat(pointOrPoints));
  20658. } else {
  20659. // Prevent the tooltip from flowing over the chart box (#6659)
  20660. if (!options.style.width || styledMode) {
  20661. label.css({
  20662. width: this.chart.spacingBox.width
  20663. });
  20664. }
  20665. label.attr({
  20666. text: text && text.join ? text.join('') : text
  20667. });
  20668. // Set the stroke color of the box to reflect the point
  20669. label.removeClass(/highcharts-color-[\d]+/g)
  20670. .addClass(
  20671. 'highcharts-color-' +
  20672. pick(point.colorIndex, currentSeries.colorIndex)
  20673. );
  20674. if (!styledMode) {
  20675. label.attr({
  20676. stroke: (
  20677. options.borderColor ||
  20678. point.color ||
  20679. currentSeries.color ||
  20680. '#666666'
  20681. )
  20682. });
  20683. }
  20684. tooltip.updatePosition({
  20685. plotX: x,
  20686. plotY: y,
  20687. negative: point.negative,
  20688. ttBelow: point.ttBelow,
  20689. h: anchor[2] || 0
  20690. });
  20691. }
  20692. this.isHidden = false;
  20693. }
  20694. },
  20695. /**
  20696. * Render the split tooltip. Loops over each point's text and adds
  20697. * a label next to the point, then uses the distribute function to
  20698. * find best non-overlapping positions.
  20699. *
  20700. * @private
  20701. * @function Highcharts.Tooltip#renderSplit
  20702. *
  20703. * @param {Array<Highcharts.Label>} labels
  20704. *
  20705. * @param {Array<Highcharts.Point>} points
  20706. */
  20707. renderSplit: function (labels, points) {
  20708. var tooltip = this,
  20709. boxes = [],
  20710. chart = this.chart,
  20711. ren = chart.renderer,
  20712. rightAligned = true,
  20713. options = this.options,
  20714. headerHeight = 0,
  20715. headerTop,
  20716. tooltipLabel = this.getLabel(),
  20717. distributionBoxTop = chart.plotTop;
  20718. // Graceful degradation for legacy formatters
  20719. if (H.isString(labels)) {
  20720. labels = [false, labels];
  20721. }
  20722. // Create the individual labels for header and points, ignore footer
  20723. labels.slice(0, points.length + 1).forEach(function (str, i) {
  20724. if (str !== false && str !== '') {
  20725. var point = points[i - 1] ||
  20726. {
  20727. // Item 0 is the header. Instead of this, we could also
  20728. // use the crosshair label
  20729. isHeader: true,
  20730. plotX: points[0].plotX,
  20731. plotY: chart.plotHeight
  20732. },
  20733. owner = point.series || tooltip,
  20734. tt = owner.tt,
  20735. series = point.series || {},
  20736. colorClass = 'highcharts-color-' + pick(
  20737. point.colorIndex,
  20738. series.colorIndex,
  20739. 'none'
  20740. ),
  20741. target,
  20742. x,
  20743. bBox,
  20744. boxWidth,
  20745. attribs;
  20746. // Store the tooltip referance on the series
  20747. if (!tt) {
  20748. attribs = {
  20749. padding: options.padding,
  20750. r: options.borderRadius
  20751. };
  20752. if (!chart.styledMode) {
  20753. attribs.fill = options.backgroundColor;
  20754. attribs.stroke = (
  20755. options.borderColor ||
  20756. point.color ||
  20757. series.color ||
  20758. '#333333'
  20759. );
  20760. attribs['stroke-width'] = options.borderWidth;
  20761. }
  20762. owner.tt = tt = ren
  20763. .label(
  20764. null,
  20765. null,
  20766. null,
  20767. (
  20768. point.isHeader ? options.headerShape :
  20769. options.shape
  20770. ) || 'callout',
  20771. null,
  20772. null,
  20773. options.useHTML
  20774. )
  20775. .addClass('highcharts-tooltip-box ' + colorClass)
  20776. .attr(attribs)
  20777. .add(tooltipLabel);
  20778. }
  20779. tt.isActive = true;
  20780. tt.attr({
  20781. text: str
  20782. });
  20783. if (!chart.styledMode) {
  20784. tt.css(options.style)
  20785. .shadow(options.shadow);
  20786. }
  20787. // Get X position now, so we can move all to the other side in
  20788. // case of overflow
  20789. bBox = tt.getBBox();
  20790. boxWidth = bBox.width + tt.strokeWidth();
  20791. if (point.isHeader) {
  20792. headerHeight = bBox.height;
  20793. if (chart.xAxis[0].opposite) {
  20794. headerTop = true;
  20795. distributionBoxTop -= headerHeight;
  20796. }
  20797. x = Math.max(
  20798. 0, // No left overflow
  20799. Math.min(
  20800. point.plotX + chart.plotLeft - boxWidth / 2,
  20801. // No right overflow (#5794)
  20802. chart.chartWidth +
  20803. (
  20804. // Scrollable plot area
  20805. chart.scrollablePixels ?
  20806. chart.scrollablePixels - chart.marginRight :
  20807. 0
  20808. ) -
  20809. boxWidth
  20810. )
  20811. );
  20812. } else {
  20813. x = point.plotX + chart.plotLeft -
  20814. pick(options.distance, 16) - boxWidth;
  20815. }
  20816. // If overflow left, we don't use this x in the next loop
  20817. if (x < 0) {
  20818. rightAligned = false;
  20819. }
  20820. // Prepare for distribution
  20821. target = (point.series && point.series.yAxis &&
  20822. point.series.yAxis.pos) + (point.plotY || 0);
  20823. target -= distributionBoxTop;
  20824. if (point.isHeader) {
  20825. target = headerTop ?
  20826. -headerHeight :
  20827. chart.plotHeight + headerHeight;
  20828. }
  20829. boxes.push({
  20830. target: target,
  20831. rank: point.isHeader ? 1 : 0,
  20832. size: owner.tt.getBBox().height + 1,
  20833. point: point,
  20834. x: x,
  20835. tt: tt
  20836. });
  20837. }
  20838. });
  20839. // Clean previous run (for missing points)
  20840. this.cleanSplit();
  20841. if (options.positioner) {
  20842. boxes.forEach(function (box) {
  20843. var boxPosition = options.positioner.call(
  20844. tooltip,
  20845. box.tt.getBBox().width,
  20846. box.size,
  20847. box.point
  20848. );
  20849. box.x = boxPosition.x;
  20850. box.align = 0; // 0-align to the top, 1-align to the bottom
  20851. box.target = boxPosition.y;
  20852. box.rank = pick(boxPosition.rank, box.rank);
  20853. });
  20854. }
  20855. // Distribute and put in place
  20856. H.distribute(boxes, chart.plotHeight + headerHeight);
  20857. boxes.forEach(function (box) {
  20858. var point = box.point,
  20859. series = point.series;
  20860. // Put the label in place
  20861. box.tt.attr({
  20862. visibility: box.pos === undefined ? 'hidden' : 'inherit',
  20863. x: (rightAligned || point.isHeader || options.positioner ?
  20864. box.x :
  20865. point.plotX + chart.plotLeft + tooltip.distance),
  20866. y: box.pos + distributionBoxTop,
  20867. anchorX: point.isHeader ?
  20868. point.plotX + chart.plotLeft :
  20869. point.plotX + series.xAxis.pos,
  20870. anchorY: point.isHeader ?
  20871. chart.plotTop + chart.plotHeight / 2 :
  20872. point.plotY + series.yAxis.pos
  20873. });
  20874. });
  20875. },
  20876. /**
  20877. * Find the new position and perform the move
  20878. *
  20879. * @private
  20880. * @function Highcharts.Tooltip#updatePosition
  20881. *
  20882. * @param {Highcharts.Point} point
  20883. */
  20884. updatePosition: function (point) {
  20885. var chart = this.chart,
  20886. label = this.getLabel(),
  20887. pos = (this.options.positioner || this.getPosition).call(
  20888. this,
  20889. label.width,
  20890. label.height,
  20891. point
  20892. ),
  20893. anchorX = point.plotX + chart.plotLeft,
  20894. anchorY = point.plotY + chart.plotTop,
  20895. pad;
  20896. // Set the renderer size dynamically to prevent document size to change
  20897. if (this.outside) {
  20898. pad = (this.options.borderWidth || 0) + 2 * this.distance;
  20899. this.renderer.setSize(
  20900. label.width + pad,
  20901. label.height + pad,
  20902. false
  20903. );
  20904. anchorX += chart.pointer.chartPosition.left - pos.x;
  20905. anchorY += chart.pointer.chartPosition.top - pos.y;
  20906. }
  20907. // do the move
  20908. this.move(
  20909. Math.round(pos.x),
  20910. Math.round(pos.y || 0), // can be undefined (#3977)
  20911. anchorX,
  20912. anchorY
  20913. );
  20914. },
  20915. /**
  20916. * Get the optimal date format for a point, based on a range.
  20917. *
  20918. * @private
  20919. * @function Highcharts.Tooltip#getDateFormat
  20920. *
  20921. * @param {number} range
  20922. * The time range
  20923. *
  20924. * @param {number|Date} date
  20925. * The date of the point in question
  20926. *
  20927. * @param {number} startOfWeek
  20928. * An integer representing the first day of the week, where 0 is
  20929. * Sunday.
  20930. *
  20931. * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
  20932. * A map of time units to formats.
  20933. *
  20934. * @return {string}
  20935. * The optimal date format for a point.
  20936. */
  20937. getDateFormat: function (range, date, startOfWeek, dateTimeLabelFormats) {
  20938. var time = this.chart.time,
  20939. dateStr = time.dateFormat('%m-%d %H:%M:%S.%L', date),
  20940. format,
  20941. n,
  20942. blank = '01-01 00:00:00.000',
  20943. strpos = {
  20944. millisecond: 15,
  20945. second: 12,
  20946. minute: 9,
  20947. hour: 6,
  20948. day: 3
  20949. },
  20950. lastN = 'millisecond'; // for sub-millisecond data, #4223
  20951. for (n in timeUnits) {
  20952. // If the range is exactly one week and we're looking at a
  20953. // Sunday/Monday, go for the week format
  20954. if (
  20955. range === timeUnits.week &&
  20956. +time.dateFormat('%w', date) === startOfWeek &&
  20957. dateStr.substr(6) === blank.substr(6)
  20958. ) {
  20959. n = 'week';
  20960. break;
  20961. }
  20962. // The first format that is too great for the range
  20963. if (timeUnits[n] > range) {
  20964. n = lastN;
  20965. break;
  20966. }
  20967. // If the point is placed every day at 23:59, we need to show
  20968. // the minutes as well. #2637.
  20969. if (
  20970. strpos[n] &&
  20971. dateStr.substr(strpos[n]) !== blank.substr(strpos[n])
  20972. ) {
  20973. break;
  20974. }
  20975. // Weeks are outside the hierarchy, only apply them on
  20976. // Mondays/Sundays like in the first condition
  20977. if (n !== 'week') {
  20978. lastN = n;
  20979. }
  20980. }
  20981. if (n) {
  20982. format = time.resolveDTLFormat(dateTimeLabelFormats[n]).main;
  20983. }
  20984. return format;
  20985. },
  20986. /**
  20987. * Get the best X date format based on the closest point range on the axis.
  20988. *
  20989. * @private
  20990. * @function Highcharts.Tooltip#getXDateFormat
  20991. *
  20992. * @param {Highcharts.Point} point
  20993. *
  20994. * @param {Highcharts.TooltipOptions} options
  20995. *
  20996. * @param {Highcharts.Axis} xAxis
  20997. *
  20998. * @return {string}
  20999. */
  21000. getXDateFormat: function (point, options, xAxis) {
  21001. var xDateFormat,
  21002. dateTimeLabelFormats = options.dateTimeLabelFormats,
  21003. closestPointRange = xAxis && xAxis.closestPointRange;
  21004. if (closestPointRange) {
  21005. xDateFormat = this.getDateFormat(
  21006. closestPointRange,
  21007. point.x,
  21008. xAxis.options.startOfWeek,
  21009. dateTimeLabelFormats
  21010. );
  21011. } else {
  21012. xDateFormat = dateTimeLabelFormats.day;
  21013. }
  21014. return xDateFormat || dateTimeLabelFormats.year; // #2546, 2581
  21015. },
  21016. /**
  21017. * Format the footer/header of the tooltip
  21018. * #3397: abstraction to enable formatting of footer and header
  21019. *
  21020. * @private
  21021. * @function Highcharts.Tooltip#tooltipFooterHeaderFormatter
  21022. *
  21023. * @param {*} labelConfig
  21024. *
  21025. * @param {boolean} isFooter
  21026. *
  21027. * @return {string}
  21028. */
  21029. tooltipFooterHeaderFormatter: function (labelConfig, isFooter) {
  21030. var footOrHead = isFooter ? 'footer' : 'header',
  21031. series = labelConfig.series,
  21032. tooltipOptions = series.tooltipOptions,
  21033. xDateFormat = tooltipOptions.xDateFormat,
  21034. xAxis = series.xAxis,
  21035. isDateTime = (
  21036. xAxis &&
  21037. xAxis.options.type === 'datetime' &&
  21038. isNumber(labelConfig.key)
  21039. ),
  21040. formatString = tooltipOptions[footOrHead + 'Format'],
  21041. evt = { isFooter: isFooter, labelConfig: labelConfig };
  21042. H.fireEvent(this, 'headerFormatter', evt, function (e) {
  21043. // Guess the best date format based on the closest point distance
  21044. // (#568, #3418)
  21045. if (isDateTime && !xDateFormat) {
  21046. xDateFormat = this.getXDateFormat(
  21047. labelConfig,
  21048. tooltipOptions,
  21049. xAxis
  21050. );
  21051. }
  21052. // Insert the footer date format if any
  21053. if (isDateTime && xDateFormat) {
  21054. ((labelConfig.point && labelConfig.point.tooltipDateKeys) ||
  21055. ['key']).forEach(
  21056. function (key) {
  21057. formatString = formatString.replace(
  21058. '{point.' + key + '}',
  21059. '{point.' + key + ':' + xDateFormat + '}'
  21060. );
  21061. }
  21062. );
  21063. }
  21064. // Replace default header style with class name
  21065. if (series.chart.styledMode) {
  21066. formatString = this.styledModeFormat(formatString);
  21067. }
  21068. e.text = format(formatString, {
  21069. point: labelConfig,
  21070. series: series
  21071. }, this.chart.time);
  21072. });
  21073. return evt.text;
  21074. },
  21075. /**
  21076. * Build the body (lines) of the tooltip by iterating over the items and
  21077. * returning one entry for each item, abstracting this functionality allows
  21078. * to easily overwrite and extend it.
  21079. *
  21080. * @private
  21081. * @function Highcharts.Tooltip#bodyFormatter
  21082. *
  21083. * @param {Array<Highcharts.Point>} items
  21084. *
  21085. * @return {string}
  21086. */
  21087. bodyFormatter: function (items) {
  21088. return items.map(function (item) {
  21089. var tooltipOptions = item.series.tooltipOptions;
  21090. return (
  21091. tooltipOptions[
  21092. (item.point.formatPrefix || 'point') + 'Formatter'
  21093. ] ||
  21094. item.point.tooltipFormatter
  21095. ).call(
  21096. item.point,
  21097. tooltipOptions[
  21098. (item.point.formatPrefix || 'point') + 'Format'
  21099. ] || ''
  21100. );
  21101. });
  21102. },
  21103. styledModeFormat: function (formatString) {
  21104. return formatString
  21105. .replace(
  21106. 'style="font-size: 10px"',
  21107. 'class="highcharts-header"'
  21108. )
  21109. .replace(
  21110. /style="color:{(point|series)\.color}"/g,
  21111. 'class="highcharts-color-{$1.colorIndex}"'
  21112. );
  21113. }
  21114. };
  21115. }(Highcharts));
  21116. (function (Highcharts) {
  21117. /**
  21118. * (c) 2010-2019 Torstein Honsi
  21119. *
  21120. * License: www.highcharts.com/license
  21121. */
  21122. /**
  21123. * One position in relation to an axis.
  21124. *
  21125. * @interface Highcharts.PointerAxisCoordinateObject
  21126. *//**
  21127. * Related axis.
  21128. *
  21129. * @name Highcharts.PointerAxisCoordinateObject#axis
  21130. * @type {Highcharts.Axis}
  21131. *//**
  21132. * Axis value.
  21133. *
  21134. * @name Highcharts.PointerAxisCoordinateObject#value
  21135. * @type {number}
  21136. */
  21137. /**
  21138. * Positions in terms of axis values.
  21139. *
  21140. * @interface Highcharts.PointerAxisCoordinatesObject
  21141. *//**
  21142. * Positions on the x-axis.
  21143. * @name Highcharts.PointerAxisCoordinatesObject#xAxis
  21144. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  21145. *//**
  21146. * Positions on the y-axis.
  21147. * @name Highcharts.PointerAxisCoordinatesObject#yAxis
  21148. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  21149. */
  21150. /**
  21151. * Pointer coordinates.
  21152. *
  21153. * @interface Highcharts.PointerCoordinatesObject
  21154. *//**
  21155. * @name Highcharts.PointerCoordinatesObject#chartX
  21156. * @type {number}
  21157. *//**
  21158. * @name Highcharts.PointerCoordinatesObject#chartY
  21159. * @type {number}
  21160. */
  21161. /**
  21162. * A native browser mouse or touch event, extended with position information
  21163. * relative to the {@link Chart.container}.
  21164. *
  21165. * @interface Highcharts.PointerEventObject
  21166. * @extends global.PointerEvent
  21167. *//**
  21168. * The X coordinate of the pointer interaction relative to the chart.
  21169. *
  21170. * @name Highcharts.PointerEventObject#chartX
  21171. * @type {number}
  21172. *//**
  21173. * The Y coordinate of the pointer interaction relative to the chart.
  21174. *
  21175. * @name Highcharts.PointerEventObject#chartY
  21176. * @type {number}
  21177. */
  21178. /**
  21179. * Axis-specific data of a selection.
  21180. *
  21181. * @interface Highcharts.SelectDataObject
  21182. *//**
  21183. * @name Highcharts.SelectDataObject#axis
  21184. * @type {Highcharts.Axis}
  21185. *//**
  21186. * @name Highcharts.SelectDataObject#min
  21187. * @type {number}
  21188. *//**
  21189. * @name Highcharts.SelectDataObject#max
  21190. * @type {number}
  21191. */
  21192. /**
  21193. * Object for select events.
  21194. *
  21195. * @interface Highcharts.SelectEventObject
  21196. *//**
  21197. * @name Highcharts.SelectEventObject#originalEvent
  21198. * @type {global.Event}
  21199. *//**
  21200. * @name Highcharts.SelectEventObject#xAxis
  21201. * @type {Array<Highcharts.SelectDataObject>}
  21202. *//**
  21203. * @name Highcharts.SelectEventObject#yAxis
  21204. * @type {Array<Highcharts.SelectDataObject>}
  21205. */
  21206. var H = Highcharts,
  21207. addEvent = H.addEvent,
  21208. attr = H.attr,
  21209. charts = H.charts,
  21210. color = H.color,
  21211. css = H.css,
  21212. defined = H.defined,
  21213. extend = H.extend,
  21214. find = H.find,
  21215. fireEvent = H.fireEvent,
  21216. isNumber = H.isNumber,
  21217. isObject = H.isObject,
  21218. offset = H.offset,
  21219. pick = H.pick,
  21220. splat = H.splat,
  21221. Tooltip = H.Tooltip;
  21222. /**
  21223. * The mouse and touch tracker object. Each {@link Chart} item has one
  21224. * assosiated Pointer item that can be accessed from the {@link Chart.pointer}
  21225. * property.
  21226. *
  21227. * @class
  21228. * @name Highcharts.Pointer
  21229. *
  21230. * @param {Highcharts.Chart} chart
  21231. * The Chart instance.
  21232. *
  21233. * @param {Highcharts.Options} options
  21234. * The root options object. The pointer uses options from the chart and
  21235. * tooltip structures.
  21236. */
  21237. Highcharts.Pointer = function (chart, options) {
  21238. this.init(chart, options);
  21239. };
  21240. Highcharts.Pointer.prototype = {
  21241. /**
  21242. * Initialize the Pointer.
  21243. *
  21244. * @private
  21245. * @function Highcharts.Pointer#init
  21246. *
  21247. * @param {Highcharts.Chart} chart
  21248. * The Chart instance.
  21249. *
  21250. * @param {Highcharts.Options} options
  21251. * The root options object. The pointer uses options from the chart
  21252. * and tooltip structures.
  21253. */
  21254. init: function (chart, options) {
  21255. // Store references
  21256. this.options = options;
  21257. this.chart = chart;
  21258. // Do we need to handle click on a touch device?
  21259. this.runChartClick =
  21260. options.chart.events && !!options.chart.events.click;
  21261. this.pinchDown = [];
  21262. this.lastValidTouch = {};
  21263. if (Tooltip) {
  21264. /**
  21265. * Tooltip object for points of series.
  21266. *
  21267. * @name Highcharts.Chart#tooltip
  21268. * @type {Highcharts.Tooltip}
  21269. */
  21270. chart.tooltip = new Tooltip(chart, options.tooltip);
  21271. this.followTouchMove = pick(options.tooltip.followTouchMove, true);
  21272. }
  21273. this.setDOMEvents();
  21274. },
  21275. /**
  21276. * Resolve the zoomType option, this is reset on all touch start and mouse
  21277. * down events.
  21278. *
  21279. * @private
  21280. * @function Highcharts.Pointer#zoomOption
  21281. *
  21282. * @param {global.Event} e
  21283. * Event object.
  21284. */
  21285. zoomOption: function (e) {
  21286. var chart = this.chart,
  21287. options = chart.options.chart,
  21288. zoomType = options.zoomType || '',
  21289. inverted = chart.inverted,
  21290. zoomX,
  21291. zoomY;
  21292. // Look for the pinchType option
  21293. if (/touch/.test(e.type)) {
  21294. zoomType = pick(options.pinchType, zoomType);
  21295. }
  21296. this.zoomX = zoomX = /x/.test(zoomType);
  21297. this.zoomY = zoomY = /y/.test(zoomType);
  21298. this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
  21299. this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
  21300. this.hasZoom = zoomX || zoomY;
  21301. },
  21302. /**
  21303. * Takes a browser event object and extends it with custom Highcharts
  21304. * properties `chartX` and `chartY` in order to work on the internal
  21305. * coordinate system.
  21306. *
  21307. * @function Highcharts.Pointer#normalize
  21308. *
  21309. * @param {global.Event} e
  21310. * Event object in standard browsers.
  21311. *
  21312. * @return {Highcharts.PointerEventObject}
  21313. * A browser event with extended properties `chartX` and `chartY`.
  21314. */
  21315. normalize: function (e, chartPosition) {
  21316. var ePos;
  21317. // iOS (#2757)
  21318. ePos = e.touches ?
  21319. (e.touches.length ? e.touches.item(0) : e.changedTouches[0]) :
  21320. e;
  21321. // Get mouse position
  21322. if (!chartPosition) {
  21323. this.chartPosition = chartPosition = offset(this.chart.container);
  21324. }
  21325. return extend(e, {
  21326. chartX: Math.round(ePos.pageX - chartPosition.left),
  21327. chartY: Math.round(ePos.pageY - chartPosition.top)
  21328. });
  21329. },
  21330. /**
  21331. * Get the click position in terms of axis values.
  21332. *
  21333. * @function Highcharts.Pointer#getCoordinates
  21334. *
  21335. * @param {Highcharts.PointerEventObject} e
  21336. * Pointer event, extended with `chartX` and `chartY` properties.
  21337. *
  21338. * @return {Highcharts.PointerAxisCoordinatesObject}
  21339. */
  21340. getCoordinates: function (e) {
  21341. var coordinates = {
  21342. xAxis: [],
  21343. yAxis: []
  21344. };
  21345. this.chart.axes.forEach(function (axis) {
  21346. coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
  21347. axis: axis,
  21348. value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
  21349. });
  21350. });
  21351. return coordinates;
  21352. },
  21353. /**
  21354. * Finds the closest point to a set of coordinates, using the k-d-tree
  21355. * algorithm.
  21356. *
  21357. * @function Highcharts.Pointer#findNearestKDPoints
  21358. *
  21359. * @param {Array<Highcharts.Series>} series
  21360. * All the series to search in.
  21361. *
  21362. * @param {boolean} shared
  21363. * Whether it is a shared tooltip or not.
  21364. *
  21365. * @param {Highcharts.PointerEventObject} e
  21366. * The pointer event object, containing chart coordinates of the
  21367. * pointer.
  21368. *
  21369. * @return {Point|undefined}
  21370. * The point closest to given coordinates.
  21371. */
  21372. findNearestKDPoint: function (series, shared, e) {
  21373. var closest,
  21374. sort = function (p1, p2) {
  21375. var isCloserX = p1.distX - p2.distX,
  21376. isCloser = p1.dist - p2.dist,
  21377. isAbove =
  21378. (p2.series.group && p2.series.group.zIndex) -
  21379. (p1.series.group && p1.series.group.zIndex),
  21380. result;
  21381. // We have two points which are not in the same place on xAxis
  21382. // and shared tooltip:
  21383. if (isCloserX !== 0 && shared) { // #5721
  21384. result = isCloserX;
  21385. // Points are not exactly in the same place on x/yAxis:
  21386. } else if (isCloser !== 0) {
  21387. result = isCloser;
  21388. // The same xAxis and yAxis position, sort by z-index:
  21389. } else if (isAbove !== 0) {
  21390. result = isAbove;
  21391. // The same zIndex, sort by array index:
  21392. } else {
  21393. result = p1.series.index > p2.series.index ? -1 : 1;
  21394. }
  21395. return result;
  21396. };
  21397. series.forEach(function (s) {
  21398. var noSharedTooltip = s.noSharedTooltip && shared,
  21399. compareX = (
  21400. !noSharedTooltip &&
  21401. s.options.findNearestPointBy.indexOf('y') < 0
  21402. ),
  21403. point = s.searchPoint(
  21404. e,
  21405. compareX
  21406. );
  21407. if (
  21408. // Check that we actually found a point on the series.
  21409. isObject(point, true) &&
  21410. // Use the new point if it is closer.
  21411. (!isObject(closest, true) || (sort(closest, point) > 0))
  21412. ) {
  21413. closest = point;
  21414. }
  21415. });
  21416. return closest;
  21417. },
  21418. /**
  21419. * @private
  21420. * @function Highcharts.Pointer#getPointFromEvent
  21421. *
  21422. * @param {global.Event} e
  21423. *
  21424. * @return {Highcharts.Point|undefined}
  21425. */
  21426. getPointFromEvent: function (e) {
  21427. var target = e.target,
  21428. point;
  21429. while (target && !point) {
  21430. point = target.point;
  21431. target = target.parentNode;
  21432. }
  21433. return point;
  21434. },
  21435. /**
  21436. * @private
  21437. * @function Highcharts.Pointer#getChartCoordinatesFromPoint
  21438. *
  21439. * @param {Highcharts.Point} point
  21440. *
  21441. * @param {boolean} inverted
  21442. *
  21443. * @return {Highcharts.PointerCoordinatesObject}
  21444. */
  21445. getChartCoordinatesFromPoint: function (point, inverted) {
  21446. var series = point.series,
  21447. xAxis = series.xAxis,
  21448. yAxis = series.yAxis,
  21449. plotX = pick(point.clientX, point.plotX),
  21450. shapeArgs = point.shapeArgs;
  21451. if (xAxis && yAxis) {
  21452. return inverted ? {
  21453. chartX: xAxis.len + xAxis.pos - plotX,
  21454. chartY: yAxis.len + yAxis.pos - point.plotY
  21455. } : {
  21456. chartX: plotX + xAxis.pos,
  21457. chartY: point.plotY + yAxis.pos
  21458. };
  21459. }
  21460. if (shapeArgs && shapeArgs.x && shapeArgs.y) {
  21461. // E.g. pies do not have axes
  21462. return {
  21463. chartX: shapeArgs.x,
  21464. chartY: shapeArgs.y
  21465. };
  21466. }
  21467. },
  21468. /**
  21469. * Calculates what is the current hovered point/points and series.
  21470. *
  21471. * @private
  21472. * @function Highcharts.Pointer#getHoverData
  21473. *
  21474. * @param {Highcharts.Point|undefined} existingHoverPoint
  21475. * The point currrently beeing hovered.
  21476. *
  21477. * @param {Highcharts.Series|undefined} existingHoverSeries
  21478. * The series currently beeing hovered.
  21479. *
  21480. * @param {Array<Highcharts.Series>} series
  21481. * All the series in the chart.
  21482. *
  21483. * @param {boolean} isDirectTouch
  21484. * Is the pointer directly hovering the point.
  21485. *
  21486. * @param {boolean} shared
  21487. * Whether it is a shared tooltip or not.
  21488. *
  21489. * @param {Highcharts.PointerEventObject} e
  21490. * The triggering event, containing chart coordinates of the pointer.
  21491. *
  21492. * @return {*}
  21493. * Object containing resulting hover data: hoverPoint, hoverSeries,
  21494. * and hoverPoints.
  21495. */
  21496. getHoverData: function (
  21497. existingHoverPoint,
  21498. existingHoverSeries,
  21499. series,
  21500. isDirectTouch,
  21501. shared,
  21502. e
  21503. ) {
  21504. var hoverPoint,
  21505. hoverPoints = [],
  21506. hoverSeries = existingHoverSeries,
  21507. useExisting = !!(isDirectTouch && existingHoverPoint),
  21508. notSticky = hoverSeries && !hoverSeries.stickyTracking,
  21509. filter = function (s) {
  21510. return (
  21511. s.visible &&
  21512. !(!shared && s.directTouch) && // #3821
  21513. pick(s.options.enableMouseTracking, true)
  21514. );
  21515. },
  21516. // Which series to look in for the hover point
  21517. searchSeries = notSticky ?
  21518. // Only search on hovered series if it has stickyTracking false
  21519. [hoverSeries] :
  21520. // Filter what series to look in.
  21521. series.filter(function (s) {
  21522. return filter(s) && s.stickyTracking;
  21523. });
  21524. // Use existing hovered point or find the one closest to coordinates.
  21525. hoverPoint = useExisting ?
  21526. existingHoverPoint :
  21527. this.findNearestKDPoint(searchSeries, shared, e);
  21528. // Assign hover series
  21529. hoverSeries = hoverPoint && hoverPoint.series;
  21530. // If we have a hoverPoint, assign hoverPoints.
  21531. if (hoverPoint) {
  21532. // When tooltip is shared, it displays more than one point
  21533. if (shared && !hoverSeries.noSharedTooltip) {
  21534. searchSeries = series.filter(function (s) {
  21535. return filter(s) && !s.noSharedTooltip;
  21536. });
  21537. // Get all points with the same x value as the hoverPoint
  21538. searchSeries.forEach(function (s) {
  21539. var point = find(s.points, function (p) {
  21540. return p.x === hoverPoint.x && !p.isNull;
  21541. });
  21542. if (isObject(point)) {
  21543. /*
  21544. * Boost returns a minimal point. Convert it to a usable
  21545. * point for tooltip and states.
  21546. */
  21547. if (s.chart.isBoosting) {
  21548. point = s.getPoint(point);
  21549. }
  21550. hoverPoints.push(point);
  21551. }
  21552. });
  21553. } else {
  21554. hoverPoints.push(hoverPoint);
  21555. }
  21556. }
  21557. return {
  21558. hoverPoint: hoverPoint,
  21559. hoverSeries: hoverSeries,
  21560. hoverPoints: hoverPoints
  21561. };
  21562. },
  21563. /**
  21564. * With line type charts with a single tracker, get the point closest to the
  21565. * mouse. Run Point.onMouseOver and display tooltip for the point or points.
  21566. *
  21567. * @private
  21568. * @function Highcharts.Pointer#runPointActions
  21569. *
  21570. * @param {global.Event} e
  21571. *
  21572. * @param {Highcharts.Point} p
  21573. *
  21574. * @fires Highcharts.Point#event:mouseOut
  21575. * @fires Highcharts.Point#event:mouseOver
  21576. */
  21577. runPointActions: function (e, p) {
  21578. var pointer = this,
  21579. chart = pointer.chart,
  21580. series = chart.series,
  21581. tooltip = chart.tooltip && chart.tooltip.options.enabled ?
  21582. chart.tooltip :
  21583. undefined,
  21584. shared = tooltip ? tooltip.shared : false,
  21585. hoverPoint = p || chart.hoverPoint,
  21586. hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries,
  21587. // onMouseOver or already hovering a series with directTouch
  21588. isDirectTouch = e.type !== 'touchmove' && (
  21589. !!p || (
  21590. (hoverSeries && hoverSeries.directTouch) &&
  21591. pointer.isDirectTouch
  21592. )
  21593. ),
  21594. hoverData = this.getHoverData(
  21595. hoverPoint,
  21596. hoverSeries,
  21597. series,
  21598. isDirectTouch,
  21599. shared,
  21600. e
  21601. ),
  21602. useSharedTooltip,
  21603. followPointer,
  21604. anchor,
  21605. points;
  21606. // Update variables from hoverData.
  21607. hoverPoint = hoverData.hoverPoint;
  21608. points = hoverData.hoverPoints;
  21609. hoverSeries = hoverData.hoverSeries;
  21610. followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
  21611. useSharedTooltip = (
  21612. shared &&
  21613. hoverSeries &&
  21614. !hoverSeries.noSharedTooltip
  21615. );
  21616. // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
  21617. // #3926, #4200
  21618. if (
  21619. hoverPoint &&
  21620. // !(hoverSeries && hoverSeries.directTouch) &&
  21621. (hoverPoint !== chart.hoverPoint || (tooltip && tooltip.isHidden))
  21622. ) {
  21623. (chart.hoverPoints || []).forEach(function (p) {
  21624. if (points.indexOf(p) === -1) {
  21625. p.setState();
  21626. }
  21627. });
  21628. // Do mouseover on all points (#3919, #3985, #4410, #5622)
  21629. (points || []).forEach(function (p) {
  21630. p.setState('hover');
  21631. });
  21632. // set normal state to previous series
  21633. if (chart.hoverSeries !== hoverSeries) {
  21634. hoverSeries.onMouseOver();
  21635. }
  21636. // If tracking is on series in stead of on each point,
  21637. // fire mouseOver on hover point. // #4448
  21638. if (chart.hoverPoint) {
  21639. chart.hoverPoint.firePointEvent('mouseOut');
  21640. }
  21641. // Hover point may have been destroyed in the event handlers (#7127)
  21642. if (!hoverPoint.series) {
  21643. return;
  21644. }
  21645. hoverPoint.firePointEvent('mouseOver');
  21646. chart.hoverPoints = points;
  21647. chart.hoverPoint = hoverPoint;
  21648. // Draw tooltip if necessary
  21649. if (tooltip) {
  21650. tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
  21651. }
  21652. // Update positions (regardless of kdpoint or hoverPoint)
  21653. } else if (followPointer && tooltip && !tooltip.isHidden) {
  21654. anchor = tooltip.getAnchor([{}], e);
  21655. tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
  21656. }
  21657. // Start the event listener to pick up the tooltip and crosshairs
  21658. if (!pointer.unDocMouseMove) {
  21659. pointer.unDocMouseMove = addEvent(
  21660. chart.container.ownerDocument,
  21661. 'mousemove',
  21662. function (e) {
  21663. var chart = charts[H.hoverChartIndex];
  21664. if (chart) {
  21665. chart.pointer.onDocumentMouseMove(e);
  21666. }
  21667. }
  21668. );
  21669. }
  21670. // Issues related to crosshair #4927, #5269 #5066, #5658
  21671. chart.axes.forEach(function drawAxisCrosshair(axis) {
  21672. var snap = pick(axis.crosshair.snap, true),
  21673. point = !snap ?
  21674. undefined :
  21675. H.find(points, function (p) {
  21676. return p.series[axis.coll] === axis;
  21677. });
  21678. // Axis has snapping crosshairs, and one of the hover points belongs
  21679. // to axis. Always call drawCrosshair when it is not snap.
  21680. if (point || !snap) {
  21681. axis.drawCrosshair(e, point);
  21682. // Axis has snapping crosshairs, but no hover point belongs to axis
  21683. } else {
  21684. axis.hideCrosshair();
  21685. }
  21686. });
  21687. },
  21688. /**
  21689. * Reset the tracking by hiding the tooltip, the hover series state and the
  21690. * hover point
  21691. *
  21692. * @function Highcharts.Pointer#reset
  21693. *
  21694. * @param {boolean} allowMove
  21695. * Instead of destroying the tooltip altogether, allow moving it if
  21696. * possible.
  21697. *
  21698. * @param {number} delay
  21699. */
  21700. reset: function (allowMove, delay) {
  21701. var pointer = this,
  21702. chart = pointer.chart,
  21703. hoverSeries = chart.hoverSeries,
  21704. hoverPoint = chart.hoverPoint,
  21705. hoverPoints = chart.hoverPoints,
  21706. tooltip = chart.tooltip,
  21707. tooltipPoints = tooltip && tooltip.shared ?
  21708. hoverPoints :
  21709. hoverPoint;
  21710. // Check if the points have moved outside the plot area (#1003, #4736,
  21711. // #5101)
  21712. if (allowMove && tooltipPoints) {
  21713. splat(tooltipPoints).forEach(function (point) {
  21714. if (point.series.isCartesian && point.plotX === undefined) {
  21715. allowMove = false;
  21716. }
  21717. });
  21718. }
  21719. // Just move the tooltip, #349
  21720. if (allowMove) {
  21721. if (tooltip && tooltipPoints && tooltipPoints.length) {
  21722. tooltip.refresh(tooltipPoints);
  21723. if (tooltip.shared && hoverPoints) { // #8284
  21724. hoverPoints.forEach(function (point) {
  21725. point.setState(point.state, true);
  21726. if (point.series.isCartesian) {
  21727. if (point.series.xAxis.crosshair) {
  21728. point.series.xAxis.drawCrosshair(null, point);
  21729. }
  21730. if (point.series.yAxis.crosshair) {
  21731. point.series.yAxis.drawCrosshair(null, point);
  21732. }
  21733. }
  21734. });
  21735. } else if (hoverPoint) { // #2500
  21736. hoverPoint.setState(hoverPoint.state, true);
  21737. chart.axes.forEach(function (axis) {
  21738. if (axis.crosshair) {
  21739. axis.drawCrosshair(null, hoverPoint);
  21740. }
  21741. });
  21742. }
  21743. }
  21744. // Full reset
  21745. } else {
  21746. if (hoverPoint) {
  21747. hoverPoint.onMouseOut();
  21748. }
  21749. if (hoverPoints) {
  21750. hoverPoints.forEach(function (point) {
  21751. point.setState();
  21752. });
  21753. }
  21754. if (hoverSeries) {
  21755. hoverSeries.onMouseOut();
  21756. }
  21757. if (tooltip) {
  21758. tooltip.hide(delay);
  21759. }
  21760. if (pointer.unDocMouseMove) {
  21761. pointer.unDocMouseMove = pointer.unDocMouseMove();
  21762. }
  21763. // Remove crosshairs
  21764. chart.axes.forEach(function (axis) {
  21765. axis.hideCrosshair();
  21766. });
  21767. pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
  21768. }
  21769. },
  21770. /**
  21771. * Scale series groups to a certain scale and translation.
  21772. *
  21773. * @private
  21774. * @function Highcharts.Pointer#scaleGroups
  21775. *
  21776. * @param {Highcharts.SeriesPlotBoxObject} attribs
  21777. *
  21778. * @param {boolean} clip
  21779. */
  21780. scaleGroups: function (attribs, clip) {
  21781. var chart = this.chart,
  21782. seriesAttribs;
  21783. // Scale each series
  21784. chart.series.forEach(function (series) {
  21785. seriesAttribs = attribs || series.getPlotBox(); // #1701
  21786. if (series.xAxis && series.xAxis.zoomEnabled && series.group) {
  21787. series.group.attr(seriesAttribs);
  21788. if (series.markerGroup) {
  21789. series.markerGroup.attr(seriesAttribs);
  21790. series.markerGroup.clip(clip ? chart.clipRect : null);
  21791. }
  21792. if (series.dataLabelsGroup) {
  21793. series.dataLabelsGroup.attr(seriesAttribs);
  21794. }
  21795. }
  21796. });
  21797. // Clip
  21798. chart.clipRect.attr(clip || chart.clipBox);
  21799. },
  21800. /**
  21801. * Start a drag operation.
  21802. *
  21803. * @private
  21804. * @function Highcharts.Pointer#dragStart
  21805. *
  21806. * @param {Highcharts.PointerEventObject} e
  21807. */
  21808. dragStart: function (e) {
  21809. var chart = this.chart;
  21810. // Record the start position
  21811. chart.mouseIsDown = e.type;
  21812. chart.cancelClick = false;
  21813. chart.mouseDownX = this.mouseDownX = e.chartX;
  21814. chart.mouseDownY = this.mouseDownY = e.chartY;
  21815. },
  21816. /**
  21817. * Perform a drag operation in response to a mousemove event while the mouse
  21818. * is down.
  21819. *
  21820. * @private
  21821. * @function Highcharts.Pointer#drag
  21822. *
  21823. * @param {Highcharts.PointerEventObject} e
  21824. */
  21825. drag: function (e) {
  21826. var chart = this.chart,
  21827. chartOptions = chart.options.chart,
  21828. chartX = e.chartX,
  21829. chartY = e.chartY,
  21830. zoomHor = this.zoomHor,
  21831. zoomVert = this.zoomVert,
  21832. plotLeft = chart.plotLeft,
  21833. plotTop = chart.plotTop,
  21834. plotWidth = chart.plotWidth,
  21835. plotHeight = chart.plotHeight,
  21836. clickedInside,
  21837. size,
  21838. selectionMarker = this.selectionMarker,
  21839. mouseDownX = this.mouseDownX,
  21840. mouseDownY = this.mouseDownY,
  21841. panKey = chartOptions.panKey && e[chartOptions.panKey + 'Key'];
  21842. // If the device supports both touch and mouse (like IE11), and we are
  21843. // touch-dragging inside the plot area, don't handle the mouse event.
  21844. // #4339.
  21845. if (selectionMarker && selectionMarker.touch) {
  21846. return;
  21847. }
  21848. // If the mouse is outside the plot area, adjust to cooordinates
  21849. // inside to prevent the selection marker from going outside
  21850. if (chartX < plotLeft) {
  21851. chartX = plotLeft;
  21852. } else if (chartX > plotLeft + plotWidth) {
  21853. chartX = plotLeft + plotWidth;
  21854. }
  21855. if (chartY < plotTop) {
  21856. chartY = plotTop;
  21857. } else if (chartY > plotTop + plotHeight) {
  21858. chartY = plotTop + plotHeight;
  21859. }
  21860. // determine if the mouse has moved more than 10px
  21861. this.hasDragged = Math.sqrt(
  21862. Math.pow(mouseDownX - chartX, 2) +
  21863. Math.pow(mouseDownY - chartY, 2)
  21864. );
  21865. if (this.hasDragged > 10) {
  21866. clickedInside = chart.isInsidePlot(
  21867. mouseDownX - plotLeft,
  21868. mouseDownY - plotTop
  21869. );
  21870. // make a selection
  21871. if (
  21872. chart.hasCartesianSeries &&
  21873. (this.zoomX || this.zoomY) &&
  21874. clickedInside &&
  21875. !panKey
  21876. ) {
  21877. if (!selectionMarker) {
  21878. this.selectionMarker = selectionMarker =
  21879. chart.renderer.rect(
  21880. plotLeft,
  21881. plotTop,
  21882. zoomHor ? 1 : plotWidth,
  21883. zoomVert ? 1 : plotHeight,
  21884. 0
  21885. )
  21886. .attr({
  21887. 'class': 'highcharts-selection-marker',
  21888. 'zIndex': 7
  21889. })
  21890. .add();
  21891. if (!chart.styledMode) {
  21892. selectionMarker.attr({
  21893. fill: (
  21894. chartOptions.selectionMarkerFill ||
  21895. color('#335cad')
  21896. .setOpacity(0.25).get()
  21897. )
  21898. });
  21899. }
  21900. }
  21901. }
  21902. // adjust the width of the selection marker
  21903. if (selectionMarker && zoomHor) {
  21904. size = chartX - mouseDownX;
  21905. selectionMarker.attr({
  21906. width: Math.abs(size),
  21907. x: (size > 0 ? 0 : size) + mouseDownX
  21908. });
  21909. }
  21910. // adjust the height of the selection marker
  21911. if (selectionMarker && zoomVert) {
  21912. size = chartY - mouseDownY;
  21913. selectionMarker.attr({
  21914. height: Math.abs(size),
  21915. y: (size > 0 ? 0 : size) + mouseDownY
  21916. });
  21917. }
  21918. // panning
  21919. if (clickedInside && !selectionMarker && chartOptions.panning) {
  21920. chart.pan(e, chartOptions.panning);
  21921. }
  21922. }
  21923. },
  21924. /**
  21925. * On mouse up or touch end across the entire document, drop the selection.
  21926. *
  21927. * @private
  21928. * @function Highcharts.Pointer#drop
  21929. *
  21930. * @param {global.Event} e
  21931. */
  21932. drop: function (e) {
  21933. var pointer = this,
  21934. chart = this.chart,
  21935. hasPinched = this.hasPinched;
  21936. if (this.selectionMarker) {
  21937. var selectionData = {
  21938. originalEvent: e, // #4890
  21939. xAxis: [],
  21940. yAxis: []
  21941. },
  21942. selectionBox = this.selectionMarker,
  21943. selectionLeft = selectionBox.attr ?
  21944. selectionBox.attr('x') :
  21945. selectionBox.x,
  21946. selectionTop = selectionBox.attr ?
  21947. selectionBox.attr('y') :
  21948. selectionBox.y,
  21949. selectionWidth = selectionBox.attr ?
  21950. selectionBox.attr('width') :
  21951. selectionBox.width,
  21952. selectionHeight = selectionBox.attr ?
  21953. selectionBox.attr('height') :
  21954. selectionBox.height,
  21955. runZoom;
  21956. // a selection has been made
  21957. if (this.hasDragged || hasPinched) {
  21958. // record each axis' min and max
  21959. chart.axes.forEach(function (axis) {
  21960. if (
  21961. axis.zoomEnabled &&
  21962. defined(axis.min) &&
  21963. (
  21964. hasPinched ||
  21965. pointer[{
  21966. xAxis: 'zoomX',
  21967. yAxis: 'zoomY'
  21968. }[axis.coll]]
  21969. )
  21970. ) { // #859, #3569
  21971. var horiz = axis.horiz,
  21972. minPixelPadding = e.type === 'touchend' ?
  21973. axis.minPixelPadding :
  21974. 0, // #1207, #3075
  21975. selectionMin = axis.toValue(
  21976. (horiz ? selectionLeft : selectionTop) +
  21977. minPixelPadding
  21978. ),
  21979. selectionMax = axis.toValue(
  21980. (
  21981. horiz ?
  21982. selectionLeft + selectionWidth :
  21983. selectionTop + selectionHeight
  21984. ) - minPixelPadding
  21985. );
  21986. selectionData[axis.coll].push({
  21987. axis: axis,
  21988. // Min/max for reversed axes
  21989. min: Math.min(selectionMin, selectionMax),
  21990. max: Math.max(selectionMin, selectionMax)
  21991. });
  21992. runZoom = true;
  21993. }
  21994. });
  21995. if (runZoom) {
  21996. fireEvent(
  21997. chart,
  21998. 'selection',
  21999. selectionData,
  22000. function (args) {
  22001. chart.zoom(
  22002. extend(
  22003. args,
  22004. hasPinched ? { animation: false } : null
  22005. )
  22006. );
  22007. }
  22008. );
  22009. }
  22010. }
  22011. if (isNumber(chart.index)) {
  22012. this.selectionMarker = this.selectionMarker.destroy();
  22013. }
  22014. // Reset scaling preview
  22015. if (hasPinched) {
  22016. this.scaleGroups();
  22017. }
  22018. }
  22019. // Reset all. Check isNumber because it may be destroyed on mouse up
  22020. // (#877)
  22021. if (chart && isNumber(chart.index)) {
  22022. css(chart.container, { cursor: chart._cursor });
  22023. chart.cancelClick = this.hasDragged > 10; // #370
  22024. chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
  22025. this.pinchDown = [];
  22026. }
  22027. },
  22028. /**
  22029. * @private
  22030. * @function Highcharts.Pointer#onContainerMouseDown
  22031. *
  22032. * @param {global.Event} e
  22033. */
  22034. onContainerMouseDown: function (e) {
  22035. // Normalize before the 'if' for the legacy IE (#7850)
  22036. e = this.normalize(e);
  22037. if (e.button !== 2) {
  22038. this.zoomOption(e);
  22039. // issue #295, dragging not always working in Firefox
  22040. if (e.preventDefault) {
  22041. e.preventDefault();
  22042. }
  22043. this.dragStart(e);
  22044. }
  22045. },
  22046. /**
  22047. * @private
  22048. * @function Highcharts.Pointer#onDocumentMouseUp
  22049. *
  22050. * @param {global.Event} e
  22051. */
  22052. onDocumentMouseUp: function (e) {
  22053. if (charts[H.hoverChartIndex]) {
  22054. charts[H.hoverChartIndex].pointer.drop(e);
  22055. }
  22056. },
  22057. /**
  22058. * Special handler for mouse move that will hide the tooltip when the mouse
  22059. * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
  22060. * always fire.
  22061. *
  22062. * @private
  22063. * @function Highcharts.Pointer#onDocumentMouseMove
  22064. *
  22065. * @param {Highcharts.PointerEventObject} e
  22066. */
  22067. onDocumentMouseMove: function (e) {
  22068. var chart = this.chart,
  22069. chartPosition = this.chartPosition;
  22070. e = this.normalize(e, chartPosition);
  22071. // If we're outside, hide the tooltip
  22072. if (
  22073. chartPosition &&
  22074. !this.inClass(e.target, 'highcharts-tracker') &&
  22075. !chart.isInsidePlot(
  22076. e.chartX - chart.plotLeft,
  22077. e.chartY - chart.plotTop
  22078. )
  22079. ) {
  22080. this.reset();
  22081. }
  22082. },
  22083. /**
  22084. * When mouse leaves the container, hide the tooltip.
  22085. *
  22086. * @private
  22087. * @function Highcharts.Pointer#onContainerMouseLeave
  22088. *
  22089. * @param {global.Event} e
  22090. */
  22091. onContainerMouseLeave: function (e) {
  22092. var chart = charts[H.hoverChartIndex];
  22093. // #4886, MS Touch end fires mouseleave but with no related target
  22094. if (chart && (e.relatedTarget || e.toElement)) {
  22095. chart.pointer.reset();
  22096. // Also reset the chart position, used in #149 fix
  22097. chart.pointer.chartPosition = null;
  22098. }
  22099. },
  22100. /**
  22101. * The mousemove, touchmove and touchstart event handler
  22102. *
  22103. * @private
  22104. * @function Highcharts.Pointer#onContainerMouseMove
  22105. *
  22106. * @param {Highcharts.PointerEventObject} e
  22107. */
  22108. onContainerMouseMove: function (e) {
  22109. var chart = this.chart;
  22110. if (
  22111. !defined(H.hoverChartIndex) ||
  22112. !charts[H.hoverChartIndex] ||
  22113. !charts[H.hoverChartIndex].mouseIsDown
  22114. ) {
  22115. H.hoverChartIndex = chart.index;
  22116. }
  22117. e = this.normalize(e);
  22118. e.returnValue = false; // #2251, #3224
  22119. if (chart.mouseIsDown === 'mousedown') {
  22120. this.drag(e);
  22121. }
  22122. // Show the tooltip and run mouse over events (#977)
  22123. if (
  22124. (
  22125. this.inClass(e.target, 'highcharts-tracker') ||
  22126. chart.isInsidePlot(
  22127. e.chartX - chart.plotLeft,
  22128. e.chartY - chart.plotTop
  22129. )
  22130. ) &&
  22131. !chart.openMenu
  22132. ) {
  22133. this.runPointActions(e);
  22134. }
  22135. },
  22136. /**
  22137. * Utility to detect whether an element has, or has a parent with, a
  22138. * specificclass name. Used on detection of tracker objects and on deciding
  22139. * whether hovering the tooltip should cause the active series to mouse out.
  22140. *
  22141. * @function Highcharts.Pointer#inClass
  22142. *
  22143. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  22144. * The element to investigate.
  22145. *
  22146. * @param {string} className
  22147. * The class name to look for.
  22148. *
  22149. * @return {boolean}
  22150. * True if either the element or one of its parents has the given
  22151. * class name.
  22152. */
  22153. inClass: function (element, className) {
  22154. var elemClassName;
  22155. while (element) {
  22156. elemClassName = attr(element, 'class');
  22157. if (elemClassName) {
  22158. if (elemClassName.indexOf(className) !== -1) {
  22159. return true;
  22160. }
  22161. if (elemClassName.indexOf('highcharts-container') !== -1) {
  22162. return false;
  22163. }
  22164. }
  22165. element = element.parentNode;
  22166. }
  22167. },
  22168. /**
  22169. * @private
  22170. * @function Highcharts.Pointer#onTrackerMouseOut
  22171. *
  22172. * @param {global.Event} e
  22173. */
  22174. onTrackerMouseOut: function (e) {
  22175. var series = this.chart.hoverSeries,
  22176. relatedTarget = e.relatedTarget || e.toElement;
  22177. this.isDirectTouch = false;
  22178. if (
  22179. series &&
  22180. relatedTarget &&
  22181. !series.stickyTracking &&
  22182. !this.inClass(relatedTarget, 'highcharts-tooltip') &&
  22183. (
  22184. !this.inClass(
  22185. relatedTarget,
  22186. 'highcharts-series-' + series.index
  22187. ) || // #2499, #4465
  22188. !this.inClass(relatedTarget, 'highcharts-tracker') // #5553
  22189. )
  22190. ) {
  22191. series.onMouseOut();
  22192. }
  22193. },
  22194. /**
  22195. * @private
  22196. * @function Highcharts.Pointer#onContainerClick
  22197. *
  22198. * @param {Highcharts.PointerEventObject} e
  22199. */
  22200. onContainerClick: function (e) {
  22201. var chart = this.chart,
  22202. hoverPoint = chart.hoverPoint,
  22203. plotLeft = chart.plotLeft,
  22204. plotTop = chart.plotTop;
  22205. e = this.normalize(e);
  22206. if (!chart.cancelClick) {
  22207. // On tracker click, fire the series and point events. #783, #1583
  22208. if (hoverPoint && this.inClass(e.target, 'highcharts-tracker')) {
  22209. // the series click event
  22210. fireEvent(hoverPoint.series, 'click', extend(e, {
  22211. point: hoverPoint
  22212. }));
  22213. // the point click event
  22214. if (chart.hoverPoint) { // it may be destroyed (#1844)
  22215. hoverPoint.firePointEvent('click', e);
  22216. }
  22217. // When clicking outside a tracker, fire a chart event
  22218. } else {
  22219. extend(e, this.getCoordinates(e));
  22220. // fire a click event in the chart
  22221. if (
  22222. chart.isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)
  22223. ) {
  22224. fireEvent(chart, 'click', e);
  22225. }
  22226. }
  22227. }
  22228. },
  22229. /**
  22230. * Set the JS DOM events on the container and document. This method should
  22231. * contain a one-to-one assignment between methods and their handlers. Any
  22232. * advanced logic should be moved to the handler reflecting the event's
  22233. * name.
  22234. *
  22235. * @private
  22236. * @function Highcharts.Pointer#setDOMEvents
  22237. */
  22238. setDOMEvents: function () {
  22239. var pointer = this,
  22240. container = pointer.chart.container,
  22241. ownerDoc = container.ownerDocument;
  22242. container.onmousedown = function (e) {
  22243. pointer.onContainerMouseDown(e);
  22244. };
  22245. container.onmousemove = function (e) {
  22246. pointer.onContainerMouseMove(e);
  22247. };
  22248. container.onclick = function (e) {
  22249. pointer.onContainerClick(e);
  22250. };
  22251. this.unbindContainerMouseLeave = addEvent(
  22252. container,
  22253. 'mouseleave',
  22254. pointer.onContainerMouseLeave
  22255. );
  22256. if (!H.unbindDocumentMouseUp) {
  22257. H.unbindDocumentMouseUp = addEvent(
  22258. ownerDoc,
  22259. 'mouseup',
  22260. pointer.onDocumentMouseUp
  22261. );
  22262. }
  22263. if (H.hasTouch) {
  22264. container.ontouchstart = function (e) {
  22265. pointer.onContainerTouchStart(e);
  22266. };
  22267. container.ontouchmove = function (e) {
  22268. pointer.onContainerTouchMove(e);
  22269. };
  22270. if (!H.unbindDocumentTouchEnd) {
  22271. H.unbindDocumentTouchEnd = addEvent(
  22272. ownerDoc,
  22273. 'touchend',
  22274. pointer.onDocumentTouchEnd
  22275. );
  22276. }
  22277. }
  22278. },
  22279. /**
  22280. * Destroys the Pointer object and disconnects DOM events.
  22281. *
  22282. * @function Highcharts.Pointer#destroy
  22283. */
  22284. destroy: function () {
  22285. var pointer = this;
  22286. if (pointer.unDocMouseMove) {
  22287. pointer.unDocMouseMove();
  22288. }
  22289. this.unbindContainerMouseLeave();
  22290. if (!H.chartCount) {
  22291. if (H.unbindDocumentMouseUp) {
  22292. H.unbindDocumentMouseUp = H.unbindDocumentMouseUp();
  22293. }
  22294. if (H.unbindDocumentTouchEnd) {
  22295. H.unbindDocumentTouchEnd = H.unbindDocumentTouchEnd();
  22296. }
  22297. }
  22298. // memory and CPU leak
  22299. clearInterval(pointer.tooltipTimeout);
  22300. H.objectEach(pointer, function (val, prop) {
  22301. pointer[prop] = null;
  22302. });
  22303. }
  22304. };
  22305. }(Highcharts));
  22306. (function (H) {
  22307. /**
  22308. * (c) 2010-2019 Torstein Honsi
  22309. *
  22310. * License: www.highcharts.com/license
  22311. */
  22312. var charts = H.charts,
  22313. extend = H.extend,
  22314. noop = H.noop,
  22315. pick = H.pick,
  22316. Pointer = H.Pointer;
  22317. // Support for touch devices
  22318. extend(Pointer.prototype, /** @lends Pointer.prototype */ {
  22319. /**
  22320. * Run translation operations
  22321. *
  22322. * @private
  22323. * @function Highcharts.Pointer#pinchTranslate
  22324. *
  22325. * @param {Array<*>} pinchDown
  22326. *
  22327. * @param {Array<*>} touches
  22328. *
  22329. * @param {*} transform
  22330. *
  22331. * @param {*} selectionMarker
  22332. *
  22333. * @param {*} clip
  22334. *
  22335. * @param {*} lastValidTouch
  22336. */
  22337. pinchTranslate: function (
  22338. pinchDown,
  22339. touches,
  22340. transform,
  22341. selectionMarker,
  22342. clip,
  22343. lastValidTouch
  22344. ) {
  22345. if (this.zoomHor) {
  22346. this.pinchTranslateDirection(
  22347. true,
  22348. pinchDown,
  22349. touches,
  22350. transform,
  22351. selectionMarker,
  22352. clip,
  22353. lastValidTouch
  22354. );
  22355. }
  22356. if (this.zoomVert) {
  22357. this.pinchTranslateDirection(
  22358. false,
  22359. pinchDown,
  22360. touches,
  22361. transform,
  22362. selectionMarker,
  22363. clip,
  22364. lastValidTouch
  22365. );
  22366. }
  22367. },
  22368. /**
  22369. * Run translation operations for each direction (horizontal and vertical)
  22370. * independently.
  22371. *
  22372. * @private
  22373. * @function Highcharts.Pointer#pinchTranslateDirection
  22374. *
  22375. * @param {boolean} horiz
  22376. *
  22377. * @param {Array<*>} pinchDown
  22378. *
  22379. * @param {Array<*>} touches
  22380. *
  22381. * @param {*} transform
  22382. *
  22383. * @param {*} selectionMarker
  22384. *
  22385. * @param {*} clip
  22386. *
  22387. * @param {*} lastValidTouch
  22388. *
  22389. * @param {number|undefined} [forcedScale=1]
  22390. */
  22391. pinchTranslateDirection: function (
  22392. horiz,
  22393. pinchDown,
  22394. touches,
  22395. transform,
  22396. selectionMarker,
  22397. clip,
  22398. lastValidTouch,
  22399. forcedScale
  22400. ) {
  22401. var chart = this.chart,
  22402. xy = horiz ? 'x' : 'y',
  22403. XY = horiz ? 'X' : 'Y',
  22404. sChartXY = 'chart' + XY,
  22405. wh = horiz ? 'width' : 'height',
  22406. plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')],
  22407. selectionWH,
  22408. selectionXY,
  22409. clipXY,
  22410. scale = forcedScale || 1,
  22411. inverted = chart.inverted,
  22412. bounds = chart.bounds[horiz ? 'h' : 'v'],
  22413. singleTouch = pinchDown.length === 1,
  22414. touch0Start = pinchDown[0][sChartXY],
  22415. touch0Now = touches[0][sChartXY],
  22416. touch1Start = !singleTouch && pinchDown[1][sChartXY],
  22417. touch1Now = !singleTouch && touches[1][sChartXY],
  22418. outOfBounds,
  22419. transformScale,
  22420. scaleKey,
  22421. setScale = function () {
  22422. // Don't zoom if fingers are too close on this axis
  22423. if (!singleTouch && Math.abs(touch0Start - touch1Start) > 20) {
  22424. scale = forcedScale ||
  22425. Math.abs(touch0Now - touch1Now) /
  22426. Math.abs(touch0Start - touch1Start);
  22427. }
  22428. clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
  22429. selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] /
  22430. scale;
  22431. };
  22432. // Set the scale, first pass
  22433. setScale();
  22434. // The clip position (x or y) is altered if out of bounds, the selection
  22435. // position is not
  22436. selectionXY = clipXY;
  22437. // Out of bounds
  22438. if (selectionXY < bounds.min) {
  22439. selectionXY = bounds.min;
  22440. outOfBounds = true;
  22441. } else if (selectionXY + selectionWH > bounds.max) {
  22442. selectionXY = bounds.max - selectionWH;
  22443. outOfBounds = true;
  22444. }
  22445. // Is the chart dragged off its bounds, determined by dataMin and
  22446. // dataMax?
  22447. if (outOfBounds) {
  22448. // Modify the touchNow position in order to create an elastic drag
  22449. // movement. This indicates to the user that the chart is responsive
  22450. // but can't be dragged further.
  22451. touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
  22452. if (!singleTouch) {
  22453. touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
  22454. }
  22455. // Set the scale, second pass to adapt to the modified touchNow
  22456. // positions
  22457. setScale();
  22458. } else {
  22459. lastValidTouch[xy] = [touch0Now, touch1Now];
  22460. }
  22461. // Set geometry for clipping, selection and transformation
  22462. if (!inverted) {
  22463. clip[xy] = clipXY - plotLeftTop;
  22464. clip[wh] = selectionWH;
  22465. }
  22466. scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
  22467. transformScale = inverted ? 1 / scale : scale;
  22468. selectionMarker[wh] = selectionWH;
  22469. selectionMarker[xy] = selectionXY;
  22470. transform[scaleKey] = scale;
  22471. transform['translate' + XY] = (transformScale * plotLeftTop) +
  22472. (touch0Now - (transformScale * touch0Start));
  22473. },
  22474. /**
  22475. * Handle touch events with two touches
  22476. *
  22477. * @private
  22478. * @function Highcharts.Pointer#pinch
  22479. *
  22480. * @param {Highcharts.PointerEvent} e
  22481. */
  22482. pinch: function (e) {
  22483. var self = this,
  22484. chart = self.chart,
  22485. pinchDown = self.pinchDown,
  22486. touches = e.touches,
  22487. touchesLength = touches.length,
  22488. lastValidTouch = self.lastValidTouch,
  22489. hasZoom = self.hasZoom,
  22490. selectionMarker = self.selectionMarker,
  22491. transform = {},
  22492. fireClickEvent = touchesLength === 1 && (
  22493. (
  22494. self.inClass(e.target, 'highcharts-tracker') &&
  22495. chart.runTrackerClick
  22496. ) ||
  22497. self.runChartClick
  22498. ),
  22499. clip = {};
  22500. // Don't initiate panning until the user has pinched. This prevents us
  22501. // from blocking page scrolling as users scroll down a long page
  22502. // (#4210).
  22503. if (touchesLength > 1) {
  22504. self.initiated = true;
  22505. }
  22506. // On touch devices, only proceed to trigger click if a handler is
  22507. // defined
  22508. if (hasZoom && self.initiated && !fireClickEvent) {
  22509. e.preventDefault();
  22510. }
  22511. // Normalize each touch
  22512. [].map.call(touches, function (e) {
  22513. return self.normalize(e);
  22514. });
  22515. // Register the touch start position
  22516. if (e.type === 'touchstart') {
  22517. [].forEach.call(touches, function (e, i) {
  22518. pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
  22519. });
  22520. lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
  22521. pinchDown[1].chartX];
  22522. lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
  22523. pinchDown[1].chartY];
  22524. // Identify the data bounds in pixels
  22525. chart.axes.forEach(function (axis) {
  22526. if (axis.zoomEnabled) {
  22527. var bounds = chart.bounds[axis.horiz ? 'h' : 'v'],
  22528. minPixelPadding = axis.minPixelPadding,
  22529. min = axis.toPixels(
  22530. pick(axis.options.min, axis.dataMin)
  22531. ),
  22532. max = axis.toPixels(
  22533. pick(axis.options.max, axis.dataMax)
  22534. ),
  22535. absMin = Math.min(min, max),
  22536. absMax = Math.max(min, max);
  22537. // Store the bounds for use in the touchmove handler
  22538. bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
  22539. bounds.max = Math.max(
  22540. axis.pos + axis.len,
  22541. absMax + minPixelPadding
  22542. );
  22543. }
  22544. });
  22545. self.res = true; // reset on next move
  22546. // Optionally move the tooltip on touchmove
  22547. } else if (self.followTouchMove && touchesLength === 1) {
  22548. this.runPointActions(self.normalize(e));
  22549. // Event type is touchmove, handle panning and pinching
  22550. } else if (pinchDown.length) { // can be 0 when releasing, if touchend
  22551. // fires first
  22552. // Set the marker
  22553. if (!selectionMarker) {
  22554. self.selectionMarker = selectionMarker = extend({
  22555. destroy: noop,
  22556. touch: true
  22557. }, chart.plotBox);
  22558. }
  22559. self.pinchTranslate(
  22560. pinchDown,
  22561. touches,
  22562. transform,
  22563. selectionMarker,
  22564. clip,
  22565. lastValidTouch
  22566. );
  22567. self.hasPinched = hasZoom;
  22568. // Scale and translate the groups to provide visual feedback during
  22569. // pinching
  22570. self.scaleGroups(transform, clip);
  22571. if (self.res) {
  22572. self.res = false;
  22573. this.reset(false, 0);
  22574. }
  22575. }
  22576. },
  22577. /**
  22578. * General touch handler shared by touchstart and touchmove.
  22579. *
  22580. * @private
  22581. * @function Highcharts.Pointer#touch
  22582. *
  22583. * @param {Highcharts.PointerEvent} e
  22584. *
  22585. * @param {boolean} start
  22586. */
  22587. touch: function (e, start) {
  22588. var chart = this.chart,
  22589. hasMoved,
  22590. pinchDown,
  22591. isInside;
  22592. if (chart.index !== H.hoverChartIndex) {
  22593. this.onContainerMouseLeave({ relatedTarget: true });
  22594. }
  22595. H.hoverChartIndex = chart.index;
  22596. if (e.touches.length === 1) {
  22597. e = this.normalize(e);
  22598. isInside = chart.isInsidePlot(
  22599. e.chartX - chart.plotLeft,
  22600. e.chartY - chart.plotTop
  22601. );
  22602. if (isInside && !chart.openMenu) {
  22603. // Run mouse events and display tooltip etc
  22604. if (start) {
  22605. this.runPointActions(e);
  22606. }
  22607. // Android fires touchmove events after the touchstart even if
  22608. // the finger hasn't moved, or moved only a pixel or two. In iOS
  22609. // however, the touchmove doesn't fire unless the finger moves
  22610. // more than ~4px. So we emulate this behaviour in Android by
  22611. // checking how much it moved, and cancelling on small
  22612. // distances. #3450.
  22613. if (e.type === 'touchmove') {
  22614. pinchDown = this.pinchDown;
  22615. hasMoved = pinchDown[0] ? Math.sqrt( // #5266
  22616. Math.pow(pinchDown[0].chartX - e.chartX, 2) +
  22617. Math.pow(pinchDown[0].chartY - e.chartY, 2)
  22618. ) >= 4 : false;
  22619. }
  22620. if (pick(hasMoved, true)) {
  22621. this.pinch(e);
  22622. }
  22623. } else if (start) {
  22624. // Hide the tooltip on touching outside the plot area (#1203)
  22625. this.reset();
  22626. }
  22627. } else if (e.touches.length === 2) {
  22628. this.pinch(e);
  22629. }
  22630. },
  22631. /**
  22632. * @private
  22633. * @function Highcharts.Pointer#onContainerTouchStart
  22634. *
  22635. * @param {Highcharts.PointerEvent} e
  22636. */
  22637. onContainerTouchStart: function (e) {
  22638. this.zoomOption(e);
  22639. this.touch(e, true);
  22640. },
  22641. /**
  22642. * @private
  22643. * @function Highcharts.Pointer#onContainerTouchMove
  22644. *
  22645. * @param {Highcharts.PointerEvent} e
  22646. */
  22647. onContainerTouchMove: function (e) {
  22648. this.touch(e);
  22649. },
  22650. /**
  22651. * @private
  22652. * @function Highcharts.Pointer#onDocumentTouchEnd
  22653. *
  22654. * @param {Highcharts.PointerEvent} e
  22655. */
  22656. onDocumentTouchEnd: function (e) {
  22657. if (charts[H.hoverChartIndex]) {
  22658. charts[H.hoverChartIndex].pointer.drop(e);
  22659. }
  22660. }
  22661. });
  22662. }(Highcharts));
  22663. (function (H) {
  22664. /**
  22665. * (c) 2010-2019 Torstein Honsi
  22666. *
  22667. * License: www.highcharts.com/license
  22668. */
  22669. var addEvent = H.addEvent,
  22670. charts = H.charts,
  22671. css = H.css,
  22672. doc = H.doc,
  22673. extend = H.extend,
  22674. hasTouch = H.hasTouch,
  22675. noop = H.noop,
  22676. Pointer = H.Pointer,
  22677. removeEvent = H.removeEvent,
  22678. win = H.win,
  22679. wrap = H.wrap;
  22680. if (!hasTouch && (win.PointerEvent || win.MSPointerEvent)) {
  22681. // The touches object keeps track of the points being touched at all times
  22682. var touches = {},
  22683. hasPointerEvent = !!win.PointerEvent,
  22684. getWebkitTouches = function () {
  22685. var fake = [];
  22686. fake.item = function (i) {
  22687. return this[i];
  22688. };
  22689. H.objectEach(touches, function (touch) {
  22690. fake.push({
  22691. pageX: touch.pageX,
  22692. pageY: touch.pageY,
  22693. target: touch.target
  22694. });
  22695. });
  22696. return fake;
  22697. },
  22698. translateMSPointer = function (e, method, wktype, func) {
  22699. var p;
  22700. if (
  22701. (
  22702. e.pointerType === 'touch' ||
  22703. e.pointerType === e.MSPOINTER_TYPE_TOUCH
  22704. ) && charts[H.hoverChartIndex]
  22705. ) {
  22706. func(e);
  22707. p = charts[H.hoverChartIndex].pointer;
  22708. p[method]({
  22709. type: wktype,
  22710. target: e.currentTarget,
  22711. preventDefault: noop,
  22712. touches: getWebkitTouches()
  22713. });
  22714. }
  22715. };
  22716. // Extend the Pointer prototype with methods for each event handler and more
  22717. extend(Pointer.prototype, /** @lends Pointer.prototype */ {
  22718. /**
  22719. * @private
  22720. * @function Highcharts.Pointer#onContainerPointerDown
  22721. *
  22722. * @param {Highcharts.PointerEventObject} e
  22723. */
  22724. onContainerPointerDown: function (e) {
  22725. translateMSPointer(
  22726. e,
  22727. 'onContainerTouchStart',
  22728. 'touchstart',
  22729. function (e) {
  22730. touches[e.pointerId] = {
  22731. pageX: e.pageX,
  22732. pageY: e.pageY,
  22733. target: e.currentTarget
  22734. };
  22735. }
  22736. );
  22737. },
  22738. /**
  22739. * @private
  22740. * @function Highcharts.Pointer#onContainerPointerMove
  22741. *
  22742. * @param {Highcharts.PointerEventObject} e
  22743. */
  22744. onContainerPointerMove: function (e) {
  22745. translateMSPointer(
  22746. e,
  22747. 'onContainerTouchMove',
  22748. 'touchmove',
  22749. function (e) {
  22750. touches[e.pointerId] = { pageX: e.pageX, pageY: e.pageY };
  22751. if (!touches[e.pointerId].target) {
  22752. touches[e.pointerId].target = e.currentTarget;
  22753. }
  22754. }
  22755. );
  22756. },
  22757. /**
  22758. * @private
  22759. * @function Highcharts.Pointer#onDocumentPointerUp
  22760. *
  22761. * @param {Highcharts.PointerEventObject} e
  22762. */
  22763. onDocumentPointerUp: function (e) {
  22764. translateMSPointer(
  22765. e,
  22766. 'onDocumentTouchEnd',
  22767. 'touchend',
  22768. function (e) {
  22769. delete touches[e.pointerId];
  22770. }
  22771. );
  22772. },
  22773. /**
  22774. * Add or remove the MS Pointer specific events
  22775. *
  22776. * @private
  22777. * @function Highcharts.Pointer#batchMSEvents
  22778. *
  22779. * @param {Function} fn
  22780. */
  22781. batchMSEvents: function (fn) {
  22782. fn(
  22783. this.chart.container,
  22784. hasPointerEvent ? 'pointerdown' : 'MSPointerDown',
  22785. this.onContainerPointerDown
  22786. );
  22787. fn(
  22788. this.chart.container,
  22789. hasPointerEvent ? 'pointermove' : 'MSPointerMove',
  22790. this.onContainerPointerMove
  22791. );
  22792. fn(
  22793. doc,
  22794. hasPointerEvent ? 'pointerup' : 'MSPointerUp',
  22795. this.onDocumentPointerUp
  22796. );
  22797. }
  22798. });
  22799. // Disable default IE actions for pinch and such on chart element
  22800. wrap(Pointer.prototype, 'init', function (proceed, chart, options) {
  22801. proceed.call(this, chart, options);
  22802. if (this.hasZoom) { // #4014
  22803. css(chart.container, {
  22804. '-ms-touch-action': 'none',
  22805. 'touch-action': 'none'
  22806. });
  22807. }
  22808. });
  22809. // Add IE specific touch events to chart
  22810. wrap(Pointer.prototype, 'setDOMEvents', function (proceed) {
  22811. proceed.apply(this);
  22812. if (this.hasZoom || this.followTouchMove) {
  22813. this.batchMSEvents(addEvent);
  22814. }
  22815. });
  22816. // Destroy MS events also
  22817. wrap(Pointer.prototype, 'destroy', function (proceed) {
  22818. this.batchMSEvents(removeEvent);
  22819. proceed.call(this);
  22820. });
  22821. }
  22822. }(Highcharts));
  22823. (function (Highcharts) {
  22824. /**
  22825. * (c) 2010-2019 Torstein Honsi
  22826. *
  22827. * License: www.highcharts.com/license
  22828. */
  22829. var H = Highcharts,
  22830. addEvent = H.addEvent,
  22831. css = H.css,
  22832. discardElement = H.discardElement,
  22833. defined = H.defined,
  22834. fireEvent = H.fireEvent,
  22835. isFirefox = H.isFirefox,
  22836. marginNames = H.marginNames,
  22837. merge = H.merge,
  22838. pick = H.pick,
  22839. setAnimation = H.setAnimation,
  22840. stableSort = H.stableSort,
  22841. win = H.win,
  22842. wrap = H.wrap;
  22843. /**
  22844. * The overview of the chart's series. The legend object is instanciated
  22845. * internally in the chart constructor, and is available from the `chart.legend`
  22846. * property. Each chart has only one legend.
  22847. *
  22848. * @class
  22849. * @name Highcharts.Legend
  22850. *
  22851. * @param {Highcharts.Chart} chart
  22852. * The chart instance.
  22853. *
  22854. * @param {Highcharts.LegendOptions} options
  22855. * Legend options.
  22856. */
  22857. Highcharts.Legend = function (chart, options) {
  22858. this.init(chart, options);
  22859. };
  22860. Highcharts.Legend.prototype = {
  22861. /**
  22862. * Initialize the legend.
  22863. *
  22864. * @private
  22865. * @function Highcharts.Legend#init
  22866. *
  22867. * @param {Highcharts.Chart} chart
  22868. * The chart instance.
  22869. *
  22870. * @param {Highcharts.LegendOptions} options
  22871. * Legend options.
  22872. */
  22873. init: function (chart, options) {
  22874. /**
  22875. * Chart of this legend.
  22876. *
  22877. * @readonly
  22878. * @name Highcharts.Legend#chart
  22879. * @type {Highcharts.Chart}
  22880. */
  22881. this.chart = chart;
  22882. this.setOptions(options);
  22883. if (options.enabled) {
  22884. // Render it
  22885. this.render();
  22886. // move checkboxes
  22887. addEvent(this.chart, 'endResize', function () {
  22888. this.legend.positionCheckboxes();
  22889. });
  22890. if (this.proximate) {
  22891. this.unchartrender = addEvent(
  22892. this.chart,
  22893. 'render',
  22894. function () {
  22895. this.legend.proximatePositions();
  22896. this.legend.positionItems();
  22897. }
  22898. );
  22899. } else if (this.unchartrender) {
  22900. this.unchartrender();
  22901. }
  22902. }
  22903. },
  22904. /**
  22905. * @private
  22906. * @function Highcharts.Legend#setOptions
  22907. *
  22908. * @param {Highcharts.LegendOptions} options
  22909. */
  22910. setOptions: function (options) {
  22911. var padding = pick(options.padding, 8);
  22912. /**
  22913. * Legend options.
  22914. *
  22915. * @readonly
  22916. * @name Highcharts.Legend#options
  22917. * @type {Highcharts.LegendOptions}
  22918. */
  22919. this.options = options;
  22920. if (!this.chart.styledMode) {
  22921. this.itemStyle = options.itemStyle;
  22922. this.itemHiddenStyle = merge(
  22923. this.itemStyle,
  22924. options.itemHiddenStyle
  22925. );
  22926. }
  22927. this.itemMarginTop = options.itemMarginTop || 0;
  22928. this.padding = padding;
  22929. this.initialItemY = padding - 5; // 5 is pixels above the text
  22930. this.symbolWidth = pick(options.symbolWidth, 16);
  22931. this.pages = [];
  22932. this.proximate = options.layout === 'proximate' && !this.chart.inverted;
  22933. },
  22934. /**
  22935. * Update the legend with new options. Equivalent to running `chart.update`
  22936. * with a legend configuration option.
  22937. *
  22938. * @sample highcharts/legend/legend-update/
  22939. * Legend update
  22940. *
  22941. * @function Highcharts.Legend#update
  22942. *
  22943. * @param {Highcharts.LegendOptions} options
  22944. * Legend options.
  22945. *
  22946. * @param {boolean} [redraw=true]
  22947. * Whether to redraw the chart after the axis is altered. If doing
  22948. * more operations on the chart, it is a good idea to set redraw to
  22949. * false and call {@link Chart#redraw} after.
  22950. * Whether to redraw the chart.
  22951. *
  22952. * @fires Highcharts.Legends#event:afterUpdate
  22953. */
  22954. update: function (options, redraw) {
  22955. var chart = this.chart;
  22956. this.setOptions(merge(true, this.options, options));
  22957. this.destroy();
  22958. chart.isDirtyLegend = chart.isDirtyBox = true;
  22959. if (pick(redraw, true)) {
  22960. chart.redraw();
  22961. }
  22962. fireEvent(this, 'afterUpdate');
  22963. },
  22964. /**
  22965. * Set the colors for the legend item.
  22966. *
  22967. * @private
  22968. * @function Highcharts.Legend#colorizeItem
  22969. *
  22970. * @param {Highcharts.Point|Highcharts.Series} item
  22971. * A Series or Point instance
  22972. *
  22973. * @param {boolean} [visible=false]
  22974. * Dimmed or colored
  22975. *
  22976. * @todo
  22977. * Make events official: Fires the event `afterColorizeItem`.
  22978. */
  22979. colorizeItem: function (item, visible) {
  22980. item.legendGroup[visible ? 'removeClass' : 'addClass'](
  22981. 'highcharts-legend-item-hidden'
  22982. );
  22983. if (!this.chart.styledMode) {
  22984. var legend = this,
  22985. options = legend.options,
  22986. legendItem = item.legendItem,
  22987. legendLine = item.legendLine,
  22988. legendSymbol = item.legendSymbol,
  22989. hiddenColor = legend.itemHiddenStyle.color,
  22990. textColor = visible ? options.itemStyle.color : hiddenColor,
  22991. symbolColor = visible ?
  22992. (item.color || hiddenColor) :
  22993. hiddenColor,
  22994. markerOptions = item.options && item.options.marker,
  22995. symbolAttr = { fill: symbolColor };
  22996. if (legendItem) {
  22997. legendItem.css({
  22998. fill: textColor,
  22999. color: textColor // #1553, oldIE
  23000. });
  23001. }
  23002. if (legendLine) {
  23003. legendLine.attr({ stroke: symbolColor });
  23004. }
  23005. if (legendSymbol) {
  23006. // Apply marker options
  23007. if (markerOptions && legendSymbol.isMarker) { // #585
  23008. symbolAttr = item.pointAttribs();
  23009. if (!visible) {
  23010. // #6769
  23011. symbolAttr.stroke = symbolAttr.fill = hiddenColor;
  23012. }
  23013. }
  23014. legendSymbol.attr(symbolAttr);
  23015. }
  23016. }
  23017. fireEvent(this, 'afterColorizeItem', { item: item, visible: visible });
  23018. },
  23019. /**
  23020. * @private
  23021. * @function Highcharts.Legend#positionItems
  23022. */
  23023. positionItems: function () {
  23024. // Now that the legend width and height are established, put the items
  23025. // in the final position
  23026. this.allItems.forEach(this.positionItem, this);
  23027. if (!this.chart.isResizing) {
  23028. this.positionCheckboxes();
  23029. }
  23030. },
  23031. /**
  23032. * Position the legend item.
  23033. *
  23034. * @private
  23035. * @function Highcharts.Legend#positionItem
  23036. *
  23037. * @param {Highcharts.Point|Highcharts.Series} item
  23038. * The item to position
  23039. */
  23040. positionItem: function (item) {
  23041. var legend = this,
  23042. options = legend.options,
  23043. symbolPadding = options.symbolPadding,
  23044. ltr = !options.rtl,
  23045. legendItemPos = item._legendItemPos,
  23046. itemX = legendItemPos[0],
  23047. itemY = legendItemPos[1],
  23048. checkbox = item.checkbox,
  23049. legendGroup = item.legendGroup;
  23050. if (legendGroup && legendGroup.element) {
  23051. legendGroup[defined(legendGroup.translateY) ? 'animate' : 'attr']({
  23052. translateX: ltr ?
  23053. itemX :
  23054. legend.legendWidth - itemX - 2 * symbolPadding - 4,
  23055. translateY: itemY
  23056. });
  23057. }
  23058. if (checkbox) {
  23059. checkbox.x = itemX;
  23060. checkbox.y = itemY;
  23061. }
  23062. },
  23063. /**
  23064. * Destroy a single legend item, used internally on removing series items.
  23065. *
  23066. * @private
  23067. * @function Highcharts.Legend#destroyItem
  23068. *
  23069. * @param {Highcharts.Point|Highcharts.Series} item
  23070. * The item to remove
  23071. */
  23072. destroyItem: function (item) {
  23073. var checkbox = item.checkbox;
  23074. // destroy SVG elements
  23075. ['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'].forEach(
  23076. function (key) {
  23077. if (item[key]) {
  23078. item[key] = item[key].destroy();
  23079. }
  23080. }
  23081. );
  23082. if (checkbox) {
  23083. discardElement(item.checkbox);
  23084. }
  23085. },
  23086. /**
  23087. * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
  23088. * must be called after destruction.
  23089. *
  23090. * @private
  23091. * @function Highcharts.Legend#destroy
  23092. */
  23093. destroy: function () {
  23094. function destroyItems(key) {
  23095. if (this[key]) {
  23096. this[key] = this[key].destroy();
  23097. }
  23098. }
  23099. // Destroy items
  23100. this.getAllItems().forEach(function (item) {
  23101. ['legendItem', 'legendGroup'].forEach(destroyItems, item);
  23102. });
  23103. // Destroy legend elements
  23104. [
  23105. 'clipRect',
  23106. 'up',
  23107. 'down',
  23108. 'pager',
  23109. 'nav',
  23110. 'box',
  23111. 'title',
  23112. 'group'
  23113. ].forEach(destroyItems, this);
  23114. this.display = null; // Reset in .render on update.
  23115. },
  23116. /**
  23117. * Position the checkboxes after the width is determined.
  23118. *
  23119. * @private
  23120. * @function Highcharts.Legend#positionCheckboxes
  23121. */
  23122. positionCheckboxes: function () {
  23123. var alignAttr = this.group && this.group.alignAttr,
  23124. translateY,
  23125. clipHeight = this.clipHeight || this.legendHeight,
  23126. titleHeight = this.titleHeight;
  23127. if (alignAttr) {
  23128. translateY = alignAttr.translateY;
  23129. this.allItems.forEach(function (item) {
  23130. var checkbox = item.checkbox,
  23131. top;
  23132. if (checkbox) {
  23133. top = translateY + titleHeight + checkbox.y +
  23134. (this.scrollOffset || 0) + 3;
  23135. css(checkbox, {
  23136. left: (alignAttr.translateX + item.checkboxOffset +
  23137. checkbox.x - 20) + 'px',
  23138. top: top + 'px',
  23139. display: this.proximate || (
  23140. top > translateY - 6 &&
  23141. top < translateY + clipHeight - 6
  23142. ) ?
  23143. '' :
  23144. 'none'
  23145. });
  23146. }
  23147. }, this);
  23148. }
  23149. },
  23150. /**
  23151. * Render the legend title on top of the legend.
  23152. *
  23153. * @private
  23154. * @function Highcharts.Legend#renderTitle
  23155. */
  23156. renderTitle: function () {
  23157. var options = this.options,
  23158. padding = this.padding,
  23159. titleOptions = options.title,
  23160. titleHeight = 0,
  23161. bBox;
  23162. if (titleOptions.text) {
  23163. if (!this.title) {
  23164. /**
  23165. * SVG element of the legend title.
  23166. *
  23167. * @readonly
  23168. * @name Highcharts.Legend#title
  23169. * @type {Highcharts.SVGElement}
  23170. */
  23171. this.title = this.chart.renderer.label(
  23172. titleOptions.text,
  23173. padding - 3,
  23174. padding - 4,
  23175. null,
  23176. null,
  23177. null,
  23178. options.useHTML,
  23179. null,
  23180. 'legend-title'
  23181. )
  23182. .attr({ zIndex: 1 });
  23183. if (!this.chart.styledMode) {
  23184. this.title.css(titleOptions.style);
  23185. }
  23186. this.title.add(this.group);
  23187. }
  23188. // Set the max title width (#7253)
  23189. if (!titleOptions.width) {
  23190. this.title.css({
  23191. width: this.maxLegendWidth + 'px'
  23192. });
  23193. }
  23194. bBox = this.title.getBBox();
  23195. titleHeight = bBox.height;
  23196. this.offsetWidth = bBox.width; // #1717
  23197. this.contentGroup.attr({ translateY: titleHeight });
  23198. }
  23199. this.titleHeight = titleHeight;
  23200. },
  23201. /**
  23202. * Set the legend item text.
  23203. *
  23204. * @function Highcharts.Legend#setText
  23205. *
  23206. * @param {Highcharts.Point|Highcharts.Series} item
  23207. * The item for which to update the text in the legend.
  23208. */
  23209. setText: function (item) {
  23210. var options = this.options;
  23211. item.legendItem.attr({
  23212. text: options.labelFormat ?
  23213. H.format(options.labelFormat, item, this.chart.time) :
  23214. options.labelFormatter.call(item)
  23215. });
  23216. },
  23217. /**
  23218. * Render a single specific legend item. Called internally from the `render`
  23219. * function.
  23220. *
  23221. * @private
  23222. * @function Highcharts.Legend#renderItem
  23223. *
  23224. * @param {Highcharts.Point|Highcharts.Series} item
  23225. * The item to render.
  23226. */
  23227. renderItem: function (item) {
  23228. var legend = this,
  23229. chart = legend.chart,
  23230. renderer = chart.renderer,
  23231. options = legend.options,
  23232. horizontal = options.layout === 'horizontal',
  23233. symbolWidth = legend.symbolWidth,
  23234. symbolPadding = options.symbolPadding,
  23235. itemStyle = legend.itemStyle,
  23236. itemHiddenStyle = legend.itemHiddenStyle,
  23237. itemDistance = horizontal ? pick(options.itemDistance, 20) : 0,
  23238. ltr = !options.rtl,
  23239. bBox,
  23240. li = item.legendItem,
  23241. isSeries = !item.series,
  23242. series = !isSeries && item.series.drawLegendSymbol ?
  23243. item.series :
  23244. item,
  23245. seriesOptions = series.options,
  23246. showCheckbox = legend.createCheckboxForItem &&
  23247. seriesOptions &&
  23248. seriesOptions.showCheckbox,
  23249. // full width minus text width
  23250. itemExtraWidth = symbolWidth + symbolPadding + itemDistance +
  23251. (showCheckbox ? 20 : 0),
  23252. useHTML = options.useHTML,
  23253. itemClassName = item.options.className;
  23254. if (!li) { // generate it once, later move it
  23255. // Generate the group box, a group to hold the symbol and text. Text
  23256. // is to be appended in Legend class.
  23257. item.legendGroup = renderer.g('legend-item')
  23258. .addClass(
  23259. 'highcharts-' + series.type + '-series ' +
  23260. 'highcharts-color-' + item.colorIndex +
  23261. (itemClassName ? ' ' + itemClassName : '') +
  23262. (isSeries ? ' highcharts-series-' + item.index : '')
  23263. )
  23264. .attr({ zIndex: 1 })
  23265. .add(legend.scrollGroup);
  23266. // Generate the list item text and add it to the group
  23267. item.legendItem = li = renderer.text(
  23268. '',
  23269. ltr ? symbolWidth + symbolPadding : -symbolPadding,
  23270. legend.baseline || 0,
  23271. useHTML
  23272. );
  23273. if (!chart.styledMode) {
  23274. // merge to prevent modifying original (#1021)
  23275. li.css(merge(item.visible ? itemStyle : itemHiddenStyle));
  23276. }
  23277. li.attr({
  23278. align: ltr ? 'left' : 'right',
  23279. zIndex: 2
  23280. })
  23281. .add(item.legendGroup);
  23282. // Get the baseline for the first item - the font size is equal for
  23283. // all
  23284. if (!legend.baseline) {
  23285. legend.fontMetrics = renderer.fontMetrics(
  23286. chart.styledMode ? 12 : itemStyle.fontSize,
  23287. li
  23288. );
  23289. legend.baseline =
  23290. legend.fontMetrics.f + 3 + legend.itemMarginTop;
  23291. li.attr('y', legend.baseline);
  23292. }
  23293. // Draw the legend symbol inside the group box
  23294. legend.symbolHeight = options.symbolHeight || legend.fontMetrics.f;
  23295. series.drawLegendSymbol(legend, item);
  23296. if (legend.setItemEvents) {
  23297. legend.setItemEvents(item, li, useHTML);
  23298. }
  23299. // add the HTML checkbox on top
  23300. if (showCheckbox) {
  23301. legend.createCheckboxForItem(item);
  23302. }
  23303. }
  23304. // Colorize the items
  23305. legend.colorizeItem(item, item.visible);
  23306. // Take care of max width and text overflow (#6659)
  23307. if (chart.styledMode || !itemStyle.width) {
  23308. li.css({
  23309. width: (
  23310. options.itemWidth ||
  23311. legend.widthOption ||
  23312. chart.spacingBox.width
  23313. ) - itemExtraWidth
  23314. });
  23315. }
  23316. // Always update the text
  23317. legend.setText(item);
  23318. // calculate the positions for the next line
  23319. bBox = li.getBBox();
  23320. item.itemWidth = item.checkboxOffset =
  23321. options.itemWidth ||
  23322. item.legendItemWidth ||
  23323. bBox.width + itemExtraWidth;
  23324. legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
  23325. legend.totalItemWidth += item.itemWidth;
  23326. legend.itemHeight = item.itemHeight = Math.round(
  23327. item.legendItemHeight || bBox.height || legend.symbolHeight
  23328. );
  23329. },
  23330. /**
  23331. * Get the position of the item in the layout. We now know the
  23332. * maxItemWidth from the previous loop.
  23333. *
  23334. * @private
  23335. * @function Highcharts.Legend#layoutItem
  23336. *
  23337. * @param {Highcharts.Point|Highcharts.Series} item
  23338. */
  23339. layoutItem: function (item) {
  23340. var options = this.options,
  23341. padding = this.padding,
  23342. horizontal = options.layout === 'horizontal',
  23343. itemHeight = item.itemHeight,
  23344. itemMarginBottom = options.itemMarginBottom || 0,
  23345. itemMarginTop = this.itemMarginTop,
  23346. itemDistance = horizontal ? pick(options.itemDistance, 20) : 0,
  23347. maxLegendWidth = this.maxLegendWidth,
  23348. itemWidth = (
  23349. options.alignColumns &&
  23350. this.totalItemWidth > maxLegendWidth
  23351. ) ?
  23352. this.maxItemWidth :
  23353. item.itemWidth;
  23354. // If the item exceeds the width, start a new line
  23355. if (
  23356. horizontal &&
  23357. this.itemX - padding + itemWidth > maxLegendWidth
  23358. ) {
  23359. this.itemX = padding;
  23360. this.itemY += itemMarginTop + this.lastLineHeight +
  23361. itemMarginBottom;
  23362. this.lastLineHeight = 0; // reset for next line (#915, #3976)
  23363. }
  23364. // Set the edge positions
  23365. this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
  23366. this.lastLineHeight = Math.max( // #915
  23367. itemHeight,
  23368. this.lastLineHeight
  23369. );
  23370. // cache the position of the newly generated or reordered items
  23371. item._legendItemPos = [this.itemX, this.itemY];
  23372. // advance
  23373. if (horizontal) {
  23374. this.itemX += itemWidth;
  23375. } else {
  23376. this.itemY += itemMarginTop + itemHeight + itemMarginBottom;
  23377. this.lastLineHeight = itemHeight;
  23378. }
  23379. // the width of the widest item
  23380. this.offsetWidth = this.widthOption || Math.max(
  23381. (
  23382. horizontal ? this.itemX - padding - (item.checkbox ?
  23383. // decrease by itemDistance only when no checkbox #4853
  23384. 0 :
  23385. itemDistance
  23386. ) : itemWidth
  23387. ) + padding,
  23388. this.offsetWidth
  23389. );
  23390. },
  23391. /**
  23392. * Get all items, which is one item per series for most series and one
  23393. * item per point for pie series and its derivatives.
  23394. *
  23395. * @private
  23396. * @function Highcharts.Legend#getAllItems
  23397. *
  23398. * @return {Array<Highcharts.Point|Highcharts.Series>}
  23399. * The current items in the legend.
  23400. *
  23401. * @fires Highcharts.Legend#event:afterGetAllItems
  23402. *
  23403. * @todo
  23404. * Make events official: Fires the event `afterGetAllItems`.
  23405. */
  23406. getAllItems: function () {
  23407. var allItems = [];
  23408. this.chart.series.forEach(function (series) {
  23409. var seriesOptions = series && series.options;
  23410. // Handle showInLegend. If the series is linked to another series,
  23411. // defaults to false.
  23412. if (series && pick(
  23413. seriesOptions.showInLegend,
  23414. !defined(seriesOptions.linkedTo) ? undefined : false, true
  23415. )) {
  23416. // Use points or series for the legend item depending on
  23417. // legendType
  23418. allItems = allItems.concat(
  23419. series.legendItems ||
  23420. (
  23421. seriesOptions.legendType === 'point' ?
  23422. series.data :
  23423. series
  23424. )
  23425. );
  23426. }
  23427. });
  23428. fireEvent(this, 'afterGetAllItems', { allItems: allItems });
  23429. return allItems;
  23430. },
  23431. /**
  23432. * Get a short, three letter string reflecting the alignment and layout.
  23433. *
  23434. * @private
  23435. * @function Highcharts.Legend#getAlignment
  23436. *
  23437. * @return {string}
  23438. * The alignment, empty string if floating
  23439. */
  23440. getAlignment: function () {
  23441. var options = this.options;
  23442. // Use the first letter of each alignment option in order to detect
  23443. // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
  23444. if (this.proximate) {
  23445. return options.align.charAt(0) + 'tv';
  23446. }
  23447. return options.floating ? '' : (
  23448. options.align.charAt(0) +
  23449. options.verticalAlign.charAt(0) +
  23450. options.layout.charAt(0)
  23451. );
  23452. },
  23453. /**
  23454. * Adjust the chart margins by reserving space for the legend on only one
  23455. * side of the chart. If the position is set to a corner, top or bottom is
  23456. * reserved for horizontal legends and left or right for vertical ones.
  23457. *
  23458. * @private
  23459. * @function Highcharts.Legend#adjustMargins
  23460. *
  23461. * @param {Array<number>} margin
  23462. *
  23463. * @param {number} spacing
  23464. */
  23465. adjustMargins: function (margin, spacing) {
  23466. var chart = this.chart,
  23467. options = this.options,
  23468. alignment = this.getAlignment();
  23469. if (alignment) {
  23470. ([
  23471. /(lth|ct|rth)/,
  23472. /(rtv|rm|rbv)/,
  23473. /(rbh|cb|lbh)/,
  23474. /(lbv|lm|ltv)/
  23475. ]).forEach(function (alignments, side) {
  23476. if (alignments.test(alignment) && !defined(margin[side])) {
  23477. // Now we have detected on which side of the chart we should
  23478. // reserve space for the legend
  23479. chart[marginNames[side]] = Math.max(
  23480. chart[marginNames[side]],
  23481. (
  23482. chart.legend[
  23483. (side + 1) % 2 ? 'legendHeight' : 'legendWidth'
  23484. ] +
  23485. [1, -1, -1, 1][side] * options[
  23486. (side % 2) ? 'x' : 'y'
  23487. ] +
  23488. pick(options.margin, 12) +
  23489. spacing[side] +
  23490. (
  23491. side === 0 &&
  23492. chart.options.title.margin !== undefined ?
  23493. chart.titleOffset +
  23494. chart.options.title.margin :
  23495. 0
  23496. ) // #7428, #7894
  23497. )
  23498. );
  23499. }
  23500. });
  23501. }
  23502. },
  23503. /**
  23504. * @private
  23505. * @function Highcharts.Legend#proximatePositions
  23506. */
  23507. proximatePositions: function () {
  23508. var chart = this.chart,
  23509. boxes = [],
  23510. alignLeft = this.options.align === 'left';
  23511. this.allItems.forEach(function (item) {
  23512. var lastPoint,
  23513. height,
  23514. useFirstPoint = alignLeft;
  23515. if (item.xAxis && item.points) {
  23516. if (item.xAxis.options.reversed) {
  23517. useFirstPoint = !useFirstPoint;
  23518. }
  23519. lastPoint = H.find(
  23520. useFirstPoint ?
  23521. item.points :
  23522. item.points.slice(0).reverse(),
  23523. function (item) {
  23524. return H.isNumber(item.plotY);
  23525. }
  23526. );
  23527. height = item.legendGroup.getBBox().height;
  23528. boxes.push({
  23529. target: item.visible ?
  23530. (lastPoint ? lastPoint.plotY : item.xAxis.height) -
  23531. 0.3 * height :
  23532. chart.plotHeight,
  23533. size: height,
  23534. item: item
  23535. });
  23536. }
  23537. }, this);
  23538. H.distribute(boxes, chart.plotHeight);
  23539. boxes.forEach(function (box) {
  23540. box.item._legendItemPos[1] =
  23541. chart.plotTop - chart.spacing[0] + box.pos;
  23542. });
  23543. },
  23544. /**
  23545. * Render the legend. This method can be called both before and after
  23546. * `chart.render`. If called after, it will only rearrange items instead
  23547. * of creating new ones. Called internally on initial render and after
  23548. * redraws.
  23549. *
  23550. * @private
  23551. * @function Highcharts.Legend#render
  23552. */
  23553. render: function () {
  23554. var legend = this,
  23555. chart = legend.chart,
  23556. renderer = chart.renderer,
  23557. legendGroup = legend.group,
  23558. allItems,
  23559. display,
  23560. legendWidth,
  23561. legendHeight,
  23562. box = legend.box,
  23563. options = legend.options,
  23564. padding = legend.padding,
  23565. alignTo,
  23566. allowedWidth;
  23567. legend.itemX = padding;
  23568. legend.itemY = legend.initialItemY;
  23569. legend.offsetWidth = 0;
  23570. legend.lastItemY = 0;
  23571. legend.widthOption = H.relativeLength(
  23572. options.width,
  23573. chart.spacingBox.width - padding
  23574. );
  23575. // Compute how wide the legend is allowed to be
  23576. allowedWidth = chart.spacingBox.width - 2 * padding - options.x;
  23577. if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
  23578. allowedWidth /= 2;
  23579. }
  23580. legend.maxLegendWidth = legend.widthOption || allowedWidth;
  23581. if (!legendGroup) {
  23582. /**
  23583. * SVG group of the legend.
  23584. *
  23585. * @readonly
  23586. * @name Highcharts.Legend#group
  23587. * @type {Highcharts.SVGElement}
  23588. */
  23589. legend.group = legendGroup = renderer.g('legend')
  23590. .attr({ zIndex: 7 })
  23591. .add();
  23592. legend.contentGroup = renderer.g()
  23593. .attr({ zIndex: 1 }) // above background
  23594. .add(legendGroup);
  23595. legend.scrollGroup = renderer.g()
  23596. .add(legend.contentGroup);
  23597. }
  23598. legend.renderTitle();
  23599. // add each series or point
  23600. allItems = legend.getAllItems();
  23601. // sort by legendIndex
  23602. stableSort(allItems, function (a, b) {
  23603. return ((a.options && a.options.legendIndex) || 0) -
  23604. ((b.options && b.options.legendIndex) || 0);
  23605. });
  23606. // reversed legend
  23607. if (options.reversed) {
  23608. allItems.reverse();
  23609. }
  23610. /**
  23611. * All items for the legend, which is an array of series for most series
  23612. * and an array of points for pie series and its derivatives.
  23613. *
  23614. * @readonly
  23615. * @name Highcharts.Legend#allItems
  23616. * @type {Array<Highcharts.Point|Highcharts.Series>}
  23617. */
  23618. legend.allItems = allItems;
  23619. legend.display = display = !!allItems.length;
  23620. // Render the items. First we run a loop to set the text and properties
  23621. // and read all the bounding boxes. The next loop computes the item
  23622. // positions based on the bounding boxes.
  23623. legend.lastLineHeight = 0;
  23624. legend.maxItemWidth = 0;
  23625. legend.totalItemWidth = 0;
  23626. legend.itemHeight = 0;
  23627. allItems.forEach(legend.renderItem, legend);
  23628. allItems.forEach(legend.layoutItem, legend);
  23629. // Get the box
  23630. legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
  23631. legendHeight = legend.lastItemY + legend.lastLineHeight +
  23632. legend.titleHeight;
  23633. legendHeight = legend.handleOverflow(legendHeight);
  23634. legendHeight += padding;
  23635. // Draw the border and/or background
  23636. if (!box) {
  23637. /**
  23638. * SVG element of the legend box.
  23639. *
  23640. * @readonly
  23641. * @name Highcharts.Legend#box
  23642. * @type {Highcharts.SVGElement}
  23643. */
  23644. legend.box = box = renderer.rect()
  23645. .addClass('highcharts-legend-box')
  23646. .attr({
  23647. r: options.borderRadius
  23648. })
  23649. .add(legendGroup);
  23650. box.isNew = true;
  23651. }
  23652. // Presentational
  23653. if (!chart.styledMode) {
  23654. box
  23655. .attr({
  23656. stroke: options.borderColor,
  23657. 'stroke-width': options.borderWidth || 0,
  23658. fill: options.backgroundColor || 'none'
  23659. })
  23660. .shadow(options.shadow);
  23661. }
  23662. if (legendWidth > 0 && legendHeight > 0) {
  23663. box[box.isNew ? 'attr' : 'animate'](
  23664. box.crisp.call({}, { // #7260
  23665. x: 0,
  23666. y: 0,
  23667. width: legendWidth,
  23668. height: legendHeight
  23669. }, box.strokeWidth())
  23670. );
  23671. box.isNew = false;
  23672. }
  23673. // hide the border if no items
  23674. box[display ? 'show' : 'hide']();
  23675. // Open for responsiveness
  23676. if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
  23677. legendWidth = legendHeight = 0;
  23678. }
  23679. legend.legendWidth = legendWidth;
  23680. legend.legendHeight = legendHeight;
  23681. if (display) {
  23682. // If aligning to the top and the layout is horizontal, adjust for
  23683. // the title (#7428)
  23684. alignTo = chart.spacingBox;
  23685. if (/(lth|ct|rth)/.test(legend.getAlignment())) {
  23686. alignTo = merge(alignTo, {
  23687. y: alignTo.y + chart.titleOffset +
  23688. chart.options.title.margin
  23689. });
  23690. }
  23691. legendGroup.align(merge(options, {
  23692. width: legendWidth,
  23693. height: legendHeight,
  23694. verticalAlign: this.proximate ? 'top' : options.verticalAlign
  23695. }), true, alignTo);
  23696. }
  23697. if (!this.proximate) {
  23698. this.positionItems();
  23699. }
  23700. },
  23701. /**
  23702. * Set up the overflow handling by adding navigation with up and down arrows
  23703. * below the legend.
  23704. *
  23705. * @private
  23706. * @function Highcharts.Legend#handleOverflow
  23707. *
  23708. * @param {number} legendHeight
  23709. *
  23710. * @return {number}
  23711. */
  23712. handleOverflow: function (legendHeight) {
  23713. var legend = this,
  23714. chart = this.chart,
  23715. renderer = chart.renderer,
  23716. options = this.options,
  23717. optionsY = options.y,
  23718. alignTop = options.verticalAlign === 'top',
  23719. padding = this.padding,
  23720. spaceHeight = chart.spacingBox.height +
  23721. (alignTop ? -optionsY : optionsY) - padding,
  23722. maxHeight = options.maxHeight,
  23723. clipHeight,
  23724. clipRect = this.clipRect,
  23725. navOptions = options.navigation,
  23726. animation = pick(navOptions.animation, true),
  23727. arrowSize = navOptions.arrowSize || 12,
  23728. nav = this.nav,
  23729. pages = this.pages,
  23730. lastY,
  23731. allItems = this.allItems,
  23732. clipToHeight = function (height) {
  23733. if (typeof height === 'number') {
  23734. clipRect.attr({
  23735. height: height
  23736. });
  23737. } else if (clipRect) { // Reset (#5912)
  23738. legend.clipRect = clipRect.destroy();
  23739. legend.contentGroup.clip();
  23740. }
  23741. // useHTML
  23742. if (legend.contentGroup.div) {
  23743. legend.contentGroup.div.style.clip = height ?
  23744. 'rect(' + padding + 'px,9999px,' +
  23745. (padding + height) + 'px,0)' :
  23746. 'auto';
  23747. }
  23748. };
  23749. // Adjust the height
  23750. if (
  23751. options.layout === 'horizontal' &&
  23752. options.verticalAlign !== 'middle' &&
  23753. !options.floating
  23754. ) {
  23755. spaceHeight /= 2;
  23756. }
  23757. if (maxHeight) {
  23758. spaceHeight = Math.min(spaceHeight, maxHeight);
  23759. }
  23760. // Reset the legend height and adjust the clipping rectangle
  23761. pages.length = 0;
  23762. if (legendHeight > spaceHeight && navOptions.enabled !== false) {
  23763. this.clipHeight = clipHeight =
  23764. Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
  23765. this.currentPage = pick(this.currentPage, 1);
  23766. this.fullHeight = legendHeight;
  23767. // Fill pages with Y positions so that the top of each a legend item
  23768. // defines the scroll top for each page (#2098)
  23769. allItems.forEach(function (item, i) {
  23770. var y = item._legendItemPos[1],
  23771. h = Math.round(item.legendItem.getBBox().height),
  23772. len = pages.length;
  23773. if (!len || (y - pages[len - 1] > clipHeight &&
  23774. (lastY || y) !== pages[len - 1])) {
  23775. pages.push(lastY || y);
  23776. len++;
  23777. }
  23778. // Keep track of which page each item is on
  23779. item.pageIx = len - 1;
  23780. if (lastY) {
  23781. allItems[i - 1].pageIx = len - 1;
  23782. }
  23783. if (
  23784. i === allItems.length - 1 &&
  23785. y + h - pages[len - 1] > clipHeight &&
  23786. y !== lastY // #2617
  23787. ) {
  23788. pages.push(y);
  23789. item.pageIx = len;
  23790. }
  23791. if (y !== lastY) {
  23792. lastY = y;
  23793. }
  23794. });
  23795. // Only apply clipping if needed. Clipping causes blurred legend in
  23796. // PDF export (#1787)
  23797. if (!clipRect) {
  23798. clipRect = legend.clipRect =
  23799. renderer.clipRect(0, padding, 9999, 0);
  23800. legend.contentGroup.clip(clipRect);
  23801. }
  23802. clipToHeight(clipHeight);
  23803. // Add navigation elements
  23804. if (!nav) {
  23805. this.nav = nav = renderer.g()
  23806. .attr({ zIndex: 1 })
  23807. .add(this.group);
  23808. this.up = renderer
  23809. .symbol(
  23810. 'triangle',
  23811. 0,
  23812. 0,
  23813. arrowSize,
  23814. arrowSize
  23815. )
  23816. .on('click', function () {
  23817. legend.scroll(-1, animation);
  23818. })
  23819. .add(nav);
  23820. this.pager = renderer.text('', 15, 10)
  23821. .addClass('highcharts-legend-navigation');
  23822. if (!chart.styledMode) {
  23823. this.pager.css(navOptions.style);
  23824. }
  23825. this.pager.add(nav);
  23826. this.down = renderer
  23827. .symbol(
  23828. 'triangle-down',
  23829. 0,
  23830. 0,
  23831. arrowSize,
  23832. arrowSize
  23833. )
  23834. .on('click', function () {
  23835. legend.scroll(1, animation);
  23836. })
  23837. .add(nav);
  23838. }
  23839. // Set initial position
  23840. legend.scroll(0);
  23841. legendHeight = spaceHeight;
  23842. // Reset
  23843. } else if (nav) {
  23844. clipToHeight();
  23845. this.nav = nav.destroy(); // #6322
  23846. this.scrollGroup.attr({
  23847. translateY: 1
  23848. });
  23849. this.clipHeight = 0; // #1379
  23850. }
  23851. return legendHeight;
  23852. },
  23853. /**
  23854. * Scroll the legend by a number of pages.
  23855. *
  23856. * @private
  23857. * @function Highcharts.Legend#scroll
  23858. *
  23859. * @param {number} scrollBy
  23860. * The number of pages to scroll.
  23861. *
  23862. * @param {Highcharts.AnimationOptionsObject} animation
  23863. * Whether and how to apply animation.
  23864. */
  23865. scroll: function (scrollBy, animation) {
  23866. var pages = this.pages,
  23867. pageCount = pages.length,
  23868. currentPage = this.currentPage + scrollBy,
  23869. clipHeight = this.clipHeight,
  23870. navOptions = this.options.navigation,
  23871. pager = this.pager,
  23872. padding = this.padding;
  23873. // When resizing while looking at the last page
  23874. if (currentPage > pageCount) {
  23875. currentPage = pageCount;
  23876. }
  23877. if (currentPage > 0) {
  23878. if (animation !== undefined) {
  23879. setAnimation(animation, this.chart);
  23880. }
  23881. this.nav.attr({
  23882. translateX: padding,
  23883. translateY: clipHeight + this.padding + 7 + this.titleHeight,
  23884. visibility: 'visible'
  23885. });
  23886. this.up.attr({
  23887. 'class': currentPage === 1 ?
  23888. 'highcharts-legend-nav-inactive' :
  23889. 'highcharts-legend-nav-active'
  23890. });
  23891. pager.attr({
  23892. text: currentPage + '/' + pageCount
  23893. });
  23894. this.down.attr({
  23895. 'x': 18 + this.pager.getBBox().width, // adjust to text width
  23896. 'class': currentPage === pageCount ?
  23897. 'highcharts-legend-nav-inactive' :
  23898. 'highcharts-legend-nav-active'
  23899. });
  23900. if (!this.chart.styledMode) {
  23901. this.up
  23902. .attr({
  23903. fill: currentPage === 1 ?
  23904. navOptions.inactiveColor :
  23905. navOptions.activeColor
  23906. })
  23907. .css({
  23908. cursor: currentPage === 1 ? 'default' : 'pointer'
  23909. });
  23910. this.down
  23911. .attr({
  23912. fill: currentPage === pageCount ?
  23913. navOptions.inactiveColor :
  23914. navOptions.activeColor
  23915. })
  23916. .css({
  23917. cursor: currentPage === pageCount ?
  23918. 'default' :
  23919. 'pointer'
  23920. });
  23921. }
  23922. this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
  23923. this.scrollGroup.animate({
  23924. translateY: this.scrollOffset
  23925. });
  23926. this.currentPage = currentPage;
  23927. this.positionCheckboxes();
  23928. }
  23929. }
  23930. };
  23931. /**
  23932. * Legend symbol mixin.
  23933. *
  23934. * @private
  23935. * @mixin Highcharts.LegendSymbolMixin
  23936. */
  23937. H.LegendSymbolMixin = {
  23938. /**
  23939. * Get the series' symbol in the legend
  23940. *
  23941. * @private
  23942. * @function Highcharts.LegendSymbolMixin.drawRectangle
  23943. *
  23944. * @param {Highcharts.Legend} legend
  23945. * The legend object
  23946. *
  23947. * @param {Highcharts.Point|Highcharts.Series} item
  23948. * The series (this) or point
  23949. */
  23950. drawRectangle: function (legend, item) {
  23951. var options = legend.options,
  23952. symbolHeight = legend.symbolHeight,
  23953. square = options.squareSymbol,
  23954. symbolWidth = square ? symbolHeight : legend.symbolWidth;
  23955. item.legendSymbol = this.chart.renderer.rect(
  23956. square ? (legend.symbolWidth - symbolHeight) / 2 : 0,
  23957. legend.baseline - symbolHeight + 1, // #3988
  23958. symbolWidth,
  23959. symbolHeight,
  23960. pick(legend.options.symbolRadius, symbolHeight / 2)
  23961. )
  23962. .addClass('highcharts-point')
  23963. .attr({
  23964. zIndex: 3
  23965. }).add(item.legendGroup);
  23966. },
  23967. /**
  23968. * Get the series' symbol in the legend. This method should be overridable
  23969. * to create custom symbols through
  23970. * Highcharts.seriesTypes[type].prototype.drawLegendSymbols.
  23971. *
  23972. * @private
  23973. * @function Highcharts.LegendSymbolMixin.drawLineMarker
  23974. *
  23975. * @param {Highcharts.Legend} legend
  23976. * The legend object.
  23977. */
  23978. drawLineMarker: function (legend) {
  23979. var options = this.options,
  23980. markerOptions = options.marker,
  23981. radius,
  23982. legendSymbol,
  23983. symbolWidth = legend.symbolWidth,
  23984. symbolHeight = legend.symbolHeight,
  23985. generalRadius = symbolHeight / 2,
  23986. renderer = this.chart.renderer,
  23987. legendItemGroup = this.legendGroup,
  23988. verticalCenter = legend.baseline -
  23989. Math.round(legend.fontMetrics.b * 0.3),
  23990. attr = {};
  23991. // Draw the line
  23992. if (!this.chart.styledMode) {
  23993. attr = {
  23994. 'stroke-width': options.lineWidth || 0
  23995. };
  23996. if (options.dashStyle) {
  23997. attr.dashstyle = options.dashStyle;
  23998. }
  23999. }
  24000. this.legendLine = renderer.path([
  24001. 'M',
  24002. 0,
  24003. verticalCenter,
  24004. 'L',
  24005. symbolWidth,
  24006. verticalCenter
  24007. ])
  24008. .addClass('highcharts-graph')
  24009. .attr(attr)
  24010. .add(legendItemGroup);
  24011. // Draw the marker
  24012. if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
  24013. // Do not allow the marker to be larger than the symbolHeight
  24014. radius = Math.min(
  24015. pick(markerOptions.radius, generalRadius),
  24016. generalRadius
  24017. );
  24018. // Restrict symbol markers size
  24019. if (this.symbol.indexOf('url') === 0) {
  24020. markerOptions = merge(markerOptions, {
  24021. width: symbolHeight,
  24022. height: symbolHeight
  24023. });
  24024. radius = 0;
  24025. }
  24026. this.legendSymbol = legendSymbol = renderer.symbol(
  24027. this.symbol,
  24028. (symbolWidth / 2) - radius,
  24029. verticalCenter - radius,
  24030. 2 * radius,
  24031. 2 * radius,
  24032. markerOptions
  24033. )
  24034. .addClass('highcharts-point')
  24035. .add(legendItemGroup);
  24036. legendSymbol.isMarker = true;
  24037. }
  24038. }
  24039. };
  24040. // Workaround for #2030, horizontal legend items not displaying in IE11 Preview,
  24041. // and for #2580, a similar drawing flaw in Firefox 26.
  24042. // Explore if there's a general cause for this. The problem may be related
  24043. // to nested group elements, as the legend item texts are within 4 group
  24044. // elements.
  24045. if (/Trident\/7\.0/.test(win.navigator.userAgent) || isFirefox) {
  24046. wrap(Highcharts.Legend.prototype, 'positionItem', function (proceed, item) {
  24047. var legend = this,
  24048. // If chart destroyed in sync, this is undefined (#2030)
  24049. runPositionItem = function () {
  24050. if (item._legendItemPos) {
  24051. proceed.call(legend, item);
  24052. }
  24053. };
  24054. // Do it now, for export and to get checkbox placement
  24055. runPositionItem();
  24056. // Do it after to work around the core issue
  24057. if (!legend.bubbleLegend) {
  24058. setTimeout(runPositionItem);
  24059. }
  24060. });
  24061. }
  24062. }(Highcharts));
  24063. (function (H) {
  24064. /**
  24065. * (c) 2010-2019 Torstein Honsi
  24066. *
  24067. * License: www.highcharts.com/license
  24068. */
  24069. /**
  24070. * Callback for chart constructors.
  24071. *
  24072. * @callback Highcharts.ChartCallbackFunction
  24073. *
  24074. * @param {Highcharts.Chart} chart
  24075. * Created chart.
  24076. */
  24077. /**
  24078. * The chart title. The title has an `update` method that allows modifying the
  24079. * options directly or indirectly via `chart.update`.
  24080. *
  24081. * @interface Highcharts.TitleObject
  24082. * @extends Highcharts.SVGElement
  24083. *//**
  24084. * Modify options for the title.
  24085. *
  24086. * @function Highcharts.TitleObject#update
  24087. *
  24088. * @param {Highcharts.TitleOptions} titleOptions
  24089. * Options to modify.
  24090. *
  24091. * @param {boolean} [redraw=true]
  24092. * Whether to redraw the chart after the title is altered. If doing more
  24093. * operations on the chart, it is a good idea to set redraw to false and
  24094. * call {@link Chart#redraw} after.
  24095. */
  24096. /**
  24097. * The chart subtitle. The subtitle has an `update` method that
  24098. * allows modifying the options directly or indirectly via
  24099. * `chart.update`.
  24100. *
  24101. * @interface Highcharts.SubtitleObject
  24102. * @extends Highcharts.SVGElement
  24103. *//**
  24104. * Modify options for the subtitle.
  24105. *
  24106. * @function Highcharts.SubtitleObject#update
  24107. *
  24108. * @param {Highcharts.SubtitleOptions} subtitleOptions
  24109. * Options to modify.
  24110. *
  24111. * @param {boolean} [redraw=true]
  24112. * Whether to redraw the chart after the subtitle is altered. If doing
  24113. * more operations on the chart, it is a good idea to set redraw to false
  24114. * and call {@link Chart#redraw} after.
  24115. */
  24116. var addEvent = H.addEvent,
  24117. animate = H.animate,
  24118. animObject = H.animObject,
  24119. attr = H.attr,
  24120. doc = H.doc,
  24121. Axis = H.Axis, // @todo add as requirement
  24122. createElement = H.createElement,
  24123. defaultOptions = H.defaultOptions,
  24124. discardElement = H.discardElement,
  24125. charts = H.charts,
  24126. css = H.css,
  24127. defined = H.defined,
  24128. extend = H.extend,
  24129. find = H.find,
  24130. fireEvent = H.fireEvent,
  24131. isNumber = H.isNumber,
  24132. isObject = H.isObject,
  24133. isString = H.isString,
  24134. Legend = H.Legend, // @todo add as requirement
  24135. marginNames = H.marginNames,
  24136. merge = H.merge,
  24137. objectEach = H.objectEach,
  24138. Pointer = H.Pointer, // @todo add as requirement
  24139. pick = H.pick,
  24140. pInt = H.pInt,
  24141. removeEvent = H.removeEvent,
  24142. seriesTypes = H.seriesTypes,
  24143. splat = H.splat,
  24144. syncTimeout = H.syncTimeout,
  24145. win = H.win;
  24146. /**
  24147. * The Chart class. The recommended constructor is {@link Highcharts#chart}.
  24148. *
  24149. * @example
  24150. * var chart = Highcharts.chart('container', {
  24151. * title: {
  24152. * text: 'My chart'
  24153. * },
  24154. * series: [{
  24155. * data: [1, 3, 2, 4]
  24156. * }]
  24157. * })
  24158. *
  24159. * @class
  24160. * @name Highcharts.Chart
  24161. *
  24162. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  24163. * The DOM element to render to, or its id.
  24164. *
  24165. * @param {Highcharts.Options} options
  24166. * The chart options structure.
  24167. *
  24168. * @param {Highcharts.ChartCallbackFunction} [callback]
  24169. * Function to run when the chart has loaded and and all external images
  24170. * are loaded. Defining a
  24171. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  24172. * handler is equivalent.
  24173. */
  24174. var Chart = H.Chart = function () {
  24175. this.getArgs.apply(this, arguments);
  24176. };
  24177. /**
  24178. * Factory function for basic charts.
  24179. *
  24180. * @example
  24181. * // Render a chart in to div#container
  24182. * var chart = Highcharts.chart('container', {
  24183. * title: {
  24184. * text: 'My chart'
  24185. * },
  24186. * series: [{
  24187. * data: [1, 3, 2, 4]
  24188. * }]
  24189. * });
  24190. *
  24191. * @function Highcharts.chart
  24192. *
  24193. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  24194. * The DOM element to render to, or its id.
  24195. *
  24196. * @param {Highcharts.Options} options
  24197. * The chart options structure.
  24198. *
  24199. * @param {Highcharts.ChartCallbackFunction} [callback]
  24200. * Function to run when the chart has loaded and and all external images
  24201. * are loaded. Defining a
  24202. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  24203. * handler is equivalent.
  24204. *
  24205. * @return {Highcharts.Chart}
  24206. * Returns the Chart object.
  24207. */
  24208. H.chart = function (a, b, c) {
  24209. return new Chart(a, b, c);
  24210. };
  24211. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  24212. // Hook for adding callbacks in modules
  24213. callbacks: [],
  24214. /**
  24215. * Handle the arguments passed to the constructor.
  24216. *
  24217. * @private
  24218. * @function Highcharts.Chart#getArgs
  24219. *
  24220. * @param {...Array<*>} arguments
  24221. * All arguments for the constructor.
  24222. *
  24223. * @return {Array<*>}
  24224. * Passed arguments without renderTo.
  24225. *
  24226. * @fires Highcharts.Chart#event:init
  24227. * @fires Highcharts.Chart#event:afterInit
  24228. */
  24229. getArgs: function () {
  24230. var args = [].slice.call(arguments);
  24231. // Remove the optional first argument, renderTo, and
  24232. // set it on this.
  24233. if (isString(args[0]) || args[0].nodeName) {
  24234. this.renderTo = args.shift();
  24235. }
  24236. this.init(args[0], args[1]);
  24237. },
  24238. /**
  24239. * Overridable function that initializes the chart. The constructor's
  24240. * arguments are passed on directly.
  24241. *
  24242. * @function Highcharts.Chart#init
  24243. *
  24244. * @param {Highcharts.Options} userOptions
  24245. * Custom options.
  24246. *
  24247. * @param {Function} [callback]
  24248. * Function to run when the chart has loaded and and all external
  24249. * images are loaded.
  24250. *
  24251. * @fires Highcharts.Chart#event:init
  24252. * @fires Highcharts.Chart#event:afterInit
  24253. */
  24254. init: function (userOptions, callback) {
  24255. // Handle regular options
  24256. var options,
  24257. type,
  24258. // skip merging data points to increase performance
  24259. seriesOptions = userOptions.series,
  24260. userPlotOptions = userOptions.plotOptions || {};
  24261. // Fire the event with a default function
  24262. fireEvent(this, 'init', { args: arguments }, function () {
  24263. userOptions.series = null;
  24264. options = merge(defaultOptions, userOptions); // do the merge
  24265. // Override (by copy of user options) or clear tooltip options
  24266. // in chart.options.plotOptions (#6218)
  24267. for (type in options.plotOptions) {
  24268. options.plotOptions[type].tooltip = (
  24269. userPlotOptions[type] &&
  24270. merge(userPlotOptions[type].tooltip) // override by copy
  24271. ) || undefined; // or clear
  24272. }
  24273. // User options have higher priority than default options
  24274. // (#6218). In case of exporting: path is changed
  24275. options.tooltip.userOptions = (
  24276. userOptions.chart &&
  24277. userOptions.chart.forExport &&
  24278. userOptions.tooltip.userOptions
  24279. ) || userOptions.tooltip;
  24280. // set back the series data
  24281. options.series = userOptions.series = seriesOptions;
  24282. this.userOptions = userOptions;
  24283. var optionsChart = options.chart;
  24284. var chartEvents = optionsChart.events;
  24285. this.margin = [];
  24286. this.spacing = [];
  24287. // Pixel data bounds for touch zoom
  24288. this.bounds = { h: {}, v: {} };
  24289. // An array of functions that returns labels that should be
  24290. // considered for anti-collision
  24291. this.labelCollectors = [];
  24292. this.callback = callback;
  24293. this.isResizing = 0;
  24294. /**
  24295. * The options structure for the chart. It contains members for
  24296. * the sub elements like series, legend, tooltip etc.
  24297. *
  24298. * @name Highcharts.Chart#options
  24299. * @type {Highcharts.Options}
  24300. */
  24301. this.options = options;
  24302. /**
  24303. * All the axes in the chart.
  24304. *
  24305. * @see Highcharts.Chart.xAxis
  24306. * @see Highcharts.Chart.yAxis
  24307. *
  24308. * @name Highcharts.Chart#axes
  24309. * @type {Array<Highcharts.Axis>}
  24310. */
  24311. this.axes = [];
  24312. /**
  24313. * All the current series in the chart.
  24314. *
  24315. * @name Highcharts.Chart#series
  24316. * @type {Array<Highcharts.Series>}
  24317. */
  24318. this.series = [];
  24319. /**
  24320. * The `Time` object associated with the chart. Since v6.0.5,
  24321. * time settings can be applied individually for each chart. If
  24322. * no individual settings apply, the `Time` object is shared by
  24323. * all instances.
  24324. *
  24325. * @name Highcharts.Chart#time
  24326. * @type {Highcharts.Time}
  24327. */
  24328. this.time =
  24329. userOptions.time && Object.keys(userOptions.time).length ?
  24330. new H.Time(userOptions.time) :
  24331. H.time;
  24332. /**
  24333. * Whether the chart is in styled mode, meaning all presentatinoal
  24334. * attributes are avoided.
  24335. *
  24336. * @name Highcharts.Chart#styledMode
  24337. * @type {boolean}
  24338. */
  24339. this.styledMode = optionsChart.styledMode;
  24340. this.hasCartesianSeries = optionsChart.showAxes;
  24341. var chart = this;
  24342. // Add the chart to the global lookup
  24343. chart.index = charts.length;
  24344. charts.push(chart);
  24345. H.chartCount++;
  24346. // Chart event handlers
  24347. if (chartEvents) {
  24348. objectEach(chartEvents, function (event, eventType) {
  24349. addEvent(chart, eventType, event);
  24350. });
  24351. }
  24352. /**
  24353. * A collection of the X axes in the chart.
  24354. *
  24355. * @name Highcharts.Chart#xAxis
  24356. * @type {Array<Highcharts.Axis>}
  24357. */
  24358. chart.xAxis = [];
  24359. /**
  24360. * A collection of the Y axes in the chart.
  24361. *
  24362. * @name Highcharts.Chart#yAxis
  24363. * @type {Array<Highcharts.Axis>}
  24364. *
  24365. * @todo
  24366. * Make events official: Fire the event `afterInit`.
  24367. */
  24368. chart.yAxis = [];
  24369. chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
  24370. // Fire after init but before first render, before axes and series
  24371. // have been initialized.
  24372. fireEvent(chart, 'afterInit');
  24373. chart.firstRender();
  24374. });
  24375. },
  24376. /**
  24377. * Internal function to unitialize an individual series.
  24378. *
  24379. * @private
  24380. * @function Highcharts.Chart#initSeries
  24381. *
  24382. * @param {Highcharts.ChartOptions} options
  24383. *
  24384. * @return {Highcharts.Series}
  24385. */
  24386. initSeries: function (options) {
  24387. var chart = this,
  24388. optionsChart = chart.options.chart,
  24389. type = (
  24390. options.type ||
  24391. optionsChart.type ||
  24392. optionsChart.defaultSeriesType
  24393. ),
  24394. series,
  24395. Constr = seriesTypes[type];
  24396. // No such series type
  24397. if (!Constr) {
  24398. H.error(17, true, chart);
  24399. }
  24400. series = new Constr();
  24401. series.init(this, options);
  24402. return series;
  24403. },
  24404. /**
  24405. * Order all series above a given index. When series are added and ordered
  24406. * by configuration, only the last series is handled (#248, #1123, #2456,
  24407. * #6112). This function is called on series initialization and destroy.
  24408. *
  24409. * @private
  24410. * @function Highcharts.Series#orderSeries
  24411. *
  24412. * @param {number} fromIndex
  24413. * If this is given, only the series above this index are handled.
  24414. */
  24415. orderSeries: function (fromIndex) {
  24416. var series = this.series,
  24417. i = fromIndex || 0;
  24418. for (; i < series.length; i++) {
  24419. if (series[i]) {
  24420. series[i].index = i;
  24421. series[i].name = series[i].getName();
  24422. }
  24423. }
  24424. },
  24425. /**
  24426. * Check whether a given point is within the plot area.
  24427. *
  24428. * @function Highcharts.Chart#isInsidePlot
  24429. *
  24430. * @param {number} plotX
  24431. * Pixel x relative to the plot area.
  24432. *
  24433. * @param {number} plotY
  24434. * Pixel y relative to the plot area.
  24435. *
  24436. * @param {boolean} inverted
  24437. * Whether the chart is inverted.
  24438. *
  24439. * @return {boolean}
  24440. * Returns true if the given point is inside the plot area.
  24441. */
  24442. isInsidePlot: function (plotX, plotY, inverted) {
  24443. var x = inverted ? plotY : plotX,
  24444. y = inverted ? plotX : plotY;
  24445. return x >= 0 &&
  24446. x <= this.plotWidth &&
  24447. y >= 0 &&
  24448. y <= this.plotHeight;
  24449. },
  24450. /**
  24451. * Redraw the chart after changes have been done to the data, axis extremes
  24452. * chart size or chart elements. All methods for updating axes, series or
  24453. * points have a parameter for redrawing the chart. This is `true` by
  24454. * default. But in many cases you want to do more than one operation on the
  24455. * chart before redrawing, for example add a number of points. In those
  24456. * cases it is a waste of resources to redraw the chart for each new point
  24457. * added. So you add the points and call `chart.redraw()` after.
  24458. *
  24459. * @function Highcharts.Chart#redraw
  24460. *
  24461. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  24462. * If or how to apply animation to the redraw.
  24463. *
  24464. * @fires Highcharts.Chart#event:afterSetExtremes
  24465. * @fires Highcharts.Chart#event:beforeRedraw
  24466. * @fires Highcharts.Chart#event:predraw
  24467. * @fires Highcharts.Chart#event:redraw
  24468. * @fires Highcharts.Chart#event:render
  24469. * @fires Highcharts.Chart#event:updatedData
  24470. */
  24471. redraw: function (animation) {
  24472. fireEvent(this, 'beforeRedraw');
  24473. var chart = this,
  24474. axes = chart.axes,
  24475. series = chart.series,
  24476. pointer = chart.pointer,
  24477. legend = chart.legend,
  24478. legendUserOptions = chart.userOptions.legend,
  24479. redrawLegend = chart.isDirtyLegend,
  24480. hasStackedSeries,
  24481. hasDirtyStacks,
  24482. hasCartesianSeries = chart.hasCartesianSeries,
  24483. isDirtyBox = chart.isDirtyBox,
  24484. i,
  24485. serie,
  24486. renderer = chart.renderer,
  24487. isHiddenChart = renderer.isHidden(),
  24488. afterRedraw = [];
  24489. // Handle responsive rules, not only on resize (#6130)
  24490. if (chart.setResponsive) {
  24491. chart.setResponsive(false);
  24492. }
  24493. H.setAnimation(animation, chart);
  24494. if (isHiddenChart) {
  24495. chart.temporaryDisplay();
  24496. }
  24497. // Adjust title layout (reflow multiline text)
  24498. chart.layOutTitles();
  24499. // link stacked series
  24500. i = series.length;
  24501. while (i--) {
  24502. serie = series[i];
  24503. if (serie.options.stacking) {
  24504. hasStackedSeries = true;
  24505. if (serie.isDirty) {
  24506. hasDirtyStacks = true;
  24507. break;
  24508. }
  24509. }
  24510. }
  24511. if (hasDirtyStacks) { // mark others as dirty
  24512. i = series.length;
  24513. while (i--) {
  24514. serie = series[i];
  24515. if (serie.options.stacking) {
  24516. serie.isDirty = true;
  24517. }
  24518. }
  24519. }
  24520. // Handle updated data in the series
  24521. series.forEach(function (serie) {
  24522. if (serie.isDirty) {
  24523. if (serie.options.legendType === 'point') {
  24524. if (serie.updateTotals) {
  24525. serie.updateTotals();
  24526. }
  24527. redrawLegend = true;
  24528. } else if (
  24529. legendUserOptions &&
  24530. (
  24531. legendUserOptions.labelFormatter ||
  24532. legendUserOptions.labelFormat
  24533. )
  24534. ) {
  24535. redrawLegend = true; // #2165
  24536. }
  24537. }
  24538. if (serie.isDirtyData) {
  24539. fireEvent(serie, 'updatedData');
  24540. }
  24541. });
  24542. // handle added or removed series
  24543. if (redrawLegend && legend && legend.options.enabled) {
  24544. // draw legend graphics
  24545. legend.render();
  24546. chart.isDirtyLegend = false;
  24547. }
  24548. // reset stacks
  24549. if (hasStackedSeries) {
  24550. chart.getStacks();
  24551. }
  24552. if (hasCartesianSeries) {
  24553. // set axes scales
  24554. axes.forEach(function (axis) {
  24555. axis.updateNames();
  24556. // Update categories in a Gantt chart
  24557. if (axis.updateYNames) {
  24558. axis.updateYNames();
  24559. }
  24560. axis.setScale();
  24561. });
  24562. }
  24563. chart.getMargins(); // #3098
  24564. if (hasCartesianSeries) {
  24565. // If one axis is dirty, all axes must be redrawn (#792, #2169)
  24566. axes.forEach(function (axis) {
  24567. if (axis.isDirty) {
  24568. isDirtyBox = true;
  24569. }
  24570. });
  24571. // redraw axes
  24572. axes.forEach(function (axis) {
  24573. // Fire 'afterSetExtremes' only if extremes are set
  24574. var key = axis.min + ',' + axis.max;
  24575. if (axis.extKey !== key) { // #821, #4452
  24576. axis.extKey = key;
  24577. // prevent a recursive call to chart.redraw() (#1119)
  24578. afterRedraw.push(function () {
  24579. fireEvent(
  24580. axis,
  24581. 'afterSetExtremes',
  24582. extend(axis.eventArgs, axis.getExtremes())
  24583. ); // #747, #751
  24584. delete axis.eventArgs;
  24585. });
  24586. }
  24587. if (isDirtyBox || hasStackedSeries) {
  24588. axis.redraw();
  24589. }
  24590. });
  24591. }
  24592. // the plot areas size has changed
  24593. if (isDirtyBox) {
  24594. chart.drawChartBox();
  24595. }
  24596. // Fire an event before redrawing series, used by the boost module to
  24597. // clear previous series renderings.
  24598. fireEvent(chart, 'predraw');
  24599. // redraw affected series
  24600. series.forEach(function (serie) {
  24601. if ((isDirtyBox || serie.isDirty) && serie.visible) {
  24602. serie.redraw();
  24603. }
  24604. // Set it here, otherwise we will have unlimited 'updatedData' calls
  24605. // for a hidden series after setData(). Fixes #6012
  24606. serie.isDirtyData = false;
  24607. });
  24608. // move tooltip or reset
  24609. if (pointer) {
  24610. pointer.reset(true);
  24611. }
  24612. // redraw if canvas
  24613. renderer.draw();
  24614. // Fire the events
  24615. fireEvent(chart, 'redraw');
  24616. fireEvent(chart, 'render');
  24617. if (isHiddenChart) {
  24618. chart.temporaryDisplay(true);
  24619. }
  24620. // Fire callbacks that are put on hold until after the redraw
  24621. afterRedraw.forEach(function (callback) {
  24622. callback.call();
  24623. });
  24624. },
  24625. /**
  24626. * Get an axis, series or point object by `id` as given in the configuration
  24627. * options. Returns `undefined` if no item is found.
  24628. *
  24629. * @sample highcharts/plotoptions/series-id/
  24630. * Get series by id
  24631. *
  24632. * @function Highcharts.Chart#get
  24633. *
  24634. * @param {string} id
  24635. * The id as given in the configuration options.
  24636. *
  24637. * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
  24638. * The retrieved item.
  24639. */
  24640. get: function (id) {
  24641. var ret,
  24642. series = this.series,
  24643. i;
  24644. function itemById(item) {
  24645. return item.id === id || (item.options && item.options.id === id);
  24646. }
  24647. ret =
  24648. // Search axes
  24649. find(this.axes, itemById) ||
  24650. // Search series
  24651. find(this.series, itemById);
  24652. // Search points
  24653. for (i = 0; !ret && i < series.length; i++) {
  24654. ret = find(series[i].points || [], itemById);
  24655. }
  24656. return ret;
  24657. },
  24658. /**
  24659. * Create the Axis instances based on the config options.
  24660. *
  24661. * @private
  24662. * @function Highcharts.Chart#getAxes
  24663. *
  24664. * @fires Highcharts.Chart#event:afterGetAxes
  24665. * @fires Highcharts.Chart#event:getAxes
  24666. */
  24667. getAxes: function () {
  24668. var chart = this,
  24669. options = this.options,
  24670. xAxisOptions = options.xAxis = splat(options.xAxis || {}),
  24671. yAxisOptions = options.yAxis = splat(options.yAxis || {}),
  24672. optionsArray;
  24673. fireEvent(this, 'getAxes');
  24674. // make sure the options are arrays and add some members
  24675. xAxisOptions.forEach(function (axis, i) {
  24676. axis.index = i;
  24677. axis.isX = true;
  24678. });
  24679. yAxisOptions.forEach(function (axis, i) {
  24680. axis.index = i;
  24681. });
  24682. // concatenate all axis options into one array
  24683. optionsArray = xAxisOptions.concat(yAxisOptions);
  24684. optionsArray.forEach(function (axisOptions) {
  24685. new Axis(chart, axisOptions); // eslint-disable-line no-new
  24686. });
  24687. fireEvent(this, 'afterGetAxes');
  24688. },
  24689. /**
  24690. * Returns an array of all currently selected points in the chart. Points
  24691. * can be selected by clicking or programmatically by the
  24692. * {@link Highcharts.Point#select}
  24693. * function.
  24694. *
  24695. * @sample highcharts/plotoptions/series-allowpointselect-line/
  24696. * Get selected points
  24697. *
  24698. * @function Highcharts.Chart#getSelectedPoints
  24699. *
  24700. * @return {Array<Highcharts.Point>}
  24701. * The currently selected points.
  24702. */
  24703. getSelectedPoints: function () {
  24704. var points = [];
  24705. this.series.forEach(function (serie) {
  24706. // For one-to-one points inspect series.data in order to retrieve
  24707. // points outside the visible range (#6445). For grouped data,
  24708. // inspect the generated series.points.
  24709. points = points.concat(
  24710. (serie[serie.hasGroupedData ? 'points' : 'data'] || []).filter(
  24711. function (point) {
  24712. return point.selected;
  24713. }
  24714. )
  24715. );
  24716. });
  24717. return points;
  24718. },
  24719. /**
  24720. * Returns an array of all currently selected series in the chart. Series
  24721. * can be selected either programmatically by the
  24722. * {@link Highcharts.Series#select}
  24723. * function or by checking the checkbox next to the legend item if
  24724. * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
  24725. * is true.
  24726. *
  24727. * @sample highcharts/members/chart-getselectedseries/
  24728. * Get selected series
  24729. *
  24730. * @function Highcharts.Chart#getSelectedSeries
  24731. *
  24732. * @return {Array<Highcharts.Series>}
  24733. * The currently selected series.
  24734. */
  24735. getSelectedSeries: function () {
  24736. return this.series.filter(function (serie) {
  24737. return serie.selected;
  24738. });
  24739. },
  24740. /**
  24741. * Set a new title or subtitle for the chart.
  24742. *
  24743. * @sample highcharts/members/chart-settitle/
  24744. * Set title text and styles
  24745. *
  24746. * @function Highcharts.Chart#setTitle
  24747. *
  24748. * @param {Highcharts.TitleOptions} titleOptions
  24749. * New title options. The title text itself is set by the
  24750. * `titleOptions.text` property.
  24751. *
  24752. * @param {Highcharts.SubtitleOptions} subtitleOptions
  24753. * New subtitle options. The subtitle text itself is set by the
  24754. * `subtitleOptions.text` property.
  24755. *
  24756. * @param {boolean} redraw
  24757. * Whether to redraw the chart or wait for a later call to
  24758. * `chart.redraw()`.
  24759. */
  24760. setTitle: function (titleOptions, subtitleOptions, redraw) {
  24761. var chart = this,
  24762. options = chart.options,
  24763. styledMode = chart.styledMode,
  24764. chartTitleOptions,
  24765. chartSubtitleOptions;
  24766. chartTitleOptions = options.title = merge(
  24767. // Default styles
  24768. !styledMode && {
  24769. style: {
  24770. color: '#333333',
  24771. fontSize: options.isStock ? '16px' : '18px' // #2944
  24772. }
  24773. },
  24774. options.title,
  24775. titleOptions
  24776. );
  24777. chartSubtitleOptions = options.subtitle = merge(
  24778. // Default styles
  24779. !styledMode && {
  24780. style: {
  24781. color: '#666666'
  24782. }
  24783. },
  24784. options.subtitle,
  24785. subtitleOptions
  24786. );
  24787. // add title and subtitle
  24788. /**
  24789. * The chart title. The title has an `update` method that allows
  24790. * modifying the options directly or indirectly via
  24791. * `chart.update`.
  24792. *
  24793. * @sample highcharts/members/title-update/
  24794. * Updating titles
  24795. *
  24796. * @name Highcharts.Chart#title
  24797. * @type {Highcharts.TitleObject}
  24798. */
  24799. /**
  24800. * The chart subtitle. The subtitle has an `update` method that
  24801. * allows modifying the options directly or indirectly via
  24802. * `chart.update`.
  24803. *
  24804. * @name Highcharts.Chart#subtitle
  24805. * @type {Highcharts.SubtitleObject}
  24806. */
  24807. [
  24808. ['title', titleOptions, chartTitleOptions],
  24809. ['subtitle', subtitleOptions, chartSubtitleOptions]
  24810. ].forEach(function (arr, i) {
  24811. var name = arr[0],
  24812. title = chart[name],
  24813. titleOptions = arr[1],
  24814. chartTitleOptions = arr[2];
  24815. if (title && titleOptions) {
  24816. chart[name] = title = title.destroy(); // remove old
  24817. }
  24818. if (chartTitleOptions && !title) {
  24819. chart[name] = chart.renderer.text(
  24820. chartTitleOptions.text,
  24821. 0,
  24822. 0,
  24823. chartTitleOptions.useHTML
  24824. )
  24825. .attr({
  24826. align: chartTitleOptions.align,
  24827. 'class': 'highcharts-' + name,
  24828. zIndex: chartTitleOptions.zIndex || 4
  24829. })
  24830. .add();
  24831. // Update methods, shortcut to Chart.setTitle
  24832. chart[name].update = function (o) {
  24833. chart.setTitle(!i && o, i && o);
  24834. };
  24835. // Presentational
  24836. if (!styledMode) {
  24837. chart[name].css(chartTitleOptions.style);
  24838. }
  24839. }
  24840. });
  24841. chart.layOutTitles(redraw);
  24842. },
  24843. /**
  24844. * Internal function to lay out the chart titles and cache the full offset
  24845. * height for use in `getMargins`. The result is stored in
  24846. * `this.titleOffset`.
  24847. *
  24848. * @private
  24849. * @function Highcharts.Chart#layOutTitles
  24850. *
  24851. * @param {boolean} [redraw=true]
  24852. */
  24853. layOutTitles: function (redraw) {
  24854. var titleOffset = 0,
  24855. requiresDirtyBox,
  24856. renderer = this.renderer,
  24857. spacingBox = this.spacingBox;
  24858. // Lay out the title and the subtitle respectively
  24859. ['title', 'subtitle'].forEach(function (key) {
  24860. var title = this[key],
  24861. titleOptions = this.options[key],
  24862. offset = key === 'title' ? -3 :
  24863. // Floating subtitle (#6574)
  24864. titleOptions.verticalAlign ? 0 : titleOffset + 2,
  24865. titleSize;
  24866. if (title) {
  24867. if (!this.styledMode) {
  24868. titleSize = titleOptions.style.fontSize;
  24869. }
  24870. titleSize = renderer.fontMetrics(titleSize, title).b;
  24871. title
  24872. .css({
  24873. width: (titleOptions.width ||
  24874. spacingBox.width + titleOptions.widthAdjust) + 'px'
  24875. })
  24876. .align(extend({
  24877. y: offset + titleSize
  24878. }, titleOptions), false, 'spacingBox');
  24879. if (!titleOptions.floating && !titleOptions.verticalAlign) {
  24880. titleOffset = Math.ceil(
  24881. titleOffset +
  24882. // Skip the cache for HTML (#3481)
  24883. title.getBBox(titleOptions.useHTML).height
  24884. );
  24885. }
  24886. }
  24887. }, this);
  24888. requiresDirtyBox = this.titleOffset !== titleOffset;
  24889. this.titleOffset = titleOffset; // used in getMargins
  24890. if (!this.isDirtyBox && requiresDirtyBox) {
  24891. this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
  24892. // Redraw if necessary (#2719, #2744)
  24893. if (this.hasRendered && pick(redraw, true) && this.isDirtyBox) {
  24894. this.redraw();
  24895. }
  24896. }
  24897. },
  24898. /**
  24899. * Internal function to get the chart width and height according to options
  24900. * and container size. Sets
  24901. * {@link Chart.chartWidth} and
  24902. * {@link Chart.chartHeight}.
  24903. *
  24904. * @function Highcharts.Chart#getChartSize
  24905. */
  24906. getChartSize: function () {
  24907. var chart = this,
  24908. optionsChart = chart.options.chart,
  24909. widthOption = optionsChart.width,
  24910. heightOption = optionsChart.height,
  24911. renderTo = chart.renderTo;
  24912. // Get inner width and height
  24913. if (!defined(widthOption)) {
  24914. chart.containerWidth = H.getStyle(renderTo, 'width');
  24915. }
  24916. if (!defined(heightOption)) {
  24917. chart.containerHeight = H.getStyle(renderTo, 'height');
  24918. }
  24919. /**
  24920. * The current pixel width of the chart.
  24921. *
  24922. * @name Highcharts.Chart#chartWidth
  24923. * @type {number}
  24924. */
  24925. chart.chartWidth = Math.max( // #1393
  24926. 0,
  24927. widthOption || chart.containerWidth || 600 // #1460
  24928. );
  24929. /**
  24930. * The current pixel height of the chart.
  24931. *
  24932. * @name Highcharts.Chart#chartHeight
  24933. * @type {number}
  24934. */
  24935. chart.chartHeight = Math.max(
  24936. 0,
  24937. H.relativeLength(
  24938. heightOption,
  24939. chart.chartWidth
  24940. ) ||
  24941. (chart.containerHeight > 1 ? chart.containerHeight : 400)
  24942. );
  24943. },
  24944. /**
  24945. * If the renderTo element has no offsetWidth, most likely one or more of
  24946. * its parents are hidden. Loop up the DOM tree to temporarily display the
  24947. * parents, then save the original display properties, and when the true
  24948. * size is retrieved, reset them. Used on first render and on redraws.
  24949. *
  24950. * @private
  24951. * @function Highcharts.Chart#temporaryDisplay
  24952. *
  24953. * @param {boolean} revert
  24954. * Revert to the saved original styles.
  24955. */
  24956. temporaryDisplay: function (revert) {
  24957. var node = this.renderTo,
  24958. tempStyle;
  24959. if (!revert) {
  24960. while (node && node.style) {
  24961. // When rendering to a detached node, it needs to be temporarily
  24962. // attached in order to read styling and bounding boxes (#5783,
  24963. // #7024).
  24964. if (!doc.body.contains(node) && !node.parentNode) {
  24965. node.hcOrigDetached = true;
  24966. doc.body.appendChild(node);
  24967. }
  24968. if (
  24969. H.getStyle(node, 'display', false) === 'none' ||
  24970. node.hcOricDetached
  24971. ) {
  24972. node.hcOrigStyle = {
  24973. display: node.style.display,
  24974. height: node.style.height,
  24975. overflow: node.style.overflow
  24976. };
  24977. tempStyle = {
  24978. display: 'block',
  24979. overflow: 'hidden'
  24980. };
  24981. if (node !== this.renderTo) {
  24982. tempStyle.height = 0;
  24983. }
  24984. H.css(node, tempStyle);
  24985. // If it still doesn't have an offset width after setting
  24986. // display to block, it probably has an !important priority
  24987. // #2631, 6803
  24988. if (!node.offsetWidth) {
  24989. node.style.setProperty('display', 'block', 'important');
  24990. }
  24991. }
  24992. node = node.parentNode;
  24993. if (node === doc.body) {
  24994. break;
  24995. }
  24996. }
  24997. } else {
  24998. while (node && node.style) {
  24999. if (node.hcOrigStyle) {
  25000. H.css(node, node.hcOrigStyle);
  25001. delete node.hcOrigStyle;
  25002. }
  25003. if (node.hcOrigDetached) {
  25004. doc.body.removeChild(node);
  25005. node.hcOrigDetached = false;
  25006. }
  25007. node = node.parentNode;
  25008. }
  25009. }
  25010. },
  25011. /**
  25012. * Set the {@link Chart.container|chart container's} class name, in
  25013. * addition to `highcharts-container`.
  25014. *
  25015. * @function Highcharts.Chart#setClassName
  25016. *
  25017. * @param {string} className
  25018. */
  25019. setClassName: function (className) {
  25020. this.container.className = 'highcharts-container ' + (className || '');
  25021. },
  25022. /**
  25023. * Get the containing element, determine the size and create the inner
  25024. * container div to hold the chart.
  25025. *
  25026. * @private
  25027. * @function Highcharts.Chart#afterGetContainer
  25028. *
  25029. * @fires Highcharts.Chart#event:afterGetContainer
  25030. */
  25031. getContainer: function () {
  25032. var chart = this,
  25033. container,
  25034. options = chart.options,
  25035. optionsChart = options.chart,
  25036. chartWidth,
  25037. chartHeight,
  25038. renderTo = chart.renderTo,
  25039. indexAttrName = 'data-highcharts-chart',
  25040. oldChartIndex,
  25041. Ren,
  25042. containerId = H.uniqueKey(),
  25043. containerStyle,
  25044. key;
  25045. if (!renderTo) {
  25046. chart.renderTo = renderTo = optionsChart.renderTo;
  25047. }
  25048. if (isString(renderTo)) {
  25049. chart.renderTo = renderTo = doc.getElementById(renderTo);
  25050. }
  25051. // Display an error if the renderTo is wrong
  25052. if (!renderTo) {
  25053. H.error(13, true, chart);
  25054. }
  25055. // If the container already holds a chart, destroy it. The check for
  25056. // hasRendered is there because web pages that are saved to disk from
  25057. // the browser, will preserve the data-highcharts-chart attribute and
  25058. // the SVG contents, but not an interactive chart. So in this case,
  25059. // charts[oldChartIndex] will point to the wrong chart if any (#2609).
  25060. oldChartIndex = pInt(attr(renderTo, indexAttrName));
  25061. if (
  25062. isNumber(oldChartIndex) &&
  25063. charts[oldChartIndex] &&
  25064. charts[oldChartIndex].hasRendered
  25065. ) {
  25066. charts[oldChartIndex].destroy();
  25067. }
  25068. // Make a reference to the chart from the div
  25069. attr(renderTo, indexAttrName, chart.index);
  25070. // remove previous chart
  25071. renderTo.innerHTML = '';
  25072. // If the container doesn't have an offsetWidth, it has or is a child of
  25073. // a node that has display:none. We need to temporarily move it out to a
  25074. // visible state to determine the size, else the legend and tooltips
  25075. // won't render properly. The skipClone option is used in sparklines as
  25076. // a micro optimization, saving about 1-2 ms each chart.
  25077. if (!optionsChart.skipClone && !renderTo.offsetWidth) {
  25078. chart.temporaryDisplay();
  25079. }
  25080. // get the width and height
  25081. chart.getChartSize();
  25082. chartWidth = chart.chartWidth;
  25083. chartHeight = chart.chartHeight;
  25084. // Allow table cells and flex-boxes to shrink without the chart blocking
  25085. // them out (#6427)
  25086. css(renderTo, { overflow: 'hidden' });
  25087. // Create the inner container
  25088. if (!chart.styledMode) {
  25089. containerStyle = extend({
  25090. position: 'relative',
  25091. // needed for context menu (avoidscrollbars) and content
  25092. // overflow in IE
  25093. overflow: 'hidden',
  25094. width: chartWidth + 'px',
  25095. height: chartHeight + 'px',
  25096. textAlign: 'left',
  25097. lineHeight: 'normal', // #427
  25098. zIndex: 0, // #1072
  25099. '-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
  25100. }, optionsChart.style);
  25101. }
  25102. /**
  25103. * The containing HTML element of the chart. The container is
  25104. * dynamically inserted into the element given as the `renderTo`
  25105. * parameter in the {@link Highcharts#chart} constructor.
  25106. *
  25107. * @name Highcharts.Chart#container
  25108. * @type {Highcharts.HTMLDOMElement}
  25109. */
  25110. container = createElement(
  25111. 'div',
  25112. {
  25113. id: containerId
  25114. },
  25115. containerStyle,
  25116. renderTo
  25117. );
  25118. chart.container = container;
  25119. // cache the cursor (#1650)
  25120. chart._cursor = container.style.cursor;
  25121. // Initialize the renderer
  25122. Ren = H[optionsChart.renderer] || H.Renderer;
  25123. /**
  25124. * The renderer instance of the chart. Each chart instance has only one
  25125. * associated renderer.
  25126. *
  25127. * @name Highcharts.Chart#renderer
  25128. * @type {Highcharts.SVGRenderer}
  25129. */
  25130. chart.renderer = new Ren(
  25131. container,
  25132. chartWidth,
  25133. chartHeight,
  25134. null,
  25135. optionsChart.forExport,
  25136. options.exporting && options.exporting.allowHTML,
  25137. chart.styledMode
  25138. );
  25139. chart.setClassName(optionsChart.className);
  25140. if (!chart.styledMode) {
  25141. chart.renderer.setStyle(optionsChart.style);
  25142. } else {
  25143. // Initialize definitions
  25144. for (key in options.defs) {
  25145. this.renderer.definition(options.defs[key]);
  25146. }
  25147. }
  25148. // Add a reference to the charts index
  25149. chart.renderer.chartIndex = chart.index;
  25150. fireEvent(this, 'afterGetContainer');
  25151. },
  25152. /**
  25153. * Calculate margins by rendering axis labels in a preliminary position.
  25154. * Title, subtitle and legend have already been rendered at this stage, but
  25155. * will be moved into their final positions.
  25156. *
  25157. * @private
  25158. * @function Highcharts.Chart#getMargins
  25159. *
  25160. * @param {boolean} skipAxes
  25161. *
  25162. * @fires Highcharts.Chart#event:getMargins
  25163. */
  25164. getMargins: function (skipAxes) {
  25165. var chart = this,
  25166. spacing = chart.spacing,
  25167. margin = chart.margin,
  25168. titleOffset = chart.titleOffset;
  25169. chart.resetMargins();
  25170. // Adjust for title and subtitle
  25171. if (titleOffset && !defined(margin[0])) {
  25172. chart.plotTop = Math.max(
  25173. chart.plotTop,
  25174. titleOffset + chart.options.title.margin + spacing[0]
  25175. );
  25176. }
  25177. // Adjust for legend
  25178. if (chart.legend && chart.legend.display) {
  25179. chart.legend.adjustMargins(margin, spacing);
  25180. }
  25181. fireEvent(this, 'getMargins');
  25182. if (!skipAxes) {
  25183. this.getAxisMargins();
  25184. }
  25185. },
  25186. /**
  25187. * @private
  25188. * @function Highcharts.Chart#getAxisMargins
  25189. */
  25190. getAxisMargins: function () {
  25191. var chart = this,
  25192. // [top, right, bottom, left]
  25193. axisOffset = chart.axisOffset = [0, 0, 0, 0],
  25194. margin = chart.margin;
  25195. // pre-render axes to get labels offset width
  25196. if (chart.hasCartesianSeries) {
  25197. chart.axes.forEach(function (axis) {
  25198. if (axis.visible) {
  25199. axis.getOffset();
  25200. }
  25201. });
  25202. }
  25203. // Add the axis offsets
  25204. marginNames.forEach(function (m, side) {
  25205. if (!defined(margin[side])) {
  25206. chart[m] += axisOffset[side];
  25207. }
  25208. });
  25209. chart.setChartSize();
  25210. },
  25211. /**
  25212. * Reflows the chart to its container. By default, the chart reflows
  25213. * automatically to its container following a `window.resize` event, as per
  25214. * the [chart.reflow](https://api.highcharts/highcharts/chart.reflow)
  25215. * option. However, there are no reliable events for div resize, so if the
  25216. * container is resized without a window resize event, this must be called
  25217. * explicitly.
  25218. *
  25219. * @sample highcharts/members/chart-reflow/
  25220. * Resize div and reflow
  25221. * @sample highcharts/chart/events-container/
  25222. * Pop up and reflow
  25223. *
  25224. * @function Highcharts.Chart#reflow
  25225. *
  25226. * @param {global.Event} [e]
  25227. * Event arguments. Used primarily when the function is called
  25228. * internally as a response to window resize.
  25229. */
  25230. reflow: function (e) {
  25231. var chart = this,
  25232. optionsChart = chart.options.chart,
  25233. renderTo = chart.renderTo,
  25234. hasUserSize = (
  25235. defined(optionsChart.width) &&
  25236. defined(optionsChart.height)
  25237. ),
  25238. width = optionsChart.width || H.getStyle(renderTo, 'width'),
  25239. height = optionsChart.height || H.getStyle(renderTo, 'height'),
  25240. target = e ? e.target : win;
  25241. // Width and height checks for display:none. Target is doc in IE8 and
  25242. // Opera, win in Firefox, Chrome and IE9.
  25243. if (
  25244. !hasUserSize &&
  25245. !chart.isPrinting &&
  25246. width &&
  25247. height &&
  25248. (target === win || target === doc)
  25249. ) {
  25250. if (
  25251. width !== chart.containerWidth ||
  25252. height !== chart.containerHeight
  25253. ) {
  25254. H.clearTimeout(chart.reflowTimeout);
  25255. // When called from window.resize, e is set, else it's called
  25256. // directly (#2224)
  25257. chart.reflowTimeout = syncTimeout(function () {
  25258. // Set size, it may have been destroyed in the meantime
  25259. // (#1257)
  25260. if (chart.container) {
  25261. chart.setSize(undefined, undefined, false);
  25262. }
  25263. }, e ? 100 : 0);
  25264. }
  25265. chart.containerWidth = width;
  25266. chart.containerHeight = height;
  25267. }
  25268. },
  25269. /**
  25270. * Toggle the event handlers necessary for auto resizing, depending on the
  25271. * `chart.reflow` option.
  25272. *
  25273. * @private
  25274. * @function Highcharts.Chart#setReflow
  25275. *
  25276. * @param {boolean} reflow
  25277. */
  25278. setReflow: function (reflow) {
  25279. var chart = this;
  25280. if (reflow !== false && !this.unbindReflow) {
  25281. this.unbindReflow = addEvent(win, 'resize', function (e) {
  25282. chart.reflow(e);
  25283. });
  25284. addEvent(this, 'destroy', this.unbindReflow);
  25285. } else if (reflow === false && this.unbindReflow) {
  25286. // Unbind and unset
  25287. this.unbindReflow = this.unbindReflow();
  25288. }
  25289. // The following will add listeners to re-fit the chart before and after
  25290. // printing (#2284). However it only works in WebKit. Should have worked
  25291. // in Firefox, but not supported in IE.
  25292. /*
  25293. if (win.matchMedia) {
  25294. win.matchMedia('print').addListener(function reflow() {
  25295. chart.reflow();
  25296. });
  25297. }
  25298. //*/
  25299. },
  25300. /**
  25301. * Resize the chart to a given width and height. In order to set the width
  25302. * only, the height argument may be skipped. To set the height only, pass
  25303. * `undefined` for the width.
  25304. *
  25305. * @sample highcharts/members/chart-setsize-button/
  25306. * Test resizing from buttons
  25307. * @sample highcharts/members/chart-setsize-jquery-resizable/
  25308. * Add a jQuery UI resizable
  25309. * @sample stock/members/chart-setsize/
  25310. * Highstock with UI resizable
  25311. *
  25312. * @function Highcharts.Chart#setSize
  25313. *
  25314. * @param {number|null} [width]
  25315. * The new pixel width of the chart. Since v4.2.6, the argument can
  25316. * be `undefined` in order to preserve the current value (when
  25317. * setting height only), or `null` to adapt to the width of the
  25318. * containing element.
  25319. *
  25320. * @param {number|null} [height]
  25321. * The new pixel height of the chart. Since v4.2.6, the argument can
  25322. * be `undefined` in order to preserve the current value, or `null`
  25323. * in order to adapt to the height of the containing element.
  25324. *
  25325. * @param {Highcharts.AnimationOptionsObject} [animation=true]
  25326. * Whether and how to apply animation.
  25327. *
  25328. * @fires Highcharts.Chart#event:endResize
  25329. * @fires Highcharts.Chart#event:resize
  25330. */
  25331. setSize: function (width, height, animation) {
  25332. var chart = this,
  25333. renderer = chart.renderer,
  25334. globalAnimation;
  25335. // Handle the isResizing counter
  25336. chart.isResizing += 1;
  25337. // set the animation for the current process
  25338. H.setAnimation(animation, chart);
  25339. chart.oldChartHeight = chart.chartHeight;
  25340. chart.oldChartWidth = chart.chartWidth;
  25341. if (width !== undefined) {
  25342. chart.options.chart.width = width;
  25343. }
  25344. if (height !== undefined) {
  25345. chart.options.chart.height = height;
  25346. }
  25347. chart.getChartSize();
  25348. // Resize the container with the global animation applied if enabled
  25349. // (#2503)
  25350. if (!chart.styledMode) {
  25351. globalAnimation = renderer.globalAnimation;
  25352. (globalAnimation ? animate : css)(chart.container, {
  25353. width: chart.chartWidth + 'px',
  25354. height: chart.chartHeight + 'px'
  25355. }, globalAnimation);
  25356. }
  25357. chart.setChartSize(true);
  25358. renderer.setSize(chart.chartWidth, chart.chartHeight, animation);
  25359. // handle axes
  25360. chart.axes.forEach(function (axis) {
  25361. axis.isDirty = true;
  25362. axis.setScale();
  25363. });
  25364. chart.isDirtyLegend = true; // force legend redraw
  25365. chart.isDirtyBox = true; // force redraw of plot and chart border
  25366. chart.layOutTitles(); // #2857
  25367. chart.getMargins();
  25368. chart.redraw(animation);
  25369. chart.oldChartHeight = null;
  25370. fireEvent(chart, 'resize');
  25371. // Fire endResize and set isResizing back. If animation is disabled,
  25372. // fire without delay
  25373. syncTimeout(function () {
  25374. if (chart) {
  25375. fireEvent(chart, 'endResize', null, function () {
  25376. chart.isResizing -= 1;
  25377. });
  25378. }
  25379. }, animObject(globalAnimation).duration);
  25380. },
  25381. /**
  25382. * Set the public chart properties. This is done before and after the
  25383. * pre-render to determine margin sizes.
  25384. *
  25385. * @private
  25386. * @function Highcharts.Chart#setChartSize
  25387. *
  25388. * @param {boolean} skipAxes
  25389. *
  25390. * @fires Highcharts.Chart#event:afterSetChartSize
  25391. */
  25392. setChartSize: function (skipAxes) {
  25393. var chart = this,
  25394. inverted = chart.inverted,
  25395. renderer = chart.renderer,
  25396. chartWidth = chart.chartWidth,
  25397. chartHeight = chart.chartHeight,
  25398. optionsChart = chart.options.chart,
  25399. spacing = chart.spacing,
  25400. clipOffset = chart.clipOffset,
  25401. clipX,
  25402. clipY,
  25403. plotLeft,
  25404. plotTop,
  25405. plotWidth,
  25406. plotHeight,
  25407. plotBorderWidth;
  25408. /**
  25409. * The current left position of the plot area in pixels.
  25410. *
  25411. * @name Highcharts.Chart#plotLeft
  25412. * @type {number}
  25413. */
  25414. chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
  25415. /**
  25416. * The current top position of the plot area in pixels.
  25417. *
  25418. * @name Highcharts.Chart#plotTop
  25419. * @type {number}
  25420. */
  25421. chart.plotTop = plotTop = Math.round(chart.plotTop);
  25422. /**
  25423. * The current width of the plot area in pixels.
  25424. *
  25425. * @name Highcharts.Chart#plotWidth
  25426. * @type {number}
  25427. */
  25428. chart.plotWidth = plotWidth = Math.max(
  25429. 0,
  25430. Math.round(chartWidth - plotLeft - chart.marginRight)
  25431. );
  25432. /**
  25433. * The current height of the plot area in pixels.
  25434. *
  25435. * @name Highcharts.Chart#plotHeight
  25436. * @type {number}
  25437. */
  25438. chart.plotHeight = plotHeight = Math.max(
  25439. 0,
  25440. Math.round(chartHeight - plotTop - chart.marginBottom)
  25441. );
  25442. chart.plotSizeX = inverted ? plotHeight : plotWidth;
  25443. chart.plotSizeY = inverted ? plotWidth : plotHeight;
  25444. chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
  25445. // Set boxes used for alignment
  25446. chart.spacingBox = renderer.spacingBox = {
  25447. x: spacing[3],
  25448. y: spacing[0],
  25449. width: chartWidth - spacing[3] - spacing[1],
  25450. height: chartHeight - spacing[0] - spacing[2]
  25451. };
  25452. chart.plotBox = renderer.plotBox = {
  25453. x: plotLeft,
  25454. y: plotTop,
  25455. width: plotWidth,
  25456. height: plotHeight
  25457. };
  25458. plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2);
  25459. clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2);
  25460. clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
  25461. chart.clipBox = {
  25462. x: clipX,
  25463. y: clipY,
  25464. width: Math.floor(
  25465. chart.plotSizeX -
  25466. Math.max(plotBorderWidth, clipOffset[1]) / 2 -
  25467. clipX
  25468. ),
  25469. height: Math.max(
  25470. 0,
  25471. Math.floor(
  25472. chart.plotSizeY -
  25473. Math.max(plotBorderWidth, clipOffset[2]) / 2 -
  25474. clipY
  25475. )
  25476. )
  25477. };
  25478. if (!skipAxes) {
  25479. chart.axes.forEach(function (axis) {
  25480. axis.setAxisSize();
  25481. axis.setAxisTranslation();
  25482. });
  25483. }
  25484. fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
  25485. },
  25486. /**
  25487. * Initial margins before auto size margins are applied.
  25488. *
  25489. * @private
  25490. * @function Highcharts.Chart#resetMargins
  25491. */
  25492. resetMargins: function () {
  25493. fireEvent(this, 'resetMargins');
  25494. var chart = this,
  25495. chartOptions = chart.options.chart;
  25496. // Create margin and spacing array
  25497. ['margin', 'spacing'].forEach(function splashArrays(target) {
  25498. var value = chartOptions[target],
  25499. values = isObject(value) ? value : [value, value, value, value];
  25500. [
  25501. 'Top',
  25502. 'Right',
  25503. 'Bottom',
  25504. 'Left'
  25505. ].forEach(function (sideName, side) {
  25506. chart[target][side] = pick(
  25507. chartOptions[target + sideName],
  25508. values[side]
  25509. );
  25510. });
  25511. });
  25512. // Set margin names like chart.plotTop, chart.plotLeft,
  25513. // chart.marginRight, chart.marginBottom.
  25514. marginNames.forEach(function (m, side) {
  25515. chart[m] = pick(chart.margin[side], chart.spacing[side]);
  25516. });
  25517. chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
  25518. chart.clipOffset = [0, 0, 0, 0];
  25519. },
  25520. /**
  25521. * Internal function to draw or redraw the borders and backgrounds for chart
  25522. * and plot area.
  25523. *
  25524. * @private
  25525. * @function Highcharts.Chart#drawChartBox
  25526. *
  25527. * @fires Highcharts.Chart#event:afterDrawChartBox
  25528. */
  25529. drawChartBox: function () {
  25530. var chart = this,
  25531. optionsChart = chart.options.chart,
  25532. renderer = chart.renderer,
  25533. chartWidth = chart.chartWidth,
  25534. chartHeight = chart.chartHeight,
  25535. chartBackground = chart.chartBackground,
  25536. plotBackground = chart.plotBackground,
  25537. plotBorder = chart.plotBorder,
  25538. chartBorderWidth,
  25539. styledMode = chart.styledMode,
  25540. plotBGImage = chart.plotBGImage,
  25541. chartBackgroundColor = optionsChart.backgroundColor,
  25542. plotBackgroundColor = optionsChart.plotBackgroundColor,
  25543. plotBackgroundImage = optionsChart.plotBackgroundImage,
  25544. mgn,
  25545. bgAttr,
  25546. plotLeft = chart.plotLeft,
  25547. plotTop = chart.plotTop,
  25548. plotWidth = chart.plotWidth,
  25549. plotHeight = chart.plotHeight,
  25550. plotBox = chart.plotBox,
  25551. clipRect = chart.clipRect,
  25552. clipBox = chart.clipBox,
  25553. verb = 'animate';
  25554. // Chart area
  25555. if (!chartBackground) {
  25556. chart.chartBackground = chartBackground = renderer.rect()
  25557. .addClass('highcharts-background')
  25558. .add();
  25559. verb = 'attr';
  25560. }
  25561. if (!styledMode) {
  25562. // Presentational
  25563. chartBorderWidth = optionsChart.borderWidth || 0;
  25564. mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
  25565. bgAttr = {
  25566. fill: chartBackgroundColor || 'none'
  25567. };
  25568. if (chartBorderWidth || chartBackground['stroke-width']) { // #980
  25569. bgAttr.stroke = optionsChart.borderColor;
  25570. bgAttr['stroke-width'] = chartBorderWidth;
  25571. }
  25572. chartBackground
  25573. .attr(bgAttr)
  25574. .shadow(optionsChart.shadow);
  25575. } else {
  25576. chartBorderWidth = mgn = chartBackground.strokeWidth();
  25577. }
  25578. chartBackground[verb]({
  25579. x: mgn / 2,
  25580. y: mgn / 2,
  25581. width: chartWidth - mgn - chartBorderWidth % 2,
  25582. height: chartHeight - mgn - chartBorderWidth % 2,
  25583. r: optionsChart.borderRadius
  25584. });
  25585. // Plot background
  25586. verb = 'animate';
  25587. if (!plotBackground) {
  25588. verb = 'attr';
  25589. chart.plotBackground = plotBackground = renderer.rect()
  25590. .addClass('highcharts-plot-background')
  25591. .add();
  25592. }
  25593. plotBackground[verb](plotBox);
  25594. if (!styledMode) {
  25595. // Presentational attributes for the background
  25596. plotBackground
  25597. .attr({
  25598. fill: plotBackgroundColor || 'none'
  25599. })
  25600. .shadow(optionsChart.plotShadow);
  25601. // Create the background image
  25602. if (plotBackgroundImage) {
  25603. if (!plotBGImage) {
  25604. chart.plotBGImage = renderer.image(
  25605. plotBackgroundImage,
  25606. plotLeft,
  25607. plotTop,
  25608. plotWidth,
  25609. plotHeight
  25610. ).add();
  25611. } else {
  25612. plotBGImage.animate(plotBox);
  25613. }
  25614. }
  25615. }
  25616. // Plot clip
  25617. if (!clipRect) {
  25618. chart.clipRect = renderer.clipRect(clipBox);
  25619. } else {
  25620. clipRect.animate({
  25621. width: clipBox.width,
  25622. height: clipBox.height
  25623. });
  25624. }
  25625. // Plot area border
  25626. verb = 'animate';
  25627. if (!plotBorder) {
  25628. verb = 'attr';
  25629. chart.plotBorder = plotBorder = renderer.rect()
  25630. .addClass('highcharts-plot-border')
  25631. .attr({
  25632. zIndex: 1 // Above the grid
  25633. })
  25634. .add();
  25635. }
  25636. if (!styledMode) {
  25637. // Presentational
  25638. plotBorder.attr({
  25639. stroke: optionsChart.plotBorderColor,
  25640. 'stroke-width': optionsChart.plotBorderWidth || 0,
  25641. fill: 'none'
  25642. });
  25643. }
  25644. plotBorder[verb](plotBorder.crisp({
  25645. x: plotLeft,
  25646. y: plotTop,
  25647. width: plotWidth,
  25648. height: plotHeight
  25649. }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
  25650. // reset
  25651. chart.isDirtyBox = false;
  25652. fireEvent(this, 'afterDrawChartBox');
  25653. },
  25654. /**
  25655. * Detect whether a certain chart property is needed based on inspecting its
  25656. * options and series. This mainly applies to the chart.inverted property,
  25657. * and in extensions to the chart.angular and chart.polar properties.
  25658. *
  25659. * @private
  25660. * @function Highcharts.Chart#propFromSeries
  25661. */
  25662. propFromSeries: function () {
  25663. var chart = this,
  25664. optionsChart = chart.options.chart,
  25665. klass,
  25666. seriesOptions = chart.options.series,
  25667. i,
  25668. value;
  25669. ['inverted', 'angular', 'polar'].forEach(function (key) {
  25670. // The default series type's class
  25671. klass = seriesTypes[optionsChart.type ||
  25672. optionsChart.defaultSeriesType];
  25673. // Get the value from available chart-wide properties
  25674. value =
  25675. optionsChart[key] || // It is set in the options
  25676. (klass && klass.prototype[key]); // The default series class
  25677. // requires it
  25678. // 4. Check if any the chart's series require it
  25679. i = seriesOptions && seriesOptions.length;
  25680. while (!value && i--) {
  25681. klass = seriesTypes[seriesOptions[i].type];
  25682. if (klass && klass.prototype[key]) {
  25683. value = true;
  25684. }
  25685. }
  25686. // Set the chart property
  25687. chart[key] = value;
  25688. });
  25689. },
  25690. /**
  25691. * Internal function to link two or more series together, based on the
  25692. * `linkedTo` option. This is done from `Chart.render`, and after
  25693. * `Chart.addSeries` and `Series.remove`.
  25694. *
  25695. * @private
  25696. * @function Highcharts.Chart#linkSeries
  25697. *
  25698. * @fires Highcharts.Chart#event:afterLinkSeries
  25699. */
  25700. linkSeries: function () {
  25701. var chart = this,
  25702. chartSeries = chart.series;
  25703. // Reset links
  25704. chartSeries.forEach(function (series) {
  25705. series.linkedSeries.length = 0;
  25706. });
  25707. // Apply new links
  25708. chartSeries.forEach(function (series) {
  25709. var linkedTo = series.options.linkedTo;
  25710. if (isString(linkedTo)) {
  25711. if (linkedTo === ':previous') {
  25712. linkedTo = chart.series[series.index - 1];
  25713. } else {
  25714. linkedTo = chart.get(linkedTo);
  25715. }
  25716. // #3341 avoid mutual linking
  25717. if (linkedTo && linkedTo.linkedParent !== series) {
  25718. linkedTo.linkedSeries.push(series);
  25719. series.linkedParent = linkedTo;
  25720. series.visible = pick(
  25721. series.options.visible,
  25722. linkedTo.options.visible,
  25723. series.visible
  25724. ); // #3879
  25725. }
  25726. }
  25727. });
  25728. fireEvent(this, 'afterLinkSeries');
  25729. },
  25730. /**
  25731. * Render series for the chart.
  25732. *
  25733. * @private
  25734. * @function Highcharts.Chart#renderSeries
  25735. */
  25736. renderSeries: function () {
  25737. this.series.forEach(function (serie) {
  25738. serie.translate();
  25739. serie.render();
  25740. });
  25741. },
  25742. /**
  25743. * Render labels for the chart.
  25744. *
  25745. * @private
  25746. * @function Highcharts.Chart#renderLabels
  25747. */
  25748. renderLabels: function () {
  25749. var chart = this,
  25750. labels = chart.options.labels;
  25751. if (labels.items) {
  25752. labels.items.forEach(function (label) {
  25753. var style = extend(labels.style, label.style),
  25754. x = pInt(style.left) + chart.plotLeft,
  25755. y = pInt(style.top) + chart.plotTop + 12;
  25756. // delete to prevent rewriting in IE
  25757. delete style.left;
  25758. delete style.top;
  25759. chart.renderer.text(
  25760. label.html,
  25761. x,
  25762. y
  25763. )
  25764. .attr({ zIndex: 2 })
  25765. .css(style)
  25766. .add();
  25767. });
  25768. }
  25769. },
  25770. /**
  25771. * Render all graphics for the chart. Runs internally on initialization.
  25772. *
  25773. * @private
  25774. * @function Highcharts.Chart#render
  25775. */
  25776. render: function () {
  25777. var chart = this,
  25778. axes = chart.axes,
  25779. renderer = chart.renderer,
  25780. options = chart.options,
  25781. correction = 0, // correction for X axis labels
  25782. tempWidth,
  25783. tempHeight,
  25784. redoHorizontal,
  25785. redoVertical;
  25786. // Title
  25787. chart.setTitle();
  25788. /**
  25789. * The overview of the chart's series.
  25790. *
  25791. * @name Highcharts.Chart#legend
  25792. * @type {Highcharts.Legend}
  25793. */
  25794. chart.legend = new Legend(chart, options.legend);
  25795. // Get stacks
  25796. if (chart.getStacks) {
  25797. chart.getStacks();
  25798. }
  25799. // Get chart margins
  25800. chart.getMargins(true);
  25801. chart.setChartSize();
  25802. // Record preliminary dimensions for later comparison
  25803. tempWidth = chart.plotWidth;
  25804. axes.some(function (axis) {
  25805. if (
  25806. axis.horiz &&
  25807. axis.visible &&
  25808. axis.options.labels.enabled &&
  25809. axis.series.length
  25810. ) {
  25811. // 21 is the most common correction for X axis labels
  25812. correction = 21;
  25813. return true;
  25814. }
  25815. });
  25816. // use Math.max to prevent negative plotHeight
  25817. chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
  25818. tempHeight = chart.plotHeight;
  25819. // Get margins by pre-rendering axes
  25820. axes.forEach(function (axis) {
  25821. axis.setScale();
  25822. });
  25823. chart.getAxisMargins();
  25824. // If the plot area size has changed significantly, calculate tick
  25825. // positions again
  25826. redoHorizontal = tempWidth / chart.plotWidth > 1.1;
  25827. // Height is more sensitive, use lower threshold
  25828. redoVertical = tempHeight / chart.plotHeight > 1.05;
  25829. if (redoHorizontal || redoVertical) {
  25830. axes.forEach(function (axis) {
  25831. if (
  25832. (axis.horiz && redoHorizontal) ||
  25833. (!axis.horiz && redoVertical)
  25834. ) {
  25835. // update to reflect the new margins
  25836. axis.setTickInterval(true);
  25837. }
  25838. });
  25839. chart.getMargins(); // second pass to check for new labels
  25840. }
  25841. // Draw the borders and backgrounds
  25842. chart.drawChartBox();
  25843. // Axes
  25844. if (chart.hasCartesianSeries) {
  25845. axes.forEach(function (axis) {
  25846. if (axis.visible) {
  25847. axis.render();
  25848. }
  25849. });
  25850. }
  25851. // The series
  25852. if (!chart.seriesGroup) {
  25853. chart.seriesGroup = renderer.g('series-group')
  25854. .attr({ zIndex: 3 })
  25855. .add();
  25856. }
  25857. chart.renderSeries();
  25858. // Labels
  25859. chart.renderLabels();
  25860. // Credits
  25861. chart.addCredits();
  25862. // Handle responsiveness
  25863. if (chart.setResponsive) {
  25864. chart.setResponsive();
  25865. }
  25866. // Set flag
  25867. chart.hasRendered = true;
  25868. },
  25869. /**
  25870. * Set a new credits label for the chart.
  25871. *
  25872. * @sample highcharts/credits/credits-update/
  25873. * Add and update credits
  25874. *
  25875. * @function Highcharts.Chart#addCredits
  25876. *
  25877. * @param {Highcharts.CreditsOptions} options
  25878. * A configuration object for the new credits.
  25879. */
  25880. addCredits: function (credits) {
  25881. var chart = this;
  25882. credits = merge(true, this.options.credits, credits);
  25883. if (credits.enabled && !this.credits) {
  25884. /**
  25885. * The chart's credits label. The label has an `update` method that
  25886. * allows setting new options as per the
  25887. * [credits options set](https://api.highcharts.com/highcharts/credits).
  25888. *
  25889. * @name Highcharts.Chart#credits
  25890. * @type {Highcharts.SVGElement}
  25891. */
  25892. this.credits = this.renderer.text(
  25893. credits.text + (this.mapCredits || ''),
  25894. 0,
  25895. 0
  25896. )
  25897. .addClass('highcharts-credits')
  25898. .on('click', function () {
  25899. if (credits.href) {
  25900. win.location.href = credits.href;
  25901. }
  25902. })
  25903. .attr({
  25904. align: credits.position.align,
  25905. zIndex: 8
  25906. });
  25907. if (!chart.styledMode) {
  25908. this.credits.css(credits.style);
  25909. }
  25910. this.credits
  25911. .add()
  25912. .align(credits.position);
  25913. // Dynamically update
  25914. this.credits.update = function (options) {
  25915. chart.credits = chart.credits.destroy();
  25916. chart.addCredits(options);
  25917. };
  25918. }
  25919. },
  25920. /**
  25921. * Remove the chart and purge memory. This method is called internally
  25922. * before adding a second chart into the same container, as well as on
  25923. * window unload to prevent leaks.
  25924. *
  25925. * @sample highcharts/members/chart-destroy/
  25926. * Destroy the chart from a button
  25927. * @sample stock/members/chart-destroy/
  25928. * Destroy with Highstock
  25929. *
  25930. * @function Highcharts.Chart#destroy
  25931. *
  25932. * @fires Highcharts.Chart#event:destroy
  25933. */
  25934. destroy: function () {
  25935. var chart = this,
  25936. axes = chart.axes,
  25937. series = chart.series,
  25938. container = chart.container,
  25939. i,
  25940. parentNode = container && container.parentNode;
  25941. // fire the chart.destoy event
  25942. fireEvent(chart, 'destroy');
  25943. // Delete the chart from charts lookup array
  25944. if (chart.renderer.forExport) {
  25945. H.erase(charts, chart); // #6569
  25946. } else {
  25947. charts[chart.index] = undefined;
  25948. }
  25949. H.chartCount--;
  25950. chart.renderTo.removeAttribute('data-highcharts-chart');
  25951. // remove events
  25952. removeEvent(chart);
  25953. // ==== Destroy collections:
  25954. // Destroy axes
  25955. i = axes.length;
  25956. while (i--) {
  25957. axes[i] = axes[i].destroy();
  25958. }
  25959. // Destroy scroller & scroller series before destroying base series
  25960. if (this.scroller && this.scroller.destroy) {
  25961. this.scroller.destroy();
  25962. }
  25963. // Destroy each series
  25964. i = series.length;
  25965. while (i--) {
  25966. series[i] = series[i].destroy();
  25967. }
  25968. // ==== Destroy chart properties:
  25969. [
  25970. 'title', 'subtitle', 'chartBackground', 'plotBackground',
  25971. 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
  25972. 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
  25973. 'renderer'
  25974. ].forEach(function (name) {
  25975. var prop = chart[name];
  25976. if (prop && prop.destroy) {
  25977. chart[name] = prop.destroy();
  25978. }
  25979. });
  25980. // Remove container and all SVG, check container as it can break in IE
  25981. // when destroyed before finished loading
  25982. if (container) {
  25983. container.innerHTML = '';
  25984. removeEvent(container);
  25985. if (parentNode) {
  25986. discardElement(container);
  25987. }
  25988. }
  25989. // clean it all up
  25990. objectEach(chart, function (val, key) {
  25991. delete chart[key];
  25992. });
  25993. },
  25994. /**
  25995. * Prepare for first rendering after all data are loaded.
  25996. *
  25997. * @private
  25998. * @function Highcharts.Chart#firstRender
  25999. *
  26000. * @fires Highcharts.Chart#event:beforeRender
  26001. */
  26002. firstRender: function () {
  26003. var chart = this,
  26004. options = chart.options;
  26005. // Hook for oldIE to check whether the chart is ready to render
  26006. if (chart.isReadyToRender && !chart.isReadyToRender()) {
  26007. return;
  26008. }
  26009. // Create the container
  26010. chart.getContainer();
  26011. chart.resetMargins();
  26012. chart.setChartSize();
  26013. // Set the common chart properties (mainly invert) from the given series
  26014. chart.propFromSeries();
  26015. // get axes
  26016. chart.getAxes();
  26017. // Initialize the series
  26018. (H.isArray(options.series) ? options.series : []).forEach( // #9680
  26019. function (serieOptions) {
  26020. chart.initSeries(serieOptions);
  26021. }
  26022. );
  26023. chart.linkSeries();
  26024. // Run an event after axes and series are initialized, but before
  26025. // render. At this stage, the series data is indexed and cached in the
  26026. // xData and yData arrays, so we can access those before rendering. Used
  26027. // in Highstock.
  26028. fireEvent(chart, 'beforeRender');
  26029. // depends on inverted and on margins being set
  26030. if (Pointer) {
  26031. /**
  26032. * The Pointer that keeps track of mouse and touch interaction.
  26033. *
  26034. * @memberof Highcharts.Chart
  26035. * @name pointer
  26036. * @type {Highcharts.Pointer}
  26037. * @instance
  26038. */
  26039. chart.pointer = new Pointer(chart, options);
  26040. }
  26041. chart.render();
  26042. // Fire the load event if there are no external images
  26043. if (!chart.renderer.imgCount && chart.onload) {
  26044. chart.onload();
  26045. }
  26046. // If the chart was rendered outside the top container, put it back in
  26047. // (#3679)
  26048. chart.temporaryDisplay(true);
  26049. },
  26050. /**
  26051. * Internal function that runs on chart load, async if any images are loaded
  26052. * in the chart. Runs the callbacks and triggers the `load` and `render`
  26053. * events.
  26054. *
  26055. * @private
  26056. * @function Highcharts.Chart#onload
  26057. *
  26058. * @fires Highcharts.Chart#event:load
  26059. * @fires Highcharts.Chart#event:render
  26060. */
  26061. onload: function () {
  26062. // Run callbacks
  26063. [this.callback].concat(this.callbacks).forEach(function (fn) {
  26064. // Chart destroyed in its own callback (#3600)
  26065. if (fn && this.index !== undefined) {
  26066. fn.apply(this, [this]);
  26067. }
  26068. }, this);
  26069. fireEvent(this, 'load');
  26070. fireEvent(this, 'render');
  26071. // Set up auto resize, check for not destroyed (#6068)
  26072. if (defined(this.index)) {
  26073. this.setReflow(this.options.chart.reflow);
  26074. }
  26075. // Don't run again
  26076. this.onload = null;
  26077. }
  26078. }); // end Chart
  26079. }(Highcharts));
  26080. (function (H) {
  26081. /**
  26082. * (c) 2010-2019 Torstein Honsi
  26083. *
  26084. * License: www.highcharts.com/license
  26085. *
  26086. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  26087. * horizontally on mobile devices. Supports left and right side axes.
  26088. */
  26089. var addEvent = H.addEvent,
  26090. Chart = H.Chart;
  26091. /**
  26092. * Options for a scrollable plot area. This feature provides a minimum width for
  26093. * the plot area of the chart. If the width gets smaller than this, typically
  26094. * on mobile devices, a native browser scrollbar is presented below the chart.
  26095. * This scrollbar provides smooth scrolling for the contents of the plot area,
  26096. * whereas the title, legend and axes are fixed.
  26097. *
  26098. * @sample {highcharts} highcharts/chart/scrollable-plotarea
  26099. * Scrollable plot area
  26100. *
  26101. * @since 6.1.0
  26102. * @product highcharts gantt
  26103. * @apioption chart.scrollablePlotArea
  26104. */
  26105. /**
  26106. * The minimum width for the plot area. If it gets smaller than this, the plot
  26107. * area will become scrollable.
  26108. *
  26109. * @type {number}
  26110. * @apioption chart.scrollablePlotArea.minWidth
  26111. */
  26112. /**
  26113. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  26114. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  26115. * Typically we would use 1 if the chart has right aligned Y axes.
  26116. *
  26117. * @type {number}
  26118. * @apioption chart.scrollablePlotArea.scrollPositionX
  26119. */
  26120. addEvent(Chart, 'afterSetChartSize', function (e) {
  26121. var scrollablePlotArea = this.options.chart.scrollablePlotArea,
  26122. scrollableMinWidth =
  26123. scrollablePlotArea && scrollablePlotArea.minWidth,
  26124. scrollablePixels;
  26125. if (scrollableMinWidth && !this.renderer.forExport) {
  26126. // The amount of pixels to scroll, the difference between chart
  26127. // width and scrollable width
  26128. this.scrollablePixels = scrollablePixels = Math.max(
  26129. 0,
  26130. scrollableMinWidth - this.chartWidth
  26131. );
  26132. if (scrollablePixels) {
  26133. this.plotWidth += scrollablePixels;
  26134. this.clipBox.width += scrollablePixels;
  26135. if (!e.skipAxes) {
  26136. this.axes.forEach(function (axis) {
  26137. if (axis.side === 1) {
  26138. // Get the plot lines right in getPlotLinePath,
  26139. // temporarily set it to the adjusted plot width.
  26140. axis.getPlotLinePath = function () {
  26141. var right = this.right,
  26142. path;
  26143. this.right = right - axis.chart.scrollablePixels;
  26144. path = H.Axis.prototype.getPlotLinePath.apply(
  26145. this,
  26146. arguments
  26147. );
  26148. this.right = right;
  26149. return path;
  26150. };
  26151. } else {
  26152. // Apply the corrected plotWidth
  26153. axis.setAxisSize();
  26154. axis.setAxisTranslation();
  26155. }
  26156. });
  26157. }
  26158. }
  26159. }
  26160. });
  26161. addEvent(Chart, 'render', function () {
  26162. if (this.scrollablePixels) {
  26163. if (this.setUpScrolling) {
  26164. this.setUpScrolling();
  26165. }
  26166. this.applyFixed();
  26167. } else if (this.fixedDiv) { // Has been in scrollable mode
  26168. this.applyFixed();
  26169. }
  26170. });
  26171. /**
  26172. * @private
  26173. * @function Highcharts.Chart#setUpScrolling
  26174. */
  26175. Chart.prototype.setUpScrolling = function () {
  26176. // Add the necessary divs to provide scrolling
  26177. this.scrollingContainer = H.createElement('div', {
  26178. 'className': 'highcharts-scrolling'
  26179. }, {
  26180. overflowX: 'auto',
  26181. WebkitOverflowScrolling: 'touch'
  26182. }, this.renderTo);
  26183. this.innerContainer = H.createElement('div', {
  26184. 'className': 'highcharts-inner-container'
  26185. }, null, this.scrollingContainer);
  26186. // Now move the container inside
  26187. this.innerContainer.appendChild(this.container);
  26188. // Don't run again
  26189. this.setUpScrolling = null;
  26190. };
  26191. /**
  26192. * @private
  26193. * @function Highcharts.Chart#applyFixed
  26194. */
  26195. Chart.prototype.applyFixed = function () {
  26196. var container = this.container,
  26197. fixedRenderer,
  26198. scrollableWidth,
  26199. firstTime = !this.fixedDiv;
  26200. // First render
  26201. if (firstTime) {
  26202. this.fixedDiv = H.createElement(
  26203. 'div',
  26204. {
  26205. className: 'highcharts-fixed'
  26206. },
  26207. {
  26208. position: 'absolute',
  26209. overflow: 'hidden',
  26210. pointerEvents: 'none',
  26211. zIndex: 2
  26212. },
  26213. null,
  26214. true
  26215. );
  26216. this.renderTo.insertBefore(
  26217. this.fixedDiv,
  26218. this.renderTo.firstChild
  26219. );
  26220. this.renderTo.style.overflow = 'visible';
  26221. this.fixedRenderer = fixedRenderer = new H.Renderer(
  26222. this.fixedDiv,
  26223. 0,
  26224. 0
  26225. );
  26226. // Mask
  26227. this.scrollableMask = fixedRenderer.path()
  26228. .attr({
  26229. fill: H.color(
  26230. this.options.chart.backgroundColor || '#fff'
  26231. ).setOpacity(0.85).get(),
  26232. zIndex: -1
  26233. })
  26234. .addClass('highcharts-scrollable-mask')
  26235. .add();
  26236. // These elements are moved over to the fixed renderer and stay fixed
  26237. // when the user scrolls the chart.
  26238. ([
  26239. this.inverted ?
  26240. '.highcharts-xaxis' :
  26241. '.highcharts-yaxis',
  26242. this.inverted ?
  26243. '.highcharts-xaxis-labels' :
  26244. '.highcharts-yaxis-labels',
  26245. '.highcharts-contextbutton',
  26246. '.highcharts-credits',
  26247. '.highcharts-legend',
  26248. '.highcharts-subtitle',
  26249. '.highcharts-title',
  26250. '.highcharts-legend-checkbox'
  26251. ]).forEach(function (className) {
  26252. [].forEach.call(
  26253. container.querySelectorAll(className),
  26254. function (elem) {
  26255. (
  26256. elem.namespaceURI === fixedRenderer.SVG_NS ?
  26257. fixedRenderer.box :
  26258. fixedRenderer.box.parentNode
  26259. ).appendChild(elem);
  26260. elem.style.pointerEvents = 'auto';
  26261. }
  26262. );
  26263. });
  26264. }
  26265. // Set the size of the fixed renderer to the visible width
  26266. this.fixedRenderer.setSize(
  26267. this.chartWidth,
  26268. this.chartHeight
  26269. );
  26270. // Increase the size of the scrollable renderer and background
  26271. scrollableWidth = this.chartWidth + this.scrollablePixels;
  26272. H.stop(this.container);
  26273. this.container.style.width = scrollableWidth + 'px';
  26274. this.renderer.boxWrapper.attr({
  26275. width: scrollableWidth,
  26276. height: this.chartHeight,
  26277. viewBox: [0, 0, scrollableWidth, this.chartHeight].join(' ')
  26278. });
  26279. this.chartBackground.attr({ width: scrollableWidth });
  26280. // Set scroll position
  26281. if (firstTime) {
  26282. var options = this.options.chart.scrollablePlotArea;
  26283. if (options.scrollPositionX) {
  26284. this.scrollingContainer.scrollLeft =
  26285. this.scrollablePixels * options.scrollPositionX;
  26286. }
  26287. }
  26288. // Mask behind the left and right side
  26289. var axisOffset = this.axisOffset,
  26290. maskTop = this.plotTop - axisOffset[0] - 1,
  26291. maskBottom = this.plotTop + this.plotHeight + axisOffset[2],
  26292. maskPlotRight = this.plotLeft + this.plotWidth -
  26293. this.scrollablePixels;
  26294. this.scrollableMask.attr({
  26295. d: this.scrollablePixels ? [
  26296. // Left side
  26297. 'M', 0, maskTop,
  26298. 'L', this.plotLeft - 1, maskTop,
  26299. 'L', this.plotLeft - 1, maskBottom,
  26300. 'L', 0, maskBottom,
  26301. 'Z',
  26302. // Right side
  26303. 'M', maskPlotRight, maskTop,
  26304. 'L', this.chartWidth, maskTop,
  26305. 'L', this.chartWidth, maskBottom,
  26306. 'L', maskPlotRight, maskBottom,
  26307. 'Z'
  26308. ] : ['M', 0, 0]
  26309. });
  26310. };
  26311. }(Highcharts));
  26312. (function (Highcharts) {
  26313. /**
  26314. * (c) 2010-2019 Torstein Honsi
  26315. *
  26316. * License: www.highcharts.com/license
  26317. */
  26318. /**
  26319. * Configuration hash for the data label and tooltip formatters.
  26320. *
  26321. * @interface Highcharts.PointLabelObject
  26322. *//**
  26323. * The point's current color.
  26324. * @name Highcharts.PointLabelObject#color
  26325. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  26326. *//**
  26327. * The point's current color index, used in styled mode instead of `color`. The
  26328. * color index is inserted in class names used for styling.
  26329. * @name Highcharts.PointLabelObject#colorIndex
  26330. * @type {number}
  26331. *//**
  26332. * The name of the related point.
  26333. * @name Highcharts.PointLabelObject#key
  26334. * @type {number|string}
  26335. *//**
  26336. * The percentage for related points in a stacked series or pies.
  26337. * @name Highcharts.PointLabelObject#percentage
  26338. * @type {number}
  26339. *//**
  26340. * The related point.
  26341. * @name Highcharts.PointLabelObject#point
  26342. * @type {Highcharts.Point}
  26343. *//**
  26344. * The related series.
  26345. * @name Highcharts.PointLabelObject#series
  26346. * @type {Highcharts.Series}
  26347. *//**
  26348. * The total of values in either a stack for stacked series, or a pie in a pie
  26349. * series.
  26350. * @name Highcharts.PointLabelObject#total
  26351. * @type {number}
  26352. *//**
  26353. * For categorized axes this property holds the category name for the point. For
  26354. * other axes it holds the X value.
  26355. * @name Highcharts.PointLabelObject#x
  26356. * @type {number|string}
  26357. *//**
  26358. * The y value of the point.
  26359. * @name Highcharts.PointLabelObject#y
  26360. * @type {number|undefined}
  26361. */
  26362. var Point,
  26363. H = Highcharts,
  26364. extend = H.extend,
  26365. erase = H.erase,
  26366. fireEvent = H.fireEvent,
  26367. format = H.format,
  26368. isArray = H.isArray,
  26369. isNumber = H.isNumber,
  26370. pick = H.pick,
  26371. uniqueKey = H.uniqueKey,
  26372. defined = H.defined,
  26373. removeEvent = H.removeEvent;
  26374. /**
  26375. * The Point object. The point objects are generated from the `series.data`
  26376. * configuration objects or raw numbers. They can be accessed from the
  26377. * `Series.points` array. Other ways to instantiate points are through {@link
  26378. * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
  26379. *
  26380. * @class
  26381. * @name Highcharts.Point
  26382. */
  26383. Highcharts.Point = Point = function () {};
  26384. Highcharts.Point.prototype = {
  26385. /**
  26386. * Initialize the point. Called internally based on the `series.data`
  26387. * option.
  26388. *
  26389. * @function Highcharts.Point#init
  26390. *
  26391. * @param {Highcharts.Series} series
  26392. * The series object containing this point.
  26393. *
  26394. * @param {number|object|Array<number|string>|null} options
  26395. * The data in either number, array or object format.
  26396. *
  26397. * @param {number} [x]
  26398. * Optionally, the X value of the point.
  26399. *
  26400. * @return {Highcharts.Point}
  26401. * The Point instance.
  26402. *
  26403. * @fires Highcharts.Point#event:afterInit
  26404. */
  26405. init: function (series, options, x) {
  26406. var point = this,
  26407. colors,
  26408. optionsChart = series.chart.options.chart,
  26409. colorCount = optionsChart.colorCount,
  26410. styledMode = series.chart.styledMode,
  26411. colorIndex;
  26412. /**
  26413. * The series object associated with the point.
  26414. *
  26415. * @name Highcharts.Point#series
  26416. * @type {Highcharts.Series}
  26417. */
  26418. point.series = series;
  26419. /**
  26420. * The point's current color.
  26421. *
  26422. * @name Highcharts.Point#color
  26423. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  26424. */
  26425. if (!styledMode) {
  26426. point.color = series.color; // #3445
  26427. }
  26428. point.applyOptions(options, x);
  26429. // Add a unique ID to the point if none is assigned
  26430. point.id = defined(point.id) ? point.id : uniqueKey();
  26431. if (series.options.colorByPoint) {
  26432. if (!styledMode) {
  26433. colors = series.options.colors || series.chart.options.colors;
  26434. point.color = point.color || colors[series.colorCounter];
  26435. colorCount = colors.length;
  26436. }
  26437. colorIndex = series.colorCounter;
  26438. series.colorCounter++;
  26439. // loop back to zero
  26440. if (series.colorCounter === colorCount) {
  26441. series.colorCounter = 0;
  26442. }
  26443. } else {
  26444. colorIndex = series.colorIndex;
  26445. }
  26446. /**
  26447. * The point's current color index, used in styled mode instead of
  26448. * `color`. The color index is inserted in class names used for styling.
  26449. *
  26450. * @name Highcharts.Point#colorIndex
  26451. * @type {number}
  26452. */
  26453. point.colorIndex = pick(point.colorIndex, colorIndex);
  26454. series.chart.pointCount++;
  26455. fireEvent(point, 'afterInit');
  26456. return point;
  26457. },
  26458. /**
  26459. * Apply the options containing the x and y data and possible some extra
  26460. * properties. Called on point init or from point.update.
  26461. *
  26462. * @private
  26463. * @function Highcharts.Point#applyOptions
  26464. *
  26465. * @param {number|object|Array<number|string>|null} options
  26466. * The point options as defined in series.data.
  26467. *
  26468. * @param {number} [x]
  26469. * Optionally, the x value.
  26470. *
  26471. * @return {Highcharts.Point}
  26472. * The Point instance.
  26473. */
  26474. applyOptions: function (options, x) {
  26475. var point = this,
  26476. series = point.series,
  26477. pointValKey = series.options.pointValKey || series.pointValKey;
  26478. options = Point.prototype.optionsToObject.call(this, options);
  26479. // copy options directly to point
  26480. extend(point, options);
  26481. /**
  26482. * The point's options as applied in the initial configuration, or
  26483. * extended through `Point.update`.
  26484. * @name Highcharts.Point#options
  26485. * @type {object}
  26486. */
  26487. point.options = point.options ?
  26488. extend(point.options, options) :
  26489. options;
  26490. // Since options are copied into the Point instance, some accidental
  26491. // options must be shielded (#5681)
  26492. if (options.group) {
  26493. delete point.group;
  26494. }
  26495. if (options.dataLabels) {
  26496. delete point.dataLabels;
  26497. }
  26498. /**
  26499. * The y value of the point.
  26500. * @name Highcharts.Point#y
  26501. * @type {number|undefined}
  26502. */
  26503. // For higher dimension series types. For instance, for ranges, point.y
  26504. // is mapped to point.low.
  26505. if (pointValKey) {
  26506. point.y = point[pointValKey];
  26507. }
  26508. point.isNull = pick(
  26509. point.isValid && !point.isValid(),
  26510. point.x === null || !isNumber(point.y, true)
  26511. ); // #3571, check for NaN
  26512. // The point is initially selected by options (#5777)
  26513. if (point.selected) {
  26514. point.state = 'select';
  26515. }
  26516. /**
  26517. * The x value of the point.
  26518. * @name Highcharts.Point#x
  26519. * @type {number}
  26520. */
  26521. // If no x is set by now, get auto incremented value. All points must
  26522. // have an x value, however the y value can be null to create a gap in
  26523. // the series
  26524. if (
  26525. 'name' in point &&
  26526. x === undefined &&
  26527. series.xAxis &&
  26528. series.xAxis.hasNames
  26529. ) {
  26530. point.x = series.xAxis.nameToX(point);
  26531. }
  26532. if (point.x === undefined && series) {
  26533. if (x === undefined) {
  26534. point.x = series.autoIncrement(point);
  26535. } else {
  26536. point.x = x;
  26537. }
  26538. }
  26539. return point;
  26540. },
  26541. /**
  26542. * Set a value in an object, on the property defined by key. The key
  26543. * supports nested properties using dot notation. The function modifies the
  26544. * input object and does not make a copy.
  26545. *
  26546. * @function Highcharts.Point#setNestedProperty
  26547. *
  26548. * @param {object} object
  26549. * The object to set the value on.
  26550. *
  26551. * @param {*} value
  26552. * The value to set.
  26553. *
  26554. * @param {string} key
  26555. * Key to the property to set.
  26556. *
  26557. * @return {object}
  26558. * The modified object.
  26559. */
  26560. setNestedProperty: function (object, value, key) {
  26561. var nestedKeys = key.split('.');
  26562. nestedKeys.reduce(function (result, key, i, arr) {
  26563. var isLastKey = arr.length - 1 === i;
  26564. result[key] = (
  26565. isLastKey ?
  26566. value :
  26567. (H.isObject(result[key], true) ? result[key] : {})
  26568. );
  26569. return result[key];
  26570. }, object);
  26571. return object;
  26572. },
  26573. /**
  26574. * Transform number or array configs into objects. Used internally to unify
  26575. * the different configuration formats for points. For example, a simple
  26576. * number `10` in a line series will be transformed to `{ y: 10 }`, and an
  26577. * array config like `[1, 10]` in a scatter series will be transformed to
  26578. * `{ x: 1, y: 10 }`.
  26579. *
  26580. * @function Highcharts.Point#optionsToObject
  26581. *
  26582. * @param {number|object|Array<number|string>|null} options
  26583. * The input option.
  26584. *
  26585. * @return {object}
  26586. * Transformed options.
  26587. */
  26588. optionsToObject: function (options) {
  26589. var ret = {},
  26590. series = this.series,
  26591. keys = series.options.keys,
  26592. pointArrayMap = keys || series.pointArrayMap || ['y'],
  26593. valueCount = pointArrayMap.length,
  26594. firstItemType,
  26595. i = 0,
  26596. j = 0;
  26597. if (isNumber(options) || options === null) {
  26598. ret[pointArrayMap[0]] = options;
  26599. } else if (isArray(options)) {
  26600. // with leading x value
  26601. if (!keys && options.length > valueCount) {
  26602. firstItemType = typeof options[0];
  26603. if (firstItemType === 'string') {
  26604. ret.name = options[0];
  26605. } else if (firstItemType === 'number') {
  26606. ret.x = options[0];
  26607. }
  26608. i++;
  26609. }
  26610. while (j < valueCount) {
  26611. // Skip undefined positions for keys
  26612. if (!keys || options[i] !== undefined) {
  26613. if (pointArrayMap[j].indexOf('.') > 0) {
  26614. // Handle nested keys, e.g. ['color.pattern.image']
  26615. // Avoid function call unless necessary.
  26616. H.Point.prototype.setNestedProperty(
  26617. ret, options[i], pointArrayMap[j]
  26618. );
  26619. } else {
  26620. ret[pointArrayMap[j]] = options[i];
  26621. }
  26622. }
  26623. i++;
  26624. j++;
  26625. }
  26626. } else if (typeof options === 'object') {
  26627. ret = options;
  26628. // This is the fastest way to detect if there are individual point
  26629. // dataLabels that need to be considered in drawDataLabels. These
  26630. // can only occur in object configs.
  26631. if (options.dataLabels) {
  26632. series._hasPointLabels = true;
  26633. }
  26634. // Same approach as above for markers
  26635. if (options.marker) {
  26636. series._hasPointMarkers = true;
  26637. }
  26638. }
  26639. return ret;
  26640. },
  26641. /**
  26642. * Get the CSS class names for individual points. Used internally where the
  26643. * returned value is set on every point.
  26644. *
  26645. * @function Highcharts.Point#getClassName
  26646. *
  26647. * @return {string}
  26648. * The class names.
  26649. */
  26650. getClassName: function () {
  26651. return 'highcharts-point' +
  26652. (this.selected ? ' highcharts-point-select' : '') +
  26653. (this.negative ? ' highcharts-negative' : '') +
  26654. (this.isNull ? ' highcharts-null-point' : '') +
  26655. (this.colorIndex !== undefined ? ' highcharts-color-' +
  26656. this.colorIndex : '') +
  26657. (this.options.className ? ' ' + this.options.className : '') +
  26658. (this.zone && this.zone.className ? ' ' +
  26659. this.zone.className.replace('highcharts-negative', '') : '');
  26660. },
  26661. /**
  26662. * In a series with `zones`, return the zone that the point belongs to.
  26663. *
  26664. * @function Highcharts.Point#getZone
  26665. *
  26666. * @return {Highcharts.PlotSeriesZonesOptions}
  26667. * The zone item.
  26668. */
  26669. getZone: function () {
  26670. var series = this.series,
  26671. zones = series.zones,
  26672. zoneAxis = series.zoneAxis || 'y',
  26673. i = 0,
  26674. zone;
  26675. zone = zones[i];
  26676. while (this[zoneAxis] >= zone.value) {
  26677. zone = zones[++i];
  26678. }
  26679. // For resetting or reusing the point (#8100)
  26680. if (!this.nonZonedColor) {
  26681. this.nonZonedColor = this.color;
  26682. }
  26683. if (zone && zone.color && !this.options.color) {
  26684. this.color = zone.color;
  26685. } else {
  26686. this.color = this.nonZonedColor;
  26687. }
  26688. return zone;
  26689. },
  26690. /**
  26691. * Destroy a point to clear memory. Its reference still stays in
  26692. * `series.data`.
  26693. *
  26694. * @private
  26695. * @function Highcharts.Point#destroy
  26696. */
  26697. destroy: function () {
  26698. var point = this,
  26699. series = point.series,
  26700. chart = series.chart,
  26701. hoverPoints = chart.hoverPoints,
  26702. prop;
  26703. chart.pointCount--;
  26704. if (hoverPoints) {
  26705. point.setState();
  26706. erase(hoverPoints, point);
  26707. if (!hoverPoints.length) {
  26708. chart.hoverPoints = null;
  26709. }
  26710. }
  26711. if (point === chart.hoverPoint) {
  26712. point.onMouseOut();
  26713. }
  26714. // Remove all events and elements
  26715. if (point.graphic || point.dataLabel || point.dataLabels) {
  26716. removeEvent(point);
  26717. point.destroyElements();
  26718. }
  26719. if (point.legendItem) { // pies have legend items
  26720. chart.legend.destroyItem(point);
  26721. }
  26722. for (prop in point) {
  26723. point[prop] = null;
  26724. }
  26725. },
  26726. /**
  26727. * Destroy SVG elements associated with the point.
  26728. *
  26729. * @private
  26730. * @function Highcharts.Point#destroyElements
  26731. */
  26732. destroyElements: function () {
  26733. var point = this,
  26734. props = [
  26735. 'graphic',
  26736. 'dataLabel',
  26737. 'dataLabelUpper',
  26738. 'connector',
  26739. 'shadowGroup'
  26740. ],
  26741. prop,
  26742. i = 6;
  26743. while (i--) {
  26744. prop = props[i];
  26745. if (point[prop]) {
  26746. point[prop] = point[prop].destroy();
  26747. }
  26748. }
  26749. // Handle point.dataLabels and point.connectors
  26750. if (point.dataLabels) {
  26751. point.dataLabels.forEach(function (label) {
  26752. if (label.element) {
  26753. label.destroy();
  26754. }
  26755. });
  26756. delete point.dataLabels;
  26757. }
  26758. if (point.connectors) {
  26759. point.connectors.forEach(function (connector) {
  26760. if (connector.element) {
  26761. connector.destroy();
  26762. }
  26763. });
  26764. delete point.connectors;
  26765. }
  26766. },
  26767. /**
  26768. * Return the configuration hash needed for the data label and tooltip
  26769. * formatters.
  26770. *
  26771. * @function Highcharts.Point#getLabelConfig
  26772. *
  26773. * @return {Highcharts.PointLabelObject}
  26774. * Abstract object used in formatters and formats.
  26775. */
  26776. getLabelConfig: function () {
  26777. return {
  26778. x: this.category,
  26779. y: this.y,
  26780. color: this.color,
  26781. colorIndex: this.colorIndex,
  26782. key: this.name || this.category,
  26783. series: this.series,
  26784. point: this,
  26785. percentage: this.percentage,
  26786. total: this.total || this.stackTotal
  26787. };
  26788. },
  26789. /**
  26790. * Extendable method for formatting each point's tooltip line.
  26791. *
  26792. * @function Highcharts.Point#tooltipFormatter
  26793. *
  26794. * @param {string} pointFormat
  26795. * The point format.
  26796. *
  26797. * @return {string}
  26798. * A string to be concatenated in to the common tooltip text.
  26799. */
  26800. tooltipFormatter: function (pointFormat) {
  26801. // Insert options for valueDecimals, valuePrefix, and valueSuffix
  26802. var series = this.series,
  26803. seriesTooltipOptions = series.tooltipOptions,
  26804. valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''),
  26805. valuePrefix = seriesTooltipOptions.valuePrefix || '',
  26806. valueSuffix = seriesTooltipOptions.valueSuffix || '';
  26807. // Replace default point style with class name
  26808. if (series.chart.styledMode) {
  26809. pointFormat = series.chart.tooltip.styledModeFormat(pointFormat);
  26810. }
  26811. // Loop over the point array map and replace unformatted values with
  26812. // sprintf formatting markup
  26813. (series.pointArrayMap || ['y']).forEach(function (key) {
  26814. key = '{point.' + key; // without the closing bracket
  26815. if (valuePrefix || valueSuffix) {
  26816. pointFormat = pointFormat.replace(
  26817. RegExp(key + '}', 'g'),
  26818. valuePrefix + key + '}' + valueSuffix
  26819. );
  26820. }
  26821. pointFormat = pointFormat.replace(
  26822. RegExp(key + '}', 'g'),
  26823. key + ':,.' + valueDecimals + 'f}'
  26824. );
  26825. });
  26826. return format(pointFormat, {
  26827. point: this,
  26828. series: this.series
  26829. }, series.chart.time);
  26830. },
  26831. /**
  26832. * Fire an event on the Point object.
  26833. *
  26834. * @private
  26835. * @function Highcharts.Point#firePointEvent
  26836. *
  26837. * @param {string} eventType
  26838. * Type of the event.
  26839. *
  26840. * @param {object} eventArgs
  26841. * Additional event arguments.
  26842. *
  26843. * @param {Function} defaultFunction
  26844. * Default event handler.
  26845. *
  26846. * @fires Highcharts.Point#event:*
  26847. */
  26848. firePointEvent: function (eventType, eventArgs, defaultFunction) {
  26849. var point = this,
  26850. series = this.series,
  26851. seriesOptions = series.options;
  26852. // load event handlers on demand to save time on mouseover/out
  26853. if (
  26854. seriesOptions.point.events[eventType] ||
  26855. (
  26856. point.options &&
  26857. point.options.events &&
  26858. point.options.events[eventType]
  26859. )
  26860. ) {
  26861. this.importEvents();
  26862. }
  26863. // add default handler if in selection mode
  26864. if (eventType === 'click' && seriesOptions.allowPointSelect) {
  26865. defaultFunction = function (event) {
  26866. // Control key is for Windows, meta (= Cmd key) for Mac, Shift
  26867. // for Opera.
  26868. if (point.select) { // #2911
  26869. point.select(
  26870. null,
  26871. event.ctrlKey || event.metaKey || event.shiftKey
  26872. );
  26873. }
  26874. };
  26875. }
  26876. fireEvent(this, eventType, eventArgs, defaultFunction);
  26877. },
  26878. /**
  26879. * For categorized axes this property holds the category name for the
  26880. * point. For other axes it holds the X value.
  26881. *
  26882. * @name Highcharts.Point#category
  26883. * @type {number|string}
  26884. */
  26885. /**
  26886. * The name of the point. The name can be given as the first position of the
  26887. * point configuration array, or as a `name` property in the configuration:
  26888. *
  26889. * @example
  26890. * // Array config
  26891. * data: [
  26892. * ['John', 1],
  26893. * ['Jane', 2]
  26894. * ]
  26895. *
  26896. * // Object config
  26897. * data: [{
  26898. * name: 'John',
  26899. * y: 1
  26900. * }, {
  26901. * name: 'Jane',
  26902. * y: 2
  26903. * }]
  26904. *
  26905. * @name Highcharts.Point#name
  26906. * @type {string}
  26907. */
  26908. /**
  26909. * The percentage for points in a stacked series or pies.
  26910. *
  26911. * @name Highcharts.Point#percentage
  26912. * @type {number}
  26913. */
  26914. /**
  26915. * The total of values in either a stack for stacked series, or a pie in a
  26916. * pie series.
  26917. *
  26918. * @name Highcharts.Point#total
  26919. * @type {number}
  26920. */
  26921. /**
  26922. * For certain series types, like pie charts, where individual points can
  26923. * be shown or hidden.
  26924. *
  26925. * @name Highcharts.Point#visible
  26926. * @type {boolean}
  26927. */
  26928. visible: true
  26929. };
  26930. }(Highcharts));
  26931. (function (H) {
  26932. /* *
  26933. * (c) 2010-2019 Torstein Honsi
  26934. *
  26935. * License: www.highcharts.com/license
  26936. */
  26937. /**
  26938. * Function callback when a series has been animated.
  26939. *
  26940. * @callback Highcharts.SeriesAfterAnimateCallbackFunction
  26941. *
  26942. * @param {Highcharts.Series} this
  26943. * The series where the event occured.
  26944. *
  26945. * @param {Highcharts.SeriesAfterAnimateEventObject} event
  26946. * Event arguments.
  26947. */
  26948. /**
  26949. * Event information regarding completed animation of a series.
  26950. *
  26951. * @interface Highcharts.SeriesAfterAnimateEventObject
  26952. *//**
  26953. * Animated series.
  26954. * @name Highcharts.SeriesAfterAnimateEventObject#target
  26955. * @type {Highcharts.Series}
  26956. *//**
  26957. * Event type.
  26958. * @name Highcharts.SeriesAfterAnimateEventObject#type
  26959. * @type {"afterAnimate"}
  26960. */
  26961. /**
  26962. * Function callback when the checkbox next to the series' name in the legend is
  26963. * clicked.
  26964. *
  26965. * @callback Highcharts.SeriesCheckboxClickCallbackFunction
  26966. *
  26967. * @param {Highcharts.Series} this
  26968. * The series where the event occured.
  26969. *
  26970. * @param {Highcharts.SeriesCheckboxClickEventObject} event
  26971. * Event arguments.
  26972. */
  26973. /**
  26974. * Event information regarding check of a series box.
  26975. *
  26976. * @interface Highcharts.SeriesCheckboxClickEventObject
  26977. *//**
  26978. * Whether the box has been checked.
  26979. * @name Highcharts.SeriesCheckboxClickEventObject#checked
  26980. * @type {boolean}
  26981. *//**
  26982. * Related series.
  26983. * @name Highcharts.SeriesCheckboxClickEventObject#item
  26984. * @type {Highcharts.Series}
  26985. *//**
  26986. * Related series.
  26987. * @name Highcharts.SeriesCheckboxClickEventObject#target
  26988. * @type {Highcharts.Series}
  26989. *//**
  26990. * Event type.
  26991. * @name Highcharts.SeriesCheckboxClickEventObject#type
  26992. * @type {"checkboxClick"}
  26993. */
  26994. /**
  26995. * Function callback when a series is clicked. Return false to cancel toogle
  26996. * actions.
  26997. *
  26998. * @callback Highcharts.SeriesClickCallbackFunction
  26999. *
  27000. * @param {Highcharts.Series} this
  27001. * The series where the event occured.
  27002. *
  27003. * @param {Highcharts.SeriesClickEventObject} event
  27004. * Event arguments.
  27005. */
  27006. /**
  27007. * Common information for a click event on a series.
  27008. *
  27009. * @interface Highcharts.SeriesClickEventObject
  27010. * @implements {global.Event}
  27011. *//**
  27012. * Nearest point on the graph.
  27013. * @name Highcharts.SeriesClickEventObject#point
  27014. * @type {Highcharts.Point}
  27015. */
  27016. /**
  27017. * @interface Highcharts.SeriesDataLabelsFormatterContextObject
  27018. *//**
  27019. * @name Highcharts.SeriesDataLabelsFormatterContextObject#point
  27020. * @type {Highcharts.Point}
  27021. */
  27022. /**
  27023. * Gets fired when the series is hidden after chart generation time, either by
  27024. * clicking the legend item or by calling `.hide()`.
  27025. *
  27026. * @callback Highcharts.SeriesHideCallbackFunction
  27027. *
  27028. * @param {Highcharts.Series} this
  27029. * The series where the event occured.
  27030. *
  27031. * @param {global.Event} event
  27032. * The event that occured.
  27033. */
  27034. /**
  27035. * Gets fired when the legend item belonging to the series is clicked. The
  27036. * default action is to toggle the visibility of the series. This can be
  27037. * prevented by returning `false` or calling `event.preventDefault()`.
  27038. *
  27039. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  27040. *
  27041. * @param {Highcharts.Series} this
  27042. * The series where the event occured.
  27043. *
  27044. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  27045. * The event that occured.
  27046. */
  27047. /**
  27048. * Information about the event.
  27049. *
  27050. * @interface Highcharts.SeriesLegendItemClickEventObject
  27051. *//**
  27052. * Related browser event.
  27053. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  27054. * @type {Highcharts.PointerEvent}
  27055. *//**
  27056. * Prevent the default action of toggle the visibility of the series.
  27057. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  27058. * @type {Function}
  27059. *//**
  27060. * Related series.
  27061. * @name Highcharts.SeriesCheckboxClickEventObject#target
  27062. * @type {Highcharts.Series}
  27063. *//**
  27064. * Event type.
  27065. * @name Highcharts.SeriesCheckboxClickEventObject#type
  27066. * @type {"checkboxClick"}
  27067. */
  27068. /**
  27069. * Gets fired when the mouse leaves the graph.
  27070. *
  27071. * @callback Highcharts.SeriesMouseOutCallbackFunction
  27072. *
  27073. * @param {Highcharts.Series} this
  27074. * Series where the event occured.
  27075. *
  27076. * @param {global.Event} event
  27077. * Event that occured.
  27078. */
  27079. /**
  27080. * Gets fired when the mouse enters the graph.
  27081. *
  27082. * @callback Highcharts.SeriesMouseOverCallbackFunction
  27083. *
  27084. * @param {Highcharts.Series} this
  27085. * Series where the event occured.
  27086. *
  27087. * @param {global.Event} event
  27088. * Event that occured.
  27089. */
  27090. /**
  27091. * Translation and scale for the plot area of a series.
  27092. *
  27093. * @interface Highcharts.SeriesPlotBoxObject
  27094. *//**
  27095. * @name Highcharts.SeriesPlotBoxObject#translateX
  27096. * @type {number}
  27097. *//**
  27098. * @name Highcharts.SeriesPlotBoxObject#translateY
  27099. * @type {number}
  27100. *//**
  27101. * @name Highcharts.SeriesPlotBoxObject#scaleX
  27102. * @type {number}
  27103. *//**
  27104. * @name Highcharts.SeriesPlotBoxObject#scaleY
  27105. * @type {number}
  27106. */
  27107. /**
  27108. * Function callback when a series point is clicked. Return false to cancel the
  27109. * action.
  27110. *
  27111. * @callback Highcharts.SeriesPointClickCallbackFunction
  27112. *
  27113. * @param {Highcharts.Point} this
  27114. * The point where the event occured.
  27115. *
  27116. * @param {Highcharts.SeriesPointClickEventObject} event
  27117. * Event arguments.
  27118. */
  27119. /**
  27120. * Common information for a click event on a series point.
  27121. *
  27122. * @interface Highcharts.SeriesPointClickEventObject
  27123. * @implements {Highcharts.PointerEventObject}
  27124. *//**
  27125. * Clicked point.
  27126. * @name Highcharts.SeriesPointClickEventObject#point
  27127. * @type {Highcharts.Point}
  27128. */
  27129. /**
  27130. * Gets fired when the mouse leaves the area close to the point.
  27131. *
  27132. * @callback Highcharts.SeriesPointMouseOutCallbackFunction
  27133. *
  27134. * @param {Highcharts.Point} this
  27135. * Point where the event occured.
  27136. *
  27137. * @param {global.Event} event
  27138. * Event that occured.
  27139. */
  27140. /**
  27141. * Gets fired when the mouse enters the area close to the point.
  27142. *
  27143. * @callback Highcharts.SeriesPointMouseOverCallbackFunction
  27144. *
  27145. * @param {Highcharts.Point} this
  27146. * Point where the event occured.
  27147. *
  27148. * @param {global.Event} event
  27149. * Event that occured.
  27150. */
  27151. /**
  27152. * Gets fired when the point is removed using the `.remove()` method.
  27153. *
  27154. * @callback Highcharts.SeriesPointRemoveCallbackFunction
  27155. *
  27156. * @param {Highcharts.Point} this
  27157. * Point where the event occured.
  27158. *
  27159. * @param {global.Event} event
  27160. * Event that occured.
  27161. */
  27162. /**
  27163. * Gets fired when the point is selected either programmatically or following a
  27164. * click on the point.
  27165. *
  27166. * @callback Highcharts.SeriesPointSelectCallbackFunction
  27167. *
  27168. * @param {Highcharts.Point} this
  27169. * Point where the event occured.
  27170. *
  27171. * @param {Highcharts.SeriesPointSelectEventObject} event
  27172. * Event that occured.
  27173. */
  27174. /**
  27175. * Information about the select event.
  27176. *
  27177. * @interface Highcharts.SeriesPointSelectEventObject
  27178. * @implements {global.Event}
  27179. *//**
  27180. * @name Highcharts.SeriesPointSelectEventObject#accumulate
  27181. * @type {boolean}
  27182. */
  27183. /**
  27184. * Fires when the point is unselected either programmatically or following a
  27185. * click on the point.
  27186. *
  27187. * @callback Highcharts.SeriesPointUnselectCallbackFunction
  27188. *
  27189. * @param {Highcharts.Point} this
  27190. * Point where the event occured.
  27191. *
  27192. * @param {Highcharts.SeriesPointUnselectEventObject} event
  27193. * Event that occured.
  27194. */
  27195. /**
  27196. * Information about the unselect event.
  27197. *
  27198. * @interface Highcharts.SeriesPointUnselectEventObject
  27199. * @implements {global.Event}
  27200. *//**
  27201. * @name Highcharts.SeriesPointUnselectEventObject#accumulate
  27202. * @type {boolean}
  27203. */
  27204. /**
  27205. * Gets fired when the point is updated programmatically through the `.update()`
  27206. * method.
  27207. *
  27208. * @callback Highcharts.SeriesPointUpdateCallbackFunction
  27209. *
  27210. * @param {Highcharts.Point} this
  27211. * Point where the event occured.
  27212. *
  27213. * @param {Highcharts.SeriesPointUpdateEventObject} event
  27214. * Event that occured.
  27215. */
  27216. /**
  27217. * Information about the update event.
  27218. *
  27219. * @interface Highcharts.SeriesPointUpdateEventObject
  27220. * @implements {global.Event}
  27221. *//**
  27222. * Options data of the update event.
  27223. * @name Highcharts.SeriesPointUpdateEventObject#options
  27224. * @type {number|object|Array<(number|string)>|null}
  27225. */
  27226. /**
  27227. * Gets fired when the series is shown after chart generation time, either by
  27228. * clicking the legend item or by calling `.show()`.
  27229. *
  27230. * @callback Highcharts.SeriesShowCallbackFunction
  27231. *
  27232. * @param {Highcharts.Series} this
  27233. * Series where the event occured.
  27234. *
  27235. * @param {global.Event} event
  27236. * Event that occured.
  27237. */
  27238. var addEvent = H.addEvent,
  27239. animObject = H.animObject,
  27240. arrayMax = H.arrayMax,
  27241. arrayMin = H.arrayMin,
  27242. correctFloat = H.correctFloat,
  27243. defaultOptions = H.defaultOptions,
  27244. defaultPlotOptions = H.defaultPlotOptions,
  27245. defined = H.defined,
  27246. erase = H.erase,
  27247. extend = H.extend,
  27248. fireEvent = H.fireEvent,
  27249. isArray = H.isArray,
  27250. isNumber = H.isNumber,
  27251. isString = H.isString,
  27252. LegendSymbolMixin = H.LegendSymbolMixin, // @todo add as a requirement
  27253. merge = H.merge,
  27254. objectEach = H.objectEach,
  27255. pick = H.pick,
  27256. Point = H.Point, // @todo add as a requirement
  27257. removeEvent = H.removeEvent,
  27258. splat = H.splat,
  27259. SVGElement = H.SVGElement,
  27260. syncTimeout = H.syncTimeout,
  27261. win = H.win;
  27262. /**
  27263. * This is the base series prototype that all other series types inherit from.
  27264. * A new series is initialized either through the
  27265. * [series](https://api.highcharts.com/highcharts/series)
  27266. * option structure, or after the chart is initialized, through
  27267. * {@link Highcharts.Chart#addSeries}.
  27268. *
  27269. * The object can be accessed in a number of ways. All series and point event
  27270. * handlers give a reference to the `series` object. The chart object has a
  27271. * {@link Highcharts.Chart#series|series} property that is a collection of all
  27272. * the chart's series. The point objects and axis objects also have the same
  27273. * reference.
  27274. *
  27275. * Another way to reference the series programmatically is by `id`. Add an id
  27276. * in the series configuration options, and get the series object by
  27277. * {@link Highcharts.Chart#get}.
  27278. *
  27279. * Configuration options for the series are given in three levels. Options for
  27280. * all series in a chart are given in the
  27281. * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
  27282. * object. Then options for all series of a specific type
  27283. * are given in the plotOptions of that type, for example `plotOptions.line`.
  27284. * Next, options for one single series are given in the series array, or as
  27285. * arguments to `chart.addSeries`.
  27286. *
  27287. * The data in the series is stored in various arrays.
  27288. *
  27289. * - First, `series.options.data` contains all the original config options for
  27290. * each point whether added by options or methods like `series.addPoint`.
  27291. *
  27292. * - Next, `series.data` contains those values converted to points, but in case
  27293. * the series data length exceeds the `cropThreshold`, or if the data is
  27294. * grouped, `series.data` doesn't contain all the points. It only contains the
  27295. * points that have been created on demand.
  27296. *
  27297. * - Then there's `series.points` that contains all currently visible point
  27298. * objects. In case of cropping, the cropped-away points are not part of this
  27299. * array. The `series.points` array starts at `series.cropStart` compared to
  27300. * `series.data` and `series.options.data`. If however the series data is
  27301. * grouped, these can't be correlated one to one.
  27302. *
  27303. * - `series.xData` and `series.processedXData` contain clean x values,
  27304. * equivalent to `series.data` and `series.points`.
  27305. *
  27306. * - `series.yData` and `series.processedYData` contain clean y values,
  27307. * equivalent to `series.data` and `series.points`.
  27308. *
  27309. * @class
  27310. * @name Highcharts.Series
  27311. *
  27312. * @param {Highcharts.Chart} chart
  27313. * The chart instance.
  27314. *
  27315. * @param {Highcharts.SeriesOptionsType|object} options
  27316. * The series options.
  27317. *//**
  27318. * The line series is the base type and is therefor the series base prototype.
  27319. *
  27320. * @private
  27321. * @class
  27322. * @name Highcharts.seriesTypes.line
  27323. *
  27324. * @augments Highcharts.Series
  27325. */
  27326. H.Series = H.seriesType(
  27327. 'line',
  27328. /**
  27329. * Series options for specific data and the data itself. In TypeScript you
  27330. * have to cast the series options to specific series types, to get all
  27331. * possible options for a series.
  27332. *
  27333. * @example
  27334. * // TypeScript example
  27335. * Highcharts.chart('container', {
  27336. * series: [{
  27337. * color: '#06C',
  27338. * data: [[0, 1], [2, 3]]
  27339. * } as Highcharts.SeriesLineOptions ]
  27340. * });
  27341. *
  27342. * @type {Array<*>}
  27343. * @apioption series
  27344. */
  27345. /**
  27346. * An id for the series. This can be used after render time to get a pointer
  27347. * to the series object through `chart.get()`.
  27348. *
  27349. * @sample {highcharts} highcharts/plotoptions/series-id/
  27350. * Get series by id
  27351. *
  27352. * @type {string}
  27353. * @since 1.2.0
  27354. * @apioption series.id
  27355. */
  27356. /**
  27357. * The index of the series in the chart, affecting the internal index in the
  27358. * `chart.series` array, the visible Z index as well as the order in the
  27359. * legend.
  27360. *
  27361. * @type {number}
  27362. * @since 2.3.0
  27363. * @apioption series.index
  27364. */
  27365. /**
  27366. * The sequential index of the series in the legend.
  27367. *
  27368. * @see [legend.reversed](#legend.reversed),
  27369. * [yAxis.reversedStacks](#yAxis.reversedStacks)
  27370. *
  27371. * @sample {highcharts|highstock} highcharts/series/legendindex/
  27372. * Legend in opposite order
  27373. *
  27374. * @type {number}
  27375. * @apioption series.legendIndex
  27376. */
  27377. /**
  27378. * The name of the series as shown in the legend, tooltip etc.
  27379. *
  27380. * @sample {highcharts} highcharts/series/name/
  27381. * Series name
  27382. * @sample {highmaps} maps/demo/category-map/
  27383. * Series name
  27384. *
  27385. * @type {string}
  27386. * @apioption series.name
  27387. */
  27388. /**
  27389. * This option allows grouping series in a stacked chart. The stack option
  27390. * can be a string or anything else, as long as the grouped series' stack
  27391. * options match each other after conversion into a string.
  27392. *
  27393. * @sample {highcharts} highcharts/series/stack/
  27394. * Stacked and grouped columns
  27395. *
  27396. * @type {string|object}
  27397. * @since 2.1
  27398. * @product highcharts highstock
  27399. * @apioption series.stack
  27400. */
  27401. /**
  27402. * The type of series, for example `line` or `column`. By default, the
  27403. * series type is inherited from [chart.type](#chart.type), so unless the
  27404. * chart is a combination of series types, there is no need to set it on the
  27405. * series level.
  27406. *
  27407. * In TypeScript instead the `type` option must always be set.
  27408. *
  27409. * @sample {highcharts} highcharts/series/type/
  27410. * Line and column in the same chart
  27411. * @sample {highmaps} maps/demo/mapline-mappoint/
  27412. * Multiple types in the same map
  27413. *
  27414. * @type {string}
  27415. * @apioption series.type
  27416. */
  27417. /**
  27418. * When using dual or multiple x axes, this number defines which xAxis the
  27419. * particular series is connected to. It refers to either the
  27420. * {@link #xAxis.id|axis id}
  27421. * or the index of the axis in the xAxis array, with 0 being the first.
  27422. *
  27423. * @type {number|string}
  27424. * @default 0
  27425. * @product highcharts highstock
  27426. * @apioption series.xAxis
  27427. */
  27428. /**
  27429. * When using dual or multiple y axes, this number defines which yAxis the
  27430. * particular series is connected to. It refers to either the
  27431. * {@link #yAxis.id|axis id}
  27432. * or the index of the axis in the yAxis array, with 0 being the first.
  27433. *
  27434. * @sample {highcharts} highcharts/series/yaxis/
  27435. * Apply the column series to the secondary Y axis
  27436. *
  27437. * @type {number|string}
  27438. * @default 0
  27439. * @product highcharts highstock
  27440. * @apioption series.yAxis
  27441. */
  27442. /**
  27443. * Define the visual z index of the series.
  27444. *
  27445. * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
  27446. * With no z index, the series defined last are on top
  27447. * @sample {highcharts} highcharts/plotoptions/series-zindex/
  27448. * With a z index, the series with the highest z index is on top
  27449. * @sample {highstock} highcharts/plotoptions/series-zindex-default/
  27450. * With no z index, the series defined last are on top
  27451. * @sample {highstock} highcharts/plotoptions/series-zindex/
  27452. * With a z index, the series with the highest z index is on top
  27453. *
  27454. * @type {number}
  27455. * @product highcharts highstock
  27456. * @apioption series.zIndex
  27457. */
  27458. null,
  27459. /**
  27460. * General options for all series types.
  27461. *
  27462. * @optionparent plotOptions.series
  27463. */
  27464. { // base series options
  27465. /**
  27466. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  27467. * of a line graph. Round means that lines are rounded in the ends and
  27468. * bends.
  27469. *
  27470. * @type {string}
  27471. * @validvalue ["round", "butt", "square"]
  27472. * @default round
  27473. * @since 3.0.7
  27474. * @apioption plotOptions.line.linecap
  27475. */
  27476. /**
  27477. * Pixel width of the graph line.
  27478. *
  27479. * @see In styled mode, the line stroke-width can be set with the
  27480. * `.highcharts-graph` class name.
  27481. *
  27482. * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
  27483. * On all series
  27484. * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
  27485. * On one single series
  27486. *
  27487. * @product highcharts highstock
  27488. */
  27489. lineWidth: 2,
  27490. /**
  27491. * For some series, there is a limit that shuts down initial animation
  27492. * by default when the total number of points in the chart is too high.
  27493. * For example, for a column chart and its derivatives, animation does
  27494. * not run if there is more than 250 points totally. To disable this
  27495. * cap, set `animationLimit` to `Infinity`.
  27496. *
  27497. * @type {number}
  27498. * @apioption plotOptions.series.animationLimit
  27499. */
  27500. /**
  27501. * Allow this series' points to be selected by clicking on the graphic
  27502. * (columns, point markers, pie slices, map areas etc).
  27503. *
  27504. * @see {@link Highcharts.Chart#getSelectedPoints}.
  27505. *
  27506. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
  27507. * Line
  27508. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
  27509. * Column
  27510. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
  27511. * Pie
  27512. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  27513. * Map area
  27514. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  27515. * Map bubble
  27516. *
  27517. * @since 1.2.0
  27518. */
  27519. allowPointSelect: false,
  27520. /**
  27521. * If true, a checkbox is displayed next to the legend item to allow
  27522. * selecting the series. The state of the checkbox is determined by
  27523. * the `selected` option.
  27524. *
  27525. * @productdesc {highmaps}
  27526. * Note that if a `colorAxis` is defined, the color axis is represented
  27527. * in the legend, not the series.
  27528. *
  27529. * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
  27530. * Show select box
  27531. *
  27532. * @since 1.2.0
  27533. */
  27534. showCheckbox: false,
  27535. /**
  27536. * Enable or disable the initial animation when a series is displayed.
  27537. * The animation can also be set as a configuration object. Please
  27538. * note that this option only applies to the initial animation of the
  27539. * series itself. For other animations, see [chart.animation](
  27540. * #chart.animation) and the animation parameter under the API methods.
  27541. * The following properties are supported:
  27542. *
  27543. * - `duration`: The duration of the animation in milliseconds.
  27544. *
  27545. * - `easing`: Can be a string reference to an easing function set on
  27546. * the `Math` object or a function. See the _Custom easing function_
  27547. * demo below.
  27548. *
  27549. * Due to poor performance, animation is disabled in old IE browsers
  27550. * for several chart types.
  27551. *
  27552. * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
  27553. * Animation disabled
  27554. * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
  27555. * Slower animation
  27556. * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
  27557. * Custom easing function
  27558. * @sample {highstock} stock/plotoptions/animation-slower/
  27559. * Slower animation
  27560. * @sample {highstock} stock/plotoptions/animation-easing/
  27561. * Custom easing function
  27562. * @sample {highmaps} maps/plotoptions/series-animation-true/
  27563. * Animation enabled on map series
  27564. * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
  27565. * Disabled on mapbubble series
  27566. *
  27567. * @type {boolean|Highcharts.AnimationOptionsObject}
  27568. * @default {highcharts} true
  27569. * @default {highstock} true
  27570. * @default {highmaps} false
  27571. */
  27572. animation: {
  27573. /**
  27574. * @type {number}
  27575. * @default 1000
  27576. * @apioption plotOptions.series.animation.duration
  27577. */
  27578. duration: 1000
  27579. },
  27580. /**
  27581. * An additional class name to apply to the series' graphical elements.
  27582. * This option does not replace default class names of the graphical
  27583. * element.
  27584. *
  27585. * @type {string}
  27586. * @since 5.0.0
  27587. * @apioption plotOptions.series.className
  27588. */
  27589. /**
  27590. * Disable this option to allow series rendering in the whole plotting
  27591. * area.
  27592. *
  27593. * **Note:** Clipping should be always enabled when
  27594. * [chart.zoomType](#chart.zoomType) is set
  27595. *
  27596. * @sample {highcharts} highcharts/plotoptions/series-clip/
  27597. * Disabled clipping
  27598. *
  27599. * @default true
  27600. * @type {boolean}
  27601. * @since 3.0.0
  27602. * @apioption plotOptions.series.clip
  27603. */
  27604. /**
  27605. * The main color of the series. In line type series it applies to the
  27606. * line and the point markers unless otherwise specified. In bar type
  27607. * series it applies to the bars unless a color is specified per point.
  27608. * The default value is pulled from the `options.colors` array.
  27609. *
  27610. * In styled mode, the color can be defined by the
  27611. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  27612. * color can be set with the `.highcharts-series`,
  27613. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  27614. * `.highcharts-series-{n}` class, or individual classes given by the
  27615. * `className` option.
  27616. *
  27617. * @productdesc {highmaps}
  27618. * In maps, the series color is rarely used, as most choropleth maps use
  27619. * the color to denote the value of each point. The series color can
  27620. * however be used in a map with multiple series holding categorized
  27621. * data.
  27622. *
  27623. * @sample {highcharts} highcharts/plotoptions/series-color-general/
  27624. * General plot option
  27625. * @sample {highcharts} highcharts/plotoptions/series-color-specific/
  27626. * One specific series
  27627. * @sample {highcharts} highcharts/plotoptions/series-color-area/
  27628. * Area color
  27629. * @sample {highcharts} highcharts/series/infographic/
  27630. * Pattern fill
  27631. * @sample {highmaps} maps/demo/category-map/
  27632. * Category map by multiple series
  27633. *
  27634. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  27635. * @apioption plotOptions.series.color
  27636. */
  27637. /**
  27638. * Styled mode only. A specific color index to use for the series, so
  27639. * its graphic representations are given the class name
  27640. * `highcharts-color-{n}`.
  27641. *
  27642. * @type {number}
  27643. * @since 5.0.0
  27644. * @apioption plotOptions.series.colorIndex
  27645. */
  27646. /**
  27647. * Whether to connect a graph line across null points, or render a gap
  27648. * between the two points on either side of the null.
  27649. *
  27650. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
  27651. * False by default
  27652. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
  27653. * True
  27654. *
  27655. * @type {boolean}
  27656. * @default false
  27657. * @product highcharts highstock
  27658. * @apioption plotOptions.series.connectNulls
  27659. */
  27660. /**
  27661. * You can set the cursor to "pointer" if you have click events attached
  27662. * to the series, to signal to the user that the points and lines can
  27663. * be clicked.
  27664. *
  27665. * In styled mode, the series cursor can be set with the same classes
  27666. * as listed under [series.color](#plotOptions.series.color).
  27667. *
  27668. * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
  27669. * On line graph
  27670. * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
  27671. * On columns
  27672. * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
  27673. * On scatter markers
  27674. * @sample {highstock} stock/plotoptions/cursor/
  27675. * Pointer on a line graph
  27676. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  27677. * Map area
  27678. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  27679. * Map bubble
  27680. *
  27681. * @type {string|Highcharts.CursorType}
  27682. * @apioption plotOptions.series.cursor
  27683. */
  27684. /**
  27685. * A name for the dash style to use for the graph, or for some series
  27686. * types the outline of each shape.
  27687. *
  27688. * In styled mode, the
  27689. * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
  27690. * can be set with the same classes as listed under
  27691. * [series.color](#plotOptions.series.color).
  27692. *
  27693. * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
  27694. * Possible values demonstrated
  27695. * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
  27696. * Chart suitable for printing in black and white
  27697. * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
  27698. * Possible values demonstrated
  27699. * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
  27700. * Possible values demonstrated
  27701. * @sample {highmaps} maps/plotoptions/series-dashstyle/
  27702. * Dotted borders on a map
  27703. *
  27704. * @type {Highcharts.DashStyleType}
  27705. * @default Solid
  27706. * @since 2.1
  27707. * @apioption plotOptions.series.dashStyle
  27708. */
  27709. /**
  27710. * Requires the Accessibility module.
  27711. *
  27712. * A description of the series to add to the screen reader information
  27713. * about the series.
  27714. *
  27715. * @type {string}
  27716. * @since 5.0.0
  27717. * @apioption plotOptions.series.description
  27718. */
  27719. /**
  27720. * Enable or disable the mouse tracking for a specific series. This
  27721. * includes point tooltips and click events on graphs and points. For
  27722. * large datasets it improves performance.
  27723. *
  27724. * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
  27725. * No mouse tracking
  27726. * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
  27727. * No mouse tracking
  27728. *
  27729. * @type {boolean}
  27730. * @default true
  27731. * @apioption plotOptions.series.enableMouseTracking
  27732. */
  27733. /**
  27734. * By default, series are exposed to screen readers as regions. By
  27735. * enabling this option, the series element itself will be exposed in
  27736. * the same way as the data points. This is useful if the series is not
  27737. * used as a grouping entity in the chart, but you still want to attach
  27738. * a description to the series.
  27739. *
  27740. * Requires the Accessibility module.
  27741. *
  27742. * @sample highcharts/accessibility/art-grants/
  27743. * Accessible data visualization
  27744. *
  27745. * @type {boolean}
  27746. * @since 5.0.12
  27747. * @apioption plotOptions.series.exposeElementToA11y
  27748. */
  27749. /**
  27750. * Whether to use the Y extremes of the total chart width or only the
  27751. * zoomed area when zooming in on parts of the X axis. By default, the
  27752. * Y axis adjusts to the min and max of the visible data. Cartesian
  27753. * series only.
  27754. *
  27755. * @type {boolean}
  27756. * @default false
  27757. * @since 4.1.6
  27758. * @product highcharts highstock gantt
  27759. * @apioption plotOptions.series.getExtremesFromAll
  27760. */
  27761. /**
  27762. * An array specifying which option maps to which key in the data point
  27763. * array. This makes it convenient to work with unstructured data arrays
  27764. * from different sources.
  27765. *
  27766. * @see [series.data](#series.line.data)
  27767. *
  27768. * @sample {highcharts|highstock} highcharts/series/data-keys/
  27769. * An extended data array with keys
  27770. * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
  27771. * Nested keys used to access object properties
  27772. *
  27773. * @type {Array<string>}
  27774. * @since 4.1.6
  27775. * @apioption plotOptions.series.keys
  27776. */
  27777. /**
  27778. * The line cap used for line ends and line joins on the graph.
  27779. *
  27780. * @type {string}
  27781. * @default round
  27782. * @product highcharts highstock
  27783. * @validvalue ["round", "square"]
  27784. * @apioption plotOptions.series.linecap
  27785. */
  27786. /**
  27787. * The [id](#series.id) of another series to link to. Additionally,
  27788. * the value can be ":previous" to link to the previous series. When
  27789. * two series are linked, only the first one appears in the legend.
  27790. * Toggling the visibility of this also toggles the linked series.
  27791. *
  27792. * @sample {highcharts|highstock} highcharts/demo/arearange-line/
  27793. * Linked series
  27794. *
  27795. * @type {string}
  27796. * @since 3.0
  27797. * @product highcharts highstock gantt
  27798. * @apioption plotOptions.series.linkedTo
  27799. */
  27800. /**
  27801. * Options for the corresponding navigator series if `showInNavigator`
  27802. * is `true` for this series. Available options are the same as any
  27803. * series, documented at [plotOptions](#plotOptions.series) and
  27804. * [series](#series).
  27805. *
  27806. * These options are merged with options in [navigator.series](
  27807. * #navigator.series), and will take precedence if the same option is
  27808. * defined both places.
  27809. *
  27810. * @see [navigator.series](#navigator.series)
  27811. *
  27812. * @type {Highcharts.PlotSeriesOptions}
  27813. * @since 5.0.0
  27814. * @product highstock
  27815. * @apioption plotOptions.series.navigatorOptions
  27816. */
  27817. /**
  27818. * The color for the parts of the graph or points that are below the
  27819. * [threshold](#plotOptions.series.threshold).
  27820. *
  27821. * @see In styled mode, a negative color is applied by setting this option
  27822. * to `true` combined with the `.highcharts-negative` class name.
  27823. *
  27824. * @sample {highcharts} highcharts/plotoptions/series-negative-color/
  27825. * Spline, area and column
  27826. * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
  27827. * Arearange
  27828. * @sample {highcharts} highcharts/css/series-negative-color/
  27829. * Styled mode
  27830. * @sample {highstock} highcharts/plotoptions/series-negative-color/
  27831. * Spline, area and column
  27832. * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
  27833. * Arearange
  27834. * @sample {highmaps} highcharts/plotoptions/series-negative-color/
  27835. * Spline, area and column
  27836. * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
  27837. * Arearange
  27838. *
  27839. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  27840. * @since 3.0
  27841. * @apioption plotOptions.series.negativeColor
  27842. */
  27843. /**
  27844. * Same as
  27845. * [accessibility.pointDescriptionFormatter](#accessibility.pointDescriptionFormatter),
  27846. * but for an individual series. Overrides the chart wide configuration.
  27847. *
  27848. * @type {Function}
  27849. * @since 5.0.12
  27850. * @apioption plotOptions.series.pointDescriptionFormatter
  27851. */
  27852. /**
  27853. * If no x values are given for the points in a series, `pointInterval`
  27854. * defines the interval of the x values. For example, if a series
  27855. * contains one value every decade starting from year 0, set
  27856. * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
  27857. * is set in milliseconds.
  27858. *
  27859. * It can be also be combined with `pointIntervalUnit` to draw irregular
  27860. * time intervals.
  27861. *
  27862. * Please note that this options applies to the _series data_, not the
  27863. * interval of the axis ticks, which is independent.
  27864. *
  27865. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  27866. * Datetime X axis
  27867. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  27868. * Using pointStart and pointInterval
  27869. *
  27870. * @type {number}
  27871. * @default 1
  27872. * @product highcharts highstock gantt
  27873. * @apioption plotOptions.series.pointInterval
  27874. */
  27875. /**
  27876. * On datetime series, this allows for setting the
  27877. * [pointInterval](#plotOptions.series.pointInterval) to irregular time
  27878. * units, `day`, `month` and `year`. A day is usually the same as 24
  27879. * hours, but `pointIntervalUnit` also takes the DST crossover into
  27880. * consideration when dealing with local time. Combine this option with
  27881. * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
  27882. *
  27883. * Please note that this options applies to the _series data_, not the
  27884. * interval of the axis ticks, which is independent.
  27885. *
  27886. * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
  27887. * One point a month
  27888. * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
  27889. * One point a month
  27890. *
  27891. * @type {string}
  27892. * @since 4.1.0
  27893. * @product highcharts highstock gantt
  27894. * @validvalue ["day", "month", "year"]
  27895. * @apioption plotOptions.series.pointIntervalUnit
  27896. */
  27897. /**
  27898. * Possible values: `"on"`, `"between"`, `number`.
  27899. *
  27900. * In a column chart, when pointPlacement is `"on"`, the point will not
  27901. * create any padding of the X axis. In a polar column chart this means
  27902. * that the first column points directly north. If the pointPlacement is
  27903. * `"between"`, the columns will be laid out between ticks. This is
  27904. * useful for example for visualising an amount between two points in
  27905. * time or in a certain sector of a polar chart.
  27906. *
  27907. * Since Highcharts 3.0.2, the point placement can also be numeric,
  27908. * where 0 is on the axis value, -0.5 is between this value and the
  27909. * previous, and 0.5 is between this value and the next. Unlike the
  27910. * textual options, numeric point placement options won't affect axis
  27911. * padding.
  27912. *
  27913. * Note that pointPlacement needs a [pointRange](
  27914. * #plotOptions.series.pointRange) to work. For column series this is
  27915. * computed, but for line-type series it needs to be set.
  27916. *
  27917. * For the `xrange` series type and gantt charts, if the Y axis is a
  27918. * category axis, the `pointPlacement` applies to the Y axis rather than
  27919. * the (typically datetime) X axis.
  27920. *
  27921. * Defaults to `undefined` in cartesian charts, `"between"` in polar
  27922. * charts.
  27923. *
  27924. * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
  27925. *
  27926. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
  27927. * Between in a column chart
  27928. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
  27929. * Numeric placement for custom layout
  27930. * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
  27931. * Placement in heatmap
  27932. *
  27933. * @type {string|number}
  27934. * @since 2.3.0
  27935. * @product highcharts highstock gantt
  27936. * @apioption plotOptions.series.pointPlacement
  27937. */
  27938. /**
  27939. * If no x values are given for the points in a series, pointStart
  27940. * defines on what value to start. For example, if a series contains one
  27941. * yearly value starting from 1945, set pointStart to 1945.
  27942. *
  27943. * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
  27944. * Linear
  27945. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  27946. * Datetime
  27947. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  27948. * Using pointStart and pointInterval
  27949. *
  27950. * @type {number}
  27951. * @default 0
  27952. * @product highcharts highstock gantt
  27953. * @apioption plotOptions.series.pointStart
  27954. */
  27955. /**
  27956. * Whether to select the series initially. If `showCheckbox` is true,
  27957. * the checkbox next to the series name in the legend will be checked
  27958. * for a selected series.
  27959. *
  27960. * @sample {highcharts} highcharts/plotoptions/series-selected/
  27961. * One out of two series selected
  27962. *
  27963. * @type {boolean}
  27964. * @default false
  27965. * @since 1.2.0
  27966. * @apioption plotOptions.series.selected
  27967. */
  27968. /**
  27969. * Whether to apply a drop shadow to the graph line. Since 2.3 the
  27970. * shadow can be an object configuration containing `color`, `offsetX`,
  27971. * `offsetY`, `opacity` and `width`.
  27972. *
  27973. * @sample {highcharts} highcharts/plotoptions/series-shadow/
  27974. * Shadow enabled
  27975. *
  27976. * @type {boolean|Highcharts.ShadowOptionsObject}
  27977. * @default false
  27978. * @apioption plotOptions.series.shadow
  27979. */
  27980. /**
  27981. * Whether to display this particular series or series type in the
  27982. * legend. The default value is `true` for standalone series, `false`
  27983. * for linked series.
  27984. *
  27985. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  27986. * One series in the legend, one hidden
  27987. *
  27988. * @type {boolean}
  27989. * @default true
  27990. * @apioption plotOptions.series.showInLegend
  27991. */
  27992. /**
  27993. * Whether or not to show the series in the navigator. Takes precedence
  27994. * over [navigator.baseSeries](#navigator.baseSeries) if defined.
  27995. *
  27996. * @type {boolean}
  27997. * @since 5.0.0
  27998. * @product highstock
  27999. * @apioption plotOptions.series.showInNavigator
  28000. */
  28001. /**
  28002. * If set to `true`, the accessibility module will skip past the points
  28003. * in this series for keyboard navigation.
  28004. *
  28005. * @type {boolean}
  28006. * @since 5.0.12
  28007. * @apioption plotOptions.series.skipKeyboardNavigation
  28008. */
  28009. /**
  28010. * Whether to stack the values of each series on top of each other.
  28011. * Possible values are `undefined` to disable, `"normal"` to stack by
  28012. * value or `"percent"`. When stacking is enabled, data must be sorted
  28013. * in ascending X order. A special stacking option is with the
  28014. * streamgraph series type, where the stacking option is set to
  28015. * `"stream"`. The second one is `"overlap"`, which only applies to
  28016. * waterfall series.
  28017. *
  28018. * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
  28019. *
  28020. * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
  28021. * Line
  28022. * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
  28023. * Column
  28024. * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
  28025. * Bar
  28026. * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
  28027. * Area
  28028. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
  28029. * Line
  28030. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
  28031. * Column
  28032. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
  28033. * Bar
  28034. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
  28035. * Area
  28036. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
  28037. * Waterfall with normal stacking
  28038. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
  28039. * Waterfall with overlap stacking
  28040. * @sample {highstock} stock/plotoptions/stacking/
  28041. * Area
  28042. *
  28043. * @type {string}
  28044. * @product highcharts highstock
  28045. * @validvalue ["normal", "percent"]
  28046. * @apioption plotOptions.series.stacking
  28047. */
  28048. /**
  28049. * Whether to apply steps to the line. Possible values are `left`,
  28050. * `center` and `right`.
  28051. *
  28052. * @sample {highcharts} highcharts/plotoptions/line-step/
  28053. * Different step line options
  28054. * @sample {highcharts} highcharts/plotoptions/area-step/
  28055. * Stepped, stacked area
  28056. * @sample {highstock} stock/plotoptions/line-step/
  28057. * Step line
  28058. *
  28059. * @type {string}
  28060. * @since 1.2.5
  28061. * @product highcharts highstock
  28062. * @validvalue ["left", "center", "right"]
  28063. * @apioption plotOptions.series.step
  28064. */
  28065. /**
  28066. * The threshold, also called zero level or base level. For line type
  28067. * series this is only used in conjunction with
  28068. * [negativeColor](#plotOptions.series.negativeColor).
  28069. *
  28070. * @see [softThreshold](#plotOptions.series.softThreshold).
  28071. *
  28072. * @type {number}
  28073. * @default 0
  28074. * @since 3.0
  28075. * @product highcharts highstock
  28076. * @apioption plotOptions.series.threshold
  28077. */
  28078. /**
  28079. * Set the initial visibility of the series.
  28080. *
  28081. * @sample {highcharts} highcharts/plotoptions/series-visible/
  28082. * Two series, one hidden and one visible
  28083. * @sample {highstock} stock/plotoptions/series-visibility/
  28084. * Hidden series
  28085. *
  28086. * @type {boolean}
  28087. * @default true
  28088. * @apioption plotOptions.series.visible
  28089. */
  28090. /**
  28091. * Defines the Axis on which the zones are applied.
  28092. *
  28093. * @see [zones](#plotOptions.series.zones)
  28094. *
  28095. * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
  28096. * Zones on the X-Axis
  28097. * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
  28098. * Zones on the X-Axis
  28099. *
  28100. * @type {string}
  28101. * @default y
  28102. * @since 4.1.0
  28103. * @product highcharts highstock
  28104. * @apioption plotOptions.series.zoneAxis
  28105. */
  28106. /**
  28107. * General event handlers for the series items. These event hooks can
  28108. * also be attached to the series at run time using the
  28109. * `Highcharts.addEvent` function.
  28110. */
  28111. events: {},
  28112. /**
  28113. * Fires after the series has finished its initial animation, or in case
  28114. * animation is disabled, immediately as the series is displayed.
  28115. *
  28116. * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
  28117. * Show label after animate
  28118. * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
  28119. * Show label after animate
  28120. *
  28121. * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
  28122. * @since 4.0
  28123. * @product highcharts highstock gantt
  28124. * @context Highcharts.Series
  28125. * @apioption plotOptions.series.events.afterAnimate
  28126. */
  28127. /**
  28128. * Fires when the checkbox next to the series' name in the legend is
  28129. * clicked. One parameter, `event`, is passed to the function. The state
  28130. * of the checkbox is found by `event.checked`. The checked item is
  28131. * found by `event.item`. Return `false` to prevent the default action
  28132. * which is to toggle the select state of the series.
  28133. *
  28134. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  28135. * Alert checkbox status
  28136. *
  28137. * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
  28138. * @since 1.2.0
  28139. * @context Highcharts.Series
  28140. * @apioption plotOptions.series.events.checkboxClick
  28141. */
  28142. /**
  28143. * Fires when the series is clicked. One parameter, `event`, is passed
  28144. * to the function, containing common event information. Additionally,
  28145. * `event.point` holds a pointer to the nearest point on the graph.
  28146. *
  28147. * @sample {highcharts} highcharts/plotoptions/series-events-click/
  28148. * Alert click info
  28149. * @sample {highstock} stock/plotoptions/series-events-click/
  28150. * Alert click info
  28151. * @sample {highmaps} maps/plotoptions/series-events-click/
  28152. * Display click info in subtitle
  28153. *
  28154. * @type {Highcharts.SeriesClickCallbackFunction}
  28155. * @context Highcharts.Series
  28156. * @apioption plotOptions.series.events.click
  28157. */
  28158. /**
  28159. * Fires when the series is hidden after chart generation time, either
  28160. * by clicking the legend item or by calling `.hide()`.
  28161. *
  28162. * @sample {highcharts} highcharts/plotoptions/series-events-hide/
  28163. * Alert when the series is hidden by clicking the legend item
  28164. *
  28165. * @type {Highcharts.SeriesHideCallbackFunction}
  28166. * @since 1.2.0
  28167. * @context Highcharts.Series
  28168. * @apioption plotOptions.series.events.hide
  28169. */
  28170. /**
  28171. * Fires when the legend item belonging to the series is clicked. One
  28172. * parameter, `event`, is passed to the function. The default action
  28173. * is to toggle the visibility of the series. This can be prevented
  28174. * by returning `false` or calling `event.preventDefault()`.
  28175. *
  28176. * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
  28177. * Confirm hiding and showing
  28178. *
  28179. * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
  28180. * @context Highcharts.Series
  28181. * @apioption plotOptions.series.events.legendItemClick
  28182. */
  28183. /**
  28184. * Fires when the mouse leaves the graph. One parameter, `event`, is
  28185. * passed to the function, containing common event information. If the
  28186. * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
  28187. * doesn't happen before the mouse enters another graph or leaves the
  28188. * plot area.
  28189. *
  28190. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  28191. * With sticky tracking by default
  28192. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  28193. * Without sticky tracking
  28194. *
  28195. * @type {Highcharts.SeriesMouseOutCallbackFunction}
  28196. * @context Highcharts.Series
  28197. * @apioption plotOptions.series.events.mouseOut
  28198. */
  28199. /**
  28200. * Fires when the mouse enters the graph. One parameter, `event`, is
  28201. * passed to the function, containing common event information.
  28202. *
  28203. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  28204. * With sticky tracking by default
  28205. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  28206. * Without sticky tracking
  28207. *
  28208. * @type {Highcharts.SeriesMouseOverCallbackFunction}
  28209. * @context Highcharts.Series
  28210. * @apioption plotOptions.series.events.mouseOver
  28211. */
  28212. /**
  28213. * Fires when the series is shown after chart generation time, either
  28214. * by clicking the legend item or by calling `.show()`.
  28215. *
  28216. * @sample {highcharts} highcharts/plotoptions/series-events-show/
  28217. * Alert when the series is shown by clicking the legend item.
  28218. *
  28219. * @type {Highcharts.SeriesShowCallbackFunction}
  28220. * @since 1.2.0
  28221. * @context Highcharts.Series
  28222. * @apioption plotOptions.series.events.show
  28223. */
  28224. /**
  28225. * Options for the point markers of line-like series. Properties like
  28226. * `fillColor`, `lineColor` and `lineWidth` define the visual appearance
  28227. * of the markers. Other series types, like column series, don't have
  28228. * markers, but have visual options on the series level instead.
  28229. *
  28230. * In styled mode, the markers can be styled with the
  28231. * `.highcharts-point`, `.highcharts-point-hover` and
  28232. * `.highcharts-point-select` class names.
  28233. */
  28234. marker: {
  28235. /**
  28236. * The width of the point marker's outline.
  28237. *
  28238. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  28239. * 2px blue marker
  28240. */
  28241. lineWidth: 0,
  28242. /**
  28243. * The color of the point marker's outline. When `undefined`, the
  28244. * series' or point's color is used.
  28245. *
  28246. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  28247. * Inherit from series color (undefined)
  28248. *
  28249. * @type {Highcharts.ColorString}
  28250. */
  28251. lineColor: '#ffffff',
  28252. /**
  28253. * The fill color of the point marker. When `undefined`, the series'
  28254. * or point's color is used.
  28255. *
  28256. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  28257. * White fill
  28258. *
  28259. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28260. * @apioption plotOptions.series.marker.fillColor
  28261. */
  28262. /**
  28263. * Enable or disable the point marker. If `undefined`, the markers
  28264. * are hidden when the data is dense, and shown for more widespread
  28265. * data points.
  28266. *
  28267. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
  28268. * Disabled markers
  28269. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
  28270. * Disabled in normal state but enabled on hover
  28271. * @sample {highstock} stock/plotoptions/series-marker/
  28272. * Enabled markers
  28273. *
  28274. * @type {boolean}
  28275. * @default {highcharts} undefined
  28276. * @default {highstock} false
  28277. * @apioption plotOptions.series.marker.enabled
  28278. */
  28279. /**
  28280. * Image markers only. Set the image width explicitly. When using
  28281. * this option, a `width` must also be set.
  28282. *
  28283. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  28284. * Fixed width and height
  28285. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  28286. * Fixed width and height
  28287. *
  28288. * @type {number}
  28289. * @since 4.0.4
  28290. * @apioption plotOptions.series.marker.height
  28291. */
  28292. /**
  28293. * A predefined shape or symbol for the marker. When undefined, the
  28294. * symbol is pulled from options.symbols. Other possible values are
  28295. * "circle", "square", "diamond", "triangle" and "triangle-down".
  28296. *
  28297. * Additionally, the URL to a graphic can be given on this form:
  28298. * "url(graphic.png)". Note that for the image to be applied to
  28299. * exported charts, its URL needs to be accessible by the export
  28300. * server.
  28301. *
  28302. * Custom callbacks for symbol path generation can also be added to
  28303. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  28304. * used by its method name, as shown in the demo.
  28305. *
  28306. * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
  28307. * Predefined, graphic and custom markers
  28308. * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
  28309. * Predefined, graphic and custom markers
  28310. *
  28311. * @type {string}
  28312. * @apioption plotOptions.series.marker.symbol
  28313. */
  28314. /**
  28315. * The threshold for how dense the point markers should be before
  28316. * they are hidden, given that `enabled` is not defined. The number
  28317. * indicates the horizontal distance between the two closest points
  28318. * in the series, as multiples of the `marker.radius`. In other
  28319. * words, the default value of 2 means points are hidden if
  28320. * overlapping horizontally.
  28321. *
  28322. * @sample highcharts/plotoptions/series-marker-enabledthreshold
  28323. * A higher threshold
  28324. *
  28325. * @since 6.0.5
  28326. */
  28327. enabledThreshold: 2,
  28328. /**
  28329. * The radius of the point marker.
  28330. *
  28331. * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
  28332. * Bigger markers
  28333. *
  28334. * @default {highstock} 2
  28335. */
  28336. radius: 4,
  28337. /**
  28338. * Image markers only. Set the image width explicitly. When using
  28339. * this option, a `height` must also be set.
  28340. *
  28341. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  28342. * Fixed width and height
  28343. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  28344. * Fixed width and height
  28345. *
  28346. * @type {number}
  28347. * @since 4.0.4
  28348. * @apioption plotOptions.series.marker.width
  28349. */
  28350. /**
  28351. * States for a single point marker.
  28352. */
  28353. states: {
  28354. /**
  28355. * The normal state of a single point marker. Currently only
  28356. * used for setting animation when returning to normal state
  28357. * from hover.
  28358. */
  28359. normal: {
  28360. /**
  28361. * Animation when returning to normal state after hovering.
  28362. *
  28363. * @type {boolean|Highcharts.AnimationOptionsObject}
  28364. */
  28365. animation: true
  28366. },
  28367. /**
  28368. * The hover state for a single point marker.
  28369. */
  28370. hover: {
  28371. /**
  28372. * Animation when hovering over the marker.
  28373. *
  28374. * @type {boolean|Highcharts.AnimationOptionsObject}
  28375. */
  28376. animation: {
  28377. duration: 50
  28378. },
  28379. /**
  28380. * Enable or disable the point marker.
  28381. *
  28382. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
  28383. * Disabled hover state
  28384. */
  28385. enabled: true,
  28386. /**
  28387. * The fill color of the marker in hover state. When
  28388. * `undefined`, the series' or point's fillColor for normal
  28389. * state is used.
  28390. *
  28391. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28392. * @apioption plotOptions.series.marker.states.hover.fillColor
  28393. */
  28394. /**
  28395. * The color of the point marker's outline. When
  28396. * `undefined`, the series' or point's lineColor for normal
  28397. * state is used.
  28398. *
  28399. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
  28400. * White fill color, black line color
  28401. *
  28402. * @type {Highcharts.ColorString}
  28403. * @apioption plotOptions.series.marker.states.hover.lineColor
  28404. */
  28405. /**
  28406. * The width of the point marker's outline. When
  28407. * `undefined`, the series' or point's lineWidth for normal
  28408. * state is used.
  28409. *
  28410. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
  28411. * 3px line width
  28412. *
  28413. * @type {number}
  28414. * @apioption plotOptions.series.marker.states.hover.lineWidth
  28415. */
  28416. /**
  28417. * The radius of the point marker. In hover state, it
  28418. * defaults to the normal state's radius + 2 as per the
  28419. * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
  28420. * option.
  28421. *
  28422. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
  28423. * 10px radius
  28424. *
  28425. * @type {number}
  28426. * @apioption plotOptions.series.marker.states.hover.radius
  28427. */
  28428. /**
  28429. * The number of pixels to increase the radius of the
  28430. * hovered point.
  28431. *
  28432. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  28433. * 5 pixels greater radius on hover
  28434. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  28435. * 5 pixels greater radius on hover
  28436. *
  28437. * @since 4.0.3
  28438. */
  28439. radiusPlus: 2,
  28440. /**
  28441. * The additional line width for a hovered point.
  28442. *
  28443. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  28444. * 2 pixels wider on hover
  28445. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  28446. * 2 pixels wider on hover
  28447. *
  28448. * @since 4.0.3
  28449. */
  28450. lineWidthPlus: 1
  28451. },
  28452. /**
  28453. * The appearance of the point marker when selected. In order to
  28454. * allow a point to be selected, set the
  28455. * `series.allowPointSelect` option to true.
  28456. */
  28457. select: {
  28458. /**
  28459. * The radius of the point marker. In hover state, it
  28460. * defaults to the normal state's radius + 2.
  28461. *
  28462. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
  28463. * 10px radius for selected points
  28464. *
  28465. * @type {number}
  28466. * @apioption plotOptions.series.marker.states.select.radius
  28467. */
  28468. /**
  28469. * Enable or disable visible feedback for selection.
  28470. *
  28471. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
  28472. * Disabled select state
  28473. *
  28474. * @type {boolean}
  28475. * @default true
  28476. * @apioption plotOptions.series.marker.states.select.enabled
  28477. */
  28478. /**
  28479. * The fill color of the point marker.
  28480. *
  28481. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
  28482. * Solid red discs for selected points
  28483. *
  28484. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28485. */
  28486. fillColor: '#cccccc',
  28487. /**
  28488. * The color of the point marker's outline. When
  28489. * `undefined`, the series' or point's color is used.
  28490. *
  28491. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
  28492. * Red line color for selected points
  28493. *
  28494. * @type {Highcharts.ColorString}
  28495. */
  28496. lineColor: '#000000',
  28497. /**
  28498. * The width of the point marker's outline.
  28499. *
  28500. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
  28501. * 3px line width for selected points
  28502. */
  28503. lineWidth: 2
  28504. }
  28505. }
  28506. },
  28507. /**
  28508. * Properties for each single point.
  28509. */
  28510. point: {
  28511. /**
  28512. * Fires when a point is clicked. One parameter, `event`, is passed
  28513. * to the function, containing common event information.
  28514. *
  28515. * If the `series.allowPointSelect` option is true, the default
  28516. * action for the point's click event is to toggle the point's
  28517. * select state. Returning `false` cancels this action.
  28518. *
  28519. * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
  28520. * Click marker to alert values
  28521. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
  28522. * Click column
  28523. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
  28524. * Go to URL
  28525. * @sample {highmaps} maps/plotoptions/series-point-events-click/
  28526. * Click marker to display values
  28527. * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
  28528. * Go to URL
  28529. *
  28530. * @type {Highcharts.SeriesPointClickCallbackFunction}
  28531. * @context Highcharts.Point
  28532. * @apioption plotOptions.series.point.events.click
  28533. */
  28534. /**
  28535. * Fires when the mouse leaves the area close to the point. One
  28536. * parameter, `event`, is passed to the function, containing common
  28537. * event information.
  28538. *
  28539. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  28540. * Show values in the chart's corner on mouse over
  28541. *
  28542. * @type {Highcharts.SeriesPointMouseOutCallbackFunction}
  28543. * @context Highcharts.Point
  28544. * @apioption plotOptions.series.point.events.mouseOut
  28545. */
  28546. /**
  28547. * Fires when the mouse enters the area close to the point. One
  28548. * parameter, `event`, is passed to the function, containing common
  28549. * event information.
  28550. *
  28551. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  28552. * Show values in the chart's corner on mouse over
  28553. *
  28554. * @type {Highcharts.SeriesPointMouseOverCallbackFunction}
  28555. * @context Highcharts.Point
  28556. * @apioption plotOptions.series.point.events.mouseOver
  28557. */
  28558. /**
  28559. * Fires when the point is removed using the `.remove()` method. One
  28560. * parameter, `event`, is passed to the function. Returning `false`
  28561. * cancels the operation.
  28562. *
  28563. * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
  28564. * Remove point and confirm
  28565. *
  28566. * @type {Highcharts.SeriesPointRemoveCallbackFunction}
  28567. * @since 1.2.0
  28568. * @context Highcharts.Point
  28569. * @apioption plotOptions.series.point.events.remove
  28570. */
  28571. /**
  28572. * Fires when the point is selected either programmatically or
  28573. * following a click on the point. One parameter, `event`, is passed
  28574. * to the function. Returning `false` cancels the operation.
  28575. *
  28576. * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
  28577. * Report the last selected point
  28578. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28579. * Report select and unselect
  28580. *
  28581. * @type {Highcharts.SeriesPointSelectCallbackFunction}
  28582. * @since 1.2.0
  28583. * @context Highcharts.Point
  28584. * @apioption plotOptions.series.point.events.select
  28585. */
  28586. /**
  28587. * Fires when the point is unselected either programmatically or
  28588. * following a click on the point. One parameter, `event`, is passed
  28589. * to the function.
  28590. * Returning `false` cancels the operation.
  28591. *
  28592. * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
  28593. * Report the last unselected point
  28594. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28595. * Report select and unselect
  28596. *
  28597. * @type {Highcharts.SeriesPointUnselectCallbackFunction}
  28598. * @since 1.2.0
  28599. * @context Highcharts.Point
  28600. * @apioption plotOptions.series.point.events.unselect
  28601. */
  28602. /**
  28603. * Fires when the point is updated programmatically through the
  28604. * `.update()` method. One parameter, `event`, is passed to the
  28605. * function. The new point options can be accessed through
  28606. * `event.options`. Returning `false` cancels the operation.
  28607. *
  28608. * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
  28609. * Confirm point updating
  28610. *
  28611. * @type {Highcharts.SeriesPointUpdateCallbackFunction}
  28612. * @since 1.2.0
  28613. * @context Highcharts.Point
  28614. * @apioption plotOptions.series.point.events.update
  28615. */
  28616. /**
  28617. * Events for each single point.
  28618. */
  28619. events: {}
  28620. },
  28621. /**
  28622. * Options for the series data labels, appearing next to each data
  28623. * point.
  28624. *
  28625. * Since v6.2.0, multiple data labels can be applied to each single
  28626. * point by defining them as an array of configs.
  28627. *
  28628. * In styled mode, the data labels can be styled with the
  28629. * `.highcharts-data-label-box` and `.highcharts-data-label` class names
  28630. * ([see example](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-datalabels)).
  28631. *
  28632. * @sample highcharts/plotoptions/series-datalabels-enabled
  28633. * Data labels enabled
  28634. * @sample highcharts/plotoptions/series-datalabels-multiple
  28635. * Multiple data labels on a bar series
  28636. */
  28637. dataLabels: {
  28638. /**
  28639. * The alignment of the data label compared to the point. If
  28640. * `right`, the right side of the label should be touching the
  28641. * point. For points with an extent, like columns, the alignments
  28642. * also dictates how to align it inside the box, as given with the
  28643. * [inside](#plotOptions.column.dataLabels.inside) option. Can be
  28644. * one of `left`, `center` or `right`.
  28645. *
  28646. * @sample {highcharts} highcharts/plotoptions/series-datalabels-align-left/
  28647. * Left aligned
  28648. *
  28649. * @type {Highcharts.AlignType}
  28650. */
  28651. align: 'center',
  28652. /**
  28653. * Whether to allow data labels to overlap. To make the labels less
  28654. * sensitive for overlapping, the [dataLabels.padding](
  28655. * #plotOptions.series.dataLabels.padding) can be set to 0.
  28656. *
  28657. * @sample highcharts/plotoptions/series-datalabels-allowoverlap-false/
  28658. * Don't allow overlap
  28659. *
  28660. * @type {boolean}
  28661. * @default false
  28662. * @since 4.1.0
  28663. * @apioption plotOptions.series.dataLabels.allowOverlap
  28664. */
  28665. /**
  28666. * The border radius in pixels for the data label.
  28667. *
  28668. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28669. * Data labels box options
  28670. * @sample {highstock} highcharts/plotoptions/series-datalabels-box/
  28671. * Data labels box options
  28672. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  28673. * Data labels box options
  28674. *
  28675. * @type {number}
  28676. * @default 0
  28677. * @since 2.2.1
  28678. * @apioption plotOptions.series.dataLabels.borderRadius
  28679. */
  28680. /**
  28681. * The border width in pixels for the data label.
  28682. *
  28683. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28684. * Data labels box options
  28685. * @sample {highstock} highcharts/plotoptions/series-datalabels-box/
  28686. * Data labels box options
  28687. *
  28688. * @type {number}
  28689. * @default 0
  28690. * @since 2.2.1
  28691. * @apioption plotOptions.series.dataLabels.borderWidth
  28692. */
  28693. /**
  28694. * A class name for the data label. Particularly in styled mode,
  28695. * this can be used to give each series' or point's data label
  28696. * unique styling. In addition to this option, a default color class
  28697. * name is added so that we can give the labels a
  28698. * [contrast text shadow](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/data-label-contrast/).
  28699. *
  28700. * @sample {highcharts} highcharts/css/series-datalabels/
  28701. * Styling by CSS
  28702. * @sample {highstock} highcharts/css/series-datalabels/
  28703. * Styling by CSS
  28704. * @sample {highmaps} highcharts/css/series-datalabels/
  28705. * Styling by CSS
  28706. *
  28707. * @type {string}
  28708. * @since 5.0.0
  28709. * @apioption plotOptions.series.dataLabels.className
  28710. */
  28711. /**
  28712. * The text color for the data labels. Defaults to `undefined`. For
  28713. * certain series types, like column or map, the data labels can be
  28714. * drawn inside the points. In this case the data label will be
  28715. * drawn with maximum contrast by default. Additionally, it will be
  28716. * given a `text-outline` style with the opposite color, to further
  28717. * increase the contrast. This can be overridden by setting the
  28718. * `text-outline` style to `none` in the `dataLabels.style` option.
  28719. *
  28720. * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
  28721. * Red data labels
  28722. * @sample {highmaps} maps/demo/color-axis/
  28723. * White data labels
  28724. *
  28725. * @type {Highcharts.ColorString}
  28726. * @apioption plotOptions.series.dataLabels.color
  28727. */
  28728. /**
  28729. * Whether to hide data labels that are outside the plot area. By
  28730. * default, the data label is moved inside the plot area according
  28731. * to the [overflow](#plotOptions.series.dataLabels.overflow)
  28732. * option.
  28733. *
  28734. * @type {boolean}
  28735. * @default true
  28736. * @since 2.3.3
  28737. * @apioption plotOptions.series.dataLabels.crop
  28738. */
  28739. /**
  28740. * Whether to defer displaying the data labels until the initial
  28741. * series animation has finished.
  28742. *
  28743. * @type {boolean}
  28744. * @default true
  28745. * @since 4.0
  28746. * @product highcharts highstock gantt
  28747. * @apioption plotOptions.series.dataLabels.defer
  28748. */
  28749. /**
  28750. * Enable or disable the data labels.
  28751. *
  28752. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
  28753. * Data labels enabled
  28754. * @sample {highmaps} maps/demo/color-axis/
  28755. * Data labels enabled
  28756. *
  28757. * @type {boolean}
  28758. * @default false
  28759. * @apioption plotOptions.series.dataLabels.enabled
  28760. */
  28761. /**
  28762. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  28763. * for the data label. Available variables are the same as for
  28764. * `formatter`.
  28765. *
  28766. * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-format/
  28767. * Add a unit
  28768. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  28769. * Formatted value in the data label
  28770. *
  28771. * @type {string}
  28772. * @default {highcharts} {y}
  28773. * @default {highstock} {y}
  28774. * @default {highmaps} {point.value}
  28775. * @since 3.0
  28776. * @apioption plotOptions.series.dataLabels.format
  28777. */
  28778. /**
  28779. * Callback JavaScript function to format the data label. Note that
  28780. * if a `format` is defined, the format takes precedence and the
  28781. * formatter is ignored. Available data are:
  28782. *
  28783. * - `this.percentage`: Stacked series and pies only. The point's
  28784. * percentage of the total.
  28785. *
  28786. * - `this.point`: The point object. The point name, if defined, is
  28787. * available through `this.point.name`.
  28788. *
  28789. * - `this.series`: The series object. The series name is available
  28790. * through`this.series.name`.
  28791. *
  28792. * - `this.total`: Stacked series only. The total value at this
  28793. * point's x value.
  28794. *
  28795. * - `this.x`: The x value.
  28796. *
  28797. * - `this.y`: The y value.
  28798. *
  28799. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  28800. * Formatted value
  28801. *
  28802. * @type {Highcharts.FormatterCallbackFunction<Highcharts.SeriesDataLabelsFormatterContextObject>}
  28803. * @default function () { return this.y; }
  28804. */
  28805. formatter: function () {
  28806. return this.y === null ? '' : H.numberFormat(this.y, -1);
  28807. },
  28808. /**
  28809. * Styles for the label. The default `color` setting is
  28810. * `"contrast"`, which is a pseudo color that Highcharts picks up
  28811. * and applies the maximum contrast to the underlying point item,
  28812. * for example the bar in a bar chart.
  28813. *
  28814. * The `textOutline` is a pseudo property that
  28815. * applies an outline of the given width with the given color, which
  28816. * by default is the maximum contrast to the text. So a bright text
  28817. * color will result in a black text outline for maximum readability
  28818. * on a mixed background. In some cases, especially with grayscale
  28819. * text, the text outline doesn't work well, in which cases it can
  28820. * be disabled by setting it to `"none"`. When `useHTML` is true,
  28821. * the `textOutline` will not be picked up. In this, case, the same
  28822. * effect can be acheived through the `text-shadow` CSS property.
  28823. *
  28824. * For some series types, where each point has an extent, like for
  28825. * example tree maps, the data label may overflow the point. There
  28826. * are two strategies for handling overflow. By default, the text
  28827. * will wrap to multiple lines. The other strategy is to set
  28828. * `style.textOverflow` to `ellipsis`, which will keep the text on
  28829. * one line plus it will break inside long words.
  28830. *
  28831. * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
  28832. * Bold labels
  28833. * @sample {highmaps} maps/demo/color-axis/
  28834. * Bold labels
  28835. *
  28836. * @type {Highcharts.CSSObject}
  28837. * @default {"color": "contrast", "fontSize": "11px", "fontWeight": "bold", "textOutline": "1px contrast" }
  28838. * @since 4.1.0
  28839. */
  28840. style: {
  28841. /** @ignore */
  28842. fontSize: '11px',
  28843. /** @ignore */
  28844. fontWeight: 'bold',
  28845. /** @ignore */
  28846. color: 'contrast',
  28847. /** @ignore */
  28848. textOutline: '1px contrast'
  28849. },
  28850. /**
  28851. * The name of a symbol to use for the border around the label.
  28852. * Symbols are predefined functions on the Renderer object.
  28853. *
  28854. * @sample highcharts/plotoptions/series-datalabels-shape/
  28855. * A callout for annotations
  28856. *
  28857. * @type {string}
  28858. * @default square
  28859. * @since 4.1.2
  28860. * @apioption plotOptions.series.dataLabels.shape
  28861. */
  28862. /**
  28863. * The Z index of the data labels. The default Z index puts it above
  28864. * the series. Use a Z index of 2 to display it behind the series.
  28865. *
  28866. * @type {number}
  28867. * @default 6
  28868. * @since 2.3.5
  28869. * @apioption plotOptions.series.dataLabels.zIndex
  28870. */
  28871. /**
  28872. * A declarative filter for which data labels to display. The
  28873. * declarative filter is designed for use when callback functions
  28874. * are not available, like when the chart options require a pure
  28875. * JSON structure or for use with graphical editors. For
  28876. * programmatic control, use the `formatter` instead, and return
  28877. * `undefined` to disable a single data label.
  28878. *
  28879. * @example
  28880. * filter: {
  28881. * property: 'percentage',
  28882. * operator: '>',
  28883. * value: 4
  28884. * }
  28885. *
  28886. * @sample highcharts/demo/pie-monochrome
  28887. * Data labels filtered by percentage
  28888. *
  28889. * @since 6.0.3
  28890. * @apioption plotOptions.series.dataLabels.filter
  28891. */
  28892. /**
  28893. * The point property to filter by. Point options are passed
  28894. * directly to properties, additionally there are `y` value,
  28895. * `percentage` and others listed under
  28896. * [Point](https://api.highcharts.com/class-reference/Highcharts.Point)
  28897. * members.
  28898. *
  28899. * @type {string}
  28900. * @apioption plotOptions.series.dataLabels.filter.property
  28901. */
  28902. /**
  28903. * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
  28904. * `==`, and `===`.
  28905. *
  28906. * @type {string}
  28907. * @validvalue [">", "<", ">=", "<=", "==", "==="]
  28908. * @apioption plotOptions.series.dataLabels.filter.operator
  28909. */
  28910. /**
  28911. * The value to compare against.
  28912. *
  28913. * @type {*}
  28914. * @apioption plotOptions.series.dataLabels.filter.value
  28915. */
  28916. /**
  28917. * The background color or gradient for the data label.
  28918. *
  28919. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  28920. * Data labels box options
  28921. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  28922. * Data labels box options
  28923. *
  28924. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  28925. * @since 2.2.1
  28926. * @apioption plotOptions.series.dataLabels.backgroundColor
  28927. */
  28928. /**
  28929. * The border color for the data label. Defaults to `undefined`.
  28930. *
  28931. * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-box/
  28932. * Data labels box options
  28933. *
  28934. * @type {Highcharts.ColorString}
  28935. * @since 2.2.1
  28936. * @apioption plotOptions.series.dataLabels.borderColor
  28937. */
  28938. /**
  28939. * The shadow of the box. Works best with `borderWidth` or
  28940. * `backgroundColor`. Since 2.3 the shadow can be an object
  28941. * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
  28942. * and `width`.
  28943. *
  28944. * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-box/
  28945. * Data labels box options
  28946. *
  28947. * @type {boolean|Highcharts.ShadowOptionsObject}
  28948. * @default false
  28949. * @since 2.2.1
  28950. * @apioption plotOptions.series.dataLabels.shadow
  28951. */
  28952. /**
  28953. * For points with an extent, like columns or map areas, whether to
  28954. * align the data label inside the box or to the actual value point.
  28955. * Defaults to `false` in most cases, `true` in stacked columns.
  28956. *
  28957. * @type {boolean}
  28958. * @since 3.0
  28959. * @apioption plotOptions.series.dataLabels.inside
  28960. */
  28961. /**
  28962. * How to handle data labels that flow outside the plot area. The
  28963. * default is `"justify"`, which aligns them inside the plot area.
  28964. * For columns and bars, this means it will be moved inside the bar.
  28965. * To display data labels outside the plot area, set `crop` to
  28966. * `false` and `overflow` to `"allow"`.
  28967. *
  28968. * @type {string}
  28969. * @default justify
  28970. * @since 3.0.6
  28971. * @validvalue ["allow", "justify"]
  28972. * @apioption plotOptions.series.dataLabels.overflow
  28973. */
  28974. /**
  28975. * Text rotation in degrees. Note that due to a more complex
  28976. * structure, backgrounds, borders and padding will be lost on a
  28977. * rotated data label.
  28978. *
  28979. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  28980. * Vertical labels
  28981. *
  28982. * @type {number}
  28983. * @default 0
  28984. * @apioption plotOptions.series.dataLabels.rotation
  28985. */
  28986. /**
  28987. * Whether to
  28988. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  28989. * to render the labels.
  28990. *
  28991. * @type {boolean}
  28992. * @default false
  28993. * @apioption plotOptions.series.dataLabels.useHTML
  28994. */
  28995. /**
  28996. * The vertical alignment of a data label. Can be one of `top`,
  28997. * `middle` or `bottom`. The default value depends on the data, for
  28998. * instance in a column chart, the label is above positive values
  28999. * and below negative values.
  29000. *
  29001. * @type {Highcharts.VerticalAlignType}
  29002. * @since 2.3.3
  29003. */
  29004. verticalAlign: 'bottom', // above singular point
  29005. /**
  29006. * The x position offset of the label relative to the point in
  29007. * pixels.
  29008. *
  29009. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  29010. * Vertical and positioned
  29011. */
  29012. x: 0,
  29013. /**
  29014. * The y position offset of the label relative to the point in
  29015. * pixels.
  29016. *
  29017. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  29018. * Vertical and positioned
  29019. */
  29020. y: 0,
  29021. /**
  29022. * When either the `borderWidth` or the `backgroundColor` is set,
  29023. * this is the padding within the box.
  29024. *
  29025. * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-box/
  29026. * Data labels box options
  29027. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  29028. * Data labels box options
  29029. *
  29030. * @default {highcharts} 5
  29031. * @default {highstock} 5
  29032. * @default {highmaps} 0
  29033. * @since 2.2.1
  29034. */
  29035. padding: 5
  29036. },
  29037. /**
  29038. * When the series contains less points than the crop threshold, all
  29039. * points are drawn, even if the points fall outside the visible plot
  29040. * area at the current zoom. The advantage of drawing all points
  29041. * (including markers and columns), is that animation is performed on
  29042. * updates. On the other hand, when the series contains more points than
  29043. * the crop threshold, the series data is cropped to only contain points
  29044. * that fall within the plot area. The advantage of cropping away
  29045. * invisible points is to increase performance on large series.
  29046. *
  29047. * @since 2.2
  29048. * @product highcharts highstock
  29049. */
  29050. cropThreshold: 300,
  29051. /**
  29052. * The width of each point on the x axis. For example in a column chart
  29053. * with one value each day, the pointRange would be 1 day (= 24 * 3600
  29054. * * 1000 milliseconds). This is normally computed automatically, but
  29055. * this option can be used to override the automatic value.
  29056. *
  29057. * @product highstock
  29058. */
  29059. pointRange: 0,
  29060. /**
  29061. * When this is true, the series will not cause the Y axis to cross
  29062. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  29063. * unless the data actually crosses the plane.
  29064. *
  29065. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  29066. * 3 will make the Y axis show negative values according to the
  29067. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  29068. * at 0.
  29069. *
  29070. * @since 4.1.9
  29071. * @product highcharts highstock
  29072. */
  29073. softThreshold: true,
  29074. /**
  29075. * A wrapper object for all the series options in specific states.
  29076. */
  29077. states: {
  29078. /**
  29079. * The normal state of a series, or for point items in column, pie
  29080. * and similar series. Currently only used for setting animation
  29081. * when returning to normal state from hover.
  29082. */
  29083. normal: {
  29084. /**
  29085. * Animation when returning to normal state after hovering.
  29086. *
  29087. * @type {boolean|Highcharts.AnimationOptionsObject}
  29088. */
  29089. animation: true
  29090. },
  29091. /**
  29092. * Options for the hovered series. These settings override the
  29093. * normal state options when a series is moused over or touched.
  29094. */
  29095. hover: {
  29096. /**
  29097. * Enable separate styles for the hovered series to visualize
  29098. * that the user hovers either the series itself or the legend.
  29099. *
  29100. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
  29101. * Line
  29102. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
  29103. * Column
  29104. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
  29105. * Pie
  29106. *
  29107. * @type {boolean}
  29108. * @default true
  29109. * @since 1.2
  29110. * @apioption plotOptions.series.states.hover.enabled
  29111. */
  29112. /**
  29113. * Animation setting for hovering the graph in line-type series.
  29114. *
  29115. * @type {boolean|Highcharts.AnimationOptionsObject}
  29116. * @since 5.0.8
  29117. * @product highcharts
  29118. */
  29119. animation: {
  29120. /**
  29121. * The duration of the hover animation in milliseconds. By
  29122. * default the hover state animates quickly in, and slowly
  29123. * back to normal.
  29124. */
  29125. duration: 50
  29126. },
  29127. /**
  29128. * Pixel width of the graph line. By default this property is
  29129. * undefined, and the `lineWidthPlus` property dictates how much
  29130. * to increase the linewidth from normal state.
  29131. *
  29132. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
  29133. * 5px line on hover
  29134. *
  29135. * @type {number}
  29136. * @product highcharts highstock
  29137. * @apioption plotOptions.series.states.hover.lineWidth
  29138. */
  29139. /**
  29140. * The additional line width for the graph of a hovered series.
  29141. *
  29142. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29143. * 5 pixels wider
  29144. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29145. * 5 pixels wider
  29146. *
  29147. * @since 4.0.3
  29148. * @product highcharts highstock
  29149. */
  29150. lineWidthPlus: 1,
  29151. /**
  29152. * In Highcharts 1.0, the appearance of all markers belonging
  29153. * to the hovered series. For settings on the hover state of the
  29154. * individual point, see
  29155. * [marker.states.hover](#plotOptions.series.marker.states.hover).
  29156. *
  29157. * @deprecated
  29158. *
  29159. * @extends plotOptions.series.marker
  29160. * @product highcharts highstock
  29161. */
  29162. marker: {
  29163. // lineWidth: base + 1,
  29164. // radius: base + 1
  29165. },
  29166. /**
  29167. * Options for the halo appearing around the hovered point in
  29168. * line-type series as well as outside the hovered slice in pie
  29169. * charts. By default the halo is filled by the current point or
  29170. * series color with an opacity of 0.25\. The halo can be
  29171. * disabled by setting the `halo` option to `false`.
  29172. *
  29173. * In styled mode, the halo is styled with the
  29174. * `.highcharts-halo` class, with colors inherited from
  29175. * `.highcharts-color-{n}`.
  29176. *
  29177. * @sample {highcharts} highcharts/plotoptions/halo/
  29178. * Halo options
  29179. * @sample {highstock} highcharts/plotoptions/halo/
  29180. * Halo options
  29181. *
  29182. * @since 4.0
  29183. * @product highcharts highstock
  29184. */
  29185. halo: {
  29186. /**
  29187. * A collection of SVG attributes to override the appearance
  29188. * of the halo, for example `fill`, `stroke` and
  29189. * `stroke-width`.
  29190. *
  29191. * @type {Highcharts.SVGAttributes}
  29192. * @since 4.0
  29193. * @product highcharts highstock
  29194. * @apioption plotOptions.series.states.hover.halo.attributes
  29195. */
  29196. /**
  29197. * The pixel size of the halo. For point markers this is the
  29198. * radius of the halo. For pie slices it is the width of the
  29199. * halo outside the slice. For bubbles it defaults to 5 and
  29200. * is the width of the halo outside the bubble.
  29201. *
  29202. * @since 4.0
  29203. * @product highcharts highstock
  29204. */
  29205. size: 10,
  29206. /**
  29207. * Opacity for the halo unless a specific fill is overridden
  29208. * using the `attributes` setting. Note that Highcharts is
  29209. * only able to apply opacity to colors of hex or rgb(a)
  29210. * formats.
  29211. *
  29212. * @since 4.0
  29213. * @product highcharts highstock
  29214. */
  29215. opacity: 0.25
  29216. }
  29217. },
  29218. /**
  29219. * Specific options for point in selected states, after being
  29220. * selected by
  29221. * [allowPointSelect](#plotOptions.series.allowPointSelect) or
  29222. * programmatically.
  29223. *
  29224. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  29225. * Allow point select demo
  29226. *
  29227. * @extends plotOptions.series.states.hover
  29228. * @excluding brightness
  29229. * @product highmaps
  29230. */
  29231. select: {
  29232. animation: {
  29233. duration: 0
  29234. }
  29235. }
  29236. },
  29237. /**
  29238. * Sticky tracking of mouse events. When true, the `mouseOut` event on a
  29239. * series isn't triggered until the mouse moves over another series, or
  29240. * out of the plot area. When false, the `mouseOut` event on a series is
  29241. * triggered when the mouse leaves the area around the series' graph or
  29242. * markers. This also implies the tooltip when not shared. When
  29243. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  29244. * will be hidden when moving the mouse between series. Defaults to true
  29245. * for line and area type series, but to false for columns, pies etc.
  29246. *
  29247. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
  29248. * True by default
  29249. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
  29250. * False
  29251. *
  29252. * @default {highcharts} true
  29253. * @default {highstock} true
  29254. * @default {highmaps} false
  29255. * @since 2.0
  29256. */
  29257. stickyTracking: true,
  29258. /**
  29259. * A configuration object for the tooltip rendering of each single
  29260. * series. Properties are inherited from [tooltip](#tooltip), but only
  29261. * the following properties can be defined on a series level.
  29262. *
  29263. * @since 2.3
  29264. * @extends tooltip
  29265. * @excluding animation, backgroundColor, borderColor, borderRadius,
  29266. * borderWidth, crosshairs, enabled, formatter, positioner,
  29267. * shadow, shape, shared, snap, style, useHTML
  29268. * @apioption plotOptions.series.tooltip
  29269. */
  29270. /**
  29271. * When a series contains a data array that is longer than this, only
  29272. * one dimensional arrays of numbers, or two dimensional arrays with
  29273. * x and y values are allowed. Also, only the first point is tested,
  29274. * and the rest are assumed to be the same format. This saves expensive
  29275. * data checking and indexing in long series. Set it to `0` disable.
  29276. *
  29277. * @since 2.2
  29278. * @product highcharts highstock gantt
  29279. */
  29280. turboThreshold: 1000,
  29281. /**
  29282. * An array defining zones within a series. Zones can be applied to the
  29283. * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
  29284. * option. The zone definitions have to be in ascending order regarding
  29285. * to the value.
  29286. *
  29287. * In styled mode, the color zones are styled with the
  29288. * `.highcharts-zone-{n}` class, or custom classed from the `className`
  29289. * option
  29290. * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
  29291. *
  29292. * @see [zoneAxis](#plotOptions.series.zoneAxis)
  29293. *
  29294. * @sample {highcharts} highcharts/series/color-zones-simple/
  29295. * Color zones
  29296. * @sample {highstock} highcharts/series/color-zones-simple/
  29297. * Color zones
  29298. *
  29299. * @type {Array<*>}
  29300. * @since 4.1.0
  29301. * @product highcharts highstock
  29302. * @apioption plotOptions.series.zones
  29303. */
  29304. /**
  29305. * Styled mode only. A custom class name for the zone.
  29306. *
  29307. * @sample highcharts/css/color-zones/
  29308. * Zones styled by class name
  29309. *
  29310. * @type {string}
  29311. * @since 5.0.0
  29312. * @apioption plotOptions.series.zones.className
  29313. */
  29314. /**
  29315. * Defines the color of the series.
  29316. *
  29317. * @see [series color](#plotOptions.series.color)
  29318. *
  29319. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29320. * @since 4.1.0
  29321. * @product highcharts highstock
  29322. * @apioption plotOptions.series.zones.color
  29323. */
  29324. /**
  29325. * A name for the dash style to use for the graph.
  29326. *
  29327. * @see [series.dashStyle](#plotOptions.series.dashStyle)
  29328. *
  29329. * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
  29330. * Dashed line indicates prognosis
  29331. *
  29332. * @type {Highcharts.DashStyleType}
  29333. * @since 4.1.0
  29334. * @product highcharts highstock
  29335. * @apioption plotOptions.series.zones.dashStyle
  29336. */
  29337. /**
  29338. * Defines the fill color for the series (in area type series)
  29339. *
  29340. * @see [fillColor](#plotOptions.area.fillColor)
  29341. *
  29342. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29343. * @since 4.1.0
  29344. * @product highcharts highstock
  29345. * @apioption plotOptions.series.zones.fillColor
  29346. */
  29347. /**
  29348. * The value up to where the zone extends, if undefined the zones
  29349. * stretches to the last value in the series.
  29350. *
  29351. * @type {number}
  29352. * @since 4.1.0
  29353. * @product highcharts highstock
  29354. * @apioption plotOptions.series.zones.value
  29355. */
  29356. /**
  29357. * Determines whether the series should look for the nearest point
  29358. * in both dimensions or just the x-dimension when hovering the series.
  29359. * Defaults to `'xy'` for scatter series and `'x'` for most other
  29360. * series. If the data has duplicate x-values, it is recommended to
  29361. * set this to `'xy'` to allow hovering over all points.
  29362. *
  29363. * Applies only to series types using nearest neighbor search (not
  29364. * direct hover) for tooltip.
  29365. *
  29366. * @sample {highcharts} highcharts/series/findnearestpointby/
  29367. * Different hover behaviors
  29368. * @sample {highstock} highcharts/series/findnearestpointby/
  29369. * Different hover behaviors
  29370. * @sample {highmaps} highcharts/series/findnearestpointby/
  29371. * Different hover behaviors
  29372. *
  29373. * @since 5.0.10
  29374. * @validvalue ["x", "xy"]
  29375. */
  29376. findNearestPointBy: 'x'
  29377. },
  29378. /** @lends Highcharts.Series.prototype */
  29379. {
  29380. isCartesian: true,
  29381. pointClass: Point,
  29382. sorted: true, // requires the data to be sorted
  29383. requireSorting: true,
  29384. directTouch: false,
  29385. axisTypes: ['xAxis', 'yAxis'],
  29386. colorCounter: 0,
  29387. // each point's x and y values are stored in this.xData and this.yData
  29388. parallelArrays: ['x', 'y'],
  29389. coll: 'series',
  29390. init: function (chart, options) {
  29391. fireEvent(this, 'init', { options: options });
  29392. var series = this,
  29393. events,
  29394. chartSeries = chart.series,
  29395. lastSeries;
  29396. /**
  29397. * Read only. The chart that the series belongs to.
  29398. *
  29399. * @name Highcharts.Series#chart
  29400. * @type {Highcharts.Chart}
  29401. */
  29402. series.chart = chart;
  29403. /**
  29404. * Read only. The series' type, like "line", "area", "column" etc.
  29405. * The type in the series options anc can be altered using
  29406. * {@link Series#update}.
  29407. *
  29408. * @name Highcharts.Series#type
  29409. * @type {string}
  29410. */
  29411. /**
  29412. * Read only. The series' current options. To update, use
  29413. * {@link Series#update}.
  29414. *
  29415. * @name Highcharts.Series#options
  29416. * @type {Highcharts.SeriesOptionsType}
  29417. */
  29418. series.options = options = series.setOptions(options);
  29419. series.linkedSeries = [];
  29420. // bind the axes
  29421. series.bindAxes();
  29422. // set some variables
  29423. extend(series, {
  29424. /**
  29425. * The series name as given in the options. Defaults to
  29426. * "Series {n}".
  29427. *
  29428. * @name Highcharts.Series#name
  29429. * @type {string}
  29430. */
  29431. name: options.name,
  29432. state: '',
  29433. /**
  29434. * Read only. The series' visibility state as set by {@link
  29435. * Series#show}, {@link Series#hide}, or in the initial
  29436. * configuration.
  29437. *
  29438. * @name Highcharts.Series#visible
  29439. * @type {boolean}
  29440. */
  29441. visible: options.visible !== false, // true by default
  29442. /**
  29443. * Read only. The series' selected state as set by {@link
  29444. * Highcharts.Series#select}.
  29445. *
  29446. * @name Highcharts.Series#selected
  29447. * @type {boolean}
  29448. */
  29449. selected: options.selected === true // false by default
  29450. });
  29451. // register event listeners
  29452. events = options.events;
  29453. objectEach(events, function (event, eventType) {
  29454. addEvent(series, eventType, event);
  29455. });
  29456. if (
  29457. (events && events.click) ||
  29458. (
  29459. options.point &&
  29460. options.point.events &&
  29461. options.point.events.click
  29462. ) ||
  29463. options.allowPointSelect
  29464. ) {
  29465. chart.runTrackerClick = true;
  29466. }
  29467. series.getColor();
  29468. series.getSymbol();
  29469. // Set the data
  29470. series.parallelArrays.forEach(function (key) {
  29471. series[key + 'Data'] = [];
  29472. });
  29473. series.setData(options.data, false);
  29474. // Mark cartesian
  29475. if (series.isCartesian) {
  29476. chart.hasCartesianSeries = true;
  29477. }
  29478. // Get the index and register the series in the chart. The index is
  29479. // one more than the current latest series index (#5960).
  29480. if (chartSeries.length) {
  29481. lastSeries = chartSeries[chartSeries.length - 1];
  29482. }
  29483. series._i = pick(lastSeries && lastSeries._i, -1) + 1;
  29484. // Insert the series and re-order all series above the insertion
  29485. // point.
  29486. chart.orderSeries(this.insert(chartSeries));
  29487. fireEvent(this, 'afterInit');
  29488. },
  29489. /**
  29490. * Insert the series in a collection with other series, either the chart
  29491. * series or yAxis series, in the correct order according to the index
  29492. * option. Used internally when adding series.
  29493. *
  29494. * @private
  29495. * @function Highcharts.Series#insert
  29496. *
  29497. * @param {Array<Highcharts.Series>} collection
  29498. * A collection of series, like `chart.series` or `xAxis.series`.
  29499. *
  29500. * @return {number}
  29501. * The index of the series in the collection.
  29502. */
  29503. insert: function (collection) {
  29504. var indexOption = this.options.index,
  29505. i;
  29506. // Insert by index option
  29507. if (isNumber(indexOption)) {
  29508. i = collection.length;
  29509. while (i--) {
  29510. // Loop down until the interted element has higher index
  29511. if (indexOption >=
  29512. pick(collection[i].options.index, collection[i]._i)
  29513. ) {
  29514. collection.splice(i + 1, 0, this);
  29515. break;
  29516. }
  29517. }
  29518. if (i === -1) {
  29519. collection.unshift(this);
  29520. }
  29521. i = i + 1;
  29522. // Or just push it to the end
  29523. } else {
  29524. collection.push(this);
  29525. }
  29526. return pick(i, collection.length - 1);
  29527. },
  29528. /**
  29529. * Set the xAxis and yAxis properties of cartesian series, and register
  29530. * the series in the `axis.series` array.
  29531. *
  29532. * @private
  29533. * @function Highcharts.Series#bindAxes
  29534. *
  29535. * @exception 18
  29536. */
  29537. bindAxes: function () {
  29538. var series = this,
  29539. seriesOptions = series.options,
  29540. chart = series.chart,
  29541. axisOptions;
  29542. fireEvent(this, 'bindAxes', null, function () {
  29543. // repeat for xAxis and yAxis
  29544. (series.axisTypes || []).forEach(function (AXIS) {
  29545. // loop through the chart's axis objects
  29546. chart[AXIS].forEach(function (axis) {
  29547. axisOptions = axis.options;
  29548. // apply if the series xAxis or yAxis option mathches
  29549. // the number of the axis, or if undefined, use the
  29550. // first axis
  29551. if (
  29552. seriesOptions[AXIS] === axisOptions.index ||
  29553. (
  29554. seriesOptions[AXIS] !== undefined &&
  29555. seriesOptions[AXIS] === axisOptions.id
  29556. ) ||
  29557. (
  29558. seriesOptions[AXIS] === undefined &&
  29559. axisOptions.index === 0
  29560. )
  29561. ) {
  29562. // register this series in the axis.series lookup
  29563. series.insert(axis.series);
  29564. // set this series.xAxis or series.yAxis reference
  29565. /**
  29566. * Read only. The unique xAxis object associated
  29567. * with the series.
  29568. *
  29569. * @name Highcharts.Series#xAxis
  29570. * @type {Highcharts.Axis}
  29571. */
  29572. /**
  29573. * Read only. The unique yAxis object associated
  29574. * with the series.
  29575. *
  29576. * @name Highcharts.Series#yAxis
  29577. * @type {Highcharts.Axis}
  29578. */
  29579. series[AXIS] = axis;
  29580. // mark dirty for redraw
  29581. axis.isDirty = true;
  29582. }
  29583. });
  29584. // The series needs an X and an Y axis
  29585. if (!series[AXIS] && series.optionalAxis !== AXIS) {
  29586. H.error(18, true, chart);
  29587. }
  29588. });
  29589. });
  29590. },
  29591. /**
  29592. * For simple series types like line and column, the data values are
  29593. * held in arrays like xData and yData for quick lookup to find extremes
  29594. * and more. For multidimensional series like bubble and map, this can
  29595. * be extended with arrays like zData and valueData by adding to the
  29596. * `series.parallelArrays` array.
  29597. *
  29598. * @private
  29599. * @function Highcharts.Series#updateParallelArrays
  29600. *
  29601. * @param {Highcharts.Point} point
  29602. *
  29603. * @param {number|string} i
  29604. */
  29605. updateParallelArrays: function (point, i) {
  29606. var series = point.series,
  29607. args = arguments,
  29608. fn = isNumber(i) ?
  29609. // Insert the value in the given position
  29610. function (key) {
  29611. var val = key === 'y' && series.toYData ?
  29612. series.toYData(point) :
  29613. point[key];
  29614. series[key + 'Data'][i] = val;
  29615. } :
  29616. // Apply the method specified in i with the following
  29617. // arguments as arguments
  29618. function (key) {
  29619. Array.prototype[i].apply(
  29620. series[key + 'Data'],
  29621. Array.prototype.slice.call(args, 2)
  29622. );
  29623. };
  29624. series.parallelArrays.forEach(fn);
  29625. },
  29626. /**
  29627. * Return an auto incremented x value based on the pointStart and
  29628. * pointInterval options. This is only used if an x value is not given
  29629. * for the point that calls autoIncrement.
  29630. *
  29631. * @private
  29632. * @function Highcharts.Series#autoIncrement
  29633. *
  29634. * @return {number}
  29635. */
  29636. autoIncrement: function () {
  29637. var options = this.options,
  29638. xIncrement = this.xIncrement,
  29639. date,
  29640. pointInterval,
  29641. pointIntervalUnit = options.pointIntervalUnit,
  29642. time = this.chart.time;
  29643. xIncrement = pick(xIncrement, options.pointStart, 0);
  29644. this.pointInterval = pointInterval = pick(
  29645. this.pointInterval,
  29646. options.pointInterval,
  29647. 1
  29648. );
  29649. // Added code for pointInterval strings
  29650. if (pointIntervalUnit) {
  29651. date = new time.Date(xIncrement);
  29652. if (pointIntervalUnit === 'day') {
  29653. time.set(
  29654. 'Date',
  29655. date,
  29656. time.get('Date', date) + pointInterval
  29657. );
  29658. } else if (pointIntervalUnit === 'month') {
  29659. time.set(
  29660. 'Month',
  29661. date,
  29662. time.get('Month', date) + pointInterval
  29663. );
  29664. } else if (pointIntervalUnit === 'year') {
  29665. time.set(
  29666. 'FullYear',
  29667. date,
  29668. time.get('FullYear', date) + pointInterval
  29669. );
  29670. }
  29671. pointInterval = date.getTime() - xIncrement;
  29672. }
  29673. this.xIncrement = xIncrement + pointInterval;
  29674. return xIncrement;
  29675. },
  29676. /**
  29677. * Set the series options by merging from the options tree. Called
  29678. * internally on initiating and updating series. This function will not
  29679. * redraw the series. For API usage, use {@link Series#update}.
  29680. *
  29681. * @function Highcharts.Series#setOptions
  29682. *
  29683. * @param {Highcharts.SeriesOptionsType} itemOptions
  29684. * The series options.
  29685. *
  29686. * @return {Highcharts.SeriesOptionsType}
  29687. *
  29688. * @fires Highcharts.Series#event:afterSetOptions
  29689. */
  29690. setOptions: function (itemOptions) {
  29691. var chart = this.chart,
  29692. chartOptions = chart.options,
  29693. plotOptions = chartOptions.plotOptions,
  29694. userOptions = chart.userOptions || {},
  29695. userPlotOptions = userOptions.plotOptions || {},
  29696. typeOptions = plotOptions[this.type],
  29697. options,
  29698. zones,
  29699. zone,
  29700. styledMode = chart.styledMode;
  29701. // use copy to prevent undetected changes (#9762)
  29702. this.userOptions = merge(itemOptions);
  29703. // General series options take precedence over type options because
  29704. // otherwise, default type options like column.animation would be
  29705. // overwritten by the general option. But issues have been raised
  29706. // here (#3881), and the solution may be to distinguish between
  29707. // default option and userOptions like in the tooltip below.
  29708. options = merge(
  29709. typeOptions,
  29710. plotOptions.series,
  29711. itemOptions
  29712. );
  29713. // The tooltip options are merged between global and series specific
  29714. // options. Importance order asscendingly:
  29715. // globals: (1)tooltip, (2)plotOptions.series,
  29716. // (3)plotOptions[this.type]
  29717. // init userOptions with possible later updates: 4-6 like 1-3 and
  29718. // (7)this series options
  29719. this.tooltipOptions = merge(
  29720. defaultOptions.tooltip, // 1
  29721. defaultOptions.plotOptions.series &&
  29722. defaultOptions.plotOptions.series.tooltip, // 2
  29723. defaultOptions.plotOptions[this.type].tooltip, // 3
  29724. chartOptions.tooltip.userOptions, // 4
  29725. plotOptions.series && plotOptions.series.tooltip, // 5
  29726. plotOptions[this.type].tooltip, // 6
  29727. itemOptions.tooltip // 7
  29728. );
  29729. // When shared tooltip, stickyTracking is true by default,
  29730. // unless user says otherwise.
  29731. this.stickyTracking = pick(
  29732. itemOptions.stickyTracking,
  29733. userPlotOptions[this.type] &&
  29734. userPlotOptions[this.type].stickyTracking,
  29735. userPlotOptions.series && userPlotOptions.series.stickyTracking,
  29736. (
  29737. this.tooltipOptions.shared && !this.noSharedTooltip ?
  29738. true :
  29739. options.stickyTracking
  29740. )
  29741. );
  29742. // Delete marker object if not allowed (#1125)
  29743. if (typeOptions.marker === null) {
  29744. delete options.marker;
  29745. }
  29746. // Handle color zones
  29747. this.zoneAxis = options.zoneAxis;
  29748. zones = this.zones = (options.zones || []).slice();
  29749. if (
  29750. (options.negativeColor || options.negativeFillColor) &&
  29751. !options.zones
  29752. ) {
  29753. zone = {
  29754. value:
  29755. options[this.zoneAxis + 'Threshold'] ||
  29756. options.threshold ||
  29757. 0,
  29758. className: 'highcharts-negative'
  29759. };
  29760. if (!styledMode) {
  29761. zone.color = options.negativeColor;
  29762. zone.fillColor = options.negativeFillColor;
  29763. }
  29764. zones.push(zone);
  29765. }
  29766. if (zones.length) { // Push one extra zone for the rest
  29767. if (defined(zones[zones.length - 1].value)) {
  29768. zones.push(styledMode ? {} : {
  29769. color: this.color,
  29770. fillColor: this.fillColor
  29771. });
  29772. }
  29773. }
  29774. fireEvent(this, 'afterSetOptions', { options: options });
  29775. return options;
  29776. },
  29777. /**
  29778. * Return series name in "Series {Number}" format or the one defined by
  29779. * a user. This method can be simply overridden as series name format
  29780. * can vary (e.g. technical indicators).
  29781. *
  29782. * @function Highcharts.Series#getName
  29783. *
  29784. * @return {string}
  29785. * The series name.
  29786. */
  29787. getName: function () {
  29788. // #4119
  29789. return pick(this.options.name, 'Series ' + (this.index + 1));
  29790. },
  29791. /**
  29792. * @private
  29793. * @function Highcharts.Series#getCyclic
  29794. *
  29795. * @param {string} prop
  29796. *
  29797. * @param {*} value
  29798. *
  29799. * @param {*} [defaults]
  29800. */
  29801. getCyclic: function (prop, value, defaults) {
  29802. var i,
  29803. chart = this.chart,
  29804. userOptions = this.userOptions,
  29805. indexName = prop + 'Index',
  29806. counterName = prop + 'Counter',
  29807. len = defaults ? defaults.length : pick(
  29808. chart.options.chart[prop + 'Count'],
  29809. chart[prop + 'Count']
  29810. ),
  29811. setting;
  29812. if (!value) {
  29813. // Pick up either the colorIndex option, or the _colorIndex
  29814. // after Series.update()
  29815. setting = pick(
  29816. userOptions[indexName],
  29817. userOptions['_' + indexName]
  29818. );
  29819. if (defined(setting)) { // after Series.update()
  29820. i = setting;
  29821. } else {
  29822. // #6138
  29823. if (!chart.series.length) {
  29824. chart[counterName] = 0;
  29825. }
  29826. userOptions['_' + indexName] = i = chart[counterName] % len;
  29827. chart[counterName] += 1;
  29828. }
  29829. if (defaults) {
  29830. value = defaults[i];
  29831. }
  29832. }
  29833. // Set the colorIndex
  29834. if (i !== undefined) {
  29835. this[indexName] = i;
  29836. }
  29837. this[prop] = value;
  29838. },
  29839. /**
  29840. * Get the series' color based on either the options or pulled from
  29841. * global options.
  29842. *
  29843. * @function Highcharts.Series#getColor
  29844. */
  29845. getColor: function () {
  29846. if (this.chart.styledMode) {
  29847. this.getCyclic('color');
  29848. } else if (this.options.colorByPoint) {
  29849. // #4359, selected slice got series.color even when colorByPoint
  29850. // was set.
  29851. this.options.color = null;
  29852. } else {
  29853. this.getCyclic(
  29854. 'color',
  29855. this.options.color || defaultPlotOptions[this.type].color,
  29856. this.chart.options.colors
  29857. );
  29858. }
  29859. },
  29860. /**
  29861. * Get the series' symbol based on either the options or pulled from
  29862. * global options.
  29863. *
  29864. * @function Highcharts.Series#getSymbol
  29865. */
  29866. getSymbol: function () {
  29867. var seriesMarkerOption = this.options.marker;
  29868. this.getCyclic(
  29869. 'symbol',
  29870. seriesMarkerOption.symbol,
  29871. this.chart.options.symbols
  29872. );
  29873. },
  29874. /**
  29875. * @private
  29876. * @borrows LegendSymbolMixin.drawLineMarker as Highcharts.Series#drawLegendSymbol
  29877. */
  29878. drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
  29879. /**
  29880. * Internal function called from setData. If the point count is the same
  29881. * as is was, or if there are overlapping X values, just run
  29882. * Point.update which is cheaper, allows animation, and keeps references
  29883. * to points. This also allows adding or removing points if the X-es
  29884. * don't match.
  29885. *
  29886. * @private
  29887. * @function Highcharts.Series#updateData
  29888. *
  29889. * @param {Array<*>} data
  29890. *
  29891. * @return {boolean}
  29892. */
  29893. updateData: function (data) {
  29894. var options = this.options,
  29895. oldData = this.points,
  29896. pointsToAdd = [],
  29897. hasUpdatedByKey,
  29898. i,
  29899. point,
  29900. lastIndex,
  29901. requireSorting = this.requireSorting;
  29902. this.xIncrement = null;
  29903. // Iterate the new data
  29904. data.forEach(function (pointOptions) {
  29905. var id,
  29906. matchingPoint,
  29907. x,
  29908. pointIndex,
  29909. optionsObject = (
  29910. H.defined(pointOptions) &&
  29911. this.pointClass.prototype.optionsToObject.call(
  29912. { series: this },
  29913. pointOptions
  29914. )
  29915. ) || {};
  29916. // Get the x of the new data point
  29917. x = optionsObject.x;
  29918. id = optionsObject.id;
  29919. if (id || isNumber(x)) {
  29920. if (id) {
  29921. matchingPoint = this.chart.get(id);
  29922. pointIndex = matchingPoint && matchingPoint.index;
  29923. }
  29924. // Search for the same X in the existing data set
  29925. if (pointIndex === undefined && isNumber(x)) {
  29926. pointIndex = this.xData.indexOf(x, lastIndex);
  29927. }
  29928. // Matching X not found
  29929. // or used already due to ununique x values (#8995),
  29930. // add point (but later)
  29931. if (
  29932. pointIndex === -1 ||
  29933. pointIndex === undefined ||
  29934. oldData[pointIndex].touched
  29935. ) {
  29936. pointsToAdd.push(pointOptions);
  29937. // Matching X found, update
  29938. } else if (pointOptions !== options.data[pointIndex]) {
  29939. oldData[pointIndex].update(
  29940. pointOptions,
  29941. false,
  29942. null,
  29943. false
  29944. );
  29945. // Mark it touched, below we will remove all points that
  29946. // are not touched.
  29947. oldData[pointIndex].touched = true;
  29948. // Speed optimize by only searching after last known
  29949. // index. Performs ~20% bettor on large data sets.
  29950. if (requireSorting) {
  29951. lastIndex = pointIndex + 1;
  29952. }
  29953. // Point exists, no changes, don't remove it
  29954. } else if (oldData[pointIndex]) {
  29955. oldData[pointIndex].touched = true;
  29956. }
  29957. hasUpdatedByKey = true;
  29958. }
  29959. }, this);
  29960. // Remove points that don't exist in the updated data set
  29961. if (hasUpdatedByKey) {
  29962. i = oldData.length;
  29963. while (i--) {
  29964. point = oldData[i];
  29965. if (!point.touched) {
  29966. point.remove(false);
  29967. }
  29968. point.touched = false;
  29969. }
  29970. // If we did not find keys (x-values), and the length is the same,
  29971. // update one-to-one
  29972. } else if (data.length === oldData.length) {
  29973. data.forEach(function (point, i) {
  29974. // .update doesn't exist on a linked, hidden series (#3709)
  29975. if (oldData[i].update && point !== options.data[i]) {
  29976. oldData[i].update(point, false, null, false);
  29977. }
  29978. });
  29979. // Did not succeed in updating data
  29980. } else {
  29981. return false;
  29982. }
  29983. // Add new points
  29984. pointsToAdd.forEach(function (point) {
  29985. this.addPoint(point, false);
  29986. }, this);
  29987. return true;
  29988. },
  29989. /**
  29990. * Apply a new set of data to the series and optionally redraw it. The
  29991. * new data array is passed by reference (except in case of
  29992. * `updatePoints`), and may later be mutated when updating the chart
  29993. * data.
  29994. *
  29995. * Note the difference in behaviour when setting the same amount of
  29996. * points, or a different amount of points, as handled by the
  29997. * `updatePoints` parameter.
  29998. *
  29999. * @sample highcharts/members/series-setdata/
  30000. * Set new data from a button
  30001. * @sample highcharts/members/series-setdata-pie/
  30002. * Set data in a pie
  30003. * @sample stock/members/series-setdata/
  30004. * Set new data in Highstock
  30005. * @sample maps/members/series-setdata/
  30006. * Set new data in Highmaps
  30007. *
  30008. * @function Highcharts.Series#setData
  30009. *
  30010. * @param {Array<*>} data
  30011. * Takes an array of data in the same format as described under
  30012. * `series.{type}.data` for the given series type, for example a
  30013. * line series would take data in the form described under
  30014. * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
  30015. *
  30016. * @param {boolean} [redraw=true]
  30017. * Whether to redraw the chart after the series is altered. If
  30018. * doing more operations on the chart, it is a good idea to set
  30019. * redraw to false and call {@link Chart#redraw} after.
  30020. *
  30021. * @param {Highcharts.AnimationOptionsObject} [animation]
  30022. * When the updated data is the same length as the existing data,
  30023. * points will be updated by default, and animation visualizes
  30024. * how the points are changed. Set false to disable animation, or
  30025. * a configuration object to set duration or easing.
  30026. *
  30027. * @param {boolean} [updatePoints=true]
  30028. * When this is true, points will be updated instead of replaced
  30029. * whenever possible. This occurs a) when the updated data is the
  30030. * same length as the existing data, b) when points are matched
  30031. * by their id's, or c) when points can be matched by X values.
  30032. * This allows updating with animation and performs better. In
  30033. * this case, the original array is not passed by reference. Set
  30034. * `false` to prevent.
  30035. */
  30036. setData: function (data, redraw, animation, updatePoints) {
  30037. var series = this,
  30038. oldData = series.points,
  30039. oldDataLength = (oldData && oldData.length) || 0,
  30040. dataLength,
  30041. options = series.options,
  30042. chart = series.chart,
  30043. firstPoint = null,
  30044. xAxis = series.xAxis,
  30045. i,
  30046. turboThreshold = options.turboThreshold,
  30047. pt,
  30048. xData = this.xData,
  30049. yData = this.yData,
  30050. pointArrayMap = series.pointArrayMap,
  30051. valueCount = pointArrayMap && pointArrayMap.length,
  30052. keys = options.keys,
  30053. indexOfX = 0,
  30054. indexOfY = 1,
  30055. updatedData;
  30056. data = data || [];
  30057. dataLength = data.length;
  30058. redraw = pick(redraw, true);
  30059. // If the point count is the same as is was, just run Point.update
  30060. // which is cheaper, allows animation, and keeps references to
  30061. // points.
  30062. if (
  30063. updatePoints !== false &&
  30064. dataLength &&
  30065. oldDataLength &&
  30066. !series.cropped &&
  30067. !series.hasGroupedData &&
  30068. series.visible &&
  30069. // Soft updating has no benefit in boost, and causes JS error
  30070. // (#8355)
  30071. !series.isSeriesBoosting
  30072. ) {
  30073. updatedData = this.updateData(data);
  30074. }
  30075. if (!updatedData) {
  30076. // Reset properties
  30077. series.xIncrement = null;
  30078. series.colorCounter = 0; // for series with colorByPoint (#1547)
  30079. // Update parallel arrays
  30080. this.parallelArrays.forEach(function (key) {
  30081. series[key + 'Data'].length = 0;
  30082. });
  30083. // In turbo mode, only one- or twodimensional arrays of numbers
  30084. // are allowed. The first value is tested, and we assume that
  30085. // all the rest are defined the same way. Although the 'for'
  30086. // loops are similar, they are repeated inside each if-else
  30087. // conditional for max performance.
  30088. if (turboThreshold && dataLength > turboThreshold) {
  30089. // find the first non-null point
  30090. i = 0;
  30091. while (firstPoint === null && i < dataLength) {
  30092. firstPoint = data[i];
  30093. i++;
  30094. }
  30095. if (isNumber(firstPoint)) { // assume all points are numbers
  30096. for (i = 0; i < dataLength; i++) {
  30097. xData[i] = this.autoIncrement();
  30098. yData[i] = data[i];
  30099. }
  30100. // Assume all points are arrays when first point is
  30101. } else if (isArray(firstPoint)) {
  30102. if (valueCount) { // [x, low, high] or [x, o, h, l, c]
  30103. for (i = 0; i < dataLength; i++) {
  30104. pt = data[i];
  30105. xData[i] = pt[0];
  30106. yData[i] = pt.slice(1, valueCount + 1);
  30107. }
  30108. } else { // [x, y]
  30109. if (keys) {
  30110. indexOfX = keys.indexOf('x');
  30111. indexOfY = keys.indexOf('y');
  30112. indexOfX = indexOfX >= 0 ? indexOfX : 0;
  30113. indexOfY = indexOfY >= 0 ? indexOfY : 1;
  30114. }
  30115. for (i = 0; i < dataLength; i++) {
  30116. pt = data[i];
  30117. xData[i] = pt[indexOfX];
  30118. yData[i] = pt[indexOfY];
  30119. }
  30120. }
  30121. } else {
  30122. // Highcharts expects configs to be numbers or arrays in
  30123. // turbo mode
  30124. H.error(12, false, chart);
  30125. }
  30126. } else {
  30127. for (i = 0; i < dataLength; i++) {
  30128. if (data[i] !== undefined) { // stray commas in oldIE
  30129. pt = { series: series };
  30130. series.pointClass.prototype.applyOptions.apply(
  30131. pt,
  30132. [data[i]]
  30133. );
  30134. series.updateParallelArrays(pt, i);
  30135. }
  30136. }
  30137. }
  30138. // Forgetting to cast strings to numbers is a common caveat when
  30139. // handling CSV or JSON
  30140. if (yData && isString(yData[0])) {
  30141. H.error(14, true, chart);
  30142. }
  30143. series.data = [];
  30144. series.options.data = series.userOptions.data = data;
  30145. // destroy old points
  30146. i = oldDataLength;
  30147. while (i--) {
  30148. if (oldData[i] && oldData[i].destroy) {
  30149. oldData[i].destroy();
  30150. }
  30151. }
  30152. // reset minRange (#878)
  30153. if (xAxis) {
  30154. xAxis.minRange = xAxis.userMinRange;
  30155. }
  30156. // redraw
  30157. series.isDirty = chart.isDirtyBox = true;
  30158. series.isDirtyData = !!oldData;
  30159. animation = false;
  30160. }
  30161. // Typically for pie series, points need to be processed and
  30162. // generated prior to rendering the legend
  30163. if (options.legendType === 'point') {
  30164. this.processData();
  30165. this.generatePoints();
  30166. }
  30167. if (redraw) {
  30168. chart.redraw(animation);
  30169. }
  30170. },
  30171. /**
  30172. * Internal function to process the data by cropping away unused data
  30173. * points if the series is longer than the crop threshold. This saves
  30174. * computing time for large series. In Highstock, this function is
  30175. * extended to provide data grouping.
  30176. *
  30177. * @private
  30178. * @function Highcharts.Series#processData
  30179. *
  30180. * @param {boolean} force
  30181. * Force data grouping.
  30182. *
  30183. * @return {boolean|undefined}
  30184. */
  30185. processData: function (force) {
  30186. var series = this,
  30187. processedXData = series.xData, // copied during slice operation
  30188. processedYData = series.yData,
  30189. dataLength = processedXData.length,
  30190. croppedData,
  30191. cropStart = 0,
  30192. cropped,
  30193. distance,
  30194. closestPointRange,
  30195. xAxis = series.xAxis,
  30196. i, // loop variable
  30197. options = series.options,
  30198. cropThreshold = options.cropThreshold,
  30199. getExtremesFromAll =
  30200. series.getExtremesFromAll ||
  30201. options.getExtremesFromAll, // #4599
  30202. isCartesian = series.isCartesian,
  30203. xExtremes,
  30204. val2lin = xAxis && xAxis.val2lin,
  30205. isLog = xAxis && xAxis.isLog,
  30206. throwOnUnsorted = series.requireSorting,
  30207. min,
  30208. max;
  30209. // If the series data or axes haven't changed, don't go through
  30210. // this. Return false to pass the message on to override methods
  30211. // like in data grouping.
  30212. if (
  30213. isCartesian &&
  30214. !series.isDirty &&
  30215. !xAxis.isDirty &&
  30216. !series.yAxis.isDirty &&
  30217. !force
  30218. ) {
  30219. return false;
  30220. }
  30221. if (xAxis) {
  30222. // corrected for log axis (#3053)
  30223. xExtremes = xAxis.getExtremes();
  30224. min = xExtremes.min;
  30225. max = xExtremes.max;
  30226. }
  30227. // optionally filter out points outside the plot area
  30228. if (
  30229. isCartesian &&
  30230. series.sorted &&
  30231. !getExtremesFromAll &&
  30232. (
  30233. !cropThreshold ||
  30234. dataLength > cropThreshold ||
  30235. series.forceCrop
  30236. )
  30237. ) {
  30238. // it's outside current extremes
  30239. if (
  30240. processedXData[dataLength - 1] < min ||
  30241. processedXData[0] > max
  30242. ) {
  30243. processedXData = [];
  30244. processedYData = [];
  30245. // only crop if it's actually spilling out
  30246. } else if (
  30247. series.yData && (
  30248. processedXData[0] < min ||
  30249. processedXData[dataLength - 1] > max
  30250. )
  30251. ) {
  30252. croppedData = this.cropData(
  30253. series.xData,
  30254. series.yData,
  30255. min,
  30256. max
  30257. );
  30258. processedXData = croppedData.xData;
  30259. processedYData = croppedData.yData;
  30260. cropStart = croppedData.start;
  30261. cropped = true;
  30262. }
  30263. }
  30264. // Find the closest distance between processed points
  30265. i = processedXData.length || 1;
  30266. while (--i) {
  30267. distance = (
  30268. isLog ?
  30269. (
  30270. val2lin(processedXData[i]) -
  30271. val2lin(processedXData[i - 1])
  30272. ) :
  30273. processedXData[i] - processedXData[i - 1]
  30274. );
  30275. if (
  30276. distance > 0 &&
  30277. (
  30278. closestPointRange === undefined ||
  30279. distance < closestPointRange
  30280. )
  30281. ) {
  30282. closestPointRange = distance;
  30283. // Unsorted data is not supported by the line tooltip, as well
  30284. // as data grouping and navigation in Stock charts (#725) and
  30285. // width calculation of columns (#1900)
  30286. } else if (distance < 0 && throwOnUnsorted) {
  30287. H.error(15, false, series.chart);
  30288. throwOnUnsorted = false; // Only once
  30289. }
  30290. }
  30291. // Record the properties
  30292. series.cropped = cropped; // undefined or true
  30293. series.cropStart = cropStart;
  30294. series.processedXData = processedXData;
  30295. series.processedYData = processedYData;
  30296. series.closestPointRange = closestPointRange;
  30297. },
  30298. /**
  30299. * Iterate over xData and crop values between min and max. Returns
  30300. * object containing crop start/end cropped xData with corresponding
  30301. * part of yData, dataMin and dataMax within the cropped range.
  30302. *
  30303. * @private
  30304. * @function Highcharts.Series#cropData
  30305. *
  30306. * @param {Array<number>} xData
  30307. *
  30308. * @param {Array<number>} yData
  30309. *
  30310. * @param {number} min
  30311. *
  30312. * @param {number} max
  30313. *
  30314. * @param {number} [cropShoulder]
  30315. *
  30316. * @return {*}
  30317. */
  30318. cropData: function (xData, yData, min, max, cropShoulder) {
  30319. var dataLength = xData.length,
  30320. cropStart = 0,
  30321. cropEnd = dataLength,
  30322. i,
  30323. j;
  30324. // line-type series need one point outside
  30325. cropShoulder = pick(cropShoulder, this.cropShoulder, 1);
  30326. // iterate up to find slice start
  30327. for (i = 0; i < dataLength; i++) {
  30328. if (xData[i] >= min) {
  30329. cropStart = Math.max(0, i - cropShoulder);
  30330. break;
  30331. }
  30332. }
  30333. // proceed to find slice end
  30334. for (j = i; j < dataLength; j++) {
  30335. if (xData[j] > max) {
  30336. cropEnd = j + cropShoulder;
  30337. break;
  30338. }
  30339. }
  30340. return {
  30341. xData: xData.slice(cropStart, cropEnd),
  30342. yData: yData.slice(cropStart, cropEnd),
  30343. start: cropStart,
  30344. end: cropEnd
  30345. };
  30346. },
  30347. /**
  30348. * Generate the data point after the data has been processed by cropping
  30349. * away unused points and optionally grouped in Highcharts Stock.
  30350. *
  30351. * @private
  30352. * @function Highcharts.Series#generatePoints
  30353. */
  30354. generatePoints: function () {
  30355. var series = this,
  30356. options = series.options,
  30357. dataOptions = options.data,
  30358. data = series.data,
  30359. dataLength,
  30360. processedXData = series.processedXData,
  30361. processedYData = series.processedYData,
  30362. PointClass = series.pointClass,
  30363. processedDataLength = processedXData.length,
  30364. cropStart = series.cropStart || 0,
  30365. cursor,
  30366. hasGroupedData = series.hasGroupedData,
  30367. keys = options.keys,
  30368. point,
  30369. points = [],
  30370. i;
  30371. if (!data && !hasGroupedData) {
  30372. var arr = [];
  30373. arr.length = dataOptions.length;
  30374. data = series.data = arr;
  30375. }
  30376. if (keys && hasGroupedData) {
  30377. // grouped data has already applied keys (#6590)
  30378. series.options.keys = false;
  30379. }
  30380. for (i = 0; i < processedDataLength; i++) {
  30381. cursor = cropStart + i;
  30382. if (!hasGroupedData) {
  30383. point = data[cursor];
  30384. if (!point && dataOptions[cursor] !== undefined) { // #970
  30385. data[cursor] = point = (new PointClass()).init(
  30386. series,
  30387. dataOptions[cursor],
  30388. processedXData[i]
  30389. );
  30390. }
  30391. } else {
  30392. // splat the y data in case of ohlc data array
  30393. point = (new PointClass()).init(
  30394. series,
  30395. [processedXData[i]].concat(splat(processedYData[i]))
  30396. );
  30397. /**
  30398. * Highstock only. If a point object is created by data
  30399. * grouping, it doesn't reflect actual points in the raw
  30400. * data. In this case, the `dataGroup` property holds
  30401. * information that points back to the raw data.
  30402. *
  30403. * - `dataGroup.start` is the index of the first raw data
  30404. * point in the group.
  30405. *
  30406. * - `dataGroup.length` is the amount of points in the
  30407. * group.
  30408. *
  30409. * @product highstock
  30410. *
  30411. * @name Highcharts.Point#dataGroup
  30412. * @type {Highcharts.SVGElement|undefined}
  30413. */
  30414. point.dataGroup = series.groupMap[i];
  30415. if (point.dataGroup.options) {
  30416. point.options = point.dataGroup.options;
  30417. extend(point, point.dataGroup.options);
  30418. // Collision of props and options (#9770)
  30419. delete point.dataLabels;
  30420. }
  30421. }
  30422. if (point) { // #6279
  30423. point.index = cursor; // For faster access in Point.update
  30424. points[i] = point;
  30425. }
  30426. }
  30427. // restore keys options (#6590)
  30428. series.options.keys = keys;
  30429. // Hide cropped-away points - this only runs when the number of
  30430. // points is above cropThreshold, or when swithching view from
  30431. // non-grouped data to grouped data (#637)
  30432. if (
  30433. data &&
  30434. (
  30435. processedDataLength !== (dataLength = data.length) ||
  30436. hasGroupedData
  30437. )
  30438. ) {
  30439. for (i = 0; i < dataLength; i++) {
  30440. // when has grouped data, clear all points
  30441. if (i === cropStart && !hasGroupedData) {
  30442. i += processedDataLength;
  30443. }
  30444. if (data[i]) {
  30445. data[i].destroyElements();
  30446. data[i].plotX = undefined; // #1003
  30447. }
  30448. }
  30449. }
  30450. /**
  30451. * Read only. An array containing those values converted to points.
  30452. * In case the series data length exceeds the `cropThreshold`, or if
  30453. * the data is grouped, `series.data` doesn't contain all the
  30454. * points. Also, in case a series is hidden, the `data` array may be
  30455. * empty. To access raw values, `series.options.data` will always be
  30456. * up to date. `Series.data` only contains the points that have been
  30457. * created on demand. To modify the data, use
  30458. * {@link Highcharts.Series#setData} or
  30459. * {@link Highcharts.Point#update}.
  30460. *
  30461. * @see Series.points
  30462. *
  30463. * @name Highcharts.Series#data
  30464. * @type {Array<Highcharts.Point>}
  30465. */
  30466. series.data = data;
  30467. /**
  30468. * An array containing all currently visible point objects. In case
  30469. * of cropping, the cropped-away points are not part of this array.
  30470. * The `series.points` array starts at `series.cropStart` compared
  30471. * to `series.data` and `series.options.data`. If however the series
  30472. * data is grouped, these can't be correlated one to one. To modify
  30473. * the data, use {@link Highcharts.Series#setData} or
  30474. * {@link Highcharts.Point#update}.
  30475. *
  30476. * @name Highcharts.Series#points
  30477. * @type {Array<Highcharts.Point>}
  30478. */
  30479. series.points = points;
  30480. fireEvent(this, 'afterGeneratePoints');
  30481. },
  30482. /**
  30483. * Calculate Y extremes for the visible data. The result is set as
  30484. * `dataMin` and `dataMax` on the Series item.
  30485. *
  30486. * @function Highcharts.Series#getExtremes
  30487. *
  30488. * @param {Array<number>} [yData]
  30489. * The data to inspect. Defaults to the current data within the
  30490. * visible range.
  30491. */
  30492. getExtremes: function (yData) {
  30493. var xAxis = this.xAxis,
  30494. yAxis = this.yAxis,
  30495. xData = this.processedXData,
  30496. yDataLength,
  30497. activeYData = [],
  30498. activeCounter = 0,
  30499. // #2117, need to compensate for log X axis
  30500. xExtremes = xAxis.getExtremes(),
  30501. xMin = xExtremes.min,
  30502. xMax = xExtremes.max,
  30503. validValue,
  30504. withinRange,
  30505. // Handle X outside the viewed area. This does not work with
  30506. // non-sorted data like scatter (#7639).
  30507. shoulder = this.requireSorting ? 1 : 0,
  30508. x,
  30509. y,
  30510. i,
  30511. j;
  30512. yData = yData || this.stackedYData || this.processedYData || [];
  30513. yDataLength = yData.length;
  30514. for (i = 0; i < yDataLength; i++) {
  30515. x = xData[i];
  30516. y = yData[i];
  30517. // For points within the visible range, including the first
  30518. // point outside the visible range (#7061), consider y extremes.
  30519. validValue = (
  30520. (isNumber(y, true) || isArray(y)) &&
  30521. (!yAxis.positiveValuesOnly || (y.length || y > 0))
  30522. );
  30523. withinRange = (
  30524. this.getExtremesFromAll ||
  30525. this.options.getExtremesFromAll ||
  30526. this.cropped ||
  30527. (
  30528. (xData[i + shoulder] || x) >= xMin &&
  30529. (xData[i - shoulder] || x) <= xMax
  30530. )
  30531. );
  30532. if (validValue && withinRange) {
  30533. j = y.length;
  30534. if (j) { // array, like ohlc or range data
  30535. while (j--) {
  30536. if (typeof y[j] === 'number') { // #7380
  30537. activeYData[activeCounter++] = y[j];
  30538. }
  30539. }
  30540. } else {
  30541. activeYData[activeCounter++] = y;
  30542. }
  30543. }
  30544. }
  30545. this.dataMin = arrayMin(activeYData);
  30546. this.dataMax = arrayMax(activeYData);
  30547. fireEvent(this, 'afterGetExtremes');
  30548. },
  30549. /**
  30550. * Translate data points from raw data values to chart specific
  30551. * positioning data needed later in the `drawPoints` and `drawGraph`
  30552. * functions. This function can be overridden in plugins and custom
  30553. * series type implementations.
  30554. *
  30555. * @function Highcharts.Series#translate
  30556. *
  30557. * @fires Highcharts.Series#events:translate
  30558. */
  30559. translate: function () {
  30560. if (!this.processedXData) { // hidden series
  30561. this.processData();
  30562. }
  30563. this.generatePoints();
  30564. var series = this,
  30565. options = series.options,
  30566. stacking = options.stacking,
  30567. xAxis = series.xAxis,
  30568. categories = xAxis.categories,
  30569. yAxis = series.yAxis,
  30570. points = series.points,
  30571. dataLength = points.length,
  30572. hasModifyValue = !!series.modifyValue,
  30573. i,
  30574. pointPlacement = series.pointPlacementToXValue(), // #7860
  30575. dynamicallyPlaced = isNumber(pointPlacement),
  30576. threshold = options.threshold,
  30577. stackThreshold = options.startFromThreshold ? threshold : 0,
  30578. plotX,
  30579. plotY,
  30580. lastPlotX,
  30581. stackIndicator,
  30582. zoneAxis = this.zoneAxis || 'y',
  30583. closestPointRangePx = Number.MAX_VALUE;
  30584. // Plotted coordinates need to be within a limited range. Drawing
  30585. // too far outside the viewport causes various rendering issues
  30586. // (#3201, #3923, #7555).
  30587. function limitedRange(val) {
  30588. return Math.min(Math.max(-1e5, val), 1e5);
  30589. }
  30590. // Translate each point
  30591. for (i = 0; i < dataLength; i++) {
  30592. var point = points[i],
  30593. xValue = point.x,
  30594. yValue = point.y,
  30595. yBottom = point.low,
  30596. stack = stacking && yAxis.stacks[(
  30597. series.negStacks &&
  30598. yValue < (stackThreshold ? 0 : threshold) ? '-' : ''
  30599. ) + series.stackKey],
  30600. pointStack,
  30601. stackValues;
  30602. // Discard disallowed y values for log axes (#3434)
  30603. if (yAxis.positiveValuesOnly &&
  30604. yValue !== null &&
  30605. yValue <= 0
  30606. ) {
  30607. point.isNull = true;
  30608. }
  30609. // Get the plotX translation
  30610. point.plotX = plotX = correctFloat( // #5236
  30611. limitedRange(xAxis.translate( // #3923
  30612. xValue,
  30613. 0,
  30614. 0,
  30615. 0,
  30616. 1,
  30617. pointPlacement,
  30618. this.type === 'flags'
  30619. )) // #3923
  30620. );
  30621. // Calculate the bottom y value for stacked series
  30622. if (
  30623. stacking &&
  30624. series.visible &&
  30625. !point.isNull &&
  30626. stack &&
  30627. stack[xValue]
  30628. ) {
  30629. stackIndicator = series.getStackIndicator(
  30630. stackIndicator,
  30631. xValue,
  30632. series.index
  30633. );
  30634. pointStack = stack[xValue];
  30635. stackValues = pointStack.points[stackIndicator.key];
  30636. yBottom = stackValues[0];
  30637. yValue = stackValues[1];
  30638. if (
  30639. yBottom === stackThreshold &&
  30640. stackIndicator.key === stack[xValue].base
  30641. ) {
  30642. yBottom = (
  30643. pick(isNumber(threshold) && threshold, yAxis.min)
  30644. );
  30645. }
  30646. // #1200, #1232
  30647. if (yAxis.positiveValuesOnly && yBottom <= 0) {
  30648. yBottom = null;
  30649. }
  30650. point.total = point.stackTotal = pointStack.total;
  30651. point.percentage =
  30652. pointStack.total &&
  30653. (point.y / pointStack.total * 100);
  30654. point.stackY = yValue;
  30655. // Place the stack label
  30656. pointStack.setOffset(
  30657. series.pointXOffset || 0,
  30658. series.barW || 0
  30659. );
  30660. }
  30661. // Set translated yBottom or remove it
  30662. point.yBottom = defined(yBottom) ?
  30663. limitedRange(yAxis.translate(yBottom, 0, 1, 0, 1)) :
  30664. null;
  30665. // general hook, used for Highstock compare mode
  30666. if (hasModifyValue) {
  30667. yValue = series.modifyValue(yValue, point);
  30668. }
  30669. // Set the the plotY value, reset it for redraws
  30670. // #3201
  30671. point.plotY = plotY = (
  30672. (typeof yValue === 'number' && yValue !== Infinity) ?
  30673. limitedRange(yAxis.translate(yValue, 0, 1, 0, 1)) :
  30674. undefined
  30675. );
  30676. point.isInside =
  30677. plotY !== undefined &&
  30678. plotY >= 0 &&
  30679. plotY <= yAxis.len && // #3519
  30680. plotX >= 0 &&
  30681. plotX <= xAxis.len;
  30682. // Set client related positions for mouse tracking
  30683. point.clientX = dynamicallyPlaced ?
  30684. correctFloat(
  30685. xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)
  30686. ) :
  30687. plotX; // #1514, #5383, #5518
  30688. // Negative points. For bubble charts, this means negative z
  30689. // values (#9728)
  30690. point.negative = point[zoneAxis] < (
  30691. options[zoneAxis + 'Threshold'] ||
  30692. threshold ||
  30693. 0
  30694. );
  30695. // some API data
  30696. point.category = (
  30697. categories &&
  30698. categories[point.x] !== undefined ?
  30699. categories[point.x] :
  30700. point.x
  30701. );
  30702. // Determine auto enabling of markers (#3635, #5099)
  30703. if (!point.isNull) {
  30704. if (lastPlotX !== undefined) {
  30705. closestPointRangePx = Math.min(
  30706. closestPointRangePx,
  30707. Math.abs(plotX - lastPlotX)
  30708. );
  30709. }
  30710. lastPlotX = plotX;
  30711. }
  30712. // Find point zone
  30713. point.zone = this.zones.length && point.getZone();
  30714. }
  30715. series.closestPointRangePx = closestPointRangePx;
  30716. fireEvent(this, 'afterTranslate');
  30717. },
  30718. /**
  30719. * Return the series points with null points filtered out.
  30720. *
  30721. * @param {Array<Highcharts.Point>} [points]
  30722. * The points to inspect, defaults to {@link Series.points}.
  30723. *
  30724. * @param {boolean} [insideOnly=false]
  30725. * Whether to inspect only the points that are inside the visible
  30726. * view.
  30727. *
  30728. * @return {Array<Highcharts.Point>}
  30729. * The valid points.
  30730. */
  30731. getValidPoints: function (points, insideOnly) {
  30732. var chart = this.chart;
  30733. // #3916, #5029, #5085
  30734. return (points || this.points || []).filter(
  30735. function isValidPoint(point) {
  30736. if (insideOnly && !chart.isInsidePlot(
  30737. point.plotX,
  30738. point.plotY,
  30739. chart.inverted
  30740. )) {
  30741. return false;
  30742. }
  30743. return !point.isNull;
  30744. }
  30745. );
  30746. },
  30747. /**
  30748. * Set the clipping for the series. For animated series it is called
  30749. * twice, first to initiate animating the clip then the second time
  30750. * without the animation to set the final clip.
  30751. *
  30752. * @private
  30753. * @function Highcharts.Series#setClip
  30754. *
  30755. * @param {boolean} [animation]
  30756. */
  30757. setClip: function (animation) {
  30758. var chart = this.chart,
  30759. options = this.options,
  30760. renderer = chart.renderer,
  30761. inverted = chart.inverted,
  30762. seriesClipBox = this.clipBox,
  30763. clipBox = seriesClipBox || chart.clipBox,
  30764. sharedClipKey =
  30765. this.sharedClipKey ||
  30766. [
  30767. '_sharedClip',
  30768. animation && animation.duration,
  30769. animation && animation.easing,
  30770. clipBox.height,
  30771. options.xAxis,
  30772. options.yAxis
  30773. ].join(','), // #4526
  30774. clipRect = chart[sharedClipKey],
  30775. markerClipRect = chart[sharedClipKey + 'm'];
  30776. // If a clipping rectangle with the same properties is currently
  30777. // present in the chart, use that.
  30778. if (!clipRect) {
  30779. // When animation is set, prepare the initial positions
  30780. if (animation) {
  30781. clipBox.width = 0;
  30782. if (inverted) {
  30783. clipBox.x = chart.plotSizeX;
  30784. }
  30785. chart[sharedClipKey + 'm'] = markerClipRect = renderer
  30786. .clipRect(
  30787. // include the width of the first marker
  30788. inverted ? chart.plotSizeX + 99 : -99,
  30789. inverted ? -chart.plotLeft : -chart.plotTop,
  30790. 99,
  30791. inverted ? chart.chartWidth : chart.chartHeight
  30792. );
  30793. }
  30794. chart[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
  30795. // Create hashmap for series indexes
  30796. clipRect.count = { length: 0 };
  30797. }
  30798. if (animation) {
  30799. if (!clipRect.count[this.index]) {
  30800. clipRect.count[this.index] = true;
  30801. clipRect.count.length += 1;
  30802. }
  30803. }
  30804. if (options.clip !== false) {
  30805. this.group.clip(
  30806. animation || seriesClipBox ? clipRect : chart.clipRect
  30807. );
  30808. this.markerGroup.clip(markerClipRect);
  30809. this.sharedClipKey = sharedClipKey;
  30810. }
  30811. // Remove the shared clipping rectangle when all series are shown
  30812. if (!animation) {
  30813. if (clipRect.count[this.index]) {
  30814. delete clipRect.count[this.index];
  30815. clipRect.count.length -= 1;
  30816. }
  30817. if (
  30818. clipRect.count.length === 0 &&
  30819. sharedClipKey &&
  30820. chart[sharedClipKey]
  30821. ) {
  30822. if (!seriesClipBox) {
  30823. chart[sharedClipKey] = chart[sharedClipKey].destroy();
  30824. }
  30825. if (chart[sharedClipKey + 'm']) {
  30826. chart[sharedClipKey + 'm'] =
  30827. chart[sharedClipKey + 'm'].destroy();
  30828. }
  30829. }
  30830. }
  30831. },
  30832. /**
  30833. * Animate in the series. Called internally twice. First with the `init`
  30834. * parameter set to true, which sets up the initial state of the
  30835. * animation. Then when ready, it is called with the `init` parameter
  30836. * undefined, in order to perform the actual animation. After the
  30837. * second run, the function is removed.
  30838. *
  30839. * @function Highcharts.Series#animate
  30840. *
  30841. * @param {boolean} init
  30842. * Initialize the animation.
  30843. */
  30844. animate: function (init) {
  30845. var series = this,
  30846. chart = series.chart,
  30847. clipRect,
  30848. animation = animObject(series.options.animation),
  30849. sharedClipKey;
  30850. // Initialize the animation. Set up the clipping rectangle.
  30851. if (init) {
  30852. series.setClip(animation);
  30853. // Run the animation
  30854. } else {
  30855. sharedClipKey = this.sharedClipKey;
  30856. clipRect = chart[sharedClipKey];
  30857. if (clipRect) {
  30858. clipRect.animate({
  30859. width: chart.plotSizeX,
  30860. x: 0
  30861. }, animation);
  30862. }
  30863. if (chart[sharedClipKey + 'm']) {
  30864. chart[sharedClipKey + 'm'].animate({
  30865. width: chart.plotSizeX + 99,
  30866. x: 0
  30867. }, animation);
  30868. }
  30869. // Delete this function to allow it only once
  30870. series.animate = null;
  30871. }
  30872. },
  30873. /**
  30874. * This runs after animation to land on the final plot clipping.
  30875. *
  30876. * @private
  30877. * @function Highcharts.Series#afterAnimate
  30878. *
  30879. * @fires Highcharts.Series#event:afterAnimate
  30880. */
  30881. afterAnimate: function () {
  30882. this.setClip();
  30883. fireEvent(this, 'afterAnimate');
  30884. this.finishedAnimating = true;
  30885. },
  30886. /**
  30887. * Draw the markers for line-like series types, and columns or other
  30888. * graphical representation for {@link Point} objects for other series
  30889. * types. The resulting element is typically stored as
  30890. * {@link Point.graphic}, and is created on the first call and updated
  30891. * and moved on subsequent calls.
  30892. *
  30893. * @function Highcharts.Series#drawPoints
  30894. */
  30895. drawPoints: function () {
  30896. var series = this,
  30897. points = series.points,
  30898. chart = series.chart,
  30899. i,
  30900. point,
  30901. symbol,
  30902. graphic,
  30903. options = series.options,
  30904. seriesMarkerOptions = options.marker,
  30905. pointMarkerOptions,
  30906. hasPointMarker,
  30907. enabled,
  30908. isInside,
  30909. markerGroup = series[series.specialGroup] || series.markerGroup,
  30910. xAxis = series.xAxis,
  30911. markerAttribs,
  30912. globallyEnabled = pick(
  30913. seriesMarkerOptions.enabled,
  30914. !xAxis || xAxis.isRadial ? true : null,
  30915. // Use larger or equal as radius is null in bubbles (#6321)
  30916. series.closestPointRangePx >= (
  30917. seriesMarkerOptions.enabledThreshold *
  30918. seriesMarkerOptions.radius
  30919. )
  30920. );
  30921. if (seriesMarkerOptions.enabled !== false ||
  30922. series._hasPointMarkers
  30923. ) {
  30924. for (i = 0; i < points.length; i++) {
  30925. point = points[i];
  30926. graphic = point.graphic;
  30927. pointMarkerOptions = point.marker || {};
  30928. hasPointMarker = !!point.marker;
  30929. enabled = (
  30930. globallyEnabled &&
  30931. pointMarkerOptions.enabled === undefined
  30932. ) || pointMarkerOptions.enabled;
  30933. isInside = point.isInside !== false;
  30934. // only draw the point if y is defined
  30935. if (enabled && !point.isNull) {
  30936. // Shortcuts
  30937. symbol = pick(pointMarkerOptions.symbol, series.symbol);
  30938. markerAttribs = series.markerAttribs(
  30939. point,
  30940. point.selected && 'select'
  30941. );
  30942. if (graphic) { // update
  30943. // Since the marker group isn't clipped, each
  30944. // individual marker must be toggled
  30945. graphic[isInside ? 'show' : 'hide'](true)
  30946. .animate(markerAttribs);
  30947. } else if (
  30948. isInside &&
  30949. (markerAttribs.width > 0 || point.hasImage)
  30950. ) {
  30951. /**
  30952. * The graphic representation of the point.
  30953. * Typically this is a simple shape, like a `rect`
  30954. * for column charts or `path` for line markers, but
  30955. * for some complex series types like boxplot or 3D
  30956. * charts, the graphic may be a `g` element
  30957. * containing other shapes. The graphic is generated
  30958. * the first time {@link Series#drawPoints} runs,
  30959. * and updated and moved on subsequent runs.
  30960. *
  30961. * @name Point#graphic
  30962. * @type {SVGElement}
  30963. */
  30964. point.graphic = graphic = chart.renderer
  30965. .symbol(
  30966. symbol,
  30967. markerAttribs.x,
  30968. markerAttribs.y,
  30969. markerAttribs.width,
  30970. markerAttribs.height,
  30971. hasPointMarker ?
  30972. pointMarkerOptions :
  30973. seriesMarkerOptions
  30974. )
  30975. .add(markerGroup);
  30976. }
  30977. // Presentational attributes
  30978. if (graphic && !chart.styledMode) {
  30979. graphic.attr(
  30980. series.pointAttribs(
  30981. point,
  30982. point.selected && 'select'
  30983. )
  30984. );
  30985. }
  30986. if (graphic) {
  30987. graphic.addClass(point.getClassName(), true);
  30988. }
  30989. } else if (graphic) {
  30990. point.graphic = graphic.destroy(); // #1269
  30991. }
  30992. }
  30993. }
  30994. },
  30995. /**
  30996. * Get non-presentational attributes for a point. Used internally for
  30997. * both styled mode and classic. Can be overridden for different series
  30998. * types.
  30999. *
  31000. * @see Series#pointAttribs
  31001. *
  31002. * @function Highcharts.Series#markerAttribs
  31003. *
  31004. * @param {Highcharts.Point} point
  31005. * The Point to inspect.
  31006. *
  31007. * @param {string} [state]
  31008. * The state, can be either `hover`, `select` or undefined.
  31009. *
  31010. * @return {Highcharts.SVGAttributes}
  31011. * A hash containing those attributes that are not settable from
  31012. * CSS.
  31013. */
  31014. markerAttribs: function (point, state) {
  31015. var seriesMarkerOptions = this.options.marker,
  31016. seriesStateOptions,
  31017. pointMarkerOptions = point.marker || {},
  31018. symbol = (
  31019. pointMarkerOptions.symbol || seriesMarkerOptions.symbol
  31020. ),
  31021. pointStateOptions,
  31022. radius = pick(
  31023. pointMarkerOptions.radius,
  31024. seriesMarkerOptions.radius
  31025. ),
  31026. attribs;
  31027. // Handle hover and select states
  31028. if (state) {
  31029. seriesStateOptions = seriesMarkerOptions.states[state];
  31030. pointStateOptions = pointMarkerOptions.states &&
  31031. pointMarkerOptions.states[state];
  31032. radius = pick(
  31033. pointStateOptions && pointStateOptions.radius,
  31034. seriesStateOptions && seriesStateOptions.radius,
  31035. radius + (
  31036. seriesStateOptions && seriesStateOptions.radiusPlus ||
  31037. 0
  31038. )
  31039. );
  31040. }
  31041. point.hasImage = symbol && symbol.indexOf('url') === 0;
  31042. if (point.hasImage) {
  31043. radius = 0; // and subsequently width and height is not set
  31044. }
  31045. attribs = {
  31046. x: Math.floor(point.plotX) - radius, // Math.floor for #1843
  31047. y: point.plotY - radius
  31048. };
  31049. if (radius) {
  31050. attribs.width = attribs.height = 2 * radius;
  31051. }
  31052. return attribs;
  31053. },
  31054. /**
  31055. * Internal function to get presentational attributes for each point.
  31056. * Unlike {@link Series#markerAttribs}, this function should return
  31057. * those attributes that can also be set in CSS. In styled mode,
  31058. * `pointAttribs` won't be called.
  31059. *
  31060. * @private
  31061. * @function Highcharts.Series#pointAttribs
  31062. *
  31063. * @param {Highcharts.Point} point
  31064. * The point instance to inspect.
  31065. *
  31066. * @param {string} [state]
  31067. * The point state, can be either `hover`, `select` or undefined
  31068. * for normal state.
  31069. *
  31070. * @return {Highcharts.SVGAttributes}
  31071. * The presentational attributes to be set on the point.
  31072. */
  31073. pointAttribs: function (point, state) {
  31074. var seriesMarkerOptions = this.options.marker,
  31075. seriesStateOptions,
  31076. pointOptions = point && point.options,
  31077. pointMarkerOptions = (
  31078. (pointOptions && pointOptions.marker) || {}
  31079. ),
  31080. pointStateOptions,
  31081. color = this.color,
  31082. pointColorOption = pointOptions && pointOptions.color,
  31083. pointColor = point && point.color,
  31084. strokeWidth = pick(
  31085. pointMarkerOptions.lineWidth,
  31086. seriesMarkerOptions.lineWidth
  31087. ),
  31088. zoneColor = point && point.zone && point.zone.color,
  31089. fill,
  31090. stroke;
  31091. color = (
  31092. pointColorOption ||
  31093. zoneColor ||
  31094. pointColor ||
  31095. color
  31096. );
  31097. fill = (
  31098. pointMarkerOptions.fillColor ||
  31099. seriesMarkerOptions.fillColor ||
  31100. color
  31101. );
  31102. stroke = (
  31103. pointMarkerOptions.lineColor ||
  31104. seriesMarkerOptions.lineColor ||
  31105. color
  31106. );
  31107. // Handle hover and select states
  31108. if (state) {
  31109. seriesStateOptions = seriesMarkerOptions.states[state];
  31110. pointStateOptions = (
  31111. pointMarkerOptions.states &&
  31112. pointMarkerOptions.states[state]
  31113. ) || {};
  31114. strokeWidth = pick(
  31115. pointStateOptions.lineWidth,
  31116. seriesStateOptions.lineWidth,
  31117. strokeWidth + pick(
  31118. pointStateOptions.lineWidthPlus,
  31119. seriesStateOptions.lineWidthPlus,
  31120. 0
  31121. )
  31122. );
  31123. fill = (
  31124. pointStateOptions.fillColor ||
  31125. seriesStateOptions.fillColor ||
  31126. fill
  31127. );
  31128. stroke = (
  31129. pointStateOptions.lineColor ||
  31130. seriesStateOptions.lineColor ||
  31131. stroke
  31132. );
  31133. }
  31134. return {
  31135. 'stroke': stroke,
  31136. 'stroke-width': strokeWidth,
  31137. 'fill': fill
  31138. };
  31139. },
  31140. /**
  31141. * Clear DOM objects and free up memory.
  31142. *
  31143. * @private
  31144. * @function Highcharts.Series#destroy
  31145. *
  31146. * @fires Highcharts.Series#event:destroy
  31147. */
  31148. destroy: function () {
  31149. var series = this,
  31150. chart = series.chart,
  31151. issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent),
  31152. destroy,
  31153. i,
  31154. data = series.data || [],
  31155. point,
  31156. axis;
  31157. // add event hook
  31158. fireEvent(series, 'destroy');
  31159. // remove all events
  31160. removeEvent(series);
  31161. // erase from axes
  31162. (series.axisTypes || []).forEach(function (AXIS) {
  31163. axis = series[AXIS];
  31164. if (axis && axis.series) {
  31165. erase(axis.series, series);
  31166. axis.isDirty = axis.forceRedraw = true;
  31167. }
  31168. });
  31169. // remove legend items
  31170. if (series.legendItem) {
  31171. series.chart.legend.destroyItem(series);
  31172. }
  31173. // destroy all points with their elements
  31174. i = data.length;
  31175. while (i--) {
  31176. point = data[i];
  31177. if (point && point.destroy) {
  31178. point.destroy();
  31179. }
  31180. }
  31181. series.points = null;
  31182. // Clear the animation timeout if we are destroying the series
  31183. // during initial animation
  31184. H.clearTimeout(series.animationTimeout);
  31185. // Destroy all SVGElements associated to the series
  31186. objectEach(series, function (val, prop) {
  31187. // Survive provides a hook for not destroying
  31188. if (val instanceof SVGElement && !val.survive) {
  31189. // issue 134 workaround
  31190. destroy = issue134 && prop === 'group' ?
  31191. 'hide' :
  31192. 'destroy';
  31193. val[destroy]();
  31194. }
  31195. });
  31196. // remove from hoverSeries
  31197. if (chart.hoverSeries === series) {
  31198. chart.hoverSeries = null;
  31199. }
  31200. erase(chart.series, series);
  31201. chart.orderSeries();
  31202. // clear all members
  31203. objectEach(series, function (val, prop) {
  31204. delete series[prop];
  31205. });
  31206. },
  31207. /**
  31208. * Get the graph path.
  31209. *
  31210. * @private
  31211. * @function Highcharts.Series#getGraphPath
  31212. *
  31213. * @param {Array<*>} points
  31214. *
  31215. * @param {boolean} nullsAsZeroes
  31216. *
  31217. * @param {boolean} connectCliffs
  31218. *
  31219. * @return {Array<number|string>}
  31220. */
  31221. getGraphPath: function (points, nullsAsZeroes, connectCliffs) {
  31222. var series = this,
  31223. options = series.options,
  31224. step = options.step,
  31225. reversed,
  31226. graphPath = [],
  31227. xMap = [],
  31228. gap;
  31229. points = points || series.points;
  31230. // Bottom of a stack is reversed
  31231. reversed = points.reversed;
  31232. if (reversed) {
  31233. points.reverse();
  31234. }
  31235. // Reverse the steps (#5004)
  31236. step = { right: 1, center: 2 }[step] || (step && 3);
  31237. if (step && reversed) {
  31238. step = 4 - step;
  31239. }
  31240. // Remove invalid points, especially in spline (#5015)
  31241. if (options.connectNulls && !nullsAsZeroes && !connectCliffs) {
  31242. points = this.getValidPoints(points);
  31243. }
  31244. // Build the line
  31245. points.forEach(function (point, i) {
  31246. var plotX = point.plotX,
  31247. plotY = point.plotY,
  31248. lastPoint = points[i - 1],
  31249. pathToPoint; // the path to this point from the previous
  31250. if (
  31251. (point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
  31252. !connectCliffs
  31253. ) {
  31254. gap = true; // ... and continue
  31255. }
  31256. // Line series, nullsAsZeroes is not handled
  31257. if (point.isNull && !defined(nullsAsZeroes) && i > 0) {
  31258. gap = !options.connectNulls;
  31259. // Area series, nullsAsZeroes is set
  31260. } else if (point.isNull && !nullsAsZeroes) {
  31261. gap = true;
  31262. } else {
  31263. if (i === 0 || gap) {
  31264. pathToPoint = ['M', point.plotX, point.plotY];
  31265. // Generate the spline as defined in the SplineSeries object
  31266. } else if (series.getPointSpline) {
  31267. pathToPoint = series.getPointSpline(points, point, i);
  31268. } else if (step) {
  31269. if (step === 1) { // right
  31270. pathToPoint = [
  31271. 'L',
  31272. lastPoint.plotX,
  31273. plotY
  31274. ];
  31275. } else if (step === 2) { // center
  31276. pathToPoint = [
  31277. 'L',
  31278. (lastPoint.plotX + plotX) / 2,
  31279. lastPoint.plotY,
  31280. 'L',
  31281. (lastPoint.plotX + plotX) / 2,
  31282. plotY
  31283. ];
  31284. } else {
  31285. pathToPoint = [
  31286. 'L',
  31287. plotX,
  31288. lastPoint.plotY
  31289. ];
  31290. }
  31291. pathToPoint.push('L', plotX, plotY);
  31292. } else {
  31293. // normal line to next point
  31294. pathToPoint = [
  31295. 'L',
  31296. plotX,
  31297. plotY
  31298. ];
  31299. }
  31300. // Prepare for animation. When step is enabled, there are
  31301. // two path nodes for each x value.
  31302. xMap.push(point.x);
  31303. if (step) {
  31304. xMap.push(point.x);
  31305. if (step === 2) { // step = center (#8073)
  31306. xMap.push(point.x);
  31307. }
  31308. }
  31309. graphPath.push.apply(graphPath, pathToPoint);
  31310. gap = false;
  31311. }
  31312. });
  31313. graphPath.xMap = xMap;
  31314. series.graphPath = graphPath;
  31315. return graphPath;
  31316. },
  31317. /**
  31318. * Draw the graph. Called internally when rendering line-like series
  31319. * types. The first time it generates the `series.graph` item and
  31320. * optionally other series-wide items like `series.area` for area
  31321. * charts. On subsequent calls these items are updated with new
  31322. * positions and attributes.
  31323. *
  31324. * @function Highcharts.Series#drawGraph
  31325. */
  31326. drawGraph: function () {
  31327. var series = this,
  31328. options = this.options,
  31329. graphPath = (this.gappedPath || this.getGraphPath).call(this),
  31330. styledMode = this.chart.styledMode,
  31331. props = [[
  31332. 'graph',
  31333. 'highcharts-graph'
  31334. ]];
  31335. // Presentational properties
  31336. if (!styledMode) {
  31337. props[0].push(
  31338. options.lineColor || this.color,
  31339. options.dashStyle
  31340. );
  31341. }
  31342. props = series.getZonesGraphs(props);
  31343. // Draw the graph
  31344. props.forEach(function (prop, i) {
  31345. var graphKey = prop[0],
  31346. graph = series[graphKey],
  31347. attribs;
  31348. if (graph) {
  31349. graph.endX = series.preventGraphAnimation ?
  31350. null :
  31351. graphPath.xMap;
  31352. graph.animate({ d: graphPath });
  31353. } else if (graphPath.length) { // #1487
  31354. series[graphKey] = series.chart.renderer.path(graphPath)
  31355. .addClass(prop[1])
  31356. .attr({ zIndex: 1 }) // #1069
  31357. .add(series.group);
  31358. if (!styledMode) {
  31359. attribs = {
  31360. 'stroke': prop[2],
  31361. 'stroke-width': options.lineWidth,
  31362. // Polygon series use filled graph
  31363. 'fill': (series.fillGraph && series.color) || 'none'
  31364. };
  31365. if (prop[3]) {
  31366. attribs.dashstyle = prop[3];
  31367. } else if (options.linecap !== 'square') {
  31368. attribs['stroke-linecap'] =
  31369. attribs['stroke-linejoin'] = 'round';
  31370. }
  31371. graph = series[graphKey]
  31372. .attr(attribs)
  31373. // Add shadow to normal series (0) or to first
  31374. // zone (1) #3932
  31375. .shadow((i < 2) && options.shadow);
  31376. }
  31377. }
  31378. // Helpers for animation
  31379. if (graph) {
  31380. graph.startX = graphPath.xMap;
  31381. graph.isArea = graphPath.isArea; // For arearange animation
  31382. }
  31383. });
  31384. },
  31385. /**
  31386. * Get zones properties for building graphs. Extendable by series with
  31387. * multiple lines within one series.
  31388. *
  31389. * @private
  31390. * @function Highcharts.Series#getZonesGraphs
  31391. *
  31392. * @param {Array<Array<string>>} props
  31393. *
  31394. * @return {Array<Array<string>>}
  31395. */
  31396. getZonesGraphs: function (props) {
  31397. // Add the zone properties if any
  31398. this.zones.forEach(function (zone, i) {
  31399. var propset = [
  31400. 'zone-graph-' + i,
  31401. 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
  31402. (zone.className || '')
  31403. ];
  31404. if (!this.chart.styledMode) {
  31405. propset.push(
  31406. zone.color || this.color,
  31407. zone.dashStyle || this.options.dashStyle
  31408. );
  31409. }
  31410. props.push(propset);
  31411. }, this);
  31412. return props;
  31413. },
  31414. /**
  31415. * Clip the graphs into zones for colors and styling.
  31416. *
  31417. * @private
  31418. * @function Highcharts.Series#applyZones
  31419. */
  31420. applyZones: function () {
  31421. var series = this,
  31422. chart = this.chart,
  31423. renderer = chart.renderer,
  31424. zones = this.zones,
  31425. translatedFrom,
  31426. translatedTo,
  31427. clips = this.clips || [],
  31428. clipAttr,
  31429. graph = this.graph,
  31430. area = this.area,
  31431. chartSizeMax = Math.max(chart.chartWidth, chart.chartHeight),
  31432. axis = this[(this.zoneAxis || 'y') + 'Axis'],
  31433. extremes,
  31434. reversed,
  31435. inverted = chart.inverted,
  31436. horiz,
  31437. pxRange,
  31438. pxPosMin,
  31439. pxPosMax,
  31440. ignoreZones = false;
  31441. if (zones.length &&
  31442. (graph || area) &&
  31443. axis &&
  31444. axis.min !== undefined
  31445. ) {
  31446. reversed = axis.reversed;
  31447. horiz = axis.horiz;
  31448. // The use of the Color Threshold assumes there are no gaps
  31449. // so it is safe to hide the original graph and area
  31450. // unless it is not waterfall series, then use showLine property
  31451. // to set lines between columns to be visible (#7862)
  31452. if (graph && !this.showLine) {
  31453. graph.hide();
  31454. }
  31455. if (area) {
  31456. area.hide();
  31457. }
  31458. // Create the clips
  31459. extremes = axis.getExtremes();
  31460. zones.forEach(function (threshold, i) {
  31461. translatedFrom = reversed ?
  31462. (horiz ? chart.plotWidth : 0) :
  31463. (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
  31464. translatedFrom = Math.min(
  31465. Math.max(
  31466. pick(translatedTo, translatedFrom), 0
  31467. ),
  31468. chartSizeMax
  31469. );
  31470. translatedTo = Math.min(
  31471. Math.max(
  31472. Math.round(
  31473. axis.toPixels(
  31474. pick(threshold.value, extremes.max),
  31475. true
  31476. ) || 0
  31477. ),
  31478. 0
  31479. ),
  31480. chartSizeMax
  31481. );
  31482. if (ignoreZones) {
  31483. translatedFrom = translatedTo =
  31484. axis.toPixels(extremes.max);
  31485. }
  31486. pxRange = Math.abs(translatedFrom - translatedTo);
  31487. pxPosMin = Math.min(translatedFrom, translatedTo);
  31488. pxPosMax = Math.max(translatedFrom, translatedTo);
  31489. if (axis.isXAxis) {
  31490. clipAttr = {
  31491. x: inverted ? pxPosMax : pxPosMin,
  31492. y: 0,
  31493. width: pxRange,
  31494. height: chartSizeMax
  31495. };
  31496. if (!horiz) {
  31497. clipAttr.x = chart.plotHeight - clipAttr.x;
  31498. }
  31499. } else {
  31500. clipAttr = {
  31501. x: 0,
  31502. y: inverted ? pxPosMax : pxPosMin,
  31503. width: chartSizeMax,
  31504. height: pxRange
  31505. };
  31506. if (horiz) {
  31507. clipAttr.y = chart.plotWidth - clipAttr.y;
  31508. }
  31509. }
  31510. // VML SUPPPORT
  31511. if (inverted && renderer.isVML) {
  31512. if (axis.isXAxis) {
  31513. clipAttr = {
  31514. x: 0,
  31515. y: reversed ? pxPosMin : pxPosMax,
  31516. height: clipAttr.width,
  31517. width: chart.chartWidth
  31518. };
  31519. } else {
  31520. clipAttr = {
  31521. x: (
  31522. clipAttr.y -
  31523. chart.plotLeft -
  31524. chart.spacingBox.x
  31525. ),
  31526. y: 0,
  31527. width: clipAttr.height,
  31528. height: chart.chartHeight
  31529. };
  31530. }
  31531. }
  31532. // END OF VML SUPPORT
  31533. if (clips[i]) {
  31534. clips[i].animate(clipAttr);
  31535. } else {
  31536. clips[i] = renderer.clipRect(clipAttr);
  31537. if (graph) {
  31538. series['zone-graph-' + i].clip(clips[i]);
  31539. }
  31540. if (area) {
  31541. series['zone-area-' + i].clip(clips[i]);
  31542. }
  31543. }
  31544. // if this zone extends out of the axis, ignore the others
  31545. ignoreZones = threshold.value > extremes.max;
  31546. // Clear translatedTo for indicators
  31547. if (series.resetZones && translatedTo === 0) {
  31548. translatedTo = undefined;
  31549. }
  31550. });
  31551. this.clips = clips;
  31552. }
  31553. },
  31554. /**
  31555. * Initialize and perform group inversion on series.group and
  31556. * series.markerGroup.
  31557. *
  31558. * @private
  31559. * @function Highcharts.Series#invertGroups
  31560. *
  31561. * @param {boolean} inverted
  31562. */
  31563. invertGroups: function (inverted) {
  31564. var series = this,
  31565. chart = series.chart,
  31566. remover;
  31567. function setInvert() {
  31568. ['group', 'markerGroup'].forEach(function (groupName) {
  31569. if (series[groupName]) {
  31570. // VML/HTML needs explicit attributes for flipping
  31571. if (chart.renderer.isVML) {
  31572. series[groupName].attr({
  31573. width: series.yAxis.len,
  31574. height: series.xAxis.len
  31575. });
  31576. }
  31577. series[groupName].width = series.yAxis.len;
  31578. series[groupName].height = series.xAxis.len;
  31579. series[groupName].invert(inverted);
  31580. }
  31581. });
  31582. }
  31583. // Pie, go away (#1736)
  31584. if (!series.xAxis) {
  31585. return;
  31586. }
  31587. // A fixed size is needed for inversion to work
  31588. remover = addEvent(chart, 'resize', setInvert);
  31589. addEvent(series, 'destroy', remover);
  31590. // Do it now
  31591. setInvert(inverted); // do it now
  31592. // On subsequent render and redraw, just do setInvert without
  31593. // setting up events again
  31594. series.invertGroups = setInvert;
  31595. },
  31596. /**
  31597. * General abstraction for creating plot groups like series.group,
  31598. * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
  31599. * the group will only be adjusted to the updated plot size.
  31600. *
  31601. * @private
  31602. * @function Highcharts.Series#plotGroup
  31603. *
  31604. * @param {string} prop
  31605. *
  31606. * @param {string} name
  31607. *
  31608. * @param {string} visibility
  31609. *
  31610. * @param {number} zIndex
  31611. *
  31612. * @param {Highcharts.SVGElement} parent
  31613. *
  31614. * @return {Highcharts.SVGElement}
  31615. */
  31616. plotGroup: function (prop, name, visibility, zIndex, parent) {
  31617. var group = this[prop],
  31618. isNew = !group;
  31619. // Generate it on first call
  31620. if (isNew) {
  31621. this[prop] = group = this.chart.renderer.g()
  31622. .attr({
  31623. zIndex: zIndex || 0.1 // IE8 and pointer logic use this
  31624. })
  31625. .add(parent);
  31626. }
  31627. // Add the class names, and replace existing ones as response to
  31628. // Series.update (#6660)
  31629. group.addClass(
  31630. (
  31631. 'highcharts-' + name +
  31632. ' highcharts-series-' + this.index +
  31633. ' highcharts-' + this.type + '-series ' +
  31634. (
  31635. defined(this.colorIndex) ?
  31636. 'highcharts-color-' + this.colorIndex + ' ' :
  31637. ''
  31638. ) +
  31639. (this.options.className || '') +
  31640. (
  31641. group.hasClass('highcharts-tracker') ?
  31642. ' highcharts-tracker' :
  31643. ''
  31644. )
  31645. ),
  31646. true
  31647. );
  31648. // Place it on first and subsequent (redraw) calls
  31649. group.attr({ visibility: visibility })[isNew ? 'attr' : 'animate'](
  31650. this.getPlotBox()
  31651. );
  31652. return group;
  31653. },
  31654. /**
  31655. * Get the translation and scale for the plot area of this series.
  31656. *
  31657. * @function Highcharts.Series#getPlotBox
  31658. *
  31659. * @return {Highcharts.SeriesPlotBoxObject}
  31660. */
  31661. getPlotBox: function () {
  31662. var chart = this.chart,
  31663. xAxis = this.xAxis,
  31664. yAxis = this.yAxis;
  31665. // Swap axes for inverted (#2339)
  31666. if (chart.inverted) {
  31667. xAxis = yAxis;
  31668. yAxis = this.xAxis;
  31669. }
  31670. return {
  31671. translateX: xAxis ? xAxis.left : chart.plotLeft,
  31672. translateY: yAxis ? yAxis.top : chart.plotTop,
  31673. scaleX: 1, // #1623
  31674. scaleY: 1
  31675. };
  31676. },
  31677. /**
  31678. * Render the graph and markers. Called internally when first rendering
  31679. * and later when redrawing the chart. This function can be extended in
  31680. * plugins, but normally shouldn't be called directly.
  31681. *
  31682. * @function Highcharts.Series#render
  31683. *
  31684. * @fires Highcharts.Series#event:afterRender
  31685. */
  31686. render: function () {
  31687. var series = this,
  31688. chart = series.chart,
  31689. group,
  31690. options = series.options,
  31691. // Animation doesn't work in IE8 quirks when the group div is
  31692. // hidden, and looks bad in other oldIE
  31693. animDuration = (
  31694. !!series.animate &&
  31695. chart.renderer.isSVG &&
  31696. animObject(options.animation).duration
  31697. ),
  31698. visibility = series.visible ? 'inherit' : 'hidden', // #2597
  31699. zIndex = options.zIndex,
  31700. hasRendered = series.hasRendered,
  31701. chartSeriesGroup = chart.seriesGroup,
  31702. inverted = chart.inverted;
  31703. fireEvent(this, 'render');
  31704. // the group
  31705. group = series.plotGroup(
  31706. 'group',
  31707. 'series',
  31708. visibility,
  31709. zIndex,
  31710. chartSeriesGroup
  31711. );
  31712. series.markerGroup = series.plotGroup(
  31713. 'markerGroup',
  31714. 'markers',
  31715. visibility,
  31716. zIndex,
  31717. chartSeriesGroup
  31718. );
  31719. // initiate the animation
  31720. if (animDuration) {
  31721. series.animate(true);
  31722. }
  31723. // SVGRenderer needs to know this before drawing elements (#1089,
  31724. // #1795)
  31725. group.inverted = series.isCartesian ? inverted : false;
  31726. // draw the graph if any
  31727. if (series.drawGraph) {
  31728. series.drawGraph();
  31729. series.applyZones();
  31730. }
  31731. /* series.points.forEach(function (point) {
  31732. if (point.redraw) {
  31733. point.redraw();
  31734. }
  31735. }); */
  31736. // draw the data labels (inn pies they go before the points)
  31737. if (series.drawDataLabels) {
  31738. series.drawDataLabels();
  31739. }
  31740. // draw the points
  31741. if (series.visible) {
  31742. series.drawPoints();
  31743. }
  31744. // draw the mouse tracking area
  31745. if (
  31746. series.drawTracker &&
  31747. series.options.enableMouseTracking !== false
  31748. ) {
  31749. series.drawTracker();
  31750. }
  31751. // Handle inverted series and tracker groups
  31752. series.invertGroups(inverted);
  31753. // Initial clipping, must be defined after inverting groups for VML.
  31754. // Applies to columns etc. (#3839).
  31755. if (
  31756. options.clip !== false &&
  31757. !series.sharedClipKey &&
  31758. !hasRendered
  31759. ) {
  31760. group.clip(chart.clipRect);
  31761. }
  31762. // Run the animation
  31763. if (animDuration) {
  31764. series.animate();
  31765. }
  31766. // Call the afterAnimate function on animation complete (but don't
  31767. // overwrite the animation.complete option which should be available
  31768. // to the user).
  31769. if (!hasRendered) {
  31770. series.animationTimeout = syncTimeout(function () {
  31771. series.afterAnimate();
  31772. }, animDuration);
  31773. }
  31774. // Means data is in accordance with what you see
  31775. series.isDirty = false;
  31776. // (See #322) series.isDirty = series.isDirtyData = false; // means
  31777. // data is in accordance with what you see
  31778. series.hasRendered = true;
  31779. fireEvent(series, 'afterRender');
  31780. },
  31781. /**
  31782. * Redraw the series. This function is called internally from
  31783. * `chart.redraw` and normally shouldn't be called directly.
  31784. *
  31785. * @private
  31786. * @function Highcharts.Series#redraw
  31787. */
  31788. redraw: function () {
  31789. var series = this,
  31790. chart = series.chart,
  31791. // cache it here as it is set to false in render, but used after
  31792. wasDirty = series.isDirty || series.isDirtyData,
  31793. group = series.group,
  31794. xAxis = series.xAxis,
  31795. yAxis = series.yAxis;
  31796. // reposition on resize
  31797. if (group) {
  31798. if (chart.inverted) {
  31799. group.attr({
  31800. width: chart.plotWidth,
  31801. height: chart.plotHeight
  31802. });
  31803. }
  31804. group.animate({
  31805. translateX: pick(xAxis && xAxis.left, chart.plotLeft),
  31806. translateY: pick(yAxis && yAxis.top, chart.plotTop)
  31807. });
  31808. }
  31809. series.translate();
  31810. series.render();
  31811. if (wasDirty) { // #3868, #3945
  31812. delete this.kdTree;
  31813. }
  31814. },
  31815. kdAxisArray: ['clientX', 'plotY'],
  31816. /**
  31817. * @private
  31818. * @function Highcharts.Series#searchPoint
  31819. *
  31820. * @param {object} e
  31821. *
  31822. * @param {boolean} [compareX]
  31823. *
  31824. * @return {Highcharts.Point}
  31825. */
  31826. searchPoint: function (e, compareX) {
  31827. var series = this,
  31828. xAxis = series.xAxis,
  31829. yAxis = series.yAxis,
  31830. inverted = series.chart.inverted;
  31831. return this.searchKDTree({
  31832. clientX: inverted ?
  31833. xAxis.len - e.chartY + xAxis.pos :
  31834. e.chartX - xAxis.pos,
  31835. plotY: inverted ?
  31836. yAxis.len - e.chartX + yAxis.pos :
  31837. e.chartY - yAxis.pos
  31838. }, compareX, e);
  31839. },
  31840. /**
  31841. * Build the k-d-tree that is used by mouse and touch interaction to get
  31842. * the closest point. Line-like series typically have a one-dimensional
  31843. * tree where points are searched along the X axis, while scatter-like
  31844. * series typically search in two dimensions, X and Y.
  31845. *
  31846. * @private
  31847. * @function Highcharts.Series#buildKDTree
  31848. */
  31849. buildKDTree: function (e) {
  31850. // Prevent multiple k-d-trees from being built simultaneously
  31851. // (#6235)
  31852. this.buildingKdTree = true;
  31853. var series = this,
  31854. dimensions = (
  31855. series.options.findNearestPointBy.indexOf('y') > -1 ? 2 : 1
  31856. );
  31857. // Internal function
  31858. function _kdtree(points, depth, dimensions) {
  31859. var axis,
  31860. median,
  31861. length = points && points.length;
  31862. if (length) {
  31863. // alternate between the axis
  31864. axis = series.kdAxisArray[depth % dimensions];
  31865. // sort point array
  31866. points.sort(function (a, b) {
  31867. return a[axis] - b[axis];
  31868. });
  31869. median = Math.floor(length / 2);
  31870. // build and return nod
  31871. return {
  31872. point: points[median],
  31873. left: _kdtree(
  31874. points.slice(0, median), depth + 1, dimensions
  31875. ),
  31876. right: _kdtree(
  31877. points.slice(median + 1), depth + 1, dimensions
  31878. )
  31879. };
  31880. }
  31881. }
  31882. // Start the recursive build process with a clone of the points
  31883. // array and null points filtered out (#3873)
  31884. function startRecursive() {
  31885. series.kdTree = _kdtree(
  31886. series.getValidPoints(
  31887. null,
  31888. // For line-type series restrict to plot area, but
  31889. // column-type series not (#3916, #4511)
  31890. !series.directTouch
  31891. ),
  31892. dimensions,
  31893. dimensions
  31894. );
  31895. series.buildingKdTree = false;
  31896. }
  31897. delete series.kdTree;
  31898. // For testing tooltips, don't build async. Also if touchstart, we
  31899. // may be dealing with click events on mobile, so don't delay
  31900. // (#6817).
  31901. syncTimeout(
  31902. startRecursive,
  31903. series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1
  31904. );
  31905. },
  31906. /**
  31907. * @private
  31908. * @function Highcharts.Series#searchKDTree
  31909. *
  31910. * @param {object} point
  31911. *
  31912. * @param {boolean} [compareX]
  31913. *
  31914. * @return {Highcharts.Point}
  31915. */
  31916. searchKDTree: function (point, compareX, e) {
  31917. var series = this,
  31918. kdX = this.kdAxisArray[0],
  31919. kdY = this.kdAxisArray[1],
  31920. kdComparer = compareX ? 'distX' : 'dist',
  31921. kdDimensions = series.options.findNearestPointBy
  31922. .indexOf('y') > -1 ? 2 : 1;
  31923. // Set the one and two dimensional distance on the point object
  31924. function setDistance(p1, p2) {
  31925. var x = (defined(p1[kdX]) && defined(p2[kdX])) ?
  31926. Math.pow(p1[kdX] - p2[kdX], 2) :
  31927. null,
  31928. y = (defined(p1[kdY]) && defined(p2[kdY])) ?
  31929. Math.pow(p1[kdY] - p2[kdY], 2) :
  31930. null,
  31931. r = (x || 0) + (y || 0);
  31932. p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
  31933. p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
  31934. }
  31935. function _search(search, tree, depth, dimensions) {
  31936. var point = tree.point,
  31937. axis = series.kdAxisArray[depth % dimensions],
  31938. tdist,
  31939. sideA,
  31940. sideB,
  31941. ret = point,
  31942. nPoint1,
  31943. nPoint2;
  31944. setDistance(search, point);
  31945. // Pick side based on distance to splitting point
  31946. tdist = search[axis] - point[axis];
  31947. sideA = tdist < 0 ? 'left' : 'right';
  31948. sideB = tdist < 0 ? 'right' : 'left';
  31949. // End of tree
  31950. if (tree[sideA]) {
  31951. nPoint1 = _search(
  31952. search, tree[sideA], depth + 1, dimensions
  31953. );
  31954. ret = (
  31955. nPoint1[kdComparer] < ret[kdComparer] ? nPoint1 : point
  31956. );
  31957. }
  31958. if (tree[sideB]) {
  31959. // compare distance to current best to splitting point to
  31960. // decide wether to check side B or not
  31961. if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
  31962. nPoint2 = _search(
  31963. search,
  31964. tree[sideB],
  31965. depth + 1,
  31966. dimensions
  31967. );
  31968. ret = nPoint2[kdComparer] < ret[kdComparer] ?
  31969. nPoint2 :
  31970. ret;
  31971. }
  31972. }
  31973. return ret;
  31974. }
  31975. if (!this.kdTree && !this.buildingKdTree) {
  31976. this.buildKDTree(e);
  31977. }
  31978. if (this.kdTree) {
  31979. return _search(point, this.kdTree, kdDimensions, kdDimensions);
  31980. }
  31981. },
  31982. /**
  31983. * @private
  31984. * @function Highcharts.Series#pointPlacementToXValue
  31985. *
  31986. * @return {number}
  31987. */
  31988. pointPlacementToXValue: function () {
  31989. var series = this,
  31990. pointPlacement = series.options.pointPlacement;
  31991. // Point placement is relative to each series pointRange (#5889)
  31992. if (pointPlacement === 'between') {
  31993. pointPlacement = 0.5;
  31994. }
  31995. if (isNumber(pointPlacement)) {
  31996. pointPlacement *=
  31997. pick(series.options.pointRange || series.xAxis.pointRange);
  31998. }
  31999. return pointPlacement;
  32000. }
  32001. }
  32002. ); // end Series prototype
  32003. /**
  32004. * A line series displays information as a series of data points connected by
  32005. * straight line segments.
  32006. *
  32007. * @sample {highcharts} highcharts/demo/line-basic/
  32008. * Line chart
  32009. * @sample {highstock} stock/demo/basic-line/
  32010. * Line chart
  32011. *
  32012. * @extends plotOptions.series
  32013. * @product highcharts highstock
  32014. * @apioption plotOptions.line
  32015. */
  32016. /**
  32017. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  32018. * of a line graph. Round means that lines are rounded in the ends and
  32019. * bends.
  32020. *
  32021. * @type {string}
  32022. * @validvalue ["round", "butt", "square"]
  32023. * @default round
  32024. * @since 3.0.7
  32025. * @apioption plotOptions.line.linecap
  32026. */
  32027. /**
  32028. * A `line` series. If the [type](#series.line.type) option is not
  32029. * specified, it is inherited from [chart.type](#chart.type).
  32030. *
  32031. * In TypeScript instead the `type` option must always be set.
  32032. *
  32033. * @extends series,plotOptions.line
  32034. * @excluding dataParser,dataURL
  32035. * @product highcharts highstock
  32036. * @apioption series.line
  32037. */
  32038. /**
  32039. * An array of data points for the series. For the `line` series type,
  32040. * points can be given in the following ways:
  32041. *
  32042. * 1. An array of numerical values. In this case, the numerical values will be
  32043. * interpreted as `y` options. The `x` values will be automatically
  32044. * calculated, either starting at 0 and incremented by 1, or from
  32045. * `pointStart` and `pointInterval` given in the series options. If the axis
  32046. * has categories, these will be used. Example:
  32047. * ```js
  32048. * data: [0, 5, 3, 5]
  32049. * ```
  32050. *
  32051. * 2. An array of arrays with 2 values. In this case, the values correspond to
  32052. * `x,y`. If the first value is a string, it is applied as the name of the
  32053. * point, and the `x` value is inferred.
  32054. * ```js
  32055. * data: [
  32056. * [0, 1],
  32057. * [1, 2],
  32058. * [2, 8]
  32059. * ]
  32060. * ```
  32061. *
  32062. * 3. An array of objects with named values. The following snippet shows only a
  32063. * few settings, see the complete options set below. If the total number of
  32064. * data points exceeds the series'
  32065. * [turboThreshold](#series.line.turboThreshold), this option is not
  32066. * available.
  32067. * ```js
  32068. * data: [{
  32069. * x: 1,
  32070. * y: 9,
  32071. * name: "Point2",
  32072. * color: "#00FF00"
  32073. * }, {
  32074. * x: 1,
  32075. * y: 6,
  32076. * name: "Point1",
  32077. * color: "#FF00FF"
  32078. * }]
  32079. * ```
  32080. *
  32081. * @sample {highcharts} highcharts/chart/reflow-true/
  32082. * Numerical values
  32083. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  32084. * Arrays of numeric x and y
  32085. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  32086. * Arrays of datetime x and y
  32087. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  32088. * Arrays of point.name and y
  32089. * @sample {highcharts} highcharts/series/data-array-of-objects/
  32090. * Config objects
  32091. *
  32092. * @type {Array<number|Array<(number|string),number>|*>}
  32093. * @apioption series.line.data
  32094. */
  32095. /**
  32096. * An additional, individual class name for the data point's graphic
  32097. * representation.
  32098. *
  32099. * @type {string}
  32100. * @since 5.0.0
  32101. * @product highcharts gantt
  32102. * @apioption series.line.data.className
  32103. */
  32104. /**
  32105. * Individual color for the point. By default the color is pulled from
  32106. * the global `colors` array.
  32107. *
  32108. * In styled mode, the `color` option doesn't take effect. Instead, use
  32109. * `colorIndex`.
  32110. *
  32111. * @sample {highcharts} highcharts/point/color/
  32112. * Mark the highest point
  32113. *
  32114. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  32115. * @product highcharts highstock gantt
  32116. * @apioption series.line.data.color
  32117. */
  32118. /**
  32119. * A specific color index to use for the point, so its graphic representations
  32120. * are given the class name `highcharts-color-{n}`. In styled mode this will
  32121. * change the color of the graphic. In non-styled mode, the color by is set by
  32122. * the `fill` attribute, so the change in class name won't have a visual effect
  32123. * by default.
  32124. *
  32125. * @type {number}
  32126. * @since 5.0.0
  32127. * @product highcharts gantt
  32128. * @apioption series.line.data.colorIndex
  32129. */
  32130. /**
  32131. * Individual data label for each point. The options are the same as
  32132. * the ones for [plotOptions.series.dataLabels](
  32133. * #plotOptions.series.dataLabels).
  32134. *
  32135. * @sample highcharts/point/datalabels/
  32136. * Show a label for the last value
  32137. *
  32138. * @type {Highcharts.PlotSeriesDataLabelsOptions}
  32139. * @product highcharts highstock gantt
  32140. * @apioption series.line.data.dataLabels
  32141. */
  32142. /**
  32143. * A description of the point to add to the screen reader information
  32144. * about the point. Requires the Accessibility module.
  32145. *
  32146. * @type {string}
  32147. * @since 5.0.0
  32148. * @apioption series.line.data.description
  32149. */
  32150. /**
  32151. * An id for the point. This can be used after render time to get a
  32152. * pointer to the point object through `chart.get()`.
  32153. *
  32154. * @sample {highcharts} highcharts/point/id/
  32155. * Remove an id'd point
  32156. *
  32157. * @type {string}
  32158. * @since 1.2.0
  32159. * @product highcharts highstock gantt
  32160. * @apioption series.line.data.id
  32161. */
  32162. /**
  32163. * The rank for this point's data label in case of collision. If two
  32164. * data labels are about to overlap, only the one with the highest `labelrank`
  32165. * will be drawn.
  32166. *
  32167. * @type {number}
  32168. * @apioption series.line.data.labelrank
  32169. */
  32170. /**
  32171. * The name of the point as shown in the legend, tooltip, dataLabel
  32172. * etc.
  32173. *
  32174. * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
  32175. *
  32176. * @sample {highcharts} highcharts/series/data-array-of-objects/
  32177. * Point names
  32178. *
  32179. * @type {string}
  32180. * @apioption series.line.data.name
  32181. */
  32182. /**
  32183. * Whether the data point is selected initially.
  32184. *
  32185. * @type {boolean}
  32186. * @default false
  32187. * @product highcharts highstock gantt
  32188. * @apioption series.line.data.selected
  32189. */
  32190. /**
  32191. * The x value of the point. For datetime axes, the X value is the timestamp
  32192. * in milliseconds since 1970.
  32193. *
  32194. * @type {number}
  32195. * @product highcharts highstock
  32196. * @apioption series.line.data.x
  32197. */
  32198. /**
  32199. * The y value of the point.
  32200. *
  32201. * @type {number}
  32202. * @product highcharts highstock
  32203. * @apioption series.line.data.y
  32204. */
  32205. /**
  32206. * Individual point events
  32207. *
  32208. * @extends plotOptions.series.point.events
  32209. * @product highcharts highstock gantt
  32210. * @apioption series.line.data.events
  32211. */
  32212. /**
  32213. * @extends plotOptions.series.marker
  32214. * @product highcharts highstock
  32215. * @apioption series.line.data.marker
  32216. */
  32217. }(Highcharts));
  32218. (function (H) {
  32219. /**
  32220. * (c) 2010-2019 Torstein Honsi
  32221. *
  32222. * License: www.highcharts.com/license
  32223. */
  32224. var Axis = H.Axis,
  32225. Chart = H.Chart,
  32226. correctFloat = H.correctFloat,
  32227. defined = H.defined,
  32228. destroyObjectProperties = H.destroyObjectProperties,
  32229. format = H.format,
  32230. objectEach = H.objectEach,
  32231. pick = H.pick,
  32232. Series = H.Series;
  32233. /**
  32234. * The class for stacks. Each stack, on a specific X value and either negative
  32235. * or positive, has its own stack item.
  32236. *
  32237. * @private
  32238. * @class
  32239. * @name Highcharts.StackItem
  32240. *
  32241. * @param {Highcharts.Axis} axis
  32242. *
  32243. * @param {Highcharts.Options} options
  32244. *
  32245. * @param {boolean} isNegative
  32246. *
  32247. * @param {number} x
  32248. *
  32249. * @param {string|*} stackOption
  32250. */
  32251. H.StackItem = function (axis, options, isNegative, x, stackOption) {
  32252. var inverted = axis.chart.inverted;
  32253. this.axis = axis;
  32254. // Tells if the stack is negative
  32255. this.isNegative = isNegative;
  32256. // Save the options to be able to style the label
  32257. this.options = options;
  32258. // Save the x value to be able to position the label later
  32259. this.x = x;
  32260. // Initialize total value
  32261. this.total = null;
  32262. // This will keep each points' extremes stored by series.index and point
  32263. // index
  32264. this.points = {};
  32265. // Save the stack option on the series configuration object, and whether to
  32266. // treat it as percent
  32267. this.stack = stackOption;
  32268. this.leftCliff = 0;
  32269. this.rightCliff = 0;
  32270. // The align options and text align varies on whether the stack is negative
  32271. // and if the chart is inverted or not.
  32272. // First test the user supplied value, then use the dynamic.
  32273. this.alignOptions = {
  32274. align: options.align ||
  32275. (inverted ? (isNegative ? 'left' : 'right') : 'center'),
  32276. verticalAlign: options.verticalAlign ||
  32277. (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
  32278. y: pick(options.y, inverted ? 4 : (isNegative ? 14 : -6)),
  32279. x: pick(options.x, inverted ? (isNegative ? -6 : 6) : 0)
  32280. };
  32281. this.textAlign = options.textAlign ||
  32282. (inverted ? (isNegative ? 'right' : 'left') : 'center');
  32283. };
  32284. H.StackItem.prototype = {
  32285. /**
  32286. * @private
  32287. * @function Highcharts.StackItem#destroy
  32288. */
  32289. destroy: function () {
  32290. destroyObjectProperties(this, this.axis);
  32291. },
  32292. /**
  32293. * Renders the stack total label and adds it to the stack label group.
  32294. *
  32295. * @private
  32296. * @function Highcharts.StackItem#render
  32297. *
  32298. * @param {Highcharts.SVGElement} group
  32299. */
  32300. render: function (group) {
  32301. var chart = this.axis.chart,
  32302. options = this.options,
  32303. formatOption = options.format,
  32304. str = formatOption ?
  32305. format(formatOption, this, chart.time) :
  32306. options.formatter.call(this); // format the text in the label
  32307. // Change the text to reflect the new total and set visibility to hidden
  32308. // in case the serie is hidden
  32309. if (this.label) {
  32310. this.label.attr({ text: str, visibility: 'hidden' });
  32311. // Create new label
  32312. } else {
  32313. this.label =
  32314. chart.renderer.text(str, null, null, options.useHTML)
  32315. .css(options.style)
  32316. .attr({
  32317. align: this.textAlign,
  32318. rotation: options.rotation,
  32319. visibility: 'hidden' // hidden until setOffset is called
  32320. })
  32321. .add(group); // add to the labels-group
  32322. }
  32323. // Rank it higher than data labels (#8742)
  32324. this.label.labelrank = chart.plotHeight;
  32325. },
  32326. /**
  32327. * Sets the offset that the stack has from the x value and repositions the
  32328. * label.
  32329. *
  32330. * @private
  32331. * @function Highcarts.StackItem#setOffset
  32332. *
  32333. * @param {number} xOffset
  32334. *
  32335. * @param {number} xWidth
  32336. */
  32337. setOffset: function (xOffset, xWidth) {
  32338. var stackItem = this,
  32339. axis = stackItem.axis,
  32340. chart = axis.chart,
  32341. // stack value translated mapped to chart coordinates
  32342. y = axis.translate(
  32343. axis.usePercentage ? 100 : stackItem.total,
  32344. 0,
  32345. 0,
  32346. 0,
  32347. 1
  32348. ),
  32349. yZero = axis.translate(0), // stack origin
  32350. h = defined(y) && Math.abs(y - yZero), // stack height
  32351. x = chart.xAxis[0].translate(stackItem.x) + xOffset, // x position
  32352. stackBox = defined(y) && stackItem.getStackBox(
  32353. chart,
  32354. stackItem,
  32355. x,
  32356. y,
  32357. xWidth,
  32358. h,
  32359. axis
  32360. ),
  32361. label = stackItem.label,
  32362. alignAttr;
  32363. if (label && stackBox) {
  32364. // Align the label to the box
  32365. label.align(stackItem.alignOptions, null, stackBox);
  32366. // Set visibility (#678)
  32367. alignAttr = label.alignAttr;
  32368. label[
  32369. stackItem.options.crop === false || chart.isInsidePlot(
  32370. alignAttr.x,
  32371. alignAttr.y
  32372. ) ? 'show' : 'hide'](true);
  32373. }
  32374. },
  32375. /**
  32376. * @private
  32377. * @function Highcharts.StackItem#getStackBox
  32378. *
  32379. * @param {Highcharts.Chart} chart
  32380. *
  32381. * @param {Highcharts.StackItem} stackItem
  32382. *
  32383. * @param {number} x
  32384. *
  32385. * @param {number} y
  32386. *
  32387. * @param {number} xWidth
  32388. *
  32389. * @param {number} h
  32390. *
  32391. * @param {Highcharts.Axis} axis
  32392. *
  32393. * @return {*}
  32394. */
  32395. getStackBox: function (chart, stackItem, x, y, xWidth, h, axis) {
  32396. var reversed = stackItem.axis.reversed,
  32397. inverted = chart.inverted,
  32398. axisPos = axis.height + axis.pos - (inverted ? chart.plotLeft :
  32399. chart.plotTop),
  32400. neg = (stackItem.isNegative && !reversed) ||
  32401. (!stackItem.isNegative && reversed); // #4056
  32402. return { // this is the box for the complete stack
  32403. x: inverted ? (neg ? y : y - h) : x,
  32404. y: inverted ?
  32405. axisPos - x - xWidth :
  32406. (neg ?
  32407. (axisPos - y - h) :
  32408. axisPos - y
  32409. ),
  32410. width: inverted ? h : xWidth,
  32411. height: inverted ? xWidth : h
  32412. };
  32413. }
  32414. };
  32415. /**
  32416. * Generate stacks for each series and calculate stacks total values
  32417. *
  32418. * @private
  32419. * @function Highcharts.Chart#getStacks
  32420. */
  32421. Chart.prototype.getStacks = function () {
  32422. var chart = this;
  32423. // reset stacks for each yAxis
  32424. chart.yAxis.forEach(function (axis) {
  32425. if (axis.stacks && axis.hasVisibleSeries) {
  32426. axis.oldStacks = axis.stacks;
  32427. }
  32428. });
  32429. chart.series.forEach(function (series) {
  32430. if (series.options.stacking && (series.visible === true ||
  32431. chart.options.chart.ignoreHiddenSeries === false)) {
  32432. series.stackKey = series.type + pick(series.options.stack, '');
  32433. }
  32434. });
  32435. };
  32436. // Stacking methods defined on the Axis prototype
  32437. /**
  32438. * Build the stacks from top down
  32439. *
  32440. * @private
  32441. * @function Highcharts.Axis#buildStacks
  32442. */
  32443. Axis.prototype.buildStacks = function () {
  32444. var axisSeries = this.series,
  32445. reversedStacks = pick(this.options.reversedStacks, true),
  32446. len = axisSeries.length,
  32447. i;
  32448. if (!this.isXAxis) {
  32449. this.usePercentage = false;
  32450. i = len;
  32451. while (i--) {
  32452. axisSeries[reversedStacks ? i : len - i - 1].setStackedPoints();
  32453. }
  32454. // Loop up again to compute percent and stream stack
  32455. for (i = 0; i < len; i++) {
  32456. axisSeries[i].modifyStacks();
  32457. }
  32458. }
  32459. };
  32460. /**
  32461. * @private
  32462. * @function Highcharts.Axis#renderStackTotals
  32463. */
  32464. Axis.prototype.renderStackTotals = function () {
  32465. var axis = this,
  32466. chart = axis.chart,
  32467. renderer = chart.renderer,
  32468. stacks = axis.stacks,
  32469. stackTotalGroup = axis.stackTotalGroup;
  32470. // Create a separate group for the stack total labels
  32471. if (!stackTotalGroup) {
  32472. axis.stackTotalGroup = stackTotalGroup =
  32473. renderer.g('stack-labels')
  32474. .attr({
  32475. visibility: 'visible',
  32476. zIndex: 6
  32477. })
  32478. .add();
  32479. }
  32480. // plotLeft/Top will change when y axis gets wider so we need to translate
  32481. // the stackTotalGroup at every render call. See bug #506 and #516
  32482. stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
  32483. // Render each stack total
  32484. objectEach(stacks, function (type) {
  32485. objectEach(type, function (stack) {
  32486. stack.render(stackTotalGroup);
  32487. });
  32488. });
  32489. };
  32490. /**
  32491. * Set all the stacks to initial states and destroy unused ones.
  32492. *
  32493. * @private
  32494. * @function Highcharts.Axis#resetStacks
  32495. */
  32496. Axis.prototype.resetStacks = function () {
  32497. var axis = this,
  32498. stacks = axis.stacks;
  32499. if (!axis.isXAxis) {
  32500. objectEach(stacks, function (type) {
  32501. objectEach(type, function (stack, key) {
  32502. // Clean up memory after point deletion (#1044, #4320)
  32503. if (stack.touched < axis.stacksTouched) {
  32504. stack.destroy();
  32505. delete type[key];
  32506. // Reset stacks
  32507. } else {
  32508. stack.total = null;
  32509. stack.cumulative = null;
  32510. }
  32511. });
  32512. });
  32513. }
  32514. };
  32515. /**
  32516. * @private
  32517. * @function Highcharts.Axis#cleanStacks
  32518. */
  32519. Axis.prototype.cleanStacks = function () {
  32520. var stacks;
  32521. if (!this.isXAxis) {
  32522. if (this.oldStacks) {
  32523. stacks = this.stacks = this.oldStacks;
  32524. }
  32525. // reset stacks
  32526. objectEach(stacks, function (type) {
  32527. objectEach(type, function (stack) {
  32528. stack.cumulative = stack.total;
  32529. });
  32530. });
  32531. }
  32532. };
  32533. // Stacking methods defnied for Series prototype
  32534. /**
  32535. * Adds series' points value to corresponding stack
  32536. *
  32537. * @private
  32538. * @function Highcharts.Series#setStackedPoints
  32539. */
  32540. Series.prototype.setStackedPoints = function () {
  32541. if (!this.options.stacking || (this.visible !== true &&
  32542. this.chart.options.chart.ignoreHiddenSeries !== false)) {
  32543. return;
  32544. }
  32545. var series = this,
  32546. xData = series.processedXData,
  32547. yData = series.processedYData,
  32548. stackedYData = [],
  32549. yDataLength = yData.length,
  32550. seriesOptions = series.options,
  32551. threshold = seriesOptions.threshold,
  32552. stackThreshold = pick(seriesOptions.startFromThreshold && threshold, 0),
  32553. stackOption = seriesOptions.stack,
  32554. stacking = seriesOptions.stacking,
  32555. stackKey = series.stackKey,
  32556. negKey = '-' + stackKey,
  32557. negStacks = series.negStacks,
  32558. yAxis = series.yAxis,
  32559. stacks = yAxis.stacks,
  32560. oldStacks = yAxis.oldStacks,
  32561. stackIndicator,
  32562. isNegative,
  32563. stack,
  32564. other,
  32565. key,
  32566. pointKey,
  32567. i,
  32568. x,
  32569. y;
  32570. yAxis.stacksTouched += 1;
  32571. // loop over the non-null y values and read them into a local array
  32572. for (i = 0; i < yDataLength; i++) {
  32573. x = xData[i];
  32574. y = yData[i];
  32575. stackIndicator = series.getStackIndicator(
  32576. stackIndicator,
  32577. x,
  32578. series.index
  32579. );
  32580. pointKey = stackIndicator.key;
  32581. // Read stacked values into a stack based on the x value,
  32582. // the sign of y and the stack key. Stacking is also handled for null
  32583. // values (#739)
  32584. isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
  32585. key = isNegative ? negKey : stackKey;
  32586. // Create empty object for this stack if it doesn't exist yet
  32587. if (!stacks[key]) {
  32588. stacks[key] = {};
  32589. }
  32590. // Initialize StackItem for this x
  32591. if (!stacks[key][x]) {
  32592. if (oldStacks[key] && oldStacks[key][x]) {
  32593. stacks[key][x] = oldStacks[key][x];
  32594. stacks[key][x].total = null;
  32595. } else {
  32596. stacks[key][x] = new H.StackItem(
  32597. yAxis,
  32598. yAxis.options.stackLabels,
  32599. isNegative,
  32600. x,
  32601. stackOption
  32602. );
  32603. }
  32604. }
  32605. // If the StackItem doesn't exist, create it first
  32606. stack = stacks[key][x];
  32607. if (y !== null) {
  32608. stack.points[pointKey] = stack.points[series.index] =
  32609. [pick(stack.cumulative, stackThreshold)];
  32610. // Record the base of the stack
  32611. if (!defined(stack.cumulative)) {
  32612. stack.base = pointKey;
  32613. }
  32614. stack.touched = yAxis.stacksTouched;
  32615. // In area charts, if there are multiple points on the same X value,
  32616. // let the area fill the full span of those points
  32617. if (stackIndicator.index > 0 && series.singleStacks === false) {
  32618. stack.points[pointKey][0] =
  32619. stack.points[series.index + ',' + x + ',0'][0];
  32620. }
  32621. // When updating to null, reset the point stack (#7493)
  32622. } else {
  32623. stack.points[pointKey] = stack.points[series.index] = null;
  32624. }
  32625. // Add value to the stack total
  32626. if (stacking === 'percent') {
  32627. // Percent stacked column, totals are the same for the positive and
  32628. // negative stacks
  32629. other = isNegative ? stackKey : negKey;
  32630. if (negStacks && stacks[other] && stacks[other][x]) {
  32631. other = stacks[other][x];
  32632. stack.total = other.total =
  32633. Math.max(other.total, stack.total) + Math.abs(y) || 0;
  32634. // Percent stacked areas
  32635. } else {
  32636. stack.total = correctFloat(stack.total + (Math.abs(y) || 0));
  32637. }
  32638. } else {
  32639. stack.total = correctFloat(stack.total + (y || 0));
  32640. }
  32641. stack.cumulative = pick(stack.cumulative, stackThreshold) + (y || 0);
  32642. if (y !== null) {
  32643. stack.points[pointKey].push(stack.cumulative);
  32644. stackedYData[i] = stack.cumulative;
  32645. }
  32646. }
  32647. if (stacking === 'percent') {
  32648. yAxis.usePercentage = true;
  32649. }
  32650. this.stackedYData = stackedYData; // To be used in getExtremes
  32651. // Reset old stacks
  32652. yAxis.oldStacks = {};
  32653. };
  32654. /**
  32655. * Iterate over all stacks and compute the absolute values to percent
  32656. *
  32657. * @private
  32658. * @function Highcharts.Series#modifyStacks
  32659. */
  32660. Series.prototype.modifyStacks = function () {
  32661. var series = this,
  32662. stackKey = series.stackKey,
  32663. stacks = series.yAxis.stacks,
  32664. processedXData = series.processedXData,
  32665. stackIndicator,
  32666. stacking = series.options.stacking;
  32667. if (series[stacking + 'Stacker']) { // Modifier function exists
  32668. [stackKey, '-' + stackKey].forEach(function (key) {
  32669. var i = processedXData.length,
  32670. x,
  32671. stack,
  32672. pointExtremes;
  32673. while (i--) {
  32674. x = processedXData[i];
  32675. stackIndicator = series.getStackIndicator(
  32676. stackIndicator,
  32677. x,
  32678. series.index,
  32679. key
  32680. );
  32681. stack = stacks[key] && stacks[key][x];
  32682. pointExtremes = stack && stack.points[stackIndicator.key];
  32683. if (pointExtremes) {
  32684. series[stacking + 'Stacker'](pointExtremes, stack, i);
  32685. }
  32686. }
  32687. });
  32688. }
  32689. };
  32690. /**
  32691. * Modifier function for percent stacks. Blows up the stack to 100%.
  32692. *
  32693. * @private
  32694. * @function Highcharts.Series#percentStacker
  32695. *
  32696. * @param {Array<number>} pointExtremes
  32697. *
  32698. * @param {Highcharts.StackItem} stack
  32699. *
  32700. * @param {number} i
  32701. */
  32702. Series.prototype.percentStacker = function (pointExtremes, stack, i) {
  32703. var totalFactor = stack.total ? 100 / stack.total : 0;
  32704. // Y bottom value
  32705. pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
  32706. // Y value
  32707. pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
  32708. this.stackedYData[i] = pointExtremes[1];
  32709. };
  32710. /**
  32711. * Get stack indicator, according to it's x-value, to determine points with the
  32712. * same x-value
  32713. *
  32714. * @private
  32715. * @function Highcharts.Series#getStackIndicator
  32716. *
  32717. * @param {*} stackIndicator
  32718. *
  32719. * @param {number} x
  32720. *
  32721. * @param {number} index
  32722. *
  32723. * @param {string} key
  32724. *
  32725. * @return {*}
  32726. */
  32727. Series.prototype.getStackIndicator = function (stackIndicator, x, index, key) {
  32728. // Update stack indicator, when:
  32729. // first point in a stack || x changed || stack type (negative vs positive)
  32730. // changed:
  32731. if (!defined(stackIndicator) || stackIndicator.x !== x ||
  32732. (key && stackIndicator.key !== key)) {
  32733. stackIndicator = {
  32734. x: x,
  32735. index: 0,
  32736. key: key
  32737. };
  32738. } else {
  32739. stackIndicator.index++;
  32740. }
  32741. stackIndicator.key = [index, x, stackIndicator.index].join(',');
  32742. return stackIndicator;
  32743. };
  32744. }(Highcharts));
  32745. (function (H) {
  32746. /**
  32747. * (c) 2010-2019 Torstein Honsi
  32748. *
  32749. * License: www.highcharts.com/license
  32750. */
  32751. var addEvent = H.addEvent,
  32752. animate = H.animate,
  32753. Axis = H.Axis,
  32754. Chart = H.Chart,
  32755. createElement = H.createElement,
  32756. css = H.css,
  32757. defined = H.defined,
  32758. erase = H.erase,
  32759. extend = H.extend,
  32760. fireEvent = H.fireEvent,
  32761. isNumber = H.isNumber,
  32762. isObject = H.isObject,
  32763. isArray = H.isArray,
  32764. merge = H.merge,
  32765. objectEach = H.objectEach,
  32766. pick = H.pick,
  32767. Point = H.Point,
  32768. Series = H.Series,
  32769. seriesTypes = H.seriesTypes,
  32770. setAnimation = H.setAnimation,
  32771. splat = H.splat;
  32772. // Remove settings that have not changed, to avoid unnecessary rendering or
  32773. // computing (#9197)
  32774. H.cleanRecursively = function (newer, older) {
  32775. var result = {};
  32776. objectEach(newer, function (val, key) {
  32777. var ob;
  32778. // Dive into objects
  32779. if (isObject(newer[key], true) && older[key]) {
  32780. ob = H.cleanRecursively(newer[key], older[key]);
  32781. if (Object.keys(ob).length) {
  32782. result[key] = ob;
  32783. }
  32784. // Arrays or primitives are copied directly
  32785. } else if (isObject(newer[key]) || newer[key] !== older[key]) {
  32786. result[key] = newer[key];
  32787. }
  32788. });
  32789. return result;
  32790. };
  32791. // Extend the Chart prototype for dynamic methods
  32792. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  32793. /**
  32794. * Add a series to the chart after render time. Note that this method should
  32795. * never be used when adding data synchronously at chart render time, as it
  32796. * adds expense to the calculations and rendering. When adding data at the
  32797. * same time as the chart is initialized, add the series as a configuration
  32798. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  32799. *
  32800. * @sample highcharts/members/chart-addseries/
  32801. * Add a series from a button
  32802. * @sample stock/members/chart-addseries/
  32803. * Add a series in Highstock
  32804. *
  32805. * @function Highcharts.Chart#addSeries
  32806. *
  32807. * @param {Highcharts.SeriesOptionsType} options
  32808. * The config options for the series.
  32809. *
  32810. * @param {boolean} [redraw=true]
  32811. * Whether to redraw the chart after adding.
  32812. *
  32813. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  32814. * Whether to apply animation, and optionally animation
  32815. * configuration.
  32816. *
  32817. * @return {Highcharts.Series}
  32818. * The newly created series object.
  32819. *
  32820. * @fires Highcharts.Chart#event:addSeries
  32821. * @fires Highcharts.Chart#event:afterAddSeries
  32822. */
  32823. addSeries: function (options, redraw, animation) {
  32824. var series,
  32825. chart = this;
  32826. if (options) {
  32827. redraw = pick(redraw, true); // defaults to true
  32828. fireEvent(chart, 'addSeries', { options: options }, function () {
  32829. series = chart.initSeries(options);
  32830. chart.isDirtyLegend = true;
  32831. chart.linkSeries();
  32832. fireEvent(chart, 'afterAddSeries');
  32833. if (redraw) {
  32834. chart.redraw(animation);
  32835. }
  32836. });
  32837. }
  32838. return series;
  32839. },
  32840. /**
  32841. * Add an axis to the chart after render time. Note that this method should
  32842. * never be used when adding data synchronously at chart render time, as it
  32843. * adds expense to the calculations and rendering. When adding data at the
  32844. * same time as the chart is initialized, add the axis as a configuration
  32845. * option instead.
  32846. *
  32847. * @sample highcharts/members/chart-addaxis/
  32848. * Add and remove axes
  32849. *
  32850. * @function Highcharts.Chart#addAxis
  32851. *
  32852. * @param {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} options
  32853. * The axis options.
  32854. *
  32855. * @param {boolean} [isX=false]
  32856. * Whether it is an X axis or a value axis.
  32857. *
  32858. * @param {boolean} [redraw=true]
  32859. * Whether to redraw the chart after adding.
  32860. *
  32861. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  32862. * Whether and how to apply animation in the redraw.
  32863. *
  32864. * @return {Highcharts.Axis}
  32865. * The newly generated Axis object.
  32866. */
  32867. addAxis: function (options, isX, redraw, animation) {
  32868. var key = isX ? 'xAxis' : 'yAxis',
  32869. chartOptions = this.options,
  32870. userOptions = merge(options, {
  32871. index: this[key].length,
  32872. isX: isX
  32873. }),
  32874. axis;
  32875. axis = new Axis(this, userOptions);
  32876. // Push the new axis options to the chart options
  32877. chartOptions[key] = splat(chartOptions[key] || {});
  32878. chartOptions[key].push(userOptions);
  32879. if (pick(redraw, true)) {
  32880. this.redraw(animation);
  32881. }
  32882. return axis;
  32883. },
  32884. /**
  32885. * Dim the chart and show a loading text or symbol. Options for the loading
  32886. * screen are defined in {@link
  32887. * https://api.highcharts.com/highcharts/loading|the loading options}.
  32888. *
  32889. * @sample highcharts/members/chart-hideloading/
  32890. * Show and hide loading from a button
  32891. * @sample highcharts/members/chart-showloading/
  32892. * Apply different text labels
  32893. * @sample stock/members/chart-show-hide-loading/
  32894. * Toggle loading in Highstock
  32895. *
  32896. * @function Highcharts.Chart#showLoading
  32897. *
  32898. * @param {string} [str]
  32899. * An optional text to show in the loading label instead of the
  32900. * default one. The default text is set in
  32901. * [lang.loading](http://api.highcharts.com/highcharts/lang.loading).
  32902. */
  32903. showLoading: function (str) {
  32904. var chart = this,
  32905. options = chart.options,
  32906. loadingDiv = chart.loadingDiv,
  32907. loadingOptions = options.loading,
  32908. setLoadingSize = function () {
  32909. if (loadingDiv) {
  32910. css(loadingDiv, {
  32911. left: chart.plotLeft + 'px',
  32912. top: chart.plotTop + 'px',
  32913. width: chart.plotWidth + 'px',
  32914. height: chart.plotHeight + 'px'
  32915. });
  32916. }
  32917. };
  32918. // create the layer at the first call
  32919. if (!loadingDiv) {
  32920. chart.loadingDiv = loadingDiv = createElement('div', {
  32921. className: 'highcharts-loading highcharts-loading-hidden'
  32922. }, null, chart.container);
  32923. chart.loadingSpan = createElement(
  32924. 'span',
  32925. { className: 'highcharts-loading-inner' },
  32926. null,
  32927. loadingDiv
  32928. );
  32929. addEvent(chart, 'redraw', setLoadingSize); // #1080
  32930. }
  32931. loadingDiv.className = 'highcharts-loading';
  32932. // Update text
  32933. chart.loadingSpan.innerHTML = str || options.lang.loading;
  32934. if (!chart.styledMode) {
  32935. // Update visuals
  32936. css(loadingDiv, extend(loadingOptions.style, {
  32937. zIndex: 10
  32938. }));
  32939. css(chart.loadingSpan, loadingOptions.labelStyle);
  32940. // Show it
  32941. if (!chart.loadingShown) {
  32942. css(loadingDiv, {
  32943. opacity: 0,
  32944. display: ''
  32945. });
  32946. animate(loadingDiv, {
  32947. opacity: loadingOptions.style.opacity || 0.5
  32948. }, {
  32949. duration: loadingOptions.showDuration || 0
  32950. });
  32951. }
  32952. }
  32953. chart.loadingShown = true;
  32954. setLoadingSize();
  32955. },
  32956. /**
  32957. * Hide the loading layer.
  32958. *
  32959. * @see Highcharts.Chart#showLoading
  32960. *
  32961. * @sample highcharts/members/chart-hideloading/
  32962. * Show and hide loading from a button
  32963. * @sample stock/members/chart-show-hide-loading/
  32964. * Toggle loading in Highstock
  32965. *
  32966. * @function Highcharts.Chart#hideLoading
  32967. */
  32968. hideLoading: function () {
  32969. var options = this.options,
  32970. loadingDiv = this.loadingDiv;
  32971. if (loadingDiv) {
  32972. loadingDiv.className =
  32973. 'highcharts-loading highcharts-loading-hidden';
  32974. if (!this.styledMode) {
  32975. animate(loadingDiv, {
  32976. opacity: 0
  32977. }, {
  32978. duration: options.loading.hideDuration || 100,
  32979. complete: function () {
  32980. css(loadingDiv, { display: 'none' });
  32981. }
  32982. });
  32983. }
  32984. }
  32985. this.loadingShown = false;
  32986. },
  32987. /**
  32988. * These properties cause isDirtyBox to be set to true when updating. Can be
  32989. * extended from plugins.
  32990. */
  32991. propsRequireDirtyBox: [
  32992. 'backgroundColor',
  32993. 'borderColor',
  32994. 'borderWidth',
  32995. 'margin',
  32996. 'marginTop',
  32997. 'marginRight',
  32998. 'marginBottom',
  32999. 'marginLeft',
  33000. 'spacing',
  33001. 'spacingTop',
  33002. 'spacingRight',
  33003. 'spacingBottom',
  33004. 'spacingLeft',
  33005. 'borderRadius',
  33006. 'plotBackgroundColor',
  33007. 'plotBackgroundImage',
  33008. 'plotBorderColor',
  33009. 'plotBorderWidth',
  33010. 'plotShadow',
  33011. 'shadow'
  33012. ],
  33013. /**
  33014. * These properties cause all series to be updated when updating. Can be
  33015. * extended from plugins.
  33016. */
  33017. propsRequireUpdateSeries: [
  33018. 'chart.inverted',
  33019. 'chart.polar',
  33020. 'chart.ignoreHiddenSeries',
  33021. 'chart.type',
  33022. 'colors',
  33023. 'plotOptions',
  33024. 'time',
  33025. 'tooltip'
  33026. ],
  33027. /**
  33028. * These collections (arrays) implement update() methods with support for
  33029. * one-to-one option.
  33030. */
  33031. collectionsWithUpdate: [
  33032. 'xAxis',
  33033. 'yAxis',
  33034. 'zAxis',
  33035. 'series',
  33036. 'colorAxis',
  33037. 'pane'
  33038. ],
  33039. /**
  33040. * A generic function to update any element of the chart. Elements can be
  33041. * enabled and disabled, moved, re-styled, re-formatted etc.
  33042. *
  33043. * A special case is configuration objects that take arrays, for example
  33044. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  33045. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  33046. * [series](https://api.highcharts.com/highcharts/series). For these
  33047. * collections, an `id` option is used to map the new option set to an
  33048. * existing object. If an existing object of the same id is not found, the
  33049. * corresponding item is updated. So for example, running `chart.update`
  33050. * with a series item without an id, will cause the existing chart's series
  33051. * with the same index in the series array to be updated. When the
  33052. * `oneToOne` parameter is true, `chart.update` will also take care of
  33053. * adding and removing items from the collection. Read more under the
  33054. * parameter description below.
  33055. *
  33056. * Note that when changing series data, `chart.update` may mutate the passed
  33057. * data options.
  33058. *
  33059. * See also the
  33060. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  33061. * Switching between `responsive.rules` basically runs `chart.update` under
  33062. * the hood.
  33063. *
  33064. * @sample highcharts/members/chart-update/
  33065. * Update chart geometry
  33066. *
  33067. * @function Highcharts.Chart#update
  33068. *
  33069. * @param {Highcharts.Options} options
  33070. * A configuration object for the new chart options.
  33071. *
  33072. * @param {boolean} [redraw=true]
  33073. * Whether to redraw the chart.
  33074. *
  33075. * @param {boolean} [oneToOne=false]
  33076. * When `true`, the `series`, `xAxis` and `yAxis` collections will
  33077. * be updated one to one, and items will be either added or removed
  33078. * to match the new updated options. For example, if the chart has
  33079. * two series and we call `chart.update` with a configuration
  33080. * containing three series, one will be added. If we call
  33081. * `chart.update` with one series, one will be removed. Setting an
  33082. * empty `series` array will remove all series, but leaving out the
  33083. * `series` property will leave all series untouched. If the series
  33084. * have id's, the new series options will be matched by id, and the
  33085. * remaining ones removed.
  33086. *
  33087. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  33088. * Whether to apply animation, and optionally animation
  33089. * configuration.
  33090. *
  33091. * @fires Highcharts.Chart#event:update
  33092. * @fires Highcharts.Chart#event:afterUpdate
  33093. */
  33094. update: function (options, redraw, oneToOne, animation) {
  33095. var chart = this,
  33096. adders = {
  33097. credits: 'addCredits',
  33098. title: 'setTitle',
  33099. subtitle: 'setSubtitle'
  33100. },
  33101. optionsChart,
  33102. updateAllAxes,
  33103. updateAllSeries,
  33104. newWidth,
  33105. newHeight,
  33106. itemsForRemoval = [];
  33107. fireEvent(chart, 'update', { options: options });
  33108. // If there are responsive rules in action, undo the responsive rules
  33109. // before we apply the updated options and replay the responsive rules
  33110. // on top from the chart.redraw function (#9617).
  33111. if (!options.isResponsiveOptions) {
  33112. chart.setResponsive(false, true);
  33113. }
  33114. options = H.cleanRecursively(options, chart.options);
  33115. // If the top-level chart option is present, some special updates are
  33116. // required
  33117. optionsChart = options.chart;
  33118. if (optionsChart) {
  33119. merge(true, chart.options.chart, optionsChart);
  33120. // Setter function
  33121. if ('className' in optionsChart) {
  33122. chart.setClassName(optionsChart.className);
  33123. }
  33124. if ('reflow' in optionsChart) {
  33125. chart.setReflow(optionsChart.reflow);
  33126. }
  33127. if (
  33128. 'inverted' in optionsChart ||
  33129. 'polar' in optionsChart ||
  33130. 'type' in optionsChart
  33131. ) {
  33132. // Parse options.chart.inverted and options.chart.polar together
  33133. // with the available series.
  33134. chart.propFromSeries();
  33135. updateAllAxes = true;
  33136. }
  33137. if ('alignTicks' in optionsChart) { // #6452
  33138. updateAllAxes = true;
  33139. }
  33140. objectEach(optionsChart, function (val, key) {
  33141. if (
  33142. chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  33143. -1
  33144. ) {
  33145. updateAllSeries = true;
  33146. }
  33147. // Only dirty box
  33148. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  33149. chart.isDirtyBox = true;
  33150. }
  33151. });
  33152. if (!chart.styledMode && 'style' in optionsChart) {
  33153. chart.renderer.setStyle(optionsChart.style);
  33154. }
  33155. }
  33156. // Moved up, because tooltip needs updated plotOptions (#6218)
  33157. if (!chart.styledMode && options.colors) {
  33158. this.options.colors = options.colors;
  33159. }
  33160. if (options.plotOptions) {
  33161. merge(true, this.options.plotOptions, options.plotOptions);
  33162. }
  33163. // Some option stuctures correspond one-to-one to chart objects that
  33164. // have update methods, for example
  33165. // options.credits => chart.credits
  33166. // options.legend => chart.legend
  33167. // options.title => chart.title
  33168. // options.tooltip => chart.tooltip
  33169. // options.subtitle => chart.subtitle
  33170. // options.mapNavigation => chart.mapNavigation
  33171. // options.navigator => chart.navigator
  33172. // options.scrollbar => chart.scrollbar
  33173. objectEach(options, function (val, key) {
  33174. if (chart[key] && typeof chart[key].update === 'function') {
  33175. chart[key].update(val, false);
  33176. // If a one-to-one object does not exist, look for an adder function
  33177. } else if (typeof chart[adders[key]] === 'function') {
  33178. chart[adders[key]](val);
  33179. }
  33180. if (
  33181. key !== 'chart' &&
  33182. chart.propsRequireUpdateSeries.indexOf(key) !== -1
  33183. ) {
  33184. updateAllSeries = true;
  33185. }
  33186. });
  33187. // Setters for collections. For axes and series, each item is referred
  33188. // by an id. If the id is not found, it defaults to the corresponding
  33189. // item in the collection, so setting one series without an id, will
  33190. // update the first series in the chart. Setting two series without
  33191. // an id will update the first and the second respectively (#6019)
  33192. // chart.update and responsive.
  33193. this.collectionsWithUpdate.forEach(function (coll) {
  33194. var indexMap;
  33195. if (options[coll]) {
  33196. // In stock charts, the navigator series are also part of the
  33197. // chart.series array, but those series should not be handled
  33198. // here (#8196).
  33199. if (coll === 'series') {
  33200. indexMap = [];
  33201. chart[coll].forEach(function (s, i) {
  33202. if (!s.options.isInternal) {
  33203. indexMap.push(pick(s.options.index, i));
  33204. }
  33205. });
  33206. }
  33207. splat(options[coll]).forEach(function (newOptions, i) {
  33208. var item = (
  33209. defined(newOptions.id) &&
  33210. chart.get(newOptions.id)
  33211. ) || chart[coll][indexMap ? indexMap[i] : i];
  33212. if (item && item.coll === coll) {
  33213. item.update(newOptions, false);
  33214. if (oneToOne) {
  33215. item.touched = true;
  33216. }
  33217. }
  33218. // If oneToOne and no matching item is found, add one
  33219. if (!item && oneToOne) {
  33220. if (coll === 'series') {
  33221. chart.addSeries(newOptions, false)
  33222. .touched = true;
  33223. } else if (coll === 'xAxis' || coll === 'yAxis') {
  33224. chart.addAxis(newOptions, coll === 'xAxis', false)
  33225. .touched = true;
  33226. }
  33227. }
  33228. });
  33229. // Add items for removal
  33230. if (oneToOne) {
  33231. chart[coll].forEach(function (item) {
  33232. if (!item.touched && !item.options.isInternal) {
  33233. itemsForRemoval.push(item);
  33234. } else {
  33235. delete item.touched;
  33236. }
  33237. });
  33238. }
  33239. }
  33240. });
  33241. itemsForRemoval.forEach(function (item) {
  33242. if (item.remove) {
  33243. item.remove(false);
  33244. }
  33245. });
  33246. if (updateAllAxes) {
  33247. chart.axes.forEach(function (axis) {
  33248. axis.update({}, false);
  33249. });
  33250. }
  33251. // Certain options require the whole series structure to be thrown away
  33252. // and rebuilt
  33253. if (updateAllSeries) {
  33254. chart.series.forEach(function (series) {
  33255. series.update({}, false);
  33256. });
  33257. }
  33258. // For loading, just update the options, do not redraw
  33259. if (options.loading) {
  33260. merge(true, chart.options.loading, options.loading);
  33261. }
  33262. // Update size. Redraw is forced.
  33263. newWidth = optionsChart && optionsChart.width;
  33264. newHeight = optionsChart && optionsChart.height;
  33265. if ((isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  33266. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  33267. chart.setSize(newWidth, newHeight, animation);
  33268. } else if (pick(redraw, true)) {
  33269. chart.redraw(animation);
  33270. }
  33271. fireEvent(chart, 'afterUpdate', { options: options });
  33272. },
  33273. /**
  33274. * Shortcut to set the subtitle options. This can also be done from {@link
  33275. * Chart#update} or {@link Chart#setTitle}.
  33276. *
  33277. * @function Highcharts.Chart#setSubtitle
  33278. *
  33279. * @param {Highcharts.SubtitleOptions} options
  33280. * New subtitle options. The subtitle text itself is set by the
  33281. * `options.text` property.
  33282. */
  33283. setSubtitle: function (options) {
  33284. this.setTitle(undefined, options);
  33285. }
  33286. });
  33287. // extend the Point prototype for dynamic methods
  33288. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  33289. /**
  33290. * Update point with new options (typically x/y data) and optionally redraw
  33291. * the series.
  33292. *
  33293. * @sample highcharts/members/point-update-column/
  33294. * Update column value
  33295. * @sample highcharts/members/point-update-pie/
  33296. * Update pie slice
  33297. * @sample maps/members/point-update/
  33298. * Update map area value in Highmaps
  33299. *
  33300. * @function Highcharts.Point#update
  33301. *
  33302. * @param {number|object|Array<number|string>|null} options
  33303. * The point options. Point options are handled as described under
  33304. * the `series.type.data` item for each series type. For example
  33305. * for a line series, if options is a single number, the point will
  33306. * be given that number as the marin y value. If it is an array, it
  33307. * will be interpreted as x and y values respectively. If it is an
  33308. * object, advanced options are applied.
  33309. *
  33310. * @param {boolean} [redraw=true]
  33311. * Whether to redraw the chart after the point is updated. If doing
  33312. * more operations on the chart, it is best practice to set
  33313. * `redraw` to false and call `chart.redraw()` after.
  33314. *
  33315. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  33316. * Whether to apply animation, and optionally animation
  33317. * configuration.
  33318. *
  33319. * @fires Highcharts.Point#event:update
  33320. */
  33321. update: function (options, redraw, animation, runEvent) {
  33322. var point = this,
  33323. series = point.series,
  33324. graphic = point.graphic,
  33325. i,
  33326. chart = series.chart,
  33327. seriesOptions = series.options;
  33328. redraw = pick(redraw, true);
  33329. function update() {
  33330. point.applyOptions(options);
  33331. // Update visuals
  33332. if (point.y === null && graphic) { // #4146
  33333. point.graphic = graphic.destroy();
  33334. }
  33335. if (isObject(options, true)) {
  33336. // Destroy so we can get new elements
  33337. if (graphic && graphic.element) {
  33338. // "null" is also a valid symbol
  33339. if (
  33340. options &&
  33341. options.marker &&
  33342. options.marker.symbol !== undefined
  33343. ) {
  33344. point.graphic = graphic.destroy();
  33345. }
  33346. }
  33347. if (options && options.dataLabels && point.dataLabel) { // #2468
  33348. point.dataLabel = point.dataLabel.destroy();
  33349. }
  33350. if (point.connector) {
  33351. point.connector = point.connector.destroy(); // #7243
  33352. }
  33353. }
  33354. // record changes in the parallel arrays
  33355. i = point.index;
  33356. series.updateParallelArrays(point, i);
  33357. // Record the options to options.data. If the old or the new config
  33358. // is an object, use point options, otherwise use raw options
  33359. // (#4701, #4916).
  33360. seriesOptions.data[i] = (
  33361. isObject(seriesOptions.data[i], true) ||
  33362. isObject(options, true)
  33363. ) ?
  33364. point.options :
  33365. pick(options, seriesOptions.data[i]);
  33366. // redraw
  33367. series.isDirty = series.isDirtyData = true;
  33368. if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
  33369. chart.isDirtyBox = true;
  33370. }
  33371. if (seriesOptions.legendType === 'point') { // #1831, #1885
  33372. chart.isDirtyLegend = true;
  33373. }
  33374. if (redraw) {
  33375. chart.redraw(animation);
  33376. }
  33377. }
  33378. // Fire the event with a default handler of doing the update
  33379. if (runEvent === false) { // When called from setData
  33380. update();
  33381. } else {
  33382. point.firePointEvent('update', { options: options }, update);
  33383. }
  33384. },
  33385. /**
  33386. * Remove a point and optionally redraw the series and if necessary the axes
  33387. *
  33388. * @sample highcharts/plotoptions/series-point-events-remove/
  33389. * Remove point and confirm
  33390. * @sample highcharts/members/point-remove/
  33391. * Remove pie slice
  33392. * @sample maps/members/point-remove/
  33393. * Remove selected points in Highmaps
  33394. *
  33395. * @function Highcharts.Point#remove
  33396. *
  33397. * @param {boolean} redraw
  33398. * Whether to redraw the chart or wait for an explicit call. When
  33399. * doing more operations on the chart, for example running
  33400. * `point.remove()` in a loop, it is best practice to set `redraw`
  33401. * to false and call `chart.redraw()` after.
  33402. *
  33403. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=false]
  33404. * Whether to apply animation, and optionally animation
  33405. * configuration.
  33406. */
  33407. remove: function (redraw, animation) {
  33408. this.series.removePoint(
  33409. this.series.data.indexOf(this),
  33410. redraw,
  33411. animation
  33412. );
  33413. }
  33414. });
  33415. // Extend the series prototype for dynamic methods
  33416. extend(Series.prototype, /** @lends Series.prototype */ {
  33417. /**
  33418. * Add a point to the series after render time. The point can be added at
  33419. * the end, or by giving it an X value, to the start or in the middle of the
  33420. * series.
  33421. *
  33422. * @sample highcharts/members/series-addpoint-append/
  33423. * Append point
  33424. * @sample highcharts/members/series-addpoint-append-and-shift/
  33425. * Append and shift
  33426. * @sample highcharts/members/series-addpoint-x-and-y/
  33427. * Both X and Y values given
  33428. * @sample highcharts/members/series-addpoint-pie/
  33429. * Append pie slice
  33430. * @sample stock/members/series-addpoint/
  33431. * Append 100 points in Highstock
  33432. * @sample stock/members/series-addpoint-shift/
  33433. * Append and shift in Highstock
  33434. * @sample maps/members/series-addpoint/
  33435. * Add a point in Highmaps
  33436. *
  33437. * @function Highcharts.Series#addPoint
  33438. *
  33439. * @param {number|object|Array<number|string>|null} options
  33440. * The point options. If options is a single number, a point with
  33441. * that y value is appended to the series. If it is an array, it will
  33442. * be interpreted as x and y values respectively. If it is an
  33443. * object, advanced options as outlined under `series.data` are
  33444. * applied.
  33445. *
  33446. * @param {boolean} [redraw=true]
  33447. * Whether to redraw the chart after the point is added. When adding
  33448. * more than one point, it is highly recommended that the redraw
  33449. * option be set to false, and instead {@link Chart#redraw} is
  33450. * explicitly called after the adding of points is finished.
  33451. * Otherwise, the chart will redraw after adding each point.
  33452. *
  33453. * @param {boolean} [shift=false]
  33454. * If true, a point is shifted off the start of the series as one is
  33455. * appended to the end.
  33456. *
  33457. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  33458. * Whether to apply animation, and optionally animation
  33459. * configuration.
  33460. */
  33461. addPoint: function (options, redraw, shift, animation) {
  33462. var series = this,
  33463. seriesOptions = series.options,
  33464. data = series.data,
  33465. chart = series.chart,
  33466. xAxis = series.xAxis,
  33467. names = xAxis && xAxis.hasNames && xAxis.names,
  33468. dataOptions = seriesOptions.data,
  33469. point,
  33470. isInTheMiddle,
  33471. xData = series.xData,
  33472. i,
  33473. x;
  33474. // Optional redraw, defaults to true
  33475. redraw = pick(redraw, true);
  33476. // Get options and push the point to xData, yData and series.options. In
  33477. // series.generatePoints the Point instance will be created on demand
  33478. // and pushed to the series.data array.
  33479. point = { series: series };
  33480. series.pointClass.prototype.applyOptions.apply(point, [options]);
  33481. x = point.x;
  33482. // Get the insertion point
  33483. i = xData.length;
  33484. if (series.requireSorting && x < xData[i - 1]) {
  33485. isInTheMiddle = true;
  33486. while (i && xData[i - 1] > x) {
  33487. i--;
  33488. }
  33489. }
  33490. // Insert undefined item
  33491. series.updateParallelArrays(point, 'splice', i, 0, 0);
  33492. // Update it
  33493. series.updateParallelArrays(point, i);
  33494. if (names && point.name) {
  33495. names[x] = point.name;
  33496. }
  33497. dataOptions.splice(i, 0, options);
  33498. if (isInTheMiddle) {
  33499. series.data.splice(i, 0, null);
  33500. series.processData();
  33501. }
  33502. // Generate points to be added to the legend (#1329)
  33503. if (seriesOptions.legendType === 'point') {
  33504. series.generatePoints();
  33505. }
  33506. // Shift the first point off the parallel arrays
  33507. if (shift) {
  33508. if (data[0] && data[0].remove) {
  33509. data[0].remove(false);
  33510. } else {
  33511. data.shift();
  33512. series.updateParallelArrays(point, 'shift');
  33513. dataOptions.shift();
  33514. }
  33515. }
  33516. // redraw
  33517. series.isDirty = true;
  33518. series.isDirtyData = true;
  33519. if (redraw) {
  33520. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  33521. }
  33522. },
  33523. /**
  33524. * Remove a point from the series. Unlike the
  33525. * {@link Highcharts.Point#remove} method, this can also be done on a point
  33526. * that is not instanciated because it is outside the view or subject to
  33527. * Highstock data grouping.
  33528. *
  33529. * @sample highcharts/members/series-removepoint/
  33530. * Remove cropped point
  33531. *
  33532. * @function Highcharts.Series#removePoint
  33533. *
  33534. * @param {number} i
  33535. * The index of the point in the {@link Highcharts.Series.data|data}
  33536. * array.
  33537. *
  33538. * @param {boolean} [redraw=true]
  33539. * Whether to redraw the chart after the point is added. When
  33540. * removing more than one point, it is highly recommended that the
  33541. * `redraw` option be set to `false`, and instead {@link
  33542. * Highcharts.Chart#redraw} is explicitly called after the adding of
  33543. * points is finished.
  33544. *
  33545. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  33546. * Whether and optionally how the series should be animated.
  33547. *
  33548. * @fires Highcharts.Point#event:remove
  33549. */
  33550. removePoint: function (i, redraw, animation) {
  33551. var series = this,
  33552. data = series.data,
  33553. point = data[i],
  33554. points = series.points,
  33555. chart = series.chart,
  33556. remove = function () {
  33557. if (points && points.length === data.length) { // #4935
  33558. points.splice(i, 1);
  33559. }
  33560. data.splice(i, 1);
  33561. series.options.data.splice(i, 1);
  33562. series.updateParallelArrays(
  33563. point || { series: series },
  33564. 'splice',
  33565. i,
  33566. 1
  33567. );
  33568. if (point) {
  33569. point.destroy();
  33570. }
  33571. // redraw
  33572. series.isDirty = true;
  33573. series.isDirtyData = true;
  33574. if (redraw) {
  33575. chart.redraw();
  33576. }
  33577. };
  33578. setAnimation(animation, chart);
  33579. redraw = pick(redraw, true);
  33580. // Fire the event with a default handler of removing the point
  33581. if (point) {
  33582. point.firePointEvent('remove', null, remove);
  33583. } else {
  33584. remove();
  33585. }
  33586. },
  33587. /**
  33588. * Remove a series and optionally redraw the chart.
  33589. *
  33590. * @sample highcharts/members/series-remove/
  33591. * Remove first series from a button
  33592. *
  33593. * @function Highcharts.Series#remove
  33594. *
  33595. * @param {boolean} [redraw=true]
  33596. * Whether to redraw the chart or wait for an explicit call to
  33597. * {@link Highcharts.Chart#redraw}.
  33598. *
  33599. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  33600. * Whether to apply animation, and optionally animation
  33601. * configuration.
  33602. *
  33603. * @param {boolean} [withEvent=true]
  33604. * Used internally, whether to fire the series `remove` event.
  33605. *
  33606. * @fires Highcharts.Series#event:remove
  33607. */
  33608. remove: function (redraw, animation, withEvent) {
  33609. var series = this,
  33610. chart = series.chart;
  33611. function remove() {
  33612. // Destroy elements
  33613. series.destroy();
  33614. series.remove = null; // Prevent from doing again (#9097)
  33615. // Redraw
  33616. chart.isDirtyLegend = chart.isDirtyBox = true;
  33617. chart.linkSeries();
  33618. if (pick(redraw, true)) {
  33619. chart.redraw(animation);
  33620. }
  33621. }
  33622. // Fire the event with a default handler of removing the point
  33623. if (withEvent !== false) {
  33624. fireEvent(series, 'remove', null, remove);
  33625. } else {
  33626. remove();
  33627. }
  33628. },
  33629. /**
  33630. * Update the series with a new set of options. For a clean and precise
  33631. * handling of new options, all methods and elements from the series are
  33632. * removed, and it is initiated from scratch. Therefore, this method is more
  33633. * performance expensive than some other utility methods like {@link
  33634. * Series#setData} or {@link Series#setVisible}.
  33635. *
  33636. * Note that `Series.update` may mutate the passed `data` options.
  33637. *
  33638. * @sample highcharts/members/series-update/
  33639. * Updating series options
  33640. * @sample maps/members/series-update/
  33641. * Update series options in Highmaps
  33642. *
  33643. * @function Highcharts.Series#update
  33644. *
  33645. * @param {Highcharts.SeriesOptionsType} options
  33646. * New options that will be merged with the series' existing options.
  33647. *
  33648. * @param {boolean} [redraw=true]
  33649. * Whether to redraw the chart after the series is altered. If doing
  33650. * more operations on the chart, it is a good idea to set redraw to
  33651. * false and call {@link Chart#redraw} after.
  33652. *
  33653. * @fires Highcharts.Series#event:afterUpdate
  33654. */
  33655. update: function (newOptions, redraw) {
  33656. newOptions = H.cleanRecursively(newOptions, this.userOptions);
  33657. var series = this,
  33658. chart = series.chart,
  33659. // must use user options when changing type because series.options
  33660. // is merged in with type specific plotOptions
  33661. oldOptions = series.userOptions,
  33662. initialType = series.initialType || series.type,
  33663. newType = (
  33664. newOptions.type ||
  33665. oldOptions.type ||
  33666. chart.options.chart.type
  33667. ),
  33668. initialSeriesProto = seriesTypes[initialType].prototype,
  33669. n,
  33670. groups = [
  33671. 'group',
  33672. 'markerGroup',
  33673. 'dataLabelsGroup'
  33674. ],
  33675. preserve = [
  33676. 'navigatorSeries',
  33677. 'baseSeries'
  33678. ],
  33679. // Animation must be enabled when calling update before the initial
  33680. // animation has first run. This happens when calling update
  33681. // directly after chart initialization, or when applying responsive
  33682. // rules (#6912).
  33683. animation = series.finishedAnimating && { animation: false },
  33684. allowSoftUpdate = [
  33685. 'data',
  33686. 'name',
  33687. 'turboThreshold'
  33688. ],
  33689. keys = Object.keys(newOptions),
  33690. doSoftUpdate = keys.length > 0;
  33691. // Running Series.update to update the data only is an intuitive usage,
  33692. // so we want to make sure that when used like this, we run the
  33693. // cheaper setData function and allow animation instead of completely
  33694. // recreating the series instance. This includes sideways animation when
  33695. // adding points to the data set. The `name` should also support soft
  33696. // update because the data module sets name and data when setting new
  33697. // data by `chart.update`.
  33698. keys.forEach(function (key) {
  33699. if (allowSoftUpdate.indexOf(key) === -1) {
  33700. doSoftUpdate = false;
  33701. }
  33702. });
  33703. if (doSoftUpdate) {
  33704. if (newOptions.data) {
  33705. this.setData(newOptions.data, false);
  33706. }
  33707. if (newOptions.name) {
  33708. this.setName(newOptions.name, false);
  33709. }
  33710. } else {
  33711. // Make sure preserved properties are not destroyed (#3094)
  33712. preserve = groups.concat(preserve);
  33713. preserve.forEach(function (prop) {
  33714. preserve[prop] = series[prop];
  33715. delete series[prop];
  33716. });
  33717. // Do the merge, with some forced options
  33718. newOptions = merge(oldOptions, animation, {
  33719. index: series.index,
  33720. pointStart: pick(
  33721. oldOptions.pointStart, // when updating from blank (#7933)
  33722. series.xData[0] // when updating after addPoint
  33723. )
  33724. }, { data: series.options.data }, newOptions);
  33725. // Destroy the series and delete all properties. Reinsert all
  33726. // methods and properties from the new type prototype (#2270,
  33727. // #3719).
  33728. series.remove(false, null, false);
  33729. for (n in initialSeriesProto) {
  33730. series[n] = undefined;
  33731. }
  33732. if (seriesTypes[newType || initialType]) {
  33733. extend(series, seriesTypes[newType || initialType].prototype);
  33734. } else {
  33735. H.error(17, true, chart);
  33736. }
  33737. // Re-register groups (#3094) and other preserved properties
  33738. preserve.forEach(function (prop) {
  33739. series[prop] = preserve[prop];
  33740. });
  33741. series.init(chart, newOptions);
  33742. // Update the Z index of groups (#3380, #7397)
  33743. if (newOptions.zIndex !== oldOptions.zIndex) {
  33744. groups.forEach(function (groupName) {
  33745. if (series[groupName]) {
  33746. series[groupName].attr({
  33747. zIndex: newOptions.zIndex
  33748. });
  33749. }
  33750. });
  33751. }
  33752. series.initialType = initialType;
  33753. chart.linkSeries(); // Links are lost in series.remove (#3028)
  33754. }
  33755. fireEvent(this, 'afterUpdate');
  33756. if (pick(redraw, true)) {
  33757. chart.redraw(doSoftUpdate ? undefined : false);
  33758. }
  33759. },
  33760. /**
  33761. * Used from within series.update
  33762. *
  33763. * @private
  33764. * @function Highcharts.Series#setName
  33765. *
  33766. * @param {string} name
  33767. */
  33768. setName: function (name) {
  33769. this.name = this.options.name = this.userOptions.name = name;
  33770. this.chart.isDirtyLegend = true;
  33771. }
  33772. });
  33773. // Extend the Axis.prototype for dynamic methods
  33774. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  33775. /**
  33776. * Update an axis object with a new set of options. The options are merged
  33777. * with the existing options, so only new or altered options need to be
  33778. * specified.
  33779. *
  33780. * @sample highcharts/members/axis-update/
  33781. * Axis update demo
  33782. *
  33783. * @function Highcharts.Axis#update
  33784. *
  33785. * @param {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} options
  33786. * The new options that will be merged in with existing options on
  33787. * the axis.
  33788. *
  33789. * @param {boolean} [redraw=true]
  33790. * Whether to redraw the chart after the axis is altered. If doing
  33791. * more operations on the chart, it is a good idea to set redraw to
  33792. * false and call {@link Chart#redraw} after.
  33793. */
  33794. update: function (options, redraw) {
  33795. var chart = this.chart,
  33796. newEvents = ((options && options.events) || {});
  33797. options = merge(this.userOptions, options);
  33798. // Color Axis is not an array,
  33799. // This change is applied in the ColorAxis wrapper
  33800. if (chart.options[this.coll].indexOf) {
  33801. // Don't use this.options.index,
  33802. // StockChart has Axes in navigator too
  33803. chart.options[this.coll][
  33804. chart.options[this.coll].indexOf(this.userOptions)
  33805. ] = options;
  33806. }
  33807. // Remove old events, if no new exist (#8161)
  33808. objectEach(chart.options[this.coll].events, function (fn, ev) {
  33809. if (typeof newEvents[ev] === 'undefined') {
  33810. newEvents[ev] = undefined;
  33811. }
  33812. });
  33813. this.destroy(true);
  33814. this.init(chart, extend(options, { events: newEvents }));
  33815. chart.isDirtyBox = true;
  33816. if (pick(redraw, true)) {
  33817. chart.redraw();
  33818. }
  33819. },
  33820. /**
  33821. * Remove the axis from the chart.
  33822. *
  33823. * @sample highcharts/members/chart-addaxis/
  33824. * Add and remove axes
  33825. *
  33826. * @function Highcharts.Axis#remove
  33827. *
  33828. * @param {boolean} [redraw=true]
  33829. * Whether to redraw the chart following the remove.
  33830. */
  33831. remove: function (redraw) {
  33832. var chart = this.chart,
  33833. key = this.coll, // xAxis or yAxis
  33834. axisSeries = this.series,
  33835. i = axisSeries.length;
  33836. // Remove associated series (#2687)
  33837. while (i--) {
  33838. if (axisSeries[i]) {
  33839. axisSeries[i].remove(false);
  33840. }
  33841. }
  33842. // Remove the axis
  33843. erase(chart.axes, this);
  33844. erase(chart[key], this);
  33845. if (isArray(chart.options[key])) {
  33846. chart.options[key].splice(this.options.index, 1);
  33847. } else { // color axis, #6488
  33848. delete chart.options[key];
  33849. }
  33850. chart[key].forEach(function (axis, i) { // Re-index, #1706, #8075
  33851. axis.options.index = axis.userOptions.index = i;
  33852. });
  33853. this.destroy();
  33854. chart.isDirtyBox = true;
  33855. if (pick(redraw, true)) {
  33856. chart.redraw();
  33857. }
  33858. },
  33859. /**
  33860. * Update the axis title by options after render time.
  33861. *
  33862. * @sample highcharts/members/axis-settitle/
  33863. * Set a new Y axis title
  33864. *
  33865. * @function Highcharts.Axis#setTitle
  33866. *
  33867. * @param {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} titleOptions
  33868. * The additional title options.
  33869. *
  33870. * @param {boolean} [redraw=true]
  33871. * Whether to redraw the chart after setting the title.
  33872. */
  33873. setTitle: function (titleOptions, redraw) {
  33874. this.update({ title: titleOptions }, redraw);
  33875. },
  33876. /**
  33877. * Set new axis categories and optionally redraw.
  33878. *
  33879. * @sample highcharts/members/axis-setcategories/
  33880. * Set categories by click on a button
  33881. *
  33882. * @function Highcharts.Axis#setCategories
  33883. *
  33884. * @param {Array<string>} categories
  33885. * The new categories.
  33886. *
  33887. * @param {boolean} [redraw=true]
  33888. * Whether to redraw the chart.
  33889. */
  33890. setCategories: function (categories, redraw) {
  33891. this.update({ categories: categories }, redraw);
  33892. }
  33893. });
  33894. }(Highcharts));
  33895. (function (H) {
  33896. /* *
  33897. * (c) 2010-2019 Torstein Honsi
  33898. *
  33899. * License: www.highcharts.com/license
  33900. */
  33901. var color = H.color,
  33902. LegendSymbolMixin = H.LegendSymbolMixin,
  33903. pick = H.pick,
  33904. Series = H.Series,
  33905. seriesType = H.seriesType;
  33906. /**
  33907. * Area series type.
  33908. *
  33909. * @private
  33910. * @class
  33911. * @name Highcharts.seriesTypes.area
  33912. *
  33913. * @augments Highcharts.Series
  33914. */
  33915. seriesType('area', 'line',
  33916. /**
  33917. * The area series type.
  33918. *
  33919. * @sample {highcharts} highcharts/demo/area-basic/
  33920. * Area chart
  33921. * @sample {highstock} stock/demo/area/
  33922. * Area chart
  33923. *
  33924. * @extends plotOptions.line
  33925. * @excluding useOhlcData
  33926. * @product highcharts highstock
  33927. * @optionparent plotOptions.area
  33928. */
  33929. {
  33930. /**
  33931. * Fill color or gradient for the area. When `null`, the series' `color`
  33932. * is used with the series' `fillOpacity`.
  33933. *
  33934. * In styled mode, the fill color can be set with the `.highcharts-area`
  33935. * class name.
  33936. *
  33937. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  33938. * Null by default
  33939. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  33940. * Gradient
  33941. *
  33942. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  33943. * @product highcharts highstock
  33944. * @apioption plotOptions.area.fillColor
  33945. */
  33946. /**
  33947. * Fill opacity for the area. When you set an explicit `fillColor`,
  33948. * the `fillOpacity` is not applied. Instead, you should define the
  33949. * opacity in the `fillColor` with an rgba color definition. The
  33950. * `fillOpacity` setting, also the default setting, overrides the alpha
  33951. * component of the `color` setting.
  33952. *
  33953. * In styled mode, the fill opacity can be set with the
  33954. * `.highcharts-area` class name.
  33955. *
  33956. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  33957. * Automatic fill color and fill opacity of 0.1
  33958. *
  33959. * @type {number}
  33960. * @default {highcharts} 0.75
  33961. * @default {highstock} 0.75
  33962. * @product highcharts highstock
  33963. * @apioption plotOptions.area.fillOpacity
  33964. */
  33965. /**
  33966. * A separate color for the graph line. By default the line takes the
  33967. * `color` of the series, but the lineColor setting allows setting a
  33968. * separate color for the line without altering the `fillColor`.
  33969. *
  33970. * In styled mode, the line stroke can be set with the
  33971. * `.highcharts-graph` class name.
  33972. *
  33973. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  33974. * Dark gray line
  33975. *
  33976. * @type {Highcharts.ColorString}
  33977. * @product highcharts highstock
  33978. * @apioption plotOptions.area.lineColor
  33979. */
  33980. /**
  33981. * A separate color for the negative part of the area.
  33982. *
  33983. * In styled mode, a negative color is set with the `.highcharts-negative`
  33984. * class name.
  33985. *
  33986. * @see [negativeColor](#plotOptions.area.negativeColor)
  33987. *
  33988. * @sample {highcharts} highcharts/css/series-negative-color/
  33989. * Negative color in styled mode
  33990. *
  33991. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  33992. * @since 3.0
  33993. * @product highcharts
  33994. * @apioption plotOptions.area.negativeFillColor
  33995. */
  33996. /**
  33997. * Whether the whole area or just the line should respond to mouseover
  33998. * tooltips and other mouse or touch events.
  33999. *
  34000. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  34001. * Display the tooltip when the area is hovered
  34002. *
  34003. * @type {boolean}
  34004. * @default false
  34005. * @since 1.1.6
  34006. * @product highcharts highstock
  34007. * @apioption plotOptions.area.trackByArea
  34008. */
  34009. /**
  34010. * When this is true, the series will not cause the Y axis to cross
  34011. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  34012. * unless the data actually crosses the plane.
  34013. *
  34014. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  34015. * 3 will make the Y axis show negative values according to the `minPadding`
  34016. * option. If `softThreshold` is `true`, the Y axis starts at 0.
  34017. *
  34018. * @since 4.1.9
  34019. * @product highcharts highstock
  34020. */
  34021. softThreshold: false,
  34022. /**
  34023. * The Y axis value to serve as the base for the area, for distinguishing
  34024. * between values above and below a threshold. The area between the graph
  34025. * and the threshold is filled.
  34026. *
  34027. * * If a number is given, the Y axis will scale to the threshold.
  34028. * * If `null`, the scaling behaves like a line series with fill between the
  34029. * graph and the Y axis minimum.
  34030. * * If `Infinity` or `-Infinity`, the area between the graph and the
  34031. * corresponing Y axis extreme is filled (since v6.1.0).
  34032. *
  34033. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  34034. * A threshold of 100
  34035. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  34036. * A threshold of Infinity
  34037. *
  34038. * @since 2.0
  34039. * @product highcharts highstock
  34040. */
  34041. threshold: 0
  34042. }, /** @lends seriesTypes.area.prototype */ {
  34043. singleStacks: false,
  34044. // Return an array of stacked points, where null and missing points are
  34045. // replaced by dummy points in order for gaps to be drawn correctly in
  34046. // stacks.
  34047. getStackPoints: function (points) {
  34048. var series = this,
  34049. segment = [],
  34050. keys = [],
  34051. xAxis = this.xAxis,
  34052. yAxis = this.yAxis,
  34053. stack = yAxis.stacks[this.stackKey],
  34054. pointMap = {},
  34055. seriesIndex = series.index,
  34056. yAxisSeries = yAxis.series,
  34057. seriesLength = yAxisSeries.length,
  34058. visibleSeries,
  34059. upOrDown = pick(yAxis.options.reversedStacks, true) ? 1 : -1,
  34060. i;
  34061. points = points || this.points;
  34062. if (this.options.stacking) {
  34063. for (i = 0; i < points.length; i++) {
  34064. // Reset after point update (#7326)
  34065. points[i].leftNull = points[i].rightNull = null;
  34066. // Create a map where we can quickly look up the points by
  34067. // their X values.
  34068. pointMap[points[i].x] = points[i];
  34069. }
  34070. // Sort the keys (#1651)
  34071. H.objectEach(stack, function (stackX, x) {
  34072. // nulled after switching between
  34073. // grouping and not (#1651, #2336)
  34074. if (stackX.total !== null) {
  34075. keys.push(x);
  34076. }
  34077. });
  34078. keys.sort(function (a, b) {
  34079. return a - b;
  34080. });
  34081. visibleSeries = yAxisSeries.map(function (s) {
  34082. return s.visible;
  34083. });
  34084. keys.forEach(function (x, idx) {
  34085. var y = 0,
  34086. stackPoint,
  34087. stackedValues;
  34088. if (pointMap[x] && !pointMap[x].isNull) {
  34089. segment.push(pointMap[x]);
  34090. // Find left and right cliff. -1 goes left, 1 goes
  34091. // right.
  34092. [-1, 1].forEach(function (direction) {
  34093. var nullName = direction === 1 ?
  34094. 'rightNull' :
  34095. 'leftNull',
  34096. cliffName = direction === 1 ?
  34097. 'rightCliff' :
  34098. 'leftCliff',
  34099. cliff = 0,
  34100. otherStack = stack[keys[idx + direction]];
  34101. // If there is a stack next to this one,
  34102. // to the left or to the right...
  34103. if (otherStack) {
  34104. i = seriesIndex;
  34105. // Can go either up or down,
  34106. // depending on reversedStacks
  34107. while (i >= 0 && i < seriesLength) {
  34108. stackPoint = otherStack.points[i];
  34109. if (!stackPoint) {
  34110. // If the next point in this series
  34111. // is missing, mark the point
  34112. // with point.leftNull or
  34113. // point.rightNull = true.
  34114. if (i === seriesIndex) {
  34115. pointMap[x][nullName] = true;
  34116. // If there are missing points in
  34117. // the next stack in any of the
  34118. // series below this one, we need
  34119. // to substract the missing values
  34120. // and add a hiatus to the left or
  34121. // right.
  34122. } else if (visibleSeries[i]) {
  34123. stackedValues = stack[x].points[i];
  34124. if (stackedValues) {
  34125. cliff -= stackedValues[1] -
  34126. stackedValues[0];
  34127. }
  34128. }
  34129. }
  34130. // When reversedStacks is true, loop up,
  34131. // else loop down
  34132. i += upOrDown;
  34133. }
  34134. }
  34135. pointMap[x][cliffName] = cliff;
  34136. });
  34137. // There is no point for this X value in this series, so we
  34138. // insert a dummy point in order for the areas to be drawn
  34139. // correctly.
  34140. } else {
  34141. // Loop down the stack to find the series below this
  34142. // one that has a value (#1991)
  34143. i = seriesIndex;
  34144. while (i >= 0 && i < seriesLength) {
  34145. stackPoint = stack[x].points[i];
  34146. if (stackPoint) {
  34147. y = stackPoint[1];
  34148. break;
  34149. }
  34150. // When reversedStacks is true, loop up, else loop
  34151. // down
  34152. i += upOrDown;
  34153. }
  34154. y = yAxis.translate(y, 0, 1, 0, 1); // #6272
  34155. segment.push({
  34156. isNull: true,
  34157. plotX: xAxis.translate(x, 0, 0, 0, 1), // #6272
  34158. x: x,
  34159. plotY: y,
  34160. yBottom: y
  34161. });
  34162. }
  34163. });
  34164. }
  34165. return segment;
  34166. },
  34167. getGraphPath: function (points) {
  34168. var getGraphPath = Series.prototype.getGraphPath,
  34169. graphPath,
  34170. options = this.options,
  34171. stacking = options.stacking,
  34172. yAxis = this.yAxis,
  34173. topPath,
  34174. bottomPath,
  34175. bottomPoints = [],
  34176. graphPoints = [],
  34177. seriesIndex = this.index,
  34178. i,
  34179. areaPath,
  34180. plotX,
  34181. stacks = yAxis.stacks[this.stackKey],
  34182. threshold = options.threshold,
  34183. translatedThreshold = yAxis.getThreshold(options.threshold),
  34184. isNull,
  34185. yBottom,
  34186. connectNulls = options.connectNulls || stacking === 'percent',
  34187. // To display null points in underlying stacked series, this
  34188. // series graph must be broken, and the area also fall down to
  34189. // fill the gap left by the null point. #2069
  34190. addDummyPoints = function (i, otherI, side) {
  34191. var point = points[i],
  34192. stackedValues = stacking &&
  34193. stacks[point.x].points[seriesIndex],
  34194. nullVal = point[side + 'Null'] || 0,
  34195. cliffVal = point[side + 'Cliff'] || 0,
  34196. top,
  34197. bottom,
  34198. isNull = true;
  34199. if (cliffVal || nullVal) {
  34200. top = (nullVal ? stackedValues[0] : stackedValues[1]) +
  34201. cliffVal;
  34202. bottom = stackedValues[0] + cliffVal;
  34203. isNull = !!nullVal;
  34204. } else if (
  34205. !stacking &&
  34206. points[otherI] &&
  34207. points[otherI].isNull
  34208. ) {
  34209. top = bottom = threshold;
  34210. }
  34211. // Add to the top and bottom line of the area
  34212. if (top !== undefined) {
  34213. graphPoints.push({
  34214. plotX: plotX,
  34215. plotY: top === null ?
  34216. translatedThreshold :
  34217. yAxis.getThreshold(top),
  34218. isNull: isNull,
  34219. isCliff: true
  34220. });
  34221. bottomPoints.push({
  34222. plotX: plotX,
  34223. plotY: bottom === null ?
  34224. translatedThreshold :
  34225. yAxis.getThreshold(bottom),
  34226. doCurve: false // #1041, gaps in areaspline areas
  34227. });
  34228. }
  34229. };
  34230. // Find what points to use
  34231. points = points || this.points;
  34232. // Fill in missing points
  34233. if (stacking) {
  34234. points = this.getStackPoints(points);
  34235. }
  34236. for (i = 0; i < points.length; i++) {
  34237. isNull = points[i].isNull;
  34238. plotX = pick(points[i].rectPlotX, points[i].plotX);
  34239. yBottom = pick(points[i].yBottom, translatedThreshold);
  34240. if (!isNull || connectNulls) {
  34241. if (!connectNulls) {
  34242. addDummyPoints(i, i - 1, 'left');
  34243. }
  34244. // Skip null point when stacking is false and connectNulls
  34245. // true
  34246. if (!(isNull && !stacking && connectNulls)) {
  34247. graphPoints.push(points[i]);
  34248. bottomPoints.push({
  34249. x: i,
  34250. plotX: plotX,
  34251. plotY: yBottom
  34252. });
  34253. }
  34254. if (!connectNulls) {
  34255. addDummyPoints(i, i + 1, 'right');
  34256. }
  34257. }
  34258. }
  34259. topPath = getGraphPath.call(this, graphPoints, true, true);
  34260. bottomPoints.reversed = true;
  34261. bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  34262. if (bottomPath.length) {
  34263. bottomPath[0] = 'L';
  34264. }
  34265. areaPath = topPath.concat(bottomPath);
  34266. // TODO: don't set leftCliff and rightCliff when connectNulls?
  34267. graphPath = getGraphPath
  34268. .call(this, graphPoints, false, connectNulls);
  34269. areaPath.xMap = topPath.xMap;
  34270. this.areaPath = areaPath;
  34271. return graphPath;
  34272. },
  34273. // Draw the graph and the underlying area. This method calls the Series
  34274. // base function and adds the area. The areaPath is calculated in the
  34275. // getSegmentPath method called from Series.prototype.drawGraph.
  34276. drawGraph: function () {
  34277. // Define or reset areaPath
  34278. this.areaPath = [];
  34279. // Call the base method
  34280. Series.prototype.drawGraph.apply(this);
  34281. // Define local variables
  34282. var series = this,
  34283. areaPath = this.areaPath,
  34284. options = this.options,
  34285. zones = this.zones,
  34286. props = [[
  34287. 'area',
  34288. 'highcharts-area',
  34289. this.color,
  34290. options.fillColor
  34291. ]]; // area name, main color, fill color
  34292. zones.forEach(function (zone, i) {
  34293. props.push([
  34294. 'zone-area-' + i,
  34295. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  34296. zone.className,
  34297. zone.color || series.color,
  34298. zone.fillColor || options.fillColor
  34299. ]);
  34300. });
  34301. props.forEach(function (prop) {
  34302. var areaKey = prop[0],
  34303. area = series[areaKey],
  34304. attribs;
  34305. // Create or update the area
  34306. if (area) { // update
  34307. area.endX = series.preventGraphAnimation ?
  34308. null :
  34309. areaPath.xMap;
  34310. area.animate({ d: areaPath });
  34311. } else { // create
  34312. attribs = {
  34313. zIndex: 0 // #1069
  34314. };
  34315. if (!series.chart.styledMode) {
  34316. attribs.fill = pick(
  34317. prop[3],
  34318. color(prop[2])
  34319. .setOpacity(pick(options.fillOpacity, 0.75))
  34320. .get()
  34321. );
  34322. }
  34323. area = series[areaKey] = series.chart.renderer
  34324. .path(areaPath)
  34325. .addClass(prop[1])
  34326. .attr(attribs)
  34327. .add(series.group);
  34328. area.isArea = true;
  34329. }
  34330. area.startX = areaPath.xMap;
  34331. area.shiftUnit = options.step ? 2 : 1;
  34332. });
  34333. },
  34334. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  34335. });
  34336. /**
  34337. * A `area` series. If the [type](#series.area.type) option is not
  34338. * specified, it is inherited from [chart.type](#chart.type).
  34339. *
  34340. * @extends series,plotOptions.area
  34341. * @excluding dataParser, dataURL, useOhlcData
  34342. * @product highcharts highstock
  34343. * @apioption series.area
  34344. */
  34345. /**
  34346. * An array of data points for the series. For the `area` series type,
  34347. * points can be given in the following ways:
  34348. *
  34349. * 1. An array of numerical values. In this case, the numerical values will be
  34350. * interpreted as `y` options. The `x` values will be automatically
  34351. * calculated, either starting at 0 and incremented by 1, or from
  34352. * `pointStart` * and `pointInterval` given in the series options. If the
  34353. * axis has categories, these will be used. Example:
  34354. * ```js
  34355. * data: [0, 5, 3, 5]
  34356. * ```
  34357. *
  34358. * 2. An array of arrays with 2 values. In this case, the values correspond to
  34359. * `x,y`. If the first value is a string, it is applied as the name of the
  34360. * point, and the `x` value is inferred.
  34361. * ```js
  34362. * data: [
  34363. * [0, 9],
  34364. * [1, 7],
  34365. * [2, 6]
  34366. * ]
  34367. * ```
  34368. *
  34369. * 3. An array of objects with named values. The following snippet shows only a
  34370. * few settings, see the complete options set below. If the total number of
  34371. * data points exceeds the series'
  34372. * [turboThreshold](#series.area.turboThreshold), this option is not
  34373. * available.
  34374. * ```js
  34375. * data: [{
  34376. * x: 1,
  34377. * y: 9,
  34378. * name: "Point2",
  34379. * color: "#00FF00"
  34380. * }, {
  34381. * x: 1,
  34382. * y: 6,
  34383. * name: "Point1",
  34384. * color: "#FF00FF"
  34385. * }]
  34386. * ```
  34387. *
  34388. * @sample {highcharts} highcharts/chart/reflow-true/
  34389. * Numerical values
  34390. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  34391. * Arrays of numeric x and y
  34392. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  34393. * Arrays of datetime x and y
  34394. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  34395. * Arrays of point.name and y
  34396. * @sample {highcharts} highcharts/series/data-array-of-objects/
  34397. * Config objects
  34398. *
  34399. * @type {Array<number|Array<(number|string),number>|*>}
  34400. * @extends series.line.data
  34401. * @product highcharts highstock
  34402. * @apioption series.area.data
  34403. */
  34404. }(Highcharts));
  34405. (function (H) {
  34406. /* *
  34407. * (c) 2010-2019 Torstein Honsi
  34408. *
  34409. * License: www.highcharts.com/license
  34410. */
  34411. var pick = H.pick,
  34412. seriesType = H.seriesType;
  34413. /**
  34414. * Spline series type.
  34415. *
  34416. * @private
  34417. * @class
  34418. * @name Highcharts.seriesTypes.spline
  34419. *
  34420. * @augments Highcarts.Series
  34421. */
  34422. seriesType(
  34423. 'spline',
  34424. 'line',
  34425. /**
  34426. * A spline series is a special type of line series, where the segments
  34427. * between the data points are smoothed.
  34428. *
  34429. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  34430. * Spline chart
  34431. * @sample {highstock} stock/demo/spline/
  34432. * Spline chart
  34433. *
  34434. * @extends plotOptions.series
  34435. * @excluding step
  34436. * @product highcharts highstock
  34437. * @optionparent plotOptions.spline
  34438. */
  34439. {
  34440. },
  34441. /** @lends seriesTypes.spline.prototype */ {
  34442. /**
  34443. * Get the spline segment from a given point's previous neighbour to the
  34444. * given point.
  34445. *
  34446. * @private
  34447. * @function Highcharts.seriesTypes.spline#getPointSpline
  34448. *
  34449. * @param {Array<Highcharts.Point>}
  34450. *
  34451. * @param {Highcharts.Point} point
  34452. *
  34453. * @param {number} i
  34454. *
  34455. * @return {Highcharts.SVGPathArray}
  34456. */
  34457. getPointSpline: function (points, point, i) {
  34458. var
  34459. // 1 means control points midway between points, 2 means 1/3
  34460. // from the point, 3 is 1/4 etc
  34461. smoothing = 1.5,
  34462. denom = smoothing + 1,
  34463. plotX = point.plotX,
  34464. plotY = point.plotY,
  34465. lastPoint = points[i - 1],
  34466. nextPoint = points[i + 1],
  34467. leftContX,
  34468. leftContY,
  34469. rightContX,
  34470. rightContY,
  34471. ret;
  34472. function doCurve(otherPoint) {
  34473. return otherPoint &&
  34474. !otherPoint.isNull &&
  34475. otherPoint.doCurve !== false &&
  34476. !point.isCliff; // #6387, area splines next to null
  34477. }
  34478. // Find control points
  34479. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  34480. var lastX = lastPoint.plotX,
  34481. lastY = lastPoint.plotY,
  34482. nextX = nextPoint.plotX,
  34483. nextY = nextPoint.plotY,
  34484. correction = 0;
  34485. leftContX = (smoothing * plotX + lastX) / denom;
  34486. leftContY = (smoothing * plotY + lastY) / denom;
  34487. rightContX = (smoothing * plotX + nextX) / denom;
  34488. rightContY = (smoothing * plotY + nextY) / denom;
  34489. // Have the two control points make a straight line through main
  34490. // point
  34491. if (rightContX !== leftContX) { // #5016, division by zero
  34492. correction = (
  34493. ((rightContY - leftContY) * (rightContX - plotX)) /
  34494. (rightContX - leftContX) + plotY - rightContY
  34495. );
  34496. }
  34497. leftContY += correction;
  34498. rightContY += correction;
  34499. // to prevent false extremes, check that control points are
  34500. // between neighbouring points' y values
  34501. if (leftContY > lastY && leftContY > plotY) {
  34502. leftContY = Math.max(lastY, plotY);
  34503. // mirror of left control point
  34504. rightContY = 2 * plotY - leftContY;
  34505. } else if (leftContY < lastY && leftContY < plotY) {
  34506. leftContY = Math.min(lastY, plotY);
  34507. rightContY = 2 * plotY - leftContY;
  34508. }
  34509. if (rightContY > nextY && rightContY > plotY) {
  34510. rightContY = Math.max(nextY, plotY);
  34511. leftContY = 2 * plotY - rightContY;
  34512. } else if (rightContY < nextY && rightContY < plotY) {
  34513. rightContY = Math.min(nextY, plotY);
  34514. leftContY = 2 * plotY - rightContY;
  34515. }
  34516. // record for drawing in next point
  34517. point.rightContX = rightContX;
  34518. point.rightContY = rightContY;
  34519. }
  34520. // Visualize control points for debugging
  34521. /*
  34522. if (leftContX) {
  34523. this.chart.renderer.circle(
  34524. leftContX + this.chart.plotLeft,
  34525. leftContY + this.chart.plotTop,
  34526. 2
  34527. )
  34528. .attr({
  34529. stroke: 'red',
  34530. 'stroke-width': 2,
  34531. fill: 'none',
  34532. zIndex: 9
  34533. })
  34534. .add();
  34535. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  34536. leftContY + this.chart.plotTop,
  34537. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  34538. .attr({
  34539. stroke: 'red',
  34540. 'stroke-width': 2,
  34541. zIndex: 9
  34542. })
  34543. .add();
  34544. }
  34545. if (rightContX) {
  34546. this.chart.renderer.circle(
  34547. rightContX + this.chart.plotLeft,
  34548. rightContY + this.chart.plotTop,
  34549. 2
  34550. )
  34551. .attr({
  34552. stroke: 'green',
  34553. 'stroke-width': 2,
  34554. fill: 'none',
  34555. zIndex: 9
  34556. })
  34557. .add();
  34558. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  34559. rightContY + this.chart.plotTop,
  34560. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  34561. .attr({
  34562. stroke: 'green',
  34563. 'stroke-width': 2,
  34564. zIndex: 9
  34565. })
  34566. .add();
  34567. }
  34568. // */
  34569. ret = [
  34570. 'C',
  34571. pick(lastPoint.rightContX, lastPoint.plotX),
  34572. pick(lastPoint.rightContY, lastPoint.plotY),
  34573. pick(leftContX, plotX),
  34574. pick(leftContY, plotY),
  34575. plotX,
  34576. plotY
  34577. ];
  34578. // reset for updating series later
  34579. lastPoint.rightContX = lastPoint.rightContY = null;
  34580. return ret;
  34581. }
  34582. }
  34583. );
  34584. /**
  34585. * A `spline` series. If the [type](#series.spline.type) option is
  34586. * not specified, it is inherited from [chart.type](#chart.type).
  34587. *
  34588. * @extends series,plotOptions.spline
  34589. * @excluding dataParser, dataURL, step
  34590. * @product highcharts highstock
  34591. * @apioption series.spline
  34592. */
  34593. /**
  34594. * An array of data points for the series. For the `spline` series type,
  34595. * points can be given in the following ways:
  34596. *
  34597. * 1. An array of numerical values. In this case, the numerical values will be
  34598. * interpreted as `y` options. The `x` values will be automatically
  34599. * calculated, either starting at 0 and incremented by 1, or from
  34600. * `pointStart` and `pointInterval` given in the series options. If the axis
  34601. * has categories, these will be used. Example:
  34602. * ```js
  34603. * data: [0, 5, 3, 5]
  34604. * ```
  34605. *
  34606. * 2. An array of arrays with 2 values. In this case, the values correspond to
  34607. * `x,y`. If the first value is a string, it is applied as the name of the
  34608. * point, and the `x` value is inferred.
  34609. * ```js
  34610. * data: [
  34611. * [0, 9],
  34612. * [1, 2],
  34613. * [2, 8]
  34614. * ]
  34615. * ```
  34616. *
  34617. * 3. An array of objects with named values. The following snippet shows only a
  34618. * few settings, see the complete options set below. If the total number of
  34619. * data points exceeds the series'
  34620. * [turboThreshold](#series.spline.turboThreshold), this option is not
  34621. * available.
  34622. * ```js
  34623. * data: [{
  34624. * x: 1,
  34625. * y: 9,
  34626. * name: "Point2",
  34627. * color: "#00FF00"
  34628. * }, {
  34629. * x: 1,
  34630. * y: 0,
  34631. * name: "Point1",
  34632. * color: "#FF00FF"
  34633. * }]
  34634. * ```
  34635. *
  34636. * @sample {highcharts} highcharts/chart/reflow-true/
  34637. * Numerical values
  34638. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  34639. * Arrays of numeric x and y
  34640. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  34641. * Arrays of datetime x and y
  34642. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  34643. * Arrays of point.name and y
  34644. * @sample {highcharts} highcharts/series/data-array-of-objects/
  34645. * Config objects
  34646. *
  34647. * @type {Array<number|Array<(number|string),number>|*>}
  34648. * @extends series.line.data
  34649. * @product highcharts highstock
  34650. * @apioption series.spline.data
  34651. */
  34652. }(Highcharts));
  34653. (function (H) {
  34654. /* *
  34655. * (c) 2010-2019 Torstein Honsi
  34656. *
  34657. * License: www.highcharts.com/license
  34658. */
  34659. var areaProto = H.seriesTypes.area.prototype,
  34660. defaultPlotOptions = H.defaultPlotOptions,
  34661. LegendSymbolMixin = H.LegendSymbolMixin,
  34662. seriesType = H.seriesType;
  34663. /**
  34664. * AreaSpline series type.
  34665. *
  34666. * @private
  34667. * @class
  34668. * @name Highcharts.seriesTypes.areaspline
  34669. *
  34670. * @augments Highcharts.Series
  34671. */
  34672. seriesType('areaspline', 'spline',
  34673. /**
  34674. * The area spline series is an area series where the graph between the
  34675. * points is smoothed into a spline.
  34676. *
  34677. * @sample {highcharts} highcharts/demo/areaspline/
  34678. * Area spline chart
  34679. * @sample {highstock} stock/demo/areaspline/
  34680. * Area spline chart
  34681. *
  34682. * @extends plotOptions.area
  34683. * @excluding step
  34684. * @product highcharts highstock
  34685. * @apioption plotOptions.areaspline
  34686. */
  34687. defaultPlotOptions.area
  34688. , {
  34689. getStackPoints: areaProto.getStackPoints,
  34690. getGraphPath: areaProto.getGraphPath,
  34691. drawGraph: areaProto.drawGraph,
  34692. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  34693. });
  34694. /**
  34695. * A `areaspline` series. If the [type](#series.areaspline.type) option
  34696. * is not specified, it is inherited from [chart.type](#chart.type).
  34697. *
  34698. *
  34699. * @extends series,plotOptions.areaspline
  34700. * @excluding dataParser, dataURL
  34701. * @product highcharts highstock
  34702. * @apioption series.areaspline
  34703. */
  34704. /**
  34705. * An array of data points for the series. For the `areaspline` series
  34706. * type, points can be given in the following ways:
  34707. *
  34708. * 1. An array of numerical values. In this case, the numerical values will be
  34709. * interpreted as `y` options. The `x` values will be automatically
  34710. * calculated, either starting at 0 and incremented by 1, or from
  34711. * `pointStart` and `pointInterval` given in the series options. If the axis
  34712. * has categories, these will be used. Example:
  34713. * ```js
  34714. * data: [0, 5, 3, 5]
  34715. * ```
  34716. *
  34717. * 2. An array of arrays with 2 values. In this case, the values correspond to
  34718. * `x,y`. If the first value is a string, it is applied as the name of the
  34719. * point, and the `x` value is inferred.
  34720. * ```js
  34721. * data: [
  34722. * [0, 10],
  34723. * [1, 9],
  34724. * [2, 3]
  34725. * ]
  34726. * ```
  34727. *
  34728. * 3. An array of objects with named values. The following snippet shows only a
  34729. * few settings, see the complete options set below. If the total number of
  34730. * data points exceeds the series'
  34731. * [turboThreshold](#series.areaspline.turboThreshold), this option is not
  34732. * available.
  34733. * ```js
  34734. * data: [{
  34735. * x: 1,
  34736. * y: 4,
  34737. * name: "Point2",
  34738. * color: "#00FF00"
  34739. * }, {
  34740. * x: 1,
  34741. * y: 4,
  34742. * name: "Point1",
  34743. * color: "#FF00FF"
  34744. * }]
  34745. * ```
  34746. *
  34747. * @sample {highcharts} highcharts/chart/reflow-true/
  34748. * Numerical values
  34749. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  34750. * Arrays of numeric x and y
  34751. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  34752. * Arrays of datetime x and y
  34753. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  34754. * Arrays of point.name and y
  34755. * @sample {highcharts} highcharts/series/data-array-of-objects/
  34756. * Config objects
  34757. *
  34758. * @type {Array<number|Array<(number|string),number>|*>}
  34759. * @extends series.line.data
  34760. * @product highcharts highstock
  34761. * @apioption series.areaspline.data
  34762. */
  34763. }(Highcharts));
  34764. (function (H) {
  34765. /* *
  34766. * (c) 2010-2019 Torstein Honsi
  34767. *
  34768. * License: www.highcharts.com/license
  34769. */
  34770. /**
  34771. * Adjusted width and x offset of the columns for grouping.
  34772. *
  34773. * @private
  34774. * @interface Highcharts.ColumnMetricsObject
  34775. *//**
  34776. * Width of the columns.
  34777. *
  34778. * @name Highcharts.ColumnMetricsObject#width
  34779. * @type {number}
  34780. *//**
  34781. * Offset of the columns.
  34782. *
  34783. * @name Highcharts.ColumnMetricsObject#offset
  34784. * @type {number}
  34785. */
  34786. var animObject = H.animObject,
  34787. color = H.color,
  34788. extend = H.extend,
  34789. defined = H.defined,
  34790. isNumber = H.isNumber,
  34791. LegendSymbolMixin = H.LegendSymbolMixin,
  34792. merge = H.merge,
  34793. noop = H.noop,
  34794. pick = H.pick,
  34795. Series = H.Series,
  34796. seriesType = H.seriesType,
  34797. svg = H.svg;
  34798. /**
  34799. * The column series type.
  34800. *
  34801. * @private
  34802. * @class
  34803. * @name Highcharts.seriesTypes.column
  34804. *
  34805. * @augments Highcharts.Series
  34806. */
  34807. seriesType('column', 'line'
  34808. /**
  34809. * Column series display one column per value along an X axis.
  34810. *
  34811. * @sample {highcharts} highcharts/demo/column-basic/
  34812. * Column chart
  34813. * @sample {highstock} stock/demo/column/
  34814. * Column chart
  34815. *
  34816. * @extends plotOptions.line
  34817. * @excluding connectNulls, dashStyle, gapSize, gapUnit, linecap,
  34818. * lineWidth, marker, connectEnds, step, useOhlcData
  34819. * @product highcharts highstock
  34820. * @optionparent plotOptions.column
  34821. */
  34822. , {
  34823. /**
  34824. * The corner radius of the border surrounding each column or bar.
  34825. *
  34826. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  34827. * Rounded columns
  34828. *
  34829. * @product highcharts highstock gantt
  34830. */
  34831. borderRadius: 0,
  34832. /**
  34833. * When using automatic point colors pulled from the global
  34834. * [colors](colors) or series-specific
  34835. * [plotOptions.column.colors](series.colors) collections, this option
  34836. * determines whether the chart should receive one color per series or
  34837. * one color per point.
  34838. *
  34839. * In styled mode, the `colors` or `series.colors` arrays are not
  34840. * supported, and instead this option gives the points individual color
  34841. * class names on the form `highcharts-color-{n}`.
  34842. *
  34843. * @see [series colors](#plotOptions.column.colors)
  34844. *
  34845. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
  34846. * False by default
  34847. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
  34848. * True
  34849. *
  34850. * @type {boolean}
  34851. * @default false
  34852. * @since 2.0
  34853. * @product highcharts highstock gantt
  34854. * @apioption plotOptions.column.colorByPoint
  34855. */
  34856. /**
  34857. * A series specific or series type specific color set to apply instead
  34858. * of the global [colors](#colors) when [colorByPoint](
  34859. * #plotOptions.column.colorByPoint) is true.
  34860. *
  34861. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  34862. * @since 3.0
  34863. * @product highcharts highstock gantt
  34864. * @apioption plotOptions.column.colors
  34865. */
  34866. /**
  34867. * When true, each column edge is rounded to its nearest pixel in order
  34868. * to render sharp on screen. In some cases, when there are a lot of
  34869. * densely packed columns, this leads to visible difference in column
  34870. * widths or distance between columns. In these cases, setting `crisp`
  34871. * to `false` may look better, even though each column is rendered
  34872. * blurry.
  34873. *
  34874. * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
  34875. * Crisp is false
  34876. *
  34877. * @since 5.0.10
  34878. * @product highcharts highstock gantt
  34879. */
  34880. crisp: true,
  34881. /**
  34882. * Padding between each value groups, in x axis units.
  34883. *
  34884. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
  34885. * 0.2 by default
  34886. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
  34887. * No group padding - all columns are evenly spaced
  34888. *
  34889. * @product highcharts highstock gantt
  34890. */
  34891. groupPadding: 0.2,
  34892. /**
  34893. * Whether to group non-stacked columns or to let them render
  34894. * independent of each other. Non-grouped columns will be laid out
  34895. * individually and overlap each other.
  34896. *
  34897. * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
  34898. * Grouping disabled
  34899. * @sample {highstock} highcharts/plotoptions/column-grouping-false/
  34900. * Grouping disabled
  34901. *
  34902. * @type {boolean}
  34903. * @default true
  34904. * @since 2.3.0
  34905. * @product highcharts highstock gantt
  34906. * @apioption plotOptions.column.grouping
  34907. */
  34908. /**
  34909. * @ignore-option
  34910. */
  34911. marker: null, // point options are specified in the base options
  34912. /**
  34913. * The maximum allowed pixel width for a column, translated to the
  34914. * height of a bar in a bar chart. This prevents the columns from
  34915. * becoming too wide when there is a small number of points in the
  34916. * chart.
  34917. *
  34918. * @see [pointWidth](#plotOptions.column.pointWidth)
  34919. *
  34920. * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
  34921. * Limited to 50
  34922. * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
  34923. * Limited to 50
  34924. *
  34925. * @type {number}
  34926. * @since 4.1.8
  34927. * @product highcharts highstock gantt
  34928. * @apioption plotOptions.column.maxPointWidth
  34929. */
  34930. /**
  34931. * Padding between each column or bar, in x axis units.
  34932. *
  34933. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
  34934. * 0.1 by default
  34935. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
  34936. * 0.25
  34937. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
  34938. * 0 for tightly packed columns
  34939. *
  34940. * @product highcharts highstock gantt
  34941. */
  34942. pointPadding: 0.1,
  34943. /**
  34944. * A pixel value specifying a fixed width for each column or bar. When
  34945. * `null`, the width is calculated from the `pointPadding` and
  34946. * `groupPadding`.
  34947. *
  34948. * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
  34949. *
  34950. * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
  34951. * 20px wide columns regardless of chart width or the amount of
  34952. * data points
  34953. *
  34954. * @type {number}
  34955. * @since 1.2.5
  34956. * @product highcharts highstock gantt
  34957. * @apioption plotOptions.column.pointWidth
  34958. */
  34959. /**
  34960. * A pixel value specifying a fixed width for the column or bar.
  34961. * Overrides pointWidth on the series.
  34962. *
  34963. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  34964. *
  34965. * @type {number}
  34966. * @default undefined
  34967. * @since 7.0.0
  34968. * @product highcharts highstock gantt
  34969. * @apioption series.column.data.pointWidth
  34970. */
  34971. /**
  34972. * The minimal height for a column or width for a bar. By default,
  34973. * 0 values are not shown. To visualize a 0 (or close to zero) point,
  34974. * set the minimal point length to a pixel value like 3\. In stacked
  34975. * column charts, minPointLength might not be respected for tightly
  34976. * packed values.
  34977. *
  34978. * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
  34979. * Zero base value
  34980. * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
  34981. * Positive and negative close to zero values
  34982. *
  34983. * @product highcharts highstock gantt
  34984. */
  34985. minPointLength: 0,
  34986. /**
  34987. * When the series contains less points than the crop threshold, all
  34988. * points are drawn, event if the points fall outside the visible plot
  34989. * area at the current zoom. The advantage of drawing all points
  34990. * (including markers and columns), is that animation is performed on
  34991. * updates. On the other hand, when the series contains more points than
  34992. * the crop threshold, the series data is cropped to only contain points
  34993. * that fall within the plot area. The advantage of cropping away
  34994. * invisible points is to increase performance on large series.
  34995. *
  34996. * @product highcharts highstock gantt
  34997. */
  34998. cropThreshold: 50,
  34999. /**
  35000. * The X axis range that each point is valid for. This determines the
  35001. * width of the column. On a categorized axis, the range will be 1
  35002. * by default (one category unit). On linear and datetime axes, the
  35003. * range will be computed as the distance between the two closest data
  35004. * points.
  35005. *
  35006. * The default `null` means it is computed automatically, but this
  35007. * option can be used to override the automatic value.
  35008. *
  35009. * @sample {highcharts} highcharts/plotoptions/column-pointrange/
  35010. * Set the point range to one day on a data set with one week
  35011. * between the points
  35012. *
  35013. * @type {number|null}
  35014. * @since 2.3
  35015. * @product highcharts highstock gantt
  35016. */
  35017. pointRange: null,
  35018. states: {
  35019. /**
  35020. * Options for the hovered point. These settings override the normal
  35021. * state options when a point is moused over or touched.
  35022. *
  35023. * @extends plotOptions.series.states.hover
  35024. * @excluding halo, lineWidth, lineWidthPlus, marker
  35025. * @product highcharts highstock gantt
  35026. */
  35027. hover: {
  35028. /** @ignore-option */
  35029. halo: false,
  35030. /**
  35031. * A specific border color for the hovered point. Defaults to
  35032. * inherit the normal state border color.
  35033. *
  35034. * @type {Highcharts.ColorString}
  35035. * @product highcharts gantt
  35036. * @apioption plotOptions.column.states.hover.borderColor
  35037. */
  35038. /**
  35039. * A specific color for the hovered point.
  35040. *
  35041. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35042. * @product highcharts gantt
  35043. * @apioption plotOptions.column.states.hover.color
  35044. */
  35045. /**
  35046. * How much to brighten the point on interaction. Requires the
  35047. * main color to be defined in hex or rgb(a) format.
  35048. *
  35049. * In styled mode, the hover brightening is by default replaced
  35050. * with a fill-opacity set in the `.highcharts-point:hover`
  35051. * rule.
  35052. *
  35053. * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
  35054. * Brighten by 0.5
  35055. *
  35056. * @product highcharts highstock gantt
  35057. */
  35058. brightness: 0.1
  35059. },
  35060. /**
  35061. * Options for the selected point. These settings override the
  35062. * normal state options when a point is selected.
  35063. *
  35064. * @extends plotOptions.series.states.select
  35065. * @excluding halo, lineWidth, lineWidthPlus, marker
  35066. * @product highcharts highstock gantt
  35067. */
  35068. select: {
  35069. /**
  35070. * A specific color for the selected point.
  35071. *
  35072. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35073. * @default #cccccc
  35074. * @product highcharts highstock gantt
  35075. */
  35076. color: '#cccccc',
  35077. /**
  35078. * A specific border color for the selected point.
  35079. *
  35080. * @type {Highcharts.ColorString}
  35081. * @default #000000
  35082. * @product highcharts highstock gantt
  35083. */
  35084. borderColor: '#000000'
  35085. }
  35086. },
  35087. dataLabels: {
  35088. /**
  35089. * @type {Highcharts.AlignType|null}
  35090. */
  35091. align: null, // auto
  35092. /**
  35093. * @type {Highcharts.VerticalAlignType|null}
  35094. */
  35095. verticalAlign: null, // auto
  35096. /**
  35097. * @type {number|null}
  35098. */
  35099. y: null
  35100. },
  35101. /**
  35102. * When this is true, the series will not cause the Y axis to cross
  35103. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  35104. * unless the data actually crosses the plane.
  35105. *
  35106. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  35107. * 3 will make the Y axis show negative values according to the
  35108. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  35109. * at 0.
  35110. *
  35111. * @since 4.1.9
  35112. * @product highcharts highstock
  35113. */
  35114. softThreshold: false,
  35115. // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
  35116. /** @ignore-option */
  35117. startFromThreshold: true,
  35118. stickyTracking: false,
  35119. tooltip: {
  35120. distance: 6
  35121. },
  35122. /**
  35123. * The Y axis value to serve as the base for the columns, for
  35124. * distinguishing between values above and below a threshold. If `null`,
  35125. * the columns extend from the padding Y axis minimum.
  35126. *
  35127. * @since 2.0
  35128. * @product highcharts
  35129. */
  35130. threshold: 0,
  35131. /**
  35132. * The width of the border surrounding each column or bar. Defaults to
  35133. * `1` when there is room for a border, but to `0` when the columns are
  35134. * so dense that a border would cover the next column.
  35135. *
  35136. * In styled mode, the stroke width can be set with the
  35137. * `.highcharts-point` rule.
  35138. *
  35139. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  35140. * 2px black border
  35141. *
  35142. * @type {number}
  35143. * @default undefined
  35144. * @product highcharts highstock gantt
  35145. * @apioption plotOptions.column.borderWidth
  35146. */
  35147. /**
  35148. * The color of the border surrounding each column or bar.
  35149. *
  35150. * In styled mode, the border stroke can be set with the
  35151. * `.highcharts-point` rule.
  35152. *
  35153. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  35154. * Dark gray border
  35155. *
  35156. * @type {Highcharts.ColorString}
  35157. * @default #ffffff
  35158. * @product highcharts highstock gantt
  35159. */
  35160. borderColor: '#ffffff'
  35161. }, /** @lends seriesTypes.column.prototype */ {
  35162. cropShoulder: 0,
  35163. // When tooltip is not shared, this series (and derivatives) requires
  35164. // direct touch/hover. KD-tree does not apply.
  35165. directTouch: true,
  35166. trackerGroups: ['group', 'dataLabelsGroup'],
  35167. // use separate negative stacks, unlike area stacks where a negative
  35168. // point is substracted from previous (#1910)
  35169. negStacks: true,
  35170. /**
  35171. * Initialize the series. Extends the basic Series.init method by
  35172. * marking other series of the same type as dirty.
  35173. *
  35174. * @private
  35175. * @function Highcharts.seriesTypes.column#init
  35176. */
  35177. init: function () {
  35178. Series.prototype.init.apply(this, arguments);
  35179. var series = this,
  35180. chart = series.chart;
  35181. // if the series is added dynamically, force redraw of other
  35182. // series affected by a new column
  35183. if (chart.hasRendered) {
  35184. chart.series.forEach(function (otherSeries) {
  35185. if (otherSeries.type === series.type) {
  35186. otherSeries.isDirty = true;
  35187. }
  35188. });
  35189. }
  35190. },
  35191. /**
  35192. * Return the width and x offset of the columns adjusted for grouping,
  35193. * groupPadding, pointPadding, pointWidth etc.
  35194. *
  35195. * @private
  35196. * @function Highcharts.seriesTypes.column#getColumnMetrics
  35197. *
  35198. * @return {Highcharts.ColumnMetricsObject}
  35199. */
  35200. getColumnMetrics: function () {
  35201. var series = this,
  35202. options = series.options,
  35203. xAxis = series.xAxis,
  35204. yAxis = series.yAxis,
  35205. reversedStacks = xAxis.options.reversedStacks,
  35206. // Keep backward compatibility: reversed xAxis had reversed
  35207. // stacks
  35208. reverseStacks = (xAxis.reversed && !reversedStacks) ||
  35209. (!xAxis.reversed && reversedStacks),
  35210. stackKey,
  35211. stackGroups = {},
  35212. columnCount = 0;
  35213. // Get the total number of column type series. This is called on
  35214. // every series. Consider moving this logic to a chart.orderStacks()
  35215. // function and call it on init, addSeries and removeSeries
  35216. if (options.grouping === false) {
  35217. columnCount = 1;
  35218. } else {
  35219. series.chart.series.forEach(function (otherSeries) {
  35220. var otherOptions = otherSeries.options,
  35221. otherYAxis = otherSeries.yAxis,
  35222. columnIndex;
  35223. if (
  35224. otherSeries.type === series.type &&
  35225. (
  35226. otherSeries.visible ||
  35227. !series.chart.options.chart.ignoreHiddenSeries
  35228. ) &&
  35229. yAxis.len === otherYAxis.len &&
  35230. yAxis.pos === otherYAxis.pos
  35231. ) { // #642, #2086
  35232. if (otherOptions.stacking) {
  35233. stackKey = otherSeries.stackKey;
  35234. if (stackGroups[stackKey] === undefined) {
  35235. stackGroups[stackKey] = columnCount++;
  35236. }
  35237. columnIndex = stackGroups[stackKey];
  35238. } else if (otherOptions.grouping !== false) { // #1162
  35239. columnIndex = columnCount++;
  35240. }
  35241. otherSeries.columnIndex = columnIndex;
  35242. }
  35243. });
  35244. }
  35245. var categoryWidth = Math.min(
  35246. Math.abs(xAxis.transA) * (
  35247. xAxis.ordinalSlope ||
  35248. options.pointRange ||
  35249. xAxis.closestPointRange ||
  35250. xAxis.tickInterval ||
  35251. 1
  35252. ), // #2610
  35253. xAxis.len // #1535
  35254. ),
  35255. groupPadding = categoryWidth * options.groupPadding,
  35256. groupWidth = categoryWidth - 2 * groupPadding,
  35257. pointOffsetWidth = groupWidth / (columnCount || 1),
  35258. pointWidth = Math.min(
  35259. options.maxPointWidth || xAxis.len,
  35260. pick(
  35261. options.pointWidth,
  35262. pointOffsetWidth * (1 - 2 * options.pointPadding)
  35263. )
  35264. ),
  35265. pointPadding = (pointOffsetWidth - pointWidth) / 2,
  35266. // #1251, #3737
  35267. colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0),
  35268. pointXOffset =
  35269. pointPadding +
  35270. (
  35271. groupPadding +
  35272. colIndex * pointOffsetWidth -
  35273. (categoryWidth / 2)
  35274. ) * (reverseStacks ? -1 : 1);
  35275. // Save it for reading in linked series (Error bars particularly)
  35276. series.columnMetrics = {
  35277. width: pointWidth,
  35278. offset: pointXOffset
  35279. };
  35280. return series.columnMetrics;
  35281. },
  35282. /**
  35283. * Make the columns crisp. The edges are rounded to the nearest full
  35284. * pixel.
  35285. *
  35286. * @private
  35287. * @function Highcharts.seriesTypes.column#crispCol
  35288. *
  35289. * @param {number} x
  35290. *
  35291. * @param {number} y
  35292. *
  35293. * @param {number} w
  35294. *
  35295. * @param {number} h
  35296. *
  35297. * @return {*}
  35298. */
  35299. crispCol: function (x, y, w, h) {
  35300. var chart = this.chart,
  35301. borderWidth = this.borderWidth,
  35302. xCrisp = -(borderWidth % 2 ? 0.5 : 0),
  35303. yCrisp = borderWidth % 2 ? 0.5 : 1,
  35304. right,
  35305. bottom,
  35306. fromTop;
  35307. if (chart.inverted && chart.renderer.isVML) {
  35308. yCrisp += 1;
  35309. }
  35310. // Horizontal. We need to first compute the exact right edge, then
  35311. // round it and compute the width from there.
  35312. if (this.options.crisp) {
  35313. right = Math.round(x + w) + xCrisp;
  35314. x = Math.round(x) + xCrisp;
  35315. w = right - x;
  35316. }
  35317. // Vertical
  35318. bottom = Math.round(y + h) + yCrisp;
  35319. fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
  35320. y = Math.round(y) + yCrisp;
  35321. h = bottom - y;
  35322. // Top edges are exceptions
  35323. if (fromTop && h) { // #5146
  35324. y -= 1;
  35325. h += 1;
  35326. }
  35327. return {
  35328. x: x,
  35329. y: y,
  35330. width: w,
  35331. height: h
  35332. };
  35333. },
  35334. /**
  35335. * Translate each point to the plot area coordinate system and find
  35336. * shape positions
  35337. *
  35338. * @private
  35339. * @function Highcharts.seriesTypes.column#translate
  35340. */
  35341. translate: function () {
  35342. var series = this,
  35343. chart = series.chart,
  35344. options = series.options,
  35345. dense = series.dense =
  35346. series.closestPointRange * series.xAxis.transA < 2,
  35347. borderWidth = series.borderWidth = pick(
  35348. options.borderWidth,
  35349. dense ? 0 : 1 // #3635
  35350. ),
  35351. yAxis = series.yAxis,
  35352. threshold = options.threshold,
  35353. translatedThreshold = series.translatedThreshold =
  35354. yAxis.getThreshold(threshold),
  35355. minPointLength = pick(options.minPointLength, 5),
  35356. metrics = series.getColumnMetrics(),
  35357. seriesPointWidth = metrics.width,
  35358. // postprocessed for border width
  35359. seriesBarW = series.barW =
  35360. Math.max(seriesPointWidth, 1 + 2 * borderWidth),
  35361. seriesXOffset = series.pointXOffset = metrics.offset;
  35362. if (chart.inverted) {
  35363. translatedThreshold -= 0.5; // #3355
  35364. }
  35365. // When the pointPadding is 0, we want the columns to be packed
  35366. // tightly, so we allow individual columns to have individual sizes.
  35367. // When pointPadding is greater, we strive for equal-width columns
  35368. // (#2694).
  35369. if (options.pointPadding) {
  35370. seriesBarW = Math.ceil(seriesBarW);
  35371. }
  35372. Series.prototype.translate.apply(series);
  35373. // Record the new values
  35374. series.points.forEach(function (point) {
  35375. var yBottom = pick(point.yBottom, translatedThreshold),
  35376. safeDistance = 999 + Math.abs(yBottom),
  35377. pointWidth = seriesPointWidth,
  35378. // Don't draw too far outside plot area (#1303, #2241,
  35379. // #4264)
  35380. plotY = Math.min(
  35381. Math.max(-safeDistance, point.plotY),
  35382. yAxis.len + safeDistance
  35383. ),
  35384. barX = point.plotX + seriesXOffset,
  35385. barW = seriesBarW,
  35386. barY = Math.min(plotY, yBottom),
  35387. up,
  35388. barH = Math.max(plotY, yBottom) - barY;
  35389. // Handle options.minPointLength
  35390. if (minPointLength && Math.abs(barH) < minPointLength) {
  35391. barH = minPointLength;
  35392. up = (!yAxis.reversed && !point.negative) ||
  35393. (yAxis.reversed && point.negative);
  35394. // Reverse zeros if there's no positive value in the series
  35395. // in visible range (#7046)
  35396. if (
  35397. point.y === threshold &&
  35398. series.dataMax <= threshold &&
  35399. yAxis.min < threshold // and if there's room for it (#7311)
  35400. ) {
  35401. up = !up;
  35402. }
  35403. // If stacked...
  35404. barY = (
  35405. Math.abs(barY - translatedThreshold) > minPointLength ?
  35406. // ...keep position
  35407. yBottom - minPointLength :
  35408. // #1485, #4051
  35409. translatedThreshold - (up ? minPointLength : 0)
  35410. );
  35411. }
  35412. // Handle point.options.pointWidth
  35413. // @todo Handle grouping/stacking too. Calculate offset properly
  35414. if (defined(point.options.pointWidth)) {
  35415. pointWidth = barW = Math.ceil(point.options.pointWidth);
  35416. barX -= Math.round((pointWidth - seriesPointWidth) / 2);
  35417. }
  35418. // Cache for access in polar
  35419. point.barX = barX;
  35420. point.pointWidth = pointWidth;
  35421. // Fix the tooltip on center of grouped columns (#1216, #424,
  35422. // #3648)
  35423. point.tooltipPos = chart.inverted ?
  35424. [
  35425. yAxis.len + yAxis.pos - chart.plotLeft - plotY,
  35426. series.xAxis.len - barX - barW / 2, barH
  35427. ] :
  35428. [barX + barW / 2, plotY + yAxis.pos - chart.plotTop, barH];
  35429. // Register shape type and arguments to be used in drawPoints
  35430. // Allow shapeType defined on pointClass level
  35431. point.shapeType = point.shapeType || 'rect';
  35432. point.shapeArgs = series.crispCol.apply(
  35433. series,
  35434. point.isNull ?
  35435. // #3169, drilldown from null must have a position to work
  35436. // from #6585, dataLabel should be placed on xAxis, not
  35437. // floating in the middle of the chart
  35438. [barX, translatedThreshold, barW, 0] :
  35439. [barX, barY, barW, barH]
  35440. );
  35441. });
  35442. },
  35443. getSymbol: noop,
  35444. /**
  35445. * Use a solid rectangle like the area series types
  35446. *
  35447. * @private
  35448. * @function Highcharts.seriesTypes.column#drawLegendSymbol
  35449. *
  35450. * @param {Highcharts.Legend} legend
  35451. * The legend object
  35452. *
  35453. * @param {Highcharts.Series|Highcharts.Point} item
  35454. * The series (this) or point
  35455. */
  35456. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  35457. /**
  35458. * Columns have no graph
  35459. *
  35460. * @private
  35461. * @function Highcharts.seriesTypes.column#drawGraph
  35462. */
  35463. drawGraph: function () {
  35464. this.group[
  35465. this.dense ? 'addClass' : 'removeClass'
  35466. ]('highcharts-dense-data');
  35467. },
  35468. /**
  35469. * Get presentational attributes
  35470. *
  35471. * @private
  35472. * @function Highcharts.seriesTypes.column#pointAttribs
  35473. *
  35474. * @param {Highcharts.Point} point
  35475. *
  35476. * @param {string} state
  35477. *
  35478. * @return {Highcharts.Dictionary<any>}
  35479. */
  35480. pointAttribs: function (point, state) {
  35481. var options = this.options,
  35482. stateOptions,
  35483. ret,
  35484. p2o = this.pointAttrToOptions || {},
  35485. strokeOption = p2o.stroke || 'borderColor',
  35486. strokeWidthOption = p2o['stroke-width'] || 'borderWidth',
  35487. fill = (point && point.color) || this.color,
  35488. // set to fill when borderColor null:
  35489. stroke = (
  35490. (point && point[strokeOption]) ||
  35491. options[strokeOption] ||
  35492. this.color ||
  35493. fill
  35494. ),
  35495. strokeWidth = (point && point[strokeWidthOption]) ||
  35496. options[strokeWidthOption] || this[strokeWidthOption] || 0,
  35497. dashstyle = options.dashStyle,
  35498. zone,
  35499. brightness;
  35500. // Handle zone colors
  35501. if (point && this.zones.length) {
  35502. zone = point.getZone();
  35503. // When zones are present, don't use point.color (#4267).
  35504. // Changed order (#6527)
  35505. fill = (
  35506. point.options.color || (zone && zone.color) || this.color
  35507. );
  35508. }
  35509. // Select or hover states
  35510. if (state) {
  35511. stateOptions = merge(
  35512. options.states[state],
  35513. // #6401
  35514. point.options.states && point.options.states[state] || {}
  35515. );
  35516. brightness = stateOptions.brightness;
  35517. fill = stateOptions.color ||
  35518. (
  35519. brightness !== undefined &&
  35520. color(fill).brighten(stateOptions.brightness).get()
  35521. ) ||
  35522. fill;
  35523. stroke = stateOptions[strokeOption] || stroke;
  35524. strokeWidth = stateOptions[strokeWidthOption] || strokeWidth;
  35525. dashstyle = stateOptions.dashStyle || dashstyle;
  35526. }
  35527. ret = {
  35528. 'fill': fill,
  35529. 'stroke': stroke,
  35530. 'stroke-width': strokeWidth
  35531. };
  35532. if (dashstyle) {
  35533. ret.dashstyle = dashstyle;
  35534. }
  35535. return ret;
  35536. },
  35537. /**
  35538. * Draw the columns. For bars, the series.group is rotated, so the same
  35539. * coordinates apply for columns and bars. This method is inherited by
  35540. * scatter series.
  35541. *
  35542. * @private
  35543. * @function Highcharts.seriesTypes.column#drawPoints
  35544. */
  35545. drawPoints: function () {
  35546. var series = this,
  35547. chart = this.chart,
  35548. options = series.options,
  35549. renderer = chart.renderer,
  35550. animationLimit = options.animationLimit || 250,
  35551. shapeArgs;
  35552. // draw the columns
  35553. series.points.forEach(function (point) {
  35554. var plotY = point.plotY,
  35555. graphic = point.graphic,
  35556. verb = graphic && chart.pointCount < animationLimit ?
  35557. 'animate' : 'attr';
  35558. if (isNumber(plotY) && point.y !== null) {
  35559. shapeArgs = point.shapeArgs;
  35560. if (graphic) { // update
  35561. graphic[verb](
  35562. merge(shapeArgs)
  35563. );
  35564. } else {
  35565. point.graphic = graphic =
  35566. renderer[point.shapeType](shapeArgs)
  35567. .add(point.group || series.group);
  35568. }
  35569. // Border radius is not stylable (#6900)
  35570. if (options.borderRadius) {
  35571. graphic.attr({
  35572. r: options.borderRadius
  35573. });
  35574. }
  35575. // Presentational
  35576. if (!chart.styledMode) {
  35577. graphic[verb](series.pointAttribs(
  35578. point,
  35579. point.selected && 'select'
  35580. ))
  35581. .shadow(
  35582. options.shadow,
  35583. null,
  35584. options.stacking && !options.borderRadius
  35585. );
  35586. }
  35587. graphic.addClass(point.getClassName(), true);
  35588. } else if (graphic) {
  35589. point.graphic = graphic.destroy(); // #1269
  35590. }
  35591. });
  35592. },
  35593. /**
  35594. * Animate the column heights one by one from zero.
  35595. *
  35596. * @private
  35597. * @function Highcharts.seriesTypes.column#animate
  35598. *
  35599. * @param {boolean} init
  35600. * Whether to initialize the animation or run it
  35601. */
  35602. animate: function (init) {
  35603. var series = this,
  35604. yAxis = this.yAxis,
  35605. options = series.options,
  35606. inverted = this.chart.inverted,
  35607. attr = {},
  35608. translateProp = inverted ? 'translateX' : 'translateY',
  35609. translateStart,
  35610. translatedThreshold;
  35611. if (svg) { // VML is too slow anyway
  35612. if (init) {
  35613. attr.scaleY = 0.001;
  35614. translatedThreshold = Math.min(
  35615. yAxis.pos + yAxis.len,
  35616. Math.max(yAxis.pos, yAxis.toPixels(options.threshold))
  35617. );
  35618. if (inverted) {
  35619. attr.translateX = translatedThreshold - yAxis.len;
  35620. } else {
  35621. attr.translateY = translatedThreshold;
  35622. }
  35623. // apply finnal clipping (used in Highstock) (#7083)
  35624. // animation is done by scaleY, so cliping is for panes
  35625. if (series.clipBox) {
  35626. series.setClip();
  35627. }
  35628. series.group.attr(attr);
  35629. } else { // run the animation
  35630. translateStart = series.group.attr(translateProp);
  35631. series.group.animate(
  35632. { scaleY: 1 },
  35633. extend(animObject(series.options.animation), {
  35634. // Do the scale synchronously to ensure smooth
  35635. // updating (#5030, #7228)
  35636. step: function (val, fx) {
  35637. attr[translateProp] =
  35638. translateStart +
  35639. fx.pos * (yAxis.pos - translateStart);
  35640. series.group.attr(attr);
  35641. }
  35642. })
  35643. );
  35644. // delete this function to allow it only once
  35645. series.animate = null;
  35646. }
  35647. }
  35648. },
  35649. /**
  35650. * Remove this series from the chart
  35651. *
  35652. * @private
  35653. * @function Highcharts.seriesTypes.column#remove
  35654. */
  35655. remove: function () {
  35656. var series = this,
  35657. chart = series.chart;
  35658. // column and bar series affects other series of the same type
  35659. // as they are either stacked or grouped
  35660. if (chart.hasRendered) {
  35661. chart.series.forEach(function (otherSeries) {
  35662. if (otherSeries.type === series.type) {
  35663. otherSeries.isDirty = true;
  35664. }
  35665. });
  35666. }
  35667. Series.prototype.remove.apply(series, arguments);
  35668. }
  35669. });
  35670. /**
  35671. * A `column` series. If the [type](#series.column.type) option is
  35672. * not specified, it is inherited from [chart.type](#chart.type).
  35673. *
  35674. * @extends series,plotOptions.column
  35675. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  35676. * linecap, lineWidth, marker, connectEnds, step
  35677. * @product highcharts highstock
  35678. * @apioption series.column
  35679. */
  35680. /**
  35681. * An array of data points for the series. For the `column` series type,
  35682. * points can be given in the following ways:
  35683. *
  35684. * 1. An array of numerical values. In this case, the numerical values will be
  35685. * interpreted as `y` options. The `x` values will be automatically
  35686. * calculated, either starting at 0 and incremented by 1, or from
  35687. * `pointStart` and `pointInterval` given in the series options. If the axis
  35688. * has categories, these will be used. Example:
  35689. * ```js
  35690. * data: [0, 5, 3, 5]
  35691. * ```
  35692. *
  35693. * 2. An array of arrays with 2 values. In this case, the values correspond to
  35694. * `x,y`. If the first value is a string, it is applied as the name of the
  35695. * point, and the `x` value is inferred.
  35696. * ```js
  35697. * data: [
  35698. * [0, 6],
  35699. * [1, 2],
  35700. * [2, 6]
  35701. * ]
  35702. * ```
  35703. *
  35704. * 3. An array of objects with named values. The following snippet shows only a
  35705. * few settings, see the complete options set below. If the total number of
  35706. * data points exceeds the series'
  35707. * [turboThreshold](#series.column.turboThreshold), this option is not
  35708. * available.
  35709. * ```js
  35710. * data: [{
  35711. * x: 1,
  35712. * y: 9,
  35713. * name: "Point2",
  35714. * color: "#00FF00"
  35715. * }, {
  35716. * x: 1,
  35717. * y: 6,
  35718. * name: "Point1",
  35719. * color: "#FF00FF"
  35720. * }]
  35721. * ```
  35722. *
  35723. * @sample {highcharts} highcharts/chart/reflow-true/
  35724. * Numerical values
  35725. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  35726. * Arrays of numeric x and y
  35727. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  35728. * Arrays of datetime x and y
  35729. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  35730. * Arrays of point.name and y
  35731. * @sample {highcharts} highcharts/series/data-array-of-objects/
  35732. * Config objects
  35733. *
  35734. * @type {Array<number|Array<(number|string),number>|*>}
  35735. * @extends series.line.data
  35736. * @excluding marker
  35737. * @product highcharts highstock
  35738. * @apioption series.column.data
  35739. */
  35740. /**
  35741. * The color of the border surrounding the column or bar.
  35742. *
  35743. * In styled mode, the border stroke can be set with the `.highcharts-point`
  35744. * rule.
  35745. *
  35746. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  35747. * Dark gray border
  35748. *
  35749. * @type {Highcharts.ColorString}
  35750. * @product highcharts highstock
  35751. * @apioption series.column.data.borderColor
  35752. */
  35753. /**
  35754. * The width of the border surrounding the column or bar.
  35755. *
  35756. * In styled mode, the stroke width can be set with the `.highcharts-point`
  35757. * rule.
  35758. *
  35759. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  35760. * 2px black border
  35761. *
  35762. * @type {number}
  35763. * @product highcharts highstock
  35764. * @apioption series.column.data.borderWidth
  35765. */
  35766. /**
  35767. * @excluding halo, lineWidth, lineWidthPlus, marker
  35768. * @product highcharts highstock
  35769. * @apioption series.column.states.hover
  35770. */
  35771. /**
  35772. * @excluding halo, lineWidth, lineWidthPlus, marker
  35773. * @product highcharts highstock
  35774. * @apioption series.column.states.select
  35775. */
  35776. }(Highcharts));
  35777. (function (H) {
  35778. /* *
  35779. * (c) 2010-2019 Torstein Honsi
  35780. *
  35781. * License: www.highcharts.com/license
  35782. */
  35783. var seriesType = H.seriesType;
  35784. /**
  35785. * Bar series type.
  35786. *
  35787. * @private
  35788. * @class
  35789. * @name Highcharts.seriesTypes.bar
  35790. *
  35791. * @augments Highcharts.Series
  35792. */
  35793. seriesType('bar', 'column',
  35794. /**
  35795. * A bar series is a special type of column series where the columns are
  35796. * horizontal.
  35797. *
  35798. * @sample highcharts/demo/bar-basic/
  35799. * Bar chart
  35800. *
  35801. * @extends plotOptions.column
  35802. * @product highcharts
  35803. * @apioption plotOptions.bar
  35804. */
  35805. /**
  35806. * Alignment of the data label relative to the data point.
  35807. *
  35808. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  35809. * Data labels inside the bar
  35810. *
  35811. * @type {string}
  35812. * @default left
  35813. * @product highcharts
  35814. * @apioption plotOptions.bar.dataLabels.align
  35815. */
  35816. /**
  35817. * The x position of the data label relative to the data point.
  35818. *
  35819. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  35820. * Data labels inside the bar
  35821. *
  35822. * @type {number}
  35823. * @default 5
  35824. * @product highcharts
  35825. * @apioption plotOptions.bar.dataLabels.x
  35826. */
  35827. /**
  35828. * @ignore
  35829. */
  35830. null
  35831. , {
  35832. inverted: true
  35833. });
  35834. /**
  35835. * A `bar` series. If the [type](#series.bar.type) option is not specified,
  35836. * it is inherited from [chart.type](#chart.type).
  35837. *
  35838. * @extends series,plotOptions.bar
  35839. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  35840. * linecap, lineWidth, marker, connectEnds, step
  35841. * @product highcharts
  35842. * @apioption series.bar
  35843. */
  35844. /**
  35845. * An array of data points for the series. For the `bar` series type,
  35846. * points can be given in the following ways:
  35847. *
  35848. * 1. An array of numerical values. In this case, the numerical values will be
  35849. * interpreted as `y` options. The `x` values will be automatically
  35850. * calculated, either starting at 0 and incremented by 1, or from
  35851. * `pointStart` and `pointInterval` given in the series options. If the axis
  35852. * has categories, these will be used. Example:
  35853. * ```js
  35854. * data: [0, 5, 3, 5]
  35855. * ```
  35856. *
  35857. * 2. An array of arrays with 2 values. In this case, the values correspond to
  35858. * `x,y`. If the first value is a string, it is applied as the name of the
  35859. * point, and the `x` value is inferred.
  35860. * ```js
  35861. * data: [
  35862. * [0, 5],
  35863. * [1, 10],
  35864. * [2, 3]
  35865. * ]
  35866. * ```
  35867. *
  35868. * 3. An array of objects with named values. The following snippet shows only a
  35869. * few settings, see the complete options set below. If the total number of
  35870. * data points exceeds the series'
  35871. * [turboThreshold](#series.bar.turboThreshold), this option is not
  35872. * available.
  35873. * ```js
  35874. * data: [{
  35875. * x: 1,
  35876. * y: 1,
  35877. * name: "Point2",
  35878. * color: "#00FF00"
  35879. * }, {
  35880. * x: 1,
  35881. * y: 10,
  35882. * name: "Point1",
  35883. * color: "#FF00FF"
  35884. * }]
  35885. * ```
  35886. *
  35887. * @sample {highcharts} highcharts/chart/reflow-true/
  35888. * Numerical values
  35889. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  35890. * Arrays of numeric x and y
  35891. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  35892. * Arrays of datetime x and y
  35893. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  35894. * Arrays of point.name and y
  35895. * @sample {highcharts} highcharts/series/data-array-of-objects/
  35896. * Config objects
  35897. *
  35898. * @type {Array<number|Array<(number|string),number>|*>}
  35899. * @extends series.column.data
  35900. * @product highcharts
  35901. * @apioption series.bar.data
  35902. */
  35903. /**
  35904. * @excluding halo,lineWidth,lineWidthPlus,marker
  35905. * @product highcharts highstock
  35906. * @apioption series.bar.states.hover
  35907. */
  35908. /**
  35909. * @excluding halo,lineWidth,lineWidthPlus,marker
  35910. * @product highcharts highstock
  35911. * @apioption series.bar.states.select
  35912. */
  35913. }(Highcharts));
  35914. (function (H) {
  35915. /* *
  35916. * (c) 2010-2019 Torstein Honsi
  35917. *
  35918. * License: www.highcharts.com/license
  35919. */
  35920. var Series = H.Series,
  35921. seriesType = H.seriesType;
  35922. /**
  35923. * Scatter series type.
  35924. *
  35925. * @private
  35926. * @class
  35927. * @name Highcharts.seriesTypes.scatter
  35928. *
  35929. * @augments Highcharts.Series
  35930. */
  35931. seriesType(
  35932. 'scatter',
  35933. 'line',
  35934. /**
  35935. * A scatter plot uses cartesian coordinates to display values for two
  35936. * variables for a set of data.
  35937. *
  35938. * @sample {highcharts} highcharts/demo/scatter/
  35939. * Scatter plot
  35940. *
  35941. * @extends plotOptions.line
  35942. * @excluding pointPlacement, shadow, useOhlcData
  35943. * @product highcharts highstock
  35944. * @optionparent plotOptions.scatter
  35945. */
  35946. {
  35947. /**
  35948. * The width of the line connecting the data points.
  35949. *
  35950. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
  35951. * 0 by default
  35952. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
  35953. * 1px
  35954. *
  35955. * @product highcharts highstock
  35956. */
  35957. lineWidth: 0,
  35958. findNearestPointBy: 'xy',
  35959. /**
  35960. * Apply a jitter effect for the rendered markers. When plotting
  35961. * discrete values, a little random noise may help telling the points
  35962. * apart. The jitter setting applies a random displacement of up to `n`
  35963. * axis units in either direction. So for example on a horizontal X
  35964. * axis, setting the `jitter.x` to 0.24 will render the point in a
  35965. * random position between 0.24 units to the left and 0.24 units to the
  35966. * right of the true axis position. On a category axis, setting it to
  35967. * 0.5 will fill up the bin and make the data appear continuous.
  35968. *
  35969. * When rendered on top of a box plot or a column series, a jitter value
  35970. * of 0.24 will correspond to the underlying series' default
  35971. * [groupPadding](
  35972. * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
  35973. * and [pointPadding](
  35974. * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
  35975. * settings.
  35976. *
  35977. * @sample {highcharts} highcharts/series-scatter/jitter
  35978. * Jitter on a scatter plot
  35979. *
  35980. * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
  35981. * Jittered scatter plot on top of a box plot
  35982. *
  35983. * @product highcharts highstock
  35984. * @since 7.0.2
  35985. */
  35986. jitter: {
  35987. /**
  35988. * The maximal X offset for the random jitter effect.
  35989. */
  35990. x: 0,
  35991. /**
  35992. * The maximal Y offset for the random jitter effect.
  35993. */
  35994. y: 0
  35995. },
  35996. marker: {
  35997. enabled: true // Overrides auto-enabling in line series (#3647)
  35998. },
  35999. /**
  36000. * Sticky tracking of mouse events. When true, the `mouseOut` event
  36001. * on a series isn't triggered until the mouse moves over another
  36002. * series, or out of the plot area. When false, the `mouseOut` event on
  36003. * a series is triggered when the mouse leaves the area around the
  36004. * series' graph or markers. This also implies the tooltip. When
  36005. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  36006. * will be hidden when moving the mouse between series.
  36007. *
  36008. * @type {boolean}
  36009. * @default false
  36010. * @product highcharts highstock
  36011. * @apioption plotOptions.scatter.stickyTracking
  36012. */
  36013. /**
  36014. * A configuration object for the tooltip rendering of each single
  36015. * series. Properties are inherited from [tooltip](#tooltip).
  36016. * Overridable properties are `headerFormat`, `pointFormat`,
  36017. * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
  36018. * series, in a scatter plot the series.name by default shows in the
  36019. * headerFormat and point.x and point.y in the pointFormat.
  36020. *
  36021. * @product highcharts highstock
  36022. */
  36023. tooltip: {
  36024. headerFormat:
  36025. '<span style="color:{point.color}">\u25CF</span> ' +
  36026. '<span style="font-size: 10px"> {series.name}</span><br/>',
  36027. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
  36028. }
  36029. // Prototype members
  36030. }, {
  36031. sorted: false,
  36032. requireSorting: false,
  36033. noSharedTooltip: true,
  36034. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  36035. takeOrdinalPosition: false, // #2342
  36036. /**
  36037. * @private
  36038. * @function Highcharts.seriesTypes.scatter#drawGraph
  36039. */
  36040. drawGraph: function () {
  36041. if (this.options.lineWidth) {
  36042. Series.prototype.drawGraph.call(this);
  36043. }
  36044. },
  36045. // Optionally add the jitter effect
  36046. applyJitter: function () {
  36047. var series = this,
  36048. jitter = this.options.jitter,
  36049. len = this.points.length;
  36050. // Return a repeatable, pseudo-random number based on an integer
  36051. // seed
  36052. function unrandom(seed) {
  36053. var rand = Math.sin(seed) * 10000;
  36054. return rand - Math.floor(rand);
  36055. }
  36056. if (jitter) {
  36057. this.points.forEach(function (point, i) {
  36058. ['x', 'y'].forEach(function (dim, j) {
  36059. var axis,
  36060. plotProp = 'plot' + dim.toUpperCase(),
  36061. min,
  36062. max,
  36063. translatedJitter;
  36064. if (jitter[dim] && !point.isNull) {
  36065. axis = series[dim + 'Axis'];
  36066. translatedJitter = jitter[dim] * axis.transA;
  36067. if (axis && !axis.isLog) {
  36068. // Identify the outer bounds of the jitter range
  36069. min = Math.max(
  36070. 0,
  36071. point[plotProp] - translatedJitter
  36072. );
  36073. max = Math.min(
  36074. axis.len,
  36075. point[plotProp] + translatedJitter
  36076. );
  36077. // Find a random position within this range
  36078. point[plotProp] = min +
  36079. (max - min) * unrandom(i + j * len);
  36080. // Update clientX for the tooltip k-d-tree
  36081. if (dim === 'x') {
  36082. point.clientX = point.plotX;
  36083. }
  36084. }
  36085. }
  36086. });
  36087. });
  36088. }
  36089. }
  36090. }
  36091. );
  36092. H.addEvent(Series, 'afterTranslate', function () {
  36093. if (this.applyJitter) {
  36094. this.applyJitter();
  36095. }
  36096. });
  36097. /**
  36098. * A `scatter` series. If the [type](#series.scatter.type) option is
  36099. * not specified, it is inherited from [chart.type](#chart.type).
  36100. *
  36101. * @extends series,plotOptions.scatter
  36102. * @excluding dataParser, dataURL, useOhlcData
  36103. * @product highcharts highstock
  36104. * @apioption series.scatter
  36105. */
  36106. /**
  36107. * An array of data points for the series. For the `scatter` series
  36108. * type, points can be given in the following ways:
  36109. *
  36110. * 1. An array of numerical values. In this case, the numerical values will be
  36111. * interpreted as `y` options. The `x` values will be automatically
  36112. * calculated, either starting at 0 and incremented by 1, or from
  36113. * `pointStart` and `pointInterval` given in the series options. If the axis
  36114. * has categories, these will be used. Example:
  36115. * ```js
  36116. * data: [0, 5, 3, 5]
  36117. * ```
  36118. *
  36119. * 2. An array of arrays with 2 values. In this case, the values correspond to
  36120. * `x,y`. If the first value is a string, it is applied as the name of the
  36121. * point, and the `x` value is inferred.
  36122. * ```js
  36123. * data: [
  36124. * [0, 0],
  36125. * [1, 8],
  36126. * [2, 9]
  36127. * ]
  36128. * ```
  36129. *
  36130. * 3. An array of objects with named values. The following snippet shows only a
  36131. * few settings, see the complete options set below. If the total number of
  36132. * data points exceeds the series'
  36133. * [turboThreshold](#series.scatter.turboThreshold), this option is not
  36134. * available.
  36135. * ```js
  36136. * data: [{
  36137. * x: 1,
  36138. * y: 2,
  36139. * name: "Point2",
  36140. * color: "#00FF00"
  36141. * }, {
  36142. * x: 1,
  36143. * y: 4,
  36144. * name: "Point1",
  36145. * color: "#FF00FF"
  36146. * }]
  36147. * ```
  36148. *
  36149. * @sample {highcharts} highcharts/chart/reflow-true/
  36150. * Numerical values
  36151. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  36152. * Arrays of numeric x and y
  36153. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  36154. * Arrays of datetime x and y
  36155. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  36156. * Arrays of point.name and y
  36157. * @sample {highcharts} highcharts/series/data-array-of-objects/
  36158. * Config objects
  36159. *
  36160. * @type {Array<number|Array<(number|string),number>|*>}
  36161. * @extends series.line.data
  36162. * @product highcharts highstock
  36163. * @apioption series.scatter.data
  36164. */
  36165. }(Highcharts));
  36166. (function (H) {
  36167. /* *
  36168. * (c) 2010-2019 Torstein Honsi
  36169. *
  36170. * License: www.highcharts.com/license
  36171. */
  36172. /**
  36173. * @private
  36174. * @typedef Highcharts.RadianAngles
  36175. *
  36176. * @property {number} start
  36177. *
  36178. * @property {number} end
  36179. */
  36180. var deg2rad = H.deg2rad,
  36181. isNumber = H.isNumber,
  36182. pick = H.pick,
  36183. relativeLength = H.relativeLength;
  36184. /**
  36185. * @private
  36186. * @mixin Highcharts.CenteredSeriesMixin
  36187. */
  36188. H.CenteredSeriesMixin = {
  36189. /**
  36190. * Get the center of the pie based on the size and center options relative
  36191. * to the plot area. Borrowed by the polar and gauge series types.
  36192. *
  36193. * @private
  36194. * @function Highcharts.CenteredSeriesMixin.getCenter
  36195. *
  36196. * @return {Array<number>}
  36197. */
  36198. getCenter: function () {
  36199. var options = this.options,
  36200. chart = this.chart,
  36201. slicingRoom = 2 * (options.slicedOffset || 0),
  36202. handleSlicingRoom,
  36203. plotWidth = chart.plotWidth - 2 * slicingRoom,
  36204. plotHeight = chart.plotHeight - 2 * slicingRoom,
  36205. centerOption = options.center,
  36206. positions = [
  36207. pick(centerOption[0], '50%'),
  36208. pick(centerOption[1], '50%'),
  36209. options.size || '100%',
  36210. options.innerSize || 0
  36211. ],
  36212. smallestSize = Math.min(plotWidth, plotHeight),
  36213. i,
  36214. value;
  36215. for (i = 0; i < 4; ++i) {
  36216. value = positions[i];
  36217. handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
  36218. // i == 0: centerX, relative to width
  36219. // i == 1: centerY, relative to height
  36220. // i == 2: size, relative to smallestSize
  36221. // i == 3: innerSize, relative to size
  36222. positions[i] = relativeLength(
  36223. value,
  36224. [plotWidth, plotHeight, smallestSize, positions[2]][i]
  36225. ) + (handleSlicingRoom ? slicingRoom : 0);
  36226. }
  36227. // innerSize cannot be larger than size (#3632)
  36228. if (positions[3] > positions[2]) {
  36229. positions[3] = positions[2];
  36230. }
  36231. return positions;
  36232. },
  36233. /**
  36234. * getStartAndEndRadians - Calculates start and end angles in radians.
  36235. * Used in series types such as pie and sunburst.
  36236. *
  36237. * @private
  36238. * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
  36239. *
  36240. * @param {number} start
  36241. * Start angle in degrees.
  36242. *
  36243. * @param {number} end
  36244. * Start angle in degrees.
  36245. *
  36246. * @return {Highcharts.RadianAngles}
  36247. * Returns an object containing start and end angles as radians.
  36248. */
  36249. getStartAndEndRadians: function getStartAndEndRadians(start, end) {
  36250. var startAngle = isNumber(start) ? start : 0, // must be a number
  36251. endAngle = (
  36252. (
  36253. isNumber(end) && // must be a number
  36254. end > startAngle && // must be larger than the start angle
  36255. // difference must be less than 360 degrees
  36256. (end - startAngle) < 360
  36257. ) ?
  36258. end :
  36259. startAngle + 360
  36260. ),
  36261. correction = -90;
  36262. return {
  36263. start: deg2rad * (startAngle + correction),
  36264. end: deg2rad * (endAngle + correction)
  36265. };
  36266. }
  36267. };
  36268. }(Highcharts));
  36269. (function (H) {
  36270. /* *
  36271. * (c) 2010-2019 Torstein Honsi
  36272. *
  36273. * License: www.highcharts.com/license
  36274. */
  36275. var addEvent = H.addEvent,
  36276. CenteredSeriesMixin = H.CenteredSeriesMixin,
  36277. defined = H.defined,
  36278. extend = H.extend,
  36279. getStartAndEndRadians = CenteredSeriesMixin.getStartAndEndRadians,
  36280. LegendSymbolMixin = H.LegendSymbolMixin,
  36281. noop = H.noop,
  36282. pick = H.pick,
  36283. Point = H.Point,
  36284. Series = H.Series,
  36285. seriesType = H.seriesType,
  36286. seriesTypes = H.seriesTypes,
  36287. setAnimation = H.setAnimation;
  36288. /**
  36289. * Pie series type.
  36290. *
  36291. * @private
  36292. * @class
  36293. * @name Highcharts.seriesTypes.pie
  36294. *
  36295. * @augments Highcharts.Series
  36296. */
  36297. seriesType('pie', 'line',
  36298. /**
  36299. * A pie chart is a circular graphic which is divided into slices to
  36300. * illustrate numerical proportion.
  36301. *
  36302. * @sample highcharts/demo/pie-basic/
  36303. * Pie chart
  36304. *
  36305. * @extends plotOptions.line
  36306. * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
  36307. * cropThreshold, dashStyle, findNearestPointBy,
  36308. * getExtremesFromAll, lineWidth, marker, negativeColor,
  36309. * pointInterval, pointIntervalUnit, pointPlacement,
  36310. * pointStart, softThreshold, stacking, step, threshold,
  36311. * turboThreshold, zoneAxis, zones
  36312. * @product highcharts
  36313. * @optionparent plotOptions.pie
  36314. */
  36315. {
  36316. /**
  36317. * @excluding legendItemClick
  36318. * @apioption plotOptions.pie.events
  36319. */
  36320. /**
  36321. * Fires when the checkbox next to the point name in the legend is
  36322. * clicked. One parameter, event, is passed to the function. The state
  36323. * of the checkbox is found by event.checked. The checked item is found
  36324. * by event.item. Return false to prevent the default action which is to
  36325. * toggle the select state of the series.
  36326. *
  36327. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  36328. * Alert checkbox status
  36329. *
  36330. * @type {Function}
  36331. * @since 1.2.0
  36332. * @product highcharts
  36333. * @context Highcharts.Point
  36334. * @apioption plotOptions.pie.events.checkboxClick
  36335. */
  36336. /**
  36337. * The center of the pie chart relative to the plot area. Can be
  36338. * percentages or pixel values. The default behaviour (as of 3.0) is to
  36339. * center the pie so that all slices and data labels are within the plot
  36340. * area. As a consequence, the pie may actually jump around in a chart
  36341. * with dynamic values, as the data labels move. In that case, the
  36342. * center should be explicitly set, for example to `["50%", "50%"]`.
  36343. *
  36344. * @sample {highcharts} highcharts/plotoptions/pie-center/
  36345. * Centered at 100, 100
  36346. *
  36347. * @type {Array<number|string|null>}
  36348. * @default [null, null]
  36349. * @product highcharts
  36350. */
  36351. center: [null, null],
  36352. /**
  36353. * @product highcharts
  36354. */
  36355. clip: false,
  36356. /** @ignore */
  36357. colorByPoint: true, // always true for pies
  36358. /**
  36359. * A series specific or series type specific color set to use instead
  36360. * of the global [colors](#colors).
  36361. *
  36362. * @sample {highcharts} highcharts/demo/pie-monochrome/
  36363. * Set default colors for all pies
  36364. *
  36365. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  36366. * @since 3.0
  36367. * @product highcharts
  36368. * @apioption plotOptions.pie.colors
  36369. */
  36370. /**
  36371. * @extends plotOptions.series.dataLabels
  36372. * @excluding align, allowOverlap, staggerLines, step
  36373. * @product highcharts
  36374. */
  36375. dataLabels: {
  36376. allowOverlap: true,
  36377. /**
  36378. * The color of the line connecting the data label to the pie slice.
  36379. * The default color is the same as the point's color.
  36380. *
  36381. * In styled mode, the connector stroke is given in the
  36382. * `.highcharts-data-label-connector` class.
  36383. *
  36384. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
  36385. * Blue connectors
  36386. * @sample {highcharts} highcharts/css/pie-point/
  36387. * Styled connectors
  36388. *
  36389. * @type {Highcharts.ColorString}
  36390. * @since 2.1
  36391. * @product highcharts
  36392. * @apioption plotOptions.pie.dataLabels.connectorColor
  36393. */
  36394. /**
  36395. * The distance from the data label to the connector. Note that data
  36396. * labels also have a default `padding`, so in order for the
  36397. * connector to touch the text, the `padding` must also be 0.
  36398. *
  36399. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
  36400. * No padding
  36401. *
  36402. * @since 2.1
  36403. * @product highcharts
  36404. * @apioption plotOptions.pie.dataLabels.connectorPadding
  36405. */
  36406. connectorPadding: 5,
  36407. /**
  36408. * The width of the line connecting the data label to the pie slice.
  36409. *
  36410. *
  36411. * In styled mode, the connector stroke width is given in the
  36412. * `.highcharts-data-label-connector` class.
  36413. *
  36414. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
  36415. * Disable the connector
  36416. * @sample {highcharts} highcharts/css/pie-point/
  36417. * Styled connectors
  36418. *
  36419. * @type {number}
  36420. * @default 1
  36421. * @since 2.1
  36422. * @product highcharts
  36423. * @apioption plotOptions.pie.dataLabels.connectorWidth
  36424. */
  36425. /**
  36426. * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow
  36427. * Long labels truncated with an ellipsis
  36428. * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow-wrap
  36429. * Long labels are wrapped
  36430. *
  36431. * @type {Highcharts.CSSObject}
  36432. * @apioption plotOptions.pie.dataLabels.style
  36433. */
  36434. /**
  36435. * The distance of the data label from the pie's edge. Negative
  36436. * numbers put the data label on top of the pie slices. Connectors
  36437. * are only shown for data labels outside the pie.
  36438. *
  36439. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
  36440. * Data labels on top of the pie
  36441. *
  36442. * @since 2.1
  36443. * @product highcharts
  36444. */
  36445. distance: 30,
  36446. /**
  36447. * Enable or disable the data labels.
  36448. *
  36449. * @since 2.1
  36450. * @product highcharts
  36451. */
  36452. enabled: true,
  36453. /**
  36454. * @type {Highcharts.FormatterCallbackFunction<Highcharts.SeriesDataLabelsFormatterContextObject>}
  36455. * @default function () { return this.point.name; }
  36456. * @apioption plotOptions.pie.dataLabels.formatter
  36457. */
  36458. formatter: function () { // #2945
  36459. return this.point.isNull ? undefined : this.point.name;
  36460. },
  36461. /**
  36462. * Whether to render the connector as a soft arc or a line with
  36463. * sharp break. Works only if `connectorShape` equals to
  36464. * `fixedOffset`.
  36465. *
  36466. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-true/
  36467. * Soft
  36468. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-false/
  36469. * Non soft
  36470. *
  36471. * @type {number}
  36472. * @since 2.1.7
  36473. * @product highcharts
  36474. * @apioption plotOptions.pie.dataLabels.softConnector
  36475. */
  36476. softConnector: true,
  36477. /**
  36478. * Alignment method for data labels. Possible values are:
  36479. * `'toPlotEdges'` (each label touches the nearest vertical edge of
  36480. * the plot area) or `'connectors'` (connectors have the same x
  36481. * position and the widest label of each half (left & right) touches
  36482. * the nearest vertical edge of the plot area).
  36483. *
  36484. * @type {String}
  36485. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/ alignTo: connectors
  36486. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/ alignTo: plotEdges
  36487. * @since 7.0.0
  36488. * @default undefined
  36489. * @product highcharts
  36490. * @apioption plotOptions.pie.dataLabels.alignTo
  36491. */
  36492. x: 0,
  36493. /**
  36494. * Specifies the method that is used to generate the connector path.
  36495. * Highcharts provides 3 built-in connector shapes: `'fixedOffset'`
  36496. * (default), `'straight'` and `'crookedLine'`. Using
  36497. * `'crookedLine'` has the most sense (in most of the cases) when
  36498. * `'alignTo'` is set.
  36499. *
  36500. * Users can provide their own method by passing a function instead
  36501. * of a String. 3 arguments are passed to the callback:
  36502. *
  36503. * - Object that holds the information about the coordinates of the
  36504. * label (`x` & `y` properties) and how the label is located in
  36505. * relation to the pie (`alignment` property). `alignment` can by
  36506. * one of the following:
  36507. * `'left'` (pie on the left side of the data label),
  36508. * `'right'` (pie on the right side of the data label) or
  36509. * `'center'` (data label overlaps the pie).
  36510. * - Object that holds the information about the position of the
  36511. * connector. Its `touchingSliceAt` porperty tells the position
  36512. * of the place where the connector touches the slice.
  36513. * - Data label options
  36514. *
  36515. * The function has to return an SVG path definition in array form
  36516. * (see the example).
  36517. *
  36518. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-string/
  36519. * connectorShape is a String
  36520. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-function/
  36521. * connectorShape is a function
  36522. *
  36523. * @type {string|Function}
  36524. * @since 7.0.0
  36525. * @product highcharts
  36526. * @apioption plotOptions.pie.dataLabels.connectorShape
  36527. */
  36528. connectorShape: 'fixedOffset',
  36529. /**
  36530. * Works only if `connectorShape` is `'crookedLine'`. It defines how
  36531. * far from the vertical plot edge the coonnector path should be
  36532. * crooked.
  36533. *
  36534. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
  36535. * crookDistance set to 90%
  36536. *
  36537. * @type {string}
  36538. * @since 7.0.0
  36539. * @product highcharts
  36540. * @apioption plotOptions.pie.dataLabels.crookDistance
  36541. */
  36542. crookDistance: '70%'
  36543. },
  36544. /**
  36545. * The end angle of the pie in degrees where 0 is top and 90 is right.
  36546. * Defaults to `startAngle` plus 360.
  36547. *
  36548. * @sample {highcharts} highcharts/demo/pie-semi-circle/
  36549. * Semi-circle donut
  36550. *
  36551. * @type {number}
  36552. * @since 1.3.6
  36553. * @product highcharts
  36554. * @apioption plotOptions.pie.endAngle
  36555. */
  36556. /**
  36557. * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
  36558. * this option tells whether the series shall be redrawn as if the
  36559. * hidden point were `null`.
  36560. *
  36561. * The default value changed from `false` to `true` with Highcharts
  36562. * 3.0.
  36563. *
  36564. * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
  36565. * True, the hiddden point is ignored
  36566. *
  36567. * @since 2.3.0
  36568. * @product highcharts
  36569. */
  36570. ignoreHiddenPoint: true,
  36571. /**
  36572. * The size of the inner diameter for the pie. A size greater than 0
  36573. * renders a donut chart. Can be a percentage or pixel value.
  36574. * Percentages are relative to the pie size. Pixel values are given as
  36575. * integers.
  36576. *
  36577. *
  36578. * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
  36579. * area, not the pie size.
  36580. *
  36581. * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
  36582. * 80px inner size
  36583. * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
  36584. * 50% of the plot area
  36585. * @sample {highcharts} highcharts/demo/3d-pie-donut/
  36586. * 3D donut
  36587. *
  36588. * @type {number|string}
  36589. * @default 0
  36590. * @since 2.0
  36591. * @product highcharts
  36592. * @apioption plotOptions.pie.innerSize
  36593. */
  36594. /** @ignore-option */
  36595. legendType: 'point',
  36596. /** @ignore-option */
  36597. marker: null, // point options are specified in the base options
  36598. /**
  36599. * The minimum size for a pie in response to auto margins. The pie will
  36600. * try to shrink to make room for data labels in side the plot area,
  36601. * but only to this size.
  36602. *
  36603. * @type {number}
  36604. * @default 80
  36605. * @since 3.0
  36606. * @product highcharts
  36607. * @apioption plotOptions.pie.minSize
  36608. */
  36609. /**
  36610. * The diameter of the pie relative to the plot area. Can be a
  36611. * percentage or pixel value. Pixel values are given as integers. The
  36612. * default behaviour (as of 3.0) is to scale to the plot area and give
  36613. * room for data labels within the plot area.
  36614. * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
  36615. * default size calculation. As a consequence, the size of the pie may
  36616. * vary when points are updated and data labels more around. In that
  36617. * case it is best to set a fixed value, for example `"75%"`.
  36618. *
  36619. * @sample {highcharts} highcharts/plotoptions/pie-size/
  36620. * Smaller pie
  36621. *
  36622. * @type {number|string|null}
  36623. * @product highcharts
  36624. */
  36625. size: null,
  36626. /**
  36627. * Whether to display this particular series or series type in the
  36628. * legend. Since 2.1, pies are not shown in the legend by default.
  36629. *
  36630. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  36631. * One series in the legend, one hidden
  36632. *
  36633. * @product highcharts
  36634. */
  36635. showInLegend: false,
  36636. /**
  36637. * If a point is sliced, moved out from the center, how many pixels
  36638. * should it be moved?.
  36639. *
  36640. * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
  36641. * 20px offset
  36642. *
  36643. * @product highcharts
  36644. */
  36645. slicedOffset: 10,
  36646. /**
  36647. * The start angle of the pie slices in degrees where 0 is top and 90
  36648. * right.
  36649. *
  36650. * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
  36651. * Start from right
  36652. *
  36653. * @type {number}
  36654. * @default 0
  36655. * @since 2.3.4
  36656. * @product highcharts
  36657. * @apioption plotOptions.pie.startAngle
  36658. */
  36659. /**
  36660. * Sticky tracking of mouse events. When true, the `mouseOut` event
  36661. * on a series isn't triggered until the mouse moves over another
  36662. * series, or out of the plot area. When false, the `mouseOut` event on
  36663. * a series is triggered when the mouse leaves the area around the
  36664. * series' graph or markers. This also implies the tooltip. When
  36665. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  36666. * will be hidden when moving the mouse between series.
  36667. *
  36668. * @product highcharts
  36669. */
  36670. stickyTracking: false,
  36671. tooltip: {
  36672. followPointer: true
  36673. },
  36674. /**
  36675. * The color of the border surrounding each slice. When `null`, the
  36676. * border takes the same color as the slice fill. This can be used
  36677. * together with a `borderWidth` to fill drawing gaps created by
  36678. * antialiazing artefacts in borderless pies.
  36679. *
  36680. * In styled mode, the border stroke is given in the `.highcharts-point`
  36681. * class.
  36682. *
  36683. * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
  36684. * Black border
  36685. *
  36686. * @type {Highcharts.ColorString}
  36687. * @default #ffffff
  36688. * @product highcharts
  36689. */
  36690. borderColor: '#ffffff',
  36691. /**
  36692. * The width of the border surrounding each slice.
  36693. *
  36694. * When setting the border width to 0, there may be small gaps between
  36695. * the slices due to SVG antialiasing artefacts. To work around this,
  36696. * keep the border width at 0.5 or 1, but set the `borderColor` to
  36697. * `null` instead.
  36698. *
  36699. * In styled mode, the border stroke width is given in the
  36700. * `.highcharts-point` class.
  36701. *
  36702. * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
  36703. * 3px border
  36704. *
  36705. * @product highcharts
  36706. */
  36707. borderWidth: 1,
  36708. states: {
  36709. /**
  36710. * @extends plotOptions.series.states.hover
  36711. * @excluding marker, lineWidth, lineWidthPlus
  36712. * @product highcharts
  36713. */
  36714. hover: {
  36715. /**
  36716. * How much to brighten the point on interaction. Requires the main
  36717. * color to be defined in hex or rgb(a) format.
  36718. *
  36719. * In styled mode, the hover brightness is by default replaced
  36720. * by a fill-opacity given in the `.highcharts-point-hover` class.
  36721. *
  36722. * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
  36723. * Brightened by 0.5
  36724. *
  36725. * @product highcharts
  36726. */
  36727. brightness: 0.1
  36728. }
  36729. }
  36730. }, /** @lends seriesTypes.pie.prototype */ {
  36731. isCartesian: false,
  36732. requireSorting: false,
  36733. directTouch: true,
  36734. noSharedTooltip: true,
  36735. trackerGroups: ['group', 'dataLabelsGroup'],
  36736. axisTypes: [],
  36737. pointAttribs: seriesTypes.column.prototype.pointAttribs,
  36738. /**
  36739. * Animate the pies in
  36740. *
  36741. * @private
  36742. * @function Highcharts.seriesTypes.pie#animate
  36743. *
  36744. * @param {boolean} [init=false]
  36745. */
  36746. animate: function (init) {
  36747. var series = this,
  36748. points = series.points,
  36749. startAngleRad = series.startAngleRad;
  36750. if (!init) {
  36751. points.forEach(function (point) {
  36752. var graphic = point.graphic,
  36753. args = point.shapeArgs;
  36754. if (graphic) {
  36755. // start values
  36756. graphic.attr({
  36757. // animate from inner radius (#779)
  36758. r: point.startR || (series.center[3] / 2),
  36759. start: startAngleRad,
  36760. end: startAngleRad
  36761. });
  36762. // animate
  36763. graphic.animate({
  36764. r: args.r,
  36765. start: args.start,
  36766. end: args.end
  36767. }, series.options.animation);
  36768. }
  36769. });
  36770. // delete this function to allow it only once
  36771. series.animate = null;
  36772. }
  36773. },
  36774. /**
  36775. * Recompute total chart sum and update percentages of points.
  36776. *
  36777. * @private
  36778. * @function Highcharts.seriesTypes.pie#updateTotals
  36779. */
  36780. updateTotals: function () {
  36781. var i,
  36782. total = 0,
  36783. points = this.points,
  36784. len = points.length,
  36785. point,
  36786. ignoreHiddenPoint = this.options.ignoreHiddenPoint;
  36787. // Get the total sum
  36788. for (i = 0; i < len; i++) {
  36789. point = points[i];
  36790. total += (ignoreHiddenPoint && !point.visible) ?
  36791. 0 :
  36792. point.isNull ? 0 : point.y;
  36793. }
  36794. this.total = total;
  36795. // Set each point's properties
  36796. for (i = 0; i < len; i++) {
  36797. point = points[i];
  36798. point.percentage =
  36799. (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
  36800. point.y / total * 100 :
  36801. 0;
  36802. point.total = total;
  36803. }
  36804. },
  36805. /**
  36806. * Extend the generatePoints method by adding total and percentage
  36807. * properties to each point
  36808. *
  36809. * @private
  36810. * @function Highcharts.seriesTypes.pie#generatePoints
  36811. */
  36812. generatePoints: function () {
  36813. Series.prototype.generatePoints.call(this);
  36814. this.updateTotals();
  36815. },
  36816. // Utility for getting the x value from a given y, used for
  36817. // anticollision logic in data labels. Added point for using specific
  36818. // points' label distance.
  36819. getX: function (y, left, point) {
  36820. var center = this.center,
  36821. // Variable pie has individual radius
  36822. radius = this.radii ? this.radii[point.index] : center[2] / 2,
  36823. angle,
  36824. x;
  36825. angle = Math.asin(
  36826. Math.max(
  36827. Math.min(
  36828. (
  36829. (y - center[1]) /
  36830. (radius + point.labelDistance)
  36831. ),
  36832. 1
  36833. ),
  36834. -1
  36835. )
  36836. );
  36837. x = center[0] +
  36838. (left ? -1 : 1) *
  36839. (Math.cos(angle) * (radius + point.labelDistance)) +
  36840. (
  36841. point.labelDistance > 0 ?
  36842. (left ? -1 : 1) * this.options.dataLabels.padding :
  36843. 0
  36844. );
  36845. return x;
  36846. },
  36847. /**
  36848. * Do translation for pie slices
  36849. *
  36850. * @private
  36851. * @function Highcharts.seriesTypes.pie#translate
  36852. *
  36853. * @param {Array<number>} positions
  36854. */
  36855. translate: function (positions) {
  36856. this.generatePoints();
  36857. var series = this,
  36858. cumulative = 0,
  36859. precision = 1000, // issue #172
  36860. options = series.options,
  36861. slicedOffset = options.slicedOffset,
  36862. connectorOffset = slicedOffset + (options.borderWidth || 0),
  36863. finalConnectorOffset,
  36864. start,
  36865. end,
  36866. angle,
  36867. radians = getStartAndEndRadians(
  36868. options.startAngle,
  36869. options.endAngle
  36870. ),
  36871. startAngleRad = series.startAngleRad = radians.start,
  36872. endAngleRad = series.endAngleRad = radians.end,
  36873. circ = endAngleRad - startAngleRad, // 2 * Math.PI,
  36874. points = series.points,
  36875. // the x component of the radius vector for a given point
  36876. radiusX,
  36877. radiusY,
  36878. labelDistance = options.dataLabels.distance,
  36879. ignoreHiddenPoint = options.ignoreHiddenPoint,
  36880. i,
  36881. len = points.length,
  36882. point;
  36883. // Get positions - either an integer or a percentage string must be
  36884. // given. If positions are passed as a parameter, we're in a
  36885. // recursive loop for adjusting space for data labels.
  36886. if (!positions) {
  36887. series.center = positions = series.getCenter();
  36888. }
  36889. // Calculate the geometry for each point
  36890. for (i = 0; i < len; i++) {
  36891. point = points[i];
  36892. // Used for distance calculation for specific point.
  36893. point.labelDistance = pick(
  36894. (
  36895. point.options.dataLabels &&
  36896. point.options.dataLabels.distance
  36897. ),
  36898. labelDistance
  36899. );
  36900. // Saved for later dataLabels distance calculation.
  36901. series.maxLabelDistance = Math.max(
  36902. series.maxLabelDistance || 0,
  36903. point.labelDistance
  36904. );
  36905. // set start and end angle
  36906. start = startAngleRad + (cumulative * circ);
  36907. if (!ignoreHiddenPoint || point.visible) {
  36908. cumulative += point.percentage / 100;
  36909. }
  36910. end = startAngleRad + (cumulative * circ);
  36911. // set the shape
  36912. point.shapeType = 'arc';
  36913. point.shapeArgs = {
  36914. x: positions[0],
  36915. y: positions[1],
  36916. r: positions[2] / 2,
  36917. innerR: positions[3] / 2,
  36918. start: Math.round(start * precision) / precision,
  36919. end: Math.round(end * precision) / precision
  36920. };
  36921. // The angle must stay within -90 and 270 (#2645)
  36922. angle = (end + start) / 2;
  36923. if (angle > 1.5 * Math.PI) {
  36924. angle -= 2 * Math.PI;
  36925. } else if (angle < -Math.PI / 2) {
  36926. angle += 2 * Math.PI;
  36927. }
  36928. // Center for the sliced out slice
  36929. point.slicedTranslation = {
  36930. translateX: Math.round(Math.cos(angle) * slicedOffset),
  36931. translateY: Math.round(Math.sin(angle) * slicedOffset)
  36932. };
  36933. // set the anchor point for tooltips
  36934. radiusX = Math.cos(angle) * positions[2] / 2;
  36935. radiusY = Math.sin(angle) * positions[2] / 2;
  36936. point.tooltipPos = [
  36937. positions[0] + radiusX * 0.7,
  36938. positions[1] + radiusY * 0.7
  36939. ];
  36940. point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
  36941. 1 :
  36942. 0;
  36943. point.angle = angle;
  36944. // Set the anchor point for data labels. Use point.labelDistance
  36945. // instead of labelDistance // #1174
  36946. // finalConnectorOffset - not override connectorOffset value.
  36947. finalConnectorOffset = Math.min(
  36948. connectorOffset,
  36949. point.labelDistance / 5
  36950. ); // #1678
  36951. point.labelPosition = {
  36952. natural: {
  36953. // initial position of the data label - it's utilized for
  36954. // finding the final position for the label
  36955. x: positions[0] + radiusX + Math.cos(angle) *
  36956. point.labelDistance,
  36957. y: positions[1] + radiusY + Math.sin(angle) *
  36958. point.labelDistance
  36959. },
  36960. 'final': {
  36961. // used for generating connector path -
  36962. // initialized later in drawDataLabels function
  36963. // x: undefined,
  36964. // y: undefined
  36965. },
  36966. // left - pie on the left side of the data label
  36967. // right - pie on the right side of the data label
  36968. // center - data label overlaps the pie
  36969. alignment: point.labelDistance < 0 ?
  36970. 'center' : point.half ? 'right' : 'left',
  36971. connectorPosition: {
  36972. breakAt: { // used in connectorShapes.fixedOffset
  36973. x: positions[0] + radiusX + Math.cos(angle) *
  36974. finalConnectorOffset,
  36975. y: positions[1] + radiusY + Math.sin(angle) *
  36976. finalConnectorOffset
  36977. },
  36978. touchingSliceAt: { // middle of the arc
  36979. x: positions[0] + radiusX,
  36980. y: positions[1] + radiusY
  36981. }
  36982. }
  36983. };
  36984. }
  36985. },
  36986. /**
  36987. * @private
  36988. * @deprecated
  36989. * @name Highcharts.seriesTypes.pie#drawGraph
  36990. * @type {null}
  36991. */
  36992. drawGraph: null,
  36993. /**
  36994. * Draw the data points
  36995. *
  36996. * @private
  36997. * @function Highcharts.seriesTypes.pie#drawPoints
  36998. */
  36999. drawPoints: function () {
  37000. var series = this,
  37001. chart = series.chart,
  37002. renderer = chart.renderer,
  37003. groupTranslation,
  37004. graphic,
  37005. pointAttr,
  37006. shapeArgs,
  37007. shadow = series.options.shadow;
  37008. if (shadow && !series.shadowGroup && !chart.styledMode) {
  37009. series.shadowGroup = renderer.g('shadow')
  37010. .add(series.group);
  37011. }
  37012. // draw the slices
  37013. series.points.forEach(function (point) {
  37014. graphic = point.graphic;
  37015. if (!point.isNull) {
  37016. shapeArgs = point.shapeArgs;
  37017. // If the point is sliced, use special translation, else use
  37018. // plot area traslation
  37019. groupTranslation = point.getTranslate();
  37020. if (!chart.styledMode) {
  37021. // Put the shadow behind all points
  37022. var shadowGroup = point.shadowGroup;
  37023. if (shadow && !shadowGroup) {
  37024. shadowGroup = point.shadowGroup = renderer
  37025. .g('shadow')
  37026. .add(series.shadowGroup);
  37027. }
  37028. if (shadowGroup) {
  37029. shadowGroup.attr(groupTranslation);
  37030. }
  37031. pointAttr = series.pointAttribs(
  37032. point,
  37033. point.selected && 'select'
  37034. );
  37035. }
  37036. // Draw the slice
  37037. if (graphic) {
  37038. graphic
  37039. .setRadialReference(series.center);
  37040. if (!chart.styledMode) {
  37041. graphic.attr(pointAttr);
  37042. }
  37043. graphic.animate(extend(shapeArgs, groupTranslation));
  37044. } else {
  37045. point.graphic = graphic = renderer[point.shapeType](
  37046. shapeArgs
  37047. )
  37048. .setRadialReference(series.center)
  37049. .attr(groupTranslation)
  37050. .add(series.group);
  37051. if (!chart.styledMode) {
  37052. graphic
  37053. .attr(pointAttr)
  37054. .attr({ 'stroke-linejoin': 'round' })
  37055. .shadow(shadow, shadowGroup);
  37056. }
  37057. }
  37058. graphic.attr({
  37059. visibility: point.visible ? 'inherit' : 'hidden'
  37060. });
  37061. graphic.addClass(point.getClassName());
  37062. } else if (graphic) {
  37063. point.graphic = graphic.destroy();
  37064. }
  37065. });
  37066. },
  37067. /**
  37068. * @private
  37069. * @deprecated
  37070. * @function Highcharts.seriesTypes.pie#searchPoint
  37071. */
  37072. searchPoint: noop,
  37073. /**
  37074. * Utility for sorting data labels
  37075. *
  37076. * @private
  37077. * @function Highcharts.seriesTypes.pie#sortByAngle
  37078. *
  37079. * @param {Array<Highcharts.Point>} points
  37080. *
  37081. * @param {number} sign
  37082. */
  37083. sortByAngle: function (points, sign) {
  37084. points.sort(function (a, b) {
  37085. return a.angle !== undefined && (b.angle - a.angle) * sign;
  37086. });
  37087. },
  37088. /**
  37089. * Use a simple symbol from LegendSymbolMixin.
  37090. *
  37091. * @private
  37092. * @borrows Highcharts.LegendSymbolMixin.drawRectangle as Highcharts.seriesTypes.pie#drawLegendSymbol
  37093. */
  37094. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  37095. /**
  37096. * Use the getCenter method from drawLegendSymbol.
  37097. *
  37098. * @private
  37099. * @borrows Highcharts.CenteredSeriesMixin.getCenter as Highcharts.seriesTypes.pie#getCenter
  37100. */
  37101. getCenter: CenteredSeriesMixin.getCenter,
  37102. /**
  37103. * Pies don't have point marker symbols.
  37104. *
  37105. * @deprecated
  37106. * @private
  37107. * @function Highcharts.seriesTypes.pie#getSymbol
  37108. */
  37109. getSymbol: noop
  37110. }, /** @lends seriesTypes.pie.prototype.pointClass.prototype */ {
  37111. /**
  37112. * Initiate the pie slice
  37113. *
  37114. * @private
  37115. * @function Highcharts.seriesTypes.pie#pointClass#init
  37116. *
  37117. * @return {Highcharts.Point}
  37118. */
  37119. init: function () {
  37120. Point.prototype.init.apply(this, arguments);
  37121. var point = this,
  37122. toggleSlice;
  37123. point.name = pick(point.name, 'Slice');
  37124. // add event listener for select
  37125. toggleSlice = function (e) {
  37126. point.slice(e.type === 'select');
  37127. };
  37128. addEvent(point, 'select', toggleSlice);
  37129. addEvent(point, 'unselect', toggleSlice);
  37130. return point;
  37131. },
  37132. /**
  37133. * Negative points are not valid (#1530, #3623, #5322)
  37134. *
  37135. * @private
  37136. * @function Highcharts.seriesTypes.pie#pointClass#isValid
  37137. *
  37138. * @return {boolean}
  37139. */
  37140. isValid: function () {
  37141. return H.isNumber(this.y, true) && this.y >= 0;
  37142. },
  37143. /**
  37144. * Toggle the visibility of the pie slice
  37145. *
  37146. * @private
  37147. * @function Highcharts.seriesTypes.pie#pointClass#setVisible
  37148. *
  37149. * @param {boolean} vis
  37150. * Whether to show the slice or not. If undefined, the visibility is
  37151. * toggled.
  37152. *
  37153. * @param {boolean} [redraw=false]
  37154. */
  37155. setVisible: function (vis, redraw) {
  37156. var point = this,
  37157. series = point.series,
  37158. chart = series.chart,
  37159. ignoreHiddenPoint = series.options.ignoreHiddenPoint;
  37160. redraw = pick(redraw, ignoreHiddenPoint);
  37161. if (vis !== point.visible) {
  37162. // If called without an argument, toggle visibility
  37163. point.visible = point.options.visible = vis =
  37164. vis === undefined ? !point.visible : vis;
  37165. // update userOptions.data
  37166. series.options.data[series.data.indexOf(point)] = point.options;
  37167. // Show and hide associated elements. This is performed
  37168. // regardless of redraw or not, because chart.redraw only
  37169. // handles full series.
  37170. ['graphic', 'dataLabel', 'connector', 'shadowGroup'].forEach(
  37171. function (key) {
  37172. if (point[key]) {
  37173. point[key][vis ? 'show' : 'hide'](true);
  37174. }
  37175. }
  37176. );
  37177. if (point.legendItem) {
  37178. chart.legend.colorizeItem(point, vis);
  37179. }
  37180. // #4170, hide halo after hiding point
  37181. if (!vis && point.state === 'hover') {
  37182. point.setState('');
  37183. }
  37184. // Handle ignore hidden slices
  37185. if (ignoreHiddenPoint) {
  37186. series.isDirty = true;
  37187. }
  37188. if (redraw) {
  37189. chart.redraw();
  37190. }
  37191. }
  37192. },
  37193. /**
  37194. * Set or toggle whether the slice is cut out from the pie
  37195. *
  37196. * @private
  37197. * @function Highcharts.seriesTypes.pie#pointClass#slice
  37198. *
  37199. * @param {boolean} sliced
  37200. * When undefined, the slice state is toggled.
  37201. *
  37202. * @param {boolean} redraw
  37203. * Whether to redraw the chart. True by default.
  37204. */
  37205. slice: function (sliced, redraw, animation) {
  37206. var point = this,
  37207. series = point.series,
  37208. chart = series.chart;
  37209. setAnimation(animation, chart);
  37210. // redraw is true by default
  37211. redraw = pick(redraw, true);
  37212. // if called without an argument, toggle
  37213. point.sliced = point.options.sliced = sliced =
  37214. defined(sliced) ? sliced : !point.sliced;
  37215. // update userOptions.data
  37216. series.options.data[series.data.indexOf(point)] = point.options;
  37217. point.graphic.animate(this.getTranslate());
  37218. if (point.shadowGroup) {
  37219. point.shadowGroup.animate(this.getTranslate());
  37220. }
  37221. },
  37222. /**
  37223. * @private
  37224. * @function Highcharts.seriesTypes.pie#pointClass#getTranslate
  37225. *
  37226. * @return {*}
  37227. */
  37228. getTranslate: function () {
  37229. return this.sliced ? this.slicedTranslation : {
  37230. translateX: 0,
  37231. translateY: 0
  37232. };
  37233. },
  37234. /**
  37235. * @private
  37236. * @function Highcharts.seriesTypes.pie#pointClass#haloPath
  37237. *
  37238. * @param {number} size
  37239. *
  37240. * @return {Highcharts.SVGPathArray}
  37241. */
  37242. haloPath: function (size) {
  37243. var shapeArgs = this.shapeArgs;
  37244. return this.sliced || !this.visible ?
  37245. [] :
  37246. this.series.chart.renderer.symbols.arc(
  37247. shapeArgs.x,
  37248. shapeArgs.y,
  37249. shapeArgs.r + size,
  37250. shapeArgs.r + size, {
  37251. // Substract 1px to ensure the background is not bleeding
  37252. // through between the halo and the slice (#7495).
  37253. innerR: this.shapeArgs.r - 1,
  37254. start: shapeArgs.start,
  37255. end: shapeArgs.end
  37256. }
  37257. );
  37258. },
  37259. connectorShapes: {
  37260. // only one available before v7.0.0
  37261. fixedOffset: function (labelPosition, connectorPosition, options) {
  37262. var breakAt = connectorPosition.breakAt,
  37263. touchingSliceAt = connectorPosition.touchingSliceAt,
  37264. linePath = options.softConnector ? [
  37265. 'C', // soft break
  37266. // 1st control point (of the curve)
  37267. labelPosition.x +
  37268. // 5 gives the connector a little horizontal bend
  37269. (labelPosition.alignment === 'left' ? -5 : 5),
  37270. labelPosition.y, //
  37271. 2 * breakAt.x - touchingSliceAt.x, // 2nd control point
  37272. 2 * breakAt.y - touchingSliceAt.y, //
  37273. breakAt.x, // end of the curve
  37274. breakAt.y //
  37275. ] : [
  37276. 'L', // pointy break
  37277. breakAt.x,
  37278. breakAt.y
  37279. ];
  37280. // assemble the path
  37281. return [
  37282. 'M',
  37283. labelPosition.x,
  37284. labelPosition.y
  37285. ].concat(linePath).concat([
  37286. 'L',
  37287. touchingSliceAt.x,
  37288. touchingSliceAt.y
  37289. ]);
  37290. },
  37291. straight: function (labelPosition, connectorPosition) {
  37292. var touchingSliceAt = connectorPosition.touchingSliceAt;
  37293. // direct line to the slice
  37294. return [
  37295. 'M',
  37296. labelPosition.x,
  37297. labelPosition.y,
  37298. 'L',
  37299. touchingSliceAt.x,
  37300. touchingSliceAt.y
  37301. ];
  37302. },
  37303. crookedLine: function (labelPosition, connectorPosition,
  37304. options) {
  37305. var touchingSliceAt = connectorPosition.touchingSliceAt,
  37306. series = this.series,
  37307. pieCenterX = series.center[0],
  37308. plotWidth = series.chart.plotWidth,
  37309. plotLeft = series.chart.plotLeft,
  37310. alignment = labelPosition.alignment,
  37311. radius = this.shapeArgs.r,
  37312. crookDistance =
  37313. H.relativeLength(options.crookDistance, 1), // % to fraction
  37314. crookX = alignment === 'left' ?
  37315. pieCenterX + radius + (plotWidth + plotLeft -
  37316. pieCenterX - radius) * (1 - crookDistance) :
  37317. plotLeft + (pieCenterX - radius) * crookDistance,
  37318. segmentWithCrook = ['L',
  37319. crookX,
  37320. labelPosition.y];
  37321. // crookedLine formula doesn't make sense if the path overlaps
  37322. // the label - use straight line instead in that case
  37323. if (alignment === 'left' ?
  37324. (crookX > labelPosition.x || crookX < touchingSliceAt.x) :
  37325. (crookX < labelPosition.x || crookX > touchingSliceAt.x)) {
  37326. segmentWithCrook = []; // remove the crook
  37327. }
  37328. // assemble the path
  37329. return [
  37330. 'M',
  37331. labelPosition.x,
  37332. labelPosition.y
  37333. ].concat(segmentWithCrook).concat(['L',
  37334. touchingSliceAt.x,
  37335. touchingSliceAt.y
  37336. ]);
  37337. }
  37338. },
  37339. /**
  37340. * Extendable method for getting the path of the connector between the data
  37341. * label and the pie slice.
  37342. */
  37343. getConnectorPath: function () {
  37344. var labelPosition = this.labelPosition,
  37345. options = this.series.options.dataLabels,
  37346. connectorShape = options.connectorShape,
  37347. predefinedShapes = this.connectorShapes;
  37348. // find out whether to use the predefined shape
  37349. if (predefinedShapes[connectorShape]) {
  37350. connectorShape = predefinedShapes[connectorShape];
  37351. }
  37352. return connectorShape.call(this, {
  37353. // pass simplified label position object for user's convenience
  37354. x: labelPosition.final.x,
  37355. y: labelPosition.final.y,
  37356. alignment: labelPosition.alignment
  37357. }, labelPosition.connectorPosition, options);
  37358. }
  37359. });
  37360. /**
  37361. * A `pie` series. If the [type](#series.pie.type) option is not specified,
  37362. * it is inherited from [chart.type](#chart.type).
  37363. *
  37364. * @extends series,plotOptions.pie
  37365. * @excluding dataParser, dataURL, stack, xAxis, yAxis
  37366. * @product highcharts
  37367. * @apioption series.pie
  37368. */
  37369. /**
  37370. * An array of data points for the series. For the `pie` series type,
  37371. * points can be given in the following ways:
  37372. *
  37373. * 1. An array of numerical values. In this case, the numerical values
  37374. * will be interpreted as `y` options. Example:
  37375. *
  37376. * ```js
  37377. * data: [0, 5, 3, 5]
  37378. * ```
  37379. *
  37380. * 2. An array of objects with named values. The following snippet shows only a
  37381. * few settings, see the complete options set below. If the total number of data
  37382. * points exceeds the series' [turboThreshold](#series.pie.turboThreshold),
  37383. * this option is not available.
  37384. *
  37385. * ```js
  37386. * data: [{
  37387. * y: 1,
  37388. * name: "Point2",
  37389. * color: "#00FF00"
  37390. * }, {
  37391. * y: 7,
  37392. * name: "Point1",
  37393. * color: "#FF00FF"
  37394. * }]</pre>
  37395. *
  37396. * @sample {highcharts} highcharts/chart/reflow-true/
  37397. * Numerical values
  37398. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  37399. * Arrays of numeric x and y
  37400. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  37401. * Arrays of datetime x and y
  37402. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  37403. * Arrays of point.name and y
  37404. * @sample {highcharts} highcharts/series/data-array-of-objects/
  37405. * Config objects
  37406. *
  37407. * @type {Array<number|Array<string,number>|*>}
  37408. * @extends series.line.data
  37409. * @excluding marker, x
  37410. * @product highcharts
  37411. * @apioption series.pie.data
  37412. */
  37413. /**
  37414. * The sequential index of the data point in the legend.
  37415. *
  37416. * @type {number}
  37417. * @product highcharts
  37418. * @apioption series.pie.data.legendIndex
  37419. */
  37420. /**
  37421. * Fires when the legend item belonging to the pie point (slice) is
  37422. * clicked. The `this` keyword refers to the point itself. One parameter,
  37423. * `event`, is passed to the function, containing common event information. The
  37424. * default action is to toggle the visibility of the point. This can be
  37425. * prevented by calling `event.preventDefault()`.
  37426. *
  37427. * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
  37428. * Confirm toggle visibility
  37429. *
  37430. * @type {Function}
  37431. * @since 1.2.0
  37432. * @product highcharts
  37433. * @apioption plotOptions.pie.point.events.legendItemClick
  37434. */
  37435. /**
  37436. * Whether to display a slice offset from the center.
  37437. *
  37438. * @sample {highcharts} highcharts/point/sliced/
  37439. * One sliced point
  37440. *
  37441. * @type {boolean}
  37442. * @product highcharts
  37443. * @apioption series.pie.data.sliced
  37444. */
  37445. /**
  37446. * @excluding legendItemClick
  37447. * @apioption series.pie.events
  37448. */
  37449. }(Highcharts));
  37450. (function (H) {
  37451. /**
  37452. * (c) 2010-2019 Torstein Honsi
  37453. *
  37454. * License: www.highcharts.com/license
  37455. */
  37456. var addEvent = H.addEvent,
  37457. arrayMax = H.arrayMax,
  37458. defined = H.defined,
  37459. extend = H.extend,
  37460. format = H.format,
  37461. merge = H.merge,
  37462. noop = H.noop,
  37463. pick = H.pick,
  37464. relativeLength = H.relativeLength,
  37465. Series = H.Series,
  37466. seriesTypes = H.seriesTypes,
  37467. stableSort = H.stableSort,
  37468. isArray = H.isArray,
  37469. splat = H.splat;
  37470. /**
  37471. * General distribution algorithm for distributing labels of differing size
  37472. * along a confined length in two dimensions. The algorithm takes an array of
  37473. * objects containing a size, a target and a rank. It will place the labels as
  37474. * close as possible to their targets, skipping the lowest ranked labels if
  37475. * necessary.
  37476. *
  37477. * @private
  37478. * @function Highcharts.distribute
  37479. *
  37480. * @param {Array<object>} boxes
  37481. *
  37482. * @param {number} len
  37483. *
  37484. * @param {number} maxDistance
  37485. */
  37486. H.distribute = function (boxes, len, maxDistance) {
  37487. var i,
  37488. overlapping = true,
  37489. origBoxes = boxes, // Original array will be altered with added .pos
  37490. restBoxes = [], // The outranked overshoot
  37491. box,
  37492. target,
  37493. total = 0,
  37494. reducedLen = origBoxes.reducedLen || len;
  37495. function sortByTarget(a, b) {
  37496. return a.target - b.target;
  37497. }
  37498. // If the total size exceeds the len, remove those boxes with the lowest
  37499. // rank
  37500. i = boxes.length;
  37501. while (i--) {
  37502. total += boxes[i].size;
  37503. }
  37504. // Sort by rank, then slice away overshoot
  37505. if (total > reducedLen) {
  37506. stableSort(boxes, function (a, b) {
  37507. return (b.rank || 0) - (a.rank || 0);
  37508. });
  37509. i = 0;
  37510. total = 0;
  37511. while (total <= reducedLen) {
  37512. total += boxes[i].size;
  37513. i++;
  37514. }
  37515. restBoxes = boxes.splice(i - 1, boxes.length);
  37516. }
  37517. // Order by target
  37518. stableSort(boxes, sortByTarget);
  37519. // So far we have been mutating the original array. Now
  37520. // create a copy with target arrays
  37521. boxes = boxes.map(function (box) {
  37522. return {
  37523. size: box.size,
  37524. targets: [box.target],
  37525. align: pick(box.align, 0.5)
  37526. };
  37527. });
  37528. while (overlapping) {
  37529. // Initial positions: target centered in box
  37530. i = boxes.length;
  37531. while (i--) {
  37532. box = boxes[i];
  37533. // Composite box, average of targets
  37534. target = (
  37535. Math.min.apply(0, box.targets) +
  37536. Math.max.apply(0, box.targets)
  37537. ) / 2;
  37538. box.pos = Math.min(
  37539. Math.max(0, target - box.size * box.align),
  37540. len - box.size
  37541. );
  37542. }
  37543. // Detect overlap and join boxes
  37544. i = boxes.length;
  37545. overlapping = false;
  37546. while (i--) {
  37547. // Overlap
  37548. if (i > 0 && boxes[i - 1].pos + boxes[i - 1].size > boxes[i].pos) {
  37549. // Add this size to the previous box
  37550. boxes[i - 1].size += boxes[i].size;
  37551. boxes[i - 1].targets = boxes[i - 1]
  37552. .targets
  37553. .concat(boxes[i].targets);
  37554. boxes[i - 1].align = 0.5;
  37555. // Overlapping right, push left
  37556. if (boxes[i - 1].pos + boxes[i - 1].size > len) {
  37557. boxes[i - 1].pos = len - boxes[i - 1].size;
  37558. }
  37559. boxes.splice(i, 1); // Remove this item
  37560. overlapping = true;
  37561. }
  37562. }
  37563. }
  37564. // Add the rest (hidden boxes)
  37565. origBoxes.push.apply(origBoxes, restBoxes);
  37566. // Now the composite boxes are placed, we need to put the original boxes
  37567. // within them
  37568. i = 0;
  37569. boxes.some(function (box) {
  37570. var posInCompositeBox = 0;
  37571. if (box.targets.some(function () {
  37572. origBoxes[i].pos = box.pos + posInCompositeBox;
  37573. // If the distance between the position and the target exceeds
  37574. // maxDistance, abort the loop and decrease the length in increments
  37575. // of 10% to recursively reduce the number of visible boxes by
  37576. // rank. Once all boxes are within the maxDistance, we're good.
  37577. if (
  37578. Math.abs(origBoxes[i].pos - origBoxes[i].target) >
  37579. maxDistance
  37580. ) {
  37581. // Reset the positions that are already set
  37582. origBoxes.slice(0, i + 1).forEach(function (box) {
  37583. delete box.pos;
  37584. });
  37585. // Try with a smaller length
  37586. origBoxes.reducedLen =
  37587. (origBoxes.reducedLen || len) - (len * 0.1);
  37588. // Recurse
  37589. if (origBoxes.reducedLen > len * 0.1) {
  37590. H.distribute(origBoxes, len, maxDistance);
  37591. }
  37592. // Exceeded maxDistance => abort
  37593. return true;
  37594. }
  37595. posInCompositeBox += origBoxes[i].size;
  37596. i++;
  37597. })) {
  37598. // Exceeded maxDistance => abort
  37599. return true;
  37600. }
  37601. });
  37602. // Add the rest (hidden) boxes and sort by target
  37603. stableSort(origBoxes, sortByTarget);
  37604. };
  37605. /**
  37606. * Draw the data labels
  37607. *
  37608. * @private
  37609. * @function Highcharts.Series#drawDataLabels
  37610. *
  37611. * @fires Highcharts.Series#event:afterDrawDataLabels
  37612. */
  37613. Series.prototype.drawDataLabels = function () {
  37614. var series = this,
  37615. chart = series.chart,
  37616. seriesOptions = series.options,
  37617. seriesDlOptions = seriesOptions.dataLabels,
  37618. points = series.points,
  37619. pointOptions,
  37620. hasRendered = series.hasRendered || 0,
  37621. dataLabelsGroup,
  37622. defer = pick(seriesDlOptions.defer, !!seriesOptions.animation),
  37623. renderer = chart.renderer;
  37624. /*
  37625. * Handle the dataLabels.filter option.
  37626. */
  37627. function applyFilter(point, options) {
  37628. var filter = options.filter,
  37629. op,
  37630. prop,
  37631. val;
  37632. if (filter) {
  37633. op = filter.operator;
  37634. prop = point[filter.property];
  37635. val = filter.value;
  37636. if (
  37637. (op === '>' && prop > val) ||
  37638. (op === '<' && prop < val) ||
  37639. (op === '>=' && prop >= val) ||
  37640. (op === '<=' && prop <= val) ||
  37641. (op === '==' && prop == val) || // eslint-disable-line eqeqeq
  37642. (op === '===' && prop === val)
  37643. ) {
  37644. return true;
  37645. }
  37646. return false;
  37647. }
  37648. return true;
  37649. }
  37650. /*
  37651. * Merge two objects that can be arrays. If one of them is an array, the
  37652. * other is merged into each element. If both are arrays, each element is
  37653. * merged by index. If neither are arrays, we use normal merge.
  37654. */
  37655. function mergeArrays(one, two) {
  37656. var res = [],
  37657. i;
  37658. if (isArray(one) && !isArray(two)) {
  37659. res = one.map(function (el) {
  37660. return merge(el, two);
  37661. });
  37662. } else if (isArray(two) && !isArray(one)) {
  37663. res = two.map(function (el) {
  37664. return merge(one, el);
  37665. });
  37666. } else if (!isArray(one) && !isArray(two)) {
  37667. res = merge(one, two);
  37668. } else {
  37669. i = Math.max(one.length, two.length);
  37670. while (i--) {
  37671. res[i] = merge(one[i], two[i]);
  37672. }
  37673. }
  37674. return res;
  37675. }
  37676. // Merge in plotOptions.dataLabels for series
  37677. seriesDlOptions = mergeArrays(
  37678. mergeArrays(
  37679. chart.options.plotOptions &&
  37680. chart.options.plotOptions.series &&
  37681. chart.options.plotOptions.series.dataLabels,
  37682. chart.options.plotOptions &&
  37683. chart.options.plotOptions[series.type] &&
  37684. chart.options.plotOptions[series.type].dataLabels
  37685. ),
  37686. seriesDlOptions
  37687. );
  37688. H.fireEvent(this, 'drawDataLabels');
  37689. if (
  37690. isArray(seriesDlOptions) ||
  37691. seriesDlOptions.enabled ||
  37692. series._hasPointLabels
  37693. ) {
  37694. // Create a separate group for the data labels to avoid rotation
  37695. dataLabelsGroup = series.plotGroup(
  37696. 'dataLabelsGroup',
  37697. 'data-labels',
  37698. defer && !hasRendered ? 'hidden' : 'visible', // #5133
  37699. seriesDlOptions.zIndex || 6
  37700. );
  37701. if (defer) {
  37702. dataLabelsGroup.attr({ opacity: +hasRendered }); // #3300
  37703. if (!hasRendered) {
  37704. addEvent(series, 'afterAnimate', function () {
  37705. if (series.visible) { // #2597, #3023, #3024
  37706. dataLabelsGroup.show(true);
  37707. }
  37708. dataLabelsGroup[
  37709. seriesOptions.animation ? 'animate' : 'attr'
  37710. ]({ opacity: 1 }, { duration: 200 });
  37711. });
  37712. }
  37713. }
  37714. // Make the labels for each point
  37715. points.forEach(function (point) {
  37716. // Merge in series options for the point.
  37717. // @note dataLabelAttribs (like pointAttribs) would eradicate
  37718. // the need for dlOptions, and simplify the section below.
  37719. pointOptions = splat(
  37720. mergeArrays(
  37721. seriesDlOptions,
  37722. point.dlOptions || // dlOptions is used in treemaps
  37723. (point.options && point.options.dataLabels)
  37724. )
  37725. );
  37726. // Handle each individual data label for this point
  37727. pointOptions.forEach(function (labelOptions, i) {
  37728. // Options for one datalabel
  37729. var labelEnabled = labelOptions.enabled &&
  37730. !point.isNull && // #2282, #4641, #7112
  37731. applyFilter(point, labelOptions),
  37732. labelConfig,
  37733. formatString,
  37734. labelText,
  37735. style,
  37736. rotation,
  37737. attr,
  37738. dataLabel = point.dataLabels ? point.dataLabels[i] :
  37739. point.dataLabel,
  37740. connector = point.connectors ? point.connectors[i] :
  37741. point.connector,
  37742. isNew = !dataLabel;
  37743. if (labelEnabled) {
  37744. // Create individual options structure that can be extended
  37745. // without affecting others
  37746. labelConfig = point.getLabelConfig();
  37747. formatString = (
  37748. labelOptions[point.formatPrefix + 'Format'] ||
  37749. labelOptions.format
  37750. );
  37751. labelText = defined(formatString) ?
  37752. format(formatString, labelConfig, chart.time) :
  37753. (
  37754. labelOptions[point.formatPrefix + 'Formatter'] ||
  37755. labelOptions.formatter
  37756. ).call(labelConfig, labelOptions);
  37757. style = labelOptions.style;
  37758. rotation = labelOptions.rotation;
  37759. if (!chart.styledMode) {
  37760. // Determine the color
  37761. style.color = pick(
  37762. labelOptions.color,
  37763. style.color,
  37764. series.color,
  37765. '#000000'
  37766. );
  37767. // Get automated contrast color
  37768. if (style.color === 'contrast') {
  37769. point.contrastColor = renderer.getContrast(
  37770. point.color || series.color
  37771. );
  37772. style.color = labelOptions.inside ||
  37773. pick(
  37774. labelOptions.distance,
  37775. point.labelDistance
  37776. ) < 0 ||
  37777. !!seriesOptions.stacking ?
  37778. point.contrastColor :
  37779. '#000000';
  37780. }
  37781. if (seriesOptions.cursor) {
  37782. style.cursor = seriesOptions.cursor;
  37783. }
  37784. }
  37785. attr = {
  37786. r: labelOptions.borderRadius || 0,
  37787. rotation: rotation,
  37788. padding: labelOptions.padding,
  37789. zIndex: 1
  37790. };
  37791. if (!chart.styledMode) {
  37792. attr.fill = labelOptions.backgroundColor;
  37793. attr.stroke = labelOptions.borderColor;
  37794. attr['stroke-width'] = labelOptions.borderWidth;
  37795. }
  37796. // Remove unused attributes (#947)
  37797. H.objectEach(attr, function (val, name) {
  37798. if (val === undefined) {
  37799. delete attr[name];
  37800. }
  37801. });
  37802. }
  37803. // If the point is outside the plot area, destroy it. #678, #820
  37804. if (dataLabel && (!labelEnabled || !defined(labelText))) {
  37805. point.dataLabel =
  37806. point.dataLabel && point.dataLabel.destroy();
  37807. if (point.dataLabels) {
  37808. // Remove point.dataLabels if this was the last one
  37809. if (point.dataLabels.length === 1) {
  37810. delete point.dataLabels;
  37811. } else {
  37812. delete point.dataLabels[i];
  37813. }
  37814. }
  37815. if (!i) {
  37816. delete point.dataLabel;
  37817. }
  37818. if (connector) {
  37819. point.connector = point.connector.destroy();
  37820. if (point.connectors) {
  37821. // Remove point.connectors if this was the last one
  37822. if (point.connectors.length === 1) {
  37823. delete point.connectors;
  37824. } else {
  37825. delete point.connectors[i];
  37826. }
  37827. }
  37828. }
  37829. // Individual labels are disabled if the are explicitly disabled
  37830. // in the point options, or if they fall outside the plot area.
  37831. } else if (labelEnabled && defined(labelText)) {
  37832. if (!dataLabel) {
  37833. // Create new label element
  37834. point.dataLabels = point.dataLabels || [];
  37835. dataLabel = point.dataLabels[i] = rotation ?
  37836. // Labels don't rotate, use text element
  37837. renderer.text(labelText, 0, -9999)
  37838. .addClass('highcharts-data-label') :
  37839. // We can use label
  37840. renderer.label(
  37841. labelText,
  37842. 0,
  37843. -9999,
  37844. labelOptions.shape,
  37845. null,
  37846. null,
  37847. labelOptions.useHTML,
  37848. null,
  37849. 'data-label'
  37850. );
  37851. // Store for backwards compatibility
  37852. if (!i) {
  37853. point.dataLabel = dataLabel;
  37854. }
  37855. dataLabel.addClass(
  37856. ' highcharts-data-label-color-' + point.colorIndex +
  37857. ' ' + (labelOptions.className || '') +
  37858. ( // #3398
  37859. labelOptions.useHTML ?
  37860. ' highcharts-tracker' :
  37861. ''
  37862. )
  37863. );
  37864. } else {
  37865. // Use old element and just update text
  37866. attr.text = labelText;
  37867. }
  37868. // Store data label options for later access
  37869. dataLabel.options = labelOptions;
  37870. dataLabel.attr(attr);
  37871. if (!chart.styledMode) {
  37872. // Styles must be applied before add in order to read
  37873. // text bounding box
  37874. dataLabel.css(style).shadow(labelOptions.shadow);
  37875. }
  37876. if (!dataLabel.added) {
  37877. dataLabel.add(dataLabelsGroup);
  37878. }
  37879. // Now the data label is created and placed at 0,0, so we
  37880. // need to align it
  37881. series.alignDataLabel(
  37882. point, dataLabel, labelOptions, null, isNew
  37883. );
  37884. }
  37885. });
  37886. });
  37887. }
  37888. H.fireEvent(this, 'afterDrawDataLabels');
  37889. };
  37890. /**
  37891. * Align each individual data label.
  37892. *
  37893. * @private
  37894. * @function Highcharts.Series#alignDataLabel
  37895. *
  37896. * @param {Highcharts.Point} point
  37897. *
  37898. * @param {Highcharts.SVGElement} dataLabel
  37899. *
  37900. * @param {Highcharts.PlotSeriesDataLabelsOptions} options
  37901. *
  37902. * @param {Highcharts.BBoxObject} alignTo
  37903. *
  37904. * @param {boolean} isNew
  37905. */
  37906. Series.prototype.alignDataLabel = function (
  37907. point,
  37908. dataLabel,
  37909. options,
  37910. alignTo,
  37911. isNew
  37912. ) {
  37913. var chart = this.chart,
  37914. inverted = this.isCartesian && chart.inverted,
  37915. plotX = pick(point.dlBox && point.dlBox.centerX, point.plotX, -9999),
  37916. plotY = pick(point.plotY, -9999),
  37917. bBox = dataLabel.getBBox(),
  37918. baseline,
  37919. rotation = options.rotation,
  37920. normRotation,
  37921. negRotation,
  37922. align = options.align,
  37923. rotCorr, // rotation correction
  37924. // Math.round for rounding errors (#2683), alignTo to allow column
  37925. // labels (#2700)
  37926. visible =
  37927. this.visible &&
  37928. (
  37929. point.series.forceDL ||
  37930. chart.isInsidePlot(plotX, Math.round(plotY), inverted) ||
  37931. (
  37932. alignTo && chart.isInsidePlot(
  37933. plotX,
  37934. inverted ?
  37935. alignTo.x + 1 :
  37936. alignTo.y + alignTo.height - 1,
  37937. inverted
  37938. )
  37939. )
  37940. ),
  37941. alignAttr, // the final position;
  37942. justify = pick(options.overflow, 'justify') === 'justify';
  37943. if (visible) {
  37944. baseline = chart.renderer.fontMetrics(
  37945. chart.styledMode ? undefined : options.style.fontSize,
  37946. dataLabel
  37947. ).b;
  37948. // The alignment box is a singular point
  37949. alignTo = extend({
  37950. x: inverted ? this.yAxis.len - plotY : plotX,
  37951. y: Math.round(inverted ? this.xAxis.len - plotX : plotY),
  37952. width: 0,
  37953. height: 0
  37954. }, alignTo);
  37955. // Add the text size for alignment calculation
  37956. extend(options, {
  37957. width: bBox.width,
  37958. height: bBox.height
  37959. });
  37960. // Allow a hook for changing alignment in the last moment, then do the
  37961. // alignment
  37962. if (rotation) {
  37963. justify = false; // Not supported for rotated text
  37964. rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
  37965. alignAttr = {
  37966. x: alignTo.x + options.x + alignTo.width / 2 + rotCorr.x,
  37967. y: (
  37968. alignTo.y +
  37969. options.y +
  37970. { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
  37971. alignTo.height
  37972. )
  37973. };
  37974. dataLabel[isNew ? 'attr' : 'animate'](alignAttr)
  37975. .attr({ // #3003
  37976. align: align
  37977. });
  37978. // Compensate for the rotated label sticking out on the sides
  37979. normRotation = (rotation + 720) % 360;
  37980. negRotation = normRotation > 180 && normRotation < 360;
  37981. if (align === 'left') {
  37982. alignAttr.y -= negRotation ? bBox.height : 0;
  37983. } else if (align === 'center') {
  37984. alignAttr.x -= bBox.width / 2;
  37985. alignAttr.y -= bBox.height / 2;
  37986. } else if (align === 'right') {
  37987. alignAttr.x -= bBox.width;
  37988. alignAttr.y -= negRotation ? 0 : bBox.height;
  37989. }
  37990. dataLabel.placed = true;
  37991. dataLabel.alignAttr = alignAttr;
  37992. } else {
  37993. dataLabel.align(options, null, alignTo);
  37994. alignAttr = dataLabel.alignAttr;
  37995. }
  37996. // Handle justify or crop
  37997. if (justify && alignTo.height >= 0) { // #8830
  37998. point.isLabelJustified = this.justifyDataLabel(
  37999. dataLabel,
  38000. options,
  38001. alignAttr,
  38002. bBox,
  38003. alignTo,
  38004. isNew
  38005. );
  38006. // Now check that the data label is within the plot area
  38007. } else if (pick(options.crop, true)) {
  38008. visible =
  38009. chart.isInsidePlot(
  38010. alignAttr.x,
  38011. alignAttr.y
  38012. ) &&
  38013. chart.isInsidePlot(
  38014. alignAttr.x + bBox.width,
  38015. alignAttr.y + bBox.height
  38016. );
  38017. }
  38018. // When we're using a shape, make it possible with a connector or an
  38019. // arrow pointing to thie point
  38020. if (options.shape && !rotation) {
  38021. dataLabel[isNew ? 'attr' : 'animate']({
  38022. anchorX: inverted ? chart.plotWidth - point.plotY : point.plotX,
  38023. anchorY: inverted ? chart.plotHeight - point.plotX : point.plotY
  38024. });
  38025. }
  38026. }
  38027. // Show or hide based on the final aligned position
  38028. if (!visible) {
  38029. dataLabel.attr({ y: -9999 });
  38030. dataLabel.placed = false; // don't animate back in
  38031. }
  38032. };
  38033. /**
  38034. * If data labels fall partly outside the plot area, align them back in, in a
  38035. * way that doesn't hide the point.
  38036. *
  38037. * @private
  38038. * @function Highcharts.Series#justifyDataLabel
  38039. *
  38040. * @param {Highcharts.SVGElement} dataLabel
  38041. *
  38042. * @param {Highcharts.PlotSeriesDataLabelsOptions} options
  38043. *
  38044. * @param {*} alignAttr
  38045. *
  38046. * @param {Highcharts.BBoxObject} bBox
  38047. *
  38048. * @param {boolean} isNew
  38049. *
  38050. * @return {boolean}
  38051. */
  38052. Series.prototype.justifyDataLabel = function (
  38053. dataLabel,
  38054. options,
  38055. alignAttr,
  38056. bBox,
  38057. alignTo,
  38058. isNew
  38059. ) {
  38060. var chart = this.chart,
  38061. align = options.align,
  38062. verticalAlign = options.verticalAlign,
  38063. off,
  38064. justified,
  38065. padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
  38066. // Off left
  38067. off = alignAttr.x + padding;
  38068. if (off < 0) {
  38069. if (align === 'right') {
  38070. options.align = 'left';
  38071. } else {
  38072. options.x = -off;
  38073. }
  38074. justified = true;
  38075. }
  38076. // Off right
  38077. off = alignAttr.x + bBox.width - padding;
  38078. if (off > chart.plotWidth) {
  38079. if (align === 'left') {
  38080. options.align = 'right';
  38081. } else {
  38082. options.x = chart.plotWidth - off;
  38083. }
  38084. justified = true;
  38085. }
  38086. // Off top
  38087. off = alignAttr.y + padding;
  38088. if (off < 0) {
  38089. if (verticalAlign === 'bottom') {
  38090. options.verticalAlign = 'top';
  38091. } else {
  38092. options.y = -off;
  38093. }
  38094. justified = true;
  38095. }
  38096. // Off bottom
  38097. off = alignAttr.y + bBox.height - padding;
  38098. if (off > chart.plotHeight) {
  38099. if (verticalAlign === 'top') {
  38100. options.verticalAlign = 'bottom';
  38101. } else {
  38102. options.y = chart.plotHeight - off;
  38103. }
  38104. justified = true;
  38105. }
  38106. if (justified) {
  38107. dataLabel.placed = !isNew;
  38108. dataLabel.align(options, null, alignTo);
  38109. }
  38110. return justified;
  38111. };
  38112. if (seriesTypes.pie) {
  38113. seriesTypes.pie.prototype.dataLabelPositioners = {
  38114. // Based on the value computed in Highcharts' distribute algorithm.
  38115. radialDistributionY: function (point) {
  38116. return point.top + point.distributeBox.pos;
  38117. },
  38118. // get the x - use the natural x position for labels near the
  38119. // top and bottom, to prevent the top and botton slice
  38120. // connectors from touching each other on either side
  38121. // Based on the value computed in Highcharts' distribute algorithm.
  38122. radialDistributionX: function (series, point, y, naturalY) {
  38123. return series.getX(
  38124. y < point.top + 2 || y > point.bottom - 2 ?
  38125. naturalY :
  38126. y,
  38127. point.half,
  38128. point
  38129. );
  38130. },
  38131. // dataLabels.distance determines the x position of the label
  38132. justify: function (point, radius, seriesCenter) {
  38133. return seriesCenter[0] + (point.half ? -1 : 1) *
  38134. (radius + point.labelDistance);
  38135. },
  38136. // Left edges of the left-half labels touch the left edge of the plot
  38137. // area. Right edges of the right-half labels touch the right edge of
  38138. // the plot area.
  38139. alignToPlotEdges: function (
  38140. dataLabel,
  38141. half,
  38142. plotWidth,
  38143. plotLeft
  38144. ) {
  38145. var dataLabelWidth = dataLabel.getBBox().width;
  38146. return half ? dataLabelWidth + plotLeft :
  38147. plotWidth - dataLabelWidth - plotLeft;
  38148. },
  38149. // Connectors of each side end in the same x position. Labels are
  38150. // aligned to them. Left edge of the widest left-half label touches the
  38151. // left edge of the plot area. Right edge of the widest right-half label
  38152. // touches the right edge of the plot area.
  38153. alignToConnectors: function (
  38154. points,
  38155. half,
  38156. plotWidth,
  38157. plotLeft
  38158. ) {
  38159. var maxDataLabelWidth = 0,
  38160. dataLabelWidth;
  38161. // find widest data label
  38162. points.forEach(function (point) {
  38163. dataLabelWidth = point.dataLabel.getBBox().width;
  38164. if (dataLabelWidth > maxDataLabelWidth) {
  38165. maxDataLabelWidth = dataLabelWidth;
  38166. }
  38167. });
  38168. return half ? maxDataLabelWidth + plotLeft :
  38169. plotWidth - maxDataLabelWidth - plotLeft;
  38170. }
  38171. };
  38172. /**
  38173. * Override the base drawDataLabels method by pie specific functionality
  38174. *
  38175. * @private
  38176. * @function Highcharts.seriesTypes.pie#drawDataLabels
  38177. */
  38178. seriesTypes.pie.prototype.drawDataLabels = function () {
  38179. var series = this,
  38180. data = series.data,
  38181. point,
  38182. chart = series.chart,
  38183. options = series.options.dataLabels,
  38184. connectorPadding = options.connectorPadding,
  38185. connectorWidth = pick(options.connectorWidth, 1),
  38186. plotWidth = chart.plotWidth,
  38187. plotHeight = chart.plotHeight,
  38188. plotLeft = chart.plotLeft,
  38189. maxWidth = Math.round(chart.chartWidth / 3),
  38190. connector,
  38191. seriesCenter = series.center,
  38192. radius = seriesCenter[2] / 2,
  38193. centerY = seriesCenter[1],
  38194. dataLabel,
  38195. dataLabelWidth,
  38196. // labelPos,
  38197. labelPosition,
  38198. labelHeight,
  38199. // divide the points into right and left halves for anti collision
  38200. halves = [
  38201. [], // right
  38202. [] // left
  38203. ],
  38204. x,
  38205. y,
  38206. visibility,
  38207. j,
  38208. overflow = [0, 0, 0, 0], // top, right, bottom, left
  38209. dataLabelPositioners = series.dataLabelPositioners;
  38210. // get out if not enabled
  38211. if (!series.visible || (!options.enabled && !series._hasPointLabels)) {
  38212. return;
  38213. }
  38214. // Reset all labels that have been shortened
  38215. data.forEach(function (point) {
  38216. if (point.dataLabel && point.visible && point.dataLabel.shortened) {
  38217. point.dataLabel
  38218. .attr({
  38219. width: 'auto'
  38220. }).css({
  38221. width: 'auto',
  38222. textOverflow: 'clip'
  38223. });
  38224. point.dataLabel.shortened = false;
  38225. }
  38226. });
  38227. // run parent method
  38228. Series.prototype.drawDataLabels.apply(series);
  38229. data.forEach(function (point) {
  38230. if (point.dataLabel) {
  38231. if (point.visible) { // #407, #2510
  38232. // Arrange points for detection collision
  38233. halves[point.half].push(point);
  38234. // Reset positions (#4905)
  38235. point.dataLabel._pos = null;
  38236. // Avoid long labels squeezing the pie size too far down
  38237. if (
  38238. !defined(options.style.width) &&
  38239. !defined(
  38240. point.options.dataLabels &&
  38241. point.options.dataLabels.style &&
  38242. point.options.dataLabels.style.width
  38243. )
  38244. ) {
  38245. if (point.dataLabel.getBBox().width > maxWidth) {
  38246. point.dataLabel.css({
  38247. // Use a fraction of the maxWidth to avoid
  38248. // wrapping close to the end of the string.
  38249. width: maxWidth * 0.7
  38250. });
  38251. point.dataLabel.shortened = true;
  38252. }
  38253. }
  38254. } else {
  38255. point.dataLabel = point.dataLabel.destroy();
  38256. // Workaround to make pies destroy multiple datalabels
  38257. // correctly. This logic needs rewriting to support multiple
  38258. // datalabels fully.
  38259. if (point.dataLabels && point.dataLabels.length === 1) {
  38260. delete point.dataLabels;
  38261. }
  38262. }
  38263. }
  38264. });
  38265. /* Loop over the points in each half, starting from the top and bottom
  38266. * of the pie to detect overlapping labels.
  38267. */
  38268. halves.forEach(function (points, i) {
  38269. var top,
  38270. bottom,
  38271. length = points.length,
  38272. positions = [],
  38273. naturalY,
  38274. sideOverflow,
  38275. size,
  38276. distributionLength;
  38277. if (!length) {
  38278. return;
  38279. }
  38280. // Sort by angle
  38281. series.sortByAngle(points, i - 0.5);
  38282. // Only do anti-collision when we have dataLabels outside the pie
  38283. // and have connectors. (#856)
  38284. if (series.maxLabelDistance > 0) {
  38285. top = Math.max(
  38286. 0,
  38287. centerY - radius - series.maxLabelDistance
  38288. );
  38289. bottom = Math.min(
  38290. centerY + radius + series.maxLabelDistance,
  38291. chart.plotHeight
  38292. );
  38293. points.forEach(function (point) {
  38294. // check if specific points' label is outside the pie
  38295. if (point.labelDistance > 0 && point.dataLabel) {
  38296. // point.top depends on point.labelDistance value
  38297. // Used for calculation of y value in getX method
  38298. point.top = Math.max(
  38299. 0,
  38300. centerY - radius - point.labelDistance
  38301. );
  38302. point.bottom = Math.min(
  38303. centerY + radius + point.labelDistance,
  38304. chart.plotHeight
  38305. );
  38306. size = point.dataLabel.getBBox().height || 21;
  38307. // point.positionsIndex is needed for getting index of
  38308. // parameter related to specific point inside positions
  38309. // array - not every point is in positions array.
  38310. point.distributeBox = {
  38311. target: point.labelPosition.natural.y -
  38312. point.top + size / 2,
  38313. size: size,
  38314. rank: point.y
  38315. };
  38316. positions.push(point.distributeBox);
  38317. }
  38318. });
  38319. distributionLength = bottom + size - top;
  38320. H.distribute(
  38321. positions,
  38322. distributionLength,
  38323. distributionLength / 5
  38324. );
  38325. }
  38326. // Now the used slots are sorted, fill them up sequentially
  38327. for (j = 0; j < length; j++) {
  38328. point = points[j];
  38329. // labelPos = point.labelPos;
  38330. labelPosition = point.labelPosition;
  38331. dataLabel = point.dataLabel;
  38332. visibility = point.visible === false ? 'hidden' : 'inherit';
  38333. naturalY = labelPosition.natural.y;
  38334. y = naturalY;
  38335. if (positions && defined(point.distributeBox)) {
  38336. if (point.distributeBox.pos === undefined) {
  38337. visibility = 'hidden';
  38338. } else {
  38339. labelHeight = point.distributeBox.size;
  38340. // Find label's y position
  38341. y = dataLabelPositioners.radialDistributionY(point);
  38342. }
  38343. }
  38344. // It is needed to delete point.positionIndex for
  38345. // dynamically added points etc.
  38346. delete point.positionIndex;
  38347. // Find label's x position
  38348. // justify is undocumented in the API - preserve support for it
  38349. if (options.justify) {
  38350. x = dataLabelPositioners.justify(point, radius,
  38351. seriesCenter);
  38352. } else {
  38353. switch (options.alignTo) {
  38354. case 'connectors':
  38355. x = dataLabelPositioners.alignToConnectors(points,
  38356. i, plotWidth, plotLeft);
  38357. break;
  38358. case 'plotEdges':
  38359. x = dataLabelPositioners.alignToPlotEdges(dataLabel,
  38360. i, plotWidth, plotLeft);
  38361. break;
  38362. default:
  38363. x = dataLabelPositioners.radialDistributionX(series,
  38364. point, y, naturalY);
  38365. }
  38366. }
  38367. // Record the placement and visibility
  38368. dataLabel._attr = {
  38369. visibility: visibility,
  38370. align: labelPosition.alignment
  38371. };
  38372. dataLabel._pos = {
  38373. x: (
  38374. x +
  38375. options.x +
  38376. ({
  38377. left: connectorPadding,
  38378. right: -connectorPadding
  38379. }[labelPosition.alignment] || 0)
  38380. ),
  38381. // 10 is for the baseline (label vs text)
  38382. y: y + options.y - 10
  38383. };
  38384. // labelPos.x = x;
  38385. // labelPos.y = y;
  38386. labelPosition.final.x = x;
  38387. labelPosition.final.y = y;
  38388. // Detect overflowing data labels
  38389. if (pick(options.crop, true)) {
  38390. dataLabelWidth = dataLabel.getBBox().width;
  38391. sideOverflow = null;
  38392. // Overflow left
  38393. if (
  38394. x - dataLabelWidth < connectorPadding &&
  38395. i === 1 // left half
  38396. ) {
  38397. sideOverflow = Math.round(
  38398. dataLabelWidth - x + connectorPadding
  38399. );
  38400. overflow[3] = Math.max(sideOverflow, overflow[3]);
  38401. // Overflow right
  38402. } else if (
  38403. x + dataLabelWidth > plotWidth - connectorPadding &&
  38404. i === 0 // right half
  38405. ) {
  38406. sideOverflow = Math.round(
  38407. x + dataLabelWidth - plotWidth + connectorPadding
  38408. );
  38409. overflow[1] = Math.max(sideOverflow, overflow[1]);
  38410. }
  38411. // Overflow top
  38412. if (y - labelHeight / 2 < 0) {
  38413. overflow[0] = Math.max(
  38414. Math.round(-y + labelHeight / 2),
  38415. overflow[0]
  38416. );
  38417. // Overflow left
  38418. } else if (y + labelHeight / 2 > plotHeight) {
  38419. overflow[2] = Math.max(
  38420. Math.round(y + labelHeight / 2 - plotHeight),
  38421. overflow[2]
  38422. );
  38423. }
  38424. dataLabel.sideOverflow = sideOverflow;
  38425. }
  38426. } // for each point
  38427. }); // for each half
  38428. // Do not apply the final placement and draw the connectors until we
  38429. // have verified that labels are not spilling over.
  38430. if (
  38431. arrayMax(overflow) === 0 ||
  38432. this.verifyDataLabelOverflow(overflow)
  38433. ) {
  38434. // Place the labels in the final position
  38435. this.placeDataLabels();
  38436. // Draw the connectors
  38437. if (connectorWidth) {
  38438. this.points.forEach(function (point) {
  38439. var isNew;
  38440. connector = point.connector;
  38441. dataLabel = point.dataLabel;
  38442. if (
  38443. dataLabel &&
  38444. dataLabel._pos &&
  38445. point.visible &&
  38446. point.labelDistance > 0
  38447. ) {
  38448. visibility = dataLabel._attr.visibility;
  38449. isNew = !connector;
  38450. if (isNew) {
  38451. point.connector = connector = chart.renderer.path()
  38452. .addClass(
  38453. 'highcharts-data-label-connector ' +
  38454. ' highcharts-color-' + point.colorIndex +
  38455. (
  38456. point.className ?
  38457. ' ' + point.className :
  38458. ''
  38459. )
  38460. )
  38461. .add(series.dataLabelsGroup);
  38462. if (!chart.styledMode) {
  38463. connector.attr({
  38464. 'stroke-width': connectorWidth,
  38465. 'stroke': (
  38466. options.connectorColor ||
  38467. point.color ||
  38468. '#666666'
  38469. )
  38470. });
  38471. }
  38472. }
  38473. connector[isNew ? 'attr' : 'animate']({
  38474. d: point.getConnectorPath()
  38475. });
  38476. connector.attr('visibility', visibility);
  38477. } else if (connector) {
  38478. point.connector = connector.destroy();
  38479. }
  38480. });
  38481. }
  38482. }
  38483. };
  38484. /**
  38485. * Extendable method for getting the path of the connector between the data
  38486. * label and the pie slice.
  38487. *
  38488. * @private
  38489. * @function Highcharts.seriesTypes.pie#connectorPath
  38490. *
  38491. * @param {*} labelPos
  38492. *
  38493. * @return {Highcharts.PathObject}
  38494. */
  38495. // TODO: depracated - remove it
  38496. /*
  38497. seriesTypes.pie.prototype.connectorPath = function (labelPos) {
  38498. var x = labelPos.x,
  38499. y = labelPos.y;
  38500. return pick(this.options.dataLabels.softConnector, true) ? [
  38501. 'M',
  38502. // end of the string at the label
  38503. x + (labelPos[6] === 'left' ? 5 : -5), y,
  38504. 'C',
  38505. x, y, // first break, next to the label
  38506. 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
  38507. labelPos[2], labelPos[3], // second break
  38508. 'L',
  38509. labelPos[4], labelPos[5] // base
  38510. ] : [
  38511. 'M',
  38512. // end of the string at the label
  38513. x + (labelPos[6] === 'left' ? 5 : -5), y,
  38514. 'L',
  38515. labelPos[2], labelPos[3], // second break
  38516. 'L',
  38517. labelPos[4], labelPos[5] // base
  38518. ];
  38519. };
  38520. */
  38521. /**
  38522. * Perform the final placement of the data labels after we have verified
  38523. * that they fall within the plot area.
  38524. *
  38525. * @private
  38526. * @function Highcharts.seriesTypes.pie#placeDataLabels
  38527. */
  38528. seriesTypes.pie.prototype.placeDataLabels = function () {
  38529. this.points.forEach(function (point) {
  38530. var dataLabel = point.dataLabel,
  38531. _pos;
  38532. if (dataLabel && point.visible) {
  38533. _pos = dataLabel._pos;
  38534. if (_pos) {
  38535. // Shorten data labels with ellipsis if they still overflow
  38536. // after the pie has reached minSize (#223).
  38537. if (dataLabel.sideOverflow) {
  38538. dataLabel._attr.width =
  38539. dataLabel.getBBox().width - dataLabel.sideOverflow;
  38540. dataLabel.css({
  38541. width: dataLabel._attr.width + 'px',
  38542. textOverflow: (
  38543. (this.options.dataLabels.style || {})
  38544. .textOverflow ||
  38545. 'ellipsis'
  38546. )
  38547. });
  38548. dataLabel.shortened = true;
  38549. }
  38550. dataLabel.attr(dataLabel._attr);
  38551. dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
  38552. dataLabel.moved = true;
  38553. } else if (dataLabel) {
  38554. dataLabel.attr({ y: -9999 });
  38555. }
  38556. }
  38557. }, this);
  38558. };
  38559. seriesTypes.pie.prototype.alignDataLabel = noop;
  38560. /**
  38561. * Verify whether the data labels are allowed to draw, or we should run more
  38562. * translation and data label positioning to keep them inside the plot area.
  38563. * Returns true when data labels are ready to draw.
  38564. *
  38565. * @private
  38566. * @function Highcharts.seriesTypes.pie#verifyDataLabelOverflow
  38567. *
  38568. * @param {boolean} overflow
  38569. *
  38570. * @return {boolean}
  38571. */
  38572. seriesTypes.pie.prototype.verifyDataLabelOverflow = function (overflow) {
  38573. var center = this.center,
  38574. options = this.options,
  38575. centerOption = options.center,
  38576. minSize = options.minSize || 80,
  38577. newSize = minSize,
  38578. // If a size is set, return true and don't try to shrink the pie
  38579. // to fit the labels.
  38580. ret = options.size !== null;
  38581. if (!ret) {
  38582. // Handle horizontal size and center
  38583. if (centerOption[0] !== null) { // Fixed center
  38584. newSize = Math.max(center[2] -
  38585. Math.max(overflow[1], overflow[3]), minSize);
  38586. } else { // Auto center
  38587. newSize = Math.max(
  38588. // horizontal overflow
  38589. center[2] - overflow[1] - overflow[3],
  38590. minSize
  38591. );
  38592. // horizontal center
  38593. center[0] += (overflow[3] - overflow[1]) / 2;
  38594. }
  38595. // Handle vertical size and center
  38596. if (centerOption[1] !== null) { // Fixed center
  38597. newSize = Math.max(Math.min(newSize, center[2] -
  38598. Math.max(overflow[0], overflow[2])), minSize);
  38599. } else { // Auto center
  38600. newSize = Math.max(
  38601. Math.min(
  38602. newSize,
  38603. // vertical overflow
  38604. center[2] - overflow[0] - overflow[2]
  38605. ),
  38606. minSize
  38607. );
  38608. // vertical center
  38609. center[1] += (overflow[0] - overflow[2]) / 2;
  38610. }
  38611. // If the size must be decreased, we need to run translate and
  38612. // drawDataLabels again
  38613. if (newSize < center[2]) {
  38614. center[2] = newSize;
  38615. center[3] = Math.min( // #3632
  38616. relativeLength(options.innerSize || 0, newSize),
  38617. newSize
  38618. );
  38619. this.translate(center);
  38620. if (this.drawDataLabels) {
  38621. this.drawDataLabels();
  38622. }
  38623. // Else, return true to indicate that the pie and its labels is
  38624. // within the plot area
  38625. } else {
  38626. ret = true;
  38627. }
  38628. }
  38629. return ret;
  38630. };
  38631. }
  38632. if (seriesTypes.column) {
  38633. /**
  38634. * Override the basic data label alignment by adjusting for the position of
  38635. * the column.
  38636. *
  38637. * @private
  38638. * @function Highcharts.seriesTypes.column#alignDataLabel
  38639. *
  38640. * @param {Highcharts.Point} point
  38641. *
  38642. * @param {Highcharts.SVGElement} dataLabel
  38643. *
  38644. * @param {Highcharts.PlotSeriesDataLabelsOptions} options
  38645. *
  38646. * @param {Highcharts.BBoxObject} alignTo
  38647. *
  38648. * @param {boolean} isNew
  38649. */
  38650. seriesTypes.column.prototype.alignDataLabel = function (
  38651. point,
  38652. dataLabel,
  38653. options,
  38654. alignTo,
  38655. isNew
  38656. ) {
  38657. var inverted = this.chart.inverted,
  38658. series = point.series,
  38659. // data label box for alignment
  38660. dlBox = point.dlBox || point.shapeArgs,
  38661. below = pick(
  38662. point.below, // range series
  38663. point.plotY > pick(this.translatedThreshold, series.yAxis.len)
  38664. ),
  38665. // draw it inside the box?
  38666. inside = pick(options.inside, !!this.options.stacking),
  38667. overshoot;
  38668. // Align to the column itself, or the top of it
  38669. if (dlBox) { // Area range uses this method but not alignTo
  38670. alignTo = merge(dlBox);
  38671. if (alignTo.y < 0) {
  38672. alignTo.height += alignTo.y;
  38673. alignTo.y = 0;
  38674. }
  38675. overshoot = alignTo.y + alignTo.height - series.yAxis.len;
  38676. if (overshoot > 0) {
  38677. alignTo.height -= overshoot;
  38678. }
  38679. if (inverted) {
  38680. alignTo = {
  38681. x: series.yAxis.len - alignTo.y - alignTo.height,
  38682. y: series.xAxis.len - alignTo.x - alignTo.width,
  38683. width: alignTo.height,
  38684. height: alignTo.width
  38685. };
  38686. }
  38687. // Compute the alignment box
  38688. if (!inside) {
  38689. if (inverted) {
  38690. alignTo.x += below ? 0 : alignTo.width;
  38691. alignTo.width = 0;
  38692. } else {
  38693. alignTo.y += below ? alignTo.height : 0;
  38694. alignTo.height = 0;
  38695. }
  38696. }
  38697. }
  38698. // When alignment is undefined (typically columns and bars), display the
  38699. // individual point below or above the point depending on the threshold
  38700. options.align = pick(
  38701. options.align,
  38702. !inverted || inside ? 'center' : below ? 'right' : 'left'
  38703. );
  38704. options.verticalAlign = pick(
  38705. options.verticalAlign,
  38706. inverted || inside ? 'middle' : below ? 'top' : 'bottom'
  38707. );
  38708. // Call the parent method
  38709. Series.prototype.alignDataLabel.call(
  38710. this,
  38711. point,
  38712. dataLabel,
  38713. options,
  38714. alignTo,
  38715. isNew
  38716. );
  38717. // If label was justified and we have contrast, set it:
  38718. if (point.isLabelJustified && point.contrastColor) {
  38719. dataLabel.css({
  38720. color: point.contrastColor
  38721. });
  38722. }
  38723. };
  38724. }
  38725. }(Highcharts));
  38726. (function (H) {
  38727. /* *
  38728. * Highcharts module to hide overlapping data labels. This module is included in
  38729. * Highcharts.
  38730. *
  38731. * (c) 2009-2019 Torstein Honsi
  38732. *
  38733. * License: www.highcharts.com/license
  38734. */
  38735. var Chart = H.Chart,
  38736. isArray = H.isArray,
  38737. objectEach = H.objectEach,
  38738. pick = H.pick,
  38739. addEvent = H.addEvent,
  38740. fireEvent = H.fireEvent;
  38741. // Collect potensial overlapping data labels. Stack labels probably don't need
  38742. // to be considered because they are usually accompanied by data labels that lie
  38743. // inside the columns.
  38744. addEvent(Chart, 'render', function collectAndHide() {
  38745. var labels = [];
  38746. // Consider external label collectors
  38747. (this.labelCollectors || []).forEach(function (collector) {
  38748. labels = labels.concat(collector());
  38749. });
  38750. (this.yAxis || []).forEach(function (yAxis) {
  38751. if (
  38752. yAxis.options.stackLabels &&
  38753. !yAxis.options.stackLabels.allowOverlap
  38754. ) {
  38755. objectEach(yAxis.stacks, function (stack) {
  38756. objectEach(stack, function (stackItem) {
  38757. labels.push(stackItem.label);
  38758. });
  38759. });
  38760. }
  38761. });
  38762. (this.series || []).forEach(function (series) {
  38763. var dlOptions = series.options.dataLabels;
  38764. if (
  38765. series.visible &&
  38766. !(dlOptions.enabled === false && !series._hasPointLabels)
  38767. ) { // #3866
  38768. series.points.forEach(function (point) {
  38769. if (point.visible) {
  38770. var dataLabels = (
  38771. isArray(point.dataLabels) ?
  38772. point.dataLabels :
  38773. (point.dataLabel ? [point.dataLabel] : [])
  38774. );
  38775. dataLabels.forEach(function (label) {
  38776. var options = label.options;
  38777. label.labelrank = pick(
  38778. options.labelrank,
  38779. point.labelrank,
  38780. point.shapeArgs && point.shapeArgs.height
  38781. ); // #4118
  38782. if (!options.allowOverlap) {
  38783. labels.push(label);
  38784. }
  38785. });
  38786. }
  38787. });
  38788. }
  38789. });
  38790. this.hideOverlappingLabels(labels);
  38791. });
  38792. /**
  38793. * Hide overlapping labels. Labels are moved and faded in and out on zoom to
  38794. * provide a smooth visual imression.
  38795. *
  38796. * @private
  38797. * @function Highcharts.Chart#hideOverlappingLabels
  38798. *
  38799. * @param {Array<Highcharts.SVGElement>} labels
  38800. */
  38801. Chart.prototype.hideOverlappingLabels = function (labels) {
  38802. var chart = this,
  38803. len = labels.length,
  38804. ren = chart.renderer,
  38805. label,
  38806. i,
  38807. j,
  38808. label1,
  38809. label2,
  38810. isIntersecting,
  38811. box1,
  38812. box2,
  38813. intersectRect = function (x1, y1, w1, h1, x2, y2, w2, h2) {
  38814. return !(
  38815. x2 > x1 + w1 ||
  38816. x2 + w2 < x1 ||
  38817. y2 > y1 + h1 ||
  38818. y2 + h2 < y1
  38819. );
  38820. },
  38821. // Get the box with its position inside the chart, as opposed to getBBox
  38822. // that only reports the position relative to the parent.
  38823. getAbsoluteBox = function (label) {
  38824. var pos,
  38825. parent,
  38826. bBox,
  38827. // Substract the padding if no background or border (#4333)
  38828. padding = label.box ? 0 : (label.padding || 0),
  38829. lineHeightCorrection = 0;
  38830. if (
  38831. label &&
  38832. (!label.alignAttr || label.placed)
  38833. ) {
  38834. pos = label.alignAttr || {
  38835. x: label.attr('x'),
  38836. y: label.attr('y')
  38837. };
  38838. parent = label.parentGroup;
  38839. // Get width and height if pure text nodes (stack labels)
  38840. if (!label.width) {
  38841. bBox = label.getBBox();
  38842. label.width = bBox.width;
  38843. label.height = bBox.height;
  38844. // Labels positions are computed from top left corner, so
  38845. // we need to substract the text height from text nodes too.
  38846. lineHeightCorrection = ren
  38847. .fontMetrics(null, label.element).h;
  38848. }
  38849. return {
  38850. x: pos.x + (parent.translateX || 0) + padding,
  38851. y: pos.y + (parent.translateY || 0) + padding -
  38852. lineHeightCorrection,
  38853. width: label.width - 2 * padding,
  38854. height: label.height - 2 * padding
  38855. };
  38856. }
  38857. };
  38858. for (i = 0; i < len; i++) {
  38859. label = labels[i];
  38860. if (label) {
  38861. // Mark with initial opacity
  38862. label.oldOpacity = label.opacity;
  38863. label.newOpacity = 1;
  38864. label.absoluteBox = getAbsoluteBox(label);
  38865. }
  38866. }
  38867. // Prevent a situation in a gradually rising slope, that each label will
  38868. // hide the previous one because the previous one always has lower rank.
  38869. labels.sort(function (a, b) {
  38870. return (b.labelrank || 0) - (a.labelrank || 0);
  38871. });
  38872. // Detect overlapping labels
  38873. for (i = 0; i < len; i++) {
  38874. label1 = labels[i];
  38875. box1 = label1 && label1.absoluteBox;
  38876. for (j = i + 1; j < len; ++j) {
  38877. label2 = labels[j];
  38878. box2 = label2 && label2.absoluteBox;
  38879. if (
  38880. box1 &&
  38881. box2 &&
  38882. label1 !== label2 && // #6465, polar chart with connectEnds
  38883. label1.newOpacity !== 0 &&
  38884. label2.newOpacity !== 0
  38885. ) {
  38886. isIntersecting = intersectRect(
  38887. box1.x,
  38888. box1.y,
  38889. box1.width,
  38890. box1.height,
  38891. box2.x,
  38892. box2.y,
  38893. box2.width,
  38894. box2.height
  38895. );
  38896. if (isIntersecting) {
  38897. (label1.labelrank < label2.labelrank ? label1 : label2)
  38898. .newOpacity = 0;
  38899. }
  38900. }
  38901. }
  38902. }
  38903. // Hide or show
  38904. labels.forEach(function (label) {
  38905. var complete,
  38906. newOpacity;
  38907. if (label) {
  38908. newOpacity = label.newOpacity;
  38909. if (label.oldOpacity !== newOpacity) {
  38910. // Make sure the label is completely hidden to avoid catching
  38911. // clicks (#4362)
  38912. if (label.alignAttr && label.placed) { // data labels
  38913. if (newOpacity) {
  38914. label.show(true);
  38915. } else {
  38916. complete = function () {
  38917. label.hide();
  38918. };
  38919. }
  38920. // Animate or set the opacity
  38921. label.alignAttr.opacity = newOpacity;
  38922. label[label.isOld ? 'animate' : 'attr'](
  38923. label.alignAttr,
  38924. null,
  38925. complete
  38926. );
  38927. fireEvent(chart, 'afterHideOverlappingLabels');
  38928. } else { // other labels, tick labels
  38929. label.attr({
  38930. opacity: newOpacity
  38931. });
  38932. }
  38933. }
  38934. label.isOld = true;
  38935. }
  38936. });
  38937. };
  38938. }(Highcharts));
  38939. (function (H) {
  38940. /**
  38941. * (c) 2010-2019 Torstein Honsi
  38942. *
  38943. * License: www.highcharts.com/license
  38944. */
  38945. var addEvent = H.addEvent,
  38946. Chart = H.Chart,
  38947. createElement = H.createElement,
  38948. css = H.css,
  38949. defaultOptions = H.defaultOptions,
  38950. defaultPlotOptions = H.defaultPlotOptions,
  38951. extend = H.extend,
  38952. fireEvent = H.fireEvent,
  38953. hasTouch = H.hasTouch,
  38954. isObject = H.isObject,
  38955. Legend = H.Legend,
  38956. merge = H.merge,
  38957. pick = H.pick,
  38958. Point = H.Point,
  38959. Series = H.Series,
  38960. seriesTypes = H.seriesTypes,
  38961. svg = H.svg,
  38962. TrackerMixin;
  38963. /**
  38964. * TrackerMixin for points and graphs.
  38965. *
  38966. * @private
  38967. * @mixin Highcharts.TrackerMixin
  38968. */
  38969. TrackerMixin = H.TrackerMixin = {
  38970. /**
  38971. * Draw the tracker for a point.
  38972. *
  38973. * @private
  38974. * @function Highcharts.TrackerMixin.drawTrackerPoint
  38975. *
  38976. * @fires Highcharts.Series#event:afterDrawTracker
  38977. */
  38978. drawTrackerPoint: function () {
  38979. var series = this,
  38980. chart = series.chart,
  38981. pointer = chart.pointer,
  38982. onMouseOver = function (e) {
  38983. var point = pointer.getPointFromEvent(e);
  38984. // undefined on graph in scatterchart
  38985. if (point !== undefined) {
  38986. pointer.isDirectTouch = true;
  38987. point.onMouseOver(e);
  38988. }
  38989. };
  38990. // Add reference to the point
  38991. series.points.forEach(function (point) {
  38992. if (point.graphic) {
  38993. point.graphic.element.point = point;
  38994. }
  38995. if (point.dataLabel) {
  38996. if (point.dataLabel.div) {
  38997. point.dataLabel.div.point = point;
  38998. } else {
  38999. point.dataLabel.element.point = point;
  39000. }
  39001. }
  39002. });
  39003. // Add the event listeners, we need to do this only once
  39004. if (!series._hasTracking) {
  39005. series.trackerGroups.forEach(function (key) {
  39006. if (series[key]) { // we don't always have dataLabelsGroup
  39007. series[key]
  39008. .addClass('highcharts-tracker')
  39009. .on('mouseover', onMouseOver)
  39010. .on('mouseout', function (e) {
  39011. pointer.onTrackerMouseOut(e);
  39012. });
  39013. if (hasTouch) {
  39014. series[key].on('touchstart', onMouseOver);
  39015. }
  39016. if (!chart.styledMode && series.options.cursor) {
  39017. series[key]
  39018. .css(css)
  39019. .css({ cursor: series.options.cursor });
  39020. }
  39021. }
  39022. });
  39023. series._hasTracking = true;
  39024. }
  39025. fireEvent(this, 'afterDrawTracker');
  39026. },
  39027. /**
  39028. * Draw the tracker object that sits above all data labels and markers to
  39029. * track mouse events on the graph or points. For the line type charts
  39030. * the tracker uses the same graphPath, but with a greater stroke width
  39031. * for better control.
  39032. *
  39033. * @private
  39034. * @function Highcharts.TrackerMixin.drawTrackerGraph
  39035. *
  39036. * @fires Highcharts.Series#event:afterDrawTracker
  39037. */
  39038. drawTrackerGraph: function () {
  39039. var series = this,
  39040. options = series.options,
  39041. trackByArea = options.trackByArea,
  39042. trackerPath = [].concat(
  39043. trackByArea ? series.areaPath : series.graphPath
  39044. ),
  39045. trackerPathLength = trackerPath.length,
  39046. chart = series.chart,
  39047. pointer = chart.pointer,
  39048. renderer = chart.renderer,
  39049. snap = chart.options.tooltip.snap,
  39050. tracker = series.tracker,
  39051. i,
  39052. onMouseOver = function () {
  39053. if (chart.hoverSeries !== series) {
  39054. series.onMouseOver();
  39055. }
  39056. },
  39057. /*
  39058. * Empirical lowest possible opacities for TRACKER_FILL for an
  39059. * element to stay invisible but clickable
  39060. * IE6: 0.002
  39061. * IE7: 0.002
  39062. * IE8: 0.002
  39063. * IE9: 0.00000000001 (unlimited)
  39064. * IE10: 0.0001 (exporting only)
  39065. * FF: 0.00000000001 (unlimited)
  39066. * Chrome: 0.000001
  39067. * Safari: 0.000001
  39068. * Opera: 0.00000000001 (unlimited)
  39069. */
  39070. TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
  39071. // Extend end points. A better way would be to use round linecaps,
  39072. // but those are not clickable in VML.
  39073. if (trackerPathLength && !trackByArea) {
  39074. i = trackerPathLength + 1;
  39075. while (i--) {
  39076. if (trackerPath[i] === 'M') { // extend left side
  39077. trackerPath.splice(
  39078. i + 1, 0,
  39079. trackerPath[i + 1] - snap,
  39080. trackerPath[i + 2],
  39081. 'L'
  39082. );
  39083. }
  39084. if (
  39085. (i && trackerPath[i] === 'M') ||
  39086. i === trackerPathLength
  39087. ) { // extend right side
  39088. trackerPath.splice(
  39089. i,
  39090. 0,
  39091. 'L',
  39092. trackerPath[i - 2] + snap,
  39093. trackerPath[i - 1]
  39094. );
  39095. }
  39096. }
  39097. }
  39098. // draw the tracker
  39099. if (tracker) {
  39100. tracker.attr({ d: trackerPath });
  39101. } else if (series.graph) { // create
  39102. series.tracker = renderer.path(trackerPath)
  39103. .attr({
  39104. visibility: series.visible ? 'visible' : 'hidden',
  39105. zIndex: 2
  39106. })
  39107. .addClass(
  39108. trackByArea ?
  39109. 'highcharts-tracker-area' :
  39110. 'highcharts-tracker-line'
  39111. )
  39112. .add(series.group);
  39113. if (!chart.styledMode) {
  39114. series.tracker.attr({
  39115. 'stroke-linejoin': 'round', // #1225
  39116. stroke: TRACKER_FILL,
  39117. fill: trackByArea ? TRACKER_FILL : 'none',
  39118. 'stroke-width': series.graph.strokeWidth() +
  39119. (trackByArea ? 0 : 2 * snap)
  39120. });
  39121. }
  39122. // The tracker is added to the series group, which is clipped, but
  39123. // is covered by the marker group. So the marker group also needs to
  39124. // capture events.
  39125. [series.tracker, series.markerGroup].forEach(function (tracker) {
  39126. tracker.addClass('highcharts-tracker')
  39127. .on('mouseover', onMouseOver)
  39128. .on('mouseout', function (e) {
  39129. pointer.onTrackerMouseOut(e);
  39130. });
  39131. if (options.cursor && !chart.styledMode) {
  39132. tracker.css({ cursor: options.cursor });
  39133. }
  39134. if (hasTouch) {
  39135. tracker.on('touchstart', onMouseOver);
  39136. }
  39137. });
  39138. }
  39139. fireEvent(this, 'afterDrawTracker');
  39140. }
  39141. };
  39142. /* End TrackerMixin */
  39143. /*
  39144. * Add tracking event listener to the series group, so the point graphics
  39145. * themselves act as trackers
  39146. */
  39147. if (seriesTypes.column) {
  39148. /**
  39149. * @private
  39150. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.column#drawTracker
  39151. */
  39152. seriesTypes.column.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  39153. }
  39154. if (seriesTypes.pie) {
  39155. /**
  39156. * @private
  39157. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.pie#drawTracker
  39158. */
  39159. seriesTypes.pie.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  39160. }
  39161. if (seriesTypes.scatter) {
  39162. /**
  39163. * @private
  39164. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.scatter#drawTracker
  39165. */
  39166. seriesTypes.scatter.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  39167. }
  39168. // Extend Legend for item events.
  39169. extend(Legend.prototype, {
  39170. /**
  39171. * @private
  39172. * @function Highcharts.Legend#setItemEvents
  39173. *
  39174. * @param {Highcharts.Point|Highcharts.Series} item
  39175. *
  39176. * @param {Highcharts.SVGElement} legendItem
  39177. *
  39178. * @param {boolean} [useHTML=false]
  39179. *
  39180. * @fires Highcharts.Point#event:legendItemClick
  39181. * @fires Highcharts.Series#event:legendItemClick
  39182. */
  39183. setItemEvents: function (item, legendItem, useHTML) {
  39184. var legend = this,
  39185. boxWrapper = legend.chart.renderer.boxWrapper,
  39186. activeClass = 'highcharts-legend-' +
  39187. (item instanceof Point ? 'point' : 'series') + '-active',
  39188. styledMode = legend.chart.styledMode;
  39189. // Set the events on the item group, or in case of useHTML, the item
  39190. // itself (#1249)
  39191. (useHTML ? legendItem : item.legendGroup).on('mouseover', function () {
  39192. item.setState('hover');
  39193. // A CSS class to dim or hide other than the hovered series
  39194. boxWrapper.addClass(activeClass);
  39195. if (!styledMode) {
  39196. legendItem.css(legend.options.itemHoverStyle);
  39197. }
  39198. })
  39199. .on('mouseout', function () {
  39200. if (!legend.styledMode) {
  39201. legendItem.css(
  39202. merge(
  39203. item.visible ?
  39204. legend.itemStyle :
  39205. legend.itemHiddenStyle
  39206. )
  39207. );
  39208. }
  39209. // A CSS class to dim or hide other than the hovered series
  39210. boxWrapper.removeClass(activeClass);
  39211. item.setState();
  39212. })
  39213. .on('click', function (event) {
  39214. var strLegendItemClick = 'legendItemClick',
  39215. fnLegendItemClick = function () {
  39216. if (item.setVisible) {
  39217. item.setVisible();
  39218. }
  39219. };
  39220. // A CSS class to dim or hide other than the hovered series.
  39221. // Event handling in iOS causes the activeClass to be added
  39222. // prior to click in some cases (#7418).
  39223. boxWrapper.removeClass(activeClass);
  39224. // Pass over the click/touch event. #4.
  39225. event = {
  39226. browserEvent: event
  39227. };
  39228. // click the name or symbol
  39229. if (item.firePointEvent) { // point
  39230. item.firePointEvent(
  39231. strLegendItemClick,
  39232. event,
  39233. fnLegendItemClick
  39234. );
  39235. } else {
  39236. fireEvent(
  39237. item, strLegendItemClick, event, fnLegendItemClick
  39238. );
  39239. }
  39240. });
  39241. },
  39242. /**
  39243. * @private
  39244. * @function Highcharts.Legend#createCheckboxForItem
  39245. *
  39246. * @param {Highcharts.Point|Highcharts.Series} item
  39247. *
  39248. * @fires Highcharts.Series#event:checkboxClick
  39249. */
  39250. createCheckboxForItem: function (item) {
  39251. var legend = this;
  39252. item.checkbox = createElement('input', {
  39253. type: 'checkbox',
  39254. className: 'highcharts-legend-checkbox',
  39255. checked: item.selected,
  39256. defaultChecked: item.selected // required by IE7
  39257. }, legend.options.itemCheckboxStyle, legend.chart.container);
  39258. addEvent(item.checkbox, 'click', function (event) {
  39259. var target = event.target;
  39260. fireEvent(
  39261. item.series || item,
  39262. 'checkboxClick',
  39263. { // #3712
  39264. checked: target.checked,
  39265. item: item
  39266. },
  39267. function () {
  39268. item.select();
  39269. }
  39270. );
  39271. });
  39272. }
  39273. });
  39274. /*
  39275. * Extend the Chart object with interaction
  39276. */
  39277. extend(Chart.prototype, /** @lends Chart.prototype */ {
  39278. /**
  39279. * Display the zoom button.
  39280. *
  39281. * @private
  39282. * @function Highcharts.Chart#showResetZoom
  39283. *
  39284. * @fires Highcharts.Chart#event:beforeShowResetZoom
  39285. */
  39286. showResetZoom: function () {
  39287. var chart = this,
  39288. lang = defaultOptions.lang,
  39289. btnOptions = chart.options.chart.resetZoomButton,
  39290. theme = btnOptions.theme,
  39291. states = theme.states,
  39292. alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox';
  39293. function zoomOut() {
  39294. chart.zoomOut();
  39295. }
  39296. fireEvent(this, 'beforeShowResetZoom', null, function () {
  39297. chart.resetZoomButton = chart.renderer.button(
  39298. lang.resetZoom,
  39299. null,
  39300. null,
  39301. zoomOut,
  39302. theme,
  39303. states && states.hover
  39304. )
  39305. .attr({
  39306. align: btnOptions.position.align,
  39307. title: lang.resetZoomTitle
  39308. })
  39309. .addClass('highcharts-reset-zoom')
  39310. .add()
  39311. .align(btnOptions.position, false, alignTo);
  39312. });
  39313. },
  39314. /**
  39315. * Zoom the chart out after a user has zoomed in. See also
  39316. * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
  39317. *
  39318. * @function Highcharts.Chart#zoomOut
  39319. *
  39320. * @fires Highcharts.Chart#event:selection
  39321. */
  39322. zoomOut: function () {
  39323. fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
  39324. },
  39325. /**
  39326. * Zoom into a given portion of the chart given by axis coordinates.
  39327. *
  39328. * @private
  39329. * @function Highcharts.Chart#zoom
  39330. *
  39331. * @param {Highcharts.SelectEventObject} event
  39332. */
  39333. zoom: function (event) {
  39334. var chart = this,
  39335. hasZoomed,
  39336. pointer = chart.pointer,
  39337. displayButton = false,
  39338. resetZoomButton;
  39339. // If zoom is called with no arguments, reset the axes
  39340. if (!event || event.resetSelection) {
  39341. chart.axes.forEach(function (axis) {
  39342. hasZoomed = axis.zoom();
  39343. });
  39344. pointer.initiated = false; // #6804
  39345. } else { // else, zoom in on all axes
  39346. event.xAxis.concat(event.yAxis).forEach(function (axisData) {
  39347. var axis = axisData.axis,
  39348. isXAxis = axis.isXAxis;
  39349. // don't zoom more than minRange
  39350. if (pointer[isXAxis ? 'zoomX' : 'zoomY']) {
  39351. hasZoomed = axis.zoom(axisData.min, axisData.max);
  39352. if (axis.displayBtn) {
  39353. displayButton = true;
  39354. }
  39355. }
  39356. });
  39357. }
  39358. // Show or hide the Reset zoom button
  39359. resetZoomButton = chart.resetZoomButton;
  39360. if (displayButton && !resetZoomButton) {
  39361. chart.showResetZoom();
  39362. } else if (!displayButton && isObject(resetZoomButton)) {
  39363. chart.resetZoomButton = resetZoomButton.destroy();
  39364. }
  39365. // Redraw
  39366. if (hasZoomed) {
  39367. chart.redraw(
  39368. pick(
  39369. chart.options.chart.animation,
  39370. event && event.animation,
  39371. chart.pointCount < 100
  39372. )
  39373. );
  39374. }
  39375. },
  39376. /**
  39377. * Pan the chart by dragging the mouse across the pane. This function is
  39378. * called on mouse move, and the distance to pan is computed from chartX
  39379. * compared to the first chartX position in the dragging operation.
  39380. *
  39381. * @private
  39382. * @function Highcharts.Chart#pan
  39383. *
  39384. * @param {Highcharts.PointerEventObject} e
  39385. *
  39386. * @param {string} panning
  39387. */
  39388. pan: function (e, panning) {
  39389. var chart = this,
  39390. hoverPoints = chart.hoverPoints,
  39391. doRedraw;
  39392. fireEvent(this, 'pan', { originalEvent: e }, function () {
  39393. // remove active points for shared tooltip
  39394. if (hoverPoints) {
  39395. hoverPoints.forEach(function (point) {
  39396. point.setState();
  39397. });
  39398. }
  39399. // xy is used in maps
  39400. (panning === 'xy' ? [1, 0] : [1]).forEach(function (isX) {
  39401. var axis = chart[isX ? 'xAxis' : 'yAxis'][0],
  39402. horiz = axis.horiz,
  39403. mousePos = e[horiz ? 'chartX' : 'chartY'],
  39404. mouseDown = horiz ? 'mouseDownX' : 'mouseDownY',
  39405. startPos = chart[mouseDown],
  39406. halfPointRange = (axis.pointRange || 0) / 2,
  39407. pointRangeDirection =
  39408. (axis.reversed && !chart.inverted) ||
  39409. (!axis.reversed && chart.inverted) ?
  39410. -1 :
  39411. 1,
  39412. extremes = axis.getExtremes(),
  39413. panMin = axis.toValue(startPos - mousePos, true) +
  39414. halfPointRange * pointRangeDirection,
  39415. panMax =
  39416. axis.toValue(
  39417. startPos + axis.len - mousePos, true
  39418. ) -
  39419. halfPointRange * pointRangeDirection,
  39420. flipped = panMax < panMin,
  39421. newMin = flipped ? panMax : panMin,
  39422. newMax = flipped ? panMin : panMax,
  39423. paddedMin = Math.min(
  39424. extremes.dataMin,
  39425. halfPointRange ?
  39426. extremes.min :
  39427. axis.toValue(
  39428. axis.toPixels(extremes.min) -
  39429. axis.minPixelPadding
  39430. )
  39431. ),
  39432. paddedMax = Math.max(
  39433. extremes.dataMax,
  39434. halfPointRange ?
  39435. extremes.max :
  39436. axis.toValue(
  39437. axis.toPixels(extremes.max) +
  39438. axis.minPixelPadding
  39439. )
  39440. ),
  39441. spill;
  39442. // If the new range spills over, either to the min or max,
  39443. // adjust the new range.
  39444. spill = paddedMin - newMin;
  39445. if (spill > 0) {
  39446. newMax += spill;
  39447. newMin = paddedMin;
  39448. }
  39449. spill = newMax - paddedMax;
  39450. if (spill > 0) {
  39451. newMax = paddedMax;
  39452. newMin -= spill;
  39453. }
  39454. // Set new extremes if they are actually new
  39455. if (
  39456. axis.series.length &&
  39457. newMin !== extremes.min &&
  39458. newMax !== extremes.max
  39459. ) {
  39460. axis.setExtremes(
  39461. newMin,
  39462. newMax,
  39463. false,
  39464. false,
  39465. { trigger: 'pan' }
  39466. );
  39467. doRedraw = true;
  39468. }
  39469. chart[mouseDown] = mousePos; // set new reference for next run
  39470. });
  39471. if (doRedraw) {
  39472. chart.redraw(false);
  39473. }
  39474. css(chart.container, { cursor: 'move' });
  39475. });
  39476. }
  39477. });
  39478. // Extend the Point object with interaction
  39479. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  39480. /**
  39481. * Toggle the selection status of a point.
  39482. *
  39483. * @see Highcharts.Chart#getSelectedPoints
  39484. *
  39485. * @sample highcharts/members/point-select/
  39486. * Select a point from a button
  39487. * @sample highcharts/chart/events-selection-points/
  39488. * Select a range of points through a drag selection
  39489. * @sample maps/series/data-id/
  39490. * Select a point in Highmaps
  39491. *
  39492. * @function Highcharts.Point#select
  39493. *
  39494. * @param {boolean} [selected]
  39495. * When `true`, the point is selected. When `false`, the point is
  39496. * unselected. When `null` or `undefined`, the selection state is
  39497. * toggled.
  39498. *
  39499. * @param {boolean} [accumulate=false]
  39500. * When `true`, the selection is added to other selected points.
  39501. * When `false`, other selected points are deselected. Internally in
  39502. * Highcharts, when
  39503. * [allowPointSelect](http://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
  39504. * is `true`, selected points are accumulated on Control, Shift or
  39505. * Cmd clicking the point.
  39506. *
  39507. * @fires Highcharts.Point#event:select
  39508. * @fires Highcharts.Point#event:unselect
  39509. */
  39510. select: function (selected, accumulate) {
  39511. var point = this,
  39512. series = point.series,
  39513. chart = series.chart;
  39514. selected = pick(selected, !point.selected);
  39515. // fire the event with the default handler
  39516. point.firePointEvent(
  39517. selected ? 'select' : 'unselect',
  39518. { accumulate: accumulate },
  39519. function () {
  39520. /**
  39521. * Whether the point is selected or not.
  39522. *
  39523. * @see Point#select
  39524. * @see Chart#getSelectedPoints
  39525. *
  39526. * @name Highcharts.Point#selected
  39527. * @type {boolean}
  39528. */
  39529. point.selected = point.options.selected = selected;
  39530. series.options.data[series.data.indexOf(point)] =
  39531. point.options;
  39532. point.setState(selected && 'select');
  39533. // unselect all other points unless Ctrl or Cmd + click
  39534. if (!accumulate) {
  39535. chart.getSelectedPoints().forEach(function (loopPoint) {
  39536. if (loopPoint.selected && loopPoint !== point) {
  39537. loopPoint.selected = loopPoint.options.selected =
  39538. false;
  39539. series.options.data[
  39540. series.data.indexOf(loopPoint)
  39541. ] = loopPoint.options;
  39542. loopPoint.setState('');
  39543. loopPoint.firePointEvent('unselect');
  39544. }
  39545. });
  39546. }
  39547. }
  39548. );
  39549. },
  39550. /**
  39551. * Runs on mouse over the point. Called internally from mouse and touch
  39552. * events.
  39553. *
  39554. * @function Highcharts.Point#onMouseOver
  39555. *
  39556. * @param {Highcharts.PointerEventObject} e
  39557. * The event arguments.
  39558. */
  39559. onMouseOver: function (e) {
  39560. var point = this,
  39561. series = point.series,
  39562. chart = series.chart,
  39563. pointer = chart.pointer;
  39564. e = e ?
  39565. pointer.normalize(e) :
  39566. // In cases where onMouseOver is called directly without an event
  39567. pointer.getChartCoordinatesFromPoint(point, chart.inverted);
  39568. pointer.runPointActions(e, point);
  39569. },
  39570. /**
  39571. * Runs on mouse out from the point. Called internally from mouse and touch
  39572. * events.
  39573. *
  39574. * @function Highcharts.Point#onMouseOut
  39575. *
  39576. * @fires Highcharts.Point#event:mouseOut
  39577. */
  39578. onMouseOut: function () {
  39579. var point = this,
  39580. chart = point.series.chart;
  39581. point.firePointEvent('mouseOut');
  39582. (chart.hoverPoints || []).forEach(function (p) {
  39583. p.setState();
  39584. });
  39585. chart.hoverPoints = chart.hoverPoint = null;
  39586. },
  39587. /**
  39588. * Import events from the series' and point's options. Only do it on
  39589. * demand, to save processing time on hovering.
  39590. *
  39591. * @private
  39592. * @function Highcharts.Point#importEvents
  39593. */
  39594. importEvents: function () {
  39595. if (!this.hasImportedEvents) {
  39596. var point = this,
  39597. options = merge(point.series.options.point, point.options),
  39598. events = options.events;
  39599. point.events = events;
  39600. H.objectEach(events, function (event, eventType) {
  39601. addEvent(point, eventType, event);
  39602. });
  39603. this.hasImportedEvents = true;
  39604. }
  39605. },
  39606. /**
  39607. * Set the point's state.
  39608. *
  39609. * @function Highcharts.Point#setState
  39610. *
  39611. * @param {string} [state]
  39612. * The new state, can be one of `''` (an empty string), `hover` or
  39613. * `select`.
  39614. *
  39615. * @param {boolean} [move]
  39616. * State for animation.
  39617. *
  39618. * @fires Highcharts.Point#event:afterSetState
  39619. */
  39620. setState: function (state, move) {
  39621. var point = this,
  39622. plotX = Math.floor(point.plotX), // #4586
  39623. plotY = point.plotY,
  39624. series = point.series,
  39625. stateOptions = series.options.states[state || 'normal'] || {},
  39626. markerOptions = defaultPlotOptions[series.type].marker &&
  39627. series.options.marker,
  39628. normalDisabled = markerOptions && markerOptions.enabled === false,
  39629. markerStateOptions = (
  39630. markerOptions &&
  39631. markerOptions.states &&
  39632. markerOptions.states[state || 'normal']
  39633. ) || {},
  39634. stateDisabled = markerStateOptions.enabled === false,
  39635. stateMarkerGraphic = series.stateMarkerGraphic,
  39636. pointMarker = point.marker || {},
  39637. chart = series.chart,
  39638. halo = series.halo,
  39639. haloOptions,
  39640. markerAttribs,
  39641. hasMarkers = markerOptions && series.markerAttribs,
  39642. newSymbol;
  39643. state = state || ''; // empty string
  39644. if (
  39645. // already has this state
  39646. (state === point.state && !move) ||
  39647. // selected points don't respond to hover
  39648. (point.selected && state !== 'select') ||
  39649. // series' state options is disabled
  39650. (stateOptions.enabled === false) ||
  39651. // general point marker's state options is disabled
  39652. (state && (
  39653. stateDisabled ||
  39654. (normalDisabled && markerStateOptions.enabled === false)
  39655. )) ||
  39656. // individual point marker's state options is disabled
  39657. (
  39658. state &&
  39659. pointMarker.states &&
  39660. pointMarker.states[state] &&
  39661. pointMarker.states[state].enabled === false
  39662. ) // #1610
  39663. ) {
  39664. return;
  39665. }
  39666. if (hasMarkers) {
  39667. markerAttribs = series.markerAttribs(point, state);
  39668. }
  39669. // Apply hover styles to the existing point
  39670. if (point.graphic) {
  39671. if (point.state) {
  39672. point.graphic.removeClass('highcharts-point-' + point.state);
  39673. }
  39674. if (state) {
  39675. point.graphic.addClass('highcharts-point-' + state);
  39676. }
  39677. if (!chart.styledMode) {
  39678. point.graphic.animate(
  39679. series.pointAttribs(point, state),
  39680. pick(
  39681. chart.options.chart.animation,
  39682. stateOptions.animation
  39683. )
  39684. );
  39685. }
  39686. if (markerAttribs) {
  39687. point.graphic.animate(
  39688. markerAttribs,
  39689. pick(
  39690. chart.options.chart.animation, // Turn off globally
  39691. markerStateOptions.animation,
  39692. markerOptions.animation
  39693. )
  39694. );
  39695. }
  39696. // Zooming in from a range with no markers to a range with markers
  39697. if (stateMarkerGraphic) {
  39698. stateMarkerGraphic.hide();
  39699. }
  39700. } else {
  39701. // if a graphic is not applied to each point in the normal state,
  39702. // create a shared graphic for the hover state
  39703. if (state && markerStateOptions) {
  39704. newSymbol = pointMarker.symbol || series.symbol;
  39705. // If the point has another symbol than the previous one, throw
  39706. // away the state marker graphic and force a new one (#1459)
  39707. if (
  39708. stateMarkerGraphic &&
  39709. stateMarkerGraphic.currentSymbol !== newSymbol
  39710. ) {
  39711. stateMarkerGraphic = stateMarkerGraphic.destroy();
  39712. }
  39713. // Add a new state marker graphic
  39714. if (!stateMarkerGraphic) {
  39715. if (newSymbol) {
  39716. series.stateMarkerGraphic = stateMarkerGraphic =
  39717. chart.renderer.symbol(
  39718. newSymbol,
  39719. markerAttribs.x,
  39720. markerAttribs.y,
  39721. markerAttribs.width,
  39722. markerAttribs.height
  39723. )
  39724. .add(series.markerGroup);
  39725. stateMarkerGraphic.currentSymbol = newSymbol;
  39726. }
  39727. // Move the existing graphic
  39728. } else {
  39729. stateMarkerGraphic[move ? 'animate' : 'attr']({ // #1054
  39730. x: markerAttribs.x,
  39731. y: markerAttribs.y
  39732. });
  39733. }
  39734. if (!chart.styledMode && stateMarkerGraphic) {
  39735. stateMarkerGraphic.attr(series.pointAttribs(point, state));
  39736. }
  39737. }
  39738. if (stateMarkerGraphic) {
  39739. stateMarkerGraphic[
  39740. state && chart.isInsidePlot(plotX, plotY, chart.inverted) ?
  39741. 'show' :
  39742. 'hide'
  39743. ](); // #2450
  39744. stateMarkerGraphic.element.point = point; // #4310
  39745. }
  39746. }
  39747. // Show me your halo
  39748. haloOptions = stateOptions.halo;
  39749. if (haloOptions && haloOptions.size) {
  39750. if (!halo) {
  39751. series.halo = halo = chart.renderer.path()
  39752. // #5818, #5903, #6705
  39753. .add((point.graphic || stateMarkerGraphic).parentGroup);
  39754. }
  39755. halo.show()[move ? 'animate' : 'attr']({
  39756. d: point.haloPath(haloOptions.size)
  39757. });
  39758. halo.attr({
  39759. 'class': 'highcharts-halo highcharts-color-' +
  39760. pick(point.colorIndex, series.colorIndex) +
  39761. (point.className ? ' ' + point.className : ''),
  39762. 'zIndex': -1 // #4929, #8276
  39763. });
  39764. halo.point = point; // #6055
  39765. if (!chart.styledMode) {
  39766. halo.attr(extend({
  39767. 'fill': point.color || series.color,
  39768. 'fill-opacity': haloOptions.opacity
  39769. }, haloOptions.attributes));
  39770. }
  39771. } else if (halo && halo.point && halo.point.haloPath) {
  39772. // Animate back to 0 on the current halo point (#6055)
  39773. halo.animate(
  39774. { d: halo.point.haloPath(0) },
  39775. null,
  39776. // Hide after unhovering. The `complete` callback runs in the
  39777. // halo's context (#7681).
  39778. halo.hide
  39779. );
  39780. }
  39781. point.state = state;
  39782. fireEvent(point, 'afterSetState');
  39783. },
  39784. /**
  39785. * Get the path definition for the halo, which is usually a shadow-like
  39786. * circle around the currently hovered point.
  39787. *
  39788. * @function Highcharts.Point#haloPath
  39789. *
  39790. * @param {number} size
  39791. * The radius of the circular halo.
  39792. *
  39793. * @return {Highcharts.SVGPathArray}
  39794. * The path definition.
  39795. */
  39796. haloPath: function (size) {
  39797. var series = this.series,
  39798. chart = series.chart;
  39799. return chart.renderer.symbols.circle(
  39800. Math.floor(this.plotX) - size,
  39801. this.plotY - size,
  39802. size * 2,
  39803. size * 2
  39804. );
  39805. }
  39806. });
  39807. // Extend the Series object with interaction
  39808. extend(Series.prototype, /** @lends Highcharts.Series.prototype */ {
  39809. /**
  39810. * Runs on mouse over the series graphical items.
  39811. *
  39812. * @function Highcharts.Series#onMouseOver
  39813. *
  39814. * @fires Highcharts.Series#event:mouseOver
  39815. */
  39816. onMouseOver: function () {
  39817. var series = this,
  39818. chart = series.chart,
  39819. hoverSeries = chart.hoverSeries;
  39820. // set normal state to previous series
  39821. if (hoverSeries && hoverSeries !== series) {
  39822. hoverSeries.onMouseOut();
  39823. }
  39824. // trigger the event, but to save processing time,
  39825. // only if defined
  39826. if (series.options.events.mouseOver) {
  39827. fireEvent(series, 'mouseOver');
  39828. }
  39829. // hover this
  39830. series.setState('hover');
  39831. chart.hoverSeries = series;
  39832. },
  39833. /**
  39834. * Runs on mouse out of the series graphical items.
  39835. *
  39836. * @function Highcharts.Series#onMouseOut
  39837. *
  39838. * @fires Highcharts.Series#event:mouseOut
  39839. */
  39840. onMouseOut: function () {
  39841. // trigger the event only if listeners exist
  39842. var series = this,
  39843. options = series.options,
  39844. chart = series.chart,
  39845. tooltip = chart.tooltip,
  39846. hoverPoint = chart.hoverPoint;
  39847. // #182, set to null before the mouseOut event fires
  39848. chart.hoverSeries = null;
  39849. // trigger mouse out on the point, which must be in this series
  39850. if (hoverPoint) {
  39851. hoverPoint.onMouseOut();
  39852. }
  39853. // fire the mouse out event
  39854. if (series && options.events.mouseOut) {
  39855. fireEvent(series, 'mouseOut');
  39856. }
  39857. // hide the tooltip
  39858. if (
  39859. tooltip &&
  39860. !series.stickyTracking &&
  39861. (!tooltip.shared || series.noSharedTooltip)
  39862. ) {
  39863. tooltip.hide();
  39864. }
  39865. // set normal state
  39866. series.setState();
  39867. },
  39868. /**
  39869. * Set the state of the series. Called internally on mouse interaction
  39870. * operations, but it can also be called directly to visually
  39871. * highlight a series.
  39872. *
  39873. * @function Highcharts.Series#setState
  39874. *
  39875. * @param {string} [state]
  39876. * Can be either `hover` or undefined to set to normal state.
  39877. */
  39878. setState: function (state) {
  39879. var series = this,
  39880. options = series.options,
  39881. graph = series.graph,
  39882. stateOptions = options.states,
  39883. lineWidth = options.lineWidth,
  39884. attribs,
  39885. i = 0;
  39886. state = state || '';
  39887. if (series.state !== state) {
  39888. // Toggle class names
  39889. [
  39890. series.group,
  39891. series.markerGroup,
  39892. series.dataLabelsGroup
  39893. ].forEach(function (group) {
  39894. if (group) {
  39895. // Old state
  39896. if (series.state) {
  39897. group.removeClass('highcharts-series-' + series.state);
  39898. }
  39899. // New state
  39900. if (state) {
  39901. group.addClass('highcharts-series-' + state);
  39902. }
  39903. }
  39904. });
  39905. series.state = state;
  39906. if (!series.chart.styledMode) {
  39907. if (
  39908. stateOptions[state] &&
  39909. stateOptions[state].enabled === false
  39910. ) {
  39911. return;
  39912. }
  39913. if (state) {
  39914. lineWidth = (
  39915. stateOptions[state].lineWidth ||
  39916. lineWidth + (stateOptions[state].lineWidthPlus || 0)
  39917. ); // #4035
  39918. }
  39919. if (graph && !graph.dashstyle) {
  39920. attribs = {
  39921. 'stroke-width': lineWidth
  39922. };
  39923. // Animate the graph stroke-width. By default a quick
  39924. // animation to hover, slower to un-hover.
  39925. graph.animate(
  39926. attribs,
  39927. pick(
  39928. (
  39929. stateOptions[state || 'normal'] &&
  39930. stateOptions[state || 'normal'].animation
  39931. ),
  39932. series.chart.options.chart.animation
  39933. )
  39934. );
  39935. while (series['zone-graph-' + i]) {
  39936. series['zone-graph-' + i].attr(attribs);
  39937. i = i + 1;
  39938. }
  39939. }
  39940. }
  39941. }
  39942. },
  39943. /**
  39944. * Show or hide the series.
  39945. *
  39946. * @function Highcharts.Series#setVisible
  39947. *
  39948. * @param {boolean} [visible]
  39949. * True to show the series, false to hide. If undefined, the
  39950. * visibility is toggled.
  39951. *
  39952. * @param {boolean} [redraw=true]
  39953. * Whether to redraw the chart after the series is altered. If doing
  39954. * more operations on the chart, it is a good idea to set redraw to
  39955. * false and call {@link Chart#redraw|chart.redraw()} after.
  39956. *
  39957. * @fires Highcharts.Series#event:hide
  39958. * @fires Highcharts.Series#event:show
  39959. */
  39960. setVisible: function (vis, redraw) {
  39961. var series = this,
  39962. chart = series.chart,
  39963. legendItem = series.legendItem,
  39964. showOrHide,
  39965. ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
  39966. oldVisibility = series.visible;
  39967. // if called without an argument, toggle visibility
  39968. series.visible =
  39969. vis =
  39970. series.options.visible =
  39971. series.userOptions.visible =
  39972. vis === undefined ? !oldVisibility : vis; // #5618
  39973. showOrHide = vis ? 'show' : 'hide';
  39974. // show or hide elements
  39975. [
  39976. 'group',
  39977. 'dataLabelsGroup',
  39978. 'markerGroup',
  39979. 'tracker',
  39980. 'tt'
  39981. ].forEach(function (key) {
  39982. if (series[key]) {
  39983. series[key][showOrHide]();
  39984. }
  39985. });
  39986. // hide tooltip (#1361)
  39987. if (
  39988. chart.hoverSeries === series ||
  39989. (chart.hoverPoint && chart.hoverPoint.series) === series
  39990. ) {
  39991. series.onMouseOut();
  39992. }
  39993. if (legendItem) {
  39994. chart.legend.colorizeItem(series, vis);
  39995. }
  39996. // rescale or adapt to resized chart
  39997. series.isDirty = true;
  39998. // in a stack, all other series are affected
  39999. if (series.options.stacking) {
  40000. chart.series.forEach(function (otherSeries) {
  40001. if (otherSeries.options.stacking && otherSeries.visible) {
  40002. otherSeries.isDirty = true;
  40003. }
  40004. });
  40005. }
  40006. // show or hide linked series
  40007. series.linkedSeries.forEach(function (otherSeries) {
  40008. otherSeries.setVisible(vis, false);
  40009. });
  40010. if (ignoreHiddenSeries) {
  40011. chart.isDirtyBox = true;
  40012. }
  40013. fireEvent(series, showOrHide);
  40014. if (redraw !== false) {
  40015. chart.redraw();
  40016. }
  40017. },
  40018. /**
  40019. * Show the series if hidden.
  40020. *
  40021. * @sample highcharts/members/series-hide/
  40022. * Toggle visibility from a button
  40023. *
  40024. * @function Highcharts.Series#show
  40025. *
  40026. * @fires Highcharts.Series#event:show
  40027. */
  40028. show: function () {
  40029. this.setVisible(true);
  40030. },
  40031. /**
  40032. * Hide the series if visible. If the {@link
  40033. * https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries|
  40034. * chart.ignoreHiddenSeries} option is true, the chart is redrawn without
  40035. * this series.
  40036. *
  40037. * @sample highcharts/members/series-hide/
  40038. * Toggle visibility from a button
  40039. *
  40040. * @function Highcharts.Series#hide
  40041. *
  40042. * @fires Highcharts.Series#event:hide
  40043. */
  40044. hide: function () {
  40045. this.setVisible(false);
  40046. },
  40047. /**
  40048. * Select or unselect the series. This means its
  40049. * {@link Highcharts.Series.selected|selected}
  40050. * property is set, the checkbox in the legend is toggled and when selected,
  40051. * the series is returned by the
  40052. * {@link Highcharts.Chart#getSelectedSeries}
  40053. * function.
  40054. *
  40055. * @sample highcharts/members/series-select/
  40056. * Select a series from a button
  40057. *
  40058. * @function Highcharts.Series#select
  40059. *
  40060. * @param {boolean} [selected]
  40061. * True to select the series, false to unselect. If undefined, the
  40062. * selection state is toggled.
  40063. *
  40064. * @fires Highcharts.Series#event:select
  40065. * @fires Highcharts.Series#event:unselect
  40066. */
  40067. select: function (selected) {
  40068. var series = this;
  40069. series.selected =
  40070. selected =
  40071. this.options.selected = (
  40072. selected === undefined ?
  40073. !series.selected :
  40074. selected
  40075. );
  40076. if (series.checkbox) {
  40077. series.checkbox.checked = selected;
  40078. }
  40079. fireEvent(series, selected ? 'select' : 'unselect');
  40080. },
  40081. /**
  40082. * @private
  40083. * @borrows Highcharts.TrackerMixin.drawTrackerGraph as Highcharts.Series#drawTracker
  40084. */
  40085. drawTracker: TrackerMixin.drawTrackerGraph
  40086. });
  40087. }(Highcharts));
  40088. (function (H) {
  40089. /**
  40090. * (c) 2010-2019 Torstein Honsi
  40091. *
  40092. * License: www.highcharts.com/license
  40093. */
  40094. /**
  40095. * A callback function to gain complete control on when the responsive rule
  40096. * applies.
  40097. *
  40098. * @callback Highcharts.ResponsiveCallbackFunction
  40099. *
  40100. * @return {boolean}
  40101. * Return `true` if it applies.
  40102. */
  40103. var Chart = H.Chart,
  40104. isArray = H.isArray,
  40105. isObject = H.isObject,
  40106. pick = H.pick,
  40107. splat = H.splat;
  40108. /**
  40109. * Allows setting a set of rules to apply for different screen or chart
  40110. * sizes. Each rule specifies additional chart options.
  40111. *
  40112. * @sample {highstock} stock/demo/responsive/
  40113. * Stock chart
  40114. * @sample highcharts/responsive/axis/
  40115. * Axis
  40116. * @sample highcharts/responsive/legend/
  40117. * Legend
  40118. * @sample highcharts/responsive/classname/
  40119. * Class name
  40120. *
  40121. * @since 5.0.0
  40122. * @apioption responsive
  40123. */
  40124. /**
  40125. * A set of rules for responsive settings. The rules are executed from
  40126. * the top down.
  40127. *
  40128. * @sample {highcharts} highcharts/responsive/axis/
  40129. * Axis changes
  40130. * @sample {highstock} highcharts/responsive/axis/
  40131. * Axis changes
  40132. * @sample {highmaps} highcharts/responsive/axis/
  40133. * Axis changes
  40134. *
  40135. * @type {Array<*>}
  40136. * @since 5.0.0
  40137. * @apioption responsive.rules
  40138. */
  40139. /**
  40140. * A full set of chart options to apply as overrides to the general
  40141. * chart options. The chart options are applied when the given rule
  40142. * is active.
  40143. *
  40144. * A special case is configuration objects that take arrays, for example
  40145. * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
  40146. * collections, an `id` option is used to map the new option set to
  40147. * an existing object. If an existing object of the same id is not found,
  40148. * the item of the same indexupdated. So for example, setting `chartOptions`
  40149. * with two series items without an `id`, will cause the existing chart's
  40150. * two series to be updated with respective options.
  40151. *
  40152. * @sample {highstock} stock/demo/responsive/
  40153. * Stock chart
  40154. * @sample highcharts/responsive/axis/
  40155. * Axis
  40156. * @sample highcharts/responsive/legend/
  40157. * Legend
  40158. * @sample highcharts/responsive/classname/
  40159. * Class name
  40160. *
  40161. * @type {Highcharts.Options}
  40162. * @since 5.0.0
  40163. * @apioption responsive.rules.chartOptions
  40164. */
  40165. /**
  40166. * Under which conditions the rule applies.
  40167. *
  40168. * @since 5.0.0
  40169. * @apioption responsive.rules.condition
  40170. */
  40171. /**
  40172. * A callback function to gain complete control on when the responsive
  40173. * rule applies. Return `true` if it applies. This opens for checking
  40174. * against other metrics than the chart size, or example the document
  40175. * size or other elements.
  40176. *
  40177. * @type {Highcharts.ResponsiveCallbackFunction}
  40178. * @since 5.0.0
  40179. * @context Highcharts.Chart
  40180. * @apioption responsive.rules.condition.callback
  40181. */
  40182. /**
  40183. * The responsive rule applies if the chart height is less than this.
  40184. *
  40185. * @type {number}
  40186. * @since 5.0.0
  40187. * @apioption responsive.rules.condition.maxHeight
  40188. */
  40189. /**
  40190. * The responsive rule applies if the chart width is less than this.
  40191. *
  40192. * @sample highcharts/responsive/axis/
  40193. * Max width is 500
  40194. *
  40195. * @type {number}
  40196. * @since 5.0.0
  40197. * @apioption responsive.rules.condition.maxWidth
  40198. */
  40199. /**
  40200. * The responsive rule applies if the chart height is greater than this.
  40201. *
  40202. * @type {number}
  40203. * @default 0
  40204. * @since 5.0.0
  40205. * @apioption responsive.rules.condition.minHeight
  40206. */
  40207. /**
  40208. * The responsive rule applies if the chart width is greater than this.
  40209. *
  40210. * @type {number}
  40211. * @default 0
  40212. * @since 5.0.0
  40213. * @apioption responsive.rules.condition.minWidth
  40214. */
  40215. /**
  40216. * Update the chart based on the current chart/document size and options for
  40217. * responsiveness.
  40218. *
  40219. * @private
  40220. * @function Highcharts.Chart#setResponsive
  40221. *
  40222. * @param {boolean} [redraw=true]
  40223. * @param {Array} [reset=false]
  40224. * Reset by un-applying all rules. Chart.update resets all rules before
  40225. * applying updated options.
  40226. */
  40227. Chart.prototype.setResponsive = function (redraw, reset) {
  40228. var options = this.options.responsive,
  40229. ruleIds = [],
  40230. currentResponsive = this.currentResponsive,
  40231. currentRuleIds,
  40232. undoOptions;
  40233. if (!reset && options && options.rules) {
  40234. options.rules.forEach(function (rule) {
  40235. if (rule._id === undefined) {
  40236. rule._id = H.uniqueKey();
  40237. }
  40238. this.matchResponsiveRule(rule, ruleIds, redraw);
  40239. }, this);
  40240. }
  40241. // Merge matching rules
  40242. var mergedOptions = H.merge.apply(0, ruleIds.map(function (ruleId) {
  40243. return H.find(options.rules, function (rule) {
  40244. return rule._id === ruleId;
  40245. }).chartOptions;
  40246. }));
  40247. mergedOptions.isResponsiveOptions = true;
  40248. // Stringified key for the rules that currently apply.
  40249. ruleIds = ruleIds.toString() || undefined;
  40250. currentRuleIds = currentResponsive && currentResponsive.ruleIds;
  40251. // Changes in what rules apply
  40252. if (ruleIds !== currentRuleIds) {
  40253. // Undo previous rules. Before we apply a new set of rules, we need to
  40254. // roll back completely to base options (#6291).
  40255. if (currentResponsive) {
  40256. this.update(currentResponsive.undoOptions, redraw);
  40257. }
  40258. if (ruleIds) {
  40259. // Get undo-options for matching rules
  40260. undoOptions = this.currentOptions(mergedOptions);
  40261. undoOptions.isResponsiveOptions = true;
  40262. this.currentResponsive = {
  40263. ruleIds: ruleIds,
  40264. mergedOptions: mergedOptions,
  40265. undoOptions: undoOptions
  40266. };
  40267. this.update(mergedOptions, redraw);
  40268. } else {
  40269. this.currentResponsive = undefined;
  40270. }
  40271. }
  40272. };
  40273. /**
  40274. * Handle a single responsiveness rule.
  40275. *
  40276. * @private
  40277. * @function Highcharts.Chart#matchResponsiveRule
  40278. *
  40279. * @param {Highcharts.ResponsiveRulesConditionOptions} rule
  40280. *
  40281. * @param {Array<number>} matches
  40282. */
  40283. Chart.prototype.matchResponsiveRule = function (rule, matches) {
  40284. var condition = rule.condition,
  40285. fn = condition.callback || function () {
  40286. return (
  40287. this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
  40288. this.chartHeight <=
  40289. pick(condition.maxHeight, Number.MAX_VALUE) &&
  40290. this.chartWidth >= pick(condition.minWidth, 0) &&
  40291. this.chartHeight >= pick(condition.minHeight, 0)
  40292. );
  40293. };
  40294. if (fn.call(this)) {
  40295. matches.push(rule._id);
  40296. }
  40297. };
  40298. /**
  40299. * Get the current values for a given set of options. Used before we update
  40300. * the chart with a new responsiveness rule.
  40301. * TODO: Restore axis options (by id?)
  40302. *
  40303. * @private
  40304. * @function Highcharts.Chart#currentOptions
  40305. *
  40306. * @param {Highcharts.Options} options
  40307. *
  40308. * @return {Highcharts.Options}
  40309. */
  40310. Chart.prototype.currentOptions = function (options) {
  40311. var ret = {};
  40312. /**
  40313. * Recurse over a set of options and its current values,
  40314. * and store the current values in the ret object.
  40315. */
  40316. function getCurrent(options, curr, ret, depth) {
  40317. var i;
  40318. H.objectEach(options, function (val, key) {
  40319. if (!depth && ['series', 'xAxis', 'yAxis'].indexOf(key) > -1) {
  40320. val = splat(val);
  40321. ret[key] = [];
  40322. // Iterate over collections like series, xAxis or yAxis and map
  40323. // the items by index.
  40324. for (i = 0; i < val.length; i++) {
  40325. if (curr[key][i]) { // Item exists in current data (#6347)
  40326. ret[key][i] = {};
  40327. getCurrent(
  40328. val[i],
  40329. curr[key][i],
  40330. ret[key][i],
  40331. depth + 1
  40332. );
  40333. }
  40334. }
  40335. } else if (isObject(val)) {
  40336. ret[key] = isArray(val) ? [] : {};
  40337. getCurrent(val, curr[key] || {}, ret[key], depth + 1);
  40338. } else {
  40339. ret[key] = curr[key] || null;
  40340. }
  40341. });
  40342. }
  40343. getCurrent(options, this.options, ret, 0);
  40344. return ret;
  40345. };
  40346. }(Highcharts));
  40347. return (function (Highcharts) {
  40348. return Highcharts;
  40349. }(Highcharts));
  40350. }));