123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733 |
- /**
- * @license Highcharts JS v7.0.2 (2019-01-17)
- *
- * (c) 2009-2018 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- 'use strict';
- (function (root, factory) {
- if (typeof module === 'object' && module.exports) {
- factory['default'] = factory;
- module.exports = root.document ?
- factory(root) :
- factory;
- } else if (typeof define === 'function' && define.amd) {
- define(function () {
- return factory(root);
- });
- } else {
- root.Highcharts = factory(root);
- }
- }(typeof window !== 'undefined' ? window : this, function (win) {
- var Highcharts = (function () {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Reference to the global SVGElement class as a workaround for a name conflict
- * in the Highcharts namespace.
- *
- * @global
- * @typedef {global.SVGElement} GlobalSVGElement
- *
- * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
- */
- /* global win, window */
- // glob is a temporary fix to allow our es-modules to work.
- var glob = typeof win === 'undefined' ? window : win,
- doc = glob.document,
- SVG_NS = 'http://www.w3.org/2000/svg',
- userAgent = (glob.navigator && glob.navigator.userAgent) || '',
- svg = (
- doc &&
- doc.createElementNS &&
- !!doc.createElementNS(SVG_NS, 'svg').createSVGRect
- ),
- isMS = /(edge|msie|trident)/i.test(userAgent) && !glob.opera,
- isFirefox = userAgent.indexOf('Firefox') !== -1,
- isChrome = userAgent.indexOf('Chrome') !== -1,
- hasBidiBug = (
- isFirefox &&
- parseInt(userAgent.split('Firefox/')[1], 10) < 4 // issue #38
- );
- var Highcharts = glob.Highcharts ? glob.Highcharts.error(16, true) : {
- product: 'Highcharts',
- version: '7.0.2',
- deg2rad: Math.PI * 2 / 360,
- doc: doc,
- hasBidiBug: hasBidiBug,
- hasTouch: doc && doc.documentElement.ontouchstart !== undefined,
- isMS: isMS,
- isWebKit: userAgent.indexOf('AppleWebKit') !== -1,
- isFirefox: isFirefox,
- isChrome: isChrome,
- isSafari: !isChrome && userAgent.indexOf('Safari') !== -1,
- isTouchDevice: /(Mobile|Android|Windows Phone)/.test(userAgent),
- SVG_NS: SVG_NS,
- chartCount: 0,
- seriesTypes: {},
- symbolSizes: {},
- svg: svg,
- win: glob,
- marginNames: ['plotTop', 'marginRight', 'marginBottom', 'plotLeft'],
- noop: function () {
- return undefined;
- },
- /**
- * An array containing the current chart objects in the page. A chart's
- * position in the array is preserved throughout the page's lifetime. When
- * a chart is destroyed, the array item becomes `undefined`.
- *
- * @name Highcharts.charts
- * @type {Array<Highcharts.Chart>}
- */
- charts: []
- };
- return Highcharts;
- }());
- (function (H) {
- /* *
- *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- *
- * */
- /**
- * Reference to the global SVGElement class as a workaround for a name conflict
- * in the Highcharts namespace.
- *
- * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
- *
- * @global
- * @typedef {global.SVGElement} GlobalSVGElement
- */
- /**
- * An animation configuration. Animation configurations can also be defined as
- * booleans, where `false` turns off animation and `true` defaults to a duration
- * of 500ms.
- *
- * @interface Highcharts.AnimationOptionsObject
- *//**
- * A callback function to exectute when the animation finishes.
- * @name Highcharts.AnimationOptionsObject#complete
- * @type {Function|undefined}
- *//**
- * The animation duration in milliseconds.
- * @name Highcharts.AnimationOptionsObject#duration
- * @type {number}
- *//**
- * The name of an easing function as defined on the `Math` object.
- * @name Highcharts.AnimationOptionsObject#easing
- * @type {string|undefined}
- *//**
- * A callback function to execute on each step of each attribute or CSS property
- * that's being animated. The first argument contains information about the
- * animation and progress.
- * @name Highcharts.AnimationOptionsObject#step
- * @type {Function|undefined}
- */
- /**
- * A style object with camel case property names to define visual appearance of
- * a SVG element or HTML element. The properties can be whatever styles are
- * supported on the given SVG or HTML element.
- *
- * @example
- * {
- * fontFamily: 'monospace',
- * fontSize: '1.2em'
- * }
- *
- * @interface Highcharts.CSSObject
- *//**
- * @name Highcharts.CSSObject#[key:string]
- * @type {boolean|number|string|undefined}
- *//**
- * Background style for the element.
- * @name Highcharts.CSSObject#background
- * @type {string|undefined}
- *//**
- * Background color of the element.
- * @name Highcharts.CSSObject#backgroundColor
- * @type {Highcharts.ColorString|undefined}
- *//**
- * Border style for the element.
- * @name Highcharts.CSSObject#border
- * @type {string|undefined}
- *//**
- * Radius of the element border.
- * @name Highcharts.CSSObject#borderRadius
- * @type {number|undefined}
- *//**
- * Color used in the element. The "contrast" option is a Highcharts custom
- * property that results in black or white, depending on the background of the
- * element.
- * @name Highcharts.CSSObject#color
- * @type {"contrast"|Highcharts.ColorString|undefined}
- *//**
- * Style of the mouse cursor when resting over the element.
- * @name Highcharts.CSSObject#cursor
- * @type {Highcharts.CursorType|undefined}
- *//**
- * Font family of the element text. Multiple values have to be in decreasing
- * preference order and separated by comma.
- * @name Highcharts.CSSObject#fontFamily
- * @type {string|undefined}
- *//**
- * Font size of the element text.
- * @name Highcharts.CSSObject#fontSize
- * @type {string|undefined}
- *//**
- * Font weight of the element text.
- * @name Highcharts.CSSObject#fontWeight
- * @type {string|undefined}
- *//**
- * Height of the element.
- * @name Highcharts.CSSObject#height
- * @type {number|undefined}
- *//**
- * Width of the element border.
- * @name Highcharts.CSSObject#lineWidth
- * @type {number|undefined}
- *//**
- * Opacity of the element.
- * @name Highcharts.CSSObject#opacity
- * @type {number|undefined}
- *//**
- * Space around the element content.
- * @name Highcharts.CSSObject#padding
- * @type {string|undefined}
- *//**
- * Behaviour of the element when the mouse cursor rests over it.
- * @name Highcharts.CSSObject#pointerEvents
- * @type {string|undefined}
- *//**
- * Positioning of the element.
- * @name Highcharts.CSSObject#position
- * @type {string|undefined}
- *//**
- * Alignment of the element text.
- * @name Highcharts.CSSObject#textAlign
- * @type {string|undefined}
- *//**
- * Outline style of the element text.
- * @name Highcharts.CSSObject#textOutline
- * @type {string|undefined}
- *//**
- * Additional decoration of the element text.
- * @name Highcharts.CSSObject#textDecoration
- * @type {string|undefined}
- *//**
- * Line break style of the element text. Highcharts SVG elements support
- * `ellipsis` when a `width` is set.
- * @name Highcharts.CSSObject#textOverflow
- * @type {string|undefined}
- *//**
- * Top spacing of the element relative to the parent element.
- * @name Highcharts.CSSObject#top
- * @type {string|undefined}
- *//**
- * Animated transition of selected element properties.
- * @name Highcharts.CSSObject#transition
- * @type {string|undefined}
- *//**
- * Line break style of the element text.
- * @name Highcharts.CSSObject#whiteSpace
- * @type {string|undefined}
- *//**
- * Width of the element.
- * @name Highcharts.CSSObject#width
- * @type {number|undefined}
- */
- /**
- * All possible cursor styles.
- *
- * @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
- */
- /**
- * All possible dash styles.
- *
- * @typedef {"Dash"|"DashDot"|"Dot"|"LongDash"|"LongDashDot"|"LongDashDotDot"|"ShortDash"|"ShortDashDot"|"ShortDashDotDot"|"ShortDot"|"Solid"} Highcharts.DashStyleType
- */
- /**
- * Generic dictionary in TypeScript notation.
- *
- * @interface Highcharts.Dictionary<T>
- *//**
- * @name Highcharts.Dictionary<T>#[key:string]
- * @type {T}
- */
- /**
- * The function callback to execute when the event is fired. The `this` context
- * contains the instance, that fired the event.
- *
- * @callback Highcharts.EventCallbackFunction<T>
- *
- * @param {T} this
- *
- * @param {Highcharts.Dictionary<*>} [eventArguments]
- * Event arguments.
- */
- /**
- * The event options for adding function callback.
- *
- * @interface Highcharts.EventOptionsObject
- *//**
- * The order the event handler should be called. This opens for having one
- * handler be called before another, independent of in which order they were
- * added.
- * @name Highcharts.EventOptionsObject#order
- * @type {number}
- */
- /**
- * Formats data as a string. Usually the data is accessible throught the `this`
- * keyword.
- *
- * @callback Highcharts.FormatterCallbackFunction<T>
- *
- * @param {T} this
- *
- * @return {string}
- */
- /**
- * An object of key-value pairs for HTML attributes.
- *
- * @typedef {Highcharts.Dictionary<boolean|number|string>} Highcharts.HTMLAttributes
- */
- /**
- * An HTML DOM element. The type is a reference to the regular HTMLElement in
- * the global scope.
- *
- * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
- *
- * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
- */
- /**
- * The iterator callback.
- *
- * @callback Highcharts.ObjectEachCallbackFunction
- *
- * @param {*} value
- * The property value.
- *
- * @param {string} key
- * The property key.
- *
- * @param {*} obj
- * The object that objectEach is being applied to.
- */
- /**
- * An object containing `left` and `top` properties for the position in the
- * page.
- *
- * @interface Highcharts.OffsetObject
- *//**
- * Left distance to the page border.
- * @name Highcharts.OffsetObject#left
- * @type {number}
- *//**
- * Top distance to the page border.
- * @name Highcharts.OffsetObject#top
- * @type {number}
- */
- /**
- * If a number is given, it defines the pixel length. If a percentage string is
- * given, like for example `'50%'`, the setting defines a length relative to a
- * base size, for example the size of a container.
- *
- * @typedef {number|string} Highcharts.RelativeSize
- */
- /**
- * An object of key-value pairs for SVG attributes. Attributes in Highcharts
- * elements for the most parts correspond to SVG, but some are specific to
- * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
- * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
- * attributes containing a hyphen are _not_ camel-cased, they should be
- * quoted to preserve the hyphen.
- *
- * @example
- * {
- * 'stroke': '#ff0000', // basic
- * 'stroke-width': 2, // hyphenated
- * 'rotation': 45 // custom
- * 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
- * }
- *
- * @interface Highcharts.SVGAttributes
- *//**
- * @name Highcharts.SVGAttributes#[key:string]
- * @type {boolean|number|string|Array<number|string>|undefined}
- *//**
- * @name Highcharts.SVGAttributes#d
- * @type {string|Highcharts.SVGPathArray|undefined}
- *//**
- * @name Highcharts.SVGAttributes#inverted
- * @type {boolean|undefined}
- *//**
- * @name Highcharts.SVGAttributes#matrix
- * @type {Array<number>|undefined}
- *//**
- * @name Highcharts.SVGAttributes#stroke
- * @type {Highcharts.ColorString|undefined}
- *//**
- * @name Highcharts.SVGAttributes#rotation
- * @type {string|undefined}
- *//**
- * @name Highcharts.SVGAttributes#rotationOriginX
- * @type {number|undefined}
- *//**
- * @name Highcharts.SVGAttributes#rotationOriginY
- * @type {number|undefined}
- *//**
- * @name Highcharts.SVGAttributes#scaleX
- * @type {number|undefined}
- *//**
- * @name Highcharts.SVGAttributes#scaleY
- * @type {number|undefined}
- *//**
- * @name Highcharts.SVGAttributes#translateX
- * @type {number|undefined}
- *//**
- * @name Highcharts.SVGAttributes#translateY
- * @type {number|undefined}
- *//**
- * @name Highcharts.SVGAttributes#zIndex
- * @type {number|undefined}
- */
- /**
- * An SVG DOM element. The type is a reference to the regular SVGElement in the
- * global scope.
- *
- * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
- *
- * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
- */
- /**
- * Array of path commands, that will go into the `d` attribute of an SVG
- * element.
- *
- * @typedef {Array<number|Highcharts.SVGPathCommand>} Highcharts.SVGPathArray
- */
- /**
- * Possible path commands in a SVG path array.
- *
- * @typedef {string} Highcharts.SVGPathCommand
- * @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
- */
- /**
- * The Highcharts object is the placeholder for all other members, and various
- * utility functions. The most important member of the namespace would be the
- * chart constructor.
- *
- * @example
- * var chart = Highcharts.chart('container', { ... });
- *
- * @namespace Highcharts
- */
- H.timers = [];
- var charts = H.charts,
- doc = H.doc,
- win = H.win;
- /**
- * Provide error messages for debugging, with links to online explanation. This
- * function can be overridden to provide custom error handling.
- *
- * @sample highcharts/chart/highcharts-error/
- * Custom error handler
- *
- * @function Highcharts.error
- *
- * @param {number|string} code
- * The error code. See
- * [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
- * for available codes. If it is a string, the error message is printed
- * directly in the console.
- *
- * @param {boolean} [stop=false]
- * Whether to throw an error or just log a warning in the console.
- *
- * @param {Highcharts.Chart} [chart]
- * Reference to the chart that causes the error. Used in 'debugger'
- * module to display errors directly on the chart.
- * Important note: This argument is undefined for errors that lack
- * access to the Chart instance.
- */
- H.error = function (code, stop, chart) {
- var msg = H.isNumber(code) ?
- 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code :
- code;
- if (chart) {
- H.fireEvent(chart, 'displayError', { code: code });
- }
- if (stop) {
- throw new Error(msg);
- }
- // else ...
- if (win.console) {
- console.log(msg); // eslint-disable-line no-console
- }
- };
- /**
- * An animator object used internally. One instance applies to one property
- * (attribute or style prop) on one element. Animation is always initiated
- * through {@link SVGElement#animate}.
- *
- * @example
- * var rect = renderer.rect(0, 0, 10, 10).add();
- * rect.animate({ width: 100 });
- *
- * @private
- * @class Highcharts.Fx
- *
- * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
- * The element to animate.
- *
- * @param {Highcharts.AnimationOptionsObject} options
- * Animation options.
- *
- * @param {string} prop
- * The single attribute or CSS property to animate.
- */
- H.Fx = function (elem, options, prop) {
- this.options = options;
- this.elem = elem;
- this.prop = prop;
- };
- H.Fx.prototype = {
- /**
- * Set the current step of a path definition on SVGElement.
- *
- * @function Highcharts.Fx#dSetter
- */
- dSetter: function () {
- var start = this.paths[0],
- end = this.paths[1],
- ret = [],
- now = this.now,
- i = start.length,
- startVal;
- // Land on the final path without adjustment points appended in the ends
- if (now === 1) {
- ret = this.toD;
- } else if (i === end.length && now < 1) {
- while (i--) {
- startVal = parseFloat(start[i]);
- ret[i] =
- isNaN(startVal) ? // a letter instruction like M or L
- end[i] :
- now * (parseFloat(end[i] - startVal)) + startVal;
- }
- // If animation is finished or length not matching, land on right value
- } else {
- ret = end;
- }
- this.elem.attr('d', ret, null, true);
- },
- /**
- * Update the element with the current animation step.
- *
- * @function Highcharts.Fx#update
- */
- update: function () {
- var elem = this.elem,
- prop = this.prop, // if destroyed, it is null
- now = this.now,
- step = this.options.step;
- // Animation setter defined from outside
- if (this[prop + 'Setter']) {
- this[prop + 'Setter']();
- // Other animations on SVGElement
- } else if (elem.attr) {
- if (elem.element) {
- elem.attr(prop, now, null, true);
- }
- // HTML styles, raw HTML content like container size
- } else {
- elem.style[prop] = now + this.unit;
- }
- if (step) {
- step.call(elem, now, this);
- }
- },
- /**
- * Run an animation.
- *
- * @function Highcharts.Fx#run
- *
- * @param {number} from
- * The current value, value to start from.
- *
- * @param {number} to
- * The end value, value to land on.
- *
- * @param {string} [unit]
- * The property unit, for example `px`.
- */
- run: function (from, to, unit) {
- var self = this,
- options = self.options,
- timer = function (gotoEnd) {
- return timer.stopped ? false : self.step(gotoEnd);
- },
- requestAnimationFrame =
- win.requestAnimationFrame ||
- function (step) {
- setTimeout(step, 13);
- },
- step = function () {
- for (var i = 0; i < H.timers.length; i++) {
- if (!H.timers[i]()) {
- H.timers.splice(i--, 1);
- }
- }
- if (H.timers.length) {
- requestAnimationFrame(step);
- }
- };
- if (from === to && !this.elem['forceAnimate:' + this.prop]) {
- delete options.curAnim[this.prop];
- if (options.complete && Object.keys(options.curAnim).length === 0) {
- options.complete.call(this.elem);
- }
- } else { // #7166
- this.startTime = +new Date();
- this.start = from;
- this.end = to;
- this.unit = unit;
- this.now = this.start;
- this.pos = 0;
- timer.elem = this.elem;
- timer.prop = this.prop;
- if (timer() && H.timers.push(timer) === 1) {
- requestAnimationFrame(step);
- }
- }
- },
- /**
- * Run a single step in the animation.
- *
- * @function Highcharts.Fx#step
- *
- * @param {boolean} [gotoEnd]
- * Whether to go to the endpoint of the animation after abort.
- *
- * @return {boolean}
- * Returns `true` if animation continues.
- */
- step: function (gotoEnd) {
- var t = +new Date(),
- ret,
- done,
- options = this.options,
- elem = this.elem,
- complete = options.complete,
- duration = options.duration,
- curAnim = options.curAnim;
- if (elem.attr && !elem.element) { // #2616, element is destroyed
- ret = false;
- } else if (gotoEnd || t >= duration + this.startTime) {
- this.now = this.end;
- this.pos = 1;
- this.update();
- curAnim[this.prop] = true;
- done = true;
- H.objectEach(curAnim, function (val) {
- if (val !== true) {
- done = false;
- }
- });
- if (done && complete) {
- complete.call(elem);
- }
- ret = false;
- } else {
- this.pos = options.easing((t - this.startTime) / duration);
- this.now = this.start + ((this.end - this.start) * this.pos);
- this.update();
- ret = true;
- }
- return ret;
- },
- /**
- * Prepare start and end values so that the path can be animated one to one.
- *
- * @function Highcharts.Fx#initPath
- *
- * @param {Highcharts.SVGElement} elem
- * The SVGElement item.
- *
- * @param {string} fromD
- * Starting path definition.
- *
- * @param {Highcharts.SVGPathArray} toD
- * Ending path definition.
- *
- * @return {Array<Highcharts.SVGPathArray>}
- * An array containing start and end paths in array form so that
- * they can be animated in parallel.
- */
- initPath: function (elem, fromD, toD) {
- fromD = fromD || '';
- var shift,
- startX = elem.startX,
- endX = elem.endX,
- bezier = fromD.indexOf('C') > -1,
- numParams = bezier ? 7 : 3,
- fullLength,
- slice,
- i,
- start = fromD.split(' '),
- end = toD.slice(), // copy
- isArea = elem.isArea,
- positionFactor = isArea ? 2 : 1,
- reverse;
- /**
- * In splines make moveTo and lineTo points have six parameters like
- * bezier curves, to allow animation one-to-one.
- */
- function sixify(arr) {
- var isOperator,
- nextIsOperator;
- i = arr.length;
- while (i--) {
- // Fill in dummy coordinates only if the next operator comes
- // three places behind (#5788)
- isOperator = arr[i] === 'M' || arr[i] === 'L';
- nextIsOperator = /[a-zA-Z]/.test(arr[i + 3]);
- if (isOperator && nextIsOperator) {
- arr.splice(
- i + 1, 0,
- arr[i + 1], arr[i + 2],
- arr[i + 1], arr[i + 2]
- );
- }
- }
- }
- /**
- * Insert an array at the given position of another array
- */
- function insertSlice(arr, subArr, index) {
- [].splice.apply(
- arr,
- [index, 0].concat(subArr)
- );
- }
- /**
- * If shifting points, prepend a dummy point to the end path.
- */
- function prepend(arr, other) {
- while (arr.length < fullLength) {
- // Move to, line to or curve to?
- arr[0] = other[fullLength - arr.length];
- // Prepend a copy of the first point
- insertSlice(arr, arr.slice(0, numParams), 0);
- // For areas, the bottom path goes back again to the left, so we
- // need to append a copy of the last point.
- if (isArea) {
- insertSlice(
- arr,
- arr.slice(arr.length - numParams), arr.length
- );
- i--;
- }
- }
- arr[0] = 'M';
- }
- /**
- * Copy and append last point until the length matches the end length.
- */
- function append(arr, other) {
- var i = (fullLength - arr.length) / numParams;
- while (i > 0 && i--) {
- // Pull out the slice that is going to be appended or inserted.
- // In a line graph, the positionFactor is 1, and the last point
- // is sliced out. In an area graph, the positionFactor is 2,
- // causing the middle two points to be sliced out, since an area
- // path starts at left, follows the upper path then turns and
- // follows the bottom back.
- slice = arr.slice().splice(
- (arr.length / positionFactor) - numParams,
- numParams * positionFactor
- );
- // Move to, line to or curve to?
- slice[0] = other[fullLength - numParams - (i * numParams)];
- // Disable first control point
- if (bezier) {
- slice[numParams - 6] = slice[numParams - 2];
- slice[numParams - 5] = slice[numParams - 1];
- }
- // Now insert the slice, either in the middle (for areas) or at
- // the end (for lines)
- insertSlice(arr, slice, arr.length / positionFactor);
- if (isArea) {
- i--;
- }
- }
- }
- if (bezier) {
- sixify(start);
- sixify(end);
- }
- // For sideways animation, find out how much we need to shift to get the
- // start path Xs to match the end path Xs.
- if (startX && endX) {
- for (i = 0; i < startX.length; i++) {
- // Moving left, new points coming in on right
- if (startX[i] === endX[0]) {
- shift = i;
- break;
- // Moving right
- } else if (startX[0] ===
- endX[endX.length - startX.length + i]) {
- shift = i;
- reverse = true;
- break;
- }
- }
- if (shift === undefined) {
- start = [];
- }
- }
- if (start.length && H.isNumber(shift)) {
- // The common target length for the start and end array, where both
- // arrays are padded in opposite ends
- fullLength = end.length + shift * positionFactor * numParams;
- if (!reverse) {
- prepend(end, start);
- append(start, end);
- } else {
- prepend(start, end);
- append(end, start);
- }
- }
- return [start, end];
- },
- /**
- * Handle animation of the color attributes directly.
- *
- * @function Highcharts.Fx#fillSetter
- */
- fillSetter: function () {
- H.Fx.prototype.strokeSetter.apply(this, arguments);
- },
- /**
- * Handle animation of the color attributes directly.
- *
- * @function Highcharts.Fx#strokeSetter
- */
- strokeSetter: function () {
- this.elem.attr(
- this.prop,
- H.color(this.start).tweenTo(H.color(this.end), this.pos),
- null,
- true
- );
- }
- }; // End of Fx prototype
- /**
- * Utility function to deep merge two or more objects and return a third object.
- * The merge function can also be used with a single object argument to create a
- * deep copy of an object.
- *
- * @function Highcharts.merge
- *
- * @param {*} a
- * The first object to extend. When only this is given, the function
- * returns a deep copy.
- *
- * @param {*} [n]
- * An object to merge into the previous one.
- *
- * @return {*}
- * The merged object. If the first argument is true, the return is the
- * same as the second argument.
- *//**
- * Utility function to deep merge two or more objects and return a third object.
- * If the first argument is true, the contents of the second object is copied
- * into the first object. The merge function can also be used with a single
- * object argument to create a deep copy of an object.
- *
- * @function Highcharts.merge
- *
- * @param {boolean} extend
- * Whether to extend the left-side object (a) or return a whole new
- * object.
- *
- * @param {*} a
- * The first object to extend. When only this is given, the function
- * returns a deep copy.
- *
- * @param {*} [n]
- * An object to merge into the previous one.
- *
- * @return {*}
- * The merged object. If the first argument is true, the return is the
- * same as the second argument.
- */
- H.merge = function () {
- var i,
- args = arguments,
- len,
- ret = {},
- doCopy = function (copy, original) {
- // An object is replacing a primitive
- if (typeof copy !== 'object') {
- copy = {};
- }
- H.objectEach(original, function (value, key) {
- // Copy the contents of objects, but not arrays or DOM nodes
- if (H.isObject(value, true) &&
- !H.isClass(value) &&
- !H.isDOMElement(value)
- ) {
- copy[key] = doCopy(copy[key] || {}, value);
- // Primitives and arrays are copied over directly
- } else {
- copy[key] = original[key];
- }
- });
- return copy;
- };
- // If first argument is true, copy into the existing object. Used in
- // setOptions.
- if (args[0] === true) {
- ret = args[1];
- args = Array.prototype.slice.call(args, 2);
- }
- // For each argument, extend the return
- len = args.length;
- for (i = 0; i < len; i++) {
- ret = doCopy(ret, args[i]);
- }
- return ret;
- };
- /**
- * Shortcut for parseInt
- *
- * @private
- * @function Highcharts.pInt
- *
- * @param {*} s
- *
- * @param {number} mag
- * Magnitude
- *
- * @return {number}
- */
- H.pInt = function (s, mag) {
- return parseInt(s, mag || 10);
- };
- /**
- * Utility function to check for string type.
- *
- * @function Highcharts.isString
- *
- * @param {*} s
- * The item to check.
- *
- * @return {boolean}
- * True if the argument is a string.
- */
- H.isString = function (s) {
- return typeof s === 'string';
- };
- /**
- * Utility function to check if an item is an array.
- *
- * @function Highcharts.isArray
- *
- * @param {*} obj
- * The item to check.
- *
- * @return {boolean}
- * True if the argument is an array.
- */
- H.isArray = function (obj) {
- var str = Object.prototype.toString.call(obj);
- return str === '[object Array]' || str === '[object Array Iterator]';
- };
- /**
- * Utility function to check if an item is of type object.
- *
- * @function Highcharts.isObject
- *
- * @param {*} obj
- * The item to check.
- *
- * @param {boolean} [strict=false]
- * Also checks that the object is not an array.
- *
- * @return {boolean}
- * True if the argument is an object.
- */
- H.isObject = function (obj, strict) {
- return !!obj && typeof obj === 'object' && (!strict || !H.isArray(obj));
- };
- /**
- * Utility function to check if an Object is a HTML Element.
- *
- * @function Highcharts.isDOMElement
- *
- * @param {*} obj
- * The item to check.
- *
- * @return {boolean}
- * True if the argument is a HTML Element.
- */
- H.isDOMElement = function (obj) {
- return H.isObject(obj) && typeof obj.nodeType === 'number';
- };
- /**
- * Utility function to check if an Object is an class.
- *
- * @function Highcharts.isClass
- *
- * @param {*} obj
- * The item to check.
- *
- * @return {boolean}
- * True if the argument is an class.
- */
- H.isClass = function (obj) {
- var c = obj && obj.constructor;
- return !!(
- H.isObject(obj, true) &&
- !H.isDOMElement(obj) &&
- (c && c.name && c.name !== 'Object')
- );
- };
- /**
- * Utility function to check if an item is a number and it is finite (not NaN,
- * Infinity or -Infinity).
- *
- * @function Highcharts.isNumber
- *
- * @param {*} n
- * The item to check.
- *
- * @return {boolean}
- * True if the item is a finite number
- */
- H.isNumber = function (n) {
- return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
- };
- /**
- * Remove the last occurence of an item from an array.
- *
- * @function Highcharts.erase
- *
- * @param {Array} arr
- * The array.
- *
- * @param {*} item
- * The item to remove.
- */
- H.erase = function (arr, item) {
- var i = arr.length;
- while (i--) {
- if (arr[i] === item) {
- arr.splice(i, 1);
- break;
- }
- }
- };
- /**
- * Check if an object is null or undefined.
- *
- * @function Highcharts.defined
- *
- * @param {*} obj
- * The object to check.
- *
- * @return {boolean}
- * False if the object is null or undefined, otherwise true.
- */
- H.defined = function (obj) {
- return obj !== undefined && obj !== null;
- };
- /**
- * Set or get an attribute or an object of attributes. To use as a setter, pass
- * a key and a value, or let the second argument be a collection of keys and
- * values. To use as a getter, pass only a string as the second argument.
- *
- * @function Highcharts.attr
- *
- * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
- * The DOM element to receive the attribute(s).
- *
- * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [prop]
- * The property or an object of key-value pairs.
- *
- * @param {string} [value]
- * The value if a single property is set.
- *
- * @return {*}
- * When used as a getter, return the value.
- */
- H.attr = function (elem, prop, value) {
- var ret;
- // if the prop is a string
- if (H.isString(prop)) {
- // set the value
- if (H.defined(value)) {
- elem.setAttribute(prop, value);
- // get the value
- } else if (elem && elem.getAttribute) {
- ret = elem.getAttribute(prop);
- // IE7 and below cannot get class through getAttribute (#7850)
- if (!ret && prop === 'class') {
- ret = elem.getAttribute(prop + 'Name');
- }
- }
- // else if prop is defined, it is a hash of key/value pairs
- } else if (H.defined(prop) && H.isObject(prop)) {
- H.objectEach(prop, function (val, key) {
- elem.setAttribute(key, val);
- });
- }
- return ret;
- };
- /**
- * Check if an element is an array, and if not, make it into an array.
- *
- * @function Highcharts.splat
- *
- * @param {*} obj
- * The object to splat.
- *
- * @return {Array}
- * The produced or original array.
- */
- H.splat = function (obj) {
- return H.isArray(obj) ? obj : [obj];
- };
- /**
- * Set a timeout if the delay is given, otherwise perform the function
- * synchronously.
- *
- * @function Highcharts.syncTimeout
- *
- * @param {Function} fn
- * The function callback.
- *
- * @param {number} delay
- * Delay in milliseconds.
- *
- * @param {*} [parameter]
- * An optional parameter to send to the function callback.
- *
- * @return {number}
- * An identifier for the timeout that can later be cleared with
- * Highcharts.clearTimeout.
- */
- H.syncTimeout = function (fn, delay, context) {
- if (delay) {
- return setTimeout(fn, delay, context);
- }
- fn.call(0, context);
- };
- /**
- * Internal clear timeout. The function checks that the `id` was not removed
- * (e.g. by `chart.destroy()`). For the details see
- * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
- *
- * @function Highcharts.clearTimeout
- *
- * @param {number} id
- * Id of a timeout.
- */
- H.clearTimeout = function (id) {
- if (H.defined(id)) {
- clearTimeout(id);
- }
- };
- /**
- * Utility function to extend an object with the members of another.
- *
- * @function Highcharts.extend
- *
- * @param {Highcharts.Dictionary<*>} a
- * The object to be extended.
- *
- * @param {Highcharts.Dictionary<*>} b
- * The object to add to the first one.
- *
- * @return {Highcharts.Dictionary<*>}
- * Object a, the original object.
- */
- H.extend = function (a, b) {
- var n;
- if (!a) {
- a = {};
- }
- for (n in b) {
- a[n] = b[n];
- }
- return a;
- };
- /**
- * Return the first value that is not null or undefined.
- *
- * @function Highcharts.pick
- *
- * @param {...*} items
- * Variable number of arguments to inspect.
- *
- * @return {*}
- * The value of the first argument that is not null or undefined.
- */
- H.pick = function () {
- var args = arguments,
- i,
- arg,
- length = args.length;
- for (i = 0; i < length; i++) {
- arg = args[i];
- if (arg !== undefined && arg !== null) {
- return arg;
- }
- }
- };
- /**
- * Set CSS on a given element.
- *
- * @function Highcharts.css
- *
- * @param {Highcharts.HTMLDOMElement} el
- * An HTML DOM element.
- *
- * @param {Highcharts.CSSObject} styles
- * Style object with camel case property names.
- */
- H.css = function (el, styles) {
- if (H.isMS && !H.svg) { // #2686
- if (styles && styles.opacity !== undefined) {
- styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')';
- }
- }
- H.extend(el.style, styles);
- };
- /**
- * Utility function to create an HTML element with attributes and styles.
- *
- * @function Highcharts.createElement
- *
- * @param {string} tag
- * The HTML tag.
- *
- * @param {Highcharts.HTMLAttributes} [attribs]
- * Attributes as an object of key-value pairs.
- *
- * @param {Highcharts.CSSObject} [styles]
- * Styles as an object of key-value pairs.
- *
- * @param {Highcharts.HTMLDOMElement} [parent]
- * The parent HTML object.
- *
- * @param {boolean} [nopad=false]
- * If true, remove all padding, border and margin.
- *
- * @return {Highcharts.HTMLDOMElement}
- * The created DOM element.
- */
- H.createElement = function (tag, attribs, styles, parent, nopad) {
- var el = doc.createElement(tag),
- css = H.css;
- if (attribs) {
- H.extend(el, attribs);
- }
- if (nopad) {
- css(el, { padding: 0, border: 'none', margin: 0 });
- }
- if (styles) {
- css(el, styles);
- }
- if (parent) {
- parent.appendChild(el);
- }
- return el;
- };
- /**
- * Extend a prototyped class by new members.
- *
- * @function Highcharts.extendClass
- *
- * @param {*} parent
- * The parent prototype to inherit.
- *
- * @param {Highcharts.Dictionary<*>} members
- * A collection of prototype members to add or override compared to the
- * parent prototype.
- *
- * @return {*}
- * A new prototype.
- */
- H.extendClass = function (parent, members) {
- var object = function () {};
- object.prototype = new parent(); // eslint-disable-line new-cap
- H.extend(object.prototype, members);
- return object;
- };
- /**
- * Left-pad a string to a given length by adding a character repetetively.
- *
- * @function Highcharts.pad
- *
- * @param {number} number
- * The input string or number.
- *
- * @param {number} length
- * The desired string length.
- *
- * @param {string} [padder=0]
- * The character to pad with.
- *
- * @return {string}
- * The padded string.
- */
- H.pad = function (number, length, padder) {
- return new Array(
- (length || 2) +
- 1 -
- String(number)
- .replace('-', '')
- .length
- ).join(padder || 0) + number;
- };
- /**
- * Return a length based on either the integer value, or a percentage of a base.
- *
- * @function Highcharts.relativeLength
- *
- * @param {Highcharts.RelativeSize} value
- * A percentage string or a number.
- *
- * @param {number} base
- * The full length that represents 100%.
- *
- * @param {number} [offset=0]
- * A pixel offset to apply for percentage values. Used internally in
- * axis positioning.
- *
- * @return {number}
- * The computed length.
- */
- H.relativeLength = function (value, base, offset) {
- return (/%$/).test(value) ?
- (base * parseFloat(value) / 100) + (offset || 0) :
- parseFloat(value);
- };
- /**
- * Wrap a method with extended functionality, preserving the original function.
- *
- * @function Highcharts.wrap
- *
- * @param {*} obj
- * The context object that the method belongs to. In real cases, this is
- * often a prototype.
- *
- * @param {string} method
- * The name of the method to extend.
- *
- * @param {Function} func
- * A wrapper function callback. This function is called with the same
- * arguments as the original function, except that the original function
- * is unshifted and passed as the first argument.
- */
- H.wrap = function (obj, method, func) {
- var proceed = obj[method];
- obj[method] = function () {
- var args = Array.prototype.slice.call(arguments),
- outerArgs = arguments,
- ctx = this,
- ret;
- ctx.proceed = function () {
- proceed.apply(ctx, arguments.length ? arguments : outerArgs);
- };
- args.unshift(proceed);
- ret = func.apply(this, args);
- ctx.proceed = null;
- return ret;
- };
- };
- /**
- * Recursively converts all Date properties to timestamps.
- *
- * @param {Object} object - any object to convert properties of
- */
- H.datePropsToTimestamps = function (object) {
- H.objectEach(object, function (val, key) {
- if (H.isObject(val) && typeof val.getTime === 'function') {
- object[key] = val.getTime();
- } else if (H.isObject(val) || H.isArray(val)) {
- H.datePropsToTimestamps(val);
- }
- });
- };
- /**
- * Format a single variable. Similar to sprintf, without the % prefix.
- *
- * @example
- * formatSingle('.2f', 5); // => '5.00'.
- *
- * @function Highcharts.formatSingle
- *
- * @param {string} format
- * The format string.
- *
- * @param {*} val
- * The value.
- *
- * @param {Highcharts.Time} [time]
- * A `Time` instance that determines the date formatting, for example
- * for applying time zone corrections to the formatted date.
- *
- * @return {string}
- * The formatted representation of the value.
- */
- H.formatSingle = function (format, val, time) {
- var floatRegex = /f$/,
- decRegex = /\.([0-9])/,
- lang = H.defaultOptions.lang,
- decimals;
- if (floatRegex.test(format)) { // float
- decimals = format.match(decRegex);
- decimals = decimals ? decimals[1] : -1;
- if (val !== null) {
- val = H.numberFormat(
- val,
- decimals,
- lang.decimalPoint,
- format.indexOf(',') > -1 ? lang.thousandsSep : ''
- );
- }
- } else {
- val = (time || H.time).dateFormat(format, val);
- }
- return val;
- };
- /**
- * Format a string according to a subset of the rules of Python's String.format
- * method.
- *
- * @example
- * var s = Highcharts.format(
- * 'The {color} fox was {len:.2f} feet long',
- * { color: 'red', len: Math.PI }
- * );
- * // => The red fox was 3.14 feet long
- *
- * @function Highcharts.format
- *
- * @param {string} str
- * The string to format.
- *
- * @param {*} ctx
- * The context, a collection of key-value pairs where each key is
- * replaced by its value.
- *
- * @param {Highcharts.Time} [time]
- * A `Time` instance that determines the date formatting, for example
- * for applying time zone corrections to the formatted date.
- *
- * @return {string}
- * The formatted string.
- */
- H.format = function (str, ctx, time) {
- var splitter = '{',
- isInside = false,
- segment,
- valueAndFormat,
- path,
- i,
- len,
- ret = [],
- val,
- index;
- while (str) {
- index = str.indexOf(splitter);
- if (index === -1) {
- break;
- }
- segment = str.slice(0, index);
- if (isInside) { // we're on the closing bracket looking back
- valueAndFormat = segment.split(':');
- path = valueAndFormat.shift().split('.'); // get first and leave
- len = path.length;
- val = ctx;
- // Assign deeper paths
- for (i = 0; i < len; i++) {
- if (val) {
- val = val[path[i]];
- }
- }
- // Format the replacement
- if (valueAndFormat.length) {
- val = H.formatSingle(valueAndFormat.join(':'), val, time);
- }
- // Push the result and advance the cursor
- ret.push(val);
- } else {
- ret.push(segment);
- }
- str = str.slice(index + 1); // the rest
- isInside = !isInside; // toggle
- splitter = isInside ? '}' : '{'; // now look for next matching bracket
- }
- ret.push(str);
- return ret.join('');
- };
- /**
- * Get the magnitude of a number.
- *
- * @function Highcharts.getMagnitude
- *
- * @param {number} number
- * The number.
- *
- * @return {number}
- * The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
- */
- H.getMagnitude = function (num) {
- return Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
- };
- /**
- * Take an interval and normalize it to multiples of round numbers.
- *
- * @deprecated
- * @function Highcharts.normalizeTickInterval
- *
- * @param {number} interval
- * The raw, un-rounded interval.
- *
- * @param {Array} [multiples]
- * Allowed multiples.
- *
- * @param {number} [magnitude]
- * The magnitude of the number.
- *
- * @param {boolean} [allowDecimals]
- * Whether to allow decimals.
- *
- * @param {boolean} [hasTickAmount]
- * If it has tickAmount, avoid landing on tick intervals lower than
- * original.
- *
- * @return {number}
- * The normalized interval.
- *
- * @todo
- * Move this function to the Axis prototype. It is here only for historical
- * reasons.
- */
- H.normalizeTickInterval = function (
- interval,
- multiples,
- magnitude,
- allowDecimals,
- hasTickAmount
- ) {
- var normalized,
- i,
- retInterval = interval;
- // round to a tenfold of 1, 2, 2.5 or 5
- magnitude = H.pick(magnitude, 1);
- normalized = interval / magnitude;
- // multiples for a linear scale
- if (!multiples) {
- multiples = hasTickAmount ?
- // Finer grained ticks when the tick amount is hard set, including
- // when alignTicks is true on multiple axes (#4580).
- [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
- // Else, let ticks fall on rounder numbers
- [1, 2, 2.5, 5, 10];
- // the allowDecimals option
- if (allowDecimals === false) {
- if (magnitude === 1) {
- multiples = multiples.filter(function (num) {
- return num % 1 === 0;
- });
- } else if (magnitude <= 0.1) {
- multiples = [1 / magnitude];
- }
- }
- }
- // normalize the interval to the nearest multiple
- for (i = 0; i < multiples.length; i++) {
- retInterval = multiples[i];
- // only allow tick amounts smaller than natural
- if (
- (
- hasTickAmount &&
- retInterval * magnitude >= interval
- ) ||
- (
- !hasTickAmount &&
- (
- normalized <=
- (
- multiples[i] +
- (multiples[i + 1] || multiples[i])
- ) / 2
- )
- )
- ) {
- break;
- }
- }
- // Multiply back to the correct magnitude. Correct floats to appropriate
- // precision (#6085).
- retInterval = H.correctFloat(
- retInterval * magnitude,
- -Math.round(Math.log(0.001) / Math.LN10)
- );
- return retInterval;
- };
- /**
- * Sort an object array and keep the order of equal items. The ECMAScript
- * standard does not specify the behaviour when items are equal.
- *
- * @function Highcharts.stableSort
- *
- * @param {Array} arr
- * The array to sort.
- *
- * @param {Function} sortFunction
- * The function to sort it with, like with regular Array.prototype.sort.
- */
- H.stableSort = function (arr, sortFunction) {
- var length = arr.length,
- sortValue,
- i;
- // Add index to each item
- for (i = 0; i < length; i++) {
- arr[i].safeI = i; // stable sort index
- }
- arr.sort(function (a, b) {
- sortValue = sortFunction(a, b);
- return sortValue === 0 ? a.safeI - b.safeI : sortValue;
- });
- // Remove index from items
- for (i = 0; i < length; i++) {
- delete arr[i].safeI; // stable sort index
- }
- };
- /**
- * Non-recursive method to find the lowest member of an array. `Math.min` raises
- * a maximum call stack size exceeded error in Chrome when trying to apply more
- * than 150.000 points. This method is slightly slower, but safe.
- *
- * @function Highcharts.arrayMin
- *
- * @param {Array} data
- * An array of numbers.
- *
- * @return {number}
- * The lowest number.
- */
- H.arrayMin = function (data) {
- var i = data.length,
- min = data[0];
- while (i--) {
- if (data[i] < min) {
- min = data[i];
- }
- }
- return min;
- };
- /**
- * Non-recursive method to find the lowest member of an array. `Math.max` raises
- * a maximum call stack size exceeded error in Chrome when trying to apply more
- * than 150.000 points. This method is slightly slower, but safe.
- *
- * @function Highcharts.arrayMax
- *
- * @param {Array} data
- * An array of numbers.
- *
- * @return {number}
- * The highest number.
- */
- H.arrayMax = function (data) {
- var i = data.length,
- max = data[0];
- while (i--) {
- if (data[i] > max) {
- max = data[i];
- }
- }
- return max;
- };
- /**
- * Utility method that destroys any SVGElement instances that are properties on
- * the given object. It loops all properties and invokes destroy if there is a
- * destroy method. The property is then delete.
- *
- * @function Highcharts.destroyObjectProperties
- *
- * @param {*} obj
- * The object to destroy properties on.
- *
- * @param {*} [except]
- * Exception, do not destroy this property, only delete it.
- */
- H.destroyObjectProperties = function (obj, except) {
- H.objectEach(obj, function (val, n) {
- // If the object is non-null and destroy is defined
- if (val && val !== except && val.destroy) {
- // Invoke the destroy
- val.destroy();
- }
- // Delete the property from the object.
- delete obj[n];
- });
- };
- /**
- * Discard a HTML element by moving it to the bin and delete.
- *
- * @function Highcharts.discardElement
- *
- * @param {Highcharts.HTMLDOMElement} element
- * The HTML node to discard.
- */
- H.discardElement = function (element) {
- var garbageBin = H.garbageBin;
- // create a garbage bin element, not part of the DOM
- if (!garbageBin) {
- garbageBin = H.createElement('div');
- }
- // move the node and empty bin
- if (element) {
- garbageBin.appendChild(element);
- }
- garbageBin.innerHTML = '';
- };
- /**
- * Fix JS round off float errors.
- *
- * @function Highcharts.correctFloat
- *
- * @param {number} num
- * A float number to fix.
- *
- * @param {number} [prec=14]
- * The precision.
- *
- * @return {number}
- * The corrected float number.
- */
- H.correctFloat = function (num, prec) {
- return parseFloat(
- num.toPrecision(prec || 14)
- );
- };
- /**
- * Set the global animation to either a given value, or fall back to the given
- * chart's animation option.
- *
- * @function Highcharts.setAnimation
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} animation
- * The animation object.
- *
- * @param {Highcharts.Chart} chart
- * The chart instance.
- *
- * @todo
- * This function always relates to a chart, and sets a property on the renderer,
- * so it should be moved to the SVGRenderer.
- */
- H.setAnimation = function (animation, chart) {
- chart.renderer.globalAnimation = H.pick(
- animation,
- chart.options.chart.animation,
- true
- );
- };
- /**
- * Get the animation in object form, where a disabled animation is always
- * returned as `{ duration: 0 }`.
- *
- * @function Highcharts.animObject
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} animation
- * An animation setting. Can be an object with duration, complete and
- * easing properties, or a boolean to enable or disable.
- *
- * @return {Highcharts.AnimationOptionsObject}
- * An object with at least a duration property.
- */
- H.animObject = function (animation) {
- return H.isObject(animation) ?
- H.merge(animation) :
- { duration: animation ? 500 : 0 };
- };
- /**
- * The time unit lookup
- *
- * @ignore
- */
- H.timeUnits = {
- millisecond: 1,
- second: 1000,
- minute: 60000,
- hour: 3600000,
- day: 24 * 3600000,
- week: 7 * 24 * 3600000,
- month: 28 * 24 * 3600000,
- year: 364 * 24 * 3600000
- };
- /**
- * Format a number and return a string based on input settings.
- *
- * @sample highcharts/members/highcharts-numberformat/
- * Custom number format
- *
- * @function Highcharts.numberFormat
- *
- * @param {number} number
- * The input number to format.
- *
- * @param {number} decimals
- * The amount of decimals. A value of -1 preserves the amount in the
- * input number.
- *
- * @param {string} [decimalPoint]
- * The decimal point, defaults to the one given in the lang options, or
- * a dot.
- *
- * @param {string} [thousandsSep]
- * The thousands separator, defaults to the one given in the lang
- * options, or a space character.
- *
- * @return {string}
- * The formatted number.
- */
- H.numberFormat = function (number, decimals, decimalPoint, thousandsSep) {
- number = +number || 0;
- decimals = +decimals;
- var lang = H.defaultOptions.lang,
- origDec = (number.toString().split('.')[1] || '').split('e')[0].length,
- strinteger,
- thousands,
- ret,
- roundedNumber,
- exponent = number.toString().split('e'),
- fractionDigits;
- if (decimals === -1) {
- // Preserve decimals. Not huge numbers (#3793).
- decimals = Math.min(origDec, 20);
- } else if (!H.isNumber(decimals)) {
- decimals = 2;
- } else if (decimals && exponent[1] && exponent[1] < 0) {
- // Expose decimals from exponential notation (#7042)
- fractionDigits = decimals + +exponent[1];
- if (fractionDigits >= 0) {
- // remove too small part of the number while keeping the notation
- exponent[0] = (+exponent[0]).toExponential(fractionDigits)
- .split('e')[0];
- decimals = fractionDigits;
- } else {
- // fractionDigits < 0
- exponent[0] = exponent[0].split('.')[0] || 0;
- if (decimals < 20) {
- // use number instead of exponential notation (#7405)
- number = (exponent[0] * Math.pow(10, exponent[1]))
- .toFixed(decimals);
- } else {
- // or zero
- number = 0;
- }
- exponent[1] = 0;
- }
- }
- // Add another decimal to avoid rounding errors of float numbers. (#4573)
- // Then use toFixed to handle rounding.
- roundedNumber = (
- Math.abs(exponent[1] ? exponent[0] : number) +
- Math.pow(10, -Math.max(decimals, origDec) - 1)
- ).toFixed(decimals);
- // A string containing the positive integer component of the number
- strinteger = String(H.pInt(roundedNumber));
- // Leftover after grouping into thousands. Can be 0, 1 or 2.
- thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
- // Language
- decimalPoint = H.pick(decimalPoint, lang.decimalPoint);
- thousandsSep = H.pick(thousandsSep, lang.thousandsSep);
- // Start building the return
- ret = number < 0 ? '-' : '';
- // Add the leftover after grouping into thousands. For example, in the
- // number 42 000 000, this line adds 42.
- ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
- // Add the remaining thousands groups, joined by the thousands separator
- ret += strinteger
- .substr(thousands)
- .replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
- // Add the decimal point and the decimal component
- if (decimals) {
- // Get the decimal component
- ret += decimalPoint + roundedNumber.slice(-decimals);
- }
- if (exponent[1] && +ret !== 0) {
- ret += 'e' + exponent[1];
- }
- return ret;
- };
- /**
- * Easing definition
- *
- * @private
- * @function Math.easeInOutSine
- *
- * @param {number} pos
- * Current position, ranging from 0 to 1.
- *
- * @return {number}
- */
- Math.easeInOutSine = function (pos) {
- return -0.5 * (Math.cos(Math.PI * pos) - 1);
- };
- /**
- * Get the computed CSS value for given element and property, only for numerical
- * properties. For width and height, the dimension of the inner box (excluding
- * padding) is returned. Used for fitting the chart within the container.
- *
- * @function Highcharts.getStyle
- *
- * @param {Highcharts.HTMLDOMElement} el
- * An HTML element.
- *
- * @param {string} prop
- * The property name.
- *
- * @param {boolean} [toInt=true]
- * Parse to integer.
- *
- * @return {number}
- * The numeric value.
- */
- H.getStyle = function (el, prop, toInt) {
- var style;
- // For width and height, return the actual inner pixel size (#4913)
- if (prop === 'width') {
- return Math.max(
- 0, // #8377
- (
- Math.min(
- el.offsetWidth,
- el.scrollWidth,
- (
- el.getBoundingClientRect &&
- // #9871, getBoundingClientRect doesn't handle
- // transforms, so avoid that
- H.getStyle(el, 'transform', false) === 'none'
- ) ?
- Math.floor(el.getBoundingClientRect().width) : // #6427
- Infinity
- ) -
- H.getStyle(el, 'padding-left') -
- H.getStyle(el, 'padding-right')
- )
- );
- }
- if (prop === 'height') {
- return Math.max(
- 0, // #8377
- Math.min(el.offsetHeight, el.scrollHeight) -
- H.getStyle(el, 'padding-top') -
- H.getStyle(el, 'padding-bottom')
- );
- }
- if (!win.getComputedStyle) {
- // SVG not supported, forgot to load oldie.js?
- H.error(27, true);
- }
- // Otherwise, get the computed style
- style = win.getComputedStyle(el, undefined);
- if (style) {
- style = style.getPropertyValue(prop);
- if (H.pick(toInt, prop !== 'opacity')) {
- style = H.pInt(style);
- }
- }
- return style;
- };
- /**
- * Search for an item in an array.
- *
- * @function Highcharts.inArray
- *
- * @deprecated
- *
- * @param {*} item
- * The item to search for.
- *
- * @param {Array} arr
- * The array or node collection to search in.
- *
- * @param {number} [fromIndex=0]
- * The index to start searching from.
- *
- * @return {number}
- * The index within the array, or -1 if not found.
- */
- H.inArray = function (item, arr, fromIndex) {
- return arr.indexOf(item, fromIndex);
- };
- /**
- * Return the value of the first element in the array that satisfies the
- * provided testing function.
- *
- * @function Highcharts.find
- *
- * @param {Array} arr
- * The array to test.
- *
- * @param {Function} callback
- * The callback function. The function receives the item as the first
- * argument. Return `true` if this item satisfies the condition.
- *
- * @return {*}
- * The value of the element.
- */
- H.find = Array.prototype.find ?
- function (arr, callback) {
- return arr.find(callback);
- } :
- // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
- function (arr, fn) {
- var i,
- length = arr.length;
- for (i = 0; i < length; i++) {
- if (fn(arr[i], i)) {
- return arr[i];
- }
- }
- };
- /**
- * Returns an array of a given object's own properties.
- *
- * @function Highcharts.keys
- * @deprecated
- *
- * @param {*} obj
- * The object of which the properties are to be returned.
- *
- * @return {Array<string>}
- * An array of strings that represents all the properties.
- */
- H.keys = Object.keys;
- /**
- * Get the element's offset position, corrected for `overflow: auto`.
- *
- * @function Highcharts.offset
- *
- * @param {Highcharts.HTMLDOMElement} el
- * The HTML element.
- *
- * @return {Highcharts.OffsetObject}
- * An object containing `left` and `top` properties for the position in
- * the page.
- */
- H.offset = function (el) {
- var docElem = doc.documentElement,
- box = (el.parentElement || el.parentNode) ?
- el.getBoundingClientRect() :
- { top: 0, left: 0 };
- return {
- top: box.top + (win.pageYOffset || docElem.scrollTop) -
- (docElem.clientTop || 0),
- left: box.left + (win.pageXOffset || docElem.scrollLeft) -
- (docElem.clientLeft || 0)
- };
- };
- /**
- * Stop running animation.
- *
- * @function Highcharts.stop
- *
- * @param {Highcharts.SVGElement} el
- * The SVGElement to stop animation on.
- *
- * @param {string} [prop]
- * The property to stop animating. If given, the stop method will stop a
- * single property from animating, while others continue.
- *
- * @todo
- * A possible extension to this would be to stop a single property, when
- * we want to continue animating others. Then assign the prop to the timer
- * in the Fx.run method, and check for the prop here. This would be an
- * improvement in all cases where we stop the animation from .attr. Instead of
- * stopping everything, we can just stop the actual attributes we're setting.
- */
- H.stop = function (el, prop) {
- var i = H.timers.length;
- // Remove timers related to this element (#4519)
- while (i--) {
- if (H.timers[i].elem === el && (!prop || prop === H.timers[i].prop)) {
- H.timers[i].stopped = true; // #4667
- }
- }
- };
- /**
- * Iterate over object key pairs in an object.
- *
- * @function Highcharts.objectEach
- *
- * @param {*} obj
- * The object to iterate over.
- *
- * @param {Highcharts.ObjectEachCallbackFunction} fn
- * The iterator callback. It passes three arguments:
- * * value - The property value.
- * * key - The property key.
- * * obj - The object that objectEach is being applied to.
- *
- * @param {*} [ctx]
- * The context.
- */
- H.objectEach = function (obj, fn, ctx) {
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- fn.call(ctx || obj[key], obj[key], key, obj);
- }
- }
- };
- /**
- * Iterate over an array.
- *
- * @deprecated
- * @function Highcharts.each
- *
- * @param {Array<*>} arr
- * The array to iterate over.
- *
- * @param {Function} fn
- * The iterator callback. It passes three arguments:
- * - `item`: The array item.
- * - `index`: The item's index in the array.
- * - `arr`: The array that each is being applied to.
- *
- * @param {*} [ctx]
- * The context.
- */
- /**
- * Filter an array by a callback.
- *
- * @deprecated
- * @function Highcharts.grep
- *
- * @param {Array<*>} arr
- * The array to filter.
- *
- * @param {Function} callback
- * The callback function. The function receives the item as the first
- * argument. Return `true` if the item is to be preserved.
- *
- * @return {Array<*>}
- * A new, filtered array.
- */
- /**
- * Map an array by a callback.
- *
- * @deprecated
- * @function Highcharts.map
- *
- * @param {Array<*>} arr
- * The array to map.
- *
- * @param {Function} fn
- * The callback function. Return the new value for the new array.
- *
- * @return {Array<*>}
- * A new array item with modified items.
- */
- /**
- * Reduce an array to a single value.
- *
- * @deprecated
- * @function Highcharts.reduce
- *
- * @param {Array} arr
- * The array to reduce.
- *
- * @param {Function} fn
- * The callback function. Return the reduced value. Receives 4
- * arguments: Accumulated/reduced value, current value, current array
- * index, and the array.
- *
- * @param {*} initialValue
- * The initial value of the accumulator.
- *
- * @return {*}
- * The reduced value.
- */
- /**
- * Test whether at least one element in the array passes the test implemented by
- * the provided function.
- *
- * @deprecated
- * @function Highcharts.some
- *
- * @param {Array<*>} arr
- * The array to test
- *
- * @param {Function} fn
- * The function to run on each item. Return truty to pass the test.
- * Receives arguments `currentValue`, `index` and `array`.
- *
- * @param {*} ctx
- * The context.
- *
- * @return {boolean}
- */
- H.objectEach({
- map: 'map',
- each: 'forEach',
- grep: 'filter',
- reduce: 'reduce',
- some: 'some'
- }, function (val, key) {
- H[key] = function (arr) {
- return Array.prototype[val].apply(
- arr,
- [].slice.call(arguments, 1)
- );
- };
- });
- /**
- * Add an event listener.
- *
- * @function Highcharts.addEvent<T>
- *
- * @param {T} el
- * The element or object to add a listener to. It can be a
- * {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
- *
- * @param {string} type
- * The event type.
- *
- * @param {Highcharts.EventCallbackFunction<T>} fn
- * The function callback to execute when the event is fired.
- *
- * @param {Highcharts.EventOptionsObject} [options]
- * Options for adding the event.
- *
- * @return {Function}
- * A callback function to remove the added event.
- */
- H.addEvent = function (el, type, fn, options) {
- var events,
- addEventListener = el.addEventListener || H.addEventListenerPolyfill;
- // If we're setting events directly on the constructor, use a separate
- // collection, `protoEvents` to distinguish it from the item events in
- // `hcEvents`.
- if (typeof el === 'function' && el.prototype) {
- events = el.prototype.protoEvents = el.prototype.protoEvents || {};
- } else {
- events = el.hcEvents = el.hcEvents || {};
- }
- // Allow click events added to points, otherwise they will be prevented by
- // the TouchPointer.pinch function after a pinch zoom operation (#7091).
- if (H.Point && el instanceof H.Point && el.series && el.series.chart) {
- el.series.chart.runTrackerClick = true;
- }
- // Handle DOM events
- if (addEventListener) {
- addEventListener.call(el, type, fn, false);
- }
- if (!events[type]) {
- events[type] = [];
- }
- events[type].push(fn);
- // Order the calls
- if (options && H.isNumber(options.order)) {
- fn.order = options.order;
- events[type].sort(function (a, b) {
- return a.order - b.order;
- });
- }
- // Return a function that can be called to remove this event.
- return function () {
- H.removeEvent(el, type, fn);
- };
- };
- /**
- * Remove an event that was added with {@link Highcharts#addEvent}.
- *
- * @function Highcharts.removeEvent<T>
- *
- * @param {T} el
- * The element to remove events on.
- *
- * @param {string} [type]
- * The type of events to remove. If undefined, all events are removed
- * from the element.
- *
- * @param {Highcharts.EventCallbackFunction<T>} [fn]
- * The specific callback to remove. If undefined, all events that match
- * the element and optionally the type are removed.
- */
- H.removeEvent = function (el, type, fn) {
- var events,
- index;
- function removeOneEvent(type, fn) {
- var removeEventListener =
- el.removeEventListener || H.removeEventListenerPolyfill;
- if (removeEventListener) {
- removeEventListener.call(el, type, fn, false);
- }
- }
- function removeAllEvents(eventCollection) {
- var types,
- len;
- if (!el.nodeName) {
- return; // break on non-DOM events
- }
- if (type) {
- types = {};
- types[type] = true;
- } else {
- types = eventCollection;
- }
- H.objectEach(types, function (val, n) {
- if (eventCollection[n]) {
- len = eventCollection[n].length;
- while (len--) {
- removeOneEvent(n, eventCollection[n][len]);
- }
- }
- });
- }
- ['protoEvents', 'hcEvents'].forEach(function (coll) {
- var eventCollection = el[coll];
- if (eventCollection) {
- if (type) {
- events = eventCollection[type] || [];
- if (fn) {
- index = events.indexOf(fn);
- if (index > -1) {
- events.splice(index, 1);
- eventCollection[type] = events;
- }
- removeOneEvent(type, fn);
- } else {
- removeAllEvents(eventCollection);
- eventCollection[type] = [];
- }
- } else {
- removeAllEvents(eventCollection);
- el[coll] = {};
- }
- }
- });
- };
- /**
- * Fire an event that was registered with {@link Highcharts#addEvent}.
- *
- * @function Highcharts.fireEvent
- *
- * @param {*} el
- * The object to fire the event on. It can be a {@link HTMLDOMElement},
- * an {@link SVGElement} or any other object.
- *
- * @param {string} type
- * The type of event.
- *
- * @param {Highcharts.Dictionary<*>} [eventArguments]
- * Custom event arguments that are passed on as an argument to the event
- * handler.
- *
- * @param {Function} [defaultFunction]
- * The default function to execute if the other listeners haven't
- * returned false.
- */
- H.fireEvent = function (el, type, eventArguments, defaultFunction) {
- var e,
- events,
- len,
- i,
- fn;
- eventArguments = eventArguments || {};
- if (doc.createEvent && (el.dispatchEvent || el.fireEvent)) {
- e = doc.createEvent('Events');
- e.initEvent(type, true, true);
- H.extend(e, eventArguments);
- if (el.dispatchEvent) {
- el.dispatchEvent(e);
- } else {
- el.fireEvent(type, e);
- }
- } else {
- ['protoEvents', 'hcEvents'].forEach(function (coll) {
- if (el[coll]) {
- events = el[coll][type] || [];
- len = events.length;
- if (!eventArguments.target) { // We're running a custom event
- H.extend(eventArguments, {
- // Attach a simple preventDefault function to skip
- // default handler if called. The built-in
- // defaultPrevented property is not overwritable (#5112)
- preventDefault: function () {
- eventArguments.defaultPrevented = true;
- },
- // Setting target to native events fails with clicking
- // the zoom-out button in Chrome.
- target: el,
- // If the type is not set, we're running a custom event
- // (#2297). If it is set, we're running a browser event,
- // and setting it will cause en error in IE8 (#2465).
- type: type
- });
- }
- for (i = 0; i < len; i++) {
- fn = events[i];
- // If the event handler return false, prevent the default
- // handler from executing
- if (fn && fn.call(el, eventArguments) === false) {
- eventArguments.preventDefault();
- }
- }
- }
- });
- }
- // Run the default if not prevented
- if (defaultFunction && !eventArguments.defaultPrevented) {
- defaultFunction.call(el, eventArguments);
- }
- };
- /**
- * The global animate method, which uses Fx to create individual animators.
- *
- * @function Highcharts.animate
- *
- * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
- * The element to animate.
- *
- * @param {Highcharts.HTMLAttributes|Highcharts.SVGAttributes} params
- * An object containing key-value pairs of the properties to animate.
- * Supports numeric as pixel-based CSS properties for HTML objects and
- * attributes for SVGElements.
- *
- * @param {Highcharts.AnimationOptionsObject} [opt]
- * Animation options.
- */
- H.animate = function (el, params, opt) {
- var start,
- unit = '',
- end,
- fx,
- args;
- if (!H.isObject(opt)) { // Number or undefined/null
- args = arguments;
- opt = {
- duration: args[2],
- easing: args[3],
- complete: args[4]
- };
- }
- if (!H.isNumber(opt.duration)) {
- opt.duration = 400;
- }
- opt.easing = typeof opt.easing === 'function' ?
- opt.easing :
- (Math[opt.easing] || Math.easeInOutSine);
- opt.curAnim = H.merge(params);
- H.objectEach(params, function (val, prop) {
- // Stop current running animation of this property
- H.stop(el, prop);
- fx = new H.Fx(el, opt, prop);
- end = null;
- if (prop === 'd') {
- fx.paths = fx.initPath(
- el,
- el.d,
- params.d
- );
- fx.toD = params.d;
- start = 0;
- end = 1;
- } else if (el.attr) {
- start = el.attr(prop);
- } else {
- start = parseFloat(H.getStyle(el, prop)) || 0;
- if (prop !== 'opacity') {
- unit = 'px';
- }
- }
- if (!end) {
- end = val;
- }
- if (end && end.match && end.match('px')) {
- end = end.replace(/px/g, ''); // #4351
- }
- fx.run(start, end, unit);
- });
- };
- /**
- * Factory to create new series prototypes.
- *
- * @function Highcharts.seriesType
- *
- * @param {string} type
- * The series type name.
- *
- * @param {string} parent
- * The parent series type name. Use `line` to inherit from the basic
- * {@link Series} object.
- *
- * @param {*} options
- * The additional default options that is merged with the parent's
- * options.
- *
- * @param {*} props
- * The properties (functions and primitives) to set on the new
- * prototype.
- *
- * @param {*} [pointProps]
- * Members for a series-specific extension of the {@link Point}
- * prototype if needed.
- *
- * @return {Highcharts.Series}
- * The newly created prototype as extended from {@link Series} or its
- * derivatives.
- */
- // docs: add to API + extending Highcharts
- H.seriesType = function (type, parent, options, props, pointProps) {
- var defaultOptions = H.getOptions(),
- seriesTypes = H.seriesTypes;
- // Merge the options
- defaultOptions.plotOptions[type] = H.merge(
- defaultOptions.plotOptions[parent],
- options
- );
- // Create the class
- seriesTypes[type] = H.extendClass(seriesTypes[parent] ||
- function () {}, props);
- seriesTypes[type].prototype.type = type;
- // Create the point class if needed
- if (pointProps) {
- seriesTypes[type].prototype.pointClass =
- H.extendClass(H.Point, pointProps);
- }
- return seriesTypes[type];
- };
- /**
- * Get a unique key for using in internal element id's and pointers. The key is
- * composed of a random hash specific to this Highcharts instance, and a
- * counter.
- *
- * @example
- * var id = H.uniqueKey(); // => 'highcharts-x45f6hp-0'
- *
- * @function Highcharts.uniqueKey
- *
- * @return {string}
- * A unique key.
- */
- H.uniqueKey = (function () {
- var uniqueKeyHash = Math.random().toString(36).substring(2, 9),
- idCounter = 0;
- return function () {
- return 'highcharts-' + uniqueKeyHash + '-' + idCounter++;
- };
- }());
- H.isFunction = function (obj) {
- return typeof obj === 'function';
- };
- // Register Highcharts as a plugin in jQuery
- if (win.jQuery) {
- /**
- * Highcharts-extended JQuery.
- *
- * @external JQuery
- */
- /**
- * Helper function to return the chart of the current JQuery selector
- * element.
- *
- * @function external:JQuery#highcharts
- *
- * @return {Highcharts.Chart}
- * The chart that is linked to the JQuery selector element.
- *//**
- * Factory function to create a chart in the current JQuery selector
- * element.
- *
- * @function external:JQuery#highcharts
- *
- * @param {"Chart"|"Map"|"StockChart"|string} [className]
- * Name of the factory class in the Highcharts namespace.
- *
- * @param {Highcharts.Options} [options]
- * The chart options structure.
- *
- * @param {Highcharts.ChartCallbackFunction} [callback]
- * Function to run when the chart has loaded and and all external
- * images are loaded. Defining a
- * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
- * handler is equivalent.
- *
- * @return {JQuery}
- * The current JQuery selector.
- */
- win.jQuery.fn.highcharts = function () {
- var args = [].slice.call(arguments);
- if (this[0]) { // this[0] is the renderTo div
- // Create the chart
- if (args[0]) {
- new H[ // eslint-disable-line no-new
- // Constructor defaults to Chart
- H.isString(args[0]) ? args.shift() : 'Chart'
- ](this[0], args[0], args[1]);
- return this;
- }
- // When called without parameters or with the return argument,
- // return an existing chart
- return charts[H.attr(this[0], 'data-highcharts-chart')];
- }
- };
- }
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * A valid color to be parsed and handled by Highcharts. Highcharts internally
- * supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
- * rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
- * browsers and displayed correctly, but Highcharts is not able to process them
- * and apply concepts like opacity and brightening.
- *
- * @typedef {string} Highcharts.ColorString
- */
- var isNumber = H.isNumber,
- merge = H.merge,
- pInt = H.pInt;
- /**
- * Handle color operations. The object methods are chainable.
- *
- * @private
- * @class
- * @name Highcharts.Color
- *
- * @param {Highcharts.ColorString} input
- * The input color in either rbga or hex format
- */
- H.Color = function (input) {
- // Backwards compatibility, allow instanciation without new
- if (!(this instanceof H.Color)) {
- return new H.Color(input);
- }
- // Initialize
- this.init(input);
- };
- H.Color.prototype = {
- // Collection of parsers. This can be extended from the outside by pushing
- // parsers to Highcharts.Color.prototype.parsers.
- parsers: [{
- // RGBA color
- 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
- parse: function (result) {
- return [
- pInt(result[1]),
- pInt(result[2]),
- pInt(result[3]),
- parseFloat(result[4], 10)
- ];
- }
- }, {
- // RGB color
- regex:
- /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
- parse: function (result) {
- return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
- }
- }],
- // Collection of named colors. Can be extended from the outside by adding
- // colors to Highcharts.Color.prototype.names.
- names: {
- white: '#ffffff',
- black: '#000000'
- },
- /**
- * Parse the input color to rgba array
- *
- * @private
- * @function Highcharts.Color#init
- *
- * @param {Highcharts.ColorString} input
- * The input color in either rbga or hex format
- */
- init: function (input) {
- var result,
- rgba,
- i,
- parser,
- len;
- this.input = input = this.names[
- input && input.toLowerCase ?
- input.toLowerCase() :
- ''
- ] || input;
- // Gradients
- if (input && input.stops) {
- this.stops = input.stops.map(function (stop) {
- return new H.Color(stop[1]);
- });
- // Solid colors
- } else {
- // Bitmasking as input[0] is not working for legacy IE.
- if (input && input.charAt && input.charAt() === '#') {
- len = input.length;
- input = parseInt(input.substr(1), 16);
- // Handle long-form, e.g. #AABBCC
- if (len === 7) {
- rgba = [
- (input & 0xFF0000) >> 16,
- (input & 0xFF00) >> 8,
- (input & 0xFF),
- 1
- ];
- // Handle short-form, e.g. #ABC
- // In short form, the value is assumed to be the same
- // for both nibbles for each component. e.g. #ABC = #AABBCC
- } else if (len === 4) {
- rgba = [
- ((input & 0xF00) >> 4) | (input & 0xF00) >> 8,
- ((input & 0xF0) >> 4) | (input & 0xF0),
- ((input & 0xF) << 4) | (input & 0xF),
- 1
- ];
- }
- }
- // Otherwise, check regex parsers
- if (!rgba) {
- i = this.parsers.length;
- while (i-- && !rgba) {
- parser = this.parsers[i];
- result = parser.regex.exec(input);
- if (result) {
- rgba = parser.parse(result);
- }
- }
- }
- }
- this.rgba = rgba || [];
- },
- /**
- * Return the color in the specified format
- *
- * @function Highcharts.Color#get
- *
- * @param {string} format
- * Possible values are 'a', 'rgb', undefined
- *
- * @return {Highcharts.ColorString}
- * This color as a string.
- */
- get: function (format) {
- var input = this.input,
- rgba = this.rgba,
- ret;
- if (this.stops) {
- ret = merge(input);
- ret.stops = [].concat(ret.stops);
- this.stops.forEach(function (stop, i) {
- ret.stops[i] = [ret.stops[i][0], stop.get(format)];
- });
- // it's NaN if gradient colors on a column chart
- } else if (rgba && isNumber(rgba[0])) {
- if (format === 'rgb' || (!format && rgba[3] === 1)) {
- ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
- } else if (format === 'a') {
- ret = rgba[3];
- } else {
- ret = 'rgba(' + rgba.join(',') + ')';
- }
- } else {
- ret = input;
- }
- return ret;
- },
- /**
- * Brighten the color instance.
- *
- * @function Highcharts.Color#brighten
- *
- * @param {number} alpha
- * The alpha value.
- *
- * @return {Highcharts.ColorString}
- * This color with modifications.
- */
- brighten: function (alpha) {
- var i,
- rgba = this.rgba;
- if (this.stops) {
- this.stops.forEach(function (stop) {
- stop.brighten(alpha);
- });
- } else if (isNumber(alpha) && alpha !== 0) {
- for (i = 0; i < 3; i++) {
- rgba[i] += pInt(alpha * 255);
- if (rgba[i] < 0) {
- rgba[i] = 0;
- }
- if (rgba[i] > 255) {
- rgba[i] = 255;
- }
- }
- }
- return this;
- },
- /**
- * Set the color's opacity to a given alpha value.
- *
- * @function Highcharts.Color#setOpacity
- *
- * @param {number} alpha
- * Opacity between 0 and 1.
- *
- * @return {Highcharts.ColorString}
- * Color with modifications.
- */
- setOpacity: function (alpha) {
- this.rgba[3] = alpha;
- return this;
- },
- /**
- * Return an intermediate color between two colors.
- *
- * @function Highcharts.Color#tweenTo
- *
- * @param {Highcharts.Color} to
- * The color object to tween to.
- *
- * @param {number} pos
- * The intermediate position, where 0 is the from color (current
- * color item), and 1 is the `to` color.
- *
- * @return {Highcharts.ColorString}
- * The intermediate color in rgba notation.
- */
- tweenTo: function (to, pos) {
- // Check for has alpha, because rgba colors perform worse due to lack of
- // support in WebKit.
- var fromRgba = this.rgba,
- toRgba = to.rgba,
- hasAlpha,
- ret;
- // Unsupported color, return to-color (#3920, #7034)
- if (!toRgba.length || !fromRgba || !fromRgba.length) {
- ret = to.input || 'none';
- // Interpolate
- } else {
- hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
- ret = (hasAlpha ? 'rgba(' : 'rgb(') +
- Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
- ',' +
- Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
- ',' +
- Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
- (
- hasAlpha ?
- (
- ',' +
- (toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))
- ) :
- ''
- ) +
- ')';
- }
- return ret;
- }
- };
- /**
- * Creates a color instance out of a color string.
- *
- * @private
- * @function Highcharts.color
- *
- * @param {Highcharts.ColorString} input
- * The input color in either rbga or hex format
- */
- H.color = function (input) {
- return new H.Color(input);
- };
- }(Highcharts));
- (function (H) {
- /* *
- *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- *
- * */
- /**
- * The horizontal alignment of an element.
- *
- * @typedef {"center"|"left"|"right"} Highcharts.AlignType
- */
- /**
- * Options to align the element relative to the chart or another box.
- *
- * @interface Highcharts.AlignObject
- *//**
- * Horizontal alignment. Can be one of `left`, `center` and `right`.
- *
- * @name Highcharts.AlignObject#align
- * @type {Highcharts.AlignType|undefined}
- *
- * @default left
- *//**
- * Vertical alignment. Can be one of `top`, `middle` and `bottom`.
- *
- * @name Highcharts.AlignObject#verticalAlign
- * @type {Highcharts.VerticalAlignType|undefined}
- *
- * @default top
- *//**
- * Horizontal pixel offset from alignment.
- *
- * @name Highcharts.AlignObject#x
- * @type {number|undefined}
- *
- * @default 0
- *//**
- * Vertical pixel offset from alignment.
- *
- * @name Highcharts.AlignObject#y
- * @type {number|undefined}
- *
- * @default 0
- *//**
- * Use the `transform` attribute with translateX and translateY custom
- * attributes to align this elements rather than `x` and `y` attributes.
- *
- * @name Highcharts.AlignObject#alignByTranslate
- * @type {boolean|undefined}
- *
- * @default false
- */
- /**
- * Bounding box of an element.
- *
- * @interface Highcharts.BBoxObject
- *//**
- * Height of the bounding box.
- *
- * @name Highcharts.BBoxObject#height
- * @type {number}
- *//**
- * Width of the bounding box.
- *
- * @name Highcharts.BBoxObject#width
- * @type {number}
- *//**
- * Horizontal position of the bounding box.
- *
- * @name Highcharts.BBoxObject#x
- * @type {number}
- *//**
- * Vertical position of the bounding box.
- *
- * @name Highcharts.BBoxObject#y
- * @type {number}
- */
- /**
- * A clipping rectangle that can be applied to one or more {@link SVGElement}
- * instances. It is instanciated with the {@link SVGRenderer#clipRect} function
- * and applied with the {@link SVGElement#clip} function.
- *
- * @example
- * var circle = renderer.circle(100, 100, 100)
- * .attr({ fill: 'red' })
- * .add();
- * var clipRect = renderer.clipRect(100, 100, 100, 100);
- *
- * // Leave only the lower right quarter visible
- * circle.clip(clipRect);
- *
- * @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
- */
- /**
- * The font metrics.
- *
- * @interface Highcharts.FontMetricsObject
- *//**
- * The baseline relative to the top of the box.
- *
- * @name Highcharts.FontMetricsObject#b
- * @type {number}
- *//**
- * The line height.
- *
- * @name Highcharts.FontMetricsObject#h
- * @type {number}
- *//**
- * The font size.
- *
- * @name Highcharts.FontMetricsObject#f
- * @type {number}
- */
- /**
- * Gradient options instead of a solid color.
- *
- * @example
- * // Linear gradient used as a color option
- * color: {
- * linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
- * stops: [
- * [0, '#003399'], // start
- * [0.5, '#ffffff'], // middle
- * [1, '#3366AA'] // end
- * ]
- * }
- * }
- *
- * @interface Highcharts.GradientColorObject
- *//**
- * Holds an object that defines the start position and the end position relative
- * to the shape.
- * @name Highcharts.GradientColorObject#linearGradient
- * @type {Highcharts.LinearGradientColorObject|undefined}
- *//**
- * Holds an object that defines the center position and the radius.
- * @name Highcharts.GradientColorObject#radialGradient
- * @type {Highcharts.RadialGradientColorObject|undefined}
- *//**
- * The first item in each tuple is the position in the gradient, where 0 is the
- * start of the gradient and 1 is the end of the gradient. Multiple stops can be
- * applied. The second item is the color for each stop. This color can also be
- * given in the rgba format.
- * @name Highcharts.GradientColorObject#stops
- * @type {Array<Array<number,Highcharts.ColorString>>|undefined}
- */
- /**
- * Defines the start position and the end position for a gradient relative
- * to the shape. Start position (x1, y1) and end position (x2, y2) are relative
- * to the shape, where 0 means top/left and 1 is bottom/right.
- *
- * @interface Highcharts.LinearGradientColorObject
- *//**
- * Start horizontal position of the gradient. Float ranges 0-1.
- * @name Highcharts.LinearGradientColorObject#x1
- * @type {number}
- *//**
- * End horizontal position of the gradient. Float ranges 0-1.
- * @name Highcharts.LinearGradientColorObject#x2
- * @type {number}
- *//**
- * Start vertical position of the gradient. Float ranges 0-1.
- * @name Highcharts.LinearGradientColorObject#y1
- * @type {number}
- *//**
- * End vertical position of the gradient. Float ranges 0-1.
- * @name Highcharts.LinearGradientColorObject#y2
- * @type {number}
- */
- /**
- * An object containing `x` and `y` properties for the position of an element.
- *
- * @interface Highcharts.PositionObject
- *//**
- * X position of the element.
- * @name Highcharts.PositionObject#x
- * @type {number}
- *//**
- * Y position of the element.
- * @name Highcharts.PositionObject#y
- * @type {number}
- */
- /**
- * Defines the center position and the radius for a gradient.
- *
- * @interface Highcharts.RadialGradientColorObject
- *//**
- * Center horizontal position relative to the shape. Float ranges 0-1.
- * @name Highcharts.RadialGradientColorObject#cx
- * @type {number}
- *//**
- * Center vertical position relative to the shape. Float ranges 0-1.
- * @name Highcharts.RadialGradientColorObject#cy
- * @type {number}
- *//**
- * Radius relative to the shape. Float ranges 0-1.
- * @name Highcharts.RadialGradientColorObject#r
- * @type {number}
- */
- /**
- * A rectangle.
- *
- * @interface Highcharts.RectangleObject
- *//**
- * Height of the rectangle.
- * @name Highcharts.RectangleObject#height
- * @type {number}
- *//**
- * Width of the rectangle.
- * @name Highcharts.RectangleObject#width
- * @type {number}
- *//**
- * Horizontal position of the rectangle.
- * @name Highcharts.RectangleObject#x
- * @type {number}
- *//**
- * Vertical position of the rectangle.
- * @name Highcharts.RectangleObject#y
- * @type {number}
- */
- /**
- * The shadow options.
- *
- * @interface Highcharts.ShadowOptionsObject
- *//**
- * The shadow color.
- * @name Highcharts.ShadowOptionsObject#color
- * @type {string|undefined}
- * @default #000000
- *//**
- * The horizontal offset from the element.
- *
- * @name Highcharts.ShadowOptionsObject#offsetX
- * @type {number|undefined}
- * @default 1
- *//**
- * The vertical offset from the element.
- * @name Highcharts.ShadowOptionsObject#offsetY
- * @type {number|undefined}
- * @default 1
- *//**
- * The shadow opacity.
- *
- * @name Highcharts.ShadowOptionsObject#opacity
- * @type {number|undefined}
- * @default 0.15
- *//**
- * The shadow width or distance from the element.
- * @name Highcharts.ShadowOptionsObject#width
- * @type {number|undefined}
- * @default 3
- */
- /**
- * Serialized form of an SVG definition, including children. Some key
- * property names are reserved: tagName, textContent, and children.
- *
- * @interface Highcharts.SVGDefinitionObject
- *//**
- * @name Highcharts.SVGDefinitionObject#[key:string]
- * @type {number|string|Array<Highcharts.SVGDefinitionObject>|undefined}
- *//**
- * @name Highcharts.SVGDefinitionObject#children
- * @type {Array<Highcharts.SVGDefinitionObject>|undefined}
- *//**
- * @name Highcharts.SVGDefinitionObject#tagName
- * @type {string|undefined}
- *//**
- * @name Highcharts.SVGDefinitionObject#textContent
- * @type {string|undefined}
- */
- /**
- * An extendable collection of functions for defining symbol paths.
- *
- * @typedef Highcharts.SymbolDictionary
- *
- * @property {Function|undefined} [key:Highcharts.SymbolKey]
- */
- /**
- * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`,
- * `triangle`, `triangle-down`. Symbols are used internally for point
- * markers, button and label borders and backgrounds, or custom shapes.
- * Extendable by adding to {@link SVGRenderer#symbols}.
- *
- * @typedef {string} Highcharts.SymbolKey
- * @validvalue ["arc", "callout", "circle", "diamond", "square", "triangle",
- * "triangle-down"]
- */
- /**
- * Additional options, depending on the actual symbol drawn.
- *
- * @interface Highcharts.SymbolOptionsObject
- *//**
- * The anchor X position for the `callout` symbol. This is where the chevron
- * points to.
- *
- * @name Highcharts.SymbolOptionsObject#anchorX
- * @type {number}
- *//**
- * The anchor Y position for the `callout` symbol. This is where the chevron
- * points to.
- *
- * @name Highcharts.SymbolOptionsObject#anchorY
- * @type {number}
- *//**
- * The end angle of an `arc` symbol.
- *
- * @name Highcharts.SymbolOptionsObject#end
- * @type {number}
- *//**
- * Whether to draw `arc` symbol open or closed.
- *
- * @name Highcharts.SymbolOptionsObject#open
- * @type {boolean}
- *//**
- * The radius of an `arc` symbol, or the border radius for the `callout` symbol.
- *
- * @name Highcharts.SymbolOptionsObject#r
- * @type {number}
- *//**
- * The start angle of an `arc` symbol.
- *
- * @name Highcharts.SymbolOptionsObject#start
- * @type {number}
- */
- /**
- * The vertical alignment of an element.
- *
- * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignType
- */
- var SVGElement,
- SVGRenderer,
- addEvent = H.addEvent,
- animate = H.animate,
- attr = H.attr,
- charts = H.charts,
- color = H.color,
- css = H.css,
- createElement = H.createElement,
- defined = H.defined,
- deg2rad = H.deg2rad,
- destroyObjectProperties = H.destroyObjectProperties,
- doc = H.doc,
- extend = H.extend,
- erase = H.erase,
- hasTouch = H.hasTouch,
- isArray = H.isArray,
- isFirefox = H.isFirefox,
- isMS = H.isMS,
- isObject = H.isObject,
- isString = H.isString,
- isWebKit = H.isWebKit,
- merge = H.merge,
- noop = H.noop,
- objectEach = H.objectEach,
- pick = H.pick,
- pInt = H.pInt,
- removeEvent = H.removeEvent,
- splat = H.splat,
- stop = H.stop,
- svg = H.svg,
- SVG_NS = H.SVG_NS,
- symbolSizes = H.symbolSizes,
- win = H.win;
- /**
- * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
- * rendering layer of Highcharts. Combined with the {@link
- * Highcharts.SVGRenderer} object, these prototypes allow freeform annotation
- * in the charts or even in HTML pages without instanciating a chart. The
- * SVGElement can also wrap HTML labels, when `text` or `label` elements are
- * created with the `useHTML` parameter.
- *
- * The SVGElement instances are created through factory functions on the {@link
- * Highcharts.SVGRenderer} object, like {@link Highcharts.SVGRenderer#rect|
- * rect}, {@link Highcharts.SVGRenderer#path|path}, {@link
- * Highcharts.SVGRenderer#text|text}, {@link Highcharts.SVGRenderer#label|
- * label}, {@link Highcharts.SVGRenderer#g|g} and more.
- *
- * @class
- * @name Highcharts.SVGElement
- */
- SVGElement = H.SVGElement = function () {
- return this;
- };
- extend(SVGElement.prototype, /** @lends Highcharts.SVGElement.prototype */ {
- // Default base for animation
- opacity: 1,
- SVG_NS: SVG_NS,
- /**
- * For labels, these CSS properties are applied to the `text` node directly.
- *
- * @private
- * @name Highcharts.SVGElement#textProps
- * @type {Array<string>}
- */
- textProps: ['direction', 'fontSize', 'fontWeight', 'fontFamily',
- 'fontStyle', 'color', 'lineHeight', 'width', 'textAlign',
- 'textDecoration', 'textOverflow', 'textOutline', 'cursor'],
- /**
- * Initialize the SVG element. This function only exists to make the
- * initiation process overridable. It should not be called directly.
- *
- * @function Highcharts.SVGElement#init
- *
- * @param {Highcharts.SVGRenderer} renderer
- * The SVGRenderer instance to initialize to.
- *
- * @param {string} nodeName
- * The SVG node name.
- */
- init: function (renderer, nodeName) {
- /**
- * The primary DOM node. Each `SVGElement` instance wraps a main DOM
- * node, but may also represent more nodes.
- *
- * @name Highcharts.SVGElement#element
- * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
- */
- this.element = nodeName === 'span' ?
- createElement(nodeName) :
- doc.createElementNS(this.SVG_NS, nodeName);
- /**
- * The renderer that the SVGElement belongs to.
- *
- * @name Highcharts.SVGElement#renderer
- * @type {Highcharts.SVGRenderer}
- */
- this.renderer = renderer;
- H.fireEvent(this, 'afterInit');
- },
- /**
- * Animate to given attributes or CSS properties.
- *
- * @sample highcharts/members/element-on/
- * Setting some attributes by animation
- *
- * @function Highcharts.SVGElement#animate
- *
- * @param {Highcharts.SVGAttributes} params
- * SVG attributes or CSS to animate.
- *
- * @param {Highcharts.AnimationOptionsObject} [options]
- * Animation options.
- *
- * @param {Function} [complete]
- * Function to perform at the end of animation.
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVGElement for chaining.
- */
- animate: function (params, options, complete) {
- var animOptions = H.animObject(
- pick(options, this.renderer.globalAnimation, true)
- );
- // When the page is hidden save resources in the background by not
- // running animation at all (#9749).
- if (pick(doc.hidden, doc.msHidden, doc.webkitHidden, false)) {
- animOptions.duration = 0;
- }
- if (animOptions.duration !== 0) {
- // allows using a callback with the global animation without
- // overwriting it
- if (complete) {
- animOptions.complete = complete;
- }
- animate(this, params, animOptions);
- } else {
- this.attr(params, null, complete);
- if (animOptions.step) {
- animOptions.step.call(this);
- }
- }
- return this;
- },
- /**
- * Build and apply an SVG gradient out of a common JavaScript configuration
- * object. This function is called from the attribute setters. An event
- * hook is added for supporting other complex color types.
- *
- * @private
- * @function Highcharts.SVGElement#complexColor
- *
- * @param {Highcharts.GradientColorObject} color
- * The gradient options structure.
- *
- * @param {string} prop
- * The property to apply, can either be `fill` or `stroke`.
- *
- * @param {Highcharts.SVGDOMElement} elem
- * SVG DOM element to apply the gradient on.
- */
- complexColor: function (color, prop, elem) {
- var renderer = this.renderer,
- colorObject,
- gradName,
- gradAttr,
- radAttr,
- gradients,
- gradientObject,
- stops,
- stopColor,
- stopOpacity,
- radialReference,
- id,
- key = [],
- value;
- H.fireEvent(this.renderer, 'complexColor', {
- args: arguments
- }, function () {
- // Apply linear or radial gradients
- if (color.radialGradient) {
- gradName = 'radialGradient';
- } else if (color.linearGradient) {
- gradName = 'linearGradient';
- }
- if (gradName) {
- gradAttr = color[gradName];
- gradients = renderer.gradients;
- stops = color.stops;
- radialReference = elem.radialReference;
- // Keep < 2.2 kompatibility
- if (isArray(gradAttr)) {
- color[gradName] = gradAttr = {
- x1: gradAttr[0],
- y1: gradAttr[1],
- x2: gradAttr[2],
- y2: gradAttr[3],
- gradientUnits: 'userSpaceOnUse'
- };
- }
- // Correct the radial gradient for the radial reference system
- if (
- gradName === 'radialGradient' &&
- radialReference &&
- !defined(gradAttr.gradientUnits)
- ) {
- // Save the radial attributes for updating
- radAttr = gradAttr;
- gradAttr = merge(
- gradAttr,
- renderer.getRadialAttr(radialReference, radAttr),
- { gradientUnits: 'userSpaceOnUse' }
- );
- }
- // Build the unique key to detect whether we need to create a
- // new element (#1282)
- objectEach(gradAttr, function (val, n) {
- if (n !== 'id') {
- key.push(n, val);
- }
- });
- objectEach(stops, function (val) {
- key.push(val);
- });
- key = key.join(',');
- // Check if a gradient object with the same config object is
- // created within this renderer
- if (gradients[key]) {
- id = gradients[key].attr('id');
- } else {
- // Set the id and create the element
- gradAttr.id = id = H.uniqueKey();
- gradients[key] = gradientObject =
- renderer.createElement(gradName)
- .attr(gradAttr)
- .add(renderer.defs);
- gradientObject.radAttr = radAttr;
- // The gradient needs to keep a list of stops to be able to
- // destroy them
- gradientObject.stops = [];
- stops.forEach(function (stop) {
- var stopObject;
- if (stop[1].indexOf('rgba') === 0) {
- colorObject = H.color(stop[1]);
- stopColor = colorObject.get('rgb');
- stopOpacity = colorObject.get('a');
- } else {
- stopColor = stop[1];
- stopOpacity = 1;
- }
- stopObject = renderer.createElement('stop').attr({
- offset: stop[0],
- 'stop-color': stopColor,
- 'stop-opacity': stopOpacity
- }).add(gradientObject);
- // Add the stop element to the gradient
- gradientObject.stops.push(stopObject);
- });
- }
- // Set the reference to the gradient object
- value = 'url(' + renderer.url + '#' + id + ')';
- elem.setAttribute(prop, value);
- elem.gradient = key;
- // Allow the color to be concatenated into tooltips formatters
- // etc. (#2995)
- color.toString = function () {
- return value;
- };
- }
- });
- },
- /**
- * Apply a text outline through a custom CSS property, by copying the text
- * element and apply stroke to the copy. Used internally. Contrast checks at
- * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
- *
- * @example
- * // Specific color
- * text.css({
- * textOutline: '1px black'
- * });
- * // Automatic contrast
- * text.css({
- * color: '#000000', // black text
- * textOutline: '1px contrast' // => white outline
- * });
- *
- * @private
- * @function Highcharts.SVGElement#applyTextOutline
- *
- * @param {string} textOutline
- * A custom CSS `text-outline` setting, defined by `width color`.
- */
- applyTextOutline: function (textOutline) {
- var elem = this.element,
- tspans,
- tspan,
- hasContrast = textOutline.indexOf('contrast') !== -1,
- styles = {},
- color,
- strokeWidth,
- firstRealChild,
- i;
- // When the text shadow is set to contrast, use dark stroke for light
- // text and vice versa.
- if (hasContrast) {
- styles.textOutline = textOutline = textOutline.replace(
- /contrast/g,
- this.renderer.getContrast(elem.style.fill)
- );
- }
- // Extract the stroke width and color
- textOutline = textOutline.split(' ');
- color = textOutline[textOutline.length - 1];
- strokeWidth = textOutline[0];
- if (strokeWidth && strokeWidth !== 'none' && H.svg) {
- this.fakeTS = true; // Fake text shadow
- tspans = [].slice.call(elem.getElementsByTagName('tspan'));
- // In order to get the right y position of the clone,
- // copy over the y setter
- this.ySetter = this.xSetter;
- // Since the stroke is applied on center of the actual outline, we
- // need to double it to get the correct stroke-width outside the
- // glyphs.
- strokeWidth = strokeWidth.replace(
- /(^[\d\.]+)(.*?)$/g,
- function (match, digit, unit) {
- return (2 * digit) + unit;
- }
- );
- // Remove shadows from previous runs. Iterate from the end to
- // support removing items inside the cycle (#6472).
- i = tspans.length;
- while (i--) {
- tspan = tspans[i];
- if (tspan.getAttribute('class') === 'highcharts-text-outline') {
- // Remove then erase
- erase(tspans, elem.removeChild(tspan));
- }
- }
- // For each of the tspans, create a stroked copy behind it.
- firstRealChild = elem.firstChild;
- tspans.forEach(function (tspan, y) {
- var clone;
- // Let the first line start at the correct X position
- if (y === 0) {
- tspan.setAttribute('x', elem.getAttribute('x'));
- y = elem.getAttribute('y');
- tspan.setAttribute('y', y || 0);
- if (y === null) {
- elem.setAttribute('y', 0);
- }
- }
- // Create the clone and apply outline properties
- clone = tspan.cloneNode(1);
- attr(clone, {
- 'class': 'highcharts-text-outline',
- 'fill': color,
- 'stroke': color,
- 'stroke-width': strokeWidth,
- 'stroke-linejoin': 'round'
- });
- elem.insertBefore(clone, firstRealChild);
- });
- }
- },
- // Custom attributes used for symbols, these should be filtered out when
- // setting SVGElement attributes (#9375).
- symbolCustomAttribs: [
- 'x',
- 'y',
- 'width',
- 'height',
- 'r',
- 'start',
- 'end',
- 'innerR',
- 'anchorX',
- 'anchorY',
- 'rounded'
- ],
- /**
- * Apply native and custom attributes to the SVG elements.
- *
- * In order to set the rotation center for rotation, set x and y to 0 and
- * use `translateX` and `translateY` attributes to position the element
- * instead.
- *
- * Attributes frequently used in Highcharts are `fill`, `stroke`,
- * `stroke-width`.
- *
- * @sample highcharts/members/renderer-rect/
- * Setting some attributes
- *
- * @example
- * // Set multiple attributes
- * element.attr({
- * stroke: 'red',
- * fill: 'blue',
- * x: 10,
- * y: 10
- * });
- *
- * // Set a single attribute
- * element.attr('stroke', 'red');
- *
- * // Get an attribute
- * element.attr('stroke'); // => 'red'
- *
- * @function Highcharts.SVGElement#attr
- *
- * @param {string|Highcharts.SVGAttributes} [hash]
- * The native and custom SVG attributes.
- *
- * @param {string} [val]
- * If the type of the first argument is `string`, the second can be a
- * value, which will serve as a single attribute setter. If the first
- * argument is a string and the second is undefined, the function
- * serves as a getter and the current value of the property is
- * returned.
- *
- * @param {Function} [complete]
- * A callback function to execute after setting the attributes. This
- * makes the function compliant and interchangeable with the
- * {@link SVGElement#animate} function.
- *
- * @param {boolean} [continueAnimation=true]
- * Used internally when `.attr` is called as part of an animation
- * step. Otherwise, calling `.attr` for an attribute will stop
- * animation for that attribute.
- *
- * @return {number|string|Highcharts.SVGElement}
- * If used as a setter, it returns the current
- * {@link Highcharts.SVGElement} so the calls can be chained. If
- * used as a getter, the current value of the attribute is returned.
- */
- attr: function (hash, val, complete, continueAnimation) {
- var key,
- element = this.element,
- hasSetSymbolSize,
- ret = this,
- skipAttr,
- setter,
- symbolCustomAttribs = this.symbolCustomAttribs;
- // single key-value pair
- if (typeof hash === 'string' && val !== undefined) {
- key = hash;
- hash = {};
- hash[key] = val;
- }
- // used as a getter: first argument is a string, second is undefined
- if (typeof hash === 'string') {
- ret = (this[hash + 'Getter'] || this._defaultGetter).call(
- this,
- hash,
- element
- );
- // setter
- } else {
- objectEach(hash, function eachAttribute(val, key) {
- skipAttr = false;
- // Unless .attr is from the animator update, stop current
- // running animation of this property
- if (!continueAnimation) {
- stop(this, key);
- }
- // Special handling of symbol attributes
- if (
- this.symbolName &&
- H.inArray(key, symbolCustomAttribs) !== -1
- ) {
- if (!hasSetSymbolSize) {
- this.symbolAttr(hash);
- hasSetSymbolSize = true;
- }
- skipAttr = true;
- }
- if (this.rotation && (key === 'x' || key === 'y')) {
- this.doTransform = true;
- }
- if (!skipAttr) {
- setter = this[key + 'Setter'] || this._defaultSetter;
- setter.call(this, val, key, element);
- // Let the shadow follow the main element
- if (
- !this.styledMode &&
- this.shadows &&
- /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/
- .test(key)
- ) {
- this.updateShadows(key, val, setter);
- }
- }
- }, this);
- this.afterSetters();
- }
- // In accordance with animate, run a complete callback
- if (complete) {
- complete.call(this);
- }
- return ret;
- },
- /**
- * This method is executed in the end of `attr()`, after setting all
- * attributes in the hash. In can be used to efficiently consolidate
- * multiple attributes in one SVG property -- e.g., translate, rotate and
- * scale are merged in one "transform" attribute in the SVG node.
- *
- * @private
- * @function Highcharts.SVGElement#afterSetters
- */
- afterSetters: function () {
- // Update transform. Do this outside the loop to prevent redundant
- // updating for batch setting of attributes.
- if (this.doTransform) {
- this.updateTransform();
- this.doTransform = false;
- }
- },
- /**
- * Update the shadow elements with new attributes.
- *
- * @private
- * @function Highcharts.SVGElement#updateShadows
- *
- * @param {string} key
- * The attribute name.
- *
- * @param {string|number} value
- * The value of the attribute.
- *
- * @param {Function} setter
- * The setter function, inherited from the parent wrapper.
- */
- updateShadows: function (key, value, setter) {
- var shadows = this.shadows,
- i = shadows.length;
- while (i--) {
- setter.call(
- shadows[i],
- key === 'height' ?
- Math.max(value - (shadows[i].cutHeight || 0), 0) :
- key === 'd' ? this.d : value,
- key,
- shadows[i]
- );
- }
- },
- /**
- * Add a class name to an element.
- *
- * @function Highcharts.SVGElement#addClass
- *
- * @param {string} className
- * The new class name to add.
- *
- * @param {boolean} [replace=false]
- * When true, the existing class name(s) will be overwritten with
- * the new one. When false, the new one is added.
- *
- * @return {Highcharts.SVGElement}
- * Return the SVG element for chainability.
- */
- addClass: function (className, replace) {
- var currentClassName = this.attr('class') || '';
- if (currentClassName.indexOf(className) === -1) {
- if (!replace) {
- className =
- (currentClassName + (currentClassName ? ' ' : '') +
- className).replace(' ', ' ');
- }
- this.attr('class', className);
- }
- return this;
- },
- /**
- * Check if an element has the given class name.
- *
- * @function Highcharts.SVGElement#hasClass
- *
- * @param {string} className
- * The class name to check for.
- *
- * @return {boolean}
- * Whether the class name is found.
- */
- hasClass: function (className) {
- return (this.attr('class') || '').split(' ').indexOf(className) !== -1;
- },
- /**
- * Remove a class name from the element.
- *
- * @function Highcharts.SVGElement#removeClass
- *
- * @param {string|RegExp} className
- * The class name to remove.
- *
- * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
- */
- removeClass: function (className) {
- return this.attr(
- 'class',
- (this.attr('class') || '').replace(className, '')
- );
- },
- /**
- * If one of the symbol size affecting parameters are changed,
- * check all the others only once for each call to an element's
- * .attr() method
- *
- * @private
- * @function Highcharts.SVGElement#symbolAttr
- *
- * @param {Highcharts.Dictionary<number|string>} hash
- * The attributes to set.
- */
- symbolAttr: function (hash) {
- var wrapper = this;
- [
- 'x',
- 'y',
- 'r',
- 'start',
- 'end',
- 'width',
- 'height',
- 'innerR',
- 'anchorX',
- 'anchorY'
- ].forEach(function (key) {
- wrapper[key] = pick(hash[key], wrapper[key]);
- });
- wrapper.attr({
- d: wrapper.renderer.symbols[wrapper.symbolName](
- wrapper.x,
- wrapper.y,
- wrapper.width,
- wrapper.height,
- wrapper
- )
- });
- },
- /**
- * Apply a clipping rectangle to this element.
- *
- * @function Highcharts.SVGElement#clip
- *
- * @param {Highcharts.ClipRectElement} [clipRect]
- * The clipping rectangle. If skipped, the current clip is removed.
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVG element to allow chaining.
- */
- clip: function (clipRect) {
- return this.attr(
- 'clip-path',
- clipRect ?
- 'url(' + this.renderer.url + '#' + clipRect.id + ')' :
- 'none'
- );
- },
- /**
- * Calculate the coordinates needed for drawing a rectangle crisply and
- * return the calculated attributes.
- *
- * @function Highcharts.SVGElement#crisp
- *
- * @param {Highcharts.RectangleObject} rect
- * Rectangle to crisp.
- *
- * @param {number} [strokeWidth]
- * The stroke width to consider when computing crisp positioning. It
- * can also be set directly on the rect parameter.
- *
- * @return {Highcharts.RectangleObject}
- * The modified rectangle arguments.
- */
- crisp: function (rect, strokeWidth) {
- var wrapper = this,
- normalizer;
- strokeWidth = strokeWidth || rect.strokeWidth || 0;
- // Math.round because strokeWidth can sometimes have roundoff errors
- normalizer = Math.round(strokeWidth) % 2 / 2;
- // normalize for crisp edges
- rect.x = Math.floor(rect.x || wrapper.x || 0) + normalizer;
- rect.y = Math.floor(rect.y || wrapper.y || 0) + normalizer;
- rect.width = Math.floor(
- (rect.width || wrapper.width || 0) - 2 * normalizer
- );
- rect.height = Math.floor(
- (rect.height || wrapper.height || 0) - 2 * normalizer
- );
- if (defined(rect.strokeWidth)) {
- rect.strokeWidth = strokeWidth;
- }
- return rect;
- },
- /**
- * Set styles for the element. In addition to CSS styles supported by
- * native SVG and HTML elements, there are also some custom made for
- * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
- * elements.
- *
- * @sample highcharts/members/renderer-text-on-chart/
- * Styled text
- *
- * @function Highcharts.SVGElement#css
- *
- * @param {Highcharts.CSSObject} styles
- * The new CSS styles.
- *
- * @return {Highcharts.SVGElement}
- * Return the SVG element for chaining.
- */
- css: function (styles) {
- var oldStyles = this.styles,
- newStyles = {},
- elem = this.element,
- textWidth,
- serializedCss = '',
- hyphenate,
- hasNew = !oldStyles,
- // These CSS properties are interpreted internally by the SVG
- // renderer, but are not supported by SVG and should not be added to
- // the DOM. In styled mode, no CSS should find its way to the DOM
- // whatsoever (#6173, #6474).
- svgPseudoProps = ['textOutline', 'textOverflow', 'width'];
- // convert legacy
- if (styles && styles.color) {
- styles.fill = styles.color;
- }
- // Filter out existing styles to increase performance (#2640)
- if (oldStyles) {
- objectEach(styles, function (style, n) {
- if (style !== oldStyles[n]) {
- newStyles[n] = style;
- hasNew = true;
- }
- });
- }
- if (hasNew) {
- // Merge the new styles with the old ones
- if (oldStyles) {
- styles = extend(
- oldStyles,
- newStyles
- );
- }
- // Get the text width from style
- if (styles) {
- // Previously set, unset it (#8234)
- if (styles.width === null || styles.width === 'auto') {
- delete this.textWidth;
- // Apply new
- } else if (
- elem.nodeName.toLowerCase() === 'text' &&
- styles.width
- ) {
- textWidth = this.textWidth = pInt(styles.width);
- }
- }
- // store object
- this.styles = styles;
- if (textWidth && (!svg && this.renderer.forExport)) {
- delete styles.width;
- }
- // Serialize and set style attribute
- if (elem.namespaceURI === this.SVG_NS) { // #7633
- hyphenate = function (a, b) {
- return '-' + b.toLowerCase();
- };
- objectEach(styles, function (style, n) {
- if (svgPseudoProps.indexOf(n) === -1) {
- serializedCss +=
- n.replace(/([A-Z])/g, hyphenate) + ':' +
- style + ';';
- }
- });
- if (serializedCss) {
- attr(elem, 'style', serializedCss); // #1881
- }
- } else {
- css(elem, styles);
- }
- if (this.added) {
- // Rebuild text after added. Cache mechanisms in the buildText
- // will prevent building if there are no significant changes.
- if (this.element.nodeName === 'text') {
- this.renderer.buildText(this);
- }
- // Apply text outline after added
- if (styles && styles.textOutline) {
- this.applyTextOutline(styles.textOutline);
- }
- }
- }
- return this;
- },
- /**
- * Get the computed style. Only in styled mode.
- *
- * @example
- * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
- *
- * @function Highcharts.SVGElement#getStyle
- *
- * @param {string} prop
- * The property name to check for.
- *
- * @return {string}
- * The current computed value.
- */
- getStyle: function (prop) {
- return win.getComputedStyle(this.element || this, '')
- .getPropertyValue(prop);
- },
- /**
- * Get the computed stroke width in pixel values. This is used extensively
- * when drawing shapes to ensure the shapes are rendered crisp and
- * positioned correctly relative to each other. Using
- * `shape-rendering: crispEdges` leaves us less control over positioning,
- * for example when we want to stack columns next to each other, or position
- * things pixel-perfectly within the plot box.
- *
- * The common pattern when placing a shape is:
- * - Create the SVGElement and add it to the DOM. In styled mode, it will
- * now receive a stroke width from the style sheet. In classic mode we
- * will add the `stroke-width` attribute.
- * - Read the computed `elem.strokeWidth()`.
- * - Place it based on the stroke width.
- *
- * @function Highcharts.SVGElement#strokeWidth
- *
- * @return {number}
- * The stroke width in pixels. Even if the given stroke widtch (in
- * CSS or by attributes) is based on `em` or other units, the pixel
- * size is returned.
- */
- strokeWidth: function () {
- // In non-styled mode, read the stroke width as set by .attr
- if (!this.renderer.styledMode) {
- return this['stroke-width'] || 0;
- }
- // In styled mode, read computed stroke width
- var val = this.getStyle('stroke-width'),
- ret,
- dummy;
- // Read pixel values directly
- if (val.indexOf('px') === val.length - 2) {
- ret = pInt(val);
- // Other values like em, pt etc need to be measured
- } else {
- dummy = doc.createElementNS(SVG_NS, 'rect');
- attr(dummy, {
- 'width': val,
- 'stroke-width': 0
- });
- this.element.parentNode.appendChild(dummy);
- ret = dummy.getBBox().width;
- dummy.parentNode.removeChild(dummy);
- }
- return ret;
- },
- /**
- * Add an event listener. This is a simple setter that replaces all other
- * events of the same type, opposed to the {@link Highcharts#addEvent}
- * function.
- *
- * @sample highcharts/members/element-on/
- * A clickable rectangle
- *
- * @function Highcharts.SVGElement#on
- *
- * @param {string} eventType
- * The event type. If the type is `click`, Highcharts will internally
- * translate it to a `touchstart` event on touch devices, to prevent
- * the browser from waiting for a click event from firing.
- *
- * @param {Function} handler
- * The handler callback.
- *
- * @return {Highcharts.SVGElement}
- * The SVGElement for chaining.
- */
- on: function (eventType, handler) {
- var svgElement = this,
- element = svgElement.element;
- // touch
- if (hasTouch && eventType === 'click') {
- element.ontouchstart = function (e) {
- svgElement.touchEventFired = Date.now(); // #2269
- e.preventDefault();
- handler.call(element, e);
- };
- element.onclick = function (e) {
- if (win.navigator.userAgent.indexOf('Android') === -1 ||
- Date.now() - (svgElement.touchEventFired || 0) > 1100) {
- handler.call(element, e);
- }
- };
- } else {
- // simplest possible event model for internal use
- element['on' + eventType] = handler;
- }
- return this;
- },
- /**
- * Set the coordinates needed to draw a consistent radial gradient across
- * a shape regardless of positioning inside the chart. Used on pie slices
- * to make all the slices have the same radial reference point.
- *
- * @function Highcharts.SVGElement#setRadialReference
- *
- * @param {Array<number>} coordinates
- * The center reference. The format is `[centerX, centerY, diameter]`
- * in pixels.
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVGElement for chaining.
- */
- setRadialReference: function (coordinates) {
- var existingGradient = this.renderer.gradients[this.element.gradient];
- this.element.radialReference = coordinates;
- // On redrawing objects with an existing gradient, the gradient needs
- // to be repositioned (#3801)
- if (existingGradient && existingGradient.radAttr) {
- existingGradient.animate(
- this.renderer.getRadialAttr(
- coordinates,
- existingGradient.radAttr
- )
- );
- }
- return this;
- },
- /**
- * Move an object and its children by x and y values.
- *
- * @function Highcharts.SVGElement#translate
- *
- * @param {number} x
- * The x value.
- *
- * @param {number} y
- * The y value.
- */
- translate: function (x, y) {
- return this.attr({
- translateX: x,
- translateY: y
- });
- },
- /**
- * Invert a group, rotate and flip. This is used internally on inverted
- * charts, where the points and graphs are drawn as if not inverted, then
- * the series group elements are inverted.
- *
- * @function Highcharts.SVGElement#invert
- *
- * @param {boolean} inverted
- * Whether to invert or not. An inverted shape can be un-inverted by
- * setting it to false.
- *
- * @return {Highcharts.SVGElement}
- * Return the SVGElement for chaining.
- */
- invert: function (inverted) {
- var wrapper = this;
- wrapper.inverted = inverted;
- wrapper.updateTransform();
- return wrapper;
- },
- /**
- * Update the transform attribute based on internal properties. Deals with
- * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
- * attributes and updates the SVG `transform` attribute.
- *
- * @private
- * @function Highcharts.SVGElement#updateTransform
- */
- updateTransform: function () {
- var wrapper = this,
- translateX = wrapper.translateX || 0,
- translateY = wrapper.translateY || 0,
- scaleX = wrapper.scaleX,
- scaleY = wrapper.scaleY,
- inverted = wrapper.inverted,
- rotation = wrapper.rotation,
- matrix = wrapper.matrix,
- element = wrapper.element,
- transform;
- // Flipping affects translate as adjustment for flipping around the
- // group's axis
- if (inverted) {
- translateX += wrapper.width;
- translateY += wrapper.height;
- }
- // Apply translate. Nearly all transformed elements have translation,
- // so instead of checking for translate = 0, do it always (#1767,
- // #1846).
- transform = ['translate(' + translateX + ',' + translateY + ')'];
- // apply matrix
- if (defined(matrix)) {
- transform.push(
- 'matrix(' + matrix.join(',') + ')'
- );
- }
- // apply rotation
- if (inverted) {
- transform.push('rotate(90) scale(-1,1)');
- } else if (rotation) { // text rotation
- transform.push(
- 'rotate(' + rotation + ' ' +
- pick(this.rotationOriginX, element.getAttribute('x'), 0) +
- ' ' +
- pick(this.rotationOriginY, element.getAttribute('y') || 0) + ')'
- );
- }
- // apply scale
- if (defined(scaleX) || defined(scaleY)) {
- transform.push(
- 'scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')'
- );
- }
- if (transform.length) {
- element.setAttribute('transform', transform.join(' '));
- }
- },
- /**
- * Bring the element to the front. Alternatively, a new zIndex can be set.
- *
- * @sample highcharts/members/element-tofront/
- * Click an element to bring it to front
- *
- * @function Highcharts.SVGElement#toFront
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVGElement for chaining.
- */
- toFront: function () {
- var element = this.element;
- element.parentNode.appendChild(element);
- return this;
- },
- /**
- * Align the element relative to the chart or another box.
- *
- * @function Highcharts.SVGElement#align
- *
- * @param {Highcharts.AlignObject} [alignOptions]
- * The alignment options. The function can be called without this
- * parameter in order to re-align an element after the box has been
- * updated.
- *
- * @param {boolean} [alignByTranslate]
- * Align element by translation.
- *
- * @param {string|Highcharts.BBoxObject} [box]
- * The box to align to, needs a width and height. When the box is a
- * string, it refers to an object in the Renderer. For example, when
- * box is `spacingBox`, it refers to `Renderer.spacingBox` which
- * holds `width`, `height`, `x` and `y` properties.
- *
- * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
- */
- align: function (alignOptions, alignByTranslate, box) {
- var align,
- vAlign,
- x,
- y,
- attribs = {},
- alignTo,
- renderer = this.renderer,
- alignedObjects = renderer.alignedObjects,
- alignFactor,
- vAlignFactor;
- // First call on instanciate
- if (alignOptions) {
- this.alignOptions = alignOptions;
- this.alignByTranslate = alignByTranslate;
- if (!box || isString(box)) {
- this.alignTo = alignTo = box || 'renderer';
- // prevent duplicates, like legendGroup after resize
- erase(alignedObjects, this);
- alignedObjects.push(this);
- box = null; // reassign it below
- }
- // When called on resize, no arguments are supplied
- } else {
- alignOptions = this.alignOptions;
- alignByTranslate = this.alignByTranslate;
- alignTo = this.alignTo;
- }
- box = pick(box, renderer[alignTo], renderer);
- // Assign variables
- align = alignOptions.align;
- vAlign = alignOptions.verticalAlign;
- x = (box.x || 0) + (alignOptions.x || 0); // default: left align
- y = (box.y || 0) + (alignOptions.y || 0); // default: top align
- // Align
- if (align === 'right') {
- alignFactor = 1;
- } else if (align === 'center') {
- alignFactor = 2;
- }
- if (alignFactor) {
- x += (box.width - (alignOptions.width || 0)) / alignFactor;
- }
- attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
- // Vertical align
- if (vAlign === 'bottom') {
- vAlignFactor = 1;
- } else if (vAlign === 'middle') {
- vAlignFactor = 2;
- }
- if (vAlignFactor) {
- y += (box.height - (alignOptions.height || 0)) / vAlignFactor;
- }
- attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
- // Animate only if already placed
- this[this.placed ? 'animate' : 'attr'](attribs);
- this.placed = true;
- this.alignAttr = attribs;
- return this;
- },
- /**
- * Get the bounding box (width, height, x and y) for the element. Generally
- * used to get rendered text size. Since this is called a lot in charts,
- * the results are cached based on text properties, in order to save DOM
- * traffic. The returned bounding box includes the rotation, so for example
- * a single text line of rotation 90 will report a greater height, and a
- * width corresponding to the line-height.
- *
- * @sample highcharts/members/renderer-on-chart/
- * Draw a rectangle based on a text's bounding box
- *
- * @function Highcharts.SVGElement#getBBox
- *
- * @param {boolean} [reload]
- * Skip the cache and get the updated DOM bouding box.
- *
- * @param {number} [rot]
- * Override the element's rotation. This is internally used on axis
- * labels with a value of 0 to find out what the bounding box would
- * be have been if it were not rotated.
- *
- * @return {Highcharts.BBoxObject}
- * The bounding box with `x`, `y`, `width` and `height` properties.
- */
- getBBox: function (reload, rot) {
- var wrapper = this,
- bBox, // = wrapper.bBox,
- renderer = wrapper.renderer,
- width,
- height,
- rotation,
- rad,
- element = wrapper.element,
- styles = wrapper.styles,
- fontSize,
- textStr = wrapper.textStr,
- toggleTextShadowShim,
- cache = renderer.cache,
- cacheKeys = renderer.cacheKeys,
- isSVG = element.namespaceURI === wrapper.SVG_NS,
- cacheKey;
- rotation = pick(rot, wrapper.rotation);
- rad = rotation * deg2rad;
- fontSize = renderer.styledMode ? (
- element &&
- SVGElement.prototype.getStyle.call(element, 'font-size')
- ) : (
- styles && styles.fontSize
- );
- // Avoid undefined and null (#7316)
- if (defined(textStr)) {
- cacheKey = textStr.toString();
- // Since numbers are monospaced, and numerical labels appear a lot
- // in a chart, we assume that a label of n characters has the same
- // bounding box as others of the same length. Unless there is inner
- // HTML in the label. In that case, leave the numbers as is (#5899).
- if (cacheKey.indexOf('<') === -1) {
- cacheKey = cacheKey.replace(/[0-9]/g, '0');
- }
- // Properties that affect bounding box
- cacheKey += [
- '',
- rotation || 0,
- fontSize,
- wrapper.textWidth, // #7874, also useHTML
- styles && styles.textOverflow // #5968
- ].join(',');
- }
- if (cacheKey && !reload) {
- bBox = cache[cacheKey];
- }
- // No cache found
- if (!bBox) {
- // SVG elements
- if (isSVG || renderer.forExport) {
- try { // Fails in Firefox if the container has display: none.
- // When the text shadow shim is used, we need to hide the
- // fake shadows to get the correct bounding box (#3872)
- toggleTextShadowShim = this.fakeTS && function (display) {
- [].forEach.call(
- element.querySelectorAll(
- '.highcharts-text-outline'
- ),
- function (tspan) {
- tspan.style.display = display;
- }
- );
- };
- // Workaround for #3842, Firefox reporting wrong bounding
- // box for shadows
- if (toggleTextShadowShim) {
- toggleTextShadowShim('none');
- }
- bBox = element.getBBox ?
- // SVG: use extend because IE9 is not allowed to change
- // width and height in case of rotation (below)
- extend({}, element.getBBox()) : {
- // Legacy IE in export mode
- width: element.offsetWidth,
- height: element.offsetHeight
- };
- // #3842
- if (toggleTextShadowShim) {
- toggleTextShadowShim('');
- }
- } catch (e) {}
- // If the bBox is not set, the try-catch block above failed. The
- // other condition is for Opera that returns a width of
- // -Infinity on hidden elements.
- if (!bBox || bBox.width < 0) {
- bBox = { width: 0, height: 0 };
- }
- // VML Renderer or useHTML within SVG
- } else {
- bBox = wrapper.htmlGetBBox();
- }
- // True SVG elements as well as HTML elements in modern browsers
- // using the .useHTML option need to compensated for rotation
- if (renderer.isSVG) {
- width = bBox.width;
- height = bBox.height;
- // Workaround for wrong bounding box in IE, Edge and Chrome on
- // Windows. With Highcharts' default font, IE and Edge report
- // a box height of 16.899 and Chrome rounds it to 17. If this
- // stands uncorrected, it results in more padding added below
- // the text than above when adding a label border or background.
- // Also vertical positioning is affected.
- // https://jsfiddle.net/highcharts/em37nvuj/
- // (#1101, #1505, #1669, #2568, #6213).
- if (isSVG) {
- bBox.height = height = (
- {
- '11px,17': 14,
- '13px,20': 16
- }[
- styles && styles.fontSize + ',' + Math.round(height)
- ] ||
- height
- );
- }
- // Adjust for rotated text
- if (rotation) {
- bBox.width = Math.abs(height * Math.sin(rad)) +
- Math.abs(width * Math.cos(rad));
- bBox.height = Math.abs(height * Math.cos(rad)) +
- Math.abs(width * Math.sin(rad));
- }
- }
- // Cache it. When loading a chart in a hidden iframe in Firefox and
- // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
- if (cacheKey && bBox.height > 0) {
- // Rotate (#4681)
- while (cacheKeys.length > 250) {
- delete cache[cacheKeys.shift()];
- }
- if (!cache[cacheKey]) {
- cacheKeys.push(cacheKey);
- }
- cache[cacheKey] = bBox;
- }
- }
- return bBox;
- },
- /**
- * Show the element after it has been hidden.
- *
- * @function Highcharts.SVGElement#show
- *
- * @param {boolean} [inherit=false]
- * Set the visibility attribute to `inherit` rather than `visible`.
- * The difference is that an element with `visibility="visible"`
- * will be visible even if the parent is hidden.
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVGElement for chaining.
- */
- show: function (inherit) {
- return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
- },
- /**
- * Hide the element, equivalent to setting the `visibility` attribute to
- * `hidden`.
- *
- * @function Highcharts.SVGElement#hide
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVGElement for chaining.
- */
- hide: function () {
- return this.attr({ visibility: 'hidden' });
- },
- /**
- * Fade out an element by animating its opacity down to 0, and hide it on
- * complete. Used internally for the tooltip.
- *
- * @function Highcharts.SVGElement#fadeOut
- *
- * @param {number} [duration=150]
- * The fade duration in milliseconds.
- */
- fadeOut: function (duration) {
- var elemWrapper = this;
- elemWrapper.animate({
- opacity: 0
- }, {
- duration: duration || 150,
- complete: function () {
- // #3088, assuming we're only using this for tooltips
- elemWrapper.attr({ y: -9999 });
- }
- });
- },
- /**
- * Add the element to the DOM. All elements must be added this way.
- *
- * @sample highcharts/members/renderer-g
- * Elements added to a group
- *
- * @function Highcharts.SVGElement#add
- *
- * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement} [parent]
- * The parent item to add it to. If undefined, the element is added
- * to the {@link Highcharts.SVGRenderer.box}.
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVGElement for chaining.
- */
- add: function (parent) {
- var renderer = this.renderer,
- element = this.element,
- inserted;
- if (parent) {
- this.parentGroup = parent;
- }
- // mark as inverted
- this.parentInverted = parent && parent.inverted;
- // build formatted text
- if (this.textStr !== undefined) {
- renderer.buildText(this);
- }
- // Mark as added
- this.added = true;
- // If we're adding to renderer root, or other elements in the group
- // have a z index, we need to handle it
- if (!parent || parent.handleZ || this.zIndex) {
- inserted = this.zIndexSetter();
- }
- // If zIndex is not handled, append at the end
- if (!inserted) {
- (parent ? parent.element : renderer.box).appendChild(element);
- }
- // fire an event for internal hooks
- if (this.onAdd) {
- this.onAdd();
- }
- return this;
- },
- /**
- * Removes an element from the DOM.
- *
- * @private
- * @function Highcharts.SVGElement#safeRemoveChild
- *
- * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
- * The DOM node to remove.
- */
- safeRemoveChild: function (element) {
- var parentNode = element.parentNode;
- if (parentNode) {
- parentNode.removeChild(element);
- }
- },
- /**
- * Destroy the element and element wrapper and clear up the DOM and event
- * hooks.
- *
- * @function Highcharts.SVGElement#destroy
- */
- destroy: function () {
- var wrapper = this,
- element = wrapper.element || {},
- renderer = wrapper.renderer,
- parentToClean =
- renderer.isSVG &&
- element.nodeName === 'SPAN' &&
- wrapper.parentGroup,
- grandParent,
- ownerSVGElement = element.ownerSVGElement,
- i,
- clipPath = wrapper.clipPath;
- // remove events
- element.onclick = element.onmouseout = element.onmouseover =
- element.onmousemove = element.point = null;
- stop(wrapper); // stop running animations
- if (clipPath && ownerSVGElement) {
- // Look for existing references to this clipPath and remove them
- // before destroying the element (#6196).
- // The upper case version is for Edge
- [].forEach.call(
- ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'),
- function (el) {
- var clipPathAttr = el.getAttribute('clip-path'),
- clipPathId = clipPath.element.id;
- // Include the closing paranthesis in the test to rule out
- // id's from 10 and above (#6550). Edge puts quotes inside
- // the url, others not.
- if (
- clipPathAttr.indexOf('(#' + clipPathId + ')') > -1 ||
- clipPathAttr.indexOf('("#' + clipPathId + '")') > -1
- ) {
- el.removeAttribute('clip-path');
- }
- }
- );
- wrapper.clipPath = clipPath.destroy();
- }
- // Destroy stops in case this is a gradient object
- if (wrapper.stops) {
- for (i = 0; i < wrapper.stops.length; i++) {
- wrapper.stops[i] = wrapper.stops[i].destroy();
- }
- wrapper.stops = null;
- }
- // remove element
- wrapper.safeRemoveChild(element);
- if (!renderer.styledMode) {
- wrapper.destroyShadows();
- }
- // In case of useHTML, clean up empty containers emulating SVG groups
- // (#1960, #2393, #2697).
- while (
- parentToClean &&
- parentToClean.div &&
- parentToClean.div.childNodes.length === 0
- ) {
- grandParent = parentToClean.parentGroup;
- wrapper.safeRemoveChild(parentToClean.div);
- delete parentToClean.div;
- parentToClean = grandParent;
- }
- // remove from alignObjects
- if (wrapper.alignTo) {
- erase(renderer.alignedObjects, wrapper);
- }
- objectEach(wrapper, function (val, key) {
- delete wrapper[key];
- });
- return null;
- },
- /**
- * Add a shadow to the element. Must be called after the element is added to
- * the DOM. In styled mode, this method is not used, instead use `defs` and
- * filters.
- *
- * @example
- * renderer.rect(10, 100, 100, 100)
- * .attr({ fill: 'red' })
- * .shadow(true);
- *
- * @function Highcharts.SVGElement#shadow
- *
- * @param {boolean|Highcharts.ShadowOptionsObject} shadowOptions
- * The shadow options. If `true`, the default options are applied. If
- * `false`, the current shadow will be removed.
- *
- * @param {Highcharts.SVGElement} [group]
- * The SVG group element where the shadows will be applied. The
- * default is to add it to the same parent as the current element.
- * Internally, this is ised for pie slices, where all the shadows are
- * added to an element behind all the slices.
- *
- * @param {boolean} [cutOff]
- * Used internally for column shadows.
- *
- * @return {Highcharts.SVGElement}
- * Returns the SVGElement for chaining.
- */
- shadow: function (shadowOptions, group, cutOff) {
- var shadows = [],
- i,
- shadow,
- element = this.element,
- strokeWidth,
- shadowWidth,
- shadowElementOpacity,
- // compensate for inverted plot area
- transform;
- if (!shadowOptions) {
- this.destroyShadows();
- } else if (!this.shadows) {
- shadowWidth = pick(shadowOptions.width, 3);
- shadowElementOpacity = (shadowOptions.opacity || 0.15) /
- shadowWidth;
- transform = this.parentInverted ?
- '(-1,-1)' :
- '(' + pick(shadowOptions.offsetX, 1) + ', ' +
- pick(shadowOptions.offsetY, 1) + ')';
- for (i = 1; i <= shadowWidth; i++) {
- shadow = element.cloneNode(0);
- strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
- attr(shadow, {
- 'stroke':
- shadowOptions.color || '#000000',
- 'stroke-opacity': shadowElementOpacity * i,
- 'stroke-width': strokeWidth,
- 'transform': 'translate' + transform,
- 'fill': 'none'
- });
- shadow.setAttribute(
- 'class',
- (shadow.getAttribute('class') || '') + ' highcharts-shadow'
- );
- if (cutOff) {
- attr(
- shadow,
- 'height',
- Math.max(attr(shadow, 'height') - strokeWidth, 0)
- );
- shadow.cutHeight = strokeWidth;
- }
- if (group) {
- group.element.appendChild(shadow);
- } else if (element.parentNode) {
- element.parentNode.insertBefore(shadow, element);
- }
- shadows.push(shadow);
- }
- this.shadows = shadows;
- }
- return this;
- },
- /**
- * Destroy shadows on the element.
- *
- * @private
- * @function Highcharts.SVGElement#destroyShadows
- */
- destroyShadows: function () {
- (this.shadows || []).forEach(function (shadow) {
- this.safeRemoveChild(shadow);
- }, this);
- this.shadows = undefined;
- },
- /**
- * @private
- * @function Highcharts.SVGElement#xGetter
- *
- * @param {string} key
- *
- * @return {number|string|null}
- */
- xGetter: function (key) {
- if (this.element.nodeName === 'circle') {
- if (key === 'x') {
- key = 'cx';
- } else if (key === 'y') {
- key = 'cy';
- }
- }
- return this._defaultGetter(key);
- },
- /**
- * Get the current value of an attribute or pseudo attribute,
- * used mainly for animation. Called internally from
- * the {@link Highcharts.SVGRenderer#attr} function.
- *
- * @private
- * @function Highcharts.SVGElement#_defaultGetter
- *
- * @param {string} key
- * Property key.
- *
- * @return {number|string|null}
- * Property value.
- */
- _defaultGetter: function (key) {
- var ret = pick(
- this[key + 'Value'], // align getter
- this[key],
- this.element ? this.element.getAttribute(key) : null,
- 0
- );
- if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
- ret = parseFloat(ret);
- }
- return ret;
- },
- /**
- * @private
- * @function Highcharts.SVGElement#dSettter
- *
- * @param {number|string|Highcharts.SVGPathArray} value
- *
- * @param {string} key
- *
- * @param {Highcharts.SVGDOMElement} element
- */
- dSetter: function (value, key, element) {
- if (value && value.join) { // join path
- value = value.join(' ');
- }
- if (/(NaN| {2}|^$)/.test(value)) {
- value = 'M 0 0';
- }
- // Check for cache before resetting. Resetting causes disturbance in the
- // DOM, causing flickering in some cases in Edge/IE (#6747). Also
- // possible performance gain.
- if (this[key] !== value) {
- element.setAttribute(key, value);
- this[key] = value;
- }
- },
- /**
- * @private
- * @function Highcharts.SVGElement#dashstyleSetter
- *
- * @param {string} value
- */
- dashstyleSetter: function (value) {
- var i,
- strokeWidth = this['stroke-width'];
- // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
- // strokeWidth function, we should be able to use that instead.
- if (strokeWidth === 'inherit') {
- strokeWidth = 1;
- }
- value = value && value.toLowerCase();
- if (value) {
- value = value
- .replace('shortdashdotdot', '3,1,1,1,1,1,')
- .replace('shortdashdot', '3,1,1,1')
- .replace('shortdot', '1,1,')
- .replace('shortdash', '3,1,')
- .replace('longdash', '8,3,')
- .replace(/dot/g, '1,3,')
- .replace('dash', '4,3,')
- .replace(/,$/, '')
- .split(','); // ending comma
- i = value.length;
- while (i--) {
- value[i] = pInt(value[i]) * strokeWidth;
- }
- value = value.join(',')
- .replace(/NaN/g, 'none'); // #3226
- this.element.setAttribute('stroke-dasharray', value);
- }
- },
- /**
- * @private
- * @function Highcharts.SVGElement#alignSetter
- *
- * @param {"start"|"middle"|"end"} value
- */
- alignSetter: function (value) {
- var convert = { left: 'start', center: 'middle', right: 'end' };
- this.alignValue = value;
- this.element.setAttribute('text-anchor', convert[value]);
- },
- /**
- * @private
- * @function Highcharts.SVGElement#opacitySetter
- *
- * @param {string} value
- *
- * @param {string} key
- *
- * @param {Highcharts.SVGDOMElement} element
- */
- opacitySetter: function (value, key, element) {
- this[key] = value;
- element.setAttribute(key, value);
- },
- /**
- * @private
- * @function Highcharts.SVGElement#titleSetter
- *
- * @param {string} value
- */
- titleSetter: function (value) {
- var titleNode = this.element.getElementsByTagName('title')[0];
- if (!titleNode) {
- titleNode = doc.createElementNS(this.SVG_NS, 'title');
- this.element.appendChild(titleNode);
- }
- // Remove text content if it exists
- if (titleNode.firstChild) {
- titleNode.removeChild(titleNode.firstChild);
- }
- titleNode.appendChild(
- doc.createTextNode(
- // #3276, #3895
- (String(pick(value), ''))
- .replace(/<[^>]*>/g, '')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- )
- );
- },
- /**
- * @private
- * @function Highcharts.SVGElement#textSetter
- *
- * @param {string} value
- */
- textSetter: function (value) {
- if (value !== this.textStr) {
- // Delete bBox memo when the text changes
- delete this.bBox;
- this.textStr = value;
- if (this.added) {
- this.renderer.buildText(this);
- }
- }
- },
- /**
- * @private
- * @function Highcharts.SVGElement#fillSetter
- *
- * @param {Highcharts.Color|Highcharts.ColorString} value
- *
- * @param {string} key
- *
- * @param {Highcharts.SVGDOMElement} element
- */
- fillSetter: function (value, key, element) {
- if (typeof value === 'string') {
- element.setAttribute(key, value);
- } else if (value) {
- this.complexColor(value, key, element);
- }
- },
- /**
- * @private
- * @function Highcharts.SVGElement#visibilitySetter
- *
- * @param {string} value
- *
- * @param {string} key
- *
- * @param {Highcharts.SVGDOMElement} element
- */
- visibilitySetter: function (value, key, element) {
- // IE9-11 doesn't handle visibilty:inherit well, so we remove the
- // attribute instead (#2881, #3909)
- if (value === 'inherit') {
- element.removeAttribute(key);
- } else if (this[key] !== value) { // #6747
- element.setAttribute(key, value);
- }
- this[key] = value;
- },
- /**
- * @private
- * @function Highcharts.SVGElement#zIndexSetter
- *
- * @param {string} value
- *
- * @param {string} key
- *
- * @return {boolean}
- */
- zIndexSetter: function (value, key) {
- var renderer = this.renderer,
- parentGroup = this.parentGroup,
- parentWrapper = parentGroup || renderer,
- parentNode = parentWrapper.element || renderer.box,
- childNodes,
- otherElement,
- otherZIndex,
- element = this.element,
- inserted,
- undefinedOtherZIndex,
- svgParent = parentNode === renderer.box,
- run = this.added,
- i;
- if (defined(value)) {
- // So we can read it for other elements in the group
- element.setAttribute('data-z-index', value);
- value = +value;
- if (this[key] === value) { // Only update when needed (#3865)
- run = false;
- }
- } else if (defined(this[key])) {
- element.removeAttribute('data-z-index');
- }
- this[key] = value;
- // Insert according to this and other elements' zIndex. Before .add() is
- // called, nothing is done. Then on add, or by later calls to
- // zIndexSetter, the node is placed on the right place in the DOM.
- if (run) {
- value = this.zIndex;
- if (value && parentGroup) {
- parentGroup.handleZ = true;
- }
- childNodes = parentNode.childNodes;
- for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
- otherElement = childNodes[i];
- otherZIndex = otherElement.getAttribute('data-z-index');
- undefinedOtherZIndex = !defined(otherZIndex);
- if (otherElement !== element) {
- if (
- // Negative zIndex versus no zIndex:
- // On all levels except the highest. If the parent is
- // <svg>, then we don't want to put items before <desc>
- // or <defs>
- (value < 0 && undefinedOtherZIndex && !svgParent && !i)
- ) {
- parentNode.insertBefore(element, childNodes[i]);
- inserted = true;
- } else if (
- // Insert after the first element with a lower zIndex
- pInt(otherZIndex) <= value ||
- // If negative zIndex, add this before first undefined
- // zIndex element
- (
- undefinedOtherZIndex &&
- (!defined(value) || value >= 0)
- )
- ) {
- parentNode.insertBefore(
- element,
- childNodes[i + 1] || null // null for oldIE export
- );
- inserted = true;
- }
- }
- }
- if (!inserted) {
- parentNode.insertBefore(
- element,
- childNodes[svgParent ? 3 : 0] || null // null for oldIE
- );
- inserted = true;
- }
- }
- return inserted;
- },
- /**
- * @private
- * @function Highcharts.SVGElement#_defaultSetter
- *
- * @param {string} value
- *
- * @param {string} key
- *
- * @param {Highcharts.SVGDOMElement} element
- */
- _defaultSetter: function (value, key, element) {
- element.setAttribute(key, value);
- }
- });
- // Some shared setters and getters
- SVGElement.prototype.yGetter =
- SVGElement.prototype.xGetter;
- SVGElement.prototype.translateXSetter =
- SVGElement.prototype.translateYSetter =
- SVGElement.prototype.rotationSetter =
- SVGElement.prototype.verticalAlignSetter =
- SVGElement.prototype.rotationOriginXSetter =
- SVGElement.prototype.rotationOriginYSetter =
- SVGElement.prototype.scaleXSetter =
- SVGElement.prototype.scaleYSetter =
- SVGElement.prototype.matrixSetter = function (value, key) {
- this[key] = value;
- this.doTransform = true;
- };
- // WebKit and Batik have problems with a stroke-width of zero, so in this case
- // we remove the stroke attribute altogether. #1270, #1369, #3065, #3072.
- SVGElement.prototype['stroke-widthSetter'] =
- /**
- * @private
- * @function Highcharts.SVGElement#strokeSetter
- *
- * @param {number|string} value
- *
- * @param {string} key
- *
- * @param {Highcharts.SVGDOMElement} element
- */
- SVGElement.prototype.strokeSetter = function (value, key, element) {
- this[key] = value;
- // Only apply the stroke attribute if the stroke width is defined and larger
- // than 0
- if (this.stroke && this['stroke-width']) {
- // Use prototype as instance may be overridden
- SVGElement.prototype.fillSetter.call(
- this,
- this.stroke,
- 'stroke',
- element
- );
- element.setAttribute('stroke-width', this['stroke-width']);
- this.hasStroke = true;
- } else if (key === 'stroke-width' && value === 0 && this.hasStroke) {
- element.removeAttribute('stroke');
- this.hasStroke = false;
- }
- };
- /**
- * Allows direct access to the Highcharts rendering layer in order to draw
- * primitive shapes like circles, rectangles, paths or text directly on a chart,
- * or independent from any chart. The SVGRenderer represents a wrapper object
- * for SVG in modern browsers. Through the VMLRenderer, part of the `oldie.js`
- * module, it also brings vector graphics to IE <= 8.
- *
- * An existing chart's renderer can be accessed through {@link Chart.renderer}.
- * The renderer can also be used completely decoupled from a chart.
- *
- * @sample highcharts/members/renderer-on-chart
- * Annotating a chart programmatically.
- * @sample highcharts/members/renderer-basic
- * Independent SVG drawing.
- *
- * @example
- * // Use directly without a chart object.
- * var renderer = new Highcharts.Renderer(parentNode, 600, 400);
- *
- * @class
- * @name Highcharts.SVGRenderer
- *
- * @param {Highcharts.HTMLDOMElement} container
- * Where to put the SVG in the web page.
- *
- * @param {number} width
- * The width of the SVG.
- *
- * @param {number} height
- * The height of the SVG.
- *
- * @param {boolean} [forExport=false]
- * Whether the rendered content is intended for export.
- *
- * @param {boolean} [allowHTML=true]
- * Whether the renderer is allowed to include HTML text, which will be
- * projected on top of the SVG.
- */
- SVGRenderer = H.SVGRenderer = function () {
- this.init.apply(this, arguments);
- };
- extend(SVGRenderer.prototype, /** @lends Highcharts.SVGRenderer.prototype */ {
- /**
- * A pointer to the renderer's associated Element class. The VMLRenderer
- * will have a pointer to VMLElement here.
- *
- * @name Highcharts.SVGRenderer#Element
- * @type {Highcharts.SVGElement}
- */
- Element: SVGElement,
- SVG_NS: SVG_NS,
- /**
- * Initialize the SVGRenderer. Overridable initiator function that takes
- * the same parameters as the constructor.
- *
- * @function Highcharts.SVGRenderer#init
- *
- * @param {Highcharts.HTMLDOMElement} container
- * Where to put the SVG in the web page.
- *
- * @param {number} width
- * The width of the SVG.
- *
- * @param {number} height
- * The height of the SVG.
- *
- * @param {boolean} [forExport=false]
- * Whether the rendered content is intended for export.
- *
- * @param {boolean} [allowHTML=true]
- * Whether the renderer is allowed to include HTML text, which will
- * be projected on top of the SVG.
- *
- * @param {boolean} [styledMode=false]
- * Whether the renderer belongs to a chart that is in styled mode.
- * If it does, it will avoid setting presentational attributes in
- * some cases, but not when set explicitly through `.attr` and `.css`
- * etc.
- *
- * @return {void}
- */
- init: function (
- container,
- width,
- height,
- style,
- forExport,
- allowHTML,
- styledMode
- ) {
- var renderer = this,
- boxWrapper,
- element,
- desc;
- boxWrapper = renderer.createElement('svg')
- .attr({
- 'version': '1.1',
- 'class': 'highcharts-root'
- });
- if (!styledMode) {
- boxWrapper.css(this.getStyle(style));
- }
- element = boxWrapper.element;
- container.appendChild(element);
- // Always use ltr on the container, otherwise text-anchor will be
- // flipped and text appear outside labels, buttons, tooltip etc (#3482)
- attr(container, 'dir', 'ltr');
- // For browsers other than IE, add the namespace attribute (#1978)
- if (container.innerHTML.indexOf('xmlns') === -1) {
- attr(element, 'xmlns', this.SVG_NS);
- }
- // object properties
- renderer.isSVG = true;
- /**
- * The root `svg` node of the renderer.
- *
- * @name Highcharts.SVGRenderer#box
- * @type {Highcharts.SVGDOMElement}
- */
- this.box = element;
- /**
- * The wrapper for the root `svg` node of the renderer.
- *
- * @name Highcharts.SVGRenderer#boxWrapper
- * @type {Highcharts.SVGElement}
- */
- this.boxWrapper = boxWrapper;
- renderer.alignedObjects = [];
- /**
- * Page url used for internal references.
- *
- * @private
- * @name Highcharts.SVGRenderer#url
- * @type {string}
- */
- // #24, #672, #1070
- this.url = (
- (isFirefox || isWebKit) &&
- doc.getElementsByTagName('base').length
- ) ?
- win.location.href
- .split('#')[0] // remove the hash
- .replace(/<[^>]*>/g, '') // wing cut HTML
- // escape parantheses and quotes
- .replace(/([\('\)])/g, '\\$1')
- // replace spaces (needed for Safari only)
- .replace(/ /g, '%20') :
- '';
- // Add description
- desc = this.createElement('desc').add();
- desc.element.appendChild(
- doc.createTextNode('Created with Highcharts 7.0.2')
- );
- /**
- * A pointer to the `defs` node of the root SVG.
- *
- * @name Highcharts.SVGRenderer#defs
- * @type {Highcharts.SVGElement}
- */
- renderer.defs = this.createElement('defs').add();
- renderer.allowHTML = allowHTML;
- renderer.forExport = forExport;
- renderer.styledMode = styledMode;
- renderer.gradients = {}; // Object where gradient SvgElements are stored
- renderer.cache = {}; // Cache for numerical bounding boxes
- renderer.cacheKeys = [];
- renderer.imgCount = 0;
- renderer.setSize(width, height, false);
- // Issue 110 workaround:
- // In Firefox, if a div is positioned by percentage, its pixel position
- // may land between pixels. The container itself doesn't display this,
- // but an SVG element inside this container will be drawn at subpixel
- // precision. In order to draw sharp lines, this must be compensated
- // for. This doesn't seem to work inside iframes though (like in
- // jsFiddle).
- var subPixelFix, rect;
- if (isFirefox && container.getBoundingClientRect) {
- subPixelFix = function () {
- css(container, { left: 0, top: 0 });
- rect = container.getBoundingClientRect();
- css(container, {
- left: (Math.ceil(rect.left) - rect.left) + 'px',
- top: (Math.ceil(rect.top) - rect.top) + 'px'
- });
- };
- // run the fix now
- subPixelFix();
- // run it on resize
- renderer.unSubPixelFix = addEvent(win, 'resize', subPixelFix);
- }
- },
- /**
- * General method for adding a definition to the SVG `defs` tag. Can be used
- * for gradients, fills, filters etc. Styled mode only. A hook for adding
- * general definitions to the SVG's defs tag. Definitions can be referenced
- * from the CSS by its `id`. Read more in
- * [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
- * Styled mode only.
- *
- * @function Highcharts.SVGRenderer#definition
- *
- * @param {Highcharts.SVGDefinitionObject} def
- * A serialized form of an SVG definition, including children.
- *
- * @return {Highcharts.SVGElement}
- * The inserted node.
- */
- definition: function (def) {
- var ren = this;
- function recurse(config, parent) {
- var ret;
- splat(config).forEach(function (item) {
- var node = ren.createElement(item.tagName),
- attr = {};
- // Set attributes
- objectEach(item, function (val, key) {
- if (
- key !== 'tagName' &&
- key !== 'children' &&
- key !== 'textContent'
- ) {
- attr[key] = val;
- }
- });
- node.attr(attr);
- // Add to the tree
- node.add(parent || ren.defs);
- // Add text content
- if (item.textContent) {
- node.element.appendChild(
- doc.createTextNode(item.textContent)
- );
- }
- // Recurse
- recurse(item.children || [], node);
- ret = node;
- });
- // Return last node added (on top level it's the only one)
- return ret;
- }
- return recurse(def);
- },
- /**
- * Get the global style setting for the renderer.
- *
- * @private
- * @function Highcharts.SVGRenderer#getStyle
- *
- * @param {Highcharts.CSSObject} style
- * Style settings.
- *
- * @return {Highcharts.CSSObject}
- * The style settings mixed with defaults.
- */
- getStyle: function (style) {
- this.style = extend({
- fontFamily: '"Lucida Grande", "Lucida Sans Unicode", ' +
- 'Arial, Helvetica, sans-serif',
- fontSize: '12px'
- }, style);
- return this.style;
- },
- /**
- * Apply the global style on the renderer, mixed with the default styles.
- *
- * @function Highcharts.SVGRenderer#setStyle
- *
- * @param {Highcharts.CSSObject} style
- * CSS to apply.
- */
- setStyle: function (style) {
- this.boxWrapper.css(this.getStyle(style));
- },
- /**
- * Detect whether the renderer is hidden. This happens when one of the
- * parent elements has `display: none`. Used internally to detect when we
- * needto render preliminarily in another div to get the text bounding boxes
- * right.
- *
- * @function Highcharts.SVGRenderer#isHidden
- *
- * @return {boolean}
- * True if it is hidden.
- */
- isHidden: function () { // #608
- return !this.boxWrapper.getBBox().width;
- },
- /**
- * Destroys the renderer and its allocated members.
- *
- * @function Highcharts.SVGRenderer#destroy
- */
- destroy: function () {
- var renderer = this,
- rendererDefs = renderer.defs;
- renderer.box = null;
- renderer.boxWrapper = renderer.boxWrapper.destroy();
- // Call destroy on all gradient elements
- destroyObjectProperties(renderer.gradients || {});
- renderer.gradients = null;
- // Defs are null in VMLRenderer
- // Otherwise, destroy them here.
- if (rendererDefs) {
- renderer.defs = rendererDefs.destroy();
- }
- // Remove sub pixel fix handler (#982)
- if (renderer.unSubPixelFix) {
- renderer.unSubPixelFix();
- }
- renderer.alignedObjects = null;
- return null;
- },
- /**
- * Create a wrapper for an SVG element. Serves as a factory for
- * {@link SVGElement}, but this function is itself mostly called from
- * primitive factories like {@link SVGRenderer#path}, {@link
- * SVGRenderer#rect} or {@link SVGRenderer#text}.
- *
- * @function Highcharts.SVGRenderer#createElement
- *
- * @param {string} nodeName
- * The node name, for example `rect`, `g` etc.
- *
- * @return {Highcharts.SVGElement}
- * The generated SVGElement.
- */
- createElement: function (nodeName) {
- var wrapper = new this.Element();
- wrapper.init(this, nodeName);
- return wrapper;
- },
- /**
- * Dummy function for plugins, called every time the renderer is updated.
- * Prior to Highcharts 5, this was used for the canvg renderer.
- *
- * @deprecated
- * @function Highcharts.SVGRenderer#draw
- */
- draw: noop,
- /**
- * Get converted radial gradient attributes according to the radial
- * reference. Used internally from the {@link SVGElement#colorGradient}
- * function.
- *
- * @private
- * @function Highcharts.SVGRenderer#getRadialAttr
- *
- * @param {Array<number>} radialReference
- *
- * @param {Highcharts.SVGAttributes} gradAttr
- *
- * @return {Highcharts.SVGAttributes}
- */
- getRadialAttr: function (radialReference, gradAttr) {
- return {
- cx: (radialReference[0] - radialReference[2] / 2) +
- gradAttr.cx * radialReference[2],
- cy: (radialReference[1] - radialReference[2] / 2) +
- gradAttr.cy * radialReference[2],
- r: gradAttr.r * radialReference[2]
- };
- },
- /**
- * Truncate the text node contents to a given length. Used when the css
- * width is set. If the `textOverflow` is `ellipsis`, the text is truncated
- * character by character to the given length. If not, the text is
- * word-wrapped line by line.
- *
- * @private
- * @function Highcharts.SVGRenderer#truncate
- *
- * @param {Highcharts.SVGElement} wrapper
- *
- * @param {Highcharts.SVGDOMElement} tspan
- *
- * @param {string} text
- *
- * @param {Array.<string>} words
- *
- * @param {number} width
- *
- * @param {Function} getString
- *
- * @return {boolean}
- * True if tspan is too long.
- */
- truncate: function (
- wrapper,
- tspan,
- text,
- words,
- startAt,
- width,
- getString
- ) {
- var renderer = this,
- rotation = wrapper.rotation,
- str,
- // Word wrap can not be truncated to shorter than one word, ellipsis
- // text can be completely blank.
- minIndex = words ? 1 : 0,
- maxIndex = (text || words).length,
- currentIndex = maxIndex,
- // Cache the lengths to avoid checking the same twice
- lengths = [],
- updateTSpan = function (s) {
- if (tspan.firstChild) {
- tspan.removeChild(tspan.firstChild);
- }
- if (s) {
- tspan.appendChild(doc.createTextNode(s));
- }
- },
- getSubStringLength = function (charEnd, concatenatedEnd) {
- // charEnd is useed when finding the character-by-character
- // break for ellipsis, concatenatedEnd is used for word-by-word
- // break for word wrapping.
- var end = concatenatedEnd || charEnd;
- if (lengths[end] === undefined) {
- // Modern browsers
- if (tspan.getSubStringLength) {
- // Fails with DOM exception on unit-tests/legend/members
- // of unknown reason. Desired width is 0, text content
- // is "5" and end is 1.
- try {
- lengths[end] = startAt + tspan.getSubStringLength(
- 0,
- words ? end + 1 : end
- );
- } catch (e) {}
- // Legacy
- } else if (renderer.getSpanWidth) { // #9058 jsdom
- updateTSpan(getString(text || words, charEnd));
- lengths[end] = startAt +
- renderer.getSpanWidth(wrapper, tspan);
- }
- }
- return lengths[end];
- },
- actualWidth,
- truncated;
- wrapper.rotation = 0; // discard rotation when computing box
- actualWidth = getSubStringLength(tspan.textContent.length);
- truncated = startAt + actualWidth > width;
- if (truncated) {
- // Do a binary search for the index where to truncate the text
- while (minIndex <= maxIndex) {
- currentIndex = Math.ceil((minIndex + maxIndex) / 2);
- // When checking words for word-wrap, we need to build the
- // string and measure the subStringLength at the concatenated
- // word length.
- if (words) {
- str = getString(words, currentIndex);
- }
- actualWidth = getSubStringLength(
- currentIndex,
- str && str.length - 1
- );
- if (minIndex === maxIndex) {
- // Complete
- minIndex = maxIndex + 1;
- } else if (actualWidth > width) {
- // Too large. Set max index to current.
- maxIndex = currentIndex - 1;
- } else {
- // Within width. Set min index to current.
- minIndex = currentIndex;
- }
- }
- // If max index was 0 it means the shortest possible text was also
- // too large. For ellipsis that means only the ellipsis, while for
- // word wrap it means the whole first word.
- if (maxIndex === 0) {
- // Remove ellipsis
- updateTSpan('');
- // If the new text length is one less than the original, we don't
- // need the ellipsis
- } else if (!(text && maxIndex === text.length - 1)) {
- updateTSpan(str || getString(text || words, currentIndex));
- }
- }
- // When doing line wrapping, prepare for the next line by removing the
- // items from this line.
- if (words) {
- words.splice(0, currentIndex);
- }
- wrapper.actualWidth = actualWidth;
- wrapper.rotation = rotation; // Apply rotation again.
- return truncated;
- },
- /**
- * A collection of characters mapped to HTML entities. When `useHTML` on an
- * element is true, these entities will be rendered correctly by HTML. In
- * the SVG pseudo-HTML, they need to be unescaped back to simple characters,
- * so for example `<` will render as `<`.
- *
- * @example
- * // Add support for unescaping quotes
- * Highcharts.SVGRenderer.prototype.escapes['"'] = '"';
- *
- * @name Highcharts.SVGRenderer#escapes
- * @type {Highcharts.Dictionary<string>}
- */
- escapes: {
- '&': '&',
- '<': '<',
- '>': '>',
- "'": ''', // eslint-disable-line quotes
- '"': '"'
- },
- /**
- * Parse a simple HTML string into SVG tspans. Called internally when text
- * is set on an SVGElement. The function supports a subset of HTML tags, CSS
- * text features like `width`, `text-overflow`, `white-space`, and also
- * attributes like `href` and `style`.
- *
- * @private
- * @function Highcharts.SVGRenderer#buildText
- *
- * @param {Highcharts.SVGElement} wrapper
- * The parent SVGElement.
- */
- buildText: function (wrapper) {
- var textNode = wrapper.element,
- renderer = this,
- forExport = renderer.forExport,
- textStr = pick(wrapper.textStr, '').toString(),
- hasMarkup = textStr.indexOf('<') !== -1,
- lines,
- childNodes = textNode.childNodes,
- truncated,
- parentX = attr(textNode, 'x'),
- textStyles = wrapper.styles,
- width = wrapper.textWidth,
- textLineHeight = textStyles && textStyles.lineHeight,
- textOutline = textStyles && textStyles.textOutline,
- ellipsis = textStyles && textStyles.textOverflow === 'ellipsis',
- noWrap = textStyles && textStyles.whiteSpace === 'nowrap',
- fontSize = textStyles && textStyles.fontSize,
- textCache,
- isSubsequentLine,
- i = childNodes.length,
- tempParent = width && !wrapper.added && this.box,
- getLineHeight = function (tspan) {
- var fontSizeStyle;
- if (!renderer.styledMode) {
- fontSizeStyle =
- /(px|em)$/.test(tspan && tspan.style.fontSize) ?
- tspan.style.fontSize :
- (fontSize || renderer.style.fontSize || 12);
- }
- return textLineHeight ?
- pInt(textLineHeight) :
- renderer.fontMetrics(
- fontSizeStyle,
- // Get the computed size from parent if not explicit
- tspan.getAttribute('style') ? tspan : textNode
- ).h;
- },
- unescapeEntities = function (inputStr, except) {
- objectEach(renderer.escapes, function (value, key) {
- if (!except || except.indexOf(value) === -1) {
- inputStr = inputStr.toString().replace(
- new RegExp(value, 'g'), // eslint-disable-line security/detect-non-literal-regexp
- key
- );
- }
- });
- return inputStr;
- },
- parseAttribute = function (s, attr) {
- var start,
- delimiter;
- start = s.indexOf('<');
- s = s.substring(start, s.indexOf('>') - start);
- start = s.indexOf(attr + '=');
- if (start !== -1) {
- start = start + attr.length + 1;
- delimiter = s.charAt(start);
- if (delimiter === '"' || delimiter === "'") { // eslint-disable-line quotes
- s = s.substring(start + 1);
- return s.substring(0, s.indexOf(delimiter));
- }
- }
- };
- // The buildText code is quite heavy, so if we're not changing something
- // that affects the text, skip it (#6113).
- textCache = [
- textStr,
- ellipsis,
- noWrap,
- textLineHeight,
- textOutline,
- fontSize,
- width
- ].join(',');
- if (textCache === wrapper.textCache) {
- return;
- }
- wrapper.textCache = textCache;
- // Remove old text
- while (i--) {
- textNode.removeChild(childNodes[i]);
- }
- // Skip tspans, add text directly to text node. The forceTSpan is a hook
- // used in text outline hack.
- if (
- !hasMarkup &&
- !textOutline &&
- !ellipsis &&
- !width &&
- textStr.indexOf(' ') === -1
- ) {
- textNode.appendChild(doc.createTextNode(unescapeEntities(textStr)));
- // Complex strings, add more logic
- } else {
- if (tempParent) {
- // attach it to the DOM to read offset width
- tempParent.appendChild(textNode);
- }
- if (hasMarkup) {
- lines = renderer.styledMode ? (
- textStr
- .replace(
- /<(b|strong)>/g,
- '<span class="highcharts-strong">'
- )
- .replace(
- /<(i|em)>/g,
- '<span class="highcharts-emphasized">'
- )
- ) : (
- textStr
- .replace(
- /<(b|strong)>/g,
- '<span style="font-weight:bold">'
- )
- .replace(
- /<(i|em)>/g,
- '<span style="font-style:italic">'
- )
- );
- lines = lines
- .replace(/<a/g, '<span')
- .replace(/<\/(b|strong|i|em|a)>/g, '</span>')
- .split(/<br.*?>/g);
- } else {
- lines = [textStr];
- }
- // Trim empty lines (#5261)
- lines = lines.filter(function (line) {
- return line !== '';
- });
- // build the lines
- lines.forEach(function buildTextLines(line, lineNo) {
- var spans,
- spanNo = 0,
- lineLength = 0;
- line = line
- // Trim to prevent useless/costly process on the spaces
- // (#5258)
- .replace(/^\s+|\s+$/g, '')
- .replace(/<span/g, '|||<span')
- .replace(/<\/span>/g, '</span>|||');
- spans = line.split('|||');
- spans.forEach(function buildTextSpans(span) {
- if (span !== '' || spans.length === 1) {
- var attributes = {},
- tspan = doc.createElementNS(
- renderer.SVG_NS,
- 'tspan'
- ),
- classAttribute,
- styleAttribute, // #390
- hrefAttribute;
- classAttribute = parseAttribute(span, 'class');
- if (classAttribute) {
- attr(tspan, 'class', classAttribute);
- }
- styleAttribute = parseAttribute(span, 'style');
- if (styleAttribute) {
- styleAttribute = styleAttribute.replace(
- /(;| |^)color([ :])/,
- '$1fill$2'
- );
- attr(tspan, 'style', styleAttribute);
- }
- // Not for export - #1529
- hrefAttribute = parseAttribute(span, 'href');
- if (hrefAttribute && !forExport) {
- attr(
- tspan,
- 'onclick',
- 'location.href=\"' + hrefAttribute + '\"'
- );
- attr(tspan, 'class', 'highcharts-anchor');
- if (!renderer.styledMode) {
- css(tspan, { cursor: 'pointer' });
- }
- }
- // Strip away unsupported HTML tags (#7126)
- span = unescapeEntities(
- span.replace(/<[a-zA-Z\/](.|\n)*?>/g, '') || ' '
- );
- // Nested tags aren't supported, and cause crash in
- // Safari (#1596)
- if (span !== ' ') {
- // add the text node
- tspan.appendChild(doc.createTextNode(span));
- // First span in a line, align it to the left
- if (!spanNo) {
- if (lineNo && parentX !== null) {
- attributes.x = parentX;
- }
- } else {
- attributes.dx = 0; // #16
- }
- // add attributes
- attr(tspan, attributes);
- // Append it
- textNode.appendChild(tspan);
- // first span on subsequent line, add the line
- // height
- if (!spanNo && isSubsequentLine) {
- // allow getting the right offset height in
- // exporting in IE
- if (!svg && forExport) {
- css(tspan, { display: 'block' });
- }
- // Set the line height based on the font size of
- // either the text element or the tspan element
- attr(
- tspan,
- 'dy',
- getLineHeight(tspan)
- );
- }
- // Check width and apply soft breaks or ellipsis
- if (width) {
- var words = span.replace(
- /([^\^])-/g,
- '$1- '
- ).split(' '), // #1273
- hasWhiteSpace = !noWrap && (
- spans.length > 1 ||
- lineNo ||
- words.length > 1
- ),
- wrapLineNo = 0,
- dy = getLineHeight(tspan);
- if (ellipsis) {
- truncated = renderer.truncate(
- wrapper,
- tspan,
- span,
- undefined,
- 0,
- // Target width
- Math.max(
- 0,
- // Substract the font face to make
- // room for the ellipsis itself
- width - parseInt(fontSize || 12, 10)
- ),
- // Build the text to test for
- function (text, currentIndex) {
- return text.substring(
- 0,
- currentIndex
- ) + '\u2026';
- }
- );
- } else if (hasWhiteSpace) {
- while (words.length) {
- // For subsequent lines, create tspans
- // with the same style attributes as the
- // parent text node.
- if (
- words.length &&
- !noWrap &&
- wrapLineNo > 0
- ) {
- tspan = doc.createElementNS(
- SVG_NS,
- 'tspan'
- );
- attr(tspan, {
- dy: dy,
- x: parentX
- });
- if (styleAttribute) { // #390
- attr(
- tspan,
- 'style',
- styleAttribute
- );
- }
- // Start by appending the full
- // remaining text
- tspan.appendChild(
- doc.createTextNode(
- words.join(' ')
- .replace(/- /g, '-')
- )
- );
- textNode.appendChild(tspan);
- }
- // For each line, truncate the remaining
- // words into the line length.
- renderer.truncate(
- wrapper,
- tspan,
- null,
- words,
- wrapLineNo === 0 ? lineLength : 0,
- width,
- // Build the text to test for
- function (text, currentIndex) {
- return words
- .slice(0, currentIndex)
- .join(' ')
- .replace(/- /g, '-');
- }
- );
- lineLength = wrapper.actualWidth;
- wrapLineNo++;
- }
- }
- }
- spanNo++;
- }
- }
- });
- // To avoid beginning lines that doesn't add to the textNode
- // (#6144)
- isSubsequentLine = (
- isSubsequentLine ||
- textNode.childNodes.length
- );
- });
- if (ellipsis && truncated) {
- wrapper.attr(
- 'title',
- unescapeEntities(wrapper.textStr, ['<', '>']) // #7179
- );
- }
- if (tempParent) {
- tempParent.removeChild(textNode);
- }
- // Apply the text outline
- if (textOutline && wrapper.applyTextOutline) {
- wrapper.applyTextOutline(textOutline);
- }
- }
- },
- /**
- * Returns white for dark colors and black for bright colors.
- *
- * @function Highcharts.SVGRenderer#getContrast
- *
- * @param {Highcharts.ColorString} rgba
- * The color to get the contrast for.
- *
- * @return {string}
- * The contrast color, either `#000000` or `#FFFFFF`.
- */
- getContrast: function (rgba) {
- rgba = color(rgba).rgba;
- // The threshold may be discussed. Here's a proposal for adding
- // different weight to the color channels (#6216)
- rgba[0] *= 1; // red
- rgba[1] *= 1.2; // green
- rgba[2] *= 0.5; // blue
- return rgba[0] + rgba[1] + rgba[2] > 1.8 * 255 ? '#000000' : '#FFFFFF';
- },
- /**
- * Create a button with preset states.
- *
- * @function Highcharts.SVGRenderer#button
- *
- * @param {string} text
- * The text or HTML to draw.
- *
- * @param {number} x
- * The x position of the button's left side.
- *
- * @param {number} y
- * The y position of the button's top side.
- *
- * @param {Function} callback
- * The function to execute on button click or touch.
- *
- * @param {Highcharts.SVGAttributes} [normalState]
- * SVG attributes for the normal state.
- *
- * @param {Highcharts.SVGAttributes} [hoverState]
- * SVG attributes for the hover state.
- *
- * @param {Highcharts.SVGAttributes} [pressedState]
- * SVG attributes for the pressed state.
- *
- * @param {Highcharts.SVGAttributes} [disabledState]
- * SVG attributes for the disabled state.
- *
- * @param {Highcharts.SymbolKey} [shape=rect]
- * The shape type.
- *
- * @return {Highcharts.SVGElement}
- * The button element.
- */
- button: function (
- text,
- x,
- y,
- callback,
- normalState,
- hoverState,
- pressedState,
- disabledState,
- shape
- ) {
- var label = this.label(
- text,
- x,
- y,
- shape,
- null,
- null,
- null,
- null,
- 'button'
- ),
- curState = 0,
- styledMode = this.styledMode;
- // Default, non-stylable attributes
- label.attr(merge({
- 'padding': 8,
- 'r': 2
- }, normalState));
- if (!styledMode) {
- // Presentational
- var normalStyle,
- hoverStyle,
- pressedStyle,
- disabledStyle;
- // Normal state - prepare the attributes
- normalState = merge({
- fill: '#f7f7f7',
- stroke: '#cccccc',
- 'stroke-width': 1,
- style: {
- color: '#333333',
- cursor: 'pointer',
- fontWeight: 'normal'
- }
- }, normalState);
- normalStyle = normalState.style;
- delete normalState.style;
- // Hover state
- hoverState = merge(normalState, {
- fill: '#e6e6e6'
- }, hoverState);
- hoverStyle = hoverState.style;
- delete hoverState.style;
- // Pressed state
- pressedState = merge(normalState, {
- fill: '#e6ebf5',
- style: {
- color: '#000000',
- fontWeight: 'bold'
- }
- }, pressedState);
- pressedStyle = pressedState.style;
- delete pressedState.style;
- // Disabled state
- disabledState = merge(normalState, {
- style: {
- color: '#cccccc'
- }
- }, disabledState);
- disabledStyle = disabledState.style;
- delete disabledState.style;
- }
- // Add the events. IE9 and IE10 need mouseover and mouseout to funciton
- // (#667).
- addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
- if (curState !== 3) {
- label.setState(1);
- }
- });
- addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
- if (curState !== 3) {
- label.setState(curState);
- }
- });
- label.setState = function (state) {
- // Hover state is temporary, don't record it
- if (state !== 1) {
- label.state = curState = state;
- }
- // Update visuals
- label
- .removeClass(
- /highcharts-button-(normal|hover|pressed|disabled)/
- )
- .addClass(
- 'highcharts-button-' +
- ['normal', 'hover', 'pressed', 'disabled'][state || 0]
- );
- if (!styledMode) {
- label
- .attr([
- normalState,
- hoverState,
- pressedState,
- disabledState
- ][state || 0])
- .css([
- normalStyle,
- hoverStyle,
- pressedStyle,
- disabledStyle
- ][state || 0]);
- }
- };
- // Presentational attributes
- if (!styledMode) {
- label
- .attr(normalState)
- .css(extend({ cursor: 'default' }, normalStyle));
- }
- return label
- .on('click', function (e) {
- if (curState !== 3) {
- callback.call(label, e);
- }
- });
- },
- /**
- * Make a straight line crisper by not spilling out to neighbour pixels.
- *
- * @function Highcharts.SVGRenderer#crispLine
- *
- * @param {Highcharts.SVGPathArray} points
- * The original points on the format `['M', 0, 0, 'L', 100, 0]`.
- *
- * @param {number} width
- * The width of the line.
- *
- * @return {Highcharts.SVGPathArray}
- * The original points array, but modified to render crisply.
- */
- crispLine: function (points, width) {
- // normalize to a crisp line
- if (points[1] === points[4]) {
- // Substract due to #1129. Now bottom and left axis gridlines behave
- // the same.
- points[1] = points[4] = Math.round(points[1]) - (width % 2 / 2);
- }
- if (points[2] === points[5]) {
- points[2] = points[5] = Math.round(points[2]) + (width % 2 / 2);
- }
- return points;
- },
- /**
- * Draw a path, wraps the SVG `path` element.
- *
- * @sample highcharts/members/renderer-path-on-chart/
- * Draw a path in a chart
- * @sample highcharts/members/renderer-path/
- * Draw a path independent from a chart
- *
- * @example
- * var path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
- * .attr({ stroke: '#ff00ff' })
- * .add();
- *
- * @function Highcharts.SVGRenderer#path
- *
- * @param {Highcharts.SVGPathArray} [path]
- * An SVG path definition in array form.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- *
- *//**
- * Draw a path, wraps the SVG `path` element.
- *
- * @function Highcharts.SVGRenderer#path
- *
- * @param {Highcharts.SVGAttributes} [attribs]
- * The initial attributes.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- */
- path: function (path) {
- var attribs = this.styledMode ? {} : {
- fill: 'none'
- };
- if (isArray(path)) {
- attribs.d = path;
- } else if (isObject(path)) { // attributes
- extend(attribs, path);
- }
- return this.createElement('path').attr(attribs);
- },
- /**
- * Draw a circle, wraps the SVG `circle` element.
- *
- * @sample highcharts/members/renderer-circle/
- * Drawing a circle
- *
- * @function Highcharts.SVGRenderer#circle
- *
- * @param {number} [x]
- * The center x position.
- *
- * @param {number} [y]
- * The center y position.
- *
- * @param {number} [r]
- * The radius.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- *//**
- * Draw a circle, wraps the SVG `circle` element.
- *
- * @function Highcharts.SVGRenderer#circle
- *
- * @param {Highcharts.SVGAttributes} [attribs]
- * The initial attributes.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- */
- circle: function (x, y, r) {
- var attribs = (
- isObject(x) ?
- x :
- x === undefined ? {} : { x: x, y: y, r: r }
- ),
- wrapper = this.createElement('circle');
- // Setting x or y translates to cx and cy
- wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
- element.setAttribute('c' + key, value);
- };
- return wrapper.attr(attribs);
- },
- /**
- * Draw and return an arc.
- *
- * @sample highcharts/members/renderer-arc/
- * Drawing an arc
- *
- * @function Highcharts.SVGRenderer#arc
- *
- * @param {number} [x=0]
- * Center X position.
- *
- * @param {number} [y=0]
- * Center Y position.
- *
- * @param {number} [r=0]
- * The outer radius of the arc.
- *
- * @param {number} [innerR=0]
- * Inner radius like used in donut charts.
- *
- * @param {number} [start=0]
- * The starting angle of the arc in radians, where 0 is to the right
- * and `-Math.PI/2` is up.
- *
- * @param {number} [end=0]
- * The ending angle of the arc in radians, where 0 is to the right
- * and `-Math.PI/2` is up.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- *//**
- * Draw and return an arc. Overloaded function that takes arguments object.
- *
- * @function Highcharts.SVGRenderer#arc
- *
- * @param {Highcharts.SVGAttributes} attribs
- * Initial SVG attributes.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- */
- arc: function (x, y, r, innerR, start, end) {
- var arc,
- options;
- if (isObject(x)) {
- options = x;
- y = options.y;
- r = options.r;
- innerR = options.innerR;
- start = options.start;
- end = options.end;
- x = options.x;
- } else {
- options = {
- innerR: innerR,
- start: start,
- end: end
- };
- }
- // Arcs are defined as symbols for the ability to set
- // attributes in attr and animate
- arc = this.symbol('arc', x, y, r, r, options);
- arc.r = r; // #959
- return arc;
- },
- /**
- * Draw and return a rectangle.
- *
- * @function Highcharts.SVGRenderer#rect
- *
- * @param {number} [x]
- * Left position.
- *
- * @param {number} [y]
- * Top position.
- *
- * @param {number} [width]
- * Width of the rectangle.
- *
- * @param {number} [height]
- * Height of the rectangle.
- *
- * @param {number} [r]
- * Border corner radius.
- *
- * @param {number} [strokeWidth]
- * A stroke width can be supplied to allow crisp drawing.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- *//**
- * Draw and return a rectangle.
- *
- * @sample highcharts/members/renderer-rect-on-chart/
- * Draw a rectangle in a chart
- * @sample highcharts/members/renderer-rect/
- * Draw a rectangle independent from a chart
- *
- * @function Highcharts.SVGRenderer#rect
- *
- * @param {Highcharts.SVGAttributes} [attributes]
- * General SVG attributes for the rectangle.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- */
- rect: function (x, y, width, height, r, strokeWidth) {
- r = isObject(x) ? x.r : r;
- var wrapper = this.createElement('rect'),
- attribs = isObject(x) ? x : x === undefined ? {} : {
- x: x,
- y: y,
- width: Math.max(width, 0),
- height: Math.max(height, 0)
- };
- if (!this.styledMode) {
- if (strokeWidth !== undefined) {
- attribs.strokeWidth = strokeWidth;
- attribs = wrapper.crisp(attribs);
- }
- attribs.fill = 'none';
- }
- if (r) {
- attribs.r = r;
- }
- wrapper.rSetter = function (value, key, element) {
- attr(element, {
- rx: value,
- ry: value
- });
- };
- return wrapper.attr(attribs);
- },
- /**
- * Resize the {@link SVGRenderer#box} and re-align all aligned child
- * elements.
- *
- * @sample highcharts/members/renderer-g/
- * Show and hide grouped objects
- *
- * @function Highcharts.SVGRenderer#setSize
- *
- * @param {number} width
- * The new pixel width.
- *
- * @param {number} height
- * The new pixel height.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animate=true]
- * Whether and how to animate.
- */
- setSize: function (width, height, animate) {
- var renderer = this,
- alignedObjects = renderer.alignedObjects,
- i = alignedObjects.length;
- renderer.width = width;
- renderer.height = height;
- renderer.boxWrapper.animate({
- width: width,
- height: height
- }, {
- step: function () {
- this.attr({
- viewBox: '0 0 ' + this.attr('width') + ' ' +
- this.attr('height')
- });
- },
- duration: pick(animate, true) ? undefined : 0
- });
- while (i--) {
- alignedObjects[i].align();
- }
- },
- /**
- * Create and return an svg group element. Child
- * {@link Highcharts.SVGElement} objects are added to the group by using the
- * group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
- *
- * @function Highcharts.SVGRenderer#g
- *
- * @param {string} [name]
- * The group will be given a class name of `highcharts-{name}`. This
- * can be used for styling and scripting.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- */
- g: function (name) {
- var elem = this.createElement('g');
- return name ? elem.attr({ 'class': 'highcharts-' + name }) : elem;
- },
- /**
- * Display an image.
- *
- * @sample highcharts/members/renderer-image-on-chart/
- * Add an image in a chart
- * @sample highcharts/members/renderer-image/
- * Add an image independent of a chart
- *
- * @function Highcharts.SVGRenderer#image
- *
- * @param {string} src
- * The image source.
- *
- * @param {number} [x]
- * The X position.
- *
- * @param {number} [y]
- * The Y position.
- *
- * @param {number} [width]
- * The image width. If omitted, it defaults to the image file width.
- *
- * @param {number} [height]
- * The image height. If omitted it defaults to the image file
- * height.
- *
- * @param {Function} [onload]
- * Event handler for image load.
- *
- * @return {Highcharts.SVGElement}
- * The generated wrapper element.
- */
- image: function (src, x, y, width, height, onload) {
- var attribs = {
- preserveAspectRatio: 'none'
- },
- elemWrapper,
- dummy,
- setSVGImageSource = function (el, src) {
- // Set the href in the xlink namespace
- if (el.setAttributeNS) {
- el.setAttributeNS(
- 'http://www.w3.org/1999/xlink', 'href', src
- );
- } else {
- // could be exporting in IE
- // using href throws "not supported" in ie7 and under,
- // requries regex shim to fix later
- el.setAttribute('hc-svg-href', src);
- }
- },
- onDummyLoad = function (e) {
- setSVGImageSource(elemWrapper.element, src);
- onload.call(elemWrapper, e);
- };
- // optional properties
- if (arguments.length > 1) {
- extend(attribs, {
- x: x,
- y: y,
- width: width,
- height: height
- });
- }
- elemWrapper = this.createElement('image').attr(attribs);
- // Add load event if supplied
- if (onload) {
- // We have to use a dummy HTML image since IE support for SVG image
- // load events is very buggy. First set a transparent src, wait for
- // dummy to load, and then add the real src to the SVG image.
- setSVGImageSource(
- elemWrapper.element,
- 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' /* eslint-disable-line */
- );
- dummy = new win.Image();
- addEvent(dummy, 'load', onDummyLoad);
- dummy.src = src;
- if (dummy.complete) {
- onDummyLoad({});
- }
- } else {
- setSVGImageSource(elemWrapper.element, src);
- }
- return elemWrapper;
- },
- /**
- * Draw a symbol out of pre-defined shape paths from
- * {@link SVGRenderer#symbols}.
- * It is used in Highcharts for point makers, which cake a `symbol` option,
- * and label and button backgrounds like in the tooltip and stock flags.
- *
- * @function Highcharts.SVGRenderer#symbol
- *
- * @param {symbol} symbol
- * The symbol name.
- *
- * @param {number} x
- * The X coordinate for the top left position.
- *
- * @param {number} y
- * The Y coordinate for the top left position.
- *
- * @param {number} width
- * The pixel width.
- *
- * @param {number} height
- * The pixel height.
- *
- * @param {Highcharts.SymbolOptionsObject} [options]
- * Additional options, depending on the actual symbol drawn.
- *
- * @return {Highcharts.SVGElement}
- */
- symbol: function (symbol, x, y, width, height, options) {
- var ren = this,
- obj,
- imageRegex = /^url\((.*?)\)$/,
- isImage = imageRegex.test(symbol),
- sym = !isImage && (this.symbols[symbol] ? symbol : 'circle'),
- // get the symbol definition function
- symbolFn = sym && this.symbols[sym],
- // check if there's a path defined for this symbol
- path = defined(x) && symbolFn && symbolFn.call(
- this.symbols,
- Math.round(x),
- Math.round(y),
- width,
- height,
- options
- ),
- imageSrc,
- centerImage;
- if (symbolFn) {
- obj = this.path(path);
- if (!ren.styledMode) {
- obj.attr('fill', 'none');
- }
- // expando properties for use in animate and attr
- extend(obj, {
- symbolName: sym,
- x: x,
- y: y,
- width: width,
- height: height
- });
- if (options) {
- extend(obj, options);
- }
- // Image symbols
- } else if (isImage) {
- imageSrc = symbol.match(imageRegex)[1];
- // Create the image synchronously, add attribs async
- obj = this.image(imageSrc);
- // The image width is not always the same as the symbol width. The
- // image may be centered within the symbol, as is the case when
- // image shapes are used as label backgrounds, for example in flags.
- obj.imgwidth = pick(
- symbolSizes[imageSrc] && symbolSizes[imageSrc].width,
- options && options.width
- );
- obj.imgheight = pick(
- symbolSizes[imageSrc] && symbolSizes[imageSrc].height,
- options && options.height
- );
- /**
- * Set the size and position
- */
- centerImage = function () {
- obj.attr({
- width: obj.width,
- height: obj.height
- });
- };
- /**
- * Width and height setters that take both the image's physical size
- * and the label size into consideration, and translates the image
- * to center within the label.
- */
- ['width', 'height'].forEach(function (key) {
- obj[key + 'Setter'] = function (value, key) {
- var attribs = {},
- imgSize = this['img' + key],
- trans = key === 'width' ? 'translateX' : 'translateY';
- this[key] = value;
- if (defined(imgSize)) {
- if (this.element) {
- this.element.setAttribute(key, imgSize);
- }
- if (!this.alignByTranslate) {
- attribs[trans] = ((this[key] || 0) - imgSize) / 2;
- this.attr(attribs);
- }
- }
- };
- });
- if (defined(x)) {
- obj.attr({
- x: x,
- y: y
- });
- }
- obj.isImg = true;
- if (defined(obj.imgwidth) && defined(obj.imgheight)) {
- centerImage();
- } else {
- // Initialize image to be 0 size so export will still function
- // if there's no cached sizes.
- obj.attr({ width: 0, height: 0 });
- // Create a dummy JavaScript image to get the width and height.
- createElement('img', {
- onload: function () {
- var chart = charts[ren.chartIndex];
- // Special case for SVGs on IE11, the width is not
- // accessible until the image is part of the DOM
- // (#2854).
- if (this.width === 0) {
- css(this, {
- position: 'absolute',
- top: '-999em'
- });
- doc.body.appendChild(this);
- }
- // Center the image
- symbolSizes[imageSrc] = { // Cache for next
- width: this.width,
- height: this.height
- };
- obj.imgwidth = this.width;
- obj.imgheight = this.height;
- if (obj.element) {
- centerImage();
- }
- // Clean up after #2854 workaround.
- if (this.parentNode) {
- this.parentNode.removeChild(this);
- }
- // Fire the load event when all external images are
- // loaded
- ren.imgCount--;
- if (!ren.imgCount && chart && chart.onload) {
- chart.onload();
- }
- },
- src: imageSrc
- });
- this.imgCount++;
- }
- }
- return obj;
- },
- /**
- * An extendable collection of functions for defining symbol paths.
- *
- * @name Highcharts.SVGRenderer#symbols
- * @type {Highcharts.SymbolDictionary}
- */
- symbols: {
- 'circle': function (x, y, w, h) {
- // Return a full arc
- return this.arc(x + w / 2, y + h / 2, w / 2, h / 2, {
- start: 0,
- end: Math.PI * 2,
- open: false
- });
- },
- 'square': function (x, y, w, h) {
- return [
- 'M', x, y,
- 'L', x + w, y,
- x + w, y + h,
- x, y + h,
- 'Z'
- ];
- },
- 'triangle': function (x, y, w, h) {
- return [
- 'M', x + w / 2, y,
- 'L', x + w, y + h,
- x, y + h,
- 'Z'
- ];
- },
- 'triangle-down': function (x, y, w, h) {
- return [
- 'M', x, y,
- 'L', x + w, y,
- x + w / 2, y + h,
- 'Z'
- ];
- },
- 'diamond': function (x, y, w, h) {
- return [
- 'M', x + w / 2, y,
- 'L', x + w, y + h / 2,
- x + w / 2, y + h,
- x, y + h / 2,
- 'Z'
- ];
- },
- 'arc': function (x, y, w, h, options) {
- var start = options.start,
- rx = options.r || w,
- ry = options.r || h || w,
- proximity = 0.001,
- fullCircle =
- Math.abs(options.end - options.start - 2 * Math.PI) <
- proximity,
- // Substract a small number to prevent cos and sin of start and
- // end from becoming equal on 360 arcs (related: #1561)
- end = options.end - proximity,
- innerRadius = options.innerR,
- open = pick(options.open, fullCircle),
- cosStart = Math.cos(start),
- sinStart = Math.sin(start),
- cosEnd = Math.cos(end),
- sinEnd = Math.sin(end),
- // Proximity takes care of rounding errors around PI (#6971)
- longArc = options.end - start - Math.PI < proximity ? 0 : 1,
- arc;
- arc = [
- 'M',
- x + rx * cosStart,
- y + ry * sinStart,
- 'A', // arcTo
- rx, // x radius
- ry, // y radius
- 0, // slanting
- longArc, // long or short arc
- 1, // clockwise
- x + rx * cosEnd,
- y + ry * sinEnd
- ];
- if (defined(innerRadius)) {
- arc.push(
- open ? 'M' : 'L',
- x + innerRadius * cosEnd,
- y + innerRadius * sinEnd,
- 'A', // arcTo
- innerRadius, // x radius
- innerRadius, // y radius
- 0, // slanting
- longArc, // long or short arc
- 0, // clockwise
- x + innerRadius * cosStart,
- y + innerRadius * sinStart
- );
- }
- arc.push(open ? '' : 'Z'); // close
- return arc;
- },
- /**
- * Callout shape used for default tooltips, also used for rounded
- * rectangles in VML
- */
- 'callout': function (x, y, w, h, options) {
- var arrowLength = 6,
- halfDistance = 6,
- r = Math.min((options && options.r) || 0, w, h),
- safeDistance = r + halfDistance,
- anchorX = options && options.anchorX,
- anchorY = options && options.anchorY,
- path;
- path = [
- 'M', x + r, y,
- 'L', x + w - r, y, // top side
- 'C', x + w, y, x + w, y, x + w, y + r, // top-right corner
- 'L', x + w, y + h - r, // right side
- 'C', x + w, y + h, x + w, y + h, x + w - r, y + h, // bottom-rgt
- 'L', x + r, y + h, // bottom side
- 'C', x, y + h, x, y + h, x, y + h - r, // bottom-left corner
- 'L', x, y + r, // left side
- 'C', x, y, x, y, x + r, y // top-left corner
- ];
- // Anchor on right side
- if (anchorX && anchorX > w) {
- // Chevron
- if (
- anchorY > y + safeDistance &&
- anchorY < y + h - safeDistance
- ) {
- path.splice(
- 13,
- 3,
- 'L', x + w, anchorY - halfDistance,
- x + w + arrowLength, anchorY,
- x + w, anchorY + halfDistance,
- x + w, y + h - r
- );
- // Simple connector
- } else {
- path.splice(
- 13,
- 3,
- 'L', x + w, h / 2,
- anchorX, anchorY,
- x + w, h / 2,
- x + w, y + h - r
- );
- }
- // Anchor on left side
- } else if (anchorX && anchorX < 0) {
- // Chevron
- if (
- anchorY > y + safeDistance &&
- anchorY < y + h - safeDistance
- ) {
- path.splice(
- 33,
- 3,
- 'L', x, anchorY + halfDistance,
- x - arrowLength, anchorY,
- x, anchorY - halfDistance,
- x, y + r
- );
- // Simple connector
- } else {
- path.splice(
- 33,
- 3,
- 'L', x, h / 2,
- anchorX, anchorY,
- x, h / 2,
- x, y + r
- );
- }
- } else if ( // replace bottom
- anchorY &&
- anchorY > h &&
- anchorX > x + safeDistance &&
- anchorX < x + w - safeDistance
- ) {
- path.splice(
- 23,
- 3,
- 'L', anchorX + halfDistance, y + h,
- anchorX, y + h + arrowLength,
- anchorX - halfDistance, y + h,
- x + r, y + h
- );
- } else if ( // replace top
- anchorY &&
- anchorY < 0 &&
- anchorX > x + safeDistance &&
- anchorX < x + w - safeDistance
- ) {
- path.splice(
- 3,
- 3,
- 'L', anchorX - halfDistance, y,
- anchorX, y - arrowLength,
- anchorX + halfDistance, y,
- w - r, y
- );
- }
- return path;
- }
- },
- /**
- * Define a clipping rectangle. The clipping rectangle is later applied
- * to {@link SVGElement} objects through the {@link SVGElement#clip}
- * function.
- *
- * @example
- * var circle = renderer.circle(100, 100, 100)
- * .attr({ fill: 'red' })
- * .add();
- * var clipRect = renderer.clipRect(100, 100, 100, 100);
- *
- * // Leave only the lower right quarter visible
- * circle.clip(clipRect);
- *
- * @function Highcharts.SVGRenderer#clipRect
- *
- * @param {string} id
- *
- * @param {number} x
- *
- * @param {number} y
- *
- * @param {number} width
- *
- * @param {number} height
- *
- * @return {Highcharts.ClipRectElement}
- * A clipping rectangle.
- */
- clipRect: function (x, y, width, height) {
- var wrapper,
- id = H.uniqueKey(),
- clipPath = this.createElement('clipPath').attr({
- id: id
- }).add(this.defs);
- wrapper = this.rect(x, y, width, height, 0).add(clipPath);
- wrapper.id = id;
- wrapper.clipPath = clipPath;
- wrapper.count = 0;
- return wrapper;
- },
- /**
- * Draw text. The text can contain a subset of HTML, like spans and anchors
- * and some basic text styling of these. For more advanced features like
- * border and background, use {@link Highcharts.SVGRenderer#label} instead.
- * To update the text after render, run `text.attr({ text: 'New text' })`.
- *
- * @sample highcharts/members/renderer-text-on-chart/
- * Annotate the chart freely
- * @sample highcharts/members/renderer-on-chart/
- * Annotate with a border and in response to the data
- * @sample highcharts/members/renderer-text/
- * Formatted text
- *
- * @function Highcharts.SVGRenderer#text
- *
- * @param {string} str
- * The text of (subset) HTML to draw.
- *
- * @param {number} x
- * The x position of the text's lower left corner.
- *
- * @param {number} y
- * The y position of the text's lower left corner.
- *
- * @param {boolean} [useHTML=false]
- * Use HTML to render the text.
- *
- * @return {Highcharts.SVGElement}
- * The text object.
- */
- text: function (str, x, y, useHTML) {
- // declare variables
- var renderer = this,
- wrapper,
- attribs = {};
- if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
- return renderer.html(str, x, y);
- }
- attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
- if (y) {
- attribs.y = Math.round(y);
- }
- if (defined(str)) {
- attribs.text = str;
- }
- wrapper = renderer.createElement('text')
- .attr(attribs);
- if (!useHTML) {
- wrapper.xSetter = function (value, key, element) {
- var tspans = element.getElementsByTagName('tspan'),
- tspan,
- parentVal = element.getAttribute(key),
- i;
- for (i = 0; i < tspans.length; i++) {
- tspan = tspans[i];
- // If the x values are equal, the tspan represents a
- // linebreak
- if (tspan.getAttribute(key) === parentVal) {
- tspan.setAttribute(key, value);
- }
- }
- element.setAttribute(key, value);
- };
- }
- return wrapper;
- },
- /**
- * Utility to return the baseline offset and total line height from the font
- * size.
- *
- * @function Highcharts.SVGRenderer#fontMetrics
- *
- * @param {string} [fontSize]
- * The current font size to inspect. If not given, the font size
- * will be found from the DOM element.
- *
- * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement} [elem]
- * The element to inspect for a current font size.
- *
- * @return {Highcharts.FontMetricsObject}
- * The font metrics.
- */
- fontMetrics: function (fontSize, elem) {
- var lineHeight,
- baseline;
- if (
- (this.styledMode || !/px/.test(fontSize)) &&
- win.getComputedStyle // old IE doesn't support it
- ) {
- fontSize = elem && SVGElement.prototype.getStyle.call(
- elem,
- 'font-size'
- );
- } else {
- fontSize = fontSize ||
- // When the elem is a DOM element (#5932)
- (elem && elem.style && elem.style.fontSize) ||
- // Fall back on the renderer style default
- (this.style && this.style.fontSize);
- }
- // Handle different units
- if (/px/.test(fontSize)) {
- fontSize = pInt(fontSize);
- } else {
- fontSize = 12;
- }
- // Empirical values found by comparing font size and bounding box
- // height. Applies to the default font family.
- // https://jsfiddle.net/highcharts/7xvn7/
- lineHeight = fontSize < 24 ? fontSize + 3 : Math.round(fontSize * 1.2);
- baseline = Math.round(lineHeight * 0.8);
- return {
- h: lineHeight,
- b: baseline,
- f: fontSize
- };
- },
- /**
- * Correct X and Y positioning of a label for rotation (#1764).
- *
- * @private
- * @function Highcharts.SVGRenderer#rotCorr
- *
- * @param {number} baseline
- *
- * @param {number} rotation
- *
- * @param {boolean} alterY
- */
- rotCorr: function (baseline, rotation, alterY) {
- var y = baseline;
- if (rotation && alterY) {
- y = Math.max(y * Math.cos(rotation * deg2rad), 4);
- }
- return {
- x: (-baseline / 3) * Math.sin(rotation * deg2rad),
- y: y
- };
- },
- /**
- * Draw a label, which is an extended text element with support for border
- * and background. Highcharts creates a `g` element with a text and a `path`
- * or `rect` inside, to make it behave somewhat like a HTML div. Border and
- * background are set through `stroke`, `stroke-width` and `fill` attributes
- * using the {@link Highcharts.SVGElement#attr|attr} method. To update the
- * text after render, run `label.attr({ text: 'New text' })`.
- *
- * @sample highcharts/members/renderer-label-on-chart/
- * A label on the chart
- *
- * @function Highcharts.SVGRenderer#label
- *
- * @param {string} str
- * The initial text string or (subset) HTML to render.
- *
- * @param {number} x
- * The x position of the label's left side.
- *
- * @param {number} y
- * The y position of the label's top side or baseline, depending on
- * the `baseline` parameter.
- *
- * @param {string} [shape='rect']
- * The shape of the label's border/background, if any. Defaults to
- * `rect`. Other possible values are `callout` or other shapes
- * defined in {@link Highcharts.SVGRenderer#symbols}.
- *
- * @param {string} [shape='rect']
- * The shape of the label's border/background, if any. Defaults to
- * `rect`. Other possible values are `callout` or other shapes
- * defined in {@link Highcharts.SVGRenderer#symbols}.
- *
- * @param {number} [anchorX]
- * In case the `shape` has a pointer, like a flag, this is the
- * coordinates it should be pinned to.
- *
- * @param {number} [anchorY]
- * In case the `shape` has a pointer, like a flag, this is the
- * coordinates it should be pinned to.
- *
- * @param {boolean} [useHTML=false]
- * Wether to use HTML to render the label.
- *
- * @param {boolean} [baseline=false]
- * Whether to position the label relative to the text baseline,
- * like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
- * upper border of the rectangle.
- *
- * @param {string} [className]
- * Class name for the group.
- *
- * @return {Highcharts.SVGElement}
- * The generated label.
- */
- label: function (
- str,
- x,
- y,
- shape,
- anchorX,
- anchorY,
- useHTML,
- baseline,
- className
- ) {
- var renderer = this,
- styledMode = renderer.styledMode,
- wrapper = renderer.g(className !== 'button' && 'label'),
- text = wrapper.text = renderer.text('', 0, 0, useHTML)
- .attr({
- zIndex: 1
- }),
- box,
- bBox,
- alignFactor = 0,
- padding = 3,
- paddingLeft = 0,
- width,
- height,
- wrapperX,
- wrapperY,
- textAlign,
- deferredAttr = {},
- strokeWidth,
- baselineOffset,
- hasBGImage = /^url\((.*?)\)$/.test(shape),
- needsBox = styledMode || hasBGImage,
- getCrispAdjust = function () {
- return styledMode ?
- box.strokeWidth() % 2 / 2 :
- (strokeWidth ? parseInt(strokeWidth, 10) : 0) % 2 / 2;
- },
- updateBoxSize,
- updateTextPadding,
- boxAttr;
- if (className) {
- wrapper.addClass('highcharts-' + className);
- }
- /* This function runs after the label is added to the DOM (when the
- bounding box is available), and after the text of the label is
- updated to detect the new bounding box and reflect it in the border
- box. */
- updateBoxSize = function () {
- var style = text.element.style,
- crispAdjust,
- attribs = {};
- bBox = (
- (width === undefined || height === undefined || textAlign) &&
- defined(text.textStr) &&
- text.getBBox()
- ); // #3295 && 3514 box failure when string equals 0
- wrapper.width = (
- (width || bBox.width || 0) +
- 2 * padding +
- paddingLeft
- );
- wrapper.height = (height || bBox.height || 0) + 2 * padding;
- // Update the label-scoped y offset
- baselineOffset = padding + Math.min(
- renderer.fontMetrics(style && style.fontSize, text).b,
- // Math.min because of inline style (#9400)
- bBox ? bBox.height : Infinity
- );
- if (needsBox) {
- // Create the border box if it is not already present
- if (!box) {
- // Symbol definition exists (#5324)
- wrapper.box = box = renderer.symbols[shape] || hasBGImage ?
- renderer.symbol(shape) :
- renderer.rect();
- box.addClass( // Don't use label className for buttons
- (className === 'button' ? '' : 'highcharts-label-box') +
- (className ? ' highcharts-' + className + '-box' : '')
- );
- box.add(wrapper);
- crispAdjust = getCrispAdjust();
- attribs.x = crispAdjust;
- attribs.y = (baseline ? -baselineOffset : 0) + crispAdjust;
- }
- // Apply the box attributes
- attribs.width = Math.round(wrapper.width);
- attribs.height = Math.round(wrapper.height);
- box.attr(extend(attribs, deferredAttr));
- deferredAttr = {};
- }
- };
- /*
- * This function runs after setting text or padding, but only if padding
- * is changed.
- */
- updateTextPadding = function () {
- var textX = paddingLeft + padding,
- textY;
- // determin y based on the baseline
- textY = baseline ? 0 : baselineOffset;
- // compensate for alignment
- if (
- defined(width) &&
- bBox &&
- (textAlign === 'center' || textAlign === 'right')
- ) {
- textX += { center: 0.5, right: 1 }[textAlign] *
- (width - bBox.width);
- }
- // update if anything changed
- if (textX !== text.x || textY !== text.y) {
- text.attr('x', textX);
- // #8159 - prevent misplaced data labels in treemap
- // (useHTML: true)
- if (text.hasBoxWidthChanged) {
- bBox = text.getBBox(true);
- updateBoxSize();
- }
- if (textY !== undefined) {
- text.attr('y', textY);
- }
- }
- // record current values
- text.x = textX;
- text.y = textY;
- };
- /*
- * Set a box attribute, or defer it if the box is not yet created
- */
- boxAttr = function (key, value) {
- if (box) {
- box.attr(key, value);
- } else {
- deferredAttr[key] = value;
- }
- };
- /*
- * After the text element is added, get the desired size of the border
- * box and add it before the text in the DOM.
- */
- wrapper.onAdd = function () {
- text.add(wrapper);
- wrapper.attr({
- // Alignment is available now (#3295, 0 not rendered if given
- // as a value)
- text: (str || str === 0) ? str : '',
- x: x,
- y: y
- });
- if (box && defined(anchorX)) {
- wrapper.attr({
- anchorX: anchorX,
- anchorY: anchorY
- });
- }
- };
- /*
- * Add specific attribute setters.
- */
- // only change local variables
- wrapper.widthSetter = function (value) {
- width = H.isNumber(value) ? value : null; // width:auto => null
- };
- wrapper.heightSetter = function (value) {
- height = value;
- };
- wrapper['text-alignSetter'] = function (value) {
- textAlign = value;
- };
- wrapper.paddingSetter = function (value) {
- if (defined(value) && value !== padding) {
- padding = wrapper.padding = value;
- updateTextPadding();
- }
- };
- wrapper.paddingLeftSetter = function (value) {
- if (defined(value) && value !== paddingLeft) {
- paddingLeft = value;
- updateTextPadding();
- }
- };
- // change local variable and prevent setting attribute on the group
- wrapper.alignSetter = function (value) {
- value = { left: 0, center: 0.5, right: 1 }[value];
- if (value !== alignFactor) {
- alignFactor = value;
- // Bounding box exists, means we're dynamically changing
- if (bBox) {
- wrapper.attr({ x: wrapperX }); // #5134
- }
- }
- };
- // apply these to the box and the text alike
- wrapper.textSetter = function (value) {
- if (value !== undefined) {
- text.textSetter(value);
- }
- updateBoxSize();
- updateTextPadding();
- };
- // apply these to the box but not to the text
- wrapper['stroke-widthSetter'] = function (value, key) {
- if (value) {
- needsBox = true;
- }
- strokeWidth = this['stroke-width'] = value;
- boxAttr(key, value);
- };
- if (styledMode) {
- wrapper.rSetter = function (value, key) {
- boxAttr(key, value);
- };
- } else {
- wrapper.strokeSetter =
- wrapper.fillSetter =
- wrapper.rSetter = function (value, key) {
- if (key !== 'r') {
- if (key === 'fill' && value) {
- needsBox = true;
- }
- // for animation getter (#6776)
- wrapper[key] = value;
- }
- boxAttr(key, value);
- };
- }
- wrapper.anchorXSetter = function (value, key) {
- anchorX = wrapper.anchorX = value;
- boxAttr(key, Math.round(value) - getCrispAdjust() - wrapperX);
- };
- wrapper.anchorYSetter = function (value, key) {
- anchorY = wrapper.anchorY = value;
- boxAttr(key, value - wrapperY);
- };
- // rename attributes
- wrapper.xSetter = function (value) {
- wrapper.x = value; // for animation getter
- if (alignFactor) {
- value -= alignFactor * ((width || bBox.width) + 2 * padding);
- // Force animation even when setting to the same value (#7898)
- wrapper['forceAnimate:x'] = true;
- }
- wrapperX = Math.round(value);
- wrapper.attr('translateX', wrapperX);
- };
- wrapper.ySetter = function (value) {
- wrapperY = wrapper.y = Math.round(value);
- wrapper.attr('translateY', wrapperY);
- };
- // Redirect certain methods to either the box or the text
- var baseCss = wrapper.css;
- var wrapperExtension = {
- /**
- * Pick up some properties and apply them to the text instead of the
- * wrapper.
- */
- css: function (styles) {
- if (styles) {
- var textStyles = {};
- // Create a copy to avoid altering the original object
- // (#537)
- styles = merge(styles);
- wrapper.textProps.forEach(function (prop) {
- if (styles[prop] !== undefined) {
- textStyles[prop] = styles[prop];
- delete styles[prop];
- }
- });
- text.css(textStyles);
- // Update existing text and box
- if ('width' in textStyles) {
- updateBoxSize();
- }
- // Keep updated (#9400)
- if ('fontSize' in textStyles) {
- updateBoxSize();
- updateTextPadding();
- }
- }
- return baseCss.call(wrapper, styles);
- },
- /*
- * Return the bounding box of the box, not the group.
- */
- getBBox: function () {
- return {
- width: bBox.width + 2 * padding,
- height: bBox.height + 2 * padding,
- x: bBox.x - padding,
- y: bBox.y - padding
- };
- },
- /**
- * Destroy and release memory.
- */
- destroy: function () {
- // Added by button implementation
- removeEvent(wrapper.element, 'mouseenter');
- removeEvent(wrapper.element, 'mouseleave');
- if (text) {
- text = text.destroy();
- }
- if (box) {
- box = box.destroy();
- }
- // Call base implementation to destroy the rest
- SVGElement.prototype.destroy.call(wrapper);
- // Release local pointers (#1298)
- wrapper =
- renderer =
- updateBoxSize =
- updateTextPadding =
- boxAttr = null;
- }
- };
- if (!styledMode) {
- /**
- * Apply the shadow to the box.
- *
- * @ignore
- * @function Highcharts.SVGElement#shadow
- *
- * @return {Highcharts.SVGElement}
- */
- wrapperExtension.shadow = function (b) {
- if (b) {
- updateBoxSize();
- if (box) {
- box.shadow(b);
- }
- }
- return wrapper;
- };
- }
- return extend(wrapper, wrapperExtension);
- }
- }); // end SVGRenderer
- // general renderer
- H.Renderer = SVGRenderer;
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var attr = H.attr,
- createElement = H.createElement,
- css = H.css,
- defined = H.defined,
- extend = H.extend,
- isFirefox = H.isFirefox,
- isMS = H.isMS,
- isWebKit = H.isWebKit,
- pick = H.pick,
- pInt = H.pInt,
- SVGElement = H.SVGElement,
- SVGRenderer = H.SVGRenderer,
- win = H.win;
- // Extend SvgElement for useHTML option.
- extend(SVGElement.prototype, /** @lends SVGElement.prototype */ {
- /**
- * Apply CSS to HTML elements. This is used in text within SVG rendering and
- * by the VML renderer
- *
- * @private
- * @function Highcharts.SVGElement#htmlCss
- *
- * @param {Highcharts.CSSObject} styles
- *
- * @return {Highcharts.SVGElement}
- */
- htmlCss: function (styles) {
- var wrapper = this,
- element = wrapper.element,
- // When setting or unsetting the width style, we need to update
- // transform (#8809)
- isSettingWidth = (
- element.tagName === 'SPAN' &&
- styles &&
- 'width' in styles
- ),
- textWidth = pick(
- isSettingWidth && styles.width,
- undefined
- ),
- doTransform;
- if (isSettingWidth) {
- delete styles.width;
- wrapper.textWidth = textWidth;
- doTransform = true;
- }
- if (styles && styles.textOverflow === 'ellipsis') {
- styles.whiteSpace = 'nowrap';
- styles.overflow = 'hidden';
- }
- wrapper.styles = extend(wrapper.styles, styles);
- css(wrapper.element, styles);
- // Now that all styles are applied, to the transform
- if (doTransform) {
- wrapper.htmlUpdateTransform();
- }
- return wrapper;
- },
- /**
- * VML and useHTML method for calculating the bounding box based on offsets.
- *
- * @private
- * @function Highcharts.SVGElement#htmlGetBBox
- *
- * @param {boolean} refresh
- * Whether to force a fresh value from the DOM or to use the cached
- * value.
- *
- * @return {Highcharts.BBoxObject}
- * A hash containing values for x, y, width and height.
- */
- htmlGetBBox: function () {
- var wrapper = this,
- element = wrapper.element;
- return {
- x: element.offsetLeft,
- y: element.offsetTop,
- width: element.offsetWidth,
- height: element.offsetHeight
- };
- },
- /**
- * VML override private method to update elements based on internal
- * properties based on SVG transform.
- *
- * @private
- * @function Highcharts.SVGElement#htmlUpdateTransform
- */
- htmlUpdateTransform: function () {
- // aligning non added elements is expensive
- if (!this.added) {
- this.alignOnAdd = true;
- return;
- }
- var wrapper = this,
- renderer = wrapper.renderer,
- elem = wrapper.element,
- translateX = wrapper.translateX || 0,
- translateY = wrapper.translateY || 0,
- x = wrapper.x || 0,
- y = wrapper.y || 0,
- align = wrapper.textAlign || 'left',
- alignCorrection = { left: 0, center: 0.5, right: 1 }[align],
- styles = wrapper.styles,
- whiteSpace = styles && styles.whiteSpace;
- function getTextPxLength() {
- // Reset multiline/ellipsis in order to read width (#4928,
- // #5417)
- css(elem, {
- width: '',
- whiteSpace: whiteSpace || 'nowrap'
- });
- return elem.offsetWidth;
- }
- // apply translate
- css(elem, {
- marginLeft: translateX,
- marginTop: translateY
- });
- if (!renderer.styledMode && wrapper.shadows) { // used in labels/tooltip
- wrapper.shadows.forEach(function (shadow) {
- css(shadow, {
- marginLeft: translateX + 1,
- marginTop: translateY + 1
- });
- });
- }
- // apply inversion
- if (wrapper.inverted) { // wrapper is a group
- elem.childNodes.forEach(function (child) {
- renderer.invertChild(child, elem);
- });
- }
- if (elem.tagName === 'SPAN') {
- var rotation = wrapper.rotation,
- baseline,
- textWidth = wrapper.textWidth && pInt(wrapper.textWidth),
- currentTextTransform = [
- rotation,
- align,
- elem.innerHTML,
- wrapper.textWidth,
- wrapper.textAlign
- ].join(',');
- // Update textWidth. Use the memoized textPxLength if possible, to
- // avoid the getTextPxLength function using elem.offsetWidth.
- // Calling offsetWidth affects rendering time as it forces layout
- // (#7656).
- if (
- textWidth !== wrapper.oldTextWidth &&
- (
- (textWidth > wrapper.oldTextWidth) ||
- (wrapper.textPxLength || getTextPxLength()) > textWidth
- ) && (
- // Only set the width if the text is able to word-wrap, or
- // text-overflow is ellipsis (#9537)
- /[ \-]/.test(elem.textContent || elem.innerText) ||
- elem.style.textOverflow === 'ellipsis'
- )
- ) { // #983, #1254
- css(elem, {
- width: textWidth + 'px',
- display: 'block',
- whiteSpace: whiteSpace || 'normal' // #3331
- });
- wrapper.oldTextWidth = textWidth;
- wrapper.hasBoxWidthChanged = true; // #8159
- } else {
- wrapper.hasBoxWidthChanged = false; // #8159
- }
- // Do the calculations and DOM access only if properties changed
- if (currentTextTransform !== wrapper.cTT) {
- baseline = renderer.fontMetrics(elem.style.fontSize, elem).b;
- // Renderer specific handling of span rotation, but only if we
- // have something to update.
- if (
- defined(rotation) &&
- (
- (rotation !== (wrapper.oldRotation || 0)) ||
- (align !== wrapper.oldAlign)
- )
- ) {
- wrapper.setSpanRotation(
- rotation,
- alignCorrection,
- baseline
- );
- }
- wrapper.getSpanCorrection(
- // Avoid elem.offsetWidth if we can, it affects rendering
- // time heavily (#7656)
- (
- (!defined(rotation) && wrapper.textPxLength) || // #7920
- elem.offsetWidth
- ),
- baseline,
- alignCorrection,
- rotation,
- align
- );
- }
- // apply position with correction
- css(elem, {
- left: (x + (wrapper.xCorr || 0)) + 'px',
- top: (y + (wrapper.yCorr || 0)) + 'px'
- });
- // record current text transform
- wrapper.cTT = currentTextTransform;
- wrapper.oldRotation = rotation;
- wrapper.oldAlign = align;
- }
- },
- /**
- * Set the rotation of an individual HTML span.
- *
- * @private
- * @function Highcharts.SVGElement#setSpanRotation
- *
- * @param {number} rotation
- *
- * @param {number} alignCorrection
- *
- * @param {number} baseline
- */
- setSpanRotation: function (rotation, alignCorrection, baseline) {
- var rotationStyle = {},
- cssTransformKey = this.renderer.getTransformKey();
- rotationStyle[cssTransformKey] = rotationStyle.transform =
- 'rotate(' + rotation + 'deg)';
- rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] =
- rotationStyle.transformOrigin =
- (alignCorrection * 100) + '% ' + baseline + 'px';
- css(this.element, rotationStyle);
- },
- /**
- * Get the correction in X and Y positioning as the element is rotated.
- *
- * @private
- * @function Highcharts.SVGElement#getSpanCorrection
- *
- * @param {number} width
- *
- * @param {number} baseline
- *
- * @param {number} alignCorrection
- */
- getSpanCorrection: function (width, baseline, alignCorrection) {
- this.xCorr = -width * alignCorrection;
- this.yCorr = -baseline;
- }
- });
- // Extend SvgRenderer for useHTML option.
- extend(SVGRenderer.prototype, /** @lends SVGRenderer.prototype */ {
- /**
- * @private
- * @function Highcharts.SVGRenderer#getTransformKey
- *
- * @return {string}
- */
- getTransformKey: function () {
- return isMS && !/Edge/.test(win.navigator.userAgent) ?
- '-ms-transform' :
- isWebKit ?
- '-webkit-transform' :
- isFirefox ?
- 'MozTransform' :
- win.opera ?
- '-o-transform' :
- '';
- },
- /**
- * Create HTML text node. This is used by the VML renderer as well as the
- * SVG renderer through the useHTML option.
- *
- * @private
- * @function Highcharts.SVGRenderer#html
- *
- * @param {string} str
- * The text of (subset) HTML to draw.
- *
- * @param {number} x
- * The x position of the text's lower left corner.
- *
- * @param {number} y
- * The y position of the text's lower left corner.
- *
- * @return {Highcharts.HTMLDOMElement}
- */
- html: function (str, x, y) {
- var wrapper = this.createElement('span'),
- element = wrapper.element,
- renderer = wrapper.renderer,
- isSVG = renderer.isSVG,
- addSetters = function (element, style) {
- // These properties are set as attributes on the SVG group, and
- // as identical CSS properties on the div. (#3542)
- ['opacity', 'visibility'].forEach(function (prop) {
- element[prop + 'Setter'] = function (
- value,
- key,
- elem
- ) {
- SVGElement.prototype[prop + 'Setter']
- .call(this, value, key, elem);
- style[key] = value;
- };
- });
- element.addedSetters = true;
- },
- chart = H.charts[renderer.chartIndex],
- styledMode = chart && chart.styledMode;
- // Text setter
- wrapper.textSetter = function (value) {
- if (value !== element.innerHTML) {
- delete this.bBox;
- }
- this.textStr = value;
- element.innerHTML = pick(value, '');
- wrapper.doTransform = true;
- };
- // Add setters for the element itself (#4938)
- if (isSVG) { // #4938, only for HTML within SVG
- addSetters(wrapper, wrapper.element.style);
- }
- // Various setters which rely on update transform
- wrapper.xSetter =
- wrapper.ySetter =
- wrapper.alignSetter =
- wrapper.rotationSetter =
- function (value, key) {
- if (key === 'align') {
- // Do not overwrite the SVGElement.align method. Same as VML.
- key = 'textAlign';
- }
- wrapper[key] = value;
- wrapper.doTransform = true;
- };
- // Runs at the end of .attr()
- wrapper.afterSetters = function () {
- // Update transform. Do this outside the loop to prevent redundant
- // updating for batch setting of attributes.
- if (this.doTransform) {
- this.htmlUpdateTransform();
- this.doTransform = false;
- }
- };
- // Set the default attributes
- wrapper
- .attr({
- text: str,
- x: Math.round(x),
- y: Math.round(y)
- })
- .css({
- position: 'absolute'
- });
- if (!styledMode) {
- wrapper.css({
- fontFamily: this.style.fontFamily,
- fontSize: this.style.fontSize
- });
- }
- // Keep the whiteSpace style outside the wrapper.styles collection
- element.style.whiteSpace = 'nowrap';
- // Use the HTML specific .css method
- wrapper.css = wrapper.htmlCss;
- // This is specific for HTML within SVG
- if (isSVG) {
- wrapper.add = function (svgGroupWrapper) {
- var htmlGroup,
- container = renderer.box.parentNode,
- parentGroup,
- parents = [];
- this.parentGroup = svgGroupWrapper;
- // Create a mock group to hold the HTML elements
- if (svgGroupWrapper) {
- htmlGroup = svgGroupWrapper.div;
- if (!htmlGroup) {
- // Read the parent chain into an array and read from top
- // down
- parentGroup = svgGroupWrapper;
- while (parentGroup) {
- parents.push(parentGroup);
- // Move up to the next parent group
- parentGroup = parentGroup.parentGroup;
- }
- // Ensure dynamically updating position when any parent
- // is translated
- parents.reverse().forEach(function (parentGroup) {
- var htmlGroupStyle,
- cls = attr(parentGroup.element, 'class');
- // Common translate setter for X and Y on the HTML
- // group. Reverted the fix for #6957 du to
- // positioning problems and offline export (#7254,
- // #7280, #7529)
- function translateSetter(value, key) {
- parentGroup[key] = value;
- if (key === 'translateX') {
- htmlGroupStyle.left = value + 'px';
- } else {
- htmlGroupStyle.top = value + 'px';
- }
- parentGroup.doTransform = true;
- }
- if (cls) {
- cls = { className: cls };
- } // else null
- // Create a HTML div and append it to the parent div
- // to emulate the SVG group structure
- htmlGroup =
- parentGroup.div =
- parentGroup.div || createElement('div', cls, {
- position: 'absolute',
- left: (parentGroup.translateX || 0) + 'px',
- top: (parentGroup.translateY || 0) + 'px',
- display: parentGroup.display,
- opacity: parentGroup.opacity, // #5075
- pointerEvents: (
- parentGroup.styles &&
- parentGroup.styles.pointerEvents
- ) // #5595
- // the top group is appended to container
- }, htmlGroup || container);
- // Shortcut
- htmlGroupStyle = htmlGroup.style;
- // Set listeners to update the HTML div's position
- // whenever the SVG group position is changed.
- extend(parentGroup, {
- // (#7287) Pass htmlGroup to use
- // the related group
- classSetter: (function (htmlGroup) {
- return function (value) {
- this.element.setAttribute(
- 'class',
- value
- );
- htmlGroup.className = value;
- };
- }(htmlGroup)),
- on: function () {
- if (parents[0].div) { // #6418
- wrapper.on.apply(
- { element: parents[0].div },
- arguments
- );
- }
- return parentGroup;
- },
- translateXSetter: translateSetter,
- translateYSetter: translateSetter
- });
- if (!parentGroup.addedSetters) {
- addSetters(parentGroup, htmlGroupStyle);
- }
- });
- }
- } else {
- htmlGroup = container;
- }
- htmlGroup.appendChild(element);
- // Shared with VML:
- wrapper.added = true;
- if (wrapper.alignOnAdd) {
- wrapper.htmlUpdateTransform();
- }
- return wrapper;
- };
- }
- return wrapper;
- }
- });
- }(Highcharts));
- (function (Highcharts) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Normalized interval.
- *
- * @interface Highcharts.NormalizedIntervalObject
- *//**
- * The interval in axis values (ms).
- *
- * @name Highcharts.NormalizedIntervalObject#unitRange
- * @type {number}
- *//**
- * The count.
- *
- * @name Highcharts.NormalizedIntervalObject#count
- * @type {number}
- */
- /**
- * Function of an additional date format specifier.
- *
- * @callback Highcharts.TimeFormatCallbackFunction
- *
- * @param {number} timestamp
- * The time to format.
- *
- * @return {string}
- * The formatted portion of the date.
- */
- /**
- * Additonal time tick information.
- *
- * @interface Highcharts.TimeTicksInfoObject
- * @augments Highcharts.NormalizedIntervalObject
- *//**
- * @name Highcharts.TimeTicksInfoObject#higherRanks
- * @type {Array<string>}
- *//**
- * @name Highcharts.TimeTicksInfoObject#totalRange
- * @type {number}
- */
- /**
- * Time ticks.
- *
- * @interface Highcharts.TimeTicksObject
- * @augments Array<number>
- *//**
- * @name Highcharts.TimeTicksObject#info
- * @type {Highcharts.TimeTicksInfoObject}
- */
- var H = Highcharts,
- defined = H.defined,
- extend = H.extend,
- merge = H.merge,
- pick = H.pick,
- timeUnits = H.timeUnits,
- win = H.win;
- /**
- * The Time class. Time settings are applied in general for each page using
- * `Highcharts.setOptions`, or individually for each Chart item through the
- * [time](https://api.highcharts.com/highcharts/time) options set.
- *
- * The Time object is available from {@link Highcharts.Chart#time},
- * which refers to `Highcharts.time` if no individual time settings are
- * applied.
- *
- * @example
- * // Apply time settings globally
- * Highcharts.setOptions({
- * time: {
- * timezone: 'Europe/London'
- * }
- * });
- *
- * // Apply time settings by instance
- * var chart = Highcharts.chart('container', {
- * time: {
- * timezone: 'America/New_York'
- * },
- * series: [{
- * data: [1, 4, 3, 5]
- * }]
- * });
- *
- * // Use the Time object
- * console.log(
- * 'Current time in New York',
- * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
- * );
- *
- * @class
- * @name Highcharts.Time
- *
- * @param {Highcharts.TimeOptions} options
- * Time options as defined in [chart.options.time](/highcharts/time).
- *
- * @since 6.0.5
- */
- Highcharts.Time = function (options) {
- this.update(options, false);
- };
- Highcharts.Time.prototype = {
- /**
- * Time options that can apply globally or to individual charts. These
- * settings affect how `datetime` axes are laid out, how tooltips are
- * formatted, how series
- * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
- * the Highstock range selector handles time.
- *
- * The common use case is that all charts in the same Highcharts object
- * share the same time settings, in which case the global settings are set
- * using `setOptions`.
- *
- * ```js
- * // Apply time settings globally
- * Highcharts.setOptions({
- * time: {
- * timezone: 'Europe/London'
- * }
- * });
- * // Apply time settings by instance
- * var chart = Highcharts.chart('container', {
- * time: {
- * timezone: 'America/New_York'
- * },
- * series: [{
- * data: [1, 4, 3, 5]
- * }]
- * });
- *
- * // Use the Time object
- * console.log(
- * 'Current time in New York',
- * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
- * );
- * ```
- *
- * Since v6.0.5, the time options were moved from the `global` obect to the
- * `time` object, and time options can be set on each individual chart.
- *
- * @sample {highcharts|highstock}
- * highcharts/time/timezone/
- * Set the timezone globally
- * @sample {highcharts}
- * highcharts/time/individual/
- * Set the timezone per chart instance
- * @sample {highstock}
- * stock/time/individual/
- * Set the timezone per chart instance
- *
- * @since 6.0.5
- * @apioption time
- */
- /**
- * Whether to use UTC time for axis scaling, tickmark placement and
- * time display in `Highcharts.dateFormat`. Advantages of using UTC
- * is that the time displays equally regardless of the user agent's
- * time zone settings. Local time can be used when the data is loaded
- * in real time or when correct Daylight Saving Time transitions are
- * required.
- *
- * @sample {highcharts} highcharts/time/useutc-true/
- * True by default
- * @sample {highcharts} highcharts/time/useutc-false/
- * False
- *
- * @type {boolean}
- * @default true
- * @apioption time.useUTC
- */
- /**
- * A custom `Date` class for advanced date handling. For example,
- * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
- * handle Jalali dates.
- *
- * @type {*}
- * @since 4.0.4
- * @product highcharts highstock gantt
- * @apioption time.Date
- */
- /**
- * A callback to return the time zone offset for a given datetime. It
- * takes the timestamp in terms of milliseconds since January 1 1970,
- * and returns the timezone offset in minutes. This provides a hook
- * for drawing time based charts in specific time zones using their
- * local DST crossover dates, with the help of external libraries.
- *
- * @see [global.timezoneOffset](#global.timezoneOffset)
- *
- * @sample {highcharts|highstock} highcharts/time/gettimezoneoffset/
- * Use moment.js to draw Oslo time regardless of browser locale
- *
- * @type {Function}
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption time.getTimezoneOffset
- */
- /**
- * Requires [moment.js](http://momentjs.com/). If the timezone option
- * is specified, it creates a default
- * [getTimezoneOffset](#time.getTimezoneOffset) function that looks
- * up the specified timezone in moment.js. If moment.js is not included,
- * this throws a Highcharts error in the console, but does not crash the
- * chart.
- *
- * @see [getTimezoneOffset](#time.getTimezoneOffset)
- *
- * @sample {highcharts|highstock} highcharts/time/timezone/
- * Europe/Oslo
- *
- * @type {string}
- * @since 5.0.7
- * @product highcharts highstock gantt
- * @apioption time.timezone
- */
- /**
- * The timezone offset in minutes. Positive values are west, negative
- * values are east of UTC, as in the ECMAScript
- * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
- * method. Use this to display UTC based data in a predefined time zone.
- *
- * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
- *
- * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
- * Timezone offset
- *
- * @type {number}
- * @default 0
- * @since 3.0.8
- * @product highcharts highstock gantt
- * @apioption time.timezoneOffset
- */
- defaultOptions: {},
- /**
- * Update the Time object with current options. It is called internally on
- * initiating Highcharts, after running `Highcharts.setOptions` and on
- * `Chart.update`.
- *
- * @private
- * @function Highcharts.Time#update
- *
- * @param {Highcharts.TimeOptions} options
- */
- update: function (options) {
- var useUTC = pick(options && options.useUTC, true),
- time = this;
- this.options = options = merge(true, this.options || {}, options);
- // Allow using a different Date class
- this.Date = options.Date || win.Date;
- this.useUTC = useUTC;
- this.timezoneOffset = useUTC && options.timezoneOffset;
- /**
- * Get the time zone offset based on the current timezone information as
- * set in the global options.
- *
- * @function Highcharts.Time#getTimezoneOffset
- *
- * @param {number} timestamp
- * The JavaScript timestamp to inspect.
- *
- * @return {number}
- * The timezone offset in minutes compared to UTC.
- */
- this.getTimezoneOffset = this.timezoneOffsetFunction();
- /*
- * The time object has options allowing for variable time zones, meaning
- * the axis ticks or series data needs to consider this.
- */
- this.variableTimezone = !!(
- !useUTC ||
- options.getTimezoneOffset ||
- options.timezone
- );
- // UTC time with timezone handling
- if (this.variableTimezone || this.timezoneOffset) {
- this.get = function (unit, date) {
- var realMs = date.getTime(),
- ms = realMs - time.getTimezoneOffset(date),
- ret;
- date.setTime(ms); // Temporary adjust to timezone
- ret = date['getUTC' + unit]();
- date.setTime(realMs); // Reset
- return ret;
- };
- this.set = function (unit, date, value) {
- var ms, offset, newOffset;
- // For lower order time units, just set it directly using local
- // time
- if (
- unit === 'Milliseconds' ||
- unit === 'Seconds' ||
- // If we're dealting with minutes, we only need to
- // consider timezone if we're in Indian time zones with
- // half-hour offsets (#8768).
- (
- unit === 'Minutes' &&
- date.getTimezoneOffset() % 60 === 0
- )
- ) {
- date['set' + unit](value);
- // Higher order time units need to take the time zone into
- // account
- } else {
- // Adjust by timezone
- offset = time.getTimezoneOffset(date);
- ms = date.getTime() - offset;
- date.setTime(ms);
- date['setUTC' + unit](value);
- newOffset = time.getTimezoneOffset(date);
- ms = date.getTime() + newOffset;
- date.setTime(ms);
- }
- };
- // UTC time with no timezone handling
- } else if (useUTC) {
- this.get = function (unit, date) {
- return date['getUTC' + unit]();
- };
- this.set = function (unit, date, value) {
- return date['setUTC' + unit](value);
- };
- // Local time
- } else {
- this.get = function (unit, date) {
- return date['get' + unit]();
- };
- this.set = function (unit, date, value) {
- return date['set' + unit](value);
- };
- }
- },
- /**
- * Make a time and returns milliseconds. Interprets the inputs as UTC time,
- * local time or a specific timezone time depending on the current time
- * settings.
- *
- * @function Highcharts.Time#makeTime
- *
- * @param {number} year
- * The year
- *
- * @param {number} month
- * The month. Zero-based, so January is 0.
- *
- * @param {number} [date=1]
- * The day of the month
- *
- * @param {number} [hours=0]
- * The hour of the day, 0-23.
- *
- * @param {number} [minutes=0]
- * The minutes
- *
- * @param {number} [seconds=0]
- * The seconds
- *
- * @return {number}
- * The time in milliseconds since January 1st 1970.
- */
- makeTime: function (year, month, date, hours, minutes, seconds) {
- var d, offset, newOffset;
- if (this.useUTC) {
- d = this.Date.UTC.apply(0, arguments);
- offset = this.getTimezoneOffset(d);
- d += offset;
- newOffset = this.getTimezoneOffset(d);
- if (offset !== newOffset) {
- d += newOffset - offset;
- // A special case for transitioning from summer time to winter time.
- // When the clock is set back, the same time is repeated twice, i.e.
- // 02:30 am is repeated since the clock is set back from 3 am to
- // 2 am. We need to make the same time as local Date does.
- } else if (
- offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
- !H.isSafari
- ) {
- d -= 36e5;
- }
- } else {
- d = new this.Date(
- year,
- month,
- pick(date, 1),
- pick(hours, 0),
- pick(minutes, 0),
- pick(seconds, 0)
- ).getTime();
- }
- return d;
- },
- /**
- * Sets the getTimezoneOffset function. If the `timezone` option is set, a
- * default getTimezoneOffset function with that timezone is returned. If
- * a `getTimezoneOffset` option is defined, it is returned. If neither are
- * specified, the function using the `timezoneOffset` option or 0 offset is
- * returned.
- *
- * @private
- * @function Highcharts.Time#timezoneOffsetFunction
- *
- * @return {Function}
- * A getTimezoneOffset function
- */
- timezoneOffsetFunction: function () {
- var time = this,
- options = this.options,
- moment = win.moment;
- if (!this.useUTC) {
- return function (timestamp) {
- return new Date(timestamp).getTimezoneOffset() * 60000;
- };
- }
- if (options.timezone) {
- if (!moment) {
- // getTimezoneOffset-function stays undefined because it depends
- // on Moment.js
- H.error(25);
- } else {
- return function (timestamp) {
- return -moment.tz(
- timestamp,
- options.timezone
- ).utcOffset() * 60000;
- };
- }
- }
- // If not timezone is set, look for the getTimezoneOffset callback
- if (this.useUTC && options.getTimezoneOffset) {
- return function (timestamp) {
- return options.getTimezoneOffset(timestamp) * 60000;
- };
- }
- // Last, use the `timezoneOffset` option if set
- return function () {
- return (time.timezoneOffset || 0) * 60000;
- };
- },
- /**
- * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970)
- * into a human readable date string. The format is a subset of the formats
- * for PHP's [strftime](http://www.php.net/manual/en/function.strftime.php)
- * function. Additional formats can be given in the
- * {@link Highcharts.dateFormats} hook.
- *
- * @function Highcharts.Time#dateFormat
- *
- * @param {string} [format]
- * The desired format where various time representations are
- * prefixed with %.
- *
- * @param {number} timestamp
- * The JavaScript timestamp.
- *
- * @param {boolean} [capitalize=false]
- * Upper case first letter in the return.
- *
- * @return {string}
- * The formatted date.
- */
- dateFormat: function (format, timestamp, capitalize) {
- if (!H.defined(timestamp) || isNaN(timestamp)) {
- return H.defaultOptions.lang.invalidDate || '';
- }
- format = H.pick(format, '%Y-%m-%d %H:%M:%S');
- var time = this,
- date = new this.Date(timestamp),
- // get the basic time values
- hours = this.get('Hours', date),
- day = this.get('Day', date),
- dayOfMonth = this.get('Date', date),
- month = this.get('Month', date),
- fullYear = this.get('FullYear', date),
- lang = H.defaultOptions.lang,
- langWeekdays = lang.weekdays,
- shortWeekdays = lang.shortWeekdays,
- pad = H.pad,
- // List all format keys. Custom formats can be added from the
- // outside.
- replacements = H.extend(
- {
- // Day
- // Short weekday, like 'Mon'
- 'a': shortWeekdays ?
- shortWeekdays[day] :
- langWeekdays[day].substr(0, 3),
- // Long weekday, like 'Monday'
- 'A': langWeekdays[day],
- // Two digit day of the month, 01 to 31
- 'd': pad(dayOfMonth),
- // Day of the month, 1 through 31
- 'e': pad(dayOfMonth, 2, ' '),
- 'w': day,
- // Week (none implemented)
- // 'W': weekNumber(),
- // Month
- // Short month, like 'Jan'
- 'b': lang.shortMonths[month],
- // Long month, like 'January'
- 'B': lang.months[month],
- // Two digit month number, 01 through 12
- 'm': pad(month + 1),
- // Month number, 1 through 12 (#8150)
- 'o': month + 1,
- // Year
- // Two digits year, like 09 for 2009
- 'y': fullYear.toString().substr(2, 2),
- // Four digits year, like 2009
- 'Y': fullYear,
- // Time
- // Two digits hours in 24h format, 00 through 23
- 'H': pad(hours),
- // Hours in 24h format, 0 through 23
- 'k': hours,
- // Two digits hours in 12h format, 00 through 11
- 'I': pad((hours % 12) || 12),
- // Hours in 12h format, 1 through 12
- 'l': (hours % 12) || 12,
- // Two digits minutes, 00 through 59
- 'M': pad(time.get('Minutes', date)),
- // Upper case AM or PM
- 'p': hours < 12 ? 'AM' : 'PM',
- // Lower case AM or PM
- 'P': hours < 12 ? 'am' : 'pm',
- // Two digits seconds, 00 through 59
- 'S': pad(date.getSeconds()),
- // Milliseconds (naming from Ruby)
- 'L': pad(Math.floor(timestamp % 1000), 3)
- },
- /**
- * A hook for defining additional date format specifiers. New
- * specifiers are defined as key-value pairs by using the
- * specifier as key, and a function which takes the timestamp as
- * value. This function returns the formatted portion of the
- * date.
- *
- * @sample highcharts/global/dateformats/
- * Adding support for week number
- *
- * @name Highcharts.dateFormats
- * @type {Highcharts.Dictionary<Highcharts.TimeFormatCallbackFunction>}
- */
- H.dateFormats
- );
- // Do the replaces
- H.objectEach(replacements, function (val, key) {
- // Regex would do it in one line, but this is faster
- while (format.indexOf('%' + key) !== -1) {
- format = format.replace(
- '%' + key,
- typeof val === 'function' ? val.call(time, timestamp) : val
- );
- }
- });
- // Optionally capitalize the string and return
- return capitalize ?
- format.substr(0, 1).toUpperCase() + format.substr(1) :
- format;
- },
- /**
- * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
- * an object.
- * @param {String|Array|Object} f General format description
- * @return {Object} The object definition
- */
- resolveDTLFormat: function (f) {
- if (!H.isObject(f, true)) {
- f = H.splat(f);
- return {
- main: f[0],
- from: f[1],
- to: f[2]
- };
- }
- return f;
- },
- /**
- * Return an array with time positions distributed on round time values
- * right and right after min and max. Used in datetime axes as well as for
- * grouping data on a datetime axis.
- *
- * @function Highcharts.Time#getTimeTicks
- *
- * @param {Highcharts.NormalizedIntervalObject} normalizedInterval
- * The interval in axis values (ms) and the count
- *
- * @param {number} [min]
- * The minimum in axis values
- *
- * @param {number} [max]
- * The maximum in axis values
- *
- * @param {number} [startOfWeek=1]
- *
- * @return {Highcharts.TimeTicksObject}
- */
- getTimeTicks: function (
- normalizedInterval,
- min,
- max,
- startOfWeek
- ) {
- var time = this,
- Date = time.Date,
- tickPositions = [],
- i,
- higherRanks = {},
- minYear, // used in months and years as a basis for Date.UTC()
- // When crossing DST, use the max. Resolves #6278.
- minDate = new Date(min),
- interval = normalizedInterval.unitRange,
- count = normalizedInterval.count || 1,
- variableDayLength,
- minDay;
- startOfWeek = pick(startOfWeek, 1);
- if (defined(min)) { // #1300
- time.set(
- 'Milliseconds',
- minDate,
- interval >= timeUnits.second ?
- 0 : // #3935
- count * Math.floor(
- time.get('Milliseconds', minDate) / count
- )
- ); // #3652, #3654
- if (interval >= timeUnits.second) { // second
- time.set(
- 'Seconds',
- minDate,
- interval >= timeUnits.minute ?
- 0 : // #3935
- count * Math.floor(time.get('Seconds', minDate) / count)
- );
- }
- if (interval >= timeUnits.minute) { // minute
- time.set(
- 'Minutes',
- minDate,
- interval >= timeUnits.hour ?
- 0 :
- count * Math.floor(time.get('Minutes', minDate) / count)
- );
- }
- if (interval >= timeUnits.hour) { // hour
- time.set(
- 'Hours',
- minDate,
- interval >= timeUnits.day ?
- 0 :
- count * Math.floor(
- time.get('Hours', minDate) / count
- )
- );
- }
- if (interval >= timeUnits.day) { // day
- time.set(
- 'Date',
- minDate,
- interval >= timeUnits.month ?
- 1 :
- Math.max(
- 1,
- count * Math.floor(
- time.get('Date', minDate) / count
- )
- )
- );
- }
- if (interval >= timeUnits.month) { // month
- time.set(
- 'Month',
- minDate,
- interval >= timeUnits.year ? 0 :
- count * Math.floor(time.get('Month', minDate) / count)
- );
- minYear = time.get('FullYear', minDate);
- }
- if (interval >= timeUnits.year) { // year
- minYear -= minYear % count;
- time.set('FullYear', minDate, minYear);
- }
- // week is a special case that runs outside the hierarchy
- if (interval === timeUnits.week) {
- // get start of current week, independent of count
- minDay = time.get('Day', minDate);
- time.set(
- 'Date',
- minDate,
- (
- time.get('Date', minDate) -
- minDay + startOfWeek +
- // We don't want to skip days that are before
- // startOfWeek (#7051)
- (minDay < startOfWeek ? -7 : 0)
- )
- );
- }
- // Get basics for variable time spans
- minYear = time.get('FullYear', minDate);
- var minMonth = time.get('Month', minDate),
- minDateDate = time.get('Date', minDate),
- minHours = time.get('Hours', minDate);
- // Redefine min to the floored/rounded minimum time (#7432)
- min = minDate.getTime();
- // Handle local timezone offset
- if (time.variableTimezone) {
- // Detect whether we need to take the DST crossover into
- // consideration. If we're crossing over DST, the day length may
- // be 23h or 25h and we need to compute the exact clock time for
- // each tick instead of just adding hours. This comes at a cost,
- // so first we find out if it is needed (#4951).
- variableDayLength = (
- // Long range, assume we're crossing over.
- max - min > 4 * timeUnits.month ||
- // Short range, check if min and max are in different time
- // zones.
- time.getTimezoneOffset(min) !== time.getTimezoneOffset(max)
- );
- }
- // Iterate and add tick positions at appropriate values
- var t = minDate.getTime();
- i = 1;
- while (t < max) {
- tickPositions.push(t);
- // if the interval is years, use Date.UTC to increase years
- if (interval === timeUnits.year) {
- t = time.makeTime(minYear + i * count, 0);
- // if the interval is months, use Date.UTC to increase months
- } else if (interval === timeUnits.month) {
- t = time.makeTime(minYear, minMonth + i * count);
- // if we're using global time, the interval is not fixed as it
- // jumps one hour at the DST crossover
- } else if (
- variableDayLength &&
- (interval === timeUnits.day || interval === timeUnits.week)
- ) {
- t = time.makeTime(
- minYear,
- minMonth,
- minDateDate +
- i * count * (interval === timeUnits.day ? 1 : 7)
- );
- } else if (
- variableDayLength &&
- interval === timeUnits.hour &&
- count > 1
- ) {
- // make sure higher ranks are preserved across DST (#6797,
- // #7621)
- t = time.makeTime(
- minYear,
- minMonth,
- minDateDate,
- minHours + i * count
- );
- // else, the interval is fixed and we use simple addition
- } else {
- t += interval * count;
- }
- i++;
- }
- // push the last time
- tickPositions.push(t);
- // Handle higher ranks. Mark new days if the time is on midnight
- // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
- // to prevent looping over dense data grouping (#6156).
- if (interval <= timeUnits.hour && tickPositions.length < 10000) {
- tickPositions.forEach(function (t) {
- if (
- // Speed optimization, no need to run dateFormat unless
- // we're on a full or half hour
- t % 1800000 === 0 &&
- // Check for local or global midnight
- time.dateFormat('%H%M%S%L', t) === '000000000'
- ) {
- higherRanks[t] = 'day';
- }
- });
- }
- }
- // record information on the chosen unit - for dynamic label formatter
- tickPositions.info = extend(normalizedInterval, {
- higherRanks: higherRanks,
- totalRange: interval * count
- });
- return tickPositions;
- }
- }; // end of Time
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Gets fired when a series is added to the chart after load time, using the
- * `addSeries` method. Returning `false` prevents the series from being added.
- *
- * @callback Highcharts.ChartAddSeriesCallbackFunction
- *
- * @param {Highcharts.Chart} this
- * The chart on which the event occured.
- *
- * @param {Highcharts.ChartAddSeriesEventObject} event
- * The event that occured.
- */
- /**
- * Conaints common event information. Through the `options` property you can
- * access the series options that were passed to the `addSeries` method.
- *
- * @interface Highcharts.ChartAddSeriesEventObject
- *//**
- * The series options that were passed to the `addSeries` method.
- * @name Highcharts.ChartAddSeriesEventObject#options
- * @type {Highcharts.SeriesOptionsType}
- *//**
- * Prevents the default behaviour of the event.
- * @name Highcharts.DrilldownEventObject#preventDefault
- * @type {Function}
- *//**
- * The event target.
- * @name Highcharts.DrilldownEventObject#target
- * @type {Highcharts.Chart}
- *//**
- * The event type.
- * @name Highcharts.DrilldownEventObject#type
- * @type {"drilldown"}
- */
- /**
- * Gets fired when clicking on the plot background.
- *
- * @callback Highcharts.ChartClickCallbackFunction
- *
- * @param {Highcharts.Chart} this
- * The chart on which the event occured.
- *
- * @param {Highcharts.PointerEventObject} event
- * The event that occured.
- */
- /**
- * Contains an axes of the clicked spot.
- *
- * @interface Highcharts.ChartClickEventAxisObject
- *//**
- * Axis at the clicked spot.
- * @name Highcharts.ChartClickEventAxisObject#axis
- * @type {Highcharts.Axis}
- *//**
- * Axis value at the clicked spot.
- * @name Highcharts.ChartClickEventAxisObject#value
- * @type {number}
- */
- /**
- * Contains information about the clicked spot on the chart. Remember the unit
- * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
- *
- * @interface Highcharts.ChartClickEventObject
- * @extends Highcharts.PointerEventObject
- *//**
- * Information about the x-axis on the clicked spot.
- * @name Highcharts.ChartClickEventObject#xAxis
- * @type {Array<Highcharts.ChartClickEventAxisObject>}
- *//**
- * Information about the y-axis on the clicked spot.
- * @name Highcharts.ChartClickEventObject#yAxis
- * @type {Array<Highcharts.ChartClickEventAxisObject>}
- *//**
- * Information about the z-axis on the clicked spot.
- * @name Highcharts.ChartClickEventObject#zAxis
- * @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
- */
- /**
- * Gets fired when the chart is finished loading.
- *
- * @callback Highcharts.ChartLoadCallbackFunction
- *
- * @param {Highcharts.Chart} this
- * The chart on which the event occured.
- *
- * @param {global.Event} event
- * The event that occured.
- */
- /**
- * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
- * after an axis, series or point is modified with the `redraw` option set to
- * `true`.
- *
- * @callback Highcharts.ChartRedrawCallbackFunction
- *
- * @param {Highcharts.Chart} this
- * The chart on which the event occured.
- *
- * @param {global.Event} event
- * The event that occured.
- */
- /**
- * Gets fired after initial load of the chart (directly after the `load` event),
- * and after each redraw (directly after the `redraw` event).
- *
- * @callback Highcharts.ChartRenderCallbackFunction
- *
- * @param {Highcharts.Chart} this
- * The chart on which the event occured.
- *
- * @param {global.Event} event
- * The event that occured.
- */
- /**
- * Gets fired when an area of the chart has been selected. The default action
- * for the selection event is to zoom the chart to the selected area. It can be
- * prevented by calling `event.preventDefault()` or return false.
- *
- * @callback Highcharts.ChartSelectionCallbackFunction
- *
- * @param {Highcharts.Chart} this
- * The chart on which the event occured.
- *
- * @param {global.Event} event
- * Event informations
- *
- * @return {boolean|undefined}
- * Return false to prevent the default action, usually zoom.
- */
- /**
- * @interface Highcharts.TooltipFormatterContextObject
- *//**
- * @name Highcharts.TooltipFormatterContextObject#color
- * @type {Highcharts.ColorString}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#colorIndex
- * @type {number|undefined}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#key
- * @type {number}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#percentage
- * @type {number|undefined}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#point
- * @type {Highcharts.Point}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#series
- * @type {Highcharts.Series}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#total
- * @type {number|undefined}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#x
- * @type {number}
- *//**
- * @name Highcharts.TooltipFormatterContextObject#y
- * @type {number}
- */
- var color = H.color,
- isTouchDevice = H.isTouchDevice,
- merge = H.merge,
- svg = H.svg;
- /* ****************************************************************************
- * Handle the options *
- *****************************************************************************/
- /**
- * Global default settings.
- *
- * @name Highcharts.defaultOptions
- * @type {Highcharts.Options}
- *//**
- * @optionparent
- */
- H.defaultOptions = {
- /**
- * An array containing the default colors for the chart's series. When
- * all colors are used, new colors are pulled from the start again.
- *
- * Default colors can also be set on a series or series.type basis,
- * see [column.colors](#plotOptions.column.colors),
- * [pie.colors](#plotOptions.pie.colors).
- *
- * In styled mode, the colors option doesn't exist. Instead, colors
- * are defined in CSS and applied either through series or point class
- * names, or through the [chart.colorCount](#chart.colorCount) option.
- *
- *
- * ### Legacy
- *
- * In Highcharts 3.x, the default colors were:
- *
- * <pre>colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce',
- * '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']</pre>
- *
- * In Highcharts 2.x, the default colors were:
- *
- * <pre>colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE',
- * '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92']</pre>
- *
- * @sample {highcharts} highcharts/chart/colors/
- * Assign a global color theme
- *
- * @type {Array<Highcharts.ColorString>}
- * @default ["#7cb5ec", "#434348", "#90ed7d", "#f7a35c", "#8085e9",
- * "#f15c80", "#e4d354", "#2b908f", "#f45b5b", "#91e8e1"]
- */
- colors: '#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1'.split(' '),
- /**
- * Styled mode only. Configuration object for adding SVG definitions for
- * reusable elements. See [gradients, shadows and
- * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
- * for more information and code examples.
- *
- * @type {*}
- * @since 5.0.0
- * @apioption defs
- */
- /**
- * @ignore-option
- */
- symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
- /**
- * The language object is global and it can't be set on each chart
- * initiation. Instead, use `Highcharts.setOptions` to set it before any
- * chart is initialized.
- *
- * <pre>Highcharts.setOptions({
- * lang: {
- * months: [
- * 'Janvier', 'Février', 'Mars', 'Avril',
- * 'Mai', 'Juin', 'Juillet', 'Août',
- * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
- * ],
- * weekdays: [
- * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
- * 'Jeudi', 'Vendredi', 'Samedi'
- * ]
- * }
- * });</pre>
- */
- lang: {
- /**
- * The loading text that appears when the chart is set into the loading
- * state following a call to `chart.showLoading`.
- */
- loading: 'Loading...',
- /**
- * An array containing the months names. Corresponds to the `%B` format
- * in `Highcharts.dateFormat()`.
- *
- * @type {Array<string>}
- * @default ["January", "February", "March", "April", "May", "June",
- * "July", "August", "September", "October", "November",
- * "December"]
- */
- months: [
- 'January', 'February', 'March', 'April', 'May', 'June', 'July',
- 'August', 'September', 'October', 'November', 'December'
- ],
- /**
- * An array containing the months names in abbreviated form. Corresponds
- * to the `%b` format in `Highcharts.dateFormat()`.
- *
- * @type {Array<string>}
- * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
- * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
- */
- shortMonths: [
- 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
- 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
- ],
- /**
- * An array containing the weekday names.
- *
- * @type {Array<string>}
- * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
- * "Friday", "Saturday"]
- */
- weekdays: [
- 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
- 'Thursday', 'Friday', 'Saturday'
- ],
- /**
- * Short week days, starting Sunday. If not specified, Highcharts uses
- * the first three letters of the `lang.weekdays` option.
- *
- * @sample highcharts/lang/shortweekdays/
- * Finnish two-letter abbreviations
- *
- * @type {Array<string>}
- * @since 4.2.4
- * @apioption lang.shortWeekdays
- */
- /**
- * What to show in a date field for invalid dates. Defaults to an empty
- * string.
- *
- * @type {string}
- * @since 4.1.8
- * @product highcharts highstock
- * @apioption lang.invalidDate
- */
- /**
- * The title appearing on hovering the zoom in button. The text itself
- * defaults to "+" and can be changed in the button options.
- *
- * @type {string}
- * @default Zoom in
- * @product highmaps
- * @apioption lang.zoomIn
- */
- /**
- * The title appearing on hovering the zoom out button. The text itself
- * defaults to "-" and can be changed in the button options.
- *
- * @type {string}
- * @default Zoom out
- * @product highmaps
- * @apioption lang.zoomOut
- */
- /**
- * The default decimal point used in the `Highcharts.numberFormat`
- * method unless otherwise specified in the function arguments.
- *
- * @since 1.2.2
- */
- decimalPoint: '.',
- /**
- * [Metric prefixes](http://en.wikipedia.org/wiki/Metric_prefix) used
- * to shorten high numbers in axis labels. Replacing any of the
- * positions with `null` causes the full number to be written. Setting
- * `numericSymbols` to `null` disables shortening altogether.
- *
- * @sample {highcharts} highcharts/lang/numericsymbols/
- * Replacing the symbols with text
- * @sample {highstock} highcharts/lang/numericsymbols/
- * Replacing the symbols with text
- *
- * @type {Array<string>}
- * @default ["k", "M", "G", "T", "P", "E"]
- * @since 2.3.0
- */
- numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
- /**
- * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
- * Use 10000 for Japanese, Korean and various Chinese locales, which
- * use symbols for 10^4, 10^8 and 10^12.
- *
- * @sample highcharts/lang/numericsymbolmagnitude/
- * 10000 magnitude for Japanese
- *
- * @type {number}
- * @default 1000
- * @since 5.0.3
- * @apioption lang.numericSymbolMagnitude
- */
- /**
- * The text for the label appearing when a chart is zoomed.
- *
- * @since 1.2.4
- */
- resetZoom: 'Reset zoom',
- /**
- * The tooltip title for the label appearing when a chart is zoomed.
- *
- * @since 1.2.4
- */
- resetZoomTitle: 'Reset zoom level 1:1',
- /**
- * The default thousands separator used in the `Highcharts.numberFormat`
- * method unless otherwise specified in the function arguments. Defaults
- * to a single space character, which is recommended in
- * [ISO 31-0](https://en.wikipedia.org/wiki/ISO_31-0#Numbers) and works
- * across Anglo-American and continental European languages.
- *
- * @default \u0020
- * @since 1.2.2
- */
- thousandsSep: ' '
- },
- /**
- * Global options that don't apply to each chart. These options, like
- * the `lang` options, must be set using the `Highcharts.setOptions`
- * method.
- *
- * <pre>Highcharts.setOptions({
- * global: {
- * useUTC: false
- * }
- * });</pre>
- *
- */
- /**
- * _Canvg rendering for Android 2.x is removed as of Highcharts 5.0\.
- * Use the [libURL](#exporting.libURL) option to configure exporting._
- *
- * The URL to the additional file to lazy load for Android 2.x devices.
- * These devices don't support SVG, so we download a helper file that
- * contains [canvg](http://code.google.com/p/canvg/), its dependency
- * rbcolor, and our own CanVG Renderer class. To avoid hotlinking to
- * our site, you can install canvas-tools.js on your own server and
- * change this option accordingly.
- *
- * @deprecated
- *
- * @type {string}
- * @default http://code.highcharts.com/{version}/modules/canvas-tools.js
- * @product highcharts highmaps
- * @apioption global.canvasToolsURL
- */
- /**
- * This option is deprecated since v6.0.5. Instead, use
- * [time.useUTC](#time.useUTC) that supports individual time settings
- * per chart.
- *
- * @deprecated
- *
- * @type {boolean}
- * @apioption global.useUTC
- */
- /**
- * This option is deprecated since v6.0.5. Instead, use
- * [time.Date](#time.Date) that supports individual time settings
- * per chart.
- *
- * @deprecated
- *
- * @type {Function}
- * @product highcharts highstock
- * @apioption global.Date
- */
- /**
- * This option is deprecated since v6.0.5. Instead, use
- * [time.getTimezoneOffset](#time.getTimezoneOffset) that supports
- * individual time settings per chart.
- *
- * @deprecated
- *
- * @type {Function}
- * @product highcharts highstock
- * @apioption global.getTimezoneOffset
- */
- /**
- * This option is deprecated since v6.0.5. Instead, use
- * [time.timezone](#time.timezone) that supports individual time
- * settings per chart.
- *
- * @deprecated
- *
- * @type {string}
- * @product highcharts highstock
- * @apioption global.timezone
- */
- /**
- * This option is deprecated since v6.0.5. Instead, use
- * [time.timezoneOffset](#time.timezoneOffset) that supports individual
- * time settings per chart.
- *
- * @deprecated
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption global.timezoneOffset
- */
- global: {},
- time: H.Time.prototype.defaultOptions,
- /**
- * General options for the chart.
- */
- chart: {
- /**
- * Default `mapData` for all series. If set to a string, it functions
- * as an index into the `Highcharts.maps` array. Otherwise it is
- * interpreted s map data.
- *
- * @see [mapData](#series.map.mapData)
- *
- * @type {string|Array<*>}
- * @since 5.0.0
- * @product highmaps
- * @apioption chart.map
- */
- /**
- * Set lat/lon transformation definitions for the chart. If not defined,
- * these are extracted from the map data.
- *
- * @type {*}
- * @since 5.0.0
- * @product highmaps
- * @apioption chart.mapTransforms
- */
- /**
- * When using multiple axis, the ticks of two or more opposite axes
- * will automatically be aligned by adding ticks to the axis or axes
- * with the least ticks, as if `tickAmount` were specified.
- *
- * This can be prevented by setting `alignTicks` to false. If the grid
- * lines look messy, it's a good idea to hide them for the secondary
- * axis by setting `gridLineWidth` to 0.
- *
- * If `startOnTick` or `endOnTick` in an Axis options are set to false,
- * then the `alignTicks ` will be disabled for the Axis.
- *
- * Disabled for logarithmic axes.
- *
- * @sample {highcharts} highcharts/chart/alignticks-true/
- * True by default
- * @sample {highcharts} highcharts/chart/alignticks-false/
- * False
- * @sample {highstock} stock/chart/alignticks-true/
- * True by default
- * @sample {highstock} stock/chart/alignticks-false/
- * False
- *
- * @type {boolean}
- * @default true
- * @product highcharts highstock gantt
- * @apioption chart.alignTicks
- */
- /**
- * Set the overall animation for all chart updating. Animation can be
- * disabled throughout the chart by setting it to false here. It can
- * be overridden for each individual API method as a function parameter.
- * The only animation not affected by this option is the initial series
- * animation, see [plotOptions.series.animation](
- * #plotOptions.series.animation).
- *
- * The animation can either be set as a boolean or a configuration
- * object. If `true`, it will use the 'swing' jQuery easing and a
- * duration of 500 ms. If used as a configuration object, the following
- * properties are supported:
- *
- * <dl>
- *
- * <dt>duration</dt>
- *
- * <dd>The duration of the animation in milliseconds.</dd>
- *
- * <dt>easing</dt>
- *
- * <dd>A string reference to an easing function set on the `Math`
- * object. See [the easing
- * demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
- * </dd>
- *
- * </dl>
- *
- * @sample {highcharts} highcharts/chart/animation-none/
- * Updating with no animation
- * @sample {highcharts} highcharts/chart/animation-duration/
- * With a longer duration
- * @sample {highcharts} highcharts/chart/animation-easing/
- * With a jQuery UI easing
- * @sample {highmaps} maps/chart/animation-none/
- * Updating with no animation
- * @sample {highmaps} maps/chart/animation-duration/
- * With a longer duration
- *
- * @type {boolean|Highcharts.AnimationOptionsObject}
- * @default true
- * @apioption chart.animation
- */
- /**
- * A CSS class name to apply to the charts container `div`, allowing
- * unique CSS styling for each chart.
- *
- * @type {string}
- * @apioption chart.className
- */
- /**
- * Event listeners for the chart.
- *
- * @apioption chart.events
- */
- /**
- * Fires when a series is added to the chart after load time, using the
- * `addSeries` method. One parameter, `event`, is passed to the
- * function, containing common event information. Through
- * `event.options` you can access the series options that were passed to
- * the `addSeries` method. Returning false prevents the series from
- * being added.
- *
- * @sample {highcharts} highcharts/chart/events-addseries/
- * Alert on add series
- * @sample {highstock} stock/chart/events-addseries/
- * Alert on add series
- *
- * @type {Highcharts.ChartAddSeriesCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Chart
- * @apioption chart.events.addSeries
- */
- /**
- * Fires when clicking on the plot background. One parameter, `event`,
- * is passed to the function, containing common event information.
- *
- * Information on the clicked spot can be found through `event.xAxis`
- * and `event.yAxis`, which are arrays containing the axes of each
- * dimension and each axis' value at the clicked spot. The primary axes
- * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
- * datetime axis is milliseconds since 1970-01-01 00:00:00.
- *
- * <pre>click: function(e) {
- * console.log(
- * Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
- * e.yAxis[0].value
- * )
- * }</pre>
- *
- * @sample {highcharts} highcharts/chart/events-click/
- * Alert coordinates on click
- * @sample {highcharts} highcharts/chart/events-container/
- * Alternatively, attach event to container
- * @sample {highstock} stock/chart/events-click/
- * Alert coordinates on click
- * @sample {highstock} highcharts/chart/events-container/
- * Alternatively, attach event to container
- * @sample {highmaps} maps/chart/events-click/
- * Record coordinates on click
- * @sample {highmaps} highcharts/chart/events-container/
- * Alternatively, attach event to container
- *
- * @type {Highcharts.ChartClickCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Chart
- * @apioption chart.events.click
- */
- /**
- * Fires when the chart is finished loading. Since v4.2.2, it also waits
- * for images to be loaded, for example from point markers. One
- * parameter, `event`, is passed to the function, containing common
- * event information.
- *
- * There is also a second parameter to the chart constructor where a
- * callback function can be passed to be executed on chart.load.
- *
- * @sample {highcharts} highcharts/chart/events-load/
- * Alert on chart load
- * @sample {highstock} stock/chart/events-load/
- * Alert on chart load
- * @sample {highmaps} maps/chart/events-load/
- * Add series on chart load
- *
- * @type {Highcharts.ChartLoadCallbackFunction}
- * @context Highcharts.Chart
- * @apioption chart.events.load
- */
- /**
- * Fires when the chart is redrawn, either after a call to
- * `chart.redraw()` or after an axis, series or point is modified with
- * the `redraw` option set to `true`. One parameter, `event`, is passed
- * to the function, containing common event information.
- *
- * @sample {highcharts} highcharts/chart/events-redraw/
- * Alert on chart redraw
- * @sample {highstock} stock/chart/events-redraw/
- * Alert on chart redraw when adding a series or moving the
- * zoomed range
- * @sample {highmaps} maps/chart/events-redraw/
- * Set subtitle on chart redraw
- *
- * @type {Highcharts.ChartRedrawCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Chart
- * @apioption chart.events.redraw
- */
- /**
- * Fires after initial load of the chart (directly after the `load`
- * event), and after each redraw (directly after the `redraw` event).
- *
- * @type {Highcharts.ChartRenderCallbackFunction}
- * @since 5.0.7
- * @context Highcharts.Chart
- * @apioption chart.events.render
- */
- /**
- * Fires when an area of the chart has been selected. Selection is
- * enabled by setting the chart's zoomType. One parameter, `event`, is
- * passed to the function, containing common event information. The
- * default action for the selection event is to zoom the chart to the
- * selected area. It can be prevented by calling
- * `event.preventDefault()` or return false.
- *
- * Information on the selected area can be found through `event.xAxis`
- * and `event.yAxis`, which are arrays containing the axes of each
- * dimension and each axis' min and max values. The primary axes are
- * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
- * datetime axis is milliseconds since 1970-01-01 00:00:00.
- *
- * <pre>selection: function(event) {
- * // log the min and max of the primary, datetime x-axis
- * console.log(
- * Highcharts.dateFormat(
- * '%Y-%m-%d %H:%M:%S',
- * event.xAxis[0].min
- * ),
- * Highcharts.dateFormat(
- * '%Y-%m-%d %H:%M:%S',
- * event.xAxis[0].max
- * )
- * );
- * // log the min and max of the y axis
- * console.log(event.yAxis[0].min, event.yAxis[0].max);
- * }</pre>
- *
- * @sample {highcharts} highcharts/chart/events-selection/
- * Report on selection and reset
- * @sample {highcharts} highcharts/chart/events-selection-points/
- * Select a range of points through a drag selection
- * @sample {highstock} stock/chart/events-selection/
- * Report on selection and reset
- * @sample {highstock} highcharts/chart/events-selection-points/
- * Select a range of points through a drag selection
- * (Highcharts)
- *
- * @type {Highcharts.ChartSelectionCallbackFunction}
- * @apioption chart.events.selection
- */
- /**
- * The margin between the outer edge of the chart and the plot area.
- * The numbers in the array designate top, right, bottom and left
- * respectively. Use the options `marginTop`, `marginRight`,
- * `marginBottom` and `marginLeft` for shorthand setting of one option.
- *
- * By default there is no margin. The actual space is dynamically
- * calculated from the offset of axis labels, axis title, title,
- * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
- * `spacingBottom` and `spacingLeft` options.
- *
- * @sample {highcharts} highcharts/chart/margins-zero/
- * Zero margins
- * @sample {highstock} stock/chart/margin-zero/
- * Zero margins
- *
- * @type {number|Array<number>}
- * @apioption chart.margin
- */
- /**
- * The margin between the bottom outer edge of the chart and the plot
- * area. Use this to set a fixed pixel value for the margin as opposed
- * to the default dynamic margin. See also `spacingBottom`.
- *
- * @sample {highcharts} highcharts/chart/marginbottom/
- * 100px bottom margin
- * @sample {highstock} stock/chart/marginbottom/
- * 100px bottom margin
- * @sample {highmaps} maps/chart/margin/
- * 100px margins
- *
- * @type {number}
- * @since 2.0
- * @apioption chart.marginBottom
- */
- /**
- * The margin between the left outer edge of the chart and the plot
- * area. Use this to set a fixed pixel value for the margin as opposed
- * to the default dynamic margin. See also `spacingLeft`.
- *
- * @sample {highcharts} highcharts/chart/marginleft/
- * 150px left margin
- * @sample {highstock} stock/chart/marginleft/
- * 150px left margin
- * @sample {highmaps} maps/chart/margin/
- * 100px margins
- *
- * @type {number}
- * @since 2.0
- * @apioption chart.marginLeft
- */
- /**
- * The margin between the right outer edge of the chart and the plot
- * area. Use this to set a fixed pixel value for the margin as opposed
- * to the default dynamic margin. See also `spacingRight`.
- *
- * @sample {highcharts} highcharts/chart/marginright/
- * 100px right margin
- * @sample {highstock} stock/chart/marginright/
- * 100px right margin
- * @sample {highmaps} maps/chart/margin/
- * 100px margins
- *
- * @type {number}
- * @since 2.0
- * @apioption chart.marginRight
- */
- /**
- * The margin between the top outer edge of the chart and the plot area.
- * Use this to set a fixed pixel value for the margin as opposed to
- * the default dynamic margin. See also `spacingTop`.
- *
- * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
- * @sample {highstock} stock/chart/margintop/
- * 100px top margin
- * @sample {highmaps} maps/chart/margin/
- * 100px margins
- *
- * @type {number}
- * @since 2.0
- * @apioption chart.marginTop
- */
- /**
- * Allows setting a key to switch between zooming and panning. Can be
- * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
- * key on Windows) or `shift`. The keys are mapped directly to the key
- * properties of the click event argument (`event.altKey`,
- * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
- *
- * @type {string}
- * @since 4.0.3
- * @product highcharts gantt
- * @validvalue ["alt", "ctrl", "meta", "shift"]
- * @apioption chart.panKey
- */
- /**
- * Allow panning in a chart. Best used with [panKey](#chart.panKey)
- * to combine zooming and panning.
- *
- * On touch devices, when the [tooltip.followTouchMove](
- * #tooltip.followTouchMove) option is `true` (default), panning
- * requires two fingers. To allow panning with one finger, set
- * `followTouchMove` to `false`.
- *
- * @sample {highcharts} highcharts/chart/pankey/ Zooming and panning
- *
- * @type {boolean}
- * @default {highcharts} false
- * @default {highstock} true
- * @since 4.0.3
- * @product highcharts highstock gantt
- * @apioption chart.panning
- */
- /**
- * Equivalent to [zoomType](#chart.zoomType), but for multitouch
- * gestures only. By default, the `pinchType` is the same as the
- * `zoomType` setting. However, pinching can be enabled separately in
- * some cases, for example in stock charts where a mouse drag pans the
- * chart, while pinching is enabled. When [tooltip.followTouchMove](
- * #tooltip.followTouchMove) is true, pinchType only applies to
- * two-finger touches.
- *
- * @type {string}
- * @default {highcharts} undefined
- * @default {highstock} x
- * @since 3.0
- * @product highcharts highstock gantt
- * @validvalue ["x", "y", "xy"]
- * @apioption chart.pinchType
- */
- /**
- * Whether to apply styled mode. When in styled mode, no presentational
- * attributes or CSS are applied to the chart SVG. Instead, CSS rules
- * are required to style the chart. The default style sheet is
- * available from `https://code.highcharts.com/css/highcharts.css`.
- *
- * @type {boolean}
- * @default false
- * @since 7.0
- * @apioption chart.styledMode
- */
- styledMode: false,
- /**
- * The corner radius of the outer chart border.
- *
- * @sample {highcharts} highcharts/chart/borderradius/
- * 20px radius
- * @sample {highstock} stock/chart/border/
- * 10px radius
- * @sample {highmaps} maps/chart/border/
- * Border options
- *
- */
- borderRadius: 0,
- /**
- * In styled mode, this sets how many colors the class names
- * should rotate between. With ten colors, series (or points) are
- * given class names like `highcharts-color-0`, `highcharts-color-0`
- * [...] `highcharts-color-9`. The equivalent in non-styled mode
- * is to set colors using the [colors](#colors) setting.
- *
- * @since 5.0.0
- */
- colorCount: 10,
- /**
- * Alias of `type`.
- *
- * @sample {highcharts} highcharts/chart/defaultseriestype/
- * Bar
- *
- * @deprecated
- *
- * @product highcharts
- */
- defaultSeriesType: 'line',
- /**
- * If true, the axes will scale to the remaining visible series once
- * one series is hidden. If false, hiding and showing a series will
- * not affect the axes or the other series. For stacks, once one series
- * within the stack is hidden, the rest of the stack will close in
- * around it even if the axis is not affected.
- *
- * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
- * True by default
- * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
- * False
- * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
- * True with stack
- * @sample {highstock} stock/chart/ignorehiddenseries-true/
- * True by default
- * @sample {highstock} stock/chart/ignorehiddenseries-false/
- * False
- *
- * @since 1.2.0
- * @product highcharts highstock gantt
- */
- ignoreHiddenSeries: true,
- /**
- * Whether to invert the axes so that the x axis is vertical and y axis
- * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
- * by default.
- *
- * @productdesc {highcharts}
- * If a bar series is present in the chart, it will be inverted
- * automatically. Inverting the chart doesn't have an effect if there
- * are no cartesian series in the chart, or if the chart is
- * [polar](#chart.polar).
- *
- * @sample {highcharts} highcharts/chart/inverted/
- * Inverted line
- * @sample {highstock} stock/navigator/inverted/
- * Inverted stock chart
- *
- * @type {boolean}
- * @default false
- * @product highcharts highstock gantt
- * @apioption chart.inverted
- */
- /**
- * The distance between the outer edge of the chart and the content,
- * like title or legend, or axis title and labels if present. The
- * numbers in the array designate top, right, bottom and left
- * respectively. Use the options spacingTop, spacingRight, spacingBottom
- * and spacingLeft options for shorthand setting of one option.
- *
- * @type {Array<number>}
- * @see [chart.margin](#chart.margin)
- * @default [10, 10, 15, 10]
- * @since 3.0.6
- */
- spacing: [10, 10, 15, 10],
- /**
- * The button that appears after a selection zoom, allowing the user
- * to reset zoom.
- */
- resetZoomButton: {
- /**
- * What frame the button should be placed related to. Can be either
- * `plot` or `chart`
- *
- * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
- * Relative to the chart
- * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
- * Relative to the chart
- *
- * @type {string}
- * @default plot
- * @since 2.2
- * @validvalue ["plot", "chart"]
- * @apioption chart.resetZoomButton.relativeTo
- */
- /**
- * A collection of attributes for the button. The object takes SVG
- * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
- * border radius. The theme also supports `style`, a collection of
- * CSS properties for the text. Equivalent attributes for the hover
- * state are given in `theme.states.hover`.
- *
- * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
- * Theming the button
- * @sample {highstock} highcharts/chart/resetzoombutton-theme/
- * Theming the button
- *
- * @since 2.2
- */
- theme: {
- /**
- * The Z index for the reset zoom button. The default value
- * places it below the tooltip that has Z index 7.
- */
- zIndex: 6
- },
- /**
- * The position of the button.
- *
- * @sample {highcharts} highcharts/chart/resetzoombutton-position/
- * Above the plot area
- * @sample {highstock} highcharts/chart/resetzoombutton-position/
- * Above the plot area
- * @sample {highmaps} highcharts/chart/resetzoombutton-position/
- * Above the plot area
- *
- * @type {Highcharts.AlignObject}
- * @since 2.2
- */
- position: {
- /**
- * The horizontal alignment of the button.
- */
- align: 'right',
- /**
- * The horizontal offset of the button.
- */
- x: -10,
- /**
- * The vertical alignment of the button.
- *
- * @type {Highcharts.VerticalAlignType}
- * @default top
- * @apioption chart.resetZoomButton.position.verticalAlign
- */
- /**
- * The vertical offset of the button.
- */
- y: 10
- }
- },
- /**
- * The pixel width of the plot area border.
- *
- * @sample {highcharts} highcharts/chart/plotborderwidth/
- * 1px border
- * @sample {highstock} stock/chart/plotborder/
- * 2px border
- * @sample {highmaps} maps/chart/plotborder/
- * Plot border options
- *
- * @type {number}
- * @default 0
- * @apioption chart.plotBorderWidth
- */
- /**
- * Whether to apply a drop shadow to the plot area. Requires that
- * plotBackgroundColor be set. The shadow can be an object configuration
- * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
- *
- * @sample {highcharts} highcharts/chart/plotshadow/
- * Plot shadow
- * @sample {highstock} stock/chart/plotshadow/
- * Plot shadow
- * @sample {highmaps} maps/chart/plotborder/
- * Plot border options
- *
- * @type {boolean|Highcharts.CSSObject}
- * @default false
- * @apioption chart.plotShadow
- */
- /**
- * When true, cartesian charts like line, spline, area and column are
- * transformed into the polar coordinate system. This produces _polar
- * charts_, also known as _radar charts_. Requires
- * `highcharts-more.js`.
- *
- * @sample {highcharts} highcharts/demo/polar/
- * Polar chart
- * @sample {highcharts} highcharts/demo/polar-wind-rose/
- * Wind rose, stacked polar column chart
- * @sample {highcharts} highcharts/demo/polar-spider/
- * Spider web chart
- * @sample {highcharts} highcharts/parallel-coordinates/polar/
- * Star plot, multivariate data in a polar chart
- *
- * @type {boolean}
- * @default false
- * @since 2.3.0
- * @product highcharts
- * @apioption chart.polar
- */
- /**
- * Whether to reflow the chart to fit the width of the container div
- * on resizing the window.
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * True by default
- * @sample {highcharts} highcharts/chart/reflow-false/
- * False
- * @sample {highstock} stock/chart/reflow-true/
- * True by default
- * @sample {highstock} stock/chart/reflow-false/
- * False
- * @sample {highmaps} maps/chart/reflow-true/
- * True by default
- * @sample {highmaps} maps/chart/reflow-false/
- * False
- *
- * @type {boolean}
- * @default true
- * @since 2.1
- * @apioption chart.reflow
- */
- /**
- * The HTML element where the chart will be rendered. If it is a string,
- * the element by that id is used. The HTML element can also be passed
- * by direct reference, or as the first argument of the chart
- * constructor, in which case the option is not needed.
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * String
- * @sample {highcharts} highcharts/chart/renderto-object/
- * Object reference
- * @sample {highcharts} highcharts/chart/renderto-jquery/
- * Object reference through jQuery
- * @sample {highstock} stock/chart/renderto-string/
- * String
- * @sample {highstock} stock/chart/renderto-object/
- * Object reference
- * @sample {highstock} stock/chart/renderto-jquery/
- * Object reference through jQuery
- *
- * @type {string|Highcharts.HTMLDOMElement}
- * @apioption chart.renderTo
- */
- /**
- * The background color of the marker square when selecting (zooming
- * in on) an area of the chart.
- *
- * @see In styled mode, the selection marker fill is set with the
- * `.highcharts-selection-marker` class.
- *
- * @type {Highcharts.ColorString}
- * @default rgba(51,92,173,0.25)
- * @since 2.1.7
- * @apioption chart.selectionMarkerFill
- */
- /**
- * Whether to apply a drop shadow to the outer chart area. Requires
- * that backgroundColor be set. The shadow can be an object
- * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
- * `width`.
- *
- * @sample {highcharts} highcharts/chart/shadow/
- * Shadow
- * @sample {highstock} stock/chart/shadow/
- * Shadow
- * @sample {highmaps} maps/chart/border/
- * Chart border and shadow
- *
- * @type {boolean|Highcharts.CSSObject}
- * @default false
- * @apioption chart.shadow
- */
- /**
- * Whether to show the axes initially. This only applies to empty charts
- * where series are added dynamically, as axes are automatically added
- * to cartesian series.
- *
- * @sample {highcharts} highcharts/chart/showaxes-false/
- * False by default
- * @sample {highcharts} highcharts/chart/showaxes-true/
- * True
- *
- * @type {boolean}
- * @since 1.2.5
- * @product highcharts gantt
- * @apioption chart.showAxes
- */
- /**
- * The space between the bottom edge of the chart and the content (plot
- * area, axis title and labels, title, subtitle or legend in top
- * position).
- *
- * @sample {highcharts} highcharts/chart/spacingbottom/
- * Spacing bottom set to 100
- * @sample {highstock} stock/chart/spacingbottom/
- * Spacing bottom set to 100
- * @sample {highmaps} maps/chart/spacing/
- * Spacing 100 all around
- *
- * @type {number}
- * @default 15
- * @since 2.1
- * @apioption chart.spacingBottom
- */
- /**
- * The space between the left edge of the chart and the content (plot
- * area, axis title and labels, title, subtitle or legend in top
- * position).
- *
- * @sample {highcharts} highcharts/chart/spacingleft/
- * Spacing left set to 100
- * @sample {highstock} stock/chart/spacingleft/
- * Spacing left set to 100
- * @sample {highmaps} maps/chart/spacing/
- * Spacing 100 all around
- *
- * @type {number}
- * @default 10
- * @since 2.1
- * @apioption chart.spacingLeft
- */
- /**
- * The space between the right edge of the chart and the content (plot
- * area, axis title and labels, title, subtitle or legend in top
- * position).
- *
- * @sample {highcharts} highcharts/chart/spacingright-100/
- * Spacing set to 100
- * @sample {highcharts} highcharts/chart/spacingright-legend/
- * Legend in right position with default spacing
- * @sample {highstock} stock/chart/spacingright/
- * Spacing set to 100
- * @sample {highmaps} maps/chart/spacing/
- * Spacing 100 all around
- *
- * @type {number}
- * @default 10
- * @since 2.1
- * @apioption chart.spacingRight
- */
- /**
- * The space between the top edge of the chart and the content (plot
- * area, axis title and labels, title, subtitle or legend in top
- * position).
- *
- * @sample {highcharts} highcharts/chart/spacingtop-100/
- * A top spacing of 100
- * @sample {highcharts} highcharts/chart/spacingtop-10/
- * Floating chart title makes the plot area align to the default
- * spacingTop of 10.
- * @sample {highstock} stock/chart/spacingtop/
- * A top spacing of 100
- * @sample {highmaps} maps/chart/spacing/
- * Spacing 100 all around
- *
- * @type {number}
- * @default 10
- * @since 2.1
- * @apioption chart.spacingTop
- */
- /**
- * Additional CSS styles to apply inline to the container `div`. Note
- * that since the default font styles are applied in the renderer, it
- * is ignorant of the individual chart options and must be set globally.
- *
- * @see In styled mode, general chart styles can be set with the
- * `.highcharts-root` class.
- * @sample {highcharts} highcharts/chart/style-serif-font/
- * Using a serif type font
- * @sample {highcharts} highcharts/css/em/
- * Styled mode with relative font sizes
- * @sample {highstock} stock/chart/style/
- * Using a serif type font
- * @sample {highmaps} maps/chart/style-serif-font/
- * Using a serif type font
- *
- * @type {Highcharts.CSSObject}
- * @default {"fontFamily": "\"Lucida Grande\", \"Lucida Sans Unicode\", Verdana, Arial, Helvetica, sans-serif","fontSize":"12px"}
- * @apioption chart.style
- */
- /**
- * The default series type for the chart. Can be any of the chart types
- * listed under [plotOptions](#plotOptions).
- *
- * In TypeScript instead the `type` option must always be set in the
- * series.
- *
- * @sample {highcharts} highcharts/chart/type-bar/
- * Bar
- * @sample {highstock} stock/chart/type/
- * Areaspline
- * @sample {highmaps} maps/chart/type-mapline/
- * Mapline
- *
- * @type {string}
- * @default {highcharts} line
- * @default {highstock} line
- * @default {highmaps} map
- * @since 2.1.0
- * @validvalue ["line", "spline", "column", "bar", "area", "areaspline",
- * "pie", "arearange", "areasplinerange", "boxplot",
- * "bubble", "columnrange", "errorbar", "funnel", "gauge",
- * "heatmap", "polygon", "pyramid", "scatter", "solidgauge",
- * "treemap", "waterfall"]
- * @apioption chart.type
- */
- /**
- * Decides in what dimensions the user can zoom by dragging the mouse.
- * Can be one of `x`, `y` or `xy`.
- *
- * @see [panKey](#chart.panKey)
- *
- * @sample {highcharts} highcharts/chart/zoomtype-none/
- * None by default
- * @sample {highcharts} highcharts/chart/zoomtype-x/
- * X
- * @sample {highcharts} highcharts/chart/zoomtype-y/
- * Y
- * @sample {highcharts} highcharts/chart/zoomtype-xy/
- * Xy
- * @sample {highstock} stock/demo/basic-line/
- * None by default
- * @sample {highstock} stock/chart/zoomtype-x/
- * X
- * @sample {highstock} stock/chart/zoomtype-y/
- * Y
- * @sample {highstock} stock/chart/zoomtype-xy/
- * Xy
- *
- * @type {string}
- * @product highcharts highstock gantt
- * @validvalue ["x", "y", "xy"]
- * @apioption chart.zoomType
- */
- /**
- * An explicit width for the chart. By default (when `null`) the width
- * is calculated from the offset width of the containing element.
- *
- * @sample {highcharts} highcharts/chart/width/
- * 800px wide
- * @sample {highstock} stock/chart/width/
- * 800px wide
- * @sample {highmaps} maps/chart/size/
- * Chart with explicit size
- *
- * @type {number|null}
- */
- width: null,
- /**
- * An explicit height for the chart. If a _number_, the height is
- * given in pixels. If given a _percentage string_ (for example
- * `'56%'`), the height is given as the percentage of the actual chart
- * width. This allows for preserving the aspect ratio across responsive
- * sizes.
- *
- * By default (when `null`) the height is calculated from the offset
- * height of the containing element, or 400 pixels if the containing
- * element's height is 0.
- *
- * @sample {highcharts} highcharts/chart/height/
- * 500px height
- * @sample {highstock} stock/chart/height/
- * 300px height
- * @sample {highmaps} maps/chart/size/
- * Chart with explicit size
- * @sample highcharts/chart/height-percent/
- * Highcharts with percentage height
- *
- * @type {number|string|null}
- */
- height: null,
- /**
- * The color of the outer chart border.
- *
- * @see In styled mode, the stroke is set with the
- * `.highcharts-background` class.
- *
- * @sample {highcharts} highcharts/chart/bordercolor/
- * Brown border
- * @sample {highstock} stock/chart/border/
- * Brown border
- * @sample {highmaps} maps/chart/border/
- * Border options
- *
- * @type {Highcharts.ColorString}
- */
- borderColor: '#335cad',
- /**
- * The pixel width of the outer chart border.
- *
- * @see In styled mode, the stroke is set with the
- * `.highcharts-background` class.
- *
- * @sample {highcharts} highcharts/chart/borderwidth/
- * 5px border
- * @sample {highstock} stock/chart/border/
- * 2px border
- * @sample {highmaps} maps/chart/border/
- * Border options
- *
- * @type {number}
- * @default 0
- * @apioption chart.borderWidth
- */
- /**
- * The background color or gradient for the outer chart area.
- *
- * @see In styled mode, the background is set with the
- * `.highcharts-background` class.
- *
- * @sample {highcharts} highcharts/chart/backgroundcolor-color/
- * Color
- * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
- * Gradient
- * @sample {highstock} stock/chart/backgroundcolor-color/
- * Color
- * @sample {highstock} stock/chart/backgroundcolor-gradient/
- * Gradient
- * @sample {highmaps} maps/chart/backgroundcolor-color/
- * Color
- * @sample {highmaps} maps/chart/backgroundcolor-gradient/
- * Gradient
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- */
- backgroundColor: '#ffffff',
- /**
- * The background color or gradient for the plot area.
- *
- * @see In styled mode, the plot background is set with the
- * `.highcharts-plot-background` class.
- *
- * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
- * Color
- * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
- * Gradient
- * @sample {highstock} stock/chart/plotbackgroundcolor-color/
- * Color
- * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
- * Gradient
- * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
- * Color
- * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
- * Gradient
- *
- * @type {Highcharts.ColorString}
- * @apioption chart.plotBackgroundColor
- */
- /**
- * The URL for an image to use as the plot background. To set an image
- * as the background for the entire chart, set a CSS background image
- * to the container element. Note that for the image to be applied to
- * exported charts, its URL needs to be accessible by the export server.
- *
- * @see In styled mode, a plot background image can be set with the
- * `.highcharts-plot-background` class and a [custom pattern](
- * https://www.highcharts.com/docs/chart-design-and-style/
- * gradients-shadows-and-patterns).
- *
- * @sample {highcharts} highcharts/chart/plotbackgroundimage/
- * Skies
- * @sample {highstock} stock/chart/plotbackgroundimage/
- * Skies
- *
- * @type {string}
- * @apioption chart.plotBackgroundImage
- */
- /**
- * The color of the inner chart or plot area border.
- *
- * @see In styled mode, a plot border stroke can be set with the
- * `.highcharts-plot-border` class.
- *
- * @sample {highcharts} highcharts/chart/plotbordercolor/
- * Blue border
- * @sample {highstock} stock/chart/plotborder/
- * Blue border
- * @sample {highmaps} maps/chart/plotborder/
- * Plot border options
- *
- * @type {Highcharts.ColorString}
- */
- plotBorderColor: '#cccccc'
- },
- /**
- * The chart's main title.
- *
- * @sample {highmaps} maps/title/title/
- * Title options demonstrated
- */
- title: {
- /**
- * When the title is floating, the plot area will not move to make space
- * for it.
- *
- * @sample {highcharts} highcharts/chart/zoomtype-none/
- * False by default
- * @sample {highcharts} highcharts/title/floating/
- * True - title on top of the plot area
- * @sample {highstock} stock/chart/title-floating/
- * True - title on top of the plot area
- *
- * @type {boolean}
- * @default false
- * @since 2.1
- * @apioption title.floating
- */
- /**
- * CSS styles for the title. Use this for font styling, but use `align`,
- * `x` and `y` for text alignment.
- *
- * In styled mode, the title style is given in the `.highcharts-title`
- * class.
- *
- * @sample {highcharts} highcharts/title/style/
- * Custom color and weight
- * @sample {highstock} stock/chart/title-style/
- * Custom color and weight
- * @sample highcharts/css/titles/
- * Styled mode
- *
- * @type {Highcharts.CSSObject}
- * @default {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
- * @default {highstock} { "color": "#333333", "fontSize": "16px" }
- * @apioption title.style
- */
- /**
- * Whether to
- * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the text.
- *
- * @type {boolean}
- * @default false
- * @apioption title.useHTML
- */
- /**
- * The vertical alignment of the title. Can be one of `"top"`,
- * `"middle"` and `"bottom"`. When a value is given, the title behaves
- * as if [floating](#title.floating) were `true`.
- *
- * @sample {highcharts} highcharts/title/verticalalign/
- * Chart title in bottom right corner
- * @sample {highstock} stock/chart/title-verticalalign/
- * Chart title in bottom right corner
- *
- * @type {Highcharts.VerticalAlignType}
- * @since 2.1
- * @apioption title.verticalAlign
- */
- /**
- * The x position of the title relative to the alignment within
- * `chart.spacingLeft` and `chart.spacingRight`.
- *
- * @sample {highcharts} highcharts/title/align/
- * Aligned to the plot area (x = 70px = margin left - spacing
- * left)
- * @sample {highstock} stock/chart/title-align/
- * Aligned to the plot area (x = 50px = margin left - spacing
- * left)
- *
- * @type {number}
- * @default 0
- * @since 2.0
- * @apioption title.x
- */
- /**
- * The y position of the title relative to the alignment within
- * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
- * #chart.spacingBottom). By default it depends on the font size.
- *
- * @sample {highcharts} highcharts/title/y/
- * Title inside the plot area
- * @sample {highstock} stock/chart/title-verticalalign/
- * Chart title in bottom right corner
- *
- * @type {number}
- * @since 2.0
- * @apioption title.y
- */
- /**
- * The title of the chart. To disable the title, set the `text` to
- * `undefined`.
- *
- * @sample {highcharts} highcharts/title/text/
- * Custom title
- * @sample {highstock} stock/chart/title-text/
- * Custom title
- *
- * @default {highcharts|highmaps} Chart title
- * @default {highstock} undefined
- */
- text: 'Chart title',
- /**
- * The horizontal alignment of the title. Can be one of "left", "center"
- * and "right".
- *
- * @sample {highcharts} highcharts/title/align/
- * Aligned to the plot area (x = 70px = margin left - spacing
- * left)
- * @sample {highstock} stock/chart/title-align/
- * Aligned to the plot area (x = 50px = margin left - spacing
- * left)
- *
- * @type {Highcharts.AlignType}
- * @since 2.0
- */
- align: 'center',
- /**
- * The margin between the title and the plot area, or if a subtitle
- * is present, the margin between the subtitle and the plot area.
- *
- * @sample {highcharts} highcharts/title/margin-50/
- * A chart title margin of 50
- * @sample {highcharts} highcharts/title/margin-subtitle/
- * The same margin applied with a subtitle
- * @sample {highstock} stock/chart/title-margin/
- * A chart title margin of 50
- *
- * @since 2.1
- */
- margin: 15,
- /**
- * Adjustment made to the title width, normally to reserve space for
- * the exporting burger menu.
- *
- * @sample highcharts/title/widthadjust/
- * Wider menu, greater padding
- *
- * @since 4.2.5
- */
- widthAdjust: -44
- },
- /**
- * The chart's subtitle. This can be used both to display a subtitle below
- * the main title, and to display random text anywhere in the chart. The
- * subtitle can be updated after chart initialization through the
- * `Chart.setTitle` method.
- *
- * @sample {highmaps} maps/title/subtitle/
- * Subtitle options demonstrated
- */
- subtitle: {
- /**
- * When the subtitle is floating, the plot area will not move to make
- * space for it.
- *
- * @sample {highcharts} highcharts/subtitle/floating/
- * Floating title and subtitle
- * @sample {highstock} stock/chart/subtitle-footnote
- * Footnote floating at bottom right of plot area
- *
- * @type {boolean}
- * @default false
- * @since 2.1
- * @apioption subtitle.floating
- */
- /**
- * CSS styles for the title.
- *
- * In styled mode, the subtitle style is given in the
- * `.highcharts-subtitle` class.
- *
- * @sample {highcharts} highcharts/subtitle/style/
- * Custom color and weight
- * @sample {highcharts} highcharts/css/titles/
- * Styled mode
- * @sample {highstock} stock/chart/subtitle-style
- * Custom color and weight
- * @sample {highstock} highcharts/css/titles/
- * Styled mode
- * @sample {highmaps} highcharts/css/titles/
- * Styled mode
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#666666"}
- * @apioption subtitle.style
- */
- /**
- * Whether to
- * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the text.
- *
- * @type {boolean}
- * @default false
- * @apioption subtitle.useHTML
- */
- /**
- * The vertical alignment of the title. Can be one of "top", "middle"
- * and "bottom". When a value is given, the title behaves as floating.
- *
- * @sample {highcharts} highcharts/subtitle/verticalalign/
- * Footnote at the bottom right of plot area
- * @sample {highstock} stock/chart/subtitle-footnote
- * Footnote at the bottom right of plot area
- *
- * @type {Highcharts.VerticalAlignType}
- * @since 2.1
- * @apioption subtitle.verticalAlign
- */
- /**
- * The x position of the subtitle relative to the alignment within
- * `chart.spacingLeft` and `chart.spacingRight`.
- *
- * @sample {highcharts} highcharts/subtitle/align/
- * Footnote at right of plot area
- * @sample {highstock} stock/chart/subtitle-footnote
- * Footnote at the bottom right of plot area
- *
- * @type {number}
- * @default 0
- * @since 2.0
- * @apioption subtitle.x
- */
- /**
- * The y position of the subtitle relative to the alignment within
- * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
- * is laid out below the title unless the title is floating.
- *
- * @sample {highcharts} highcharts/subtitle/verticalalign/
- * Footnote at the bottom right of plot area
- * @sample {highstock} stock/chart/subtitle-footnote
- * Footnote at the bottom right of plot area
- *
- * @type {number}
- * @since 2.0
- * @apioption subtitle.y
- */
- /**
- * The subtitle of the chart.
- *
- * @sample {highcharts|highstock} highcharts/subtitle/text/
- * Custom subtitle
- * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
- * Formatted and linked text.
- */
- text: '',
- /**
- * The horizontal alignment of the subtitle. Can be one of "left",
- * "center" and "right".
- *
- * @sample {highcharts} highcharts/subtitle/align/
- * Footnote at right of plot area
- * @sample {highstock} stock/chart/subtitle-footnote
- * Footnote at bottom right of plot area
- *
- * @type {Highcharts.AlignType}
- * @since 2.0
- */
- align: 'center',
- /**
- * Adjustment made to the subtitle width, normally to reserve space
- * for the exporting burger menu.
- *
- * @see [title.widthAdjust](#title.widthAdjust)
- *
- * @sample highcharts/title/widthadjust/
- * Wider menu, greater padding
- *
- * @since 4.2.5
- */
- widthAdjust: -44
- },
- /**
- * The plotOptions is a wrapper object for config objects for each series
- * type. The config objects for each series can also be overridden for
- * each series item as given in the series array.
- *
- * Configuration options for the series are given in three levels. Options
- * for all series in a chart are given in the [plotOptions.series](
- * #plotOptions.series) object. Then options for all series of a specific
- * type are given in the plotOptions of that type, for example
- * `plotOptions.line`. Next, options for one single series are given in
- * [the series array](#series).
- */
- plotOptions: {},
- /**
- * HTML labels that can be positioned anywhere in the chart area.
- */
- labels: {
- /**
- * An HTML label that can be positioned anywhere in the chart area.
- *
- * @type {Array<*>}
- * @apioption labels.items
- */
- /**
- * Inner HTML or text for the label.
- *
- * @type {string}
- * @apioption labels.items.html
- */
- /**
- * CSS styles for each label. To position the label, use left and top
- * like this:
- *
- * <pre>style: {
- * left: '100px',
- * top: '100px'
- * }</pre>
- *
- * @type {Highcharts.CSSObject}
- * @apioption labels.items.style
- */
- /**
- * Shared CSS styles for all labels.
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#333333", "position": "absolute"}
- */
- style: {
- /**
- * @ignore
- */
- position: 'absolute',
- /**
- * @ignore
- */
- color: '#333333'
- }
- },
- /**
- * The legend is a box containing a symbol and name for each series
- * item or point item in the chart. Each series (or points in case
- * of pie charts) is represented by a symbol and its name in the legend.
- *
- * It is possible to override the symbol creator function and create
- * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
- *
- * @productdesc {highmaps}
- * A Highmaps legend by default contains one legend item per series, but if
- * a `colorAxis` is defined, the axis will be displayed in the legend.
- * Either as a gradient, or as multiple legend items for `dataClasses`.
- */
- legend: {
- /**
- * The background color of the legend.
- *
- * @see In styled mode, the legend background fill can be applied with
- * the `.highcharts-legend-box` class.
- *
- * @sample {highcharts} highcharts/legend/backgroundcolor/
- * Yellowish background
- * @sample {highstock} stock/legend/align/
- * Various legend options
- * @sample {highmaps} maps/legend/border-background/
- * Border and background options
- *
- * @type {Highcharts.ColorString}
- * @apioption legend.backgroundColor
- */
- /**
- * The width of the drawn border around the legend.
- *
- * @see In styled mode, the legend border stroke width can be applied
- * with the `.highcharts-legend-box` class.
- *
- * @sample {highcharts} highcharts/legend/borderwidth/
- * 2px border width
- * @sample {highstock} stock/legend/align/
- * Various legend options
- * @sample {highmaps} maps/legend/border-background/
- * Border and background options
- *
- * @type {number}
- * @default 0
- * @apioption legend.borderWidth
- */
- /**
- * Enable or disable the legend. There is also a series-specific option,
- * [showInLegend](#plotOptions.series.showInLegend), that can hide the
- * series from the legend. In some series types this is `false` by
- * default, so it must set to `true` in order to show the legend for the
- * series.
- *
- * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
- * @sample {highstock} stock/legend/align/ Various legend options
- * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
- *
- * @default {highstock} false
- * @default {highmaps} true
- * @default {gantt} false
- */
- enabled: true,
- /**
- * The horizontal alignment of the legend box within the chart area.
- * Valid values are `left`, `center` and `right`.
- *
- * In the case that the legend is aligned in a corner position, the
- * `layout` option will determine whether to place it above/below
- * or on the side of the plot area.
- *
- * @sample {highcharts} highcharts/legend/align/
- * Legend at the right of the chart
- * @sample {highstock} stock/legend/align/
- * Various legend options
- * @sample {highmaps} maps/legend/alignment/
- * Legend alignment
- *
- * @type {Highcharts.AlignType}
- * @since 2.0
- */
- align: 'center',
- /**
- * If the [layout](legend.layout) is `horizontal` and the legend items
- * span over two lines or more, whether to align the items into vertical
- * columns. Setting this to `false` makes room for more items, but will
- * look more messy.
- *
- * @since 6.1.0
- */
- alignColumns: true,
- /**
- * When the legend is floating, the plot area ignores it and is allowed
- * to be placed below it.
- *
- * @sample {highcharts} highcharts/legend/floating-false/
- * False by default
- * @sample {highcharts} highcharts/legend/floating-true/
- * True
- * @sample {highmaps} maps/legend/alignment/
- * Floating legend
- *
- * @type {boolean}
- * @default false
- * @since 2.1
- * @apioption legend.floating
- */
- /**
- * The layout of the legend items. Can be one of `horizontal` or
- * `vertical` or `proximate`. When `proximate`, the legend items will be
- * placed as close as possible to the graphs they're representing,
- * except in inverted charts or when the legend position doesn't allow
- * it.
- *
- * @sample {highcharts} highcharts/legend/layout-horizontal/
- * Horizontal by default
- * @sample {highcharts} highcharts/legend/layout-vertical/
- * Vertical
- * @sample highcharts/legend/layout-proximate
- * Labels proximate to the data
- * @sample {highstock} stock/legend/layout-horizontal/
- * Horizontal by default
- * @sample {highmaps} maps/legend/padding-itemmargin/
- * Vertical with data classes
- * @sample {highmaps} maps/legend/layout-vertical/
- * Vertical with color axis gradient
- *
- * @validvalue ["horizontal", "vertical", "proximate"]
- */
- layout: 'horizontal',
- /**
- * In a legend with horizontal layout, the itemDistance defines the
- * pixel distance between each item.
- *
- * @sample {highcharts} highcharts/legend/layout-horizontal/
- * 50px item distance
- * @sample {highstock} highcharts/legend/layout-horizontal/
- * 50px item distance
- *
- * @type {number}
- * @default {highcharts} 20
- * @default {highstock} 20
- * @default {highmaps} 8
- * @since 3.0.3
- * @apioption legend.itemDistance
- */
- /**
- * The pixel bottom margin for each legend item.
- *
- * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
- * Padding and item margins demonstrated
- * @sample {highmaps} maps/legend/padding-itemmargin/
- * Padding and item margins demonstrated
- *
- * @type {number}
- * @default 0
- * @since 2.2.0
- * @apioption legend.itemMarginBottom
- */
- /**
- * The pixel top margin for each legend item.
- *
- * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
- * Padding and item margins demonstrated
- * @sample {highmaps} maps/legend/padding-itemmargin/
- * Padding and item margins demonstrated
- *
- * @type {number}
- * @default 0
- * @since 2.2.0
- * @apioption legend.itemMarginTop
- */
- /**
- * The width for each legend item. By default the items are laid out
- * successively. In a [horizontal layout](legend.layout), if the items
- * are laid out across two rows or more, they will be vertically aligned
- * depending on the [legend.alignColumns](legend.alignColumns) option.
- *
- * @sample {highcharts} highcharts/legend/itemwidth-default/
- * Undefined by default
- * @sample {highcharts} highcharts/legend/itemwidth-80/
- * 80 for aligned legend items
- *
- * @type {number}
- * @since 2.0
- * @apioption legend.itemWidth
- */
- /**
- * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
- * for each legend label. Available variables relates to properties on
- * the series, or the point in case of pies.
- *
- * @type {string}
- * @default {name}
- * @since 1.3
- * @apioption legend.labelFormat
- */
- /**
- * Callback function to format each of the series' labels. The `this`
- * keyword refers to the series object, or the point object in case
- * of pie charts. By default the series or point name is printed.
- *
- * @productdesc {highmaps}
- * In Highmaps the context can also be a data class in case of a
- * `colorAxis`.
- *
- * @sample {highcharts} highcharts/legend/labelformatter/
- * Add text
- * @sample {highmaps} maps/legend/labelformatter/
- * Data classes with label formatter
- *
- * @context {Highcharts.Series|Highcharts.Point}
- */
- labelFormatter: function () {
- return this.name;
- },
- /**
- * Line height for the legend items. Deprecated as of 2.1\. Instead,
- * the line height for each item can be set using itemStyle.lineHeight,
- * and the padding between items using `itemMarginTop` and
- * `itemMarginBottom`.
- *
- * @sample {highcharts} highcharts/legend/lineheight/
- * Setting padding
- *
- * @deprecated
- *
- * @type {number}
- * @default 16
- * @since 2.0
- * @product highcharts gantt
- * @apioption legend.lineHeight
- */
- /**
- * If the plot area sized is calculated automatically and the legend
- * is not floating, the legend margin is the space between the legend
- * and the axis labels or plot area.
- *
- * @sample {highcharts} highcharts/legend/margin-default/
- * 12 pixels by default
- * @sample {highcharts} highcharts/legend/margin-30/
- * 30 pixels
- *
- * @type {number}
- * @default 12
- * @since 2.1
- * @apioption legend.margin
- */
- /**
- * Maximum pixel height for the legend. When the maximum height is
- * extended, navigation will show.
- *
- * @type {number}
- * @since 2.3.0
- * @apioption legend.maxHeight
- */
- /**
- * The color of the drawn border around the legend.
- *
- * @see In styled mode, the legend border stroke can be applied with the
- * `.highcharts-legend-box` class.
- *
- * @sample {highcharts} highcharts/legend/bordercolor/
- * Brown border
- * @sample {highstock} stock/legend/align/
- * Various legend options
- * @sample {highmaps} maps/legend/border-background/
- * Border and background options
- *
- * @type {Highcharts.ColorString}
- */
- borderColor: '#999999',
- /**
- * The border corner radius of the legend.
- *
- * @sample {highcharts} highcharts/legend/borderradius-default/
- * Square by default
- * @sample {highcharts} highcharts/legend/borderradius-round/
- * 5px rounded
- * @sample {highmaps} maps/legend/border-background/
- * Border and background options
- */
- borderRadius: 0,
- /**
- * Options for the paging or navigation appearing when the legend
- * is overflown. Navigation works well on screen, but not in static
- * exported images. One way of working around that is to
- * [increase the chart height in
- * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
- */
- navigation: {
- /**
- * How to animate the pages when navigating up or down. A value of
- * `true` applies the default navigation given in the
- * `chart.animation` option. Additional options can be given as an
- * object containing values for easing and duration.
- *
- * @sample {highcharts} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- * @sample {highstock} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- *
- * @type {boolean|Highcharts.AnimationOptionsObject}
- * @default true
- * @since 2.2.4
- * @apioption legend.navigation.animation
- */
- /**
- * The pixel size of the up and down arrows in the legend paging
- * navigation.
- *
- * @sample {highcharts} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- * @sample {highstock} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- *
- * @type {number}
- * @default 12
- * @since 2.2.4
- * @apioption legend.navigation.arrowSize
- */
- /**
- * Whether to enable the legend navigation. In most cases, disabling
- * the navigation results in an unwanted overflow.
- *
- * See also the [adapt chart to legend](
- * https://www.highcharts.com/products/plugin-registry/single/8/Adapt-Chart-To-Legend)
- * plugin for a solution to extend the chart height to make room for
- * the legend, optionally in exported charts only.
- *
- * @type {boolean}
- * @default true
- * @since 4.2.4
- * @apioption legend.navigation.enabled
- */
- /**
- * Text styles for the legend page navigation.
- *
- * @see In styled mode, the navigation items are styled with the
- * `.highcharts-legend-navigation` class.
- *
- * @sample {highcharts} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- * @sample {highstock} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- *
- * @type {Highcharts.CSSObject}
- * @since 2.2.4
- * @apioption legend.navigation.style
- */
- /**
- * The color for the active up or down arrow in the legend page
- * navigation.
- *
- * @see In styled mode, the active arrow be styled with the
- * `.highcharts-legend-nav-active` class.
- *
- * @sample {highcharts} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- * @sample {highstock} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- *
- * @type {Highcharts.ColorString}
- * @since 2.2.4
- */
- activeColor: '#003399',
- /**
- * The color of the inactive up or down arrow in the legend page
- * navigation. .
- *
- * @see In styled mode, the inactive arrow be styled with the
- * `.highcharts-legend-nav-inactive` class.
- *
- * @sample {highcharts} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- * @sample {highstock} highcharts/legend/navigation/
- * Legend page navigation demonstrated
- *
- * @type {Highcharts.ColorString}
- * @since 2.2.4
- */
- inactiveColor: '#cccccc'
- },
- /**
- * The inner padding of the legend box.
- *
- * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
- * Padding and item margins demonstrated
- * @sample {highmaps} maps/legend/padding-itemmargin/
- * Padding and item margins demonstrated
- *
- * @type {number}
- * @default 8
- * @since 2.2.0
- * @apioption legend.padding
- */
- /**
- * Whether to reverse the order of the legend items compared to the
- * order of the series or points as defined in the configuration object.
- *
- * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
- * [series.legendIndex](#series.legendIndex)
- *
- * @sample {highcharts} highcharts/legend/reversed/
- * Stacked bar with reversed legend
- *
- * @type {boolean}
- * @default false
- * @since 1.2.5
- * @apioption legend.reversed
- */
- /**
- * Whether to show the symbol on the right side of the text rather than
- * the left side. This is common in Arabic and Hebraic.
- *
- * @sample {highcharts} highcharts/legend/rtl/
- * Symbol to the right
- *
- * @type {boolean}
- * @default false
- * @since 2.2
- * @apioption legend.rtl
- */
- /**
- * CSS styles for the legend area. In the 1.x versions the position
- * of the legend area was determined by CSS. In 2.x, the position is
- * determined by properties like `align`, `verticalAlign`, `x` and `y`,
- * but the styles are still parsed for backwards compatibility.
- *
- * @deprecated
- *
- * @type {Highcharts.CSSObject}
- * @product highcharts highstock
- * @apioption legend.style
- */
- /**
- * CSS styles for each legend item. Only a subset of CSS is supported,
- * notably those options related to text. The default `textOverflow`
- * property makes long texts truncate. Set it to `undefined` to wrap
- * text instead. A `width` property can be added to control the text
- * width.
- *
- * @see In styled mode, the legend items can be styled with the
- * `.highcharts-legend-item` class.
- *
- * @sample {highcharts} highcharts/legend/itemstyle/
- * Bold black text
- * @sample {highmaps} maps/legend/itemstyle/
- * Item text styles
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#333333", "cursor": "pointer", "fontSize": "12px", "fontWeight": "bold", "textOverflow": "ellipsis"}
- */
- itemStyle: {
- /**
- * @ignore
- */
- color: '#333333',
- /**
- * @ignore
- */
- cursor: 'pointer',
- /**
- * @ignore
- */
- fontSize: '12px',
- /**
- * @ignore
- */
- fontWeight: 'bold',
- /**
- * @ignore
- */
- textOverflow: 'ellipsis'
- },
- /**
- * CSS styles for each legend item in hover mode. Only a subset of
- * CSS is supported, notably those options related to text. Properties
- * are inherited from `style` unless overridden here.
- *
- * @see In styled mode, the hovered legend items can be styled with
- * the `.highcharts-legend-item:hover` pesudo-class.
- *
- * @sample {highcharts} highcharts/legend/itemhoverstyle/
- * Red on hover
- * @sample {highmaps} maps/legend/itemstyle/
- * Item text styles
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#000000"}
- */
- itemHoverStyle: {
- /**
- * @ignore
- */
- color: '#000000'
- },
- /**
- * CSS styles for each legend item when the corresponding series or
- * point is hidden. Only a subset of CSS is supported, notably those
- * options related to text. Properties are inherited from `style`
- * unless overridden here.
- *
- * @see In styled mode, the hidden legend items can be styled with
- * the `.highcharts-legend-item-hidden` class.
- *
- * @sample {highcharts} highcharts/legend/itemhiddenstyle/
- * Darker gray color
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#cccccc"}
- */
- itemHiddenStyle: {
- /**
- * @ignore
- */
- color: '#cccccc'
- },
- /**
- * Whether to apply a drop shadow to the legend. A `backgroundColor`
- * also needs to be applied for this to take effect. The shadow can be
- * an object configuration containing `color`, `offsetX`, `offsetY`,
- * `opacity` and `width`.
- *
- * @sample {highcharts} highcharts/legend/shadow/
- * White background and drop shadow
- * @sample {highstock} stock/legend/align/
- * Various legend options
- * @sample {highmaps} maps/legend/border-background/
- * Border and background options
- *
- * @type {boolean|Highcharts.CSSObject}
- */
- shadow: false,
- /**
- * Default styling for the checkbox next to a legend item when
- * `showCheckbox` is true.
- *
- * @type {Highcharts.CSSObject}
- * @default {"width": "13px", "height": "13px", "position":"absolute"}
- */
- itemCheckboxStyle: {
- /**
- * @ignore
- */
- position: 'absolute',
- /**
- * @ignore
- */
- width: '13px', // for IE precision
- /**
- * @ignore
- */
- height: '13px'
- },
- // itemWidth: undefined,
- /**
- * When this is true, the legend symbol width will be the same as
- * the symbol height, which in turn defaults to the font size of the
- * legend items.
- *
- * @since 5.0.0
- */
- squareSymbol: true,
- /**
- * The pixel height of the symbol for series types that use a rectangle
- * in the legend. Defaults to the font size of legend items.
- *
- * @productdesc {highmaps}
- * In Highmaps, when the symbol is the gradient of a vertical color
- * axis, the height defaults to 200.
- *
- * @sample {highmaps} maps/legend/layout-vertical-sized/
- * Sized vertical gradient
- * @sample {highmaps} maps/legend/padding-itemmargin/
- * No distance between data classes
- *
- * @type {number}
- * @since 3.0.8
- * @apioption legend.symbolHeight
- */
- /**
- * The border radius of the symbol for series types that use a rectangle
- * in the legend. Defaults to half the `symbolHeight`.
- *
- * @sample {highcharts} highcharts/legend/symbolradius/
- * Round symbols
- * @sample {highstock} highcharts/legend/symbolradius/
- * Round symbols
- * @sample {highmaps} highcharts/legend/symbolradius/
- * Round symbols
- *
- * @type {number}
- * @since 3.0.8
- * @apioption legend.symbolRadius
- */
- /**
- * The pixel width of the legend item symbol. When the `squareSymbol`
- * option is set, this defaults to the `symbolHeight`, otherwise 16.
- *
- * @productdesc {highmaps}
- * In Highmaps, when the symbol is the gradient of a horizontal color
- * axis, the width defaults to 200.
- *
- * @sample {highcharts} highcharts/legend/symbolwidth/
- * Greater symbol width and padding
- * @sample {highmaps} maps/legend/padding-itemmargin/
- * Padding and item margins demonstrated
- * @sample {highmaps} maps/legend/layout-vertical-sized/
- * Sized vertical gradient
- *
- * @type {number}
- * @apioption legend.symbolWidth
- */
- /**
- * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the legend item texts.
- *
- * Prior to 4.1.7, when using HTML, [legend.navigation](
- * #legend.navigation) was disabled.
- *
- * @type {boolean}
- * @default false
- * @apioption legend.useHTML
- */
- /**
- * The width of the legend box. If a number is set, it translates to
- * pixels. Since v7.0.2 it allows setting a percent string of the full
- * chart width, for example `40%`.
- *
- * Defaults to the full chart width from legends below or above the
- * chart, half the chart width for legends to the left and right.
- *
- * @sample {highcharts} highcharts/legend/width/
- * Aligned to the plot area
- * @sample {highcharts} highcharts/legend/width-percent/
- * A percent of the chart width
- *
- * @type {number|string}
- * @since 2.0
- * @apioption legend.width
- */
- /**
- * The pixel padding between the legend item symbol and the legend
- * item text.
- *
- * @sample {highcharts} highcharts/legend/symbolpadding/
- * Greater symbol width and padding
- */
- symbolPadding: 5,
- /**
- * The vertical alignment of the legend box. Can be one of `top`,
- * `middle` or `bottom`. Vertical position can be further determined
- * by the `y` option.
- *
- * In the case that the legend is aligned in a corner position, the
- * `layout` option will determine whether to place it above/below
- * or on the side of the plot area.
- *
- * When the [layout](#legend.layout) option is `proximate`, the
- * `verticalAlign` option doesn't apply.
- *
- * @sample {highcharts} highcharts/legend/verticalalign/
- * Legend 100px from the top of the chart
- * @sample {highstock} stock/legend/align/
- * Various legend options
- * @sample {highmaps} maps/legend/alignment/
- * Legend alignment
- *
- * @type {Highcharts.VerticalAlignType}
- * @since 2.0
- */
- verticalAlign: 'bottom',
- // width: undefined,
- /**
- * The x offset of the legend relative to its horizontal alignment
- * `align` within chart.spacingLeft and chart.spacingRight. Negative
- * x moves it to the left, positive x moves it to the right.
- *
- * @sample {highcharts} highcharts/legend/width/
- * Aligned to the plot area
- *
- * @since 2.0
- */
- x: 0,
- /**
- * The vertical offset of the legend relative to it's vertical alignment
- * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
- * Negative y moves it up, positive y moves it down.
- *
- * @sample {highcharts} highcharts/legend/verticalalign/
- * Legend 100px from the top of the chart
- * @sample {highstock} stock/legend/align/
- * Various legend options
- * @sample {highmaps} maps/legend/alignment/
- * Legend alignment
- *
- * @since 2.0
- */
- y: 0,
- /**
- * A title to be added on top of the legend.
- *
- * @sample {highcharts} highcharts/legend/title/
- * Legend title
- * @sample {highmaps} maps/legend/alignment/
- * Legend with title
- *
- * @since 3.0
- */
- title: {
- /**
- * A text or HTML string for the title.
- *
- * @type {string}
- * @since 3.0
- * @apioption legend.title.text
- */
- /**
- * Generic CSS styles for the legend title.
- *
- * @see In styled mode, the legend title is styled with the
- * `.highcharts-legend-title` class.
- *
- * @type {Highcharts.CSSObject}
- * @default {"fontWeight": "bold"}
- * @since 3.0
- */
- style: {
- /**
- * @ignore
- */
- fontWeight: 'bold'
- }
- }
- },
- /**
- * The loading options control the appearance of the loading screen
- * that covers the plot area on chart operations. This screen only
- * appears after an explicit call to `chart.showLoading()`. It is a
- * utility for developers to communicate to the end user that something
- * is going on, for example while retrieving new data via an XHR connection.
- * The "Loading..." text itself is not part of this configuration
- * object, but part of the `lang` object.
- */
- loading: {
- /**
- * The duration in milliseconds of the fade out effect.
- *
- * @sample highcharts/loading/hideduration/
- * Fade in and out over a second
- *
- * @type {number}
- * @default 100
- * @since 1.2.0
- * @apioption loading.hideDuration
- */
- /**
- * The duration in milliseconds of the fade in effect.
- *
- * @sample highcharts/loading/hideduration/
- * Fade in and out over a second
- *
- * @type {number}
- * @default 100
- * @since 1.2.0
- * @apioption loading.showDuration
- */
- /**
- * CSS styles for the loading label `span`.
- *
- * @see In styled mode, the loading label is styled with the
- * `.highcharts-loading-inner` class.
- *
- * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
- * Vertically centered
- * @sample {highstock} stock/loading/general/
- * Label styles
- *
- * @type {Highcharts.CSSObject}
- * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
- * @since 1.2.0
- */
- labelStyle: {
- /**
- * @ignore
- */
- fontWeight: 'bold',
- /**
- * @ignore
- */
- position: 'relative',
- /**
- * @ignore
- */
- top: '45%'
- },
- /**
- * CSS styles for the loading screen that covers the plot area.
- *
- * In styled mode, the loading label is styled with the
- * `.highcharts-loading` class.
- *
- * @sample {highcharts|highmaps} highcharts/loading/style/
- * Gray plot area, white text
- * @sample {highstock} stock/loading/general/
- * Gray plot area, white text
- *
- * @type {Highcharts.CSSObject}
- * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
- * @since 1.2.0
- */
- style: {
- /**
- * @ignore
- */
- position: 'absolute',
- /**
- * @ignore
- */
- backgroundColor: '#ffffff',
- /**
- * @ignore
- */
- opacity: 0.5,
- /**
- * @ignore
- */
- textAlign: 'center'
- }
- },
- /**
- * Options for the tooltip that appears when the user hovers over a
- * series or point.
- */
- tooltip: {
- /**
- * The color of the tooltip border. When `undefined`, the border takes
- * the color of the corresponding series or point.
- *
- * @sample {highcharts} highcharts/tooltip/bordercolor-default/
- * Follow series by default
- * @sample {highcharts} highcharts/tooltip/bordercolor-black/
- * Black border
- * @sample {highstock} stock/tooltip/general/
- * Styled tooltip
- * @sample {highmaps} maps/tooltip/background-border/
- * Background and border demo
- *
- * @type {Highcharts.ColorString}
- * @apioption tooltip.borderColor
- */
- /**
- * Since 4.1, the crosshair definitions are moved to the Axis object
- * in order for a better separation from the tooltip. See
- * [xAxis.crosshair](#xAxis.crosshair)<a>.</a>
- *
- * @sample {highcharts} highcharts/tooltip/crosshairs-x/
- * Enable a crosshair for the x value
- *
- * @deprecated
- *
- * @type {*}
- * @default true
- * @apioption tooltip.crosshairs
- */
- /**
- * Whether the tooltip should follow the mouse as it moves across
- * columns, pie slices and other point types with an extent. By default
- * it behaves this way for scatter, bubble and pie series by override
- * in the `plotOptions` for those series types.
- *
- * For touch moves to behave the same way, [followTouchMove](
- * #tooltip.followTouchMove) must be `true` also.
- *
- * @type {boolean}
- * @default {highcharts} false
- * @default {highstock} false
- * @default {highmaps} true
- * @since 3.0
- * @apioption tooltip.followPointer
- */
- /**
- * Whether the tooltip should update as the finger moves on a touch
- * device. If this is `true` and [chart.panning](#chart.panning) is
- * set,`followTouchMove` will take over one-finger touches, so the user
- * needs to use two fingers for zooming and panning.
- *
- * Note the difference to [followPointer](#tooltip.followPointer) that
- * only defines the _position_ of the tooltip. If `followPointer` is
- * false in for example a column series, the tooltip will show above or
- * below the column, but as `followTouchMove` is true, the tooltip will
- * jump from column to column as the user swipes across the plot area.
- *
- * @type {boolean}
- * @default {highcharts} true
- * @default {highstock} true
- * @default {highmaps} false
- * @since 3.0.1
- * @apioption tooltip.followTouchMove
- */
- /**
- * Callback function to format the text of the tooltip from scratch.
- * Return `false` to disable tooltip for a specific point on series.
- *
- * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
- * the tooltip is parsed and converted to SVG, therefore this isn't a
- * complete HTML renderer. The following tags are supported: `<b>`,
- * `<strong>`, `<i>`, `<em>`, `<br/>`, `<span>`. Spans can be styled
- * with a `style` attribute, but only text-related CSS that is shared
- * with SVG is handled.
- *
- * Since version 2.1 the tooltip can be shared between multiple series
- * through the `shared` option. The available data in the formatter
- * differ a bit depending on whether the tooltip is shared or not. In
- * a shared tooltip, all properties except `x`, which is common for
- * all points, are kept in an array, `this.points`.
- *
- * Available data are:
- *
- * <dl>
- *
- * <dt>this.percentage (not shared) / this.points[i].percentage (shared)
- * </dt>
- *
- * <dd>Stacked series and pies only. The point's percentage of the
- * total.
- * </dd>
- *
- * <dt>this.point (not shared) / this.points[i].point (shared)</dt>
- *
- * <dd>The point object. The point name, if defined, is available
- * through `this.point.name`.</dd>
- *
- * <dt>this.points</dt>
- *
- * <dd>In a shared tooltip, this is an array containing all other
- * properties for each point.</dd>
- *
- * <dt>this.series (not shared) / this.points[i].series (shared)</dt>
- *
- * <dd>The series object. The series name is available through
- * `this.series.name`.</dd>
- *
- * <dt>this.total (not shared) / this.points[i].total (shared)</dt>
- *
- * <dd>Stacked series only. The total value at this point's x value.
- * </dd>
- *
- * <dt>this.x</dt>
- *
- * <dd>The x value. This property is the same regardless of the tooltip
- * being shared or not.</dd>
- *
- * <dt>this.y (not shared) / this.points[i].y (shared)</dt>
- *
- * <dd>The y value.</dd>
- *
- * </dl>
- *
- * @sample {highcharts} highcharts/tooltip/formatter-simple/
- * Simple string formatting
- * @sample {highcharts} highcharts/tooltip/formatter-shared/
- * Formatting with shared tooltip
- * @sample {highstock} stock/tooltip/formatter/
- * Formatting with shared tooltip
- * @sample {highmaps} maps/tooltip/formatter/
- * String formatting
- *
- * @type {Highcharts.FormatterCallbackFunction<Highcharts.TooltipFormatterContextObject>}
- * @apioption tooltip.formatter
- */
- /**
- * The number of milliseconds to wait until the tooltip is hidden when
- * mouse out from a point or chart.
- *
- * @type {number}
- * @default 500
- * @since 3.0
- * @apioption tooltip.hideDelay
- */
- /**
- * Whether to allow the tooltip to render outside the chart's SVG
- * element box. By default (`false`), the tooltip is rendered within the
- * chart's SVG element, which results in the tooltip being aligned
- * inside the chart area. For small charts, this may result in clipping
- * or overlapping. When `true`, a separate SVG element is created and
- * overlaid on the page, allowing the tooltip to be aligned inside the
- * page itself.
- *
- * @sample highcharts/tooltip/outside
- * Small charts with tooltips outside
- *
- * @type {boolean}
- * @default false
- * @since 6.1.1
- * @apioption tooltip.outside
- */
- /**
- * A callback function for formatting the HTML output for a single point
- * in the tooltip. Like the `pointFormat` string, but with more
- * flexibility.
- *
- * @type {Function}
- * @since 4.1.0
- * @context Highcharts.Point
- * @apioption tooltip.pointFormatter
- */
- /**
- * A callback function to place the tooltip in a default position. The
- * callback receives three parameters: `labelWidth`, `labelHeight` and
- * `point`, where point contains values for `plotX` and `plotY` telling
- * where the reference point is in the plot area. Add `chart.plotLeft`
- * and `chart.plotTop` to get the full coordinates.
- *
- * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
- * positioner is called for each of the boxes separately, including
- * xAxis header. xAxis header is not a point, instead `point` argument
- * contains info:
- * `{ plotX: Number, plotY: Number, isHeader: Boolean }`
- *
- *
- * The return should be an object containing x and y values, for example
- * `{ x: 100, y: 100 }`.
- *
- * @sample {highcharts} highcharts/tooltip/positioner/
- * A fixed tooltip position
- * @sample {highstock} stock/tooltip/positioner/
- * A fixed tooltip position on top of the chart
- * @sample {highmaps} maps/tooltip/positioner/
- * A fixed tooltip position
- * @sample {highstock} stock/tooltip/split-positioner/
- * Split tooltip with fixed positions
- *
- * @type {Highcharts.TooltipPositionerCallbackFunction}
- * @since 2.2.4
- * @apioption tooltip.positioner
- */
- /**
- * The name of a symbol to use for the border around the tooltip. Can
- * be one of: `"callout"`, `"circle"` or `"square"`. When
- * [tooltip.split](#tooltip.split) option is enabled, shape is applied
- * to all boxes except header, which is controlled by
- * [tooltip.headerShape](#tooltip.headerShape).
- *
- * Custom callbacks for symbol path generation can also be added to
- * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
- * [series.marker.symbol](plotOptions.line.marker.symbol).
- *
- * @type {string}
- * @default callout
- * @since 4.0
- * @validvalue ["callout", "square"]
- * @apioption tooltip.shape
- */
- /**
- * The name of a symbol to use for the border around the tooltip
- * header. Applies only when [tooltip.split](#tooltip.split) is
- * enabled.
- *
- * Custom callbacks for symbol path generation can also be added to
- * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
- * [series.marker.symbol](plotOptions.line.marker.symbol).
- *
- * @see [tooltip.shape](#tooltip.shape)
- * @type {String}
- * @default callout
- * @sample {highstock} stock/tooltip/split-positioner/
- * Different shapes for header and split boxes
- * @validvalue ["callout", "square"]
- * @since 7.0
- * @apioption tooltip.headerShape
- */
- /**
- * When the tooltip is shared, the entire plot area will capture mouse
- * movement or touch events. Tooltip texts for series types with ordered
- * data (not pie, scatter, flags etc) will be shown in a single bubble.
- * This is recommended for single series charts and for tablet/mobile
- * optimized charts.
- *
- * See also [tooltip.split](#tooltip.split), that is better suited for
- * charts with many series, especially line-type series. The
- * `tooltip.split` option takes precedence over `tooltip.shared`.
- *
- * @sample {highcharts} highcharts/tooltip/shared-false/
- * False by default
- * @sample {highcharts} highcharts/tooltip/shared-true/
- * True
- * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
- * True with x axis crosshair
- * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
- * True with mixed series types
- *
- * @type {boolean}
- * @default false
- * @since 2.1
- * @product highcharts highstock
- * @apioption tooltip.shared
- */
- /**
- * Split the tooltip into one label per series, with the header close
- * to the axis. This is recommended over [shared](#tooltip.shared)
- * tooltips for charts with multiple line series, generally making them
- * easier to read. This option takes precedence over `tooltip.shared`.
- *
- * @productdesc {highstock} In Highstock, tooltips are split by default
- * since v6.0.0. Stock charts typically contain multi-dimension points
- * and multiple panes, making split tooltips the preferred layout over
- * the previous `shared` tooltip.
- *
- * @sample highcharts/tooltip/split/
- * Split tooltip
- *
- * @type {boolean}
- * @default {highcharts} false
- * @default {highstock} true
- * @since 5.0.0
- * @product highcharts highstock
- * @apioption tooltip.split
- */
- /**
- * Use HTML to render the contents of the tooltip instead of SVG. Using
- * HTML allows advanced formatting like tables and images in the
- * tooltip. It is also recommended for rtl languages as it works around
- * rtl bugs in early Firefox.
- *
- * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
- * A table for value alignment
- * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
- * Full HTML tooltip
- * @sample {highmaps} maps/tooltip/usehtml/
- * Pure HTML tooltip
- *
- * @type {boolean}
- * @default false
- * @since 2.2
- * @apioption tooltip.useHTML
- */
- /**
- * How many decimals to show in each series' y value. This is
- * overridable in each series' tooltip options object. The default is to
- * preserve all decimals.
- *
- * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
- * Set decimals, prefix and suffix for the value
- * @sample {highmaps} maps/tooltip/valuedecimals/
- * Set decimals, prefix and suffix for the value
- *
- * @type {number}
- * @since 2.2
- * @apioption tooltip.valueDecimals
- */
- /**
- * A string to prepend to each series' y value. Overridable in each
- * series' tooltip options object.
- *
- * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
- * Set decimals, prefix and suffix for the value
- * @sample {highmaps} maps/tooltip/valuedecimals/
- * Set decimals, prefix and suffix for the value
- *
- * @type {string}
- * @since 2.2
- * @apioption tooltip.valuePrefix
- */
- /**
- * A string to append to each series' y value. Overridable in each
- * series' tooltip options object.
- *
- * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
- * Set decimals, prefix and suffix for the value
- * @sample {highmaps} maps/tooltip/valuedecimals/
- * Set decimals, prefix and suffix for the value
- *
- * @type {string}
- * @since 2.2
- * @apioption tooltip.valueSuffix
- */
- /**
- * The format for the date in the tooltip header if the X axis is a
- * datetime axis. The default is a best guess based on the smallest
- * distance between points in the chart.
- *
- * @sample {highcharts} highcharts/tooltip/xdateformat/
- * A different format
- *
- * @type {string}
- * @product highcharts highstock gantt
- * @apioption tooltip.xDateFormat
- */
- /**
- * How many decimals to show for the `point.change` value when the
- * `series.compare` option is set. This is overridable in each series'
- * tooltip options object. The default is to preserve all decimals.
- *
- * @type {number}
- * @since 1.0.1
- * @product highstock
- * @apioption tooltip.changeDecimals
- */
- /**
- * Enable or disable the tooltip.
- *
- * @sample {highcharts} highcharts/tooltip/enabled/
- * Disabled
- * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
- * Disable tooltip and show values on chart instead
- */
- enabled: true,
- /**
- * Enable or disable animation of the tooltip.
- *
- * @type {boolean}
- * @default true
- * @since 2.3.0
- */
- animation: svg,
- /**
- * The radius of the rounded border corners.
- *
- * @sample {highcharts} highcharts/tooltip/bordercolor-default/
- * 5px by default
- * @sample {highcharts} highcharts/tooltip/borderradius-0/
- * Square borders
- * @sample {highmaps} maps/tooltip/background-border/
- * Background and border demo
- */
- borderRadius: 3,
- /**
- * For series on a datetime axes, the date format in the tooltip's
- * header will by default be guessed based on the closest data points.
- * This member gives the default string representations used for
- * each unit. For an overview of the replacement codes, see
- * [dateFormat](/class-reference/Highcharts#dateFormat).
- *
- * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
- *
- * @type {Highcharts.Dictionary<string>}
- * @product highcharts highstock gantt
- */
- dateTimeLabelFormats: {
- millisecond: '%A, %b %e, %H:%M:%S.%L',
- second: '%A, %b %e, %H:%M:%S',
- minute: '%A, %b %e, %H:%M',
- hour: '%A, %b %e, %H:%M',
- day: '%A, %b %e, %Y',
- week: 'Week from %A, %b %e, %Y',
- month: '%B %Y',
- year: '%Y'
- },
- /**
- * A string to append to the tooltip format.
- *
- * @sample {highcharts} highcharts/tooltip/footerformat/
- * A table for value alignment
- * @sample {highmaps} maps/tooltip/format/
- * Format demo
- *
- * @since 2.2
- */
- footerFormat: '',
- /**
- * Padding inside the tooltip, in pixels.
- *
- * @since 5.0.0
- */
- padding: 8,
- /**
- * Proximity snap for graphs or single points. It defaults to 10 for
- * mouse-powered devices and 25 for touch devices.
- *
- * Note that in most cases the whole plot area captures the mouse
- * movement, and in these cases `tooltip.snap` doesn't make sense. This
- * applies when [stickyTracking](#plotOptions.series.stickyTracking)
- * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
- * or [split](#tooltip.split).
- *
- * @sample {highcharts} highcharts/tooltip/bordercolor-default/
- * 10 px by default
- * @sample {highcharts} highcharts/tooltip/snap-50/
- * 50 px on graph
- *
- * @type {number}
- * @default 10/25
- * @since 1.2.0
- * @product highcharts highstock
- */
- snap: isTouchDevice ? 25 : 10,
- /**
- * The HTML of the tooltip header line. Variables are enclosed by
- * curly brackets. Available variables are `point.key`, `series.name`,
- * `series.color` and other members from the `point` and `series`
- * objects. The `point.key` variable contains the category name, x
- * value or datetime string depending on the type of axis. For datetime
- * axes, the `point.key` date format can be set using
- * `tooltip.xDateFormat`.
- *
- * @sample {highcharts} highcharts/tooltip/footerformat/
- * An HTML table in the tooltip
- * @sample {highstock} highcharts/tooltip/footerformat/
- * An HTML table in the tooltip
- * @sample {highmaps} maps/tooltip/format/
- * Format demo
- *
- * @type {string}
- * @apioption tooltip.headerFormat
- */
- headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
- /**
- * The HTML of the point's line in the tooltip. Variables are enclosed
- * by curly brackets. Available variables are point.x, point.y, series.
- * name and series.color and other properties on the same form.
- * Furthermore, `point.y` can be extended by the `tooltip.valuePrefix`
- * and `tooltip.valueSuffix` variables. This can also be overridden for
- * each series, which makes it a good hook for displaying units.
- *
- * In styled mode, the dot is colored by a class name rather
- * than the point color.
- *
- * @sample {highcharts} highcharts/tooltip/pointformat/
- * A different point format with value suffix
- * @sample {highmaps} maps/tooltip/format/
- * Format demo
- *
- * @type {string}
- * @default <span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>
- * @since 2.2
- * @apioption tooltip.pointFormat
- */
- pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
- /**
- * The background color or gradient for the tooltip.
- *
- * In styled mode, the stroke width is set in the
- * `.highcharts-tooltip-box` class.
- *
- * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
- * Yellowish background
- * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
- * Gradient
- * @sample {highcharts} highcharts/css/tooltip-border-background/
- * Tooltip in styled mode
- * @sample {highstock} stock/tooltip/general/
- * Custom tooltip
- * @sample {highstock} highcharts/css/tooltip-border-background/
- * Tooltip in styled mode
- * @sample {highmaps} maps/tooltip/background-border/
- * Background and border demo
- * @sample {highmaps} highcharts/css/tooltip-border-background/
- * Tooltip in styled mode
- *
- * @type {Highcharts.ColorString}
- */
- backgroundColor: color('#f7f7f7')
- .setOpacity(0.85).get(),
- /**
- * The pixel width of the tooltip border.
- *
- * In styled mode, the stroke width is set in the
- * `.highcharts-tooltip-box` class.
- *
- * @sample {highcharts} highcharts/tooltip/bordercolor-default/
- * 2px by default
- * @sample {highcharts} highcharts/tooltip/borderwidth/
- * No border (shadow only)
- * @sample {highcharts} highcharts/css/tooltip-border-background/
- * Tooltip in styled mode
- * @sample {highstock} stock/tooltip/general/
- * Custom tooltip
- * @sample {highstock} highcharts/css/tooltip-border-background/
- * Tooltip in styled mode
- * @sample {highmaps} maps/tooltip/background-border/
- * Background and border demo
- * @sample {highmaps} highcharts/css/tooltip-border-background/
- * Tooltip in styled mode
- */
- borderWidth: 1,
- /**
- * Whether to apply a drop shadow to the tooltip.
- *
- * @sample {highcharts} highcharts/tooltip/bordercolor-default/
- * True by default
- * @sample {highcharts} highcharts/tooltip/shadow/
- * False
- * @sample {highmaps} maps/tooltip/positioner/
- * Fixed tooltip position, border and shadow disabled
- */
- shadow: true,
- /**
- * CSS styles for the tooltip. The tooltip can also be styled through
- * the CSS class `.highcharts-tooltip`.
- *
- * @sample {highcharts} highcharts/tooltip/style/
- * Greater padding, bold text
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#333333", "cursor": "default", "fontSize": "12px", "pointerEvents": "none", "whiteSpace": "nowrap"}
- */
- style: {
- /**
- * @ignore
- */
- color: '#333333',
- /**
- * @ignore
- */
- cursor: 'default',
- /**
- * @ignore
- */
- fontSize: '12px',
- /**
- * @ignore
- */
- pointerEvents: 'none',
- // #1686 http://caniuse.com/#feat=pointer-events
- /**
- * @ignore
- */
- whiteSpace: 'nowrap'
- }
- },
- /**
- * Highchart by default puts a credits label in the lower right corner
- * of the chart. This can be changed using these options.
- */
- credits: {
- /**
- * Credits for map source to be concatenated with conventional credit
- * text. By default this is a format string that collects copyright
- * information from the map if available.
- *
- * @see [mapTextFull](#credits.mapTextFull)
- * @see [text](#credits.text)
- *
- * @type {string}
- * @default \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
- * @since 4.2.2
- * @product highmaps
- * @apioption credits.mapText
- */
- /**
- * Detailed credits for map source to be displayed on hover of credits
- * text. By default this is a format string that collects copyright
- * information from the map if available.
- *
- * @see [mapText](#credits.mapText)
- * @see [text](#credits.text)
- *
- * @type {string}
- * @default {geojson.copyright}
- * @since 4.2.2
- * @product highmaps
- * @apioption credits.mapTextFull
- */
- /**
- * Whether to show the credits text.
- *
- * @sample {highcharts} highcharts/credits/enabled-false/
- * Credits disabled
- * @sample {highstock} stock/credits/enabled/
- * Credits disabled
- * @sample {highmaps} maps/credits/enabled-false/
- * Credits disabled
- */
- enabled: true,
- /**
- * The URL for the credits label.
- *
- * @sample {highcharts} highcharts/credits/href/
- * Custom URL and text
- * @sample {highmaps} maps/credits/customized/
- * Custom URL and text
- */
- href: 'https://www.highcharts.com?credits',
- /**
- * Position configuration for the credits label.
- *
- * @sample {highcharts} highcharts/credits/position-left/
- * Left aligned
- * @sample {highcharts} highcharts/credits/position-left/
- * Left aligned
- * @sample {highmaps} maps/credits/customized/
- * Left aligned
- * @sample {highmaps} maps/credits/customized/
- * Left aligned
- *
- * @type {Highcharts.AlignObject}
- * @since 2.1
- */
- position: {
- /**
- * Horizontal alignment of the credits.
- *
- * @type {Highcharts.AlignType}
- */
- align: 'right',
- /**
- * Horizontal pixel offset of the credits.
- */
- x: -10,
- /**
- * Vertical alignment of the credits.
- *
- * @type {Highcharts.VerticalAlignType}
- */
- verticalAlign: 'bottom',
- /**
- * Vertical pixel offset of the credits.
- */
- y: -5
- },
- /**
- * CSS styles for the credits label.
- *
- * @see In styled mode, credits styles can be set with the
- * `.highcharts-credits` class.
- *
- * @type {Highcharts.CSSObject}
- * @default {"cursor": "pointer", "color": "#999999", "fontSize": "10px"}
- */
- style: {
- /**
- * @ignore
- */
- cursor: 'pointer',
- /**
- * @ignore
- */
- color: '#999999',
- /**
- * @ignore
- */
- fontSize: '9px'
- },
- /**
- * The text for the credits label.
- *
- * @productdesc {highmaps}
- * If a map is loaded as GeoJSON, the text defaults to
- * `Highcharts @ {map-credits}`. Otherwise, it defaults to
- * `Highcharts.com`.
- *
- * @sample {highcharts} highcharts/credits/href/
- * Custom URL and text
- * @sample {highmaps} maps/credits/customized/
- * Custom URL and text
- */
- text: 'Highcharts.com'
- }
- };
- /**
- * Merge the default options with custom options and return the new options
- * structure. Commonly used for defining reusable templates.
- *
- * @sample highcharts/global/useutc-false Setting a global option
- * @sample highcharts/members/setoptions Applying a global theme
- *
- * @function Highcharts.setOptions
- *
- * @param {Highcharts.Options} options
- * The new custom chart options.
- *
- * @return {Highcharts.Options}
- * Updated options.
- */
- H.setOptions = function (options) {
- // Copy in the default options
- H.defaultOptions = merge(true, H.defaultOptions, options);
- // Update the time object
- H.time.update(
- merge(H.defaultOptions.global, H.defaultOptions.time),
- false
- );
- return H.defaultOptions;
- };
- /**
- * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
- * for outside modules wasn't enough because the setOptions method created a new
- * object.
- *
- * @function Highcharts.getOptions
- *
- * @return {Highcharts.Options}
- */
- H.getOptions = function () {
- return H.defaultOptions;
- };
- // Series defaults
- H.defaultPlotOptions = H.defaultOptions.plotOptions;
- /**
- * Global `Time` object with default options. Since v6.0.5, time settings can be
- * applied individually for each chart. If no individual settings apply, this
- * `Time` object is shared by all instances.
- *
- * @name Highcharts.time
- * @type {Highcharts.Time}
- */
- H.time = new H.Time(merge(H.defaultOptions.global, H.defaultOptions.time));
- /**
- * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
- * human readable date string. The format is a subset of the formats for PHP's
- * [strftime](http://www.php.net/manual/en/function.strftime.php) function.
- * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
- *
- * Since v6.0.5, all internal dates are formatted through the
- * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
- * The `Highcharts.dateFormat` function only reflects global time settings set
- * with `setOptions`.
- *
- * @function Highcharts.dateFormat
- *
- * @param {string} format
- * The desired format where various time representations are prefixed
- * with `%`.
- *
- * @param {number} timestamp
- * The JavaScript timestamp.
- *
- * @param {boolean} [capitalize=false]
- * Upper case first letter in the return.
- *
- * @return {string}
- * The formatted date.
- */
- H.dateFormat = function (format, timestamp, capitalize) {
- return H.time.dateFormat(format, timestamp, capitalize);
- };
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var correctFloat = H.correctFloat,
- defined = H.defined,
- destroyObjectProperties = H.destroyObjectProperties,
- fireEvent = H.fireEvent,
- isNumber = H.isNumber,
- merge = H.merge,
- pick = H.pick,
- deg2rad = H.deg2rad;
- /**
- * The Tick class.
- *
- * @private
- * @class
- * @name Highcharts.Tick
- *
- * @param {Highcharts.Axis} axis
- *
- * @param {number} pos The position of the tick on the axis.
- *
- * @param {string} [type] The type of tick.
- *
- * @param {boolean} [noLabel=false] Wether to disable the label or not. Defaults to
- * false.
- *
- * @param {object} [parameters] Optional parameters for the tick.
- *
- * @param {object} [parameters.tickmarkOffset] Set tickmarkOffset for the tick.
- *
- * @param {object} [parameters.category] Set category for the tick.
- */
- H.Tick = function (axis, pos, type, noLabel, parameters) {
- this.axis = axis;
- this.pos = pos;
- this.type = type || '';
- this.isNew = true;
- this.isNewLabel = true;
- this.parameters = parameters || {};
- // Usually undefined, numeric for grid axes
- this.tickmarkOffset = this.parameters.tickmarkOffset;
- this.options = this.parameters.options;
- if (!type && !noLabel) {
- this.addLabel();
- }
- };
- /** @lends Highcharts.Tick.prototype */
- H.Tick.prototype = {
- /**
- * Write the tick label.
- *
- * @private
- * @function Highcharts.Tick#addLabel
- */
- addLabel: function () {
- var tick = this,
- axis = tick.axis,
- options = axis.options,
- chart = axis.chart,
- categories = axis.categories,
- names = axis.names,
- pos = tick.pos,
- labelOptions = pick(
- tick.options && tick.options.labels,
- options.labels
- ),
- str,
- tickPositions = axis.tickPositions,
- isFirst = pos === tickPositions[0],
- isLast = pos === tickPositions[tickPositions.length - 1],
- value = this.parameters.category || (
- categories ?
- pick(categories[pos], names[pos], pos) :
- pos
- ),
- label = tick.label,
- tickPositionInfo = tickPositions.info,
- dateTimeLabelFormat,
- dateTimeLabelFormats,
- i,
- list;
- // Set the datetime label format. If a higher rank is set for this
- // position, use that. If not, use the general format.
- if (axis.isDatetimeAxis && tickPositionInfo) {
- dateTimeLabelFormats = chart.time.resolveDTLFormat(
- options.dateTimeLabelFormats[
- (
- !options.grid &&
- tickPositionInfo.higherRanks[pos]
- ) ||
- tickPositionInfo.unitName
- ]
- );
- dateTimeLabelFormat = dateTimeLabelFormats.main;
- }
- // set properties for access in render method
- tick.isFirst = isFirst;
- tick.isLast = isLast;
- // Get the string
- tick.formatCtx = {
- axis: axis,
- chart: chart,
- isFirst: isFirst,
- isLast: isLast,
- dateTimeLabelFormat: dateTimeLabelFormat,
- tickPositionInfo: tickPositionInfo,
- value: axis.isLog ? correctFloat(axis.lin2log(value)) : value,
- pos: pos
- };
- str = axis.labelFormatter.call(tick.formatCtx, this.formatCtx);
- // Set up conditional formatting based on the format list if existing.
- list = dateTimeLabelFormats && dateTimeLabelFormats.list;
- if (list) {
- tick.shortenLabel = function () {
- for (i = 0; i < list.length; i++) {
- label.attr({
- text: axis.labelFormatter.call(H.extend(
- tick.formatCtx,
- { dateTimeLabelFormat: list[i] }
- ))
- });
- if (
- label.getBBox().width <
- axis.getSlotWidth(tick) - 2 *
- pick(labelOptions.padding, 5)
- ) {
- return;
- }
- }
- label.attr({
- text: ''
- });
- };
- }
- // first call
- if (!defined(label)) {
- tick.label = label =
- defined(str) && labelOptions.enabled ?
- chart.renderer
- .text(
- str,
- 0,
- 0,
- labelOptions.useHTML
- )
- .add(axis.labelGroup) :
- null;
- // Un-rotated length
- if (label) {
- // Without position absolute, IE export sometimes is wrong
- if (!chart.styledMode) {
- label.css(merge(labelOptions.style));
- }
- label.textPxLength = label.getBBox().width;
- }
- // Base value to detect change for new calls to getBBox
- tick.rotation = 0;
- // update
- } else if (label && label.textStr !== str) {
- // When resetting text, also reset the width if dynamically set
- // (#8809)
- if (
- label.textWidth &&
- !(labelOptions.style && labelOptions.style.width) &&
- !label.styles.width
- ) {
- label.css({ width: null });
- }
- label.attr({ text: str });
- }
- },
- /**
- * Get the offset height or width of the label
- *
- * @private
- * @function Highcharts.Tick#getLabelSize
- *
- * @return {number}
- */
- getLabelSize: function () {
- return this.label ?
- this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
- 0;
- },
- /**
- * Handle the label overflow by adjusting the labels to the left and right
- * edge, or hide them if they collide into the neighbour label.
- *
- * @private
- * @function Highcharts.Tick#handleOverflow
- *
- * @param {Highcharts.PositionObject} xy
- */
- handleOverflow: function (xy) {
- var tick = this,
- axis = this.axis,
- labelOptions = axis.options.labels,
- pxPos = xy.x,
- chartWidth = axis.chart.chartWidth,
- spacing = axis.chart.spacing,
- leftBound = pick(axis.labelLeft, Math.min(axis.pos, spacing[3])),
- rightBound = pick(
- axis.labelRight,
- Math.max(
- !axis.isRadial ? axis.pos + axis.len : 0,
- chartWidth - spacing[1]
- )
- ),
- label = this.label,
- rotation = this.rotation,
- factor = { left: 0, center: 0.5, right: 1 }[
- axis.labelAlign || label.attr('align')
- ],
- labelWidth = label.getBBox().width,
- slotWidth = axis.getSlotWidth(tick),
- modifiedSlotWidth = slotWidth,
- xCorrection = factor,
- goRight = 1,
- leftPos,
- rightPos,
- textWidth,
- css = {};
- // Check if the label overshoots the chart spacing box. If it does, move
- // it. If it now overshoots the slotWidth, add ellipsis.
- if (!rotation && pick(labelOptions.overflow, 'justify') === 'justify') {
- leftPos = pxPos - factor * labelWidth;
- rightPos = pxPos + (1 - factor) * labelWidth;
- if (leftPos < leftBound) {
- modifiedSlotWidth =
- xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
- } else if (rightPos > rightBound) {
- modifiedSlotWidth =
- rightBound - xy.x + modifiedSlotWidth * factor;
- goRight = -1;
- }
- modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
- if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
- xy.x += (
- goRight *
- (
- slotWidth -
- modifiedSlotWidth -
- xCorrection * (
- slotWidth - Math.min(labelWidth, modifiedSlotWidth)
- )
- )
- );
- }
- // If the label width exceeds the available space, set a text width
- // to be picked up below. Also, if a width has been set before, we
- // need to set a new one because the reported labelWidth will be
- // limited by the box (#3938).
- if (
- labelWidth > modifiedSlotWidth ||
- (axis.autoRotation && (label.styles || {}).width)
- ) {
- textWidth = modifiedSlotWidth;
- }
- // Add ellipsis to prevent rotated labels to be clipped against the edge
- // of the chart
- } else if (rotation < 0 && pxPos - factor * labelWidth < leftBound) {
- textWidth = Math.round(
- pxPos / Math.cos(rotation * deg2rad) - leftBound
- );
- } else if (rotation > 0 && pxPos + factor * labelWidth > rightBound) {
- textWidth = Math.round(
- (chartWidth - pxPos) / Math.cos(rotation * deg2rad)
- );
- }
- if (textWidth) {
- if (tick.shortenLabel) {
- tick.shortenLabel();
- } else {
- css.width = Math.floor(textWidth);
- if (!(labelOptions.style || {}).textOverflow) {
- css.textOverflow = 'ellipsis';
- }
- label.css(css);
- }
- }
- },
- /**
- * Get the x and y position for ticks and labels
- *
- * @private
- * @function Highcharts.Tick#getPosition
- *
- * @param {boolean} horiz
- *
- * @param {number} tickPos
- *
- * @param {number} tickmarkOffset
- *
- * @param {boolean} [old]
- *
- * @return {number}
- *
- * @fires Highcharts.Tick#event:afterGetPosition
- */
- getPosition: function (horiz, tickPos, tickmarkOffset, old) {
- var axis = this.axis,
- chart = axis.chart,
- cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
- pos;
- pos = {
- x: horiz ?
- H.correctFloat(
- axis.translate(tickPos + tickmarkOffset, null, null, old) +
- axis.transB
- ) :
- (
- axis.left +
- axis.offset +
- (
- axis.opposite ?
- (
- (
- (old && chart.oldChartWidth) ||
- chart.chartWidth
- ) -
- axis.right -
- axis.left
- ) :
- 0
- )
- ),
- y: horiz ?
- (
- cHeight -
- axis.bottom +
- axis.offset -
- (axis.opposite ? axis.height : 0)
- ) :
- H.correctFloat(
- cHeight -
- axis.translate(tickPos + tickmarkOffset, null, null, old) -
- axis.transB
- )
- };
- fireEvent(this, 'afterGetPosition', { pos: pos });
- return pos;
- },
- /**
- * Get the x, y position of the tick label
- *
- * @private
- *
- */
- getLabelPosition: function (
- x,
- y,
- label,
- horiz,
- labelOptions,
- tickmarkOffset,
- index,
- step
- ) {
- var axis = this.axis,
- transA = axis.transA,
- reversed = axis.reversed,
- staggerLines = axis.staggerLines,
- rotCorr = axis.tickRotCorr || { x: 0, y: 0 },
- yOffset = labelOptions.y,
- // Adjust for label alignment if we use reserveSpace: true (#5286)
- labelOffsetCorrection = (
- !horiz && !axis.reserveSpaceDefault ?
- -axis.labelOffset * (
- axis.labelAlign === 'center' ? 0.5 : 1
- ) :
- 0
- ),
- line,
- pos = {};
- if (!defined(yOffset)) {
- if (axis.side === 0) {
- yOffset = label.rotation ? -8 : -label.getBBox().height;
- } else if (axis.side === 2) {
- yOffset = rotCorr.y + 8;
- } else {
- // #3140, #3140
- yOffset = Math.cos(label.rotation * deg2rad) *
- (rotCorr.y - label.getBBox(false, 0).height / 2);
- }
- }
- x = x +
- labelOptions.x +
- labelOffsetCorrection +
- rotCorr.x -
- (
- tickmarkOffset && horiz ?
- tickmarkOffset * transA * (reversed ? -1 : 1) :
- 0
- );
- y = y + yOffset - (tickmarkOffset && !horiz ?
- tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
- // Correct for staggered labels
- if (staggerLines) {
- line = (index / (step || 1) % staggerLines);
- if (axis.opposite) {
- line = staggerLines - line - 1;
- }
- y += line * (axis.labelOffset / staggerLines);
- }
- pos.x = x;
- pos.y = Math.round(y);
- fireEvent(
- this,
- 'afterGetLabelPosition',
- { pos: pos, tickmarkOffset: tickmarkOffset, index: index }
- );
- return pos;
- },
- /**
- * Extendible method to return the path of the marker
- *
- * @private
- *
- */
- getMarkPath: function (x, y, tickLength, tickWidth, horiz, renderer) {
- return renderer.crispLine([
- 'M',
- x,
- y,
- 'L',
- x + (horiz ? 0 : -tickLength),
- y + (horiz ? tickLength : 0)
- ], tickWidth);
- },
- /**
- * Renders the gridLine.
- *
- * @private
- *
- * @param {Boolean} old Whether or not the tick is old
- * @param {number} opacity The opacity of the grid line
- * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
- * @return {undefined}
- */
- renderGridLine: function (old, opacity, reverseCrisp) {
- var tick = this,
- axis = tick.axis,
- options = axis.options,
- gridLine = tick.gridLine,
- gridLinePath,
- attribs = {},
- pos = tick.pos,
- type = tick.type,
- tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset),
- renderer = axis.chart.renderer,
- gridPrefix = type ? type + 'Grid' : 'grid',
- gridLineWidth = options[gridPrefix + 'LineWidth'],
- gridLineColor = options[gridPrefix + 'LineColor'],
- dashStyle = options[gridPrefix + 'LineDashStyle'];
- if (!gridLine) {
- if (!axis.chart.styledMode) {
- attribs.stroke = gridLineColor;
- attribs['stroke-width'] = gridLineWidth;
- if (dashStyle) {
- attribs.dashstyle = dashStyle;
- }
- }
- if (!type) {
- attribs.zIndex = 1;
- }
- if (old) {
- opacity = 0;
- }
- tick.gridLine = gridLine = renderer.path()
- .attr(attribs)
- .addClass(
- 'highcharts-' + (type ? type + '-' : '') + 'grid-line'
- )
- .add(axis.gridGroup);
- }
- if (gridLine) {
- gridLinePath = axis.getPlotLinePath(
- pos + tickmarkOffset,
- gridLine.strokeWidth() * reverseCrisp,
- old,
- 'pass'
- );
- // If the parameter 'old' is set, the current call will be followed
- // by another call, therefore do not do any animations this time
- if (gridLinePath) {
- gridLine[old || tick.isNew ? 'attr' : 'animate']({
- d: gridLinePath,
- opacity: opacity
- });
- }
- }
- },
- /**
- * Renders the tick mark.
- *
- * @private
- *
- * @param {Object} xy The position vector of the mark
- * @param {number} xy.x The x position of the mark
- * @param {number} xy.y The y position of the mark
- * @param {number} opacity The opacity of the mark
- * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
- * @return {undefined}
- */
- renderMark: function (xy, opacity, reverseCrisp) {
- var tick = this,
- axis = tick.axis,
- options = axis.options,
- renderer = axis.chart.renderer,
- type = tick.type,
- tickPrefix = type ? type + 'Tick' : 'tick',
- tickSize = axis.tickSize(tickPrefix),
- mark = tick.mark,
- isNewMark = !mark,
- x = xy.x,
- y = xy.y,
- tickWidth = pick(
- options[tickPrefix + 'Width'],
- !type && axis.isXAxis ? 1 : 0
- ), // X axis defaults to 1
- tickColor = options[tickPrefix + 'Color'];
- if (tickSize) {
- // negate the length
- if (axis.opposite) {
- tickSize[0] = -tickSize[0];
- }
- // First time, create it
- if (isNewMark) {
- tick.mark = mark = renderer.path()
- .addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
- .add(axis.axisGroup);
- if (!axis.chart.styledMode) {
- mark.attr({
- stroke: tickColor,
- 'stroke-width': tickWidth
- });
- }
- }
- mark[isNewMark ? 'attr' : 'animate']({
- d: tick.getMarkPath(
- x,
- y,
- tickSize[0],
- mark.strokeWidth() * reverseCrisp,
- axis.horiz,
- renderer
- ),
- opacity: opacity
- });
- }
- },
- /**
- * Renders the tick label.
- * Note: The label should already be created in init(), so it should only
- * have to be moved into place.
- *
- * @private
- *
- * @param {Object} xy The position vector of the label
- * @param {number} xy.x The x position of the label
- * @param {number} xy.y The y position of the label
- * @param {Boolean} old Whether or not the tick is old
- * @param {number} opacity The opacity of the label
- * @param {number} index The index of the tick
- * @return {undefined}
- */
- renderLabel: function (xy, old, opacity, index) {
- var tick = this,
- axis = tick.axis,
- horiz = axis.horiz,
- options = axis.options,
- label = tick.label,
- labelOptions = options.labels,
- step = labelOptions.step,
- tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset),
- show = true,
- x = xy.x,
- y = xy.y;
- if (label && isNumber(x)) {
- label.xy = xy = tick.getLabelPosition(
- x,
- y,
- label,
- horiz,
- labelOptions,
- tickmarkOffset,
- index,
- step
- );
- // Apply show first and show last. If the tick is both first and
- // last, it is a single centered tick, in which case we show the
- // label anyway (#2100).
- if (
- (
- tick.isFirst &&
- !tick.isLast &&
- !pick(options.showFirstLabel, 1)
- ) ||
- (
- tick.isLast &&
- !tick.isFirst &&
- !pick(options.showLastLabel, 1)
- )
- ) {
- show = false;
- // Handle label overflow and show or hide accordingly
- } else if (
- horiz &&
- !labelOptions.step &&
- !labelOptions.rotation &&
- !old &&
- opacity !== 0
- ) {
- tick.handleOverflow(xy);
- }
- // apply step
- if (step && index % step) {
- // show those indices dividable by step
- show = false;
- }
- // Set the new position, and show or hide
- if (show && isNumber(xy.y)) {
- xy.opacity = opacity;
- label[tick.isNewLabel ? 'attr' : 'animate'](xy);
- tick.isNewLabel = false;
- } else {
- label.attr('y', -9999); // #1338
- tick.isNewLabel = true;
- }
- }
- },
- /**
- * Put everything in place
- *
- * @private
- *
- * @param index {Number}
- * @param old {Boolean} Use old coordinates to prepare an animation into new
- * position
- */
- render: function (index, old, opacity) {
- var tick = this,
- axis = tick.axis,
- horiz = axis.horiz,
- pos = tick.pos,
- tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset),
- xy = tick.getPosition(horiz, pos, tickmarkOffset, old),
- x = xy.x,
- y = xy.y,
- reverseCrisp = ((horiz && x === axis.pos + axis.len) ||
- (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
- opacity = pick(opacity, 1);
- this.isActive = true;
- // Create the grid line
- this.renderGridLine(old, opacity, reverseCrisp);
- // create the tick mark
- this.renderMark(xy, opacity, reverseCrisp);
- // the label is created on init - now move it into place
- this.renderLabel(xy, old, opacity, index);
- tick.isNew = false;
- H.fireEvent(this, 'afterRender');
- },
- /**
- * Destructor for the tick prototype
- *
- * @private
- * @function Highcharts.Tick#destroy
- */
- destroy: function () {
- destroyObjectProperties(this, this.axis);
- }
- };
- }(Highcharts));
- var Axis = (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * @callback Highcharts.AxisEventCallbackFunction
- *
- * @param {Highcharts.Axis} this
- */
- /**
- * Options for crosshairs on axes.
- *
- * @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
- */
- /**
- * @interface Highcharts.AxisLabelsFormatterContextObject
- *//**
- * @name Highcharts.AxisLabelsFormatterContextObject#axis
- * @type {Highcharts.Axis}
- *//**
- * @name Highcharts.AxisLabelsFormatterContextObject#chart
- * @type {Highcharts.Chart}
- *//**
- * @name Highcharts.AxisLabelsFormatterContextObject#isFirst
- * @type {boolean}
- *//**
- * @name Highcharts.AxisLabelsFormatterContextObject#isLast
- * @type {boolean}
- *//**
- * @name Highcharts.AxisLabelsFormatterContextObject#value
- * @type {number}
- */
- /**
- * Options for axes.
- *
- * @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
- */
- /**
- * The returned object literal from the {@link Highcharts.Axis#getExtremes}
- * function.
- *
- * @interface Highcharts.ExtremesObject
- *//**
- * The maximum value of the axis' associated series.
- * @name Highcharts.ExtremesObject#dataMax
- * @type {number}
- *//**
- * The minimum value of the axis' associated series.
- * @name Highcharts.ExtremesObject#dataMin
- * @type {number}
- *//**
- * The maximum axis value, either automatic or set manually. If the `max` option
- * is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
- * the same as `dataMax`.
- * @name Highcharts.ExtremesObject#max
- * @type {number}
- *//**
- * The minimum axis value, either automatic or set manually. If the `min` option
- * is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
- * the same as `dataMin`.
- * @name Highcharts.ExtremesObject#min
- * @type {number}
- *//**
- * The user defined maximum, either from the `max` option or from a zoom or
- * `setExtremes` action.
- * @name Highcharts.ExtremesObject#userMax
- * @type {number}
- *//**
- * The user defined minimum, either from the `min` option or from a zoom or
- * `setExtremes` action.
- * @name Highcharts.ExtremesObject#userMin
- * @type {number}
- */
- /**
- * @callback Highcharts.AxisPointBreakEventCallbackFunction
- *
- * @param {Highcharts.Axis} this
- *
- * @param {Highcharts.AxisPointBreakEventObject} event
- */
- /**
- * @interface Highcharts.AxisPointBreakEventObject
- *//**
- * @name Highcharts.AxisPointBreakEventObject#brk
- * @type {Highcharts.Dictionary<number>}
- *//**
- * @name Highcharts.AxisPointBreakEventObject#point
- * @type {Highcharts.Point}
- *//**
- * @name Highcharts.AxisPointBreakEventObject#preventDefault
- * @type {Function}
- *//**
- * @name Highcharts.AxisPointBreakEventObject#target
- * @type {Highcharts.SVGElement}
- *//**
- * @name Highcharts.AxisPointBreakEventObject#type
- * @type {"pointBreak"|"pointInBreak"}
- */
- /**
- * @callback Highcharts.AxisSetExtremesEventCallbackFunction
- *
- * @param {Highcharts.Axis} this
- *
- * @param {Highcharts.AxisSetExtremesEventObject} event
- */
- /**
- * @interface Highcharts.AxisSetExtremesEventObject
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#dataMax
- * @type {number}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#dataMin
- * @type {number}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#max
- * @type {number}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#min
- * @type {number}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#preventDefault
- * @type {Function}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#target
- * @type {Highcharts.SVGElement}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#trigger
- * @type {string}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#type
- * @type {"setExtremes"}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#userMax
- * @type {number}
- *//**
- * @name Highcharts.AxisSetExtremesEventObject#userMin
- * @type {number}
- */
- /**
- * @callback Highcharts.AxisTickPositionerCallbackFunction
- *
- * @param {Highcharts.Axis} this
- *
- * @return {Array<number>}
- */
- var addEvent = H.addEvent,
- animObject = H.animObject,
- arrayMax = H.arrayMax,
- arrayMin = H.arrayMin,
- color = H.color,
- correctFloat = H.correctFloat,
- defaultOptions = H.defaultOptions,
- defined = H.defined,
- deg2rad = H.deg2rad,
- destroyObjectProperties = H.destroyObjectProperties,
- extend = H.extend,
- fireEvent = H.fireEvent,
- format = H.format,
- getMagnitude = H.getMagnitude,
- isArray = H.isArray,
- isNumber = H.isNumber,
- isString = H.isString,
- merge = H.merge,
- normalizeTickInterval = H.normalizeTickInterval,
- objectEach = H.objectEach,
- pick = H.pick,
- removeEvent = H.removeEvent,
- splat = H.splat,
- syncTimeout = H.syncTimeout,
- Tick = H.Tick;
- /**
- * Create a new axis object. Called internally when instanciating a new chart or
- * adding axes by {@link Highcharts.Chart#addAxis}.
- *
- * A chart can have from 0 axes (pie chart) to multiples. In a normal, single
- * series cartesian chart, there is one X axis and one Y axis.
- *
- * The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
- * an array of Axis objects. If there is only one axis, it can be referenced
- * through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
- * pattern goes for Y axes.
- *
- * If you need to get the axes from a series object, use the `series.xAxis` and
- * `series.yAxis` properties. These are not arrays, as one series can only be
- * associated to one X and one Y axis.
- *
- * A third way to reference the axis programmatically is by `id`. Add an `id` in
- * the axis configuration options, and get the axis by
- * {@link Highcharts.Chart#get}.
- *
- * Configuration options for the axes are given in options.xAxis and
- * options.yAxis.
- *
- * @class
- * @name Highcharts.Axis
- *
- * @param {Highcharts.Chart} chart
- * The Chart instance to apply the axis on.
- *
- * @param {Highcharts.AxisOptions} options
- * Axis options.
- */
- var Axis = function () {
- this.init.apply(this, arguments);
- };
- H.extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */{
- /**
- * The X axis or category axis. Normally this is the horizontal axis,
- * though if the chart is inverted this is the vertical axis. In case of
- * multiple axes, the xAxis node is an array of configuration objects.
- *
- * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
- * access to the axis.
- *
- * @productdesc {highmaps}
- * In Highmaps, the axis is hidden, but it is used behind the scenes to
- * control features like zooming and panning. Zooming is in effect the same
- * as setting the extremes of one of the exes.
- *
- * @type {*|Array<*>}
- * @optionparent xAxis
- */
- defaultOptions: {
- /**
- * When using multiple axis, the ticks of two or more opposite axes
- * will automatically be aligned by adding ticks to the axis or axes
- * with the least ticks, as if `tickAmount` were specified.
- *
- * This can be prevented by setting `alignTicks` to false. If the grid
- * lines look messy, it's a good idea to hide them for the secondary
- * axis by setting `gridLineWidth` to 0.
- *
- * If `startOnTick` or `endOnTick` in an Axis options are set to false,
- * then the `alignTicks ` will be disabled for the Axis.
- *
- * Disabled for logarithmic axes.
- *
- * @type {boolean}
- * @default true
- * @product highcharts highstock gantt
- * @apioption xAxis.alignTicks
- */
- /**
- * Whether to allow decimals in this axis' ticks. When counting
- * integers, like persons or hits on a web page, decimals should
- * be avoided in the labels.
- *
- * @see [minTickInterval](#xAxis.minTickInterval)
- *
- * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
- * True by default
- * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
- * False
- *
- * @type {boolean}
- * @default true
- * @since 2.0
- * @apioption xAxis.allowDecimals
- */
- /**
- * When using an alternate grid color, a band is painted across the
- * plot area between every other grid line.
- *
- * @sample {highcharts} highcharts/yaxis/alternategridcolor/
- * Alternate grid color on the Y axis
- * @sample {highstock} stock/xaxis/alternategridcolor/
- * Alternate grid color on the Y axis
- *
- * @type {Highcharts.ColorString}
- * @apioption xAxis.alternateGridColor
- */
- /**
- * An array defining breaks in the axis, the sections defined will be
- * left out and all the points shifted closer to each other.
- *
- * @productdesc {highcharts}
- * Requires that the broken-axis.js module is loaded.
- *
- * @sample {highcharts} highcharts/axisbreak/break-simple/
- * Simple break
- * @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
- * Advanced with callback
- * @sample {highstock} stock/demo/intraday-breaks/
- * Break on nights and weekends
- *
- * @type {Array<*>}
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption xAxis.breaks
- */
- /**
- * A number indicating how much space should be left between the start
- * and the end of the break. The break size is given in axis units,
- * so for instance on a `datetime` axis, a break size of 3600000 would
- * indicate the equivalent of an hour.
- *
- * @type {number}
- * @default 0
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption xAxis.breaks.breakSize
- */
- /**
- * The point where the break starts.
- *
- * @type {number}
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption xAxis.breaks.from
- */
- /**
- * Defines an interval after which the break appears again. By default
- * the breaks do not repeat.
- *
- * @type {number}
- * @default 0
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption xAxis.breaks.repeat
- */
- /**
- * The point where the break ends.
- *
- * @type {number}
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption xAxis.breaks.to
- */
- /**
- * If categories are present for the xAxis, names are used instead of
- * numbers for that axis. Since Highcharts 3.0, categories can also
- * be extracted by giving each point a [name](#series.data) and setting
- * axis [type](#xAxis.type) to `category`. However, if you have multiple
- * series, best practice remains defining the `categories` array.
- *
- * Example:
- *
- * <pre>categories: ['Apples', 'Bananas', 'Oranges']</pre>
- *
- * @sample {highcharts} highcharts/demo/line-labels/
- * With
- * @sample {highcharts} highcharts/xaxis/categories/
- * Without
- *
- * @type {Array<string>}
- * @product highcharts gantt
- * @apioption xAxis.categories
- */
- /**
- * The highest allowed value for automatically computed axis extremes.
- *
- * @see [floor](#xAxis.floor)
- *
- * @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
- * Floor and ceiling
- *
- * @type {number}
- * @since 4.0
- * @product highcharts highstock gantt
- * @apioption xAxis.ceiling
- */
- /**
- * A class name that opens for styling the axis by CSS, especially in
- * Highcharts styled mode. The class name is applied to group elements
- * for the grid, axis elements and labels.
- *
- * @sample {highcharts|highstock|highmaps} highcharts/css/axis/
- * Multiple axes with separate styling
- *
- * @type {string}
- * @since 5.0.0
- * @apioption xAxis.className
- */
- /**
- * Configure a crosshair that follows either the mouse pointer or the
- * hovered point.
- *
- * In styled mode, the crosshairs are styled in the
- * `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
- * `.highcharts-xaxis-category` classes.
- *
- * @productdesc {highstock}
- * In Highstock, by default, the crosshair is enabled on the X axis and
- * disabled on the Y axis.
- *
- * @sample {highcharts} highcharts/xaxis/crosshair-both/
- * Crosshair on both axes
- * @sample {highstock} stock/xaxis/crosshairs-xy/
- * Crosshair on both axes
- * @sample {highmaps} highcharts/xaxis/crosshair-both/
- * Crosshair on both axes
- *
- * @type {boolean|*}
- * @default false
- * @since 4.1
- * @apioption xAxis.crosshair
- */
- /**
- * A class name for the crosshair, especially as a hook for styling.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption xAxis.crosshair.className
- */
- /**
- * The color of the crosshair. Defaults to `#cccccc` for numeric and
- * datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
- * the crosshair by default highlights the whole category.
- *
- * @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
- * Customized crosshairs
- *
- * @type {Highcharts.ColorString}
- * @default #cccccc
- * @since 4.1
- * @apioption xAxis.crosshair.color
- */
- /**
- * The dash style for the crosshair. See
- * [series.dashStyle](#plotOptions.series.dashStyle)
- * for possible values.
- *
- * @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
- * Dotted crosshair
- * @sample {highstock} stock/xaxis/crosshair-dashed/
- * Dashed X axis crosshair
- *
- * @type {Highcharts.DashStyleType}
- * @default Solid
- * @since 4.1
- * @apioption xAxis.crosshair.dashStyle
- */
- /**
- * A label on the axis next to the crosshair.
- *
- * In styled mode, the label is styled with the
- * `.highcharts-crosshair-label` class.
- *
- * @sample {highstock} stock/xaxis/crosshair-label/
- * Crosshair labels
- * @sample {highstock} highcharts/css/crosshair-label/
- * Style mode
- *
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label
- */
- /**
- * Alignment of the label compared to the axis. Defaults to `left` for
- * right-side axes, `right` for left-side axes and `center` for
- * horizontal axes.
- *
- * @type {string}
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.align
- */
- /**
- * The background color for the label. Defaults to the related series
- * color, or `#666666` if that is not available.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.backgroundColor
- */
- /**
- * The border color for the crosshair label
- *
- * @type {Highcharts.ColorString}
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.borderColor
- */
- /**
- * The border corner radius of the crosshair label.
- *
- * @type {number}
- * @default 3
- * @since 2.1.10
- * @product highstock
- * @apioption xAxis.crosshair.label.borderRadius
- */
- /**
- * The border width for the crosshair label.
- *
- * @type {number}
- * @default 0
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.borderWidth
- */
- /**
- * A format string for the crosshair label. Defaults to `{value}` for
- * numeric axes and `{value:%b %d, %Y}` for datetime axes.
- *
- * @type {string}
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.format
- */
- /**
- * Formatter function for the label text.
- *
- * @type {Highcharts.FormatterCallbackFunction<object>}
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.formatter
- */
- /**
- * Padding inside the crosshair label.
- *
- * @type {number}
- * @default 8
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.padding
- */
- /**
- * The shape to use for the label box.
- *
- * @type {string}
- * @default callout
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.shape
- */
- /**
- * Text styles for the crosshair label.
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
- * @since 2.1
- * @product highstock
- * @apioption xAxis.crosshair.label.style
- */
- /**
- * Whether the crosshair should snap to the point or follow the pointer
- * independent of points.
- *
- * @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
- * True by default
- * @sample {highmaps} maps/demo/latlon-advanced/
- * Snap is false
- *
- * @type {boolean}
- * @default true
- * @since 4.1
- * @apioption xAxis.crosshair.snap
- */
- /**
- * The pixel width of the crosshair. Defaults to 1 for numeric or
- * datetime axes, and for one category width for category axes.
- *
- * @sample {highcharts} highcharts/xaxis/crosshair-customized/
- * Customized crosshairs
- * @sample {highstock} highcharts/xaxis/crosshair-customized/
- * Customized crosshairs
- * @sample {highmaps} highcharts/xaxis/crosshair-customized/
- * Customized crosshairs
- *
- * @type {number}
- * @default 1
- * @since 4.1
- * @apioption xAxis.crosshair.width
- */
- /**
- * The Z index of the crosshair. Higher Z indices allow drawing the
- * crosshair on top of the series or behind the grid lines.
- *
- * @type {number}
- * @default 2
- * @since 4.1
- * @apioption xAxis.crosshair.zIndex
- */
- /**
- * For a datetime axis, the scale will automatically adjust to the
- * appropriate unit. This member gives the default string
- * representations used for each unit. For intermediate values,
- * different units may be used, for example the `day` unit can be used
- * on midnight and `hour` unit be used for intermediate values on the
- * same axis. For an overview of the replacement codes, see
- * [dateFormat](/class-reference/Highcharts#dateFormat). Defaults to:
- *
- * <pre>{
- * millisecond: '%H:%M:%S.%L',
- * second: '%H:%M:%S',
- * minute: '%H:%M',
- * hour: '%H:%M',
- * day: '%e. %b',
- * week: '%e. %b',
- * month: '%b \'%y',
- * year: '%Y'
- * }</pre>
- *
- * @sample {highcharts} highcharts/xaxis/datetimelabelformats/
- * Different day format on X axis
- * @sample {highstock} stock/xaxis/datetimelabelformats/
- * More information in x axis labels
- *
- * @product highcharts highstock gantt
- */
- dateTimeLabelFormats: {
- millisecond: {
- main: '%H:%M:%S.%L',
- range: false
- },
- second: {
- main: '%H:%M:%S',
- range: false
- },
- minute: {
- main: '%H:%M',
- range: false
- },
- hour: {
- main: '%H:%M',
- range: false
- },
- day: {
- main: '%e. %b'
- },
- week: {
- main: '%e. %b'
- },
- month: {
- main: '%b \'%y'
- },
- year: {
- main: '%Y'
- }
- },
- /**
- * _Requires Accessibility module_
- *
- * Description of the axis to screen reader users.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption xAxis.description
- */
- /**
- * Whether to force the axis to end on a tick. Use this option with
- * the `maxPadding` option to control the axis end.
- *
- * @productdesc {highstock}
- * In Highstock, `endOnTick` is always false when the navigator is
- * enabled, to prevent jumpy scrolling.
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * True by default
- * @sample {highcharts} highcharts/yaxis/endontick/
- * False
- * @sample {highstock} stock/demo/basic-line/
- * True by default
- * @sample {highstock} stock/xaxis/endontick/
- * False
- *
- * @since 1.2.0
- */
- endOnTick: false,
- /**
- * Event handlers for the axis.
- *
- * @type {*}
- * @apioption xAxis.events
- */
- /**
- * An event fired after the breaks have rendered.
- *
- * @see [breaks](#xAxis.breaks)
- *
- * @sample {highcharts} highcharts/axisbreak/break-event/
- * AfterBreak Event
- *
- * @type {Highcharts.AxisEventCallbackFunction}
- * @since 4.1.0
- * @product highcharts gantt
- * @apioption xAxis.events.afterBreaks
- */
- /**
- * As opposed to the `setExtremes` event, this event fires after the
- * final min and max values are computed and corrected for `minRange`.
- *
- * Fires when the minimum and maximum is set for the axis, either by
- * calling the `.setExtremes()` method or by selecting an area in the
- * chart. One parameter, `event`, is passed to the function, containing
- * common event information.
- *
- * The new user set minimum and maximum values can be found by
- * `event.min` and `event.max`. These reflect the axis minimum and
- * maximum in axis values. The actual data extremes are found in
- * `event.dataMin` and `event.dataMax`.
- *
- * @type {Highcharts.AxisEventCallbackFunction}
- * @since 2.3
- * @context Axis
- * @apioption xAxis.events.afterSetExtremes
- */
- /**
- * An event fired when a break from this axis occurs on a point.
- *
- * @see [breaks](#xAxis.breaks)
- *
- * @sample {highcharts} highcharts/axisbreak/break-visualized/
- * Visualization of a Break
- *
- * @type {Highcharts.AxisPointBreakEventCallbackFunction}
- * @since 4.1.0
- * @product highcharts gantt
- * @context Axis
- * @apioption xAxis.events.pointBreak
- */
- /**
- * An event fired when a point falls inside a break from this axis.
- *
- * @type {Highcharts.AxisPointBreakEventCallbackFunction}
- * @product highcharts highstock gantt
- * @context Axis
- * @apioption xAxis.events.pointInBreak
- */
- /**
- * Fires when the minimum and maximum is set for the axis, either by
- * calling the `.setExtremes()` method or by selecting an area in the
- * chart. One parameter, `event`, is passed to the function,
- * containing common event information.
- *
- * The new user set minimum and maximum values can be found by
- * `event.min` and `event.max`. These reflect the axis minimum and
- * maximum in data values. When an axis is zoomed all the way out from
- * the "Reset zoom" button, `event.min` and `event.max` are null, and
- * the new extremes are set based on `this.dataMin` and `this.dataMax`.
- *
- * @sample {highstock} stock/xaxis/events-setextremes/
- * Log new extremes on x axis
- *
- * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
- * @since 1.2.0
- * @context Axis
- * @apioption xAxis.events.setExtremes
- */
- /**
- * The lowest allowed value for automatically computed axis extremes.
- *
- * @see [ceiling](#yAxis.ceiling)
- *
- * @sample {highcharts} highcharts/yaxis/floor-ceiling/
- * Floor and ceiling
- * @sample {highstock} stock/demo/lazy-loading/
- * Prevent negative stock price on Y axis
- *
- * @type {number}
- * @since 4.0
- * @product highcharts highstock gantt
- * @apioption xAxis.floor
- */
- /**
- * The dash or dot style of the grid lines. For possible values, see
- * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
- *
- * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
- * Long dashes
- * @sample {highstock} stock/xaxis/gridlinedashstyle/
- * Long dashes
- *
- * @type {Highcharts.DashStyleType}
- * @default Solid
- * @since 1.2
- * @apioption xAxis.gridLineDashStyle
- */
- /**
- * The Z index of the grid lines.
- *
- * @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
- * A Z index of 4 renders the grid above the graph
- *
- * @type {number}
- * @default 1
- * @product highcharts highstock gantt
- * @apioption xAxis.gridZIndex
- */
- /**
- * An id for the axis. This can be used after render time to get
- * a pointer to the axis object through `chart.get()`.
- *
- * @sample {highcharts} highcharts/xaxis/id/
- * Get the object
- * @sample {highstock} stock/xaxis/id/
- * Get the object
- *
- * @type {string}
- * @since 1.2.0
- * @apioption xAxis.id
- */
- /**
- * The axis labels show the number or category for each tick.
- *
- * @productdesc {highmaps}
- * X and Y axis labels are by default disabled in Highmaps, but the
- * functionality is inherited from Highcharts and used on `colorAxis`,
- * and can be enabled on X and Y axes too.
- */
- labels: {
- /**
- * What part of the string the given position is anchored to.
- * If `left`, the left side of the string is at the axis position.
- * Can be one of `"left"`, `"center"` or `"right"`. Defaults to
- * an intelligent guess based on which side of the chart the axis
- * is on and the rotation of the label.
- *
- * @see [reserveSpace](#xAxis.labels.reserveSpace)
- *
- * @sample {highcharts} highcharts/xaxis/labels-align-left/
- * Left
- * @sample {highcharts} highcharts/xaxis/labels-align-right/
- * Right
- * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
- * Left-aligned labels on a vertical category axis
- *
- * @type {string}
- * @validvalue ["left", "center", "right"]
- * @apioption xAxis.labels.align
- */
- /**
- * For horizontal axes, the allowed degrees of label rotation
- * to prevent overlapping labels. If there is enough space,
- * labels are not rotated. As the chart gets narrower, it
- * will start rotating the labels -45 degrees, then remove
- * every second label and try again with rotations 0 and -45 etc.
- * Set it to `false` to disable rotation, which will
- * cause the labels to word-wrap if possible.
- *
- * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
- * Default auto rotation of 0 or -45
- * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
- * Custom graded auto rotation
- *
- * @type {Array<number>}
- * @default [-45]
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption xAxis.labels.autoRotation
- */
- /**
- * When each category width is more than this many pixels, we don't
- * apply auto rotation. Instead, we lay out the axis label with word
- * wrap. A lower limit makes sense when the label contains multiple
- * short words that don't extend the available horizontal space for
- * each label.
- *
- * @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
- * Lower limit
- *
- * @type {number}
- * @default 80
- * @since 4.1.5
- * @product highcharts gantt
- * @apioption xAxis.labels.autoRotationLimit
- */
- /**
- * Polar charts only. The label's pixel distance from the perimeter
- * of the plot area.
- *
- * @type {number}
- * @default 15
- * @product highcharts gantt
- * @apioption xAxis.labels.distance
- */
- /**
- * Enable or disable the axis labels.
- *
- * @sample {highcharts} highcharts/xaxis/labels-enabled/
- * X axis labels disabled
- * @sample {highstock} stock/xaxis/labels-enabled/
- * X axis labels disabled
- *
- * @default {highcharts|highstock|gantt} true
- * @default {highmaps} false
- */
- enabled: true,
- /**
- * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
- * for the axis label.
- *
- * @sample {highcharts|highstock} highcharts/yaxis/labels-format/
- * Add units to Y axis label
- *
- * @type {string}
- * @default {value}
- * @since 3.0
- * @apioption xAxis.labels.format
- */
- /**
- * Callback JavaScript function to format the label. The value
- * is given by `this.value`. Additional properties for `this` are
- * `axis`, `chart`, `isFirst` and `isLast`. The value of the default
- * label formatter can be retrieved by calling
- * `this.axis.defaultLabelFormatter.call(this)` within the function.
- *
- * Defaults to:
- *
- * <pre>function() {
- * return this.value;
- * }</pre>
- *
- * @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
- * Linked category names
- * @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
- * Modified numeric labels
- * @sample {highstock} stock/xaxis/labels-formatter/
- * Added units on Y axis
- *
- * @type {Highcharts.FormatterCallbackFunction<Highcharts.AxisLabelsFormatterContextObject>}
- * @apioption xAxis.labels.formatter
- */
- /**
- * The number of pixels to indent the labels per level in a treegrid
- * axis.
- *
- * @sample gantt/treegrid-axis/demo
- * Indentation 10px by default.
- * @sample gantt/treegrid-axis/indentation-0px
- * Indentation set to 0px.
- *
- * @product gantt
- */
- indentation: 10,
- /**
- * Horizontal axis only. When `staggerLines` is not set,
- * `maxStaggerLines` defines how many lines the axis is allowed to
- * add to automatically avoid overlapping X labels. Set to `1` to
- * disable overlap detection.
- *
- * @deprecated
- * @type {number}
- * @default 5
- * @since 1.3.3
- * @apioption xAxis.labels.maxStaggerLines
- */
- /**
- * How to handle overflowing labels on horizontal axis. If set to
- * `"allow"`, it will not be aligned at all. By default it
- * `"justify"` labels inside the chart area. If there is room to
- * move it, it will be aligned to the edge, else it will be removed.
- *
- * @type {boolean|string}
- * @default justify
- * @since 2.2.5
- * @validvalue ["allow", "justify"]
- * @apioption xAxis.labels.overflow
- */
- /**
- * The pixel padding for axis labels, to ensure white space between
- * them.
- *
- * @type {number}
- * @default 5
- * @product highcharts gantt
- * @apioption xAxis.labels.padding
- */
- /**
- * Whether to reserve space for the labels. By default, space is
- * reserved for the labels in these cases:
- *
- * * On all horizontal axes.
- * * On vertical axes if `label.align` is `right` on a left-side
- * axis or `left` on a right-side axis.
- * * On vertical axes if `label.align` is `center`.
- *
- * This can be turned off when for example the labels are rendered
- * inside the plot area instead of outside.
- *
- * @see [labels.align](#xAxis.labels.align)
- *
- * @sample {highcharts} highcharts/xaxis/labels-reservespace/
- * No reserved space, labels inside plot
- * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
- * Left-aligned labels on a vertical category axis
- *
- * @type {boolean}
- * @since 4.1.10
- * @product highcharts gantt
- * @apioption xAxis.labels.reserveSpace
- */
- /**
- * Rotation of the labels in degrees.
- *
- * @sample {highcharts} highcharts/xaxis/labels-rotation/
- * X axis labels rotated 90°
- *
- * @type {number}
- * @default 0
- * @apioption xAxis.labels.rotation
- */
- /**
- * Horizontal axes only. The number of lines to spread the labels
- * over to make room or tighter labels.
- *
- * @sample {highcharts} highcharts/xaxis/labels-staggerlines/
- * Show labels over two lines
- * @sample {highstock} stock/xaxis/labels-staggerlines/
- * Show labels over two lines
- *
- * @type {number}
- * @since 2.1
- * @apioption xAxis.labels.staggerLines
- */
- /**
- * To show only every _n_'th label on the axis, set the step to _n_.
- * Setting the step to 2 shows every other label.
- *
- * By default, the step is calculated automatically to avoid
- * overlap. To prevent this, set it to 1\. This usually only
- * happens on a category axis, and is often a sign that you have
- * chosen the wrong axis type.
- *
- * Read more at
- * [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
- * => What axis should I use?
- *
- * @sample {highcharts} highcharts/xaxis/labels-step/
- * Showing only every other axis label on a categorized
- * x-axis
- * @sample {highcharts} highcharts/xaxis/labels-step-auto/
- * Auto steps on a category axis
- *
- * @type {number}
- * @since 2.1
- * @apioption xAxis.labels.step
- */
- /**
- * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the labels.
- *
- * @type {boolean}
- * @default false
- * @apioption xAxis.labels.useHTML
- */
- /**
- * The x position offset of the label relative to the tick position
- * on the axis.
- *
- * @sample {highcharts} highcharts/xaxis/labels-x/
- * Y axis labels placed on grid lines
- */
- x: 0,
- /**
- * The y position offset of the label relative to the tick position
- * on the axis. The default makes it adapt to the font size on
- * bottom axis.
- *
- * @sample {highcharts} highcharts/xaxis/labels-x/
- * Y axis labels placed on grid lines
- *
- * @type {number}
- * @apioption xAxis.labels.y
- */
- /**
- * The Z index for the axis labels.
- *
- * @type {number}
- * @default 7
- * @apioption xAxis.labels.zIndex
- */
- /**
- * CSS styles for the label. Use `whiteSpace: 'nowrap'` to prevent
- * wrapping of category labels. Use `textOverflow: 'none'` to
- * prevent ellipsis (dots).
- *
- * In styled mode, the labels are styled with the
- * `.highcharts-axis-labels` class.
- *
- * @sample {highcharts} highcharts/xaxis/labels-style/
- * Red X axis labels
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#666666", "cursor": "default", "fontSize": "11px"}
- */
- style: {
- /** @ignore-option */
- color: '#666666',
- /** @ignore-option */
- cursor: 'default',
- /** @ignore-option */
- fontSize: '11px'
- }
- },
- /**
- * Index of another axis that this axis is linked to. When an axis is
- * linked to a master axis, it will take the same extremes as
- * the master, but as assigned by min or max or by setExtremes.
- * It can be used to show additional info, or to ease reading the
- * chart by duplicating the scales.
- *
- * @sample {highcharts} highcharts/xaxis/linkedto/
- * Different string formats of the same date
- * @sample {highcharts} highcharts/yaxis/linkedto/
- * Y values on both sides
- *
- * @type {number}
- * @since 2.0.2
- * @product highcharts highstock gantt
- * @apioption xAxis.linkedTo
- */
- /**
- * The maximum value of the axis. If `null`, the max value is
- * automatically calculated.
- *
- * If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
- * might be rounded up.
- *
- * If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
- * beyond the set max in order to reach the given number of ticks. The
- * same may happen in a chart with multiple axes, determined by [chart.
- * alignTicks](#chart), where a `tickAmount` is applied internally.
- *
- * @sample {highcharts} highcharts/yaxis/max-200/
- * Y axis max of 200
- * @sample {highcharts} highcharts/yaxis/max-logarithmic/
- * Y axis max on logarithmic axis
- * @sample {highstock} stock/xaxis/min-max/
- * Fixed min and max on X axis
- * @sample {highmaps} maps/axis/min-max/
- * Pre-zoomed to a specific area
- *
- * @type {number}
- * @apioption xAxis.max
- */
- /**
- * Padding of the max value relative to the length of the axis. A
- * padding of 0.05 will make a 100px axis 5px longer. This is useful
- * when you don't want the highest data value to appear on the edge
- * of the plot area. When the axis' `max` option is set or a max extreme
- * is set using `axis.setExtremes()`, the maxPadding will be ignored.
- *
- * @sample {highcharts} highcharts/yaxis/maxpadding/
- * Max padding of 0.25 on y axis
- * @sample {highstock} stock/xaxis/minpadding-maxpadding/
- * Greater min- and maxPadding
- * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
- * Add some padding
- *
- * @default {highcharts} 0.01
- * @default {highstock|highmaps} 0
- * @since 1.2.0
- */
- maxPadding: 0.01,
- /**
- * Deprecated. Use `minRange` instead.
- *
- * @deprecated
- * @type {number}
- * @product highcharts highstock
- * @apioption xAxis.maxZoom
- */
- /**
- * The minimum value of the axis. If `null` the min value is
- * automatically calculated.
- *
- * If the [startOnTick](#yAxis.startOnTick) option is true (default),
- * the `min` value might be rounded down.
- *
- * The automatically calculated minimum value is also affected by
- * [floor](#yAxis.floor), [softMin](#yAxis.softMin),
- * [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
- * as well as [series.threshold](#plotOptions.series.threshold)
- * and [series.softThreshold](#plotOptions.series.softThreshold).
- *
- * @sample {highcharts} highcharts/yaxis/min-startontick-false/
- * -50 with startOnTick to false
- * @sample {highcharts} highcharts/yaxis/min-startontick-true/
- * -50 with startOnTick true by default
- * @sample {highstock} stock/xaxis/min-max/
- * Set min and max on X axis
- * @sample {highmaps} maps/axis/min-max/
- * Pre-zoomed to a specific area
- *
- * @type {number}
- * @apioption xAxis.min
- */
- /**
- * The dash or dot style of the minor grid lines. For possible values,
- * see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
- *
- * @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
- * Long dashes on minor grid lines
- * @sample {highstock} stock/xaxis/minorgridlinedashstyle/
- * Long dashes on minor grid lines
- *
- * @type {Highcharts.DashStyleType}
- * @default Solid
- * @since 1.2
- * @apioption xAxis.minorGridLineDashStyle
- */
- /**
- * Specific tick interval in axis units for the minor ticks. On a linear
- * axis, if `"auto"`, the minor tick interval is calculated as a fifth
- * of the tickInterval. If `null` or `undefined`, minor ticks are not
- * shown.
- *
- * On logarithmic axes, the unit is the power of the value. For example,
- * setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
- * 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
- * between 1 and 10, 10 and 100 etc.
- *
- * If user settings dictate minor ticks to become too dense, they don't
- * make sense, and will be ignored to prevent performance problems.
- *
- * @sample {highcharts} highcharts/yaxis/minortickinterval-null/
- * Null by default
- * @sample {highcharts} highcharts/yaxis/minortickinterval-5/
- * 5 units
- * @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
- * "auto"
- * @sample {highcharts} highcharts/yaxis/minortickinterval-log/
- * 0.1
- * @sample {highstock} stock/demo/basic-line/
- * Null by default
- * @sample {highstock} stock/xaxis/minortickinterval-auto/
- * "auto"
- *
- * @type {number|string|null}
- * @apioption xAxis.minorTickInterval
- */
- /**
- * The pixel length of the minor tick marks.
- *
- * @sample {highcharts} highcharts/yaxis/minorticklength/
- * 10px on Y axis
- * @sample {highstock} stock/xaxis/minorticks/
- * 10px on Y axis
- */
- minorTickLength: 2,
- /**
- * The position of the minor tick marks relative to the axis line.
- * Can be one of `inside` and `outside`.
- *
- * @sample {highcharts} highcharts/yaxis/minortickposition-outside/
- * Outside by default
- * @sample {highcharts} highcharts/yaxis/minortickposition-inside/
- * Inside
- * @sample {highstock} stock/xaxis/minorticks/
- * Inside
- *
- * @validvalue ["inside", "outside"]
- */
- minorTickPosition: 'outside',
- /**
- * Enable or disable minor ticks. Unless
- * [minorTickInterval](#xAxis.minorTickInterval) is set, the tick
- * interval is calculated as a fifth of the `tickInterval`.
- *
- * On a logarithmic axis, minor ticks are laid out based on a best
- * guess, attempting to enter approximately 5 minor ticks between
- * each major tick.
- *
- * Prior to v6.0.0, ticks were unabled in auto layout by setting
- * `minorTickInterval` to `"auto"`.
- *
- * @productdesc {highcharts}
- * On axes using [categories](#xAxis.categories), minor ticks are not
- * supported.
- *
- * @sample {highcharts} highcharts/yaxis/minorticks-true/
- * Enabled on linear Y axis
- *
- * @type {boolean}
- * @default false
- * @since 6.0.0
- * @apioption xAxis.minorTicks
- */
- /**
- * The pixel width of the minor tick mark.
- *
- * @sample {highcharts} highcharts/yaxis/minortickwidth/
- * 3px width
- * @sample {highstock} stock/xaxis/minorticks/
- * 1px width
- *
- * @type {number}
- * @default 0
- * @apioption xAxis.minorTickWidth
- */
- /**
- * Padding of the min value relative to the length of the axis. A
- * padding of 0.05 will make a 100px axis 5px longer. This is useful
- * when you don't want the lowest data value to appear on the edge
- * of the plot area. When the axis' `min` option is set or a min extreme
- * is set using `axis.setExtremes()`, the minPadding will be ignored.
- *
- * @sample {highcharts} highcharts/yaxis/minpadding/
- * Min padding of 0.2
- * @sample {highstock} stock/xaxis/minpadding-maxpadding/
- * Greater min- and maxPadding
- * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
- * Add some padding
- *
- * @default {highcharts} 0.01
- * @default {highstock|highmaps} 0
- * @since 1.2.0
- * @product highcharts highstock gantt
- */
- minPadding: 0.01,
- /**
- * The minimum range to display on this axis. The entire axis will not
- * be allowed to span over a smaller interval than this. For example,
- * for a datetime axis the main unit is milliseconds. If minRange is
- * set to 3600000, you can't zoom in more than to one hour.
- *
- * The default minRange for the x axis is five times the smallest
- * interval between any of the data points.
- *
- * On a logarithmic axis, the unit for the minimum range is the power.
- * So a minRange of 1 means that the axis can be zoomed to 10-100,
- * 100-1000, 1000-10000 etc.
- *
- * Note that the `minPadding`, `maxPadding`, `startOnTick` and
- * `endOnTick` settings also affect how the extremes of the axis
- * are computed.
- *
- * @sample {highcharts} highcharts/xaxis/minrange/
- * Minimum range of 5
- * @sample {highstock} stock/xaxis/minrange/
- * Max zoom of 6 months overrides user selections
- * @sample {highmaps} maps/axis/minrange/
- * Minimum range of 1000
- *
- * @type {number}
- * @apioption xAxis.minRange
- */
- /**
- * The minimum tick interval allowed in axis values. For example on
- * zooming in on an axis with daily data, this can be used to prevent
- * the axis from showing hours. Defaults to the closest distance between
- * two points on the axis.
- *
- * @type {number}
- * @since 2.3.0
- * @apioption xAxis.minTickInterval
- */
- /**
- * The distance in pixels from the plot area to the axis line.
- * A positive offset moves the axis with it's line, labels and ticks
- * away from the plot area. This is typically used when two or more
- * axes are displayed on the same side of the plot. With multiple
- * axes the offset is dynamically adjusted to avoid collision, this
- * can be overridden by setting offset explicitly.
- *
- * @sample {highcharts} highcharts/yaxis/offset/
- * Y axis offset of 70
- * @sample {highcharts} highcharts/yaxis/offset-centered/
- * Axes positioned in the center of the plot
- * @sample {highstock} stock/xaxis/offset/
- * Y axis offset by 70 px
- *
- * @type {number}
- * @default 0
- * @apioption xAxis.offset
- */
- /**
- * Whether to display the axis on the opposite side of the normal. The
- * normal is on the left side for vertical axes and bottom for
- * horizontal, so the opposite sides will be right and top respectively.
- * This is typically used with dual or multiple axes.
- *
- * @sample {highcharts} highcharts/yaxis/opposite/
- * Secondary Y axis opposite
- * @sample {highstock} stock/xaxis/opposite/
- * Y axis on left side
- *
- * @type {boolean}
- * @default false
- * @apioption xAxis.opposite
- */
- /**
- * In an ordinal axis, the points are equally spaced in the chart
- * regardless of the actual time or x distance between them. This means
- * that missing data periods (e.g. nights or weekends for a stock chart)
- * will not take up space in the chart.
- * Having `ordinal: false` will show any gaps created by the `gapSize`
- * setting proportionate to their duration.
- *
- * In stock charts the X axis is ordinal by default, unless
- * the boost module is used and at least one of the series' data length
- * exceeds the [boostThreshold](#series.line.boostThreshold).
- *
- * @sample {highstock} stock/xaxis/ordinal-true/
- * True by default
- * @sample {highstock} stock/xaxis/ordinal-false/
- * False
- *
- * @type {boolean}
- * @default true
- * @since 1.1
- * @product highstock
- * @apioption xAxis.ordinal
- */
- /**
- * Additional range on the right side of the xAxis. Works similar to
- * `xAxis.maxPadding`, but value is set in milliseconds. Can be set for
- * both main `xAxis` and the navigator's `xAxis`.
- *
- * @sample {highstock} stock/xaxis/overscroll/
- * One minute overscroll with live data
- *
- * @type {number}
- * @default 0
- * @since 6.0.0
- * @product highstock
- * @apioption xAxis.overscroll
- */
- /**
- * Refers to the index in the [panes](#panes) array. Used for circular
- * gauges and polar charts. When the option is not set then first pane
- * will be used.
- *
- * @sample highcharts/demo/gauge-vu-meter
- * Two gauges with different center
- *
- * @type {number}
- * @product highcharts
- * @apioption xAxis.pane
- */
- /**
- * The zoomed range to display when only defining one or none of `min`
- * or `max`. For example, to show the latest month, a range of one month
- * can be set.
- *
- * @sample {highstock} stock/xaxis/range/
- * Setting a zoomed range when the rangeSelector is disabled
- *
- * @type {number}
- * @product highstock
- * @apioption xAxis.range
- */
- /**
- * Whether to reverse the axis so that the highest number is closest
- * to the origin. If the chart is inverted, the x axis is reversed by
- * default.
- *
- * @sample {highcharts} highcharts/yaxis/reversed/
- * Reversed Y axis
- * @sample {highstock} stock/xaxis/reversed/
- * Reversed Y axis
- *
- * @type {boolean}
- * @default false
- * @apioption xAxis.reversed
- */
- // reversed: false,
- /**
- * This option determines how stacks should be ordered within a group.
- * For example reversed xAxis also reverses stacks, so first series
- * comes last in a group. To keep order like for non-reversed xAxis
- * enable this option.
- *
- * @sample {highcharts} highcharts/xaxis/reversedstacks/
- * Reversed stacks comparison
- * @sample {highstock} highcharts/xaxis/reversedstacks/
- * Reversed stacks comparison
- *
- * @type {boolean}
- * @default false
- * @since 6.1.1
- * @product highcharts highstock
- * @apioption xAxis.reversedStacks
- */
- /**
- * An optional scrollbar to display on the X axis in response to
- * limiting the minimum and maximum of the axis values.
- *
- * In styled mode, all the presentational options for the scrollbar are
- * replaced by the classes `.highcharts-scrollbar-thumb`,
- * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
- * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
- *
- * @sample {highstock} stock/yaxis/heatmap-scrollbars/
- * Heatmap with both scrollbars
- *
- * @extends scrollbar
- * @since 4.2.6
- * @product highstock
- * @apioption xAxis.scrollbar
- */
- /**
- * Whether to show the axis line and title when the axis has no data.
- *
- * @sample {highcharts} highcharts/yaxis/showempty/
- * When clicking the legend to hide series, one axis preserves
- * line and title, the other doesn't
- * @sample {highstock} highcharts/yaxis/showempty/
- * When clicking the legend to hide series, one axis preserves
- * line and title, the other doesn't
- *
- * @type {boolean}
- * @default true
- * @since 1.1
- * @apioption xAxis.showEmpty
- */
- /**
- * Whether to show the first tick label.
- *
- * @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
- * Set to false on X axis
- * @sample {highstock} stock/xaxis/showfirstlabel/
- * Labels below plot lines on Y axis
- *
- * @type {boolean}
- * @default true
- * @apioption xAxis.showFirstLabel
- */
- /**
- * Whether to show the last tick label. Defaults to `true` on cartesian
- * charts, and `false` on polar charts.
- *
- * @sample {highcharts} highcharts/xaxis/showlastlabel-true/
- * Set to true on X axis
- * @sample {highstock} stock/xaxis/showfirstlabel/
- * Labels below plot lines on Y axis
- *
- * @type {boolean}
- * @default true
- * @product highcharts highstock gantt
- * @apioption xAxis.showLastLabel
- */
- /**
- * A soft maximum for the axis. If the series data maximum is less than
- * this, the axis will stay at this maximum, but if the series data
- * maximum is higher, the axis will flex to show all data.
- *
- * @sample highcharts/yaxis/softmin-softmax/
- * Soft min and max
- *
- * @type {number}
- * @since 5.0.1
- * @product highcharts highstock gantt
- * @apioption xAxis.softMax
- */
- /**
- * A soft minimum for the axis. If the series data minimum is greater
- * than this, the axis will stay at this minimum, but if the series
- * data minimum is lower, the axis will flex to show all data.
- *
- * @sample highcharts/yaxis/softmin-softmax/
- * Soft min and max
- *
- * @type {number}
- * @since 5.0.1
- * @product highcharts highstock gantt
- * @apioption xAxis.softMin
- */
- /**
- * For datetime axes, this decides where to put the tick between weeks.
- * 0 = Sunday, 1 = Monday.
- *
- * @sample {highcharts} highcharts/xaxis/startofweek-monday/
- * Monday by default
- * @sample {highcharts} highcharts/xaxis/startofweek-sunday/
- * Sunday
- * @sample {highstock} stock/xaxis/startofweek-1
- * Monday by default
- * @sample {highstock} stock/xaxis/startofweek-0
- * Sunday
- *
- * @product highcharts highstock gantt
- */
- startOfWeek: 1,
- /**
- * Whether to force the axis to start on a tick. Use this option with
- * the `minPadding` option to control the axis start.
- *
- * @productdesc {highstock}
- * In Highstock, `startOnTick` is always false when the navigator is
- * enabled, to prevent jumpy scrolling.
- *
- * @sample {highcharts} highcharts/xaxis/startontick-false/
- * False by default
- * @sample {highcharts} highcharts/xaxis/startontick-true/
- * True
- * @sample {highstock} stock/xaxis/endontick/
- * False for Y axis
- *
- * @since 1.2.0
- */
- startOnTick: false,
- /**
- * The amount of ticks to draw on the axis. This opens up for aligning
- * the ticks of multiple charts or panes within a chart. This option
- * overrides the `tickPixelInterval` option.
- *
- * This option only has an effect on linear axes. Datetime, logarithmic
- * or category axes are not affected.
- *
- * @sample {highcharts} highcharts/yaxis/tickamount/
- * 8 ticks on Y axis
- * @sample {highstock} highcharts/yaxis/tickamount/
- * 8 ticks on Y axis
- *
- * @type {number}
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @apioption xAxis.tickAmount
- */
- /**
- * The interval of the tick marks in axis units. When `undefined`, the
- * tick interval is computed to approximately follow the
- * [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
- * axes. On categorized axes, a `undefined` tickInterval will default to
- * 1, one category. Note that datetime axes are based on milliseconds,
- * so for example an interval of one day is expressed as
- * `24 * 3600 * 1000`.
- *
- * On logarithmic axes, the tickInterval is based on powers, so a
- * tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
- * tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
- * of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
- * 40 etc.
- *
- *
- * If the tickInterval is too dense for labels to be drawn, Highcharts
- * may remove ticks.
- *
- * If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
- * option may interfere with the `tickInterval` setting.
- *
- * @see [tickPixelInterval](#xAxis.tickPixelInterval)
- * @see [tickPositions](#xAxis.tickPositions)
- * @see [tickPositioner](#xAxis.tickPositioner)
- *
- * @sample {highcharts} highcharts/xaxis/tickinterval-5/
- * Tick interval of 5 on a linear axis
- * @sample {highstock} stock/xaxis/tickinterval/
- * Tick interval of 0.01 on Y axis
- *
- * @type {number}
- * @apioption xAxis.tickInterval
- */
- /**
- * The pixel length of the main tick marks.
- *
- * @sample {highcharts} highcharts/xaxis/ticklength/
- * 20 px tick length on the X axis
- * @sample {highstock} stock/xaxis/ticks/
- * Formatted ticks on X axis
- */
- tickLength: 10,
- /**
- * If tickInterval is `null` this option sets the approximate pixel
- * interval of the tick marks. Not applicable to categorized axis.
- *
- * The tick interval is also influenced by the [minTickInterval](
- * #xAxis.minTickInterval) option, that, by default prevents ticks from
- * being denser than the data points.
- *
- * @see [tickInterval](#xAxis.tickInterval)
- * @see [tickPositioner](#xAxis.tickPositioner)
- * @see [tickPositions](#xAxis.tickPositions)
- *
- * @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
- * 50 px on X axis
- * @sample {highstock} stock/xaxis/tickpixelinterval/
- * 200 px on X axis
- */
- tickPixelInterval: 100,
- /**
- * For categorized axes only. If `on` the tick mark is placed in the
- * center of the category, if `between` the tick mark is placed between
- * categories. The default is `between` if the `tickInterval` is 1, else
- * `on`.
- *
- * @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
- * "between" by default
- * @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
- * "on"
- *
- * @product highcharts gantt
- * @validvalue ["on", "between"]
- */
- tickmarkPlacement: 'between',
- /**
- * The position of the major tick marks relative to the axis line.
- * Can be one of `inside` and `outside`.
- *
- * @sample {highcharts} highcharts/xaxis/tickposition-outside/
- * "outside" by default
- * @sample {highcharts} highcharts/xaxis/tickposition-inside/
- * "inside"
- * @sample {highstock} stock/xaxis/ticks/
- * Formatted ticks on X axis
- *
- * @validvalue ["inside", "outside"]
- */
- tickPosition: 'outside',
- /**
- * A callback function returning array defining where the ticks are
- * laid out on the axis. This overrides the default behaviour of
- * [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
- * #xAxis.tickInterval). The automatic tick positions are accessible
- * through `this.tickPositions` and can be modified by the callback.
- *
- * @see [tickPositions](#xAxis.tickPositions)
- *
- * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
- * Demo of tickPositions and tickPositioner
- * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
- * Demo of tickPositions and tickPositioner
- *
- * @type {Highcharts.AxisTickPositionerCallbackFunction}
- * @apioption xAxis.tickPositioner
- */
- /**
- * An array defining where the ticks are laid out on the axis. This
- * overrides the default behaviour of [tickPixelInterval](
- * #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
- *
- * @see [tickPositioner](#xAxis.tickPositioner)
- *
- * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
- * Demo of tickPositions and tickPositioner
- * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
- * Demo of tickPositions and tickPositioner
- *
- * @type {Array<number>}
- * @apioption xAxis.tickPositions
- */
- /**
- * The pixel width of the major tick marks.
- *
- * In styled mode, the stroke width is given in the `.highcharts-tick`
- * class.
- *
- * @sample {highcharts} highcharts/xaxis/tickwidth/
- * 10 px width
- * @sample {highcharts} highcharts/css/axis-grid/
- * Styled mode
- * @sample {highstock} stock/xaxis/ticks/
- * Formatted ticks on X axis
- * @sample {highstock} highcharts/css/axis-grid/
- * Styled mode
- *
- * @type {number}
- * @default {highcharts} 1
- * @default {highstock} 1
- * @default {highmaps} 0
- * @apioption xAxis.tickWidth
- */
- /**
- * The axis title, showing next to the axis line.
- *
- * @productdesc {highmaps}
- * In Highmaps, the axis is hidden by default, but adding an axis title
- * is still possible. X axis and Y axis titles will appear at the bottom
- * and left by default.
- */
- title: {
- /**
- * Deprecated. Set the `text` to `null` to disable the title.
- *
- * @deprecated
- * @type {string}
- * @default middle
- * @product highcharts
- * @apioption xAxis.title.enabled
- */
- /**
- * The pixel distance between the axis labels or line and the title.
- * Defaults to 0 for horizontal axes, 10 for vertical
- *
- * @sample {highcharts} highcharts/xaxis/title-margin/
- * Y axis title margin of 60
- *
- * @type {number}
- * @apioption xAxis.title.margin
- */
- /**
- * The distance of the axis title from the axis line. By default,
- * this distance is computed from the offset width of the labels,
- * the labels' distance from the axis and the title's margin.
- * However when the offset option is set, it overrides all this.
- *
- * @sample {highcharts} highcharts/yaxis/title-offset/
- * Place the axis title on top of the axis
- * @sample {highstock} highcharts/yaxis/title-offset/
- * Place the axis title on top of the Y axis
- *
- * @type {number}
- * @since 2.2.0
- * @apioption xAxis.title.offset
- */
- /**
- * Whether to reserve space for the title when laying out the axis.
- *
- * @type {boolean}
- * @default true
- * @since 5.0.11
- * @product highcharts highstock gantt
- * @apioption xAxis.title.reserveSpace
- */
- /**
- * The rotation of the text in degrees. 0 is horizontal, 270 is
- * vertical reading from bottom to top.
- *
- * @sample {highcharts} highcharts/yaxis/title-offset/
- * Horizontal
- *
- * @type {number}
- * @default 0
- * @apioption xAxis.title.rotation
- */
- /**
- * The actual text of the axis title. It can contain basic HTML text
- * markup like <b>, <i> and spans with style.
- *
- * @sample {highcharts} highcharts/xaxis/title-text/
- * Custom HTML
- * @sample {highstock} stock/xaxis/title-text/
- * Titles for both axes
- *
- * @type {string|null}
- * @apioption xAxis.title.text
- */
- /**
- * Alignment of the text, can be `"left"`, `"right"` or `"center"`.
- * Default alignment depends on the
- * [title.align](xAxis.title.align):
- *
- * Horizontal axes:
- * - for `align` = `"low"`, `textAlign` is set to `left`
- * - for `align` = `"middle"`, `textAlign` is set to `center`
- * - for `align` = `"high"`, `textAlign` is set to `right`
- *
- * Vertical axes:
- * - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
- * set to `right`
- * - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
- * set to `left`
- * - for `align` = `"middle"`, `textAlign` is set to `center`
- * - for `align` = `"high"` and `opposite` = `true` `textAlign` is
- * set to `left`
- * - for `align` = `"high"` and `opposite` = `false` `textAlign` is
- * set to `right`
- *
- * @type {string}
- * @apioption xAxis.title.textAlign
- */
- /**
- * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the axis title.
- *
- * @type {boolean}
- * @default false
- * @product highcharts highstock gantt
- * @apioption xAxis.title.useHTML
- */
- /**
- * Horizontal pixel offset of the title position.
- *
- * @type {number}
- * @default 0
- * @since 4.1.6
- * @product highcharts highstock gantt
- * @apioption xAxis.title.x
- */
- /**
- * Vertical pixel offset of the title position.
- *
- * @type {number}
- * @product highcharts highstock gantt
- * @apioption xAxis.title.y
- */
- /**
- * Alignment of the title relative to the axis values. Possible
- * values are "low", "middle" or "high".
- *
- * @sample {highcharts} highcharts/xaxis/title-align-low/
- * "low"
- * @sample {highcharts} highcharts/xaxis/title-align-center/
- * "middle" by default
- * @sample {highcharts} highcharts/xaxis/title-align-high/
- * "high"
- * @sample {highcharts} highcharts/yaxis/title-offset/
- * Place the Y axis title on top of the axis
- * @sample {highstock} stock/xaxis/title-align/
- * Aligned to "high" value
- *
- * @validvalue ["low", "middle", "high"]
- */
- align: 'middle',
- /**
- * CSS styles for the title. If the title text is longer than the
- * axis length, it will wrap to multiple lines by default. This can
- * be customized by setting `textOverflow: 'ellipsis'`, by
- * setting a specific `width` or by setting `whiteSpace: 'nowrap'`.
- *
- * In styled mode, the stroke width is given in the
- * `.highcharts-axis-title` class.
- *
- * @sample {highcharts} highcharts/xaxis/title-style/
- * Red
- * @sample {highcharts} highcharts/css/axis/
- * Styled mode
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#666666"}
- */
- style: {
- /** @ignore-option */
- color: '#666666'
- }
- },
- /**
- * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
- * or `category`. In a datetime axis, the numbers are given in
- * milliseconds, and tick marks are placed on appropriate values like
- * full hours or days. In a category axis, the
- * [point names](#series.line.data.name) of the chart's series are used
- * for categories, if not a [categories](#xAxis.categories) array is
- * defined.
- *
- * @sample {highcharts} highcharts/xaxis/type-linear/
- * Linear
- * @sample {highcharts} highcharts/yaxis/type-log/
- * Logarithmic
- * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
- * Logarithmic with minor grid lines
- * @sample {highcharts} highcharts/xaxis/type-log-both/
- * Logarithmic on two axes
- * @sample {highcharts} highcharts/yaxis/type-log-negative/
- * Logarithmic with extension to emulate negative values
- *
- * @product highcharts gantt
- * @validvalue ["linear", "logarithmic", "datetime", "category"]
- */
- type: 'linear',
- /**
- * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
- * `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
- * `linear` for other chart types.
- *
- * In a datetime axis, the numbers are given in milliseconds, and tick
- * marks are placed on appropriate values, like full hours or days. In a
- * category or treegrid axis, the [point names](#series.line.data.name)
- * of the chart's series are used for categories, if a
- * [categories](#xAxis.categories) array is not defined.
- *
- * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
- * Logarithmic with minor grid lines
- * @sample {highcharts} highcharts/yaxis/type-log-negative/
- * Logarithmic with extension to emulate negative values
- * @sample {gantt} gantt/treegrid-axis/demo
- * Treegrid axis
- *
- * @default {highcharts} linear
- * @default {gantt} treegrid
- * @product highcharts gantt
- * @validvalue ["linear", "logarithmic", "datetime", "category",
- * "treegrid"]
- * @apioption yAxis.type
- */
- /**
- * Applies only when the axis `type` is `category`. When `uniqueNames`
- * is true, points are placed on the X axis according to their names.
- * If the same point name is repeated in the same or another series,
- * the point is placed on the same X position as other points of the
- * same name. When `uniqueNames` is false, the points are laid out in
- * increasing X positions regardless of their names, and the X axis
- * category will take the name of the last point in each position.
- *
- * @sample {highcharts} highcharts/xaxis/uniquenames-true/
- * True by default
- * @sample {highcharts} highcharts/xaxis/uniquenames-false/
- * False
- *
- * @type {boolean}
- * @default true
- * @since 4.2.7
- * @product highcharts gantt
- * @apioption xAxis.uniqueNames
- */
- /**
- * Datetime axis only. An array determining what time intervals the
- * ticks are allowed to fall on. Each array item is an array where the
- * first value is the time unit and the second value another array of
- * allowed multiples. Defaults to:
- *
- * <pre>units: [[
- * 'millisecond', // unit name
- * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
- * ], [
- * 'second',
- * [1, 2, 5, 10, 15, 30]
- * ], [
- * 'minute',
- * [1, 2, 5, 10, 15, 30]
- * ], [
- * 'hour',
- * [1, 2, 3, 4, 6, 8, 12]
- * ], [
- * 'day',
- * [1]
- * ], [
- * 'week',
- * [1]
- * ], [
- * 'month',
- * [1, 3, 6]
- * ], [
- * 'year',
- * null
- * ]]</pre>
- *
- * @type {Array<Array<string,(Array<number>|null)>>}
- * @product highcharts highstock gantt
- * @apioption xAxis.units
- */
- /**
- * Whether axis, including axis title, line, ticks and labels, should
- * be visible.
- *
- * @type {boolean}
- * @default true
- * @since 4.1.9
- * @product highcharts highstock gantt
- * @apioption xAxis.visible
- */
- /**
- * Color of the minor, secondary grid lines.
- *
- * In styled mode, the stroke width is given in the
- * `.highcharts-minor-grid-line` class.
- *
- * @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
- * Bright grey lines from Y axis
- * @sample {highcharts|highstock} highcharts/css/axis-grid/
- * Styled mode
- * @sample {highstock} stock/xaxis/minorgridlinecolor/
- * Bright grey lines from Y axis
- *
- * @type {Highcharts.ColorString}
- * @default #f2f2f2
- */
- minorGridLineColor: '#f2f2f2',
- /**
- * Width of the minor, secondary grid lines.
- *
- * In styled mode, the stroke width is given in the
- * `.highcharts-grid-line` class.
- *
- * @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
- * 2px lines from Y axis
- * @sample {highcharts|highstock} highcharts/css/axis-grid/
- * Styled mode
- * @sample {highstock} stock/xaxis/minorgridlinewidth/
- * 2px lines from Y axis
- */
- minorGridLineWidth: 1,
- /**
- * Color for the minor tick marks.
- *
- * @sample {highcharts} highcharts/yaxis/minortickcolor/
- * Black tick marks on Y axis
- * @sample {highstock} stock/xaxis/minorticks/
- * Black tick marks on Y axis
- *
- * @type {Highcharts.ColorString}
- * @default #999999
- */
- minorTickColor: '#999999',
- /**
- * The color of the line marking the axis itself.
- *
- * In styled mode, the line stroke is given in the
- * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
- *
- * @productdesc {highmaps}
- * In Highmaps, the axis line is hidden by default, because the axis is
- * not visible by default.
- *
- * @sample {highcharts} highcharts/yaxis/linecolor/
- * A red line on Y axis
- * @sample {highcharts|highstock} highcharts/css/axis/
- * Axes in styled mode
- * @sample {highstock} stock/xaxis/linecolor/
- * A red line on X axis
- *
- * @type {Highcharts.ColorString}
- * @default #ccd6eb
- */
- lineColor: '#ccd6eb',
- /**
- * The width of the line marking the axis itself.
- *
- * In styled mode, the stroke width is given in the
- * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
- *
- * @sample {highcharts} highcharts/yaxis/linecolor/
- * A 1px line on Y axis
- * @sample {highcharts|highstock} highcharts/css/axis/
- * Axes in styled mode
- * @sample {highstock} stock/xaxis/linewidth/
- * A 2px line on X axis
- *
- * @default {highcharts|highstock} 1
- * @default {highmaps} 0
- */
- lineWidth: 1,
- /**
- * Color of the grid lines extending the ticks across the plot area.
- *
- * In styled mode, the stroke is given in the `.highcharts-grid-line`
- * class.
- *
- * @productdesc {highmaps}
- * In Highmaps, the grid lines are hidden by default.
- *
- * @sample {highcharts} highcharts/yaxis/gridlinecolor/
- * Green lines
- * @sample {highcharts|highstock} highcharts/css/axis-grid/
- * Styled mode
- * @sample {highstock} stock/xaxis/gridlinecolor/
- * Green lines
- *
- * @type {Highcharts.ColorString}
- * @default #e6e6e6
- */
- gridLineColor: '#e6e6e6',
- // gridLineDashStyle: 'solid',
- /**
- * The width of the grid lines extending the ticks across the plot area.
- *
- * In styled mode, the stroke width is given in the
- * `.highcharts-grid-line` class.
- *
- * @sample {highcharts} highcharts/yaxis/gridlinewidth/
- * 2px lines
- * @sample {highcharts|highstock} highcharts/css/axis-grid/
- * Styled mode
- * @sample {highstock} stock/xaxis/gridlinewidth/
- * 2px lines
- *
- * @type {number}
- * @default 0
- * @apioption xAxis.gridLineWidth
- */
- // gridLineWidth: 0,
- /**
- * Color for the main tick marks.
- *
- * In styled mode, the stroke is given in the `.highcharts-tick`
- * class.
- *
- * @sample {highcharts} highcharts/xaxis/tickcolor/
- * Red ticks on X axis
- * @sample {highcharts|highstock} highcharts/css/axis-grid/
- * Styled mode
- * @sample {highstock} stock/xaxis/ticks/
- * Formatted ticks on X axis
- *
- * @type {Highcharts.ColorString}
- * @default #ccd6eb
- */
- tickColor: '#ccd6eb'
- // tickWidth: 1
- },
- /**
- * The Y axis or value axis. Normally this is the vertical axis,
- * though if the chart is inverted this is the horizontal axis.
- * In case of multiple axes, the yAxis node is an array of
- * configuration objects.
- *
- * See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
- * access to the axis.
- *
- * @type {*|Array<*>}
- * @extends xAxis
- * @excluding ordinal,overscroll,currentDateIndicator
- * @optionparent yAxis
- */
- defaultYAxisOptions: {
- /**
- * In a polar chart, this is the angle of the Y axis in degrees, where
- * 0 is up and 90 is right. The angle determines the position of the
- * axis line and the labels, though the coordinate system is unaffected.
- *
- * @sample {highcharts} highcharts/yaxis/angle/
- * Dual axis polar chart
- *
- * @type {number}
- * @default 0
- * @since 4.2.7
- * @product highcharts
- * @apioption yAxis.angle
- */
- /**
- * Polar charts only. Whether the grid lines should draw as a polygon
- * with straight lines between categories, or as circles. Can be either
- * `circle` or `polygon`.
- *
- * @sample {highcharts} highcharts/demo/polar-spider/
- * Polygon grid lines
- * @sample {highcharts} highcharts/yaxis/gridlineinterpolation/
- * Circle and polygon
- *
- * @type {string}
- * @product highcharts
- * @validvalue ["circle", "polygon"]
- * @apioption yAxis.gridLineInterpolation
- */
- /**
- * The height of the Y axis. If it's a number, it is interpreted as
- * pixels.
- *
- * Since Highstock 2: If it's a percentage string, it is interpreted
- * as percentages of the total plot height.
- *
- * @see [yAxis.top](#yAxis.top)
- *
- * @sample {highstock} stock/demo/candlestick-and-volume/
- * Percentage height panes
- *
- * @type {number|string}
- * @product highstock
- * @apioption yAxis.height
- */
- /**
- * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
- * to represent the maximum value of the Y axis.
- *
- * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
- * Min and max colors
- *
- * @type {Highcharts.ColorString}
- * @default #003399
- * @since 4.0
- * @product highcharts
- * @apioption yAxis.maxColor
- */
- /**
- * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
- * to represent the minimum value of the Y axis.
- *
- * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
- * Min and max color
- *
- * @type {Highcharts.ColorString}
- * @default #e6ebf5
- * @since 4.0
- * @product highcharts
- * @apioption yAxis.minColor
- */
- /**
- * Whether to reverse the axis so that the highest number is closest
- * to the origin.
- *
- * @sample {highcharts} highcharts/yaxis/reversed/
- * Reversed Y axis
- * @sample {highstock} stock/xaxis/reversed/
- * Reversed Y axis
- *
- * @type {boolean}
- * @default {highcharts} false
- * @default {highstock} false
- * @default {highmaps} true
- * @default {gantt} true
- * @apioption yAxis.reversed
- */
- /**
- * If `true`, the first series in a stack will be drawn on top in a
- * positive, non-reversed Y axis. If `false`, the first series is in
- * the base of the stack.
- *
- * @sample {highcharts} highcharts/yaxis/reversedstacks-false/
- * Non-reversed stacks
- * @sample {highstock} highcharts/yaxis/reversedstacks-false/
- * Non-reversed stacks
- *
- * @type {boolean}
- * @default true
- * @since 3.0.10
- * @product highcharts highstock
- * @apioption yAxis.reversedStacks
- */
- /**
- * Solid gauge series only. Color stops for the solid gauge. Use this
- * in cases where a linear gradient between a `minColor` and `maxColor`
- * is not sufficient. The stops is an array of tuples, where the first
- * item is a float between 0 and 1 assigning the relative position in
- * the gradient, and the second item is the color.
- *
- * For solid gauges, the Y axis also inherits the concept of
- * [data classes](http://api.highcharts.com/highmaps#colorAxis.dataClasses)
- * from the Highmaps color axis.
- *
- * @see [minColor](#yAxis.minColor)
- * @see [maxColor](#yAxis.maxColor)
- *
- * @sample {highcharts} highcharts/demo/gauge-solid/
- * True by default
- *
- * @type {Array<Array<number,Highcharts.ColorString>>}
- * @since 4.0
- * @product highcharts
- * @apioption yAxis.stops
- */
- /**
- * The pixel width of the major tick marks.
- *
- * @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
- * @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
- *
- * @type {number}
- * @default 0
- * @product highcharts highstock gantt
- * @apioption yAxis.tickWidth
- */
- /**
- * Angular gauges and solid gauges only. The label's pixel distance
- * from the perimeter of the plot area.
- *
- * @type {number}
- * @default -25
- * @product highcharts
- * @apioption yAxis.labels.distance
- */
- /**
- * The y position offset of the label relative to the tick position
- * on the axis.
- *
- * @sample {highcharts} highcharts/xaxis/labels-x/
- * Y axis labels placed on grid lines
- *
- * @type {number}
- * @default {highcharts} 3
- * @default {highstock} -2
- * @default {highmaps} 3
- * @apioption yAxis.labels.y
- */
- /**
- * @productdesc {highstock}
- * In Highstock, `endOnTick` is always false when the navigator is
- * enabled, to prevent jumpy scrolling.
- */
- endOnTick: true,
- /**
- * Padding of the max value relative to the length of the axis. A
- * padding of 0.05 will make a 100px axis 5px longer. This is useful
- * when you don't want the highest data value to appear on the edge
- * of the plot area. When the axis' `max` option is set or a max extreme
- * is set using `axis.setExtremes()`, the maxPadding will be ignored.
- *
- * @sample {highcharts} highcharts/yaxis/maxpadding-02/
- * Max padding of 0.2
- * @sample {highstock} stock/xaxis/minpadding-maxpadding/
- * Greater min- and maxPadding
- *
- * @since 1.2.0
- * @product highcharts highstock gantt
- */
- maxPadding: 0.05,
- /**
- * Padding of the min value relative to the length of the axis. A
- * padding of 0.05 will make a 100px axis 5px longer. This is useful
- * when you don't want the lowest data value to appear on the edge
- * of the plot area. When the axis' `min` option is set or a max extreme
- * is set using `axis.setExtremes()`, the maxPadding will be ignored.
- *
- * @sample {highcharts} highcharts/yaxis/minpadding/
- * Min padding of 0.2
- * @sample {highstock} stock/xaxis/minpadding-maxpadding/
- * Greater min- and maxPadding
- *
- * @since 1.2.0
- * @product highcharts highstock gantt
- */
- minPadding: 0.05,
- /**
- * @productdesc {highstock}
- * In Highstock 1.x, the Y axis was placed on the left side by default.
- *
- * @sample {highcharts} highcharts/yaxis/opposite/
- * Secondary Y axis opposite
- * @sample {highstock} stock/xaxis/opposite/
- * Y axis on left side
- *
- * @type {boolean}
- * @default {highstock} true
- * @default {highcharts} false
- * @product highstock highcharts gantt
- * @apioption yAxis.opposite
- */
- /**
- * @see [tickInterval](#xAxis.tickInterval)
- * @see [tickPositioner](#xAxis.tickPositioner)
- * @see [tickPositions](#xAxis.tickPositions)
- */
- tickPixelInterval: 72,
- showLastLabel: true,
- /**
- * @extends xAxis.labels
- */
- labels: {
- /**
- * What part of the string the given position is anchored to. Can
- * be one of `"left"`, `"center"` or `"right"`. The exact position
- * also depends on the `labels.x` setting.
- *
- * Angular gauges and solid gauges defaults to `center`.
- *
- * @sample {highcharts} highcharts/yaxis/labels-align-left/
- * Left
- *
- * @type {string}
- * @default {highcharts|highmaps} right
- * @default {highstock} left
- * @validvalue ["left", "center", "right"]
- * @apioption yAxis.labels.align
- */
- /**
- * The x position offset of the label relative to the tick position
- * on the axis. Defaults to -15 for left axis, 15 for right axis.
- *
- * @sample {highcharts} highcharts/xaxis/labels-x/
- * Y axis labels placed on grid lines
- */
- x: -8
- },
- /**
- * @productdesc {highmaps}
- * In Highmaps, the axis line is hidden by default, because the axis is
- * not visible by default.
- *
- * @type {Highcharts.ColorString}
- * @apioption yAxis.lineColor
- */
- /**
- * @sample {highcharts} highcharts/yaxis/max-200/
- * Y axis max of 200
- * @sample {highcharts} highcharts/yaxis/max-logarithmic/
- * Y axis max on logarithmic axis
- * @sample {highstock} stock/yaxis/min-max/
- * Fixed min and max on Y axis
- * @sample {highmaps} maps/axis/min-max/
- * Pre-zoomed to a specific area
- *
- * @type {number}
- * @apioption yAxis.max
- */
- /**
- * @sample {highcharts} highcharts/yaxis/min-startontick-false/
- * -50 with startOnTick to false
- * @sample {highcharts} highcharts/yaxis/min-startontick-true/
- * -50 with startOnTick true by default
- * @sample {highstock} stock/yaxis/min-max/
- * Fixed min and max on Y axis
- * @sample {highmaps} maps/axis/min-max/
- * Pre-zoomed to a specific area
- *
- * @type {number}
- * @apioption yAxis.min
- */
- /**
- * An optional scrollbar to display on the Y axis in response to
- * limiting the minimum an maximum of the axis values.
- *
- * In styled mode, all the presentational options for the scrollbar
- * are replaced by the classes `.highcharts-scrollbar-thumb`,
- * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
- * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
- *
- * @sample {highstock} stock/yaxis/scrollbar/
- * Scrollbar on the Y axis
- *
- * @extends scrollbar
- * @since 4.2.6
- * @product highstock
- * @excluding height
- * @apioption yAxis.scrollbar
- */
- /**
- * Enable the scrollbar on the Y axis.
- *
- * @sample {highstock} stock/yaxis/scrollbar/
- * Enabled on Y axis
- *
- * @type {boolean}
- * @default false
- * @since 4.2.6
- * @product highstock
- * @apioption yAxis.scrollbar.enabled
- */
- /**
- * Pixel margin between the scrollbar and the axis elements.
- *
- * @type {number}
- * @default 10
- * @since 4.2.6
- * @product highstock
- * @apioption yAxis.scrollbar.margin
- */
- /**
- * Whether to show the scrollbar when it is fully zoomed out at max
- * range. Setting it to `false` on the Y axis makes the scrollbar stay
- * hidden until the user zooms in, like common in browsers.
- *
- * @type {boolean}
- * @default true
- * @since 4.2.6
- * @product highstock
- * @apioption yAxis.scrollbar.showFull
- */
- /**
- * The width of a vertical scrollbar or height of a horizontal
- * scrollbar. Defaults to 20 on touch devices.
- *
- * @type {number}
- * @default 14
- * @since 4.2.6
- * @product highstock
- * @apioption yAxis.scrollbar.size
- */
- /**
- * Z index of the scrollbar elements.
- *
- * @type {number}
- * @default 3
- * @since 4.2.6
- * @product highstock
- * @apioption yAxis.scrollbar.zIndex
- */
- /**
- * A soft maximum for the axis. If the series data maximum is less
- * than this, the axis will stay at this maximum, but if the series
- * data maximum is higher, the axis will flex to show all data.
- *
- * **Note**: The [series.softThreshold](
- * #plotOptions.series.softThreshold) option takes precedence over this
- * option.
- *
- * @sample highcharts/yaxis/softmin-softmax/
- * Soft min and max
- *
- * @type {number}
- * @since 5.0.1
- * @product highcharts highstock gantt
- * @apioption yAxis.softMax
- */
- /**
- * A soft minimum for the axis. If the series data minimum is greater
- * than this, the axis will stay at this minimum, but if the series
- * data minimum is lower, the axis will flex to show all data.
- *
- * **Note**: The [series.softThreshold](
- * #plotOptions.series.softThreshold) option takes precedence over this
- * option.
- *
- * @sample highcharts/yaxis/softmin-softmax/
- * Soft min and max
- *
- * @type {number}
- * @since 5.0.1
- * @product highcharts highstock gantt
- * @apioption yAxis.softMin
- */
- /**
- * Defines the horizontal alignment of the stack total label. Can be one
- * of `"left"`, `"center"` or `"right"`. The default value is calculated
- * at runtime and depends on orientation and whether the stack is
- * positive or negative.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
- * Aligned to the left
- * @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
- * Aligned in center
- * @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
- * Aligned to the right
- *
- * @type {Highcharts.AlignType}
- * @since 2.1.5
- * @product highcharts
- * @apioption yAxis.stackLabels.align
- */
- /**
- * A [format string](http://docs.highcharts.com/#formatting) for the
- * data label. Available variables are the same as for `formatter`.
- *
- * @type {string}
- * @default {total}
- * @since 3.0.2
- * @product highcharts highstock
- * @apioption yAxis.stackLabels.format
- */
- /**
- * Rotation of the labels in degrees.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
- * Labels rotated 45°
- *
- * @type {number}
- * @default 0
- * @since 2.1.5
- * @product highcharts
- * @apioption yAxis.stackLabels.rotation
- */
- /**
- * The text alignment for the label. While `align` determines where the
- * texts anchor point is placed with regards to the stack, `textAlign`
- * determines how the text is aligned against its anchor point. Possible
- * values are `"left"`, `"center"` and `"right"`. The default value is
- * calculated at runtime and depends on orientation and whether the
- * stack is positive or negative.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
- * Label in center position but text-aligned left
- *
- * @type {Highcharts.AlignType}
- * @since 2.1.5
- * @product highcharts
- * @apioption yAxis.stackLabels.textAlign
- */
- /**
- * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the labels.
- *
- * @type {boolean}
- * @default false
- * @since 3.0
- * @product highcharts highstock
- * @apioption yAxis.stackLabels.useHTML
- */
- /**
- * Defines the vertical alignment of the stack total label. Can be one
- * of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
- * at runtime and depends on orientation and whether the stack is
- * positive or negative.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
- * Vertically aligned top
- * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
- * Vertically aligned middle
- * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
- * Vertically aligned bottom
- *
- * @type {Highcharts.VerticalAlignType}
- * @since 2.1.5
- * @product highcharts
- * @apioption yAxis.stackLabels.verticalAlign
- */
- /**
- * The x position offset of the label relative to the left of the
- * stacked bar. The default value is calculated at runtime and depends
- * on orientation and whether the stack is positive or negative.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-x/
- * Stack total labels with x offset
- *
- * @type {number}
- * @since 2.1.5
- * @product highcharts
- * @apioption yAxis.stackLabels.x
- */
- /**
- * The y position offset of the label relative to the tick position
- * on the axis. The default value is calculated at runtime and depends
- * on orientation and whether the stack is positive or negative.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-y/
- * Stack total labels with y offset
- *
- * @type {number}
- * @since 2.1.5
- * @product highcharts
- * @apioption yAxis.stackLabels.y
- */
- /**
- * Whether to force the axis to start on a tick. Use this option with
- * the `maxPadding` option to control the axis start.
- *
- * @sample {highcharts} highcharts/xaxis/startontick-false/
- * False by default
- * @sample {highcharts} highcharts/xaxis/startontick-true/
- * True
- * @sample {highstock} stock/xaxis/endontick/
- * False for Y axis
- *
- * @since 1.2.0
- * @product highcharts highstock gantt
- */
- startOnTick: true,
- title: {
- /**
- * The pixel distance between the axis labels and the title.
- * Positive values are outside the axis line, negative are inside.
- *
- * @sample {highcharts} highcharts/xaxis/title-margin/
- * Y axis title margin of 60
- *
- * @type {number}
- * @default 40
- * @apioption yAxis.title.margin
- */
- /**
- * The rotation of the text in degrees. 0 is horizontal, 270 is
- * vertical reading from bottom to top.
- *
- * @sample {highcharts} highcharts/yaxis/title-offset/
- * Horizontal
- */
- rotation: 270,
- /**
- * The actual text of the axis title. Horizontal texts can contain
- * HTML, but rotated texts are painted using vector techniques and
- * must be clean text. The Y axis title is disabled by setting the
- * `text` option to `undefined`.
- *
- * @sample {highcharts} highcharts/xaxis/title-text/
- * Custom HTML
- *
- * @type {string|null}
- * @default {highcharts} Values
- * @default {highstock} undefined
- * @product highcharts highstock gantt
- */
- text: 'Values'
- },
- /**
- * The top position of the Y axis. If it's a number, it is interpreted
- * as pixel position relative to the chart.
- *
- * Since Highstock 2: If it's a percentage string, it is interpreted
- * as percentages of the plot height, offset from plot area top.
- *
- * @see [yAxis.height](#yAxis.height)
- *
- * @sample {highstock} stock/demo/candlestick-and-volume/
- * Percentage height panes
- *
- * @type {number|string}
- * @product highstock
- * @apioption yAxis.top
- */
- /**
- * The stack labels show the total value for each bar in a stacked
- * column or bar chart. The label will be placed on top of positive
- * columns and below negative columns. In case of an inverted column
- * chart or a bar chart the label is placed to the right of positive
- * bars and to the left of negative bars.
- *
- * @product highcharts
- */
- stackLabels: {
- /**
- * Allow the stack labels to overlap.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
- * Default false
- *
- * @since 5.0.13
- * @product highcharts
- */
- allowOverlap: false,
- /**
- * Enable or disable the stack total labels.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
- * Enabled stack total labels
- *
- * @since 2.1.5
- * @product highcharts
- */
- enabled: false,
- /**
- * Callback JavaScript function to format the label. The value is
- * given by `this.total`.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
- * Added units to stack total value
- *
- * @type {Highcharts.FormatterCallbackFunction<object>}
- * @since 2.1.5
- * @product highcharts
- */
- formatter: function () {
- return H.numberFormat(this.total, -1);
- },
- /**
- * CSS styles for the label.
- *
- * In styled mode, the styles are set in the
- * `.highcharts-stack-label` class.
- *
- * @sample {highcharts} highcharts/yaxis/stacklabels-style/
- * Red stack total labels
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "#666666", "fontSize": "11px", "fontWeight": "bold", "textOutline": "1px contrast"}
- * @since 2.1.5
- * @product highcharts
- */
- style: {
- /** @ignore-option */
- color: '#000000',
- /** @ignore-option */
- fontSize: '11px',
- /** @ignore-option */
- fontWeight: 'bold',
- /** @ignore-option */
- textOutline: '1px contrast'
- }
- },
- gridLineWidth: 1,
- lineWidth: 0
- // tickWidth: 0
- },
- /**
- * The Z axis or depth axis for 3D plots.
- *
- * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
- * access to the axis.
- *
- * @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
- * Z-Axis with Categories
- * @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
- * Z-Axis with styling
- *
- * @type {*|Array<*>}
- * @extends xAxis
- * @since 5.0.0
- * @product highcharts
- * @excluding breaks, crosshair, lineColor, lineWidth, nameToX, showEmpty
- * @apioption zAxis
- */
- // This variable extends the defaultOptions for left axes.
- defaultLeftAxisOptions: {
- labels: {
- x: -15
- },
- title: {
- rotation: 270
- }
- },
- // This variable extends the defaultOptions for right axes.
- defaultRightAxisOptions: {
- labels: {
- x: 15
- },
- title: {
- rotation: 90
- }
- },
- // This variable extends the defaultOptions for bottom axes.
- defaultBottomAxisOptions: {
- labels: {
- autoRotation: [-45],
- x: 0
- // overflow: undefined,
- // staggerLines: null
- },
- title: {
- rotation: 0
- }
- },
- // This variable extends the defaultOptions for top axes.
- defaultTopAxisOptions: {
- labels: {
- autoRotation: [-45],
- x: 0
- // overflow: undefined
- // staggerLines: null
- },
- title: {
- rotation: 0
- }
- },
- /**
- * Overrideable function to initialize the axis.
- *
- * @see {@link Axis}
- *
- * @function Highcharts.Axis#init
- *
- * @param {Highcharts.Chart} chart
- *
- * @param {Highcharts.Options} userOptions
- *
- * @fires Highcharts.Axis#event:afterInit
- * @fires Highcharts.Axis#event:init
- */
- init: function (chart, userOptions) {
- var isXAxis = userOptions.isX,
- axis = this;
- /**
- * The Chart that the axis belongs to.
- *
- * @name Highcharts.Axis#chart
- * @type {Highcharts.Chart}
- */
- axis.chart = chart;
- /**
- * Whether the axis is horizontal.
- *
- * @name Highcharts.Axis#horiz
- * @type {boolean}
- */
- axis.horiz = chart.inverted && !axis.isZAxis ? !isXAxis : isXAxis;
- /**
- * Whether the axis is the x-axis.
- *
- * @name Highcharts.Axis#isXAxis
- * @type {boolean|undefined}
- */
- axis.isXAxis = isXAxis;
- /**
- * The collection where the axis belongs, for example `xAxis`, `yAxis`
- * or `colorAxis`. Corresponds to properties on Chart, for example
- * {@link Chart.xAxis}.
- *
- * @name Highcharts.Axis#coll
- * @type {string}
- */
- axis.coll = axis.coll || (isXAxis ? 'xAxis' : 'yAxis');
- fireEvent(this, 'init', { userOptions: userOptions });
- axis.opposite = userOptions.opposite; // needed in setOptions
- /**
- * The side on which the axis is rendered. 0 is top, 1 is right, 2
- * is bottom and 3 is left.
- *
- * @name Highcharts.Axis#side
- * @type {number}
- */
- axis.side = userOptions.side || (axis.horiz ?
- (axis.opposite ? 0 : 2) : // top : bottom
- (axis.opposite ? 1 : 3)); // right : left
- axis.setOptions(userOptions);
- var options = this.options,
- type = options.type,
- isDatetimeAxis = type === 'datetime';
- axis.labelFormatter = options.labels.formatter ||
- // can be overwritten by dynamic format
- axis.defaultLabelFormatter;
- // Flag, stagger lines or not
- axis.userOptions = userOptions;
- axis.minPixelPadding = 0;
- /**
- * Whether the axis is reversed. Based on the `axis.reversed`,
- * option, but inverted charts have reversed xAxis by default.
- *
- * @name Highcharts.Axis#reversed
- * @type {boolean}
- */
- axis.reversed = options.reversed;
- axis.visible = options.visible !== false;
- axis.zoomEnabled = options.zoomEnabled !== false;
- // Initial categories
- axis.hasNames = type === 'category' || options.categories === true;
- axis.categories = options.categories || axis.hasNames;
- if (!axis.names) { // Preserve on update (#3830)
- axis.names = [];
- axis.names.keys = {};
- }
- // Placeholder for plotlines and plotbands groups
- axis.plotLinesAndBandsGroups = {};
- // Shorthand types
- axis.isLog = type === 'logarithmic';
- axis.isDatetimeAxis = isDatetimeAxis;
- axis.positiveValuesOnly = axis.isLog && !axis.allowNegativeLog;
- // Flag, if axis is linked to another axis
- axis.isLinked = defined(options.linkedTo);
- /**
- * List of major ticks mapped by postition on axis.
- *
- * @see {@link Highcharts.Tick}
- *
- * @private
- * @name Highcharts.Axis#ticks
- * @type {Highcharts.Dictionary<Highcharts.Tick>}
- */
- axis.ticks = {};
- axis.labelEdge = [];
- /**
- * List of minor ticks mapped by position on the axis.
- *
- * @see {@link Highcharts.Tick}
- *
- * @private
- * @name Highcharts.Axis#minorTicks
- * @type {Highcharts.Dictionary<Highcharts.Tick>}
- */
- axis.minorTicks = {};
- // List of plotLines/Bands
- axis.plotLinesAndBands = [];
- // Alternate bands
- axis.alternateBands = {};
- // Axis metrics
- axis.len = 0;
- axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
- axis.range = options.range;
- axis.offset = options.offset || 0;
- // Dictionary for stacks
- axis.stacks = {};
- axis.oldStacks = {};
- axis.stacksTouched = 0;
- /**
- * The maximum value of the axis. In a logarithmic axis, this is the
- * logarithm of the real value, and the real value can be obtained from
- * {@link Axis#getExtremes}.
- *
- * @name Highcharts.Axis#max
- * @type {number|null}
- */
- axis.max = null;
- /**
- * The minimum value of the axis. In a logarithmic axis, this is the
- * logarithm of the real value, and the real value can be obtained from
- * {@link Axis#getExtremes}.
- *
- * @name Highcharts.Axis#min
- * @type {number|null}
- */
- axis.min = null;
- /**
- * The processed crosshair options.
- *
- * @name Highcharts.Axis#crosshair
- * @type {boolean|Highcharts.AxisCrosshairOptions}
- */
- axis.crosshair = pick(
- options.crosshair,
- splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1],
- false
- );
- var events = axis.options.events;
- // Register. Don't add it again on Axis.update().
- if (chart.axes.indexOf(axis) === -1) { //
- if (isXAxis) { // #2713
- chart.axes.splice(chart.xAxis.length, 0, axis);
- } else {
- chart.axes.push(axis);
- }
- chart[axis.coll].push(axis);
- }
- /**
- * All series associated to the axis.
- *
- * @name Highcharts.Axis#series
- * @type {Array<Highcharts.Series>}
- */
- axis.series = axis.series || []; // populated by Series
- // Reversed axis
- if (
- chart.inverted &&
- !axis.isZAxis &&
- isXAxis &&
- axis.reversed === undefined
- ) {
- axis.reversed = true;
- }
- // register event listeners
- objectEach(events, function (event, eventType) {
- addEvent(axis, eventType, event);
- });
- // extend logarithmic axis
- axis.lin2log = options.linearToLogConverter || axis.lin2log;
- if (axis.isLog) {
- axis.val2lin = axis.log2lin;
- axis.lin2val = axis.lin2log;
- }
- fireEvent(this, 'afterInit');
- },
- /**
- * Merge and set options.
- *
- * @private
- * @function Highcharts.Axis#setOptions
- *
- * @param {Highcharts.AxisOptions} userOptions
- *
- * @fires Highcharts.Axis#event:afterSetOptions
- */
- setOptions: function (userOptions) {
- this.options = merge(
- this.defaultOptions,
- this.coll === 'yAxis' && this.defaultYAxisOptions,
- [
- this.defaultTopAxisOptions,
- this.defaultRightAxisOptions,
- this.defaultBottomAxisOptions,
- this.defaultLeftAxisOptions
- ][this.side],
- merge(
- defaultOptions[this.coll], // if set in setOptions (#1053)
- userOptions
- )
- );
- fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
- },
- /**
- * The default label formatter. The context is a special config object for
- * the label. In apps, use the
- * [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
- * instead except when a modification is needed.
- * @private
- */
- defaultLabelFormatter: function () {
- var axis = this.axis,
- value = this.value,
- time = axis.chart.time,
- categories = axis.categories,
- dateTimeLabelFormat = this.dateTimeLabelFormat,
- lang = defaultOptions.lang,
- numericSymbols = lang.numericSymbols,
- numSymMagnitude = lang.numericSymbolMagnitude || 1000,
- i = numericSymbols && numericSymbols.length,
- multi,
- ret,
- formatOption = axis.options.labels.format,
- // make sure the same symbol is added for all labels on a linear
- // axis
- numericSymbolDetector = axis.isLog ?
- Math.abs(value) :
- axis.tickInterval;
- if (formatOption) {
- ret = format(formatOption, this, time);
- } else if (categories) {
- ret = value;
- } else if (dateTimeLabelFormat) { // datetime axis
- ret = time.dateFormat(dateTimeLabelFormat, value);
- } else if (i && numericSymbolDetector >= 1000) {
- // Decide whether we should add a numeric symbol like k (thousands)
- // or M (millions). If we are to enable this in tooltip or other
- // places as well, we can move this logic to the numberFormatter and
- // enable it by a parameter.
- while (i-- && ret === undefined) {
- multi = Math.pow(numSymMagnitude, i + 1);
- if (
- // Only accept a numeric symbol when the distance is more
- // than a full unit. So for example if the symbol is k, we
- // don't accept numbers like 0.5k.
- numericSymbolDetector >= multi &&
- // Accept one decimal before the symbol. Accepts 0.5k but
- // not 0.25k. How does this work with the previous?
- (value * 10) % multi === 0 &&
- numericSymbols[i] !== null &&
- value !== 0
- ) { // #5480
- ret = H.numberFormat(value / multi, -1) + numericSymbols[i];
- }
- }
- }
- if (ret === undefined) {
- if (Math.abs(value) >= 10000) { // add thousands separators
- ret = H.numberFormat(value, -1);
- } else { // small numbers
- ret = H.numberFormat(value, -1, undefined, ''); // #2466
- }
- }
- return ret;
- },
- /**
- * Get the minimum and maximum for the series of each axis. The function
- * analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
- * @private
- * @fires Highcharts.Axis#event:afterGetSeriesExtremes
- * @fires Highcharts.Axis#event:getSeriesExtremes
- */
- getSeriesExtremes: function () {
- var axis = this,
- chart = axis.chart;
- fireEvent(this, 'getSeriesExtremes', null, function () {
- axis.hasVisibleSeries = false;
- // Reset properties in case we're redrawing (#3353)
- axis.dataMin = axis.dataMax = axis.threshold = null;
- axis.softThreshold = !axis.isXAxis;
- if (axis.buildStacks) {
- axis.buildStacks();
- }
- // loop through this axis' series
- axis.series.forEach(function (series) {
- if (series.visible || !chart.options.chart.ignoreHiddenSeries) {
- var seriesOptions = series.options,
- xData,
- threshold = seriesOptions.threshold,
- seriesDataMin,
- seriesDataMax;
- axis.hasVisibleSeries = true;
- // Validate threshold in logarithmic axes
- if (axis.positiveValuesOnly && threshold <= 0) {
- threshold = null;
- }
- // Get dataMin and dataMax for X axes
- if (axis.isXAxis) {
- xData = series.xData;
- if (xData.length) {
- // If xData contains values which is not numbers,
- // then filter them out. To prevent performance hit,
- // we only do this after we have already found
- // seriesDataMin because in most cases all data is
- // valid. #5234.
- seriesDataMin = arrayMin(xData);
- seriesDataMax = arrayMax(xData);
- if (
- !isNumber(seriesDataMin) &&
- !(seriesDataMin instanceof Date) // #5010
- ) {
- xData = xData.filter(isNumber);
- // Do it again with valid data
- seriesDataMin = arrayMin(xData);
- seriesDataMax = arrayMax(xData);
- }
- if (xData.length) {
- axis.dataMin = Math.min(
- pick(axis.dataMin, xData[0], seriesDataMin),
- seriesDataMin
- );
- axis.dataMax = Math.max(
- pick(axis.dataMax, xData[0], seriesDataMax),
- seriesDataMax
- );
- }
- }
- // Get dataMin and dataMax for Y axes, as well as handle
- // stacking and processed data
- } else {
- // Get this particular series extremes
- series.getExtremes();
- seriesDataMax = series.dataMax;
- seriesDataMin = series.dataMin;
- // Get the dataMin and dataMax so far. If percentage is
- // used, the min and max are always 0 and 100. If
- // seriesDataMin and seriesDataMax is null, then series
- // doesn't have active y data, we continue with nulls
- if (defined(seriesDataMin) && defined(seriesDataMax)) {
- axis.dataMin = Math.min(
- pick(axis.dataMin, seriesDataMin),
- seriesDataMin
- );
- axis.dataMax = Math.max(
- pick(axis.dataMax, seriesDataMax),
- seriesDataMax
- );
- }
- // Adjust to threshold
- if (defined(threshold)) {
- axis.threshold = threshold;
- }
- // If any series has a hard threshold, it takes
- // precedence
- if (
- !seriesOptions.softThreshold ||
- axis.positiveValuesOnly
- ) {
- axis.softThreshold = false;
- }
- }
- }
- });
- });
- fireEvent(this, 'afterGetSeriesExtremes');
- },
- /**
- * Translate from axis value to pixel position on the chart, or back. Use
- * the `toPixels` and `toValue` functions in applications.
- * @private
- */
- translate: function (
- val,
- backwards,
- cvsCoord,
- old,
- handleLog,
- pointPlacement
- ) {
- var axis = this.linkedParent || this, // #1417
- sign = 1,
- cvsOffset = 0,
- localA = old ? axis.oldTransA : axis.transA,
- localMin = old ? axis.oldMin : axis.min,
- returnValue,
- minPixelPadding = axis.minPixelPadding,
- doPostTranslate = (
- axis.isOrdinal ||
- axis.isBroken ||
- (axis.isLog && handleLog)
- ) && axis.lin2val;
- if (!localA) {
- localA = axis.transA;
- }
- // In vertical axes, the canvas coordinates start from 0 at the top like
- // in SVG.
- if (cvsCoord) {
- sign *= -1; // canvas coordinates inverts the value
- cvsOffset = axis.len;
- }
- // Handle reversed axis
- if (axis.reversed) {
- sign *= -1;
- cvsOffset -= sign * (axis.sector || axis.len);
- }
- // From pixels to value
- if (backwards) { // reverse translation
- val = val * sign + cvsOffset;
- val -= minPixelPadding;
- returnValue = val / localA + localMin; // from chart pixel to value
- if (doPostTranslate) { // log and ordinal axes
- returnValue = axis.lin2val(returnValue);
- }
- // From value to pixels
- } else {
- if (doPostTranslate) { // log and ordinal axes
- val = axis.val2lin(val);
- }
- returnValue = isNumber(localMin) ?
- (
- sign * (val - localMin) * localA +
- cvsOffset +
- (sign * minPixelPadding) +
- (isNumber(pointPlacement) ? localA * pointPlacement : 0)
- ) :
- undefined;
- }
- return returnValue;
- },
- /**
- * Translate a value in terms of axis units into pixels within the chart.
- *
- * @function Highcharts.Axis#toPixels
- *
- * @param {number} value
- * A value in terms of axis units.
- *
- * @param {boolean} paneCoordinates
- * Whether to return the pixel coordinate relative to the chart or
- * just the axis/pane itself.
- *
- * @return {number}
- * Pixel position of the value on the chart or axis.
- */
- toPixels: function (value, paneCoordinates) {
- return this.translate(value, false, !this.horiz, null, true) +
- (paneCoordinates ? 0 : this.pos);
- },
- /**
- * Translate a pixel position along the axis to a value in terms of axis
- * units.
- *
- * @function Highcharts.Axis#toValue
- *
- * @param {number} pixel
- * The pixel value coordinate.
- *
- * @param {boolean} paneCoordiantes
- * Whether the input pixel is relative to the chart or just the
- * axis/pane itself.
- *
- * @return {number}
- * The axis value.
- */
- toValue: function (pixel, paneCoordinates) {
- return this.translate(
- pixel - (paneCoordinates ? 0 : this.pos),
- true,
- !this.horiz,
- null,
- true
- );
- },
- /**
- * Create the path for a plot line that goes from the given value on
- * this axis, across the plot to the opposite side. Also used internally for
- * grid lines and crosshairs.
- *
- * @function Highcharts.Axis#getPlotLinePath
- *
- * @param {number} value
- * Axis value.
- *
- * @param {number} [lineWidth=1]
- * Used for calculation crisp line coordinates.
- *
- * @param {boolean} [old=false]
- * Use old coordinates (for resizing and rescaling).
- *
- * @param {boolean|string} [force=false]
- * If `false`, the function will return null when it falls outside
- * the axis bounds. If `true`, the function will return a path
- * aligned to the plot area sides if it falls outside. If `pass`, it
- * will return a path outside.
- *
- * @param {number} [translatedValue]
- * If given, return the plot line path of a pixel position on the
- * axis.
- *
- * @return {Array<string|number>}
- * The SVG path definition for the plot line.
- */
- getPlotLinePath: function (value, lineWidth, old, force, translatedValue) {
- var axis = this,
- chart = axis.chart,
- axisLeft = axis.left,
- axisTop = axis.top,
- x1,
- y1,
- x2,
- y2,
- cHeight = (old && chart.oldChartHeight) || chart.chartHeight,
- cWidth = (old && chart.oldChartWidth) || chart.chartWidth,
- skip,
- transB = axis.transB,
- evt,
- /**
- * Check if x is between a and b. If not, either move to a/b
- * or skip, depending on the force parameter.
- */
- between = function (x, a, b) {
- if (force !== 'pass' && x < a || x > b) {
- if (force) {
- x = Math.min(Math.max(a, x), b);
- } else {
- skip = true;
- }
- }
- return x;
- };
- evt = {
- value: value,
- lineWidth: lineWidth,
- old: old,
- force: force,
- translatedValue: translatedValue
- };
- fireEvent(this, 'getPlotLinePath', evt, function (e) {
- translatedValue = pick(
- translatedValue,
- axis.translate(value, null, null, old)
- );
- // Keep the translated value within sane bounds, and avoid Infinity
- // to fail the isNumber test (#7709).
- translatedValue = Math.min(Math.max(-1e5, translatedValue), 1e5);
- x1 = x2 = Math.round(translatedValue + transB);
- y1 = y2 = Math.round(cHeight - translatedValue - transB);
- if (!isNumber(translatedValue)) { // no min or max
- skip = true;
- force = false; // #7175, don't force it when path is invalid
- } else if (axis.horiz) {
- y1 = axisTop;
- y2 = cHeight - axis.bottom;
- x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
- } else {
- x1 = axisLeft;
- x2 = cWidth - axis.right;
- y1 = y2 = between(y1, axisTop, axisTop + axis.height);
- }
- e.path = skip && !force ?
- null :
- chart.renderer.crispLine(
- ['M', x1, y1, 'L', x2, y2],
- lineWidth || 1
- );
- });
- return evt.path;
- },
- /**
- * Internal function to et the tick positions of a linear axis to round
- * values like whole tens or every five.
- *
- * @function Highcharts.Axis#getLinearTickPositions
- *
- * @param {number} tickInterval
- * The normalized tick interval.
- *
- * @param {number} min
- * Axis minimum.
- *
- * @param {number} max
- * Axis maximum.
- *
- * @return {Array<number>}
- * An array of axis values where ticks should be placed.
- */
- getLinearTickPositions: function (tickInterval, min, max) {
- var pos,
- lastPos,
- roundedMin =
- correctFloat(Math.floor(min / tickInterval) * tickInterval),
- roundedMax =
- correctFloat(Math.ceil(max / tickInterval) * tickInterval),
- tickPositions = [],
- precision;
- // When the precision is higher than what we filter out in
- // correctFloat, skip it (#6183).
- if (correctFloat(roundedMin + tickInterval) === roundedMin) {
- precision = 20;
- }
- // For single points, add a tick regardless of the relative position
- // (#2662, #6274)
- if (this.single) {
- return [min];
- }
- // Populate the intermediate values
- pos = roundedMin;
- while (pos <= roundedMax) {
- // Place the tick on the rounded value
- tickPositions.push(pos);
- // Always add the raw tickInterval, not the corrected one.
- pos = correctFloat(
- pos + tickInterval,
- precision
- );
- // If the interval is not big enough in the current min - max range
- // to actually increase the loop variable, we need to break out to
- // prevent endless loop. Issue #619
- if (pos === lastPos) {
- break;
- }
- // Record the last value
- lastPos = pos;
- }
- return tickPositions;
- },
- /**
- * Resolve the new minorTicks/minorTickInterval options into the legacy
- * loosely typed minorTickInterval option.
- *
- * @function Highcharts.Axis#getMinorTickInterval
- *
- * @return {number|"auto"|null}
- */
- getMinorTickInterval: function () {
- var options = this.options;
- if (options.minorTicks === true) {
- return pick(options.minorTickInterval, 'auto');
- }
- if (options.minorTicks === false) {
- return null;
- }
- return options.minorTickInterval;
- },
- /**
- * Internal function to return the minor tick positions. For logarithmic
- * axes, the same logic as for major ticks is reused.
- *
- * @function Highcharts.Axis#getMinorTickPositions
- *
- * @return {Array<number>}
- * An array of axis values where ticks should be placed.
- */
- getMinorTickPositions: function () {
- var axis = this,
- options = axis.options,
- tickPositions = axis.tickPositions,
- minorTickInterval = axis.minorTickInterval,
- minorTickPositions = [],
- pos,
- pointRangePadding = axis.pointRangePadding || 0,
- min = axis.min - pointRangePadding, // #1498
- max = axis.max + pointRangePadding, // #1498
- range = max - min;
- // If minor ticks get too dense, they are hard to read, and may cause
- // long running script. So we don't draw them.
- if (range && range / minorTickInterval < axis.len / 3) { // #3875
- if (axis.isLog) {
- // For each interval in the major ticks, compute the minor ticks
- // separately.
- this.paddedTicks.forEach(function (pos, i, paddedTicks) {
- if (i) {
- minorTickPositions.push.apply(
- minorTickPositions,
- axis.getLogTickPositions(
- minorTickInterval,
- paddedTicks[i - 1],
- paddedTicks[i],
- true
- )
- );
- }
- });
- } else if (
- axis.isDatetimeAxis &&
- this.getMinorTickInterval() === 'auto'
- ) { // #1314
- minorTickPositions = minorTickPositions.concat(
- axis.getTimeTicks(
- axis.normalizeTimeTickInterval(minorTickInterval),
- min,
- max,
- options.startOfWeek
- )
- );
- } else {
- for (
- pos = min + (tickPositions[0] - min) % minorTickInterval;
- pos <= max;
- pos += minorTickInterval
- ) {
- // Very, very, tight grid lines (#5771)
- if (pos === minorTickPositions[0]) {
- break;
- }
- minorTickPositions.push(pos);
- }
- }
- }
- if (minorTickPositions.length !== 0) {
- axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
- }
- return minorTickPositions;
- },
- /**
- * Adjust the min and max for the minimum range. Keep in mind that the
- * series data is not yet processed, so we don't have information on data
- * cropping and grouping, or updated `axis.pointRange` or
- * `series.pointRange`. The data can't be processed until we have finally
- * established min and max.
- * @private
- */
- adjustForMinRange: function () {
- var axis = this,
- options = axis.options,
- min = axis.min,
- max = axis.max,
- zoomOffset,
- spaceAvailable,
- closestDataRange,
- i,
- distance,
- xData,
- loopLength,
- minArgs,
- maxArgs,
- minRange;
- // Set the automatic minimum range based on the closest point distance
- if (axis.isXAxis && axis.minRange === undefined && !axis.isLog) {
- if (defined(options.min) || defined(options.max)) {
- axis.minRange = null; // don't do this again
- } else {
- // Find the closest distance between raw data points, as opposed
- // to closestPointRange that applies to processed points
- // (cropped and grouped)
- axis.series.forEach(function (series) {
- xData = series.xData;
- loopLength = series.xIncrement ? 1 : xData.length - 1;
- for (i = loopLength; i > 0; i--) {
- distance = xData[i] - xData[i - 1];
- if (
- closestDataRange === undefined ||
- distance < closestDataRange
- ) {
- closestDataRange = distance;
- }
- }
- });
- axis.minRange = Math.min(
- closestDataRange * 5,
- axis.dataMax - axis.dataMin
- );
- }
- }
- // if minRange is exceeded, adjust
- if (max - min < axis.minRange) {
- spaceAvailable = axis.dataMax - axis.dataMin >= axis.minRange;
- minRange = axis.minRange;
- zoomOffset = (minRange - max + min) / 2;
- // if min and max options have been set, don't go beyond it
- minArgs = [min - zoomOffset, pick(options.min, min - zoomOffset)];
- // If space is available, stay within the data range
- if (spaceAvailable) {
- minArgs[2] = axis.isLog ?
- axis.log2lin(axis.dataMin) :
- axis.dataMin;
- }
- min = arrayMax(minArgs);
- maxArgs = [min + minRange, pick(options.max, min + minRange)];
- // If space is availabe, stay within the data range
- if (spaceAvailable) {
- maxArgs[2] = axis.isLog ?
- axis.log2lin(axis.dataMax) :
- axis.dataMax;
- }
- max = arrayMin(maxArgs);
- // now if the max is adjusted, adjust the min back
- if (max - min < minRange) {
- minArgs[0] = max - minRange;
- minArgs[1] = pick(options.min, max - minRange);
- min = arrayMax(minArgs);
- }
- }
- // Record modified extremes
- axis.min = min;
- axis.max = max;
- },
- // Find the closestPointRange across all series.
- getClosest: function () {
- var ret;
- if (this.categories) {
- ret = 1;
- } else {
- this.series.forEach(function (series) {
- var seriesClosest = series.closestPointRange,
- visible = series.visible ||
- !series.chart.options.chart.ignoreHiddenSeries;
- if (
- !series.noSharedTooltip &&
- defined(seriesClosest) &&
- visible
- ) {
- ret = defined(ret) ?
- Math.min(ret, seriesClosest) :
- seriesClosest;
- }
- });
- }
- return ret;
- },
- /**
- * When a point name is given and no x, search for the name in the existing
- * categories, or if categories aren't provided, search names or create a
- * new category (#2522).
- * @private
- * @param {Highcharts.Point} point The point to inspect.
- * @return {number} The X value that the point is given.
- */
- nameToX: function (point) {
- var explicitCategories = isArray(this.categories),
- names = explicitCategories ? this.categories : this.names,
- nameX = point.options.x,
- x;
- point.series.requireSorting = false;
- if (!defined(nameX)) {
- nameX = this.options.uniqueNames === false ?
- point.series.autoIncrement() :
- (
- explicitCategories ?
- names.indexOf(point.name) :
- pick(names.keys[point.name], -1)
- );
- }
- if (nameX === -1) { // Not found in currenct categories
- if (!explicitCategories) {
- x = names.length;
- }
- } else {
- x = nameX;
- }
- // Write the last point's name to the names array
- if (x !== undefined) {
- this.names[x] = point.name;
- // Backwards mapping is much faster than array searching (#7725)
- this.names.keys[point.name] = x;
- }
- return x;
- },
- // When changes have been done to series data, update the axis.names.
- updateNames: function () {
- var axis = this,
- names = this.names,
- i = names.length;
- if (i > 0) {
- Object.keys(names.keys).forEach(function (key) {
- delete names.keys[key];
- });
- names.length = 0;
- this.minRange = this.userMinRange; // Reset
- (this.series || []).forEach(function (series) {
- // Reset incrementer (#5928)
- series.xIncrement = null;
- // When adding a series, points are not yet generated
- if (!series.points || series.isDirtyData) {
- // When we're updating the series with data that is longer
- // than it was, and cropThreshold is passed, we need to make
- // sure that the axis.max is increased _before_ running the
- // premature processData. Otherwise this early iteration of
- // processData will crop the points to axis.max, and the
- // names array will be too short (#5857).
- axis.max = Math.max(axis.max, series.xData.length - 1);
- series.processData();
- series.generatePoints();
- }
- series.data.forEach(function (point, i) { // #9487
- var x;
- if (
- point &&
- point.options &&
- point.name !== undefined // #9562
- ) {
- x = axis.nameToX(point);
- if (x !== undefined && x !== point.x) {
- point.x = x;
- series.xData[i] = x;
- }
- }
- });
- });
- }
- },
- /**
- * Update translation information.
- * @private
- * @param {boolean} saveOld
- * @fires Highcharts.Axis#event:afterSetAxisTranslation
- */
- setAxisTranslation: function (saveOld) {
- var axis = this,
- range = axis.max - axis.min,
- pointRange = axis.axisPointRange || 0,
- closestPointRange,
- minPointOffset = 0,
- pointRangePadding = 0,
- linkedParent = axis.linkedParent,
- ordinalCorrection,
- hasCategories = !!axis.categories,
- transA = axis.transA,
- isXAxis = axis.isXAxis;
- // Adjust translation for padding. Y axis with categories need to go
- // through the same (#1784).
- if (isXAxis || hasCategories || pointRange) {
- // Get the closest points
- closestPointRange = axis.getClosest();
- if (linkedParent) {
- minPointOffset = linkedParent.minPointOffset;
- pointRangePadding = linkedParent.pointRangePadding;
- } else {
- axis.series.forEach(function (series) {
- var seriesPointRange = hasCategories ?
- 1 :
- (
- isXAxis ?
- pick(
- series.options.pointRange,
- closestPointRange,
- 0
- ) :
- (axis.axisPointRange || 0)
- ), // #2806
- pointPlacement = series.options.pointPlacement;
- pointRange = Math.max(pointRange, seriesPointRange);
- if (!axis.single) {
- // minPointOffset is the value padding to the left of
- // the axis in order to make room for points with a
- // pointRange, typically columns. When the
- // pointPlacement option is 'between' or 'on', this
- // padding does not apply.
- minPointOffset = Math.max(
- minPointOffset,
- isXAxis && isString(pointPlacement) ?
- 0 : seriesPointRange / 2
- );
- // Determine the total padding needed to the length of
- // the axis to make room for the pointRange. If the
- // series' pointPlacement is 'on', no padding is added.
- pointRangePadding = Math.max(
- pointRangePadding,
- isXAxis && pointPlacement === 'on' ?
- 0 : seriesPointRange
- );
- }
- });
- }
- // Record minPointOffset and pointRangePadding
- ordinalCorrection = axis.ordinalSlope && closestPointRange ?
- axis.ordinalSlope / closestPointRange :
- 1; // #988, #1853
- axis.minPointOffset = minPointOffset =
- minPointOffset * ordinalCorrection;
- axis.pointRangePadding =
- pointRangePadding = pointRangePadding * ordinalCorrection;
- // pointRange means the width reserved for each point, like in a
- // column chart
- axis.pointRange = Math.min(pointRange, range);
- // closestPointRange means the closest distance between points. In
- // columns it is mostly equal to pointRange, but in lines pointRange
- // is 0 while closestPointRange is some other value
- if (isXAxis) {
- axis.closestPointRange = closestPointRange;
- }
- }
- // Secondary values
- if (saveOld) {
- axis.oldTransA = transA;
- }
- axis.translationSlope = axis.transA = transA =
- axis.staticScale ||
- axis.len / ((range + pointRangePadding) || 1);
- // Translation addend
- axis.transB = axis.horiz ? axis.left : axis.bottom;
- axis.minPixelPadding = transA * minPointOffset;
- fireEvent(this, 'afterSetAxisTranslation');
- },
- minFromRange: function () {
- return this.max - this.range;
- },
- /**
- * Set the tick positions to round values and optionally extend the extremes
- * to the nearest tick.
- * @private
- * @param {boolean} secondPass
- * @fires Highcharts.Axis#event:foundExtremes
- */
- setTickInterval: function (secondPass) {
- var axis = this,
- chart = axis.chart,
- options = axis.options,
- isLog = axis.isLog,
- isDatetimeAxis = axis.isDatetimeAxis,
- isXAxis = axis.isXAxis,
- isLinked = axis.isLinked,
- maxPadding = options.maxPadding,
- minPadding = options.minPadding,
- length,
- linkedParentExtremes,
- tickIntervalOption = options.tickInterval,
- minTickInterval,
- tickPixelIntervalOption = options.tickPixelInterval,
- categories = axis.categories,
- threshold = isNumber(axis.threshold) ? axis.threshold : null,
- softThreshold = axis.softThreshold,
- thresholdMin,
- thresholdMax,
- hardMin,
- hardMax;
- if (!isDatetimeAxis && !categories && !isLinked) {
- this.getTickAmount();
- }
- // Min or max set either by zooming/setExtremes or initial options
- hardMin = pick(axis.userMin, options.min);
- hardMax = pick(axis.userMax, options.max);
- // Linked axis gets the extremes from the parent axis
- if (isLinked) {
- axis.linkedParent = chart[axis.coll][options.linkedTo];
- linkedParentExtremes = axis.linkedParent.getExtremes();
- axis.min = pick(
- linkedParentExtremes.min,
- linkedParentExtremes.dataMin
- );
- axis.max = pick(
- linkedParentExtremes.max,
- linkedParentExtremes.dataMax
- );
- if (options.type !== axis.linkedParent.options.type) {
- H.error(11, 1, chart); // Can't link axes of different type
- }
- // Initial min and max from the extreme data values
- } else {
- // Adjust to hard threshold
- if (!softThreshold && defined(threshold)) {
- if (axis.dataMin >= threshold) {
- thresholdMin = threshold;
- minPadding = 0;
- } else if (axis.dataMax <= threshold) {
- thresholdMax = threshold;
- maxPadding = 0;
- }
- }
- axis.min = pick(hardMin, thresholdMin, axis.dataMin);
- axis.max = pick(hardMax, thresholdMax, axis.dataMax);
- }
- if (isLog) {
- if (
- axis.positiveValuesOnly &&
- !secondPass &&
- Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0
- ) { // #978
- H.error(10, 1, chart); // Can't plot negative values on log axis
- }
- // The correctFloat cures #934, float errors on full tens. But it
- // was too aggressive for #4360 because of conversion back to lin,
- // therefore use precision 15.
- axis.min = correctFloat(axis.log2lin(axis.min), 15);
- axis.max = correctFloat(axis.log2lin(axis.max), 15);
- }
- // handle zoomed range
- if (axis.range && defined(axis.max)) {
- axis.userMin = axis.min = hardMin =
- Math.max(axis.dataMin, axis.minFromRange()); // #618, #6773
- axis.userMax = hardMax = axis.max;
- axis.range = null; // don't use it when running setExtremes
- }
- // Hook for Highstock Scroller. Consider combining with beforePadding.
- fireEvent(axis, 'foundExtremes');
- // Hook for adjusting this.min and this.max. Used by bubble series.
- if (axis.beforePadding) {
- axis.beforePadding();
- }
- // adjust min and max for the minimum range
- axis.adjustForMinRange();
- // Pad the values to get clear of the chart's edges. To avoid
- // tickInterval taking the padding into account, we do this after
- // computing tick interval (#1337).
- if (
- !categories &&
- !axis.axisPointRange &&
- !axis.usePercentage &&
- !isLinked &&
- defined(axis.min) &&
- defined(axis.max)
- ) {
- length = axis.max - axis.min;
- if (length) {
- if (!defined(hardMin) && minPadding) {
- axis.min -= length * minPadding;
- }
- if (!defined(hardMax) && maxPadding) {
- axis.max += length * maxPadding;
- }
- }
- }
- // Handle options for floor, ceiling, softMin and softMax (#6359)
- if (isNumber(options.softMin) && !isNumber(axis.userMin)) {
- axis.min = Math.min(axis.min, options.softMin);
- }
- if (isNumber(options.softMax) && !isNumber(axis.userMax)) {
- axis.max = Math.max(axis.max, options.softMax);
- }
- if (isNumber(options.floor)) {
- axis.min = Math.min(
- Math.max(axis.min, options.floor),
- Number.MAX_VALUE
- );
- }
- if (isNumber(options.ceiling)) {
- axis.max = Math.max(
- Math.min(axis.max, options.ceiling),
- pick(axis.userMax, -Number.MAX_VALUE)
- );
- }
- // When the threshold is soft, adjust the extreme value only if the data
- // extreme and the padded extreme land on either side of the threshold.
- // For example, a series of [0, 1, 2, 3] would make the yAxis add a tick
- // for -1 because of the default minPadding and startOnTick options.
- // This is prevented by the softThreshold option.
- if (softThreshold && defined(axis.dataMin)) {
- threshold = threshold || 0;
- if (
- !defined(hardMin) &&
- axis.min < threshold &&
- axis.dataMin >= threshold
- ) {
- axis.min = threshold;
- } else if (
- !defined(hardMax) &&
- axis.max > threshold &&
- axis.dataMax <= threshold
- ) {
- axis.max = threshold;
- }
- }
- // get tickInterval
- if (
- axis.min === axis.max ||
- axis.min === undefined ||
- axis.max === undefined
- ) {
- axis.tickInterval = 1;
- } else if (
- isLinked &&
- !tickIntervalOption &&
- tickPixelIntervalOption ===
- axis.linkedParent.options.tickPixelInterval
- ) {
- axis.tickInterval = tickIntervalOption =
- axis.linkedParent.tickInterval;
- } else {
- axis.tickInterval = pick(
- tickIntervalOption,
- this.tickAmount ?
- ((axis.max - axis.min) / Math.max(this.tickAmount - 1, 1)) :
- undefined,
- // For categoried axis, 1 is default, for linear axis use
- // tickPix
- categories ?
- 1 :
- // don't let it be more than the data range
- (axis.max - axis.min) * tickPixelIntervalOption /
- Math.max(axis.len, tickPixelIntervalOption)
- );
- }
- // Now we're finished detecting min and max, crop and group series data.
- // This is in turn needed in order to find tick positions in ordinal
- // axes.
- if (isXAxis && !secondPass) {
- axis.series.forEach(function (series) {
- series.processData(
- axis.min !== axis.oldMin || axis.max !== axis.oldMax
- );
- });
- }
- // set the translation factor used in translate function
- axis.setAxisTranslation(true);
- // hook for ordinal axes and radial axes
- if (axis.beforeSetTickPositions) {
- axis.beforeSetTickPositions();
- }
- // hook for extensions, used in Highstock ordinal axes
- if (axis.postProcessTickInterval) {
- axis.tickInterval = axis.postProcessTickInterval(axis.tickInterval);
- }
- // In column-like charts, don't cramp in more ticks than there are
- // points (#1943, #4184)
- if (axis.pointRange && !tickIntervalOption) {
- axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
- }
- // Before normalizing the tick interval, handle minimum tick interval.
- // This applies only if tickInterval is not defined.
- minTickInterval = pick(
- options.minTickInterval,
- axis.isDatetimeAxis && axis.closestPointRange
- );
- if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
- axis.tickInterval = minTickInterval;
- }
- // for linear axes, get magnitude and normalize the interval
- if (!isDatetimeAxis && !isLog && !tickIntervalOption) {
- axis.tickInterval = normalizeTickInterval(
- axis.tickInterval,
- null,
- getMagnitude(axis.tickInterval),
- // If the tick interval is between 0.5 and 5 and the axis max is
- // in the order of thousands, chances are we are dealing with
- // years. Don't allow decimals. #3363.
- pick(
- options.allowDecimals,
- !(
- axis.tickInterval > 0.5 &&
- axis.tickInterval < 5 &&
- axis.max > 1000 &&
- axis.max < 9999
- )
- ),
- !!this.tickAmount
- );
- }
- // Prevent ticks from getting so close that we can't draw the labels
- if (!this.tickAmount) {
- axis.tickInterval = axis.unsquish();
- }
- this.setTickPositions();
- },
- /**
- * Now we have computed the normalized tickInterval, get the tick positions
- *
- * @function Highcharts.Axis#setTickPositions
- *
- * @fires Highcharts.Axis#event:afterSetTickPositions
- */
- setTickPositions: function () {
- var options = this.options,
- tickPositions,
- tickPositionsOption = options.tickPositions,
- minorTickIntervalOption = this.getMinorTickInterval(),
- tickPositioner = options.tickPositioner,
- startOnTick = options.startOnTick,
- endOnTick = options.endOnTick;
- // Set the tickmarkOffset
- this.tickmarkOffset = (
- this.categories &&
- options.tickmarkPlacement === 'between' &&
- this.tickInterval === 1
- ) ? 0.5 : 0; // #3202
- // get minorTickInterval
- this.minorTickInterval =
- minorTickIntervalOption === 'auto' &&
- this.tickInterval ?
- this.tickInterval / 5 :
- minorTickIntervalOption;
- // When there is only one point, or all points have the same value on
- // this axis, then min and max are equal and tickPositions.length is 0
- // or 1. In this case, add some padding in order to center the point,
- // but leave it with one tick. #1337.
- this.single =
- this.min === this.max &&
- defined(this.min) &&
- !this.tickAmount &&
- (
- // Data is on integer (#6563)
- parseInt(this.min, 10) === this.min ||
- // Between integers and decimals are not allowed (#6274)
- options.allowDecimals !== false
- );
- // Find the tick positions. Work on a copy (#1565)
- this.tickPositions = tickPositions =
- tickPositionsOption && tickPositionsOption.slice();
- if (!tickPositions) {
- // Too many ticks (#6405). Create a friendly warning and provide two
- // ticks so at least we can show the data series.
- if (
- !this.ordinalPositions &&
- (
- (this.max - this.min) / this.tickInterval >
- Math.max(2 * this.len, 200)
- )
- ) {
- tickPositions = [this.min, this.max];
- H.error(19, false, this.chart);
- } else if (this.isDatetimeAxis) {
- tickPositions = this.getTimeTicks(
- this.normalizeTimeTickInterval(
- this.tickInterval,
- options.units
- ),
- this.min,
- this.max,
- options.startOfWeek,
- this.ordinalPositions,
- this.closestPointRange,
- true
- );
- } else if (this.isLog) {
- tickPositions = this.getLogTickPositions(
- this.tickInterval,
- this.min,
- this.max
- );
- } else {
- tickPositions = this.getLinearTickPositions(
- this.tickInterval,
- this.min,
- this.max
- );
- }
- // Too dense ticks, keep only the first and last (#4477)
- if (tickPositions.length > this.len) {
- tickPositions = [tickPositions[0], tickPositions.pop()];
- // Reduce doubled value (#7339)
- if (tickPositions[0] === tickPositions[1]) {
- tickPositions.length = 1;
- }
- }
- this.tickPositions = tickPositions;
- // Run the tick positioner callback, that allows modifying auto tick
- // positions.
- if (tickPositioner) {
- tickPositioner = tickPositioner.apply(
- this,
- [this.min, this.max]
- );
- if (tickPositioner) {
- this.tickPositions = tickPositions = tickPositioner;
- }
- }
- }
- // Reset min/max or remove extremes based on start/end on tick
- this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
- this.trimTicks(tickPositions, startOnTick, endOnTick);
- if (!this.isLinked) {
- // Substract half a unit (#2619, #2846, #2515, #3390),
- // but not in case of multiple ticks (#6897)
- if (this.single && tickPositions.length < 2) {
- this.min -= 0.5;
- this.max += 0.5;
- }
- if (!tickPositionsOption && !tickPositioner) {
- this.adjustTickAmount();
- }
- }
- fireEvent(this, 'afterSetTickPositions');
- },
- // Handle startOnTick and endOnTick by either adapting to padding min/max or
- // rounded min/max. Also handle single data points.
- trimTicks: function (tickPositions, startOnTick, endOnTick) {
- var roundedMin = tickPositions[0],
- roundedMax = tickPositions[tickPositions.length - 1],
- minPointOffset = this.minPointOffset || 0;
- fireEvent(this, 'trimTicks');
- if (!this.isLinked) {
- if (startOnTick && roundedMin !== -Infinity) { // #6502
- this.min = roundedMin;
- } else {
- while (this.min - minPointOffset > tickPositions[0]) {
- tickPositions.shift();
- }
- }
- if (endOnTick) {
- this.max = roundedMax;
- } else {
- while (this.max + minPointOffset <
- tickPositions[tickPositions.length - 1]) {
- tickPositions.pop();
- }
- }
- // If no tick are left, set one tick in the middle (#3195)
- if (
- tickPositions.length === 0 &&
- defined(roundedMin) &&
- !this.options.tickPositions
- ) {
- tickPositions.push((roundedMax + roundedMin) / 2);
- }
- }
- },
- /**
- * Check if there are multiple axes in the same pane.
- * @private
- * @return {boolean} True if there are other axes.
- */
- alignToOthers: function () {
- var others = {}, // Whether there is another axis to pair with this one
- hasOther,
- options = this.options;
- if (
- // Only if alignTicks is true
- this.chart.options.chart.alignTicks !== false &&
- options.alignTicks !== false &&
- // Disabled when startOnTick or endOnTick are false (#7604)
- options.startOnTick !== false &&
- options.endOnTick !== false &&
- // Don't try to align ticks on a log axis, they are not evenly
- // spaced (#6021)
- !this.isLog
- ) {
- this.chart[this.coll].forEach(function (axis) {
- var otherOptions = axis.options,
- horiz = axis.horiz,
- key = [
- horiz ? otherOptions.left : otherOptions.top,
- otherOptions.width,
- otherOptions.height,
- otherOptions.pane
- ].join(',');
- if (axis.series.length) { // #4442
- if (others[key]) {
- hasOther = true; // #4201
- } else {
- others[key] = 1;
- }
- }
- });
- }
- return hasOther;
- },
- /**
- * Find the max ticks of either the x and y axis collection, and record it
- * in `this.tickAmount`.
- * @private
- */
- getTickAmount: function () {
- var options = this.options,
- tickAmount = options.tickAmount,
- tickPixelInterval = options.tickPixelInterval;
- if (
- !defined(options.tickInterval) &&
- this.len < tickPixelInterval &&
- !this.isRadial &&
- !this.isLog &&
- options.startOnTick &&
- options.endOnTick
- ) {
- tickAmount = 2;
- }
- if (!tickAmount && this.alignToOthers()) {
- // Add 1 because 4 tick intervals require 5 ticks (including first
- // and last)
- tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
- }
- // For tick amounts of 2 and 3, compute five ticks and remove the
- // intermediate ones. This prevents the axis from adding ticks that are
- // too far away from the data extremes.
- if (tickAmount < 4) {
- this.finalTickAmt = tickAmount;
- tickAmount = 5;
- }
- this.tickAmount = tickAmount;
- },
- /**
- * When using multiple axes, adjust the number of ticks to match the highest
- * number of ticks in that group.
- * @private
- */
- adjustTickAmount: function () {
- var axis = this,
- axisOptions = axis.options,
- tickInterval = axis.tickInterval,
- tickPositions = axis.tickPositions,
- tickAmount = axis.tickAmount,
- finalTickAmt = axis.finalTickAmt,
- currentTickAmount = tickPositions && tickPositions.length,
- threshold = pick(axis.threshold, axis.softThreshold ? 0 : null),
- min,
- len,
- i;
- if (axis.hasData()) {
- if (currentTickAmount < tickAmount) {
- min = axis.min;
- while (tickPositions.length < tickAmount) {
- // Extend evenly for both sides unless we're on the
- // threshold (#3965)
- if (
- tickPositions.length % 2 ||
- min === threshold
- ) {
- // to the end
- tickPositions.push(correctFloat(
- tickPositions[tickPositions.length - 1] +
- tickInterval
- ));
- } else {
- // to the start
- tickPositions.unshift(correctFloat(
- tickPositions[0] - tickInterval
- ));
- }
- }
- axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
- // Do not crop when ticks are not extremes (#9841)
- axis.min = axisOptions.startOnTick ?
- tickPositions[0] :
- Math.min(axis.min, tickPositions[0]);
- axis.max = axisOptions.endOnTick ?
- tickPositions[tickPositions.length - 1] :
- Math.max(axis.max, tickPositions[tickPositions.length - 1]);
- // We have too many ticks, run second pass to try to reduce ticks
- } else if (currentTickAmount > tickAmount) {
- axis.tickInterval *= 2;
- axis.setTickPositions();
- }
- // The finalTickAmt property is set in getTickAmount
- if (defined(finalTickAmt)) {
- i = len = tickPositions.length;
- while (i--) {
- if (
- // Remove every other tick
- (finalTickAmt === 3 && i % 2 === 1) ||
- // Remove all but first and last
- (finalTickAmt <= 2 && i > 0 && i < len - 1)
- ) {
- tickPositions.splice(i, 1);
- }
- }
- axis.finalTickAmt = undefined;
- }
- }
- },
- /**
- * Set the scale based on data min and max, user set min and max or options.
- * @private
- * @fires Highcharts.Axis#event:afterSetScale
- */
- setScale: function () {
- var axis = this,
- isDirtyData,
- isDirtyAxisLength;
- axis.oldMin = axis.min;
- axis.oldMax = axis.max;
- axis.oldAxisLength = axis.len;
- // set the new axisLength
- axis.setAxisSize();
- isDirtyAxisLength = axis.len !== axis.oldAxisLength;
- // is there new data?
- axis.series.forEach(function (series) {
- if (
- series.isDirtyData ||
- series.isDirty ||
- // When x axis is dirty, we need new data extremes for y as well
- series.xAxis.isDirty
- ) {
- isDirtyData = true;
- }
- });
- // do we really need to go through all this?
- if (
- isDirtyAxisLength ||
- isDirtyData ||
- axis.isLinked ||
- axis.forceRedraw ||
- axis.userMin !== axis.oldUserMin ||
- axis.userMax !== axis.oldUserMax ||
- axis.alignToOthers()
- ) {
- if (axis.resetStacks) {
- axis.resetStacks();
- }
- axis.forceRedraw = false;
- // get data extremes if needed
- axis.getSeriesExtremes();
- // get fixed positions based on tickInterval
- axis.setTickInterval();
- // record old values to decide whether a rescale is necessary later
- // on (#540)
- axis.oldUserMin = axis.userMin;
- axis.oldUserMax = axis.userMax;
- // Mark as dirty if it is not already set to dirty and extremes have
- // changed. #595.
- if (!axis.isDirty) {
- axis.isDirty =
- isDirtyAxisLength ||
- axis.min !== axis.oldMin ||
- axis.max !== axis.oldMax;
- }
- } else if (axis.cleanStacks) {
- axis.cleanStacks();
- }
- fireEvent(this, 'afterSetScale');
- },
- /**
- * Set the minimum and maximum of the axes after render time. If the
- * `startOnTick` and `endOnTick` options are true, the minimum and maximum
- * values are rounded off to the nearest tick. To prevent this, these
- * options can be set to false before calling setExtremes. Also, setExtremes
- * will not allow a range lower than the `minRange` option, which by default
- * is the range of five points.
- *
- * @sample highcharts/members/axis-setextremes/
- * Set extremes from a button
- * @sample highcharts/members/axis-setextremes-datetime/
- * Set extremes on a datetime axis
- * @sample highcharts/members/axis-setextremes-off-ticks/
- * Set extremes off ticks
- * @sample stock/members/axis-setextremes/
- * Set extremes in Highstock
- * @sample maps/members/axis-setextremes/
- * Set extremes in Highmaps
- *
- * @function Highcharts.Axis#setExtremes
- *
- * @param {number} [newMin]
- * The new minimum value.
- *
- * @param {number} [newMax]
- * The new maximum value.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart or wait for an explicit call to
- * {@link Highcharts.Chart#redraw}
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
- * Enable or modify animations.
- *
- * @param {*} [eventArguments]
- * Arguments to be accessed in event handler.
- *
- * @fires Highcharts.Axis#event:setExtremes
- */
- setExtremes: function (newMin, newMax, redraw, animation, eventArguments) {
- var axis = this,
- chart = axis.chart;
- redraw = pick(redraw, true); // defaults to true
- axis.series.forEach(function (serie) {
- delete serie.kdTree;
- });
- // Extend the arguments with min and max
- eventArguments = extend(eventArguments, {
- min: newMin,
- max: newMax
- });
- // Fire the event
- fireEvent(axis, 'setExtremes', eventArguments, function () {
- axis.userMin = newMin;
- axis.userMax = newMax;
- axis.eventArgs = eventArguments;
- if (redraw) {
- chart.redraw(animation);
- }
- });
- },
- // Overridable method for zooming chart. Pulled out in a separate method to
- // allow overriding in stock charts.
- zoom: function (newMin, newMax) {
- var dataMin = this.dataMin,
- dataMax = this.dataMax,
- options = this.options,
- min = Math.min(dataMin, pick(options.min, dataMin)),
- max = Math.max(dataMax, pick(options.max, dataMax)),
- evt = { newMin: newMin, newMax: newMax };
- fireEvent(this, 'zoom', evt, function (e) {
- // Use e.newMin and e.newMax - event handlers may have altered them
- var newMin = e.newMin,
- newMax = e.newMax;
- if (newMin !== this.min || newMax !== this.max) { // #5790
- // Prevent pinch zooming out of range. Check for defined is for
- // #1946. #1734.
- if (!this.allowZoomOutside) {
- // #6014, sometimes newMax will be smaller than min (or
- // newMin will be larger than max).
- if (defined(dataMin)) {
- if (newMin < min) {
- newMin = min;
- }
- if (newMin > max) {
- newMin = max;
- }
- }
- if (defined(dataMax)) {
- if (newMax < min) {
- newMax = min;
- }
- if (newMax > max) {
- newMax = max;
- }
- }
- }
- // In full view, displaying the reset zoom button is not
- // required
- this.displayBtn = newMin !== undefined || newMax !== undefined;
- // Do it
- this.setExtremes(
- newMin,
- newMax,
- false,
- undefined,
- { trigger: 'zoom' }
- );
- }
- e.zoomed = true;
- });
- return evt.zoomed;
- },
- // Update the axis metrics.
- setAxisSize: function () {
- var chart = this.chart,
- options = this.options,
- // [top, right, bottom, left]
- offsets = options.offsets || [0, 0, 0, 0],
- horiz = this.horiz,
- // Check for percentage based input values. Rounding fixes problems
- // with column overflow and plot line filtering (#4898, #4899)
- width = this.width = Math.round(H.relativeLength(
- pick(
- options.width,
- chart.plotWidth - offsets[3] + offsets[1]
- ),
- chart.plotWidth
- )),
- height = this.height = Math.round(H.relativeLength(
- pick(
- options.height,
- chart.plotHeight - offsets[0] + offsets[2]
- ),
- chart.plotHeight
- )),
- top = this.top = Math.round(H.relativeLength(
- pick(options.top, chart.plotTop + offsets[0]),
- chart.plotHeight,
- chart.plotTop
- )),
- left = this.left = Math.round(H.relativeLength(
- pick(options.left, chart.plotLeft + offsets[3]),
- chart.plotWidth,
- chart.plotLeft
- ));
- // Expose basic values to use in Series object and navigator
- this.bottom = chart.chartHeight - height - top;
- this.right = chart.chartWidth - width - left;
- // Direction agnostic properties
- this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
- this.pos = horiz ? left : top; // distance from SVG origin
- },
- /**
- * Get the current extremes for the axis.
- *
- * @sample highcharts/members/axis-getextremes/
- * Report extremes by click on a button
- * @sample maps/members/axis-getextremes/
- * Get extremes in Highmaps
- *
- * @function Highcharts.Axis#getExtremes
- *
- * @returns {Highcharts.ExtremesObject}
- * An object containing extremes information.
- */
- getExtremes: function () {
- var axis = this,
- isLog = axis.isLog;
- return {
- min: isLog ? correctFloat(axis.lin2log(axis.min)) : axis.min,
- max: isLog ? correctFloat(axis.lin2log(axis.max)) : axis.max,
- dataMin: axis.dataMin,
- dataMax: axis.dataMax,
- userMin: axis.userMin,
- userMax: axis.userMax
- };
- },
- /**
- * Get the zero plane either based on zero or on the min or max value.
- * Used in bar and area plots.
- *
- * @function Highcharts.Axis#getThreshold
- *
- * @param {number} threshold
- * The threshold in axis values.
- *
- * @return {number}
- * The translated threshold position in terms of pixels, and
- * corrected to stay within the axis bounds.
- */
- getThreshold: function (threshold) {
- var axis = this,
- isLog = axis.isLog,
- realMin = isLog ? axis.lin2log(axis.min) : axis.min,
- realMax = isLog ? axis.lin2log(axis.max) : axis.max;
- if (threshold === null || threshold === -Infinity) {
- threshold = realMin;
- } else if (threshold === Infinity) {
- threshold = realMax;
- } else if (realMin > threshold) {
- threshold = realMin;
- } else if (realMax < threshold) {
- threshold = realMax;
- }
- return axis.translate(threshold, 0, 1, 0, 1);
- },
- /**
- * Compute auto alignment for the axis label based on which side the axis is
- * on and the given rotation for the label.
- * @private
- * @param {number} rotation The rotation in degrees as set by either the
- * `rotation` or `autoRotation` options.
- * @return {string} Can be `center`, `left` or `right`.
- */
- autoLabelAlign: function (rotation) {
- var angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360,
- evt = { align: 'center' };
- fireEvent(this, 'autoLabelAlign', evt, function (e) {
- if (angle > 15 && angle < 165) {
- e.align = 'right';
- } else if (angle > 195 && angle < 345) {
- e.align = 'left';
- }
- });
- return evt.align;
- },
- /**
- * Get the tick length and width for the axis based on axis options.
- * @private
- * @param {string} prefix 'tick' or 'minorTick'
- * @return {Array<number>} An array of tickLength and tickWidth
- */
- tickSize: function (prefix) {
- var options = this.options,
- tickLength = options[prefix + 'Length'],
- tickWidth = pick(
- options[prefix + 'Width'],
- prefix === 'tick' && this.isXAxis ? 1 : 0 // X axis default 1
- ),
- e,
- tickSize;
- if (tickWidth && tickLength) {
- // Negate the length
- if (options[prefix + 'Position'] === 'inside') {
- tickLength = -tickLength;
- }
- tickSize = [tickLength, tickWidth];
- }
- e = { tickSize: tickSize };
- fireEvent(this, 'afterTickSize', e);
- return e.tickSize;
- },
- // Return the size of the labels.
- labelMetrics: function () {
- var index = this.tickPositions && this.tickPositions[0] || 0;
- return this.chart.renderer.fontMetrics(
- this.options.labels.style && this.options.labels.style.fontSize,
- this.ticks[index] && this.ticks[index].label
- );
- },
- // Prevent the ticks from getting so close we can't draw the labels. On a
- // horizontal axis, this is handled by rotating the labels, removing ticks
- // and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
- unsquish: function () {
- var labelOptions = this.options.labels,
- horiz = this.horiz,
- tickInterval = this.tickInterval,
- newTickInterval = tickInterval,
- slotSize = this.len / (
- ((this.categories ? 1 : 0) + this.max - this.min) / tickInterval
- ),
- rotation,
- rotationOption = labelOptions.rotation,
- labelMetrics = this.labelMetrics(),
- step,
- bestScore = Number.MAX_VALUE,
- autoRotation,
- range = this.max - this.min,
- // Return the multiple of tickInterval that is needed to avoid
- // collision
- getStep = function (spaceNeeded) {
- var step = spaceNeeded / (slotSize || 1);
- step = step > 1 ? Math.ceil(step) : 1;
- // Guard for very small or negative angles (#9835)
- if (
- step * tickInterval > range &&
- spaceNeeded !== Infinity &&
- slotSize !== Infinity
- ) {
- step = Math.ceil(range / tickInterval);
- }
- return correctFloat(step * tickInterval);
- };
- if (horiz) {
- autoRotation = !labelOptions.staggerLines &&
- !labelOptions.step &&
- ( // #3971
- defined(rotationOption) ?
- [rotationOption] :
- slotSize < pick(labelOptions.autoRotationLimit, 80) &&
- labelOptions.autoRotation
- );
- if (autoRotation) {
- // Loop over the given autoRotation options, and determine
- // which gives the best score. The best score is that with
- // the lowest number of steps and a rotation closest
- // to horizontal.
- autoRotation.forEach(function (rot) {
- var score;
- if (
- rot === rotationOption ||
- (rot && rot >= -90 && rot <= 90)
- ) { // #3891
- step = getStep(
- Math.abs(labelMetrics.h / Math.sin(deg2rad * rot))
- );
- score = step + Math.abs(rot / 360);
- if (score < bestScore) {
- bestScore = score;
- rotation = rot;
- newTickInterval = step;
- }
- }
- });
- }
- } else if (!labelOptions.step) { // #4411
- newTickInterval = getStep(labelMetrics.h);
- }
- this.autoRotation = autoRotation;
- this.labelRotation = pick(rotation, rotationOption);
- return newTickInterval;
- },
- /**
- * Get the general slot width for labels/categories on this axis. This may
- * change between the pre-render (from Axis.getOffset) and the final tick
- * rendering and placement.
- * @private
- * @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
- * basing on tick label. It is used in highcharts-3d module, where the slots
- * has different widths depending on perspective angles.
- * @return {number} The pixel width allocated to each axis label.
- */
- getSlotWidth: function (tick) {
- // #5086, #1580, #1931
- var chart = this.chart,
- horiz = this.horiz,
- labelOptions = this.options.labels,
- slotCount = Math.max(
- this.tickPositions.length - (this.categories ? 0 : 1),
- 1
- ),
- marginLeft = chart.margin[3];
- return (
- tick &&
- tick.slotWidth // Used by grid axis
- ) || (
- horiz &&
- (labelOptions.step || 0) < 2 &&
- !labelOptions.rotation && // #4415
- ((this.staggerLines || 1) * this.len) / slotCount
- ) || (
- !horiz && (
- // #7028
- (
- labelOptions.style &&
- parseInt(labelOptions.style.width, 10)
- ) ||
- (
- marginLeft &&
- (marginLeft - chart.spacing[3])
- ) ||
- chart.chartWidth * 0.33
- )
- );
- },
- // Render the axis labels and determine whether ellipsis or rotation need to
- // be applied.
- renderUnsquish: function () {
- var chart = this.chart,
- renderer = chart.renderer,
- tickPositions = this.tickPositions,
- ticks = this.ticks,
- labelOptions = this.options.labels,
- labelStyleOptions = (labelOptions && labelOptions.style || {}),
- horiz = this.horiz,
- slotWidth = this.getSlotWidth(),
- innerWidth = Math.max(
- 1,
- Math.round(slotWidth - 2 * (labelOptions.padding || 5))
- ),
- attr = {},
- labelMetrics = this.labelMetrics(),
- textOverflowOption = labelOptions.style &&
- labelOptions.style.textOverflow,
- commonWidth,
- commonTextOverflow,
- maxLabelLength = 0,
- label,
- i,
- pos;
- // Set rotation option unless it is "auto", like in gauges
- if (!isString(labelOptions.rotation)) {
- attr.rotation = labelOptions.rotation || 0; // #4443
- }
- // Get the longest label length
- tickPositions.forEach(function (tick) {
- tick = ticks[tick];
- if (
- tick &&
- tick.label &&
- tick.label.textPxLength > maxLabelLength
- ) {
- maxLabelLength = tick.label.textPxLength;
- }
- });
- this.maxLabelLength = maxLabelLength;
- // Handle auto rotation on horizontal axis
- if (this.autoRotation) {
- // Apply rotation only if the label is too wide for the slot, and
- // the label is wider than its height.
- if (
- maxLabelLength > innerWidth &&
- maxLabelLength > labelMetrics.h
- ) {
- attr.rotation = this.labelRotation;
- } else {
- this.labelRotation = 0;
- }
- // Handle word-wrap or ellipsis on vertical axis
- } else if (slotWidth) {
- // For word-wrap or ellipsis
- commonWidth = innerWidth;
- if (!textOverflowOption) {
- commonTextOverflow = 'clip';
- // On vertical axis, only allow word wrap if there is room
- // for more lines.
- i = tickPositions.length;
- while (!horiz && i--) {
- pos = tickPositions[i];
- label = ticks[pos].label;
- if (label) {
- // Reset ellipsis in order to get the correct
- // bounding box (#4070)
- if (
- label.styles &&
- label.styles.textOverflow === 'ellipsis'
- ) {
- label.css({ textOverflow: 'clip' });
- // Set the correct width in order to read
- // the bounding box height (#4678, #5034)
- } else if (label.textPxLength > slotWidth) {
- label.css({ width: slotWidth + 'px' });
- }
- if (
- label.getBBox().height > (
- this.len / tickPositions.length -
- (labelMetrics.h - labelMetrics.f)
- )
- ) {
- label.specificTextOverflow = 'ellipsis';
- }
- }
- }
- }
- }
- // Add ellipsis if the label length is significantly longer than ideal
- if (attr.rotation) {
- commonWidth = (
- maxLabelLength > chart.chartHeight * 0.5 ?
- chart.chartHeight * 0.33 :
- maxLabelLength
- );
- if (!textOverflowOption) {
- commonTextOverflow = 'ellipsis';
- }
- }
- // Set the explicit or automatic label alignment
- this.labelAlign = labelOptions.align ||
- this.autoLabelAlign(this.labelRotation);
- if (this.labelAlign) {
- attr.align = this.labelAlign;
- }
- // Apply general and specific CSS
- tickPositions.forEach(function (pos) {
- var tick = ticks[pos],
- label = tick && tick.label,
- widthOption = labelStyleOptions.width,
- css = {};
- if (label) {
- // This needs to go before the CSS in old IE (#4502)
- label.attr(attr);
- if (tick.shortenLabel) {
- tick.shortenLabel();
- } else if (
- commonWidth &&
- !widthOption &&
- // Setting width in this case messes with the bounding box
- // (#7975)
- labelStyleOptions.whiteSpace !== 'nowrap' &&
- (
- // Speed optimizing, #7656
- commonWidth < label.textPxLength ||
- // Resetting CSS, #4928
- label.element.tagName === 'SPAN'
- )
- ) {
- css.width = commonWidth;
- if (!textOverflowOption) {
- css.textOverflow = (
- label.specificTextOverflow ||
- commonTextOverflow
- );
- }
- label.css(css);
- // Reset previously shortened label (#8210)
- } else if (
- label.styles &&
- label.styles.width &&
- !css.width &&
- !widthOption
- ) {
- label.css({ width: null });
- }
- delete label.specificTextOverflow;
- tick.rotation = attr.rotation;
- }
- }, this);
- // Note: Why is this not part of getLabelPosition?
- this.tickRotCorr = renderer.rotCorr(
- labelMetrics.b,
- this.labelRotation || 0,
- this.side !== 0
- );
- },
- /**
- * Return true if the axis has associated data.
- *
- * @function Highcharts.Axis#hasData
- *
- * @return {boolean}
- * True if the axis has associated visible series and those series
- * have either valid data points or explicit `min` and `max`
- * settings.
- */
- hasData: function () {
- return (
- this.hasVisibleSeries ||
- (
- defined(this.min) &&
- defined(this.max) &&
- this.tickPositions &&
- this.tickPositions.length > 0
- )
- );
- },
- /**
- * Adds the title defined in axis.options.title.
- *
- * @function Highcharts.Axis#addTitle
- *
- * @param {boolean} display
- * Whether or not to display the title.
- */
- addTitle: function (display) {
- var axis = this,
- renderer = axis.chart.renderer,
- horiz = axis.horiz,
- opposite = axis.opposite,
- options = axis.options,
- axisTitleOptions = options.title,
- textAlign,
- styledMode = axis.chart.styledMode;
- if (!axis.axisTitle) {
- textAlign = axisTitleOptions.textAlign;
- if (!textAlign) {
- textAlign = (horiz ? {
- low: 'left',
- middle: 'center',
- high: 'right'
- } : {
- low: opposite ? 'right' : 'left',
- middle: 'center',
- high: opposite ? 'left' : 'right'
- })[axisTitleOptions.align];
- }
- axis.axisTitle = renderer.text(
- axisTitleOptions.text,
- 0,
- 0,
- axisTitleOptions.useHTML
- )
- .attr({
- zIndex: 7,
- rotation: axisTitleOptions.rotation || 0,
- align: textAlign
- })
- .addClass('highcharts-axis-title');
- // #7814, don't mutate style option
- if (!styledMode) {
- axis.axisTitle.css(merge(axisTitleOptions.style));
- }
- axis.axisTitle.add(axis.axisGroup);
- axis.axisTitle.isNew = true;
- }
- // Max width defaults to the length of the axis
- if (!styledMode && !axisTitleOptions.style.width && !axis.isRadial) {
- axis.axisTitle.css({
- width: axis.len
- });
- }
- // hide or show the title depending on whether showEmpty is set
- axis.axisTitle[display ? 'show' : 'hide'](true);
- },
- /**
- * Generates a tick for initial positioning.
- * @private
- * @param {number} pos The tick position in axis values.
- * @param {number} i The index of the tick in {@link Axis.tickPositions}.
- */
- generateTick: function (pos) {
- var ticks = this.ticks;
- if (!ticks[pos]) {
- ticks[pos] = new Tick(this, pos);
- } else {
- ticks[pos].addLabel(); // update labels depending on tick interval
- }
- },
- /**
- * Render the tick labels to a preliminary position to get their sizes
- * @private
- * @fires Highcharts.Axis#event:afterGetOffset
- */
- getOffset: function () {
- var axis = this,
- chart = axis.chart,
- renderer = chart.renderer,
- options = axis.options,
- tickPositions = axis.tickPositions,
- ticks = axis.ticks,
- horiz = axis.horiz,
- side = axis.side,
- invertedSide = chart.inverted &&
- !axis.isZAxis ? [1, 0, 3, 2][side] : side,
- hasData,
- showAxis,
- titleOffset = 0,
- titleOffsetOption,
- titleMargin = 0,
- axisTitleOptions = options.title,
- labelOptions = options.labels,
- labelOffset = 0, // reset
- labelOffsetPadded,
- axisOffset = chart.axisOffset,
- clipOffset = chart.clipOffset,
- clip,
- directionFactor = [-1, 1, 1, -1][side],
- className = options.className,
- axisParent = axis.axisParent, // Used in color axis
- lineHeightCorrection,
- tickSize;
- // For reuse in Axis.render
- hasData = axis.hasData();
- axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
- // Set/reset staggerLines
- axis.staggerLines = axis.horiz && labelOptions.staggerLines;
- // Create the axisGroup and gridGroup elements on first iteration
- if (!axis.axisGroup) {
- axis.gridGroup = renderer.g('grid')
- .attr({ zIndex: options.gridZIndex || 1 })
- .addClass(
- 'highcharts-' + this.coll.toLowerCase() + '-grid ' +
- (className || '')
- )
- .add(axisParent);
- axis.axisGroup = renderer.g('axis')
- .attr({ zIndex: options.zIndex || 2 })
- .addClass(
- 'highcharts-' + this.coll.toLowerCase() + ' ' +
- (className || '')
- )
- .add(axisParent);
- axis.labelGroup = renderer.g('axis-labels')
- .attr({ zIndex: labelOptions.zIndex || 7 })
- .addClass(
- 'highcharts-' + axis.coll.toLowerCase() + '-labels ' +
- (className || '')
- )
- .add(axisParent);
- }
- if (hasData || axis.isLinked) {
- // Generate ticks
- tickPositions.forEach(function (pos, i) {
- // i is not used here, but may be used in overrides
- axis.generateTick(pos, i);
- });
- axis.renderUnsquish();
- // Left side must be align: right and right side must
- // have align: left for labels
- axis.reserveSpaceDefault = (
- side === 0 ||
- side === 2 ||
- { 1: 'left', 3: 'right' }[side] === axis.labelAlign
- );
- if (pick(
- labelOptions.reserveSpace,
- axis.labelAlign === 'center' ? true : null,
- axis.reserveSpaceDefault
- )) {
- tickPositions.forEach(function (pos) {
- // get the highest offset
- labelOffset = Math.max(
- ticks[pos].getLabelSize(),
- labelOffset
- );
- });
- }
- if (axis.staggerLines) {
- labelOffset *= axis.staggerLines;
- }
- axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
- } else { // doesn't have data
- objectEach(ticks, function (tick, n) {
- tick.destroy();
- delete ticks[n];
- });
- }
- if (
- axisTitleOptions &&
- axisTitleOptions.text &&
- axisTitleOptions.enabled !== false
- ) {
- axis.addTitle(showAxis);
- if (showAxis && axisTitleOptions.reserveSpace !== false) {
- axis.titleOffset = titleOffset =
- axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
- titleOffsetOption = axisTitleOptions.offset;
- titleMargin = defined(titleOffsetOption) ?
- 0 :
- pick(axisTitleOptions.margin, horiz ? 5 : 10);
- }
- }
- // Render the axis line
- axis.renderLine();
- // handle automatic or user set offset
- axis.offset = directionFactor * pick(options.offset, axisOffset[side]);
- axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
- if (side === 0) {
- lineHeightCorrection = -axis.labelMetrics().h;
- } else if (side === 2) {
- lineHeightCorrection = axis.tickRotCorr.y;
- } else {
- lineHeightCorrection = 0;
- }
- // Find the padded label offset
- labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
- if (labelOffset) {
- labelOffsetPadded -= lineHeightCorrection;
- labelOffsetPadded += directionFactor * (
- horiz ?
- pick(
- labelOptions.y,
- axis.tickRotCorr.y + directionFactor * 8
- ) :
- labelOptions.x
- );
- }
- axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
- if (axis.getMaxLabelDimensions) {
- axis.maxLabelDimensions = axis.getMaxLabelDimensions(
- ticks,
- tickPositions
- );
- }
- // Due to GridAxis.tickSize, tickSize should be calculated after ticks
- // has rendered.
- tickSize = this.tickSize('tick');
- axisOffset[side] = Math.max(
- axisOffset[side],
- axis.axisTitleMargin + titleOffset + directionFactor * axis.offset,
- labelOffsetPadded, // #3027
- hasData && tickPositions.length && tickSize ?
- tickSize[0] + directionFactor * axis.offset :
- 0 // #4866
- );
- // Decide the clipping needed to keep the graph inside
- // the plot area and axis lines
- clip = options.offset ?
- 0 :
- Math.floor(axis.axisLine.strokeWidth() / 2) * 2; // #4308, #4371
- clipOffset[invertedSide] = Math.max(clipOffset[invertedSide], clip);
- fireEvent(this, 'afterGetOffset');
- },
- /**
- * Internal function to get the path for the axis line. Extended for polar
- * charts.
- *
- * @function Highcharts.Axis#getLinePath
- *
- * @param {number} lineWidth
- * The line width in pixels.
- *
- * @return {Highcharts.SVGPathArray}
- * The SVG path definition in array form.
- */
- getLinePath: function (lineWidth) {
- var chart = this.chart,
- opposite = this.opposite,
- offset = this.offset,
- horiz = this.horiz,
- lineLeft = this.left + (opposite ? this.width : 0) + offset,
- lineTop = chart.chartHeight - this.bottom -
- (opposite ? this.height : 0) + offset;
- if (opposite) {
- lineWidth *= -1; // crispify the other way - #1480, #1687
- }
- return chart.renderer
- .crispLine([
- 'M',
- horiz ?
- this.left :
- lineLeft,
- horiz ?
- lineTop :
- this.top,
- 'L',
- horiz ?
- chart.chartWidth - this.right :
- lineLeft,
- horiz ?
- lineTop :
- chart.chartHeight - this.bottom
- ], lineWidth);
- },
- /**
- * Render the axis line. Called internally when rendering and redrawing the
- * axis.
- *
- * @function Highcharts.Axis#renderLine
- */
- renderLine: function () {
- if (!this.axisLine) {
- this.axisLine = this.chart.renderer.path()
- .addClass('highcharts-axis-line')
- .add(this.axisGroup);
- if (!this.chart.styledMode) {
- this.axisLine.attr({
- stroke: this.options.lineColor,
- 'stroke-width': this.options.lineWidth,
- zIndex: 7
- });
- }
- }
- },
- /**
- * Position the axis title.
- * @private
- * @return {Highcharts.PositionObject} X and Y positions for the title.
- */
- getTitlePosition: function () {
- // compute anchor points for each of the title align options
- var horiz = this.horiz,
- axisLeft = this.left,
- axisTop = this.top,
- axisLength = this.len,
- axisTitleOptions = this.options.title,
- margin = horiz ? axisLeft : axisTop,
- opposite = this.opposite,
- offset = this.offset,
- xOption = axisTitleOptions.x || 0,
- yOption = axisTitleOptions.y || 0,
- axisTitle = this.axisTitle,
- fontMetrics = this.chart.renderer.fontMetrics(
- axisTitleOptions.style && axisTitleOptions.style.fontSize,
- axisTitle
- ),
- // The part of a multiline text that is below the baseline of the
- // first line. Subtract 1 to preserve pixel-perfectness from the
- // old behaviour (v5.0.12), where only one line was allowed.
- textHeightOvershoot = Math.max(
- axisTitle.getBBox(null, 0).height - fontMetrics.h - 1,
- 0
- ),
- // the position in the length direction of the axis
- alongAxis = {
- low: margin + (horiz ? 0 : axisLength),
- middle: margin + axisLength / 2,
- high: margin + (horiz ? axisLength : 0)
- }[axisTitleOptions.align],
- // the position in the perpendicular direction of the axis
- offAxis = (horiz ? axisTop + this.height : axisLeft) +
- (horiz ? 1 : -1) * // horizontal axis reverses the margin
- (opposite ? -1 : 1) * // so does opposite axes
- this.axisTitleMargin +
- [
- -textHeightOvershoot, // top
- textHeightOvershoot, // right
- fontMetrics.f, // bottom
- -textHeightOvershoot // left
- ][this.side],
- titlePosition = {
- x: horiz ?
- alongAxis + xOption :
- offAxis + (opposite ? this.width : 0) + offset + xOption,
- y: horiz ?
- offAxis + yOption - (opposite ? this.height : 0) + offset :
- alongAxis + yOption
- };
- fireEvent(
- this,
- 'afterGetTitlePosition',
- { titlePosition: titlePosition }
- );
- return titlePosition;
- },
- /**
- * Render a minor tick into the given position. If a minor tick already
- * exists in this position, move it.
- *
- * @function Highcharts.Axis#renderMinorTick
- *
- * @param {number} pos
- * The position in axis values.
- */
- renderMinorTick: function (pos) {
- var slideInTicks = this.chart.hasRendered && isNumber(this.oldMin),
- minorTicks = this.minorTicks;
- if (!minorTicks[pos]) {
- minorTicks[pos] = new Tick(this, pos, 'minor');
- }
- // Render new ticks in old position
- if (slideInTicks && minorTicks[pos].isNew) {
- minorTicks[pos].render(null, true);
- }
- minorTicks[pos].render(null, false, 1);
- },
- /**
- * Render a major tick into the given position. If a tick already exists
- * in this position, move it.
- *
- * @function Highcharts.Axis#renderTick
- *
- * @param {number} pos
- * The position in axis values.
- *
- * @param {number} i
- * The tick index.
- */
- renderTick: function (pos, i) {
- var isLinked = this.isLinked,
- ticks = this.ticks,
- slideInTicks = this.chart.hasRendered && isNumber(this.oldMin);
- // Linked axes need an extra check to find out if
- if (!isLinked || (pos >= this.min && pos <= this.max)) {
- if (!ticks[pos]) {
- ticks[pos] = new Tick(this, pos);
- }
- // NOTE this seems like overkill. Could be handled in tick.render by
- // setting old position in attr, then set new position in animate.
- // render new ticks in old position
- if (slideInTicks && ticks[pos].isNew) {
- // Start with negative opacity so that it is visible from
- // halfway into the animation
- ticks[pos].render(i, true, -1);
- }
- ticks[pos].render(i);
- }
- },
- /**
- * Render the axis.
- * @private
- * @fires Highcharts.Axis#event:afterRender
- */
- render: function () {
- var axis = this,
- chart = axis.chart,
- renderer = chart.renderer,
- options = axis.options,
- isLog = axis.isLog,
- isLinked = axis.isLinked,
- tickPositions = axis.tickPositions,
- axisTitle = axis.axisTitle,
- ticks = axis.ticks,
- minorTicks = axis.minorTicks,
- alternateBands = axis.alternateBands,
- stackLabelOptions = options.stackLabels,
- alternateGridColor = options.alternateGridColor,
- tickmarkOffset = axis.tickmarkOffset,
- axisLine = axis.axisLine,
- showAxis = axis.showAxis,
- animation = animObject(renderer.globalAnimation),
- from,
- to;
- // Reset
- axis.labelEdge.length = 0;
- axis.overlap = false;
- // Mark all elements inActive before we go over and mark the active ones
- [ticks, minorTicks, alternateBands].forEach(function (coll) {
- objectEach(coll, function (tick) {
- tick.isActive = false;
- });
- });
- // If the series has data draw the ticks. Else only the line and title
- if (axis.hasData() || isLinked) {
- // minor ticks
- if (axis.minorTickInterval && !axis.categories) {
- axis.getMinorTickPositions().forEach(function (pos) {
- axis.renderMinorTick(pos);
- });
- }
- // Major ticks. Pull out the first item and render it last so that
- // we can get the position of the neighbour label. #808.
- if (tickPositions.length) { // #1300
- tickPositions.forEach(function (pos, i) {
- axis.renderTick(pos, i);
- });
- // In a categorized axis, the tick marks are displayed
- // between labels. So we need to add a tick mark and
- // grid line at the left edge of the X axis.
- if (tickmarkOffset && (axis.min === 0 || axis.single)) {
- if (!ticks[-1]) {
- ticks[-1] = new Tick(axis, -1, null, true);
- }
- ticks[-1].render(-1);
- }
- }
- // alternate grid color
- if (alternateGridColor) {
- tickPositions.forEach(function (pos, i) {
- to = tickPositions[i + 1] !== undefined ?
- tickPositions[i + 1] + tickmarkOffset :
- axis.max - tickmarkOffset;
- if (
- i % 2 === 0 &&
- pos < axis.max &&
- to <= axis.max + (
- chart.polar ?
- -tickmarkOffset :
- tickmarkOffset
- )
- ) { // #2248, #4660
- if (!alternateBands[pos]) {
- alternateBands[pos] = new H.PlotLineOrBand(axis);
- }
- from = pos + tickmarkOffset; // #949
- alternateBands[pos].options = {
- from: isLog ? axis.lin2log(from) : from,
- to: isLog ? axis.lin2log(to) : to,
- color: alternateGridColor
- };
- alternateBands[pos].render();
- alternateBands[pos].isActive = true;
- }
- });
- }
- // custom plot lines and bands
- if (!axis._addedPlotLB) { // only first time
- (
- (options.plotLines || []).concat(options.plotBands || [])
- ).forEach(
- function (plotLineOptions) {
- axis.addPlotBandOrLine(plotLineOptions);
- }
- );
- axis._addedPlotLB = true;
- }
- } // end if hasData
- // Remove inactive ticks
- [ticks, minorTicks, alternateBands].forEach(function (coll) {
- var i,
- forDestruction = [],
- delay = animation.duration,
- destroyInactiveItems = function () {
- i = forDestruction.length;
- while (i--) {
- // When resizing rapidly, the same items
- // may be destroyed in different timeouts,
- // or the may be reactivated
- if (
- coll[forDestruction[i]] &&
- !coll[forDestruction[i]].isActive
- ) {
- coll[forDestruction[i]].destroy();
- delete coll[forDestruction[i]];
- }
- }
- };
- objectEach(coll, function (tick, pos) {
- if (!tick.isActive) {
- // Render to zero opacity
- tick.render(pos, false, 0);
- tick.isActive = false;
- forDestruction.push(pos);
- }
- });
- // When the objects are finished fading out, destroy them
- syncTimeout(
- destroyInactiveItems,
- coll === alternateBands ||
- !chart.hasRendered ||
- !delay ?
- 0 :
- delay
- );
- });
- // Set the axis line path
- if (axisLine) {
- axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
- d: this.getLinePath(axisLine.strokeWidth())
- });
- axisLine.isPlaced = true;
- // Show or hide the line depending on options.showEmpty
- axisLine[showAxis ? 'show' : 'hide'](true);
- }
- if (axisTitle && showAxis) {
- var titleXy = axis.getTitlePosition();
- if (isNumber(titleXy.y)) {
- axisTitle[axisTitle.isNew ? 'attr' : 'animate'](titleXy);
- axisTitle.isNew = false;
- } else {
- axisTitle.attr('y', -9999);
- axisTitle.isNew = true;
- }
- }
- // Stacked totals:
- if (stackLabelOptions && stackLabelOptions.enabled) {
- axis.renderStackTotals();
- }
- // End stacked totals
- axis.isDirty = false;
- fireEvent(this, 'afterRender');
- },
- // Redraw the axis to reflect changes in the data or axis extremes. Called
- // internally from Highcharts.Chart#redraw.
- redraw: function () {
- if (this.visible) {
- // render the axis
- this.render();
- // move plot lines and bands
- this.plotLinesAndBands.forEach(function (plotLine) {
- plotLine.render();
- });
- }
- // mark associated series as dirty and ready for redraw
- this.series.forEach(function (series) {
- series.isDirty = true;
- });
- },
- // Properties to survive after destroy, needed for Axis.update (#4317,
- // #5773, #5881).
- keepProps: ['extKey', 'hcEvents', 'names', 'series', 'userMax', 'userMin'],
- /**
- * Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
- * to fully remove the axis.
- * @private
- * @param {boolean} keepEvents Whether to preserve events, used internally
- * in Axis.update.
- */
- destroy: function (keepEvents) {
- var axis = this,
- stacks = axis.stacks,
- plotLinesAndBands = axis.plotLinesAndBands,
- plotGroup,
- i;
- fireEvent(this, 'destroy', { keepEvents: keepEvents });
- // Remove the events
- if (!keepEvents) {
- removeEvent(axis);
- }
- // Destroy each stack total
- objectEach(stacks, function (stack, stackKey) {
- destroyObjectProperties(stack);
- stacks[stackKey] = null;
- });
- // Destroy collections
- [axis.ticks, axis.minorTicks, axis.alternateBands].forEach(
- function (coll) {
- destroyObjectProperties(coll);
- }
- );
- if (plotLinesAndBands) {
- i = plotLinesAndBands.length;
- while (i--) { // #1975
- plotLinesAndBands[i].destroy();
- }
- }
- // Destroy elements
- ['stackTotalGroup', 'axisLine', 'axisTitle', 'axisGroup',
- 'gridGroup', 'labelGroup', 'cross', 'scrollbar'].forEach(
- function (prop) {
- if (axis[prop]) {
- axis[prop] = axis[prop].destroy();
- }
- }
- );
- // Destroy each generated group for plotlines and plotbands
- for (plotGroup in axis.plotLinesAndBandsGroups) {
- axis.plotLinesAndBandsGroups[plotGroup] =
- axis.plotLinesAndBandsGroups[plotGroup].destroy();
- }
- // Delete all properties and fall back to the prototype.
- objectEach(axis, function (val, key) {
- if (axis.keepProps.indexOf(key) === -1) {
- delete axis[key];
- }
- });
- },
- /**
- * Internal function to draw a crosshair.
- *
- * @function Highcharts.Axis#drawCrosshair
- *
- * @param {Highcharts.PointerEventObject} [e]
- * The event arguments from the modified pointer event, extended with
- * `chartX` and `chartY`
- *
- * @param {Highcharts.Point} [point]
- * The Point object if the crosshair snaps to points.
- *
- * @fires Highcharts.Axis#event:afterDrawCrosshair
- * @fires Highcharts.Axis#event:drawCrosshair
- */
- drawCrosshair: function (e, point) {
- var path,
- options = this.crosshair,
- snap = pick(options.snap, true),
- pos,
- categorized,
- graphic = this.cross;
- fireEvent(this, 'drawCrosshair', { e: e, point: point });
- // Use last available event when updating non-snapped crosshairs without
- // mouse interaction (#5287)
- if (!e) {
- e = this.cross && this.cross.e;
- }
- if (
- // Disabled in options
- !this.crosshair ||
- // Snap
- ((defined(point) || !snap) === false)
- ) {
- this.hideCrosshair();
- } else {
- // Get the path
- if (!snap) {
- pos = e &&
- (
- this.horiz ?
- e.chartX - this.pos :
- this.len - e.chartY + this.pos
- );
- } else if (defined(point)) {
- // #3834
- pos = pick(
- point.crosshairPos, // 3D axis extension
- this.isXAxis ? point.plotX : this.len - point.plotY
- );
- }
- if (defined(pos)) {
- path = this.getPlotLinePath(
- // First argument, value, only used on radial
- point && (this.isXAxis ?
- point.x :
- pick(point.stackY, point.y)
- ),
- null,
- null,
- null,
- pos // Translated position
- ) || null; // #3189
- }
- if (!defined(path)) {
- this.hideCrosshair();
- return;
- }
- categorized = this.categories && !this.isRadial;
- // Draw the cross
- if (!graphic) {
- this.cross = graphic = this.chart.renderer
- .path()
- .addClass(
- 'highcharts-crosshair highcharts-crosshair-' +
- (categorized ? 'category ' : 'thin ') +
- options.className
- )
- .attr({
- zIndex: pick(options.zIndex, 2)
- })
- .add();
- // Presentational attributes
- if (!this.chart.styledMode) {
- graphic.attr({
- 'stroke': options.color ||
- (
- categorized ?
- color('#ccd6eb')
- .setOpacity(0.25).get() :
- '#cccccc'
- ),
- 'stroke-width': pick(options.width, 1)
- }).css({
- 'pointer-events': 'none'
- });
- if (options.dashStyle) {
- graphic.attr({
- dashstyle: options.dashStyle
- });
- }
- }
- }
- graphic.show().attr({
- d: path
- });
- if (categorized && !options.width) {
- graphic.attr({
- 'stroke-width': this.transA
- });
- }
- this.cross.e = e;
- }
- fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
- },
- /**
- * Hide the crosshair if visible.
- *
- * @function Highcharts.Axis#hideCrosshair
- */
- hideCrosshair: function () {
- if (this.cross) {
- this.cross.hide();
- }
- fireEvent(this, 'afterHideCrosshair');
- }
- }); // end Axis
- H.Axis = Axis;
- return Axis;
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var Axis = H.Axis,
- getMagnitude = H.getMagnitude,
- normalizeTickInterval = H.normalizeTickInterval,
- timeUnits = H.timeUnits;
- /**
- * Set the tick positions to a time unit that makes sense, for example
- * on the first of each month or on every Monday. Return an array
- * with the time positions. Used in datetime axes as well as for grouping
- * data on a datetime axis.
- *
- * @private
- * @function Highcharts.Axis#getTimeTicks
- *
- * @param {*} normalizedInterval
- * The interval in axis values (ms) and thecount
- *
- * @param {number} min
- * The minimum in axis values
- *
- * @param {number} max
- * The maximum in axis values
- *
- * @param {number} startOfWeek
- *
- * @return {number}
- */
- Axis.prototype.getTimeTicks = function () {
- return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
- };
- /**
- * Get a normalized tick interval for dates. Returns a configuration object with
- * unit range (interval), count and name. Used to prepare data for getTimeTicks.
- * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
- * of segments in stock charts, the normalizing logic was extracted in order to
- * prevent it for running over again for each segment having the same interval.
- * #662, #697.
- *
- * @private
- * @function Highcharts.Axis#normalizeTimeTickInterval
- *
- * @param {number} tickInterval
- *
- * @param {Array<Array<number|string>>} [unitsOption]
- *
- * @return {*}
- */
- Axis.prototype.normalizeTimeTickInterval = function (
- tickInterval,
- unitsOption
- ) {
- var units = unitsOption || [[
- 'millisecond', // unit name
- [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
- ], [
- 'second',
- [1, 2, 5, 10, 15, 30]
- ], [
- 'minute',
- [1, 2, 5, 10, 15, 30]
- ], [
- 'hour',
- [1, 2, 3, 4, 6, 8, 12]
- ], [
- 'day',
- [1, 2]
- ], [
- 'week',
- [1, 2]
- ], [
- 'month',
- [1, 2, 3, 4, 6]
- ], [
- 'year',
- null
- ]],
- unit = units[units.length - 1], // default unit is years
- interval = timeUnits[unit[0]],
- multiples = unit[1],
- count,
- i;
- // loop through the units to find the one that best fits the tickInterval
- for (i = 0; i < units.length; i++) {
- unit = units[i];
- interval = timeUnits[unit[0]];
- multiples = unit[1];
- if (units[i + 1]) {
- // lessThan is in the middle between the highest multiple and the
- // next unit.
- var lessThan = (interval * multiples[multiples.length - 1] +
- timeUnits[units[i + 1][0]]) / 2;
- // break and keep the current unit
- if (tickInterval <= lessThan) {
- break;
- }
- }
- }
- // prevent 2.5 years intervals, though 25, 250 etc. are allowed
- if (interval === timeUnits.year && tickInterval < 5 * interval) {
- multiples = [1, 2, 5];
- }
- // get the count
- count = normalizeTickInterval(
- tickInterval / interval,
- multiples,
- unit[0] === 'year' ?
- Math.max(getMagnitude(tickInterval / interval), 1) : // #1913, #2360
- 1
- );
- return {
- unitRange: interval,
- count: count,
- unitName: unit[0]
- };
- };
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var Axis = H.Axis,
- getMagnitude = H.getMagnitude,
- normalizeTickInterval = H.normalizeTickInterval,
- pick = H.pick;
- /*
- * Methods defined on the Axis prototype
- */
- /**
- * Set the tick positions of a logarithmic axis.
- *
- * @private
- * @function Highcharts.Axis#getLogTickPositions
- *
- * @param {number} interval
- *
- * @param {number} min
- *
- * @param {number} max
- *
- * @param {number} minor
- *
- * @return {Array<number>}
- */
- Axis.prototype.getLogTickPositions = function (interval, min, max, minor) {
- var axis = this,
- options = axis.options,
- axisLength = axis.len,
- // Since we use this method for both major and minor ticks,
- // use a local variable and return the result
- positions = [];
- // Reset
- if (!minor) {
- axis._minorAutoInterval = null;
- }
- // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
- if (interval >= 0.5) {
- interval = Math.round(interval);
- positions = axis.getLinearTickPositions(interval, min, max);
- // Second case: We need intermediary ticks. For example
- // 1, 2, 4, 6, 8, 10, 20, 40 etc.
- } else if (interval >= 0.08) {
- var roundedMin = Math.floor(min),
- intermediate,
- i,
- j,
- len,
- pos,
- lastPos,
- break2;
- if (interval > 0.3) {
- intermediate = [1, 2, 4];
- // 0.2 equals five minor ticks per 1, 10, 100 etc
- } else if (interval > 0.15) {
- intermediate = [1, 2, 4, 6, 8];
- } else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
- intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
- }
- for (i = roundedMin; i < max + 1 && !break2; i++) {
- len = intermediate.length;
- for (j = 0; j < len && !break2; j++) {
- pos = axis.log2lin(axis.lin2log(i) * intermediate[j]);
- // #1670, lastPos is #3113
- if (
- pos > min &&
- (!minor || lastPos <= max) &&
- lastPos !== undefined
- ) {
- positions.push(lastPos);
- }
- if (lastPos > max) {
- break2 = true;
- }
- lastPos = pos;
- }
- }
- // Third case: We are so deep in between whole logarithmic values that
- // we might as well handle the tick positions like a linear axis. For
- // example 1.01, 1.02, 1.03, 1.04.
- } else {
- var realMin = axis.lin2log(min),
- realMax = axis.lin2log(max),
- tickIntervalOption = minor ?
- this.getMinorTickInterval() :
- options.tickInterval,
- filteredTickIntervalOption = tickIntervalOption === 'auto' ?
- null :
- tickIntervalOption,
- tickPixelIntervalOption =
- options.tickPixelInterval / (minor ? 5 : 1),
- totalPixelLength = minor ?
- axisLength / axis.tickPositions.length :
- axisLength;
- interval = pick(
- filteredTickIntervalOption,
- axis._minorAutoInterval,
- (realMax - realMin) *
- tickPixelIntervalOption / (totalPixelLength || 1)
- );
- interval = normalizeTickInterval(
- interval,
- null,
- getMagnitude(interval)
- );
- positions = axis.getLinearTickPositions(
- interval,
- realMin,
- realMax
- ).map(axis.log2lin);
- if (!minor) {
- axis._minorAutoInterval = interval / 5;
- }
- }
- // Set the axis-level tickInterval variable
- if (!minor) {
- axis.tickInterval = interval;
- }
- return positions;
- };
- /**
- * @private
- * @function Highcharts.Axis#log2lin
- *
- * @param {number} num
- *
- * @return {number}
- */
- Axis.prototype.log2lin = function (num) {
- return Math.log(num) / Math.LN10;
- };
- /**
- * @private
- * @function Highcharts.Axis#lin2log
- *
- * @param {number} num
- *
- * @return {number}
- */
- Axis.prototype.lin2log = function (num) {
- return Math.pow(10, num);
- };
- }(Highcharts));
- (function (H, Axis) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Options for plot bands on axes.
- *
- * @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
- */
- /**
- * Options for plot band labels on axes.
- *
- * @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
- */
- /**
- * Options for plot lines on axes.
- *
- * @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
- */
- /**
- * Options for plot line labels on axes.
- *
- * @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
- */
- var arrayMax = H.arrayMax,
- arrayMin = H.arrayMin,
- defined = H.defined,
- destroyObjectProperties = H.destroyObjectProperties,
- erase = H.erase,
- merge = H.merge,
- pick = H.pick;
- /**
- * The object wrapper for plot lines and plot bands
- *
- * @class
- * @name Highcharts.PlotLineOrBand
- *
- * @param {Highcharts.Axis} axis
- *
- * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} options
- */
- H.PlotLineOrBand = function (axis, options) {
- this.axis = axis;
- if (options) {
- this.options = options;
- this.id = options.id;
- }
- };
- H.PlotLineOrBand.prototype = {
- /**
- * Render the plot line or plot band. If it is already existing,
- * move it.
- *
- * @private
- * @function Highcharts.PlotLineOrBand#render
- *
- * @return {Highcharts.PlotLineOrBand|undefined}
- */
- render: function () {
- H.fireEvent(this, 'render');
- var plotLine = this,
- axis = plotLine.axis,
- horiz = axis.horiz,
- options = plotLine.options,
- optionsLabel = options.label,
- label = plotLine.label,
- to = options.to,
- from = options.from,
- value = options.value,
- isBand = defined(from) && defined(to),
- isLine = defined(value),
- svgElem = plotLine.svgElem,
- isNew = !svgElem,
- path = [],
- color = options.color,
- zIndex = pick(options.zIndex, 0),
- events = options.events,
- attribs = {
- 'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
- (options.className || '')
- },
- groupAttribs = {},
- renderer = axis.chart.renderer,
- groupName = isBand ? 'bands' : 'lines',
- group;
- // logarithmic conversion
- if (axis.isLog) {
- from = axis.log2lin(from);
- to = axis.log2lin(to);
- value = axis.log2lin(value);
- }
- // Set the presentational attributes
- if (!axis.chart.styledMode) {
- if (isLine) {
- attribs.stroke = color;
- attribs['stroke-width'] = options.width;
- if (options.dashStyle) {
- attribs.dashstyle = options.dashStyle;
- }
- } else if (isBand) { // plot band
- if (color) {
- attribs.fill = color;
- }
- if (options.borderWidth) {
- attribs.stroke = options.borderColor;
- attribs['stroke-width'] = options.borderWidth;
- }
- }
- }
- // Grouping and zIndex
- groupAttribs.zIndex = zIndex;
- groupName += '-' + zIndex;
- group = axis.plotLinesAndBandsGroups[groupName];
- if (!group) {
- axis.plotLinesAndBandsGroups[groupName] = group =
- renderer.g('plot-' + groupName)
- .attr(groupAttribs).add();
- }
- // Create the path
- if (isNew) {
- /**
- * SVG element of the plot line or band.
- *
- * @name Highcharts.PlotLineOrBand#svgElement
- * @type {Highcharts.SVGElement}
- */
- plotLine.svgElem = svgElem =
- renderer
- .path()
- .attr(attribs).add(group);
- }
- // Set the path or return
- if (isLine) {
- path = axis.getPlotLinePath(value, svgElem.strokeWidth());
- } else if (isBand) { // plot band
- path = axis.getPlotBandPath(from, to, options);
- } else {
- return;
- }
- // common for lines and bands
- if (isNew && path && path.length) {
- svgElem.attr({ d: path });
- // events
- if (events) {
- H.objectEach(events, function (event, eventType) {
- svgElem.on(eventType, function (e) {
- events[eventType].apply(plotLine, [e]);
- });
- });
- }
- } else if (svgElem) {
- if (path) {
- svgElem.show();
- svgElem.animate({ d: path });
- } else {
- svgElem.hide();
- if (label) {
- plotLine.label = label = label.destroy();
- }
- }
- }
- // the plot band/line label
- if (
- optionsLabel &&
- defined(optionsLabel.text) &&
- path &&
- path.length &&
- axis.width > 0 &&
- axis.height > 0 &&
- !path.isFlat
- ) {
- // apply defaults
- optionsLabel = merge({
- align: horiz && isBand && 'center',
- x: horiz ? !isBand && 4 : 10,
- verticalAlign: !horiz && isBand && 'middle',
- y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
- rotation: horiz && !isBand && 90
- }, optionsLabel);
- this.renderLabel(optionsLabel, path, isBand, zIndex);
- } else if (label) { // move out of sight
- label.hide();
- }
- // chainable
- return plotLine;
- },
- /**
- * Render and align label for plot line or band.
- *
- * @private
- * @function Highcharts.PlotLineOrBand#renderLabel
- *
- * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
- *
- * @param {Highcharts.SVGPathArray} path
- *
- * @param {boolean} [isBand]
- *
- * @param {number} [zIndex]
- */
- renderLabel: function (optionsLabel, path, isBand, zIndex) {
- var plotLine = this,
- label = plotLine.label,
- renderer = plotLine.axis.chart.renderer,
- attribs,
- xBounds,
- yBounds,
- x,
- y;
- // add the SVG element
- if (!label) {
- attribs = {
- align: optionsLabel.textAlign || optionsLabel.align,
- rotation: optionsLabel.rotation,
- 'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
- '-label ' + (optionsLabel.className || '')
- };
- attribs.zIndex = zIndex;
- /**
- * SVG element of the label.
- *
- * @name Highcharts.PlotLineOrBand#label
- * @type {Highcharts.SVGElement}
- */
- plotLine.label = label = renderer.text(
- optionsLabel.text,
- 0,
- 0,
- optionsLabel.useHTML
- )
- .attr(attribs)
- .add();
- if (!this.axis.chart.styledMode) {
- label.css(optionsLabel.style);
- }
- }
- // get the bounding box and align the label
- // #3000 changed to better handle choice between plotband or plotline
- xBounds = path.xBounds ||
- [path[1], path[4], (isBand ? path[6] : path[1])];
- yBounds = path.yBounds ||
- [path[2], path[5], (isBand ? path[7] : path[2])];
- x = arrayMin(xBounds);
- y = arrayMin(yBounds);
- label.align(optionsLabel, false, {
- x: x,
- y: y,
- width: arrayMax(xBounds) - x,
- height: arrayMax(yBounds) - y
- });
- label.show();
- },
- /**
- * Remove the plot line or band.
- *
- * @function Highcharts.PlotLineOrBand#destroy
- */
- destroy: function () {
- // remove it from the lookup
- erase(this.axis.plotLinesAndBands, this);
- delete this.axis;
- destroyObjectProperties(this);
- }
- };
- // Object with members for extending the Axis prototype
- H.extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
- /**
- * An array of colored bands stretching across the plot area marking an
- * interval on the axis.
- *
- * In styled mode, the plot bands are styled by the `.highcharts-plot-band`
- * class in addition to the `className` option.
- *
- * @productdesc {highcharts}
- * In a gauge, a plot band on the Y axis (value axis) will stretch along the
- * perimeter of the gauge.
- *
- * @type {Array<*>}
- * @product highcharts highstock gantt
- * @apioption xAxis.plotBands
- */
- /**
- * Border color for the plot band. Also requires `borderWidth` to be set.
- *
- * @type {Highcharts.ColorString}
- * @apioption xAxis.plotBands.borderColor
- */
- /**
- * Border width for the plot band. Also requires `borderColor` to be set.
- *
- * @type {number}
- * @default 0
- * @apioption xAxis.plotBands.borderWidth
- */
- /**
- * A custom class name, in addition to the default `highcharts-plot-band`,
- * to apply to each individual band.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption xAxis.plotBands.className
- */
- /**
- * The color of the plot band.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-color/
- * Color band
- * @sample {highstock} stock/xaxis/plotbands/
- * Plot band on Y axis
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @apioption xAxis.plotBands.color
- */
- /**
- * An object defining mouse events for the plot band. Supported properties
- * are `click`, `mouseover`, `mouseout`, `mousemove`.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-events/
- * Mouse events demonstrated
- *
- * @since 1.2
- * @context PlotLineOrBand
- * @apioption xAxis.plotBands.events
- */
- /**
- * The start position of the plot band in axis units.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-color/
- * Datetime axis
- * @sample {highcharts} highcharts/xaxis/plotbands-from/
- * Categorized axis
- * @sample {highstock} stock/xaxis/plotbands/
- * Plot band on Y axis
- *
- * @type {number}
- * @apioption xAxis.plotBands.from
- */
- /**
- * An id used for identifying the plot band in Axis.removePlotBand.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-id/
- * Remove plot band by id
- * @sample {highstock} highcharts/xaxis/plotbands-id/
- * Remove plot band by id
- *
- * @type {string}
- * @apioption xAxis.plotBands.id
- */
- /**
- * The end position of the plot band in axis units.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-color/
- * Datetime axis
- * @sample {highcharts} highcharts/xaxis/plotbands-from/
- * Categorized axis
- * @sample {highstock} stock/xaxis/plotbands/
- * Plot band on Y axis
- *
- * @type {number}
- * @apioption xAxis.plotBands.to
- */
- /**
- * The z index of the plot band within the chart, relative to other
- * elements. Using the same z index as another element may give
- * unpredictable results, as the last rendered element will be on top.
- * Values from 0 to 20 make sense.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-color/
- * Behind plot lines by default
- * @sample {highcharts} highcharts/xaxis/plotbands-zindex/
- * Above plot lines
- * @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
- * Above plot lines and series
- *
- * @type {number}
- * @since 1.2
- * @apioption xAxis.plotBands.zIndex
- */
- /**
- * Text labels for the plot bands
- *
- * @product highcharts highstock gantt
- * @apioption xAxis.plotBands.label
- */
- /**
- * Horizontal alignment of the label. Can be one of "left", "center" or
- * "right".
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
- * Aligned to the right
- * @sample {highstock} stock/xaxis/plotbands-label/
- * Plot band with labels
- *
- * @type {Highcharts.AlignType}
- * @default center
- * @since 2.1
- * @apioption xAxis.plotBands.label.align
- */
- /**
- * Rotation of the text label in degrees .
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
- * Vertical text
- *
- * @type {number}
- * @default 0
- * @since 2.1
- * @apioption xAxis.plotBands.label.rotation
- */
- /**
- * CSS styles for the text label.
- *
- * In styled mode, the labels are styled by the
- * `.highcharts-plot-band-label` class.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-label-style/
- * Blue and bold label
- *
- * @type {Highcharts.CSSObject}
- * @since 2.1
- * @apioption xAxis.plotBands.label.style
- */
- /**
- * The string text itself. A subset of HTML is supported.
- *
- * @type {string}
- * @since 2.1
- * @apioption xAxis.plotBands.label.text
- */
- /**
- * The text alignment for the label. While `align` determines where the
- * texts anchor point is placed within the plot band, `textAlign` determines
- * how the text is aligned against its anchor point. Possible values are
- * "left", "center" and "right". Defaults to the same as the `align` option.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
- * Vertical text in center position but text-aligned left
- *
- * @type {Highcharts.AlignType}
- * @since 2.1
- * @apioption xAxis.plotBands.label.textAlign
- */
- /**
- * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the labels.
- *
- * @type {boolean}
- * @default false
- * @since 3.0.3
- * @apioption xAxis.plotBands.label.useHTML
- */
- /**
- * Vertical alignment of the label relative to the plot band. Can be one of
- * "top", "middle" or "bottom".
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
- * Vertically centered label
- * @sample {highstock} stock/xaxis/plotbands-label/
- * Plot band with labels
- *
- * @type {Highcharts.VerticalAlignType}
- * @default top
- * @since 2.1
- * @apioption xAxis.plotBands.label.verticalAlign
- */
- /**
- * Horizontal position relative the alignment. Default varies by
- * orientation.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
- * Aligned 10px from the right edge
- * @sample {highstock} stock/xaxis/plotbands-label/
- * Plot band with labels
- *
- * @type {number}
- * @since 2.1
- * @apioption xAxis.plotBands.label.x
- */
- /**
- * Vertical position of the text baseline relative to the alignment. Default
- * varies by orientation.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-label-y/
- * Label on x axis
- * @sample {highstock} stock/xaxis/plotbands-label/
- * Plot band with labels
- *
- * @type {number}
- * @since 2.1
- * @apioption xAxis.plotBands.label.y
- */
- /**
- * An array of lines stretching across the plot area, marking a specific
- * value on one of the axes.
- *
- * In styled mode, the plot lines are styled by the
- * `.highcharts-plot-line` class in addition to the `className` option.
- *
- * @type {Array<*>}
- * @product highcharts highstock gantt
- * @apioption xAxis.plotLines
- */
- /**
- * A custom class name, in addition to the default `highcharts-plot-line`,
- * to apply to each individual line.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption xAxis.plotLines.className
- */
- /**
- * The color of the line.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-color/
- * A red line from X axis
- * @sample {highstock} stock/xaxis/plotlines/
- * Plot line on Y axis
- *
- * @type {Highcharts.ColorString}
- * @apioption xAxis.plotLines.color
- */
- /**
- * The dashing or dot style for the plot line. For possible values see
- * [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
- * Dash and dot pattern
- * @sample {highstock} stock/xaxis/plotlines/
- * Plot line on Y axis
- *
- * @type {Highcharts.DashStyleType}
- * @default Solid
- * @since 1.2
- * @apioption xAxis.plotLines.dashStyle
- */
- /**
- * An object defining mouse events for the plot line. Supported
- * properties are `click`, `mouseover`, `mouseout`, `mousemove`.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-events/
- * Mouse events demonstrated
- *
- * @type {*}
- * @since 1.2
- * @context PlotLineOrBand
- * @apioption xAxis.plotLines.events
- */
- /**
- * An id used for identifying the plot line in Axis.removePlotLine.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-id/
- * Remove plot line by id
- *
- * @type {string}
- * @apioption xAxis.plotLines.id
- */
- /**
- * The position of the line in axis units.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-color/
- * Between two categories on X axis
- * @sample {highstock} stock/xaxis/plotlines/
- * Plot line on Y axis
- *
- * @type {number}
- * @apioption xAxis.plotLines.value
- */
- /**
- * The width or thickness of the plot line.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-color/
- * 2px wide line from X axis
- * @sample {highstock} stock/xaxis/plotlines/
- * Plot line on Y axis
- *
- * @type {number}
- * @apioption xAxis.plotLines.width
- */
- /**
- * The z index of the plot line within the chart.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
- * Behind plot lines by default
- * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
- * Above plot lines
- * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
- * Above plot lines and series
- *
- * @type {number}
- * @since 1.2
- * @apioption xAxis.plotLines.zIndex
- */
- /**
- * Text labels for the plot bands
- *
- * @apioption xAxis.plotLines.label
- */
- /**
- * Horizontal alignment of the label. Can be one of "left", "center" or
- * "right".
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
- * Aligned to the right
- * @sample {highstock} stock/xaxis/plotlines/
- * Plot line on Y axis
- *
- * @type {Highcharts.AlignType}
- * @default left
- * @since 2.1
- * @apioption xAxis.plotLines.label.align
- */
- /**
- * Rotation of the text label in degrees. Defaults to 0 for horizontal plot
- * lines and 90 for vertical lines.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
- * Slanted text
- *
- * @type {number}
- * @since 2.1
- * @apioption xAxis.plotLines.label.rotation
- */
- /**
- * CSS styles for the text label.
- *
- * In styled mode, the labels are styled by the
- * `.highcharts-plot-line-label` class.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-label-style/
- * Blue and bold label
- *
- * @type {Highcharts.CSSObject}
- * @since 2.1
- * @apioption xAxis.plotLines.label.style
- */
- /**
- * The text itself. A subset of HTML is supported.
- *
- * @type {string}
- * @since 2.1
- * @apioption xAxis.plotLines.label.text
- */
- /**
- * The text alignment for the label. While `align` determines where the
- * texts anchor point is placed within the plot band, `textAlign` determines
- * how the text is aligned against its anchor point. Possible values are
- * "left", "center" and "right". Defaults to the same as the `align` option.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
- * Text label in bottom position
- *
- * @type {Highcharts.AlignType}
- * @since 2.1
- * @apioption xAxis.plotLines.label.textAlign
- */
- /**
- * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the labels.
- *
- * @type {boolean}
- * @default false
- * @since 3.0.3
- * @apioption xAxis.plotLines.label.useHTML
- */
- /**
- * Vertical alignment of the label relative to the plot line. Can be
- * one of "top", "middle" or "bottom".
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
- * Vertically centered label
- *
- * @type {Highcharts.VerticalAlignType}
- * @default {highcharts} top
- * @default {highstock} top
- * @since 2.1
- * @validvalue ["top", "middle", "bottom"]
- * @apioption xAxis.plotLines.label.verticalAlign
- */
- /**
- * Horizontal position relative the alignment. Default varies by
- * orientation.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
- * Aligned 10px from the right edge
- * @sample {highstock} stock/xaxis/plotlines/
- * Plot line on Y axis
- *
- * @type {number}
- * @since 2.1
- * @apioption xAxis.plotLines.label.x
- */
- /**
- * Vertical position of the text baseline relative to the alignment. Default
- * varies by orientation.
- *
- * @sample {highcharts} highcharts/xaxis/plotlines-label-y/
- * Label below the plot line
- * @sample {highstock} stock/xaxis/plotlines/
- * Plot line on Y axis
- *
- * @type {number}
- * @since 2.1
- * @apioption xAxis.plotLines.label.y
- */
- /**
- * An array of objects defining plot bands on the Y axis.
- *
- * @type {Array<*>}
- * @extends xAxis.plotBands
- * @apioption yAxis.plotBands
- */
- /**
- * In a gauge chart, this option determines the inner radius of the
- * plot band that stretches along the perimeter. It can be given as
- * a percentage string, like `"100%"`, or as a pixel number, like `100`.
- * By default, the inner radius is controlled by the [thickness](
- * #yAxis.plotBands.thickness) option.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-gauge
- * Gauge plot band
- *
- * @type {number|string}
- * @since 2.3
- * @product highcharts
- * @apioption yAxis.plotBands.innerRadius
- */
- /**
- * In a gauge chart, this option determines the outer radius of the
- * plot band that stretches along the perimeter. It can be given as
- * a percentage string, like `"100%"`, or as a pixel number, like `100`.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-gauge
- * Gauge plot band
- *
- * @type {number|string}
- * @default 100%
- * @since 2.3
- * @product highcharts
- * @apioption yAxis.plotBands.outerRadius
- */
- /**
- * In a gauge chart, this option sets the width of the plot band
- * stretching along the perimeter. It can be given as a percentage
- * string, like `"10%"`, or as a pixel number, like `10`. The default
- * value 10 is the same as the default [tickLength](#yAxis.tickLength),
- * thus making the plot band act as a background for the tick markers.
- *
- * @sample {highcharts} highcharts/xaxis/plotbands-gauge
- * Gauge plot band
- *
- * @type {number|string}
- * @default 10
- * @since 2.3
- * @product highcharts
- * @apioption yAxis.plotBands.thickness
- */
- /**
- * An array of objects representing plot lines on the X axis
- *
- * @type {Array<*>}
- * @extends xAxis.plotLines
- * @apioption yAxis.plotLines
- */
- /**
- * Internal function to create the SVG path definition for a plot band.
- *
- * @function Highcharts.Axis#getPlotBandPath
- *
- * @param {number} from
- * The axis value to start from.
- *
- * @param {number} to
- * The axis value to end on.
- *
- * @return {Highcharts.SVGPathArray}
- * The SVG path definition in array form.
- */
- getPlotBandPath: function (from, to) {
- var toPath = this.getPlotLinePath(to, null, null, true),
- path = this.getPlotLinePath(from, null, null, true),
- result = [],
- i,
- // #4964 check if chart is inverted or plotband is on yAxis
- horiz = this.horiz,
- plus = 1,
- isFlat,
- outside =
- (from < this.min && to < this.min) ||
- (from > this.max && to > this.max);
- if (path && toPath) {
- // Flat paths don't need labels (#3836)
- if (outside) {
- isFlat = path.toString() === toPath.toString();
- plus = 0;
- }
- // Go over each subpath - for panes in Highstock
- for (i = 0; i < path.length; i += 6) {
- // Add 1 pixel when coordinates are the same
- if (horiz && toPath[i + 1] === path[i + 1]) {
- toPath[i + 1] += plus;
- toPath[i + 4] += plus;
- } else if (!horiz && toPath[i + 2] === path[i + 2]) {
- toPath[i + 2] += plus;
- toPath[i + 5] += plus;
- }
- result.push(
- 'M',
- path[i + 1],
- path[i + 2],
- 'L',
- path[i + 4],
- path[i + 5],
- toPath[i + 4],
- toPath[i + 5],
- toPath[i + 1],
- toPath[i + 2],
- 'z'
- );
- result.isFlat = isFlat;
- }
- } else { // outside the axis area
- path = null;
- }
- return result;
- },
- /**
- * Add a plot band after render time.
- *
- * @sample highcharts/members/axis-addplotband/
- * Toggle the plot band from a button
- *
- * @function Highcharts.Axis#addPlotBand
- *
- * @param {Highcharts.AxisPlotBandsOptions} options
- * A configuration object for the plot band, as defined in
- * [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
- *
- * @return {Highcharts.PlotLineOrBand|undefined}
- * The added plot band.
- */
- addPlotBand: function (options) {
- return this.addPlotBandOrLine(options, 'plotBands');
- },
- /**
- * Add a plot line after render time.
- *
- * @sample highcharts/members/axis-addplotline/
- * Toggle the plot line from a button
- *
- * @function Highcharts.Axis#addPlotLine
- *
- * @param {Highcharts.AxisPlotLinesOptions} options
- * A configuration object for the plot line, as defined in
- * [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
- *
- * @return {Highcharts.PlotLineOrBand|undefined}
- * The added plot line.
- */
- addPlotLine: function (options) {
- return this.addPlotBandOrLine(options, 'plotLines');
- },
- /**
- * Add a plot band or plot line after render time. Called from addPlotBand
- * and addPlotLine internally.
- *
- * @private
- * @function Highcharts.Axis#addPlotBandOrLine
- *
- * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} options
- * The plotBand or plotLine configuration object.
- *
- * @param {"plotBands"|"plotLines"} [coll]
- *
- * @return {Highcharts.PlotLineOrBand|undefined}
- */
- addPlotBandOrLine: function (options, coll) {
- var obj = new H.PlotLineOrBand(this, options).render(),
- userOptions = this.userOptions;
- if (obj) { // #2189
- // Add it to the user options for exporting and Axis.update
- if (coll) {
- userOptions[coll] = userOptions[coll] || [];
- userOptions[coll].push(options);
- }
- this.plotLinesAndBands.push(obj);
- }
- return obj;
- },
- /**
- * Remove a plot band or plot line from the chart by id. Called internally
- * from `removePlotBand` and `removePlotLine`.
- *
- * @private
- * @function Highcharts.Axis#removePlotBandOrLine
- *
- * @param {string} id
- */
- removePlotBandOrLine: function (id) {
- var plotLinesAndBands = this.plotLinesAndBands,
- options = this.options,
- userOptions = this.userOptions,
- i = plotLinesAndBands.length;
- while (i--) {
- if (plotLinesAndBands[i].id === id) {
- plotLinesAndBands[i].destroy();
- }
- }
- ([
- options.plotLines || [],
- userOptions.plotLines || [],
- options.plotBands || [],
- userOptions.plotBands || []
- ]).forEach(function (arr) {
- i = arr.length;
- while (i--) {
- if (arr[i].id === id) {
- erase(arr, arr[i]);
- }
- }
- });
- },
- /**
- * Remove a plot band by its id.
- *
- * @sample highcharts/members/axis-removeplotband/
- * Remove plot band by id
- * @sample highcharts/members/axis-addplotband/
- * Toggle the plot band from a button
- *
- * @function Highcharts.Axis#removePlotBand
- *
- * @param {string} id
- * The plot band's `id` as given in the original configuration
- * object or in the `addPlotBand` option.
- */
- removePlotBand: function (id) {
- this.removePlotBandOrLine(id);
- },
- /**
- * Remove a plot line by its id.
- *
- * @sample highcharts/xaxis/plotlines-id/
- * Remove plot line by id
- * @sample highcharts/members/axis-addplotline/
- * Toggle the plot line from a button
- *
- * @function Highcharts.Axis#removePlotLine
- *
- * @param {string} id
- * The plot line's `id` as given in the original configuration
- * object or in the `addPlotLine` option.
- */
- removePlotLine: function (id) {
- this.removePlotBandOrLine(id);
- }
- });
- }(Highcharts, Axis));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * A callback function to place the tooltip in a specific position.
- *
- * @callback Highcharts.TooltipPositionerCallbackFunction
- *
- * @param {number} labelWidth
- * Width of the tooltip.
- *
- * @param {number} labelHeight
- * Height of the tooltip.
- *
- * @param {Highcharts.TooltipPositionerPointObject} point
- * Point information for positioning a tooltip.
- *
- * @return {Highcharts.PositionObject}
- * New position for the tooltip.
- */
- /**
- * Point information for positioning a tooltip.
- *
- * @interface Highcharts.TooltipPositionerPointObject
- *//**
- * If `tooltip.split` option is enabled and positioner is called for each of the
- * boxes separately, this property indicates the call on the xAxis header, which
- * is not a point itself.
- * @name Highcharts.TooltipPositionerPointObject#isHeader
- * @type {boolean}
- *//**
- * @name Highcharts.TooltipPositionerPointObject#negative
- * @type {boolean}
- *//**
- * The reference point relative to the plot area. Add chart.plotLeft to get the
- * full coordinates.
- * @name Highcharts.TooltipPositionerPointObject#plotX
- * @type {number}
- *//**
- * The reference point relative to the plot area. Add chart.plotTop to get the
- * full coordinates.
- * @name Highcharts.TooltipPositionerPointObject#plotY
- * @type {number}
- */
- var doc = H.doc,
- extend = H.extend,
- format = H.format,
- isNumber = H.isNumber,
- merge = H.merge,
- pick = H.pick,
- splat = H.splat,
- syncTimeout = H.syncTimeout,
- timeUnits = H.timeUnits;
- /**
- * Tooltip of a chart.
- *
- * @class
- * @name Highcharts.Tooltip
- *
- * @param {Highcharts.Chart} chart
- * The chart instance.
- *
- * @param {Highcharts.TooltipOptions} options
- * Tooltip options.
- */
- H.Tooltip = function () {
- this.init.apply(this, arguments);
- };
- H.Tooltip.prototype = {
- /**
- * @private
- * @function Highcharts.Tooltip#init
- *
- * @param {Highcharts.Chart} chart
- * The chart instance.
- *
- * @param {Highcharts.TooltipOptions} options
- * Tooltip options.
- */
- init: function (chart, options) {
- /**
- * Chart of the tooltip.
- *
- * @readonly
- * @name Highcharts.Tooltip#chart
- * @type {Highcharts.Chart}
- */
- this.chart = chart;
- /**
- * Used tooltip options.
- *
- * @readonly
- * @name Highcharts.Tooltip#options
- * @type {Highcharts.TooltipOptions}
- */
- this.options = options;
- /**
- * List of crosshairs.
- *
- * @private
- * @readonly
- * @name Highcharts.Tooltip#crosshairs
- * @type {Array<*>}
- */
- this.crosshairs = [];
- /**
- * Current values of x and y when animating.
- *
- * @private
- * @readonly
- * @name Highcharts.Tooltip#now
- * @type {*}
- */
- this.now = { x: 0, y: 0 };
- /**
- * Tooltips are initially hidden.
- *
- * @readonly
- * @name Highcharts.Tooltip#isHidden
- * @type {boolean}
- */
- this.isHidden = true;
- /**
- * True, if the tooltip is splitted into one label per series, with the
- * header close to the axis.
- *
- * @readonly
- * @name Highcharts.Tooltip#split
- * @type {boolean}
- */
- this.split = options.split && !chart.inverted;
- /**
- * When the tooltip is shared, the entire plot area will capture mouse
- * movement or touch events.
- *
- * @readonly
- * @name Highcharts.Tooltip#shared
- * @type {boolean}
- */
- this.shared = options.shared || this.split;
- /**
- * Whether to allow the tooltip to render outside the chart's SVG
- * element box. By default (false), the tooltip is rendered within the
- * chart's SVG element, which results in the tooltip being aligned
- * inside the chart area.
- *
- * @readonly
- * @name Highcharts.Tooltip#outside
- * @type {boolean}
- *
- * @todo
- * Split tooltip does not support outside in the first iteration. Should
- * not be too complicated to implement.
- */
- this.outside = options.outside && !this.split;
- },
- /**
- * Destroy the single tooltips in a split tooltip.
- * If the tooltip is active then it is not destroyed, unless forced to.
- *
- * @private
- * @function Highcharts.Tooltip#cleanSplit
- *
- * @param {boolean} force
- * Force destroy all tooltips.
- */
- cleanSplit: function (force) {
- this.chart.series.forEach(function (series) {
- var tt = series && series.tt;
- if (tt) {
- if (!tt.isActive || force) {
- series.tt = tt.destroy();
- } else {
- tt.isActive = false;
- }
- }
- });
- },
- /**
- * In styled mode, apply the default filter for the tooltip drop-shadow. It
- * needs to have an id specific to the chart, otherwise there will be issues
- * when one tooltip adopts the filter of a different chart, specifically one
- * where the container is hidden.
- *
- * @private
- * @function Highcharts.Tooltip#applyFilter
- */
- applyFilter: function () {
- var chart = this.chart;
- chart.renderer.definition({
- tagName: 'filter',
- id: 'drop-shadow-' + chart.index,
- opacity: 0.5,
- children: [{
- tagName: 'feGaussianBlur',
- 'in': 'SourceAlpha',
- stdDeviation: 1
- }, {
- tagName: 'feOffset',
- dx: 1,
- dy: 1
- }, {
- tagName: 'feComponentTransfer',
- children: [{
- tagName: 'feFuncA',
- type: 'linear',
- slope: 0.3
- }]
- }, {
- tagName: 'feMerge',
- children: [{
- tagName: 'feMergeNode'
- }, {
- tagName: 'feMergeNode',
- 'in': 'SourceGraphic'
- }]
- }]
- });
- chart.renderer.definition({
- tagName: 'style',
- textContent: '.highcharts-tooltip-' + chart.index + '{' +
- 'filter:url(#drop-shadow-' + chart.index + ')' +
- '}'
- });
- },
- /**
- * Creates the Tooltip label element if it does not exist, then returns it.
- *
- * @function Highcharts.Tooltip#getLabel
- *
- * @return {Highcharts.SVGElement}
- */
- getLabel: function () {
- var tooltip = this,
- renderer = this.chart.renderer,
- styledMode = this.chart.styledMode,
- options = this.options,
- container,
- set;
- if (!this.label) {
- if (this.outside) {
- this.container = container = H.doc.createElement('div');
- container.className = 'highcharts-tooltip-container';
- H.css(container, {
- position: 'absolute',
- top: '1px',
- pointerEvents: options.style && options.style.pointerEvents
- });
- H.doc.body.appendChild(container);
- this.renderer = renderer = new H.Renderer(container, 0, 0);
- }
- // Create the label
- if (this.split) {
- this.label = renderer.g('tooltip');
- } else {
- this.label = renderer
- .label(
- '',
- 0,
- 0,
- options.shape || 'callout',
- null,
- null,
- options.useHTML,
- null,
- 'tooltip'
- )
- .attr({
- padding: options.padding,
- r: options.borderRadius
- });
- if (!styledMode) {
- this.label
- .attr({
- 'fill': options.backgroundColor,
- 'stroke-width': options.borderWidth
- })
- // #2301, #2657
- .css(options.style)
- .shadow(options.shadow);
- }
- }
- if (styledMode) {
- // Apply the drop-shadow filter
- this.applyFilter();
- this.label.addClass('highcharts-tooltip-' + this.chart.index);
- }
- if (this.outside) {
- set = {
- x: this.label.xSetter,
- y: this.label.ySetter
- };
- this.label.xSetter = function (value, key) {
- set[key].call(this.label, tooltip.distance);
- container.style.left = value + 'px';
- };
- this.label.ySetter = function (value, key) {
- set[key].call(this.label, tooltip.distance);
- container.style.top = value + 'px';
- };
- }
- this.label
- .attr({
- zIndex: 8
- })
- .add();
- }
- return this.label;
- },
- /**
- * Updates the tooltip with the provided tooltip options.
- *
- * @function Highcharts.Tooltip#update
- *
- * @param {Highcharts.TooltipOptions} options
- * The tooltip options to update.
- */
- update: function (options) {
- this.destroy();
- // Update user options (#6218)
- merge(true, this.chart.options.tooltip.userOptions, options);
- this.init(this.chart, merge(true, this.options, options));
- },
- /**
- * Removes and destroys the tooltip and its elements.
- *
- * @function Highcharts.Tooltip#destroy
- */
- destroy: function () {
- // Destroy and clear local variables
- if (this.label) {
- this.label = this.label.destroy();
- }
- if (this.split && this.tt) {
- this.cleanSplit(this.chart, true);
- this.tt = this.tt.destroy();
- }
- if (this.renderer) {
- this.renderer = this.renderer.destroy();
- H.discardElement(this.container);
- }
- H.clearTimeout(this.hideTimer);
- H.clearTimeout(this.tooltipTimeout);
- },
- /**
- * Moves the tooltip with a soft animation to a new position.
- *
- * @function Highcharts.Tooltip#move
- *
- * @param {number} x
- *
- * @param {number} y
- *
- * @param {number} anchorX
- *
- * @param {number} anchorY
- */
- move: function (x, y, anchorX, anchorY) {
- var tooltip = this,
- now = tooltip.now,
- animate = tooltip.options.animation !== false &&
- !tooltip.isHidden &&
- // When we get close to the target position, abort animation and
- // land on the right place (#3056)
- (Math.abs(x - now.x) > 1 || Math.abs(y - now.y) > 1),
- skipAnchor = tooltip.followPointer || tooltip.len > 1;
- // Get intermediate values for animation
- extend(now, {
- x: animate ? (2 * now.x + x) / 3 : x,
- y: animate ? (now.y + y) / 2 : y,
- anchorX: skipAnchor ?
- undefined :
- animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
- anchorY: skipAnchor ?
- undefined :
- animate ? (now.anchorY + anchorY) / 2 : anchorY
- });
- // Move to the intermediate value
- tooltip.getLabel().attr(now);
- // Run on next tick of the mouse tracker
- if (animate) {
- // Never allow two timeouts
- H.clearTimeout(this.tooltipTimeout);
- // Set the fixed interval ticking for the smooth tooltip
- this.tooltipTimeout = setTimeout(function () {
- // The interval function may still be running during destroy,
- // so check that the chart is really there before calling.
- if (tooltip) {
- tooltip.move(x, y, anchorX, anchorY);
- }
- }, 32);
- }
- },
- /**
- * Hides the tooltip with a fade out animation.
- *
- * @function Highcharts.Tooltip#hide
- *
- * @param {number} [delay]
- * The fade out in milliseconds. If no value is provided the value
- * of the tooltip.hideDelay option is used. A value of 0 disables
- * the fade out animation.
- */
- hide: function (delay) {
- var tooltip = this;
- // disallow duplicate timers (#1728, #1766)
- H.clearTimeout(this.hideTimer);
- delay = pick(delay, this.options.hideDelay, 500);
- if (!this.isHidden) {
- this.hideTimer = syncTimeout(function () {
- tooltip.getLabel()[delay ? 'fadeOut' : 'hide']();
- tooltip.isHidden = true;
- }, delay);
- }
- },
- /**
- * Extendable method to get the anchor position of the tooltip
- * from a point or set of points
- *
- * @private
- * @function Highcharts.Tooltip#getAnchor
- *
- * @param {Array<Highchart.Points>} points
- *
- * @param {global.Event} [mouseEvent]
- */
- getAnchor: function (points, mouseEvent) {
- var ret,
- chart = this.chart,
- pointer = chart.pointer,
- inverted = chart.inverted,
- plotTop = chart.plotTop,
- plotLeft = chart.plotLeft,
- plotX = 0,
- plotY = 0,
- yAxis,
- xAxis;
- points = splat(points);
- // When tooltip follows mouse, relate the position to the mouse
- if (this.followPointer && mouseEvent) {
- if (mouseEvent.chartX === undefined) {
- mouseEvent = pointer.normalize(mouseEvent);
- }
- ret = [
- mouseEvent.chartX - chart.plotLeft,
- mouseEvent.chartY - plotTop
- ];
- // Pie uses a special tooltipPos
- } else if (points[0].tooltipPos) {
- ret = points[0].tooltipPos;
- // When shared, use the average position
- } else {
- points.forEach(function (point) {
- yAxis = point.series.yAxis;
- xAxis = point.series.xAxis;
- plotX += point.plotX +
- (!inverted && xAxis ? xAxis.left - plotLeft : 0);
- plotY +=
- (
- point.plotLow ?
- (point.plotLow + point.plotHigh) / 2 :
- point.plotY
- ) +
- (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
- });
- plotX /= points.length;
- plotY /= points.length;
- ret = [
- inverted ? chart.plotWidth - plotY : plotX,
- this.shared && !inverted && points.length > 1 && mouseEvent ?
- // place shared tooltip next to the mouse (#424)
- mouseEvent.chartY - plotTop :
- inverted ? chart.plotHeight - plotX : plotY
- ];
- }
- return ret.map(Math.round);
- },
- /**
- * Place the tooltip in a chart without spilling over
- * and not covering the point it self.
- *
- * @private
- * @function Highcharts.Tooltip#getPosition
- *
- * @param {number} boxWidth
- *
- * @param {number} boxHeight
- *
- * @param {Highcharts.Point} point
- *
- * @return {*}
- */
- getPosition: function (boxWidth, boxHeight, point) {
- var chart = this.chart,
- distance = this.distance,
- ret = {},
- // Don't use h if chart isn't inverted (#7242)
- h = (chart.inverted && point.h) || 0, // #4117
- swapped,
- outside = this.outside,
- outerWidth = outside ?
- // substract distance to prevent scrollbars
- doc.documentElement.clientWidth - 2 * distance :
- chart.chartWidth,
- outerHeight = outside ?
- Math.max(
- doc.body.scrollHeight,
- doc.documentElement.scrollHeight,
- doc.body.offsetHeight,
- doc.documentElement.offsetHeight,
- doc.documentElement.clientHeight
- ) :
- chart.chartHeight,
- chartPosition = chart.pointer.chartPosition,
- first = [
- 'y',
- outerHeight,
- boxHeight,
- (outside ? chartPosition.top - distance : 0) +
- point.plotY + chart.plotTop,
- outside ? 0 : chart.plotTop,
- outside ? outerHeight : chart.plotTop + chart.plotHeight
- ],
- second = [
- 'x',
- outerWidth,
- boxWidth,
- (outside ? chartPosition.left - distance : 0) +
- point.plotX + chart.plotLeft,
- outside ? 0 : chart.plotLeft,
- outside ? outerWidth : chart.plotLeft + chart.plotWidth
- ],
- // The far side is right or bottom
- preferFarSide = !this.followPointer && pick(
- point.ttBelow,
- !chart.inverted === !!point.negative
- ), // #4984
- /*
- * Handle the preferred dimension. When the preferred dimension is
- * tooltip on top or bottom of the point, it will look for space
- * there.
- *
- * @private
- */
- firstDimension = function (
- dim,
- outerSize,
- innerSize,
- point,
- min,
- max
- ) {
- var roomLeft = innerSize < point - distance,
- roomRight = point + distance + innerSize < outerSize,
- alignedLeft = point - distance - innerSize,
- alignedRight = point + distance;
- if (preferFarSide && roomRight) {
- ret[dim] = alignedRight;
- } else if (!preferFarSide && roomLeft) {
- ret[dim] = alignedLeft;
- } else if (roomLeft) {
- ret[dim] = Math.min(
- max - innerSize,
- alignedLeft - h < 0 ? alignedLeft : alignedLeft - h
- );
- } else if (roomRight) {
- ret[dim] = Math.max(
- min,
- alignedRight + h + innerSize > outerSize ?
- alignedRight :
- alignedRight + h
- );
- } else {
- return false;
- }
- },
- /*
- * Handle the secondary dimension. If the preferred dimension is
- * tooltip on top or bottom of the point, the second dimension is to
- * align the tooltip above the point, trying to align center but
- * allowing left or right align within the chart box.
- *
- * @private
- */
- secondDimension = function (dim, outerSize, innerSize, point) {
- var retVal;
- // Too close to the edge, return false and swap dimensions
- if (point < distance || point > outerSize - distance) {
- retVal = false;
- // Align left/top
- } else if (point < innerSize / 2) {
- ret[dim] = 1;
- // Align right/bottom
- } else if (point > outerSize - innerSize / 2) {
- ret[dim] = outerSize - innerSize - 2;
- // Align center
- } else {
- ret[dim] = point - innerSize / 2;
- }
- return retVal;
- },
- /*
- * Swap the dimensions
- */
- swap = function (count) {
- var temp = first;
- first = second;
- second = temp;
- swapped = count;
- },
- run = function () {
- if (firstDimension.apply(0, first) !== false) {
- if (
- secondDimension.apply(0, second) === false &&
- !swapped
- ) {
- swap(true);
- run();
- }
- } else if (!swapped) {
- swap(true);
- run();
- } else {
- ret.x = ret.y = 0;
- }
- };
- // Under these conditions, prefer the tooltip on the side of the point
- if (chart.inverted || this.len > 1) {
- swap();
- }
- run();
- return ret;
- },
- /**
- * In case no user defined formatter is given, this will be used. Note that
- * the context here is an object holding point, series, x, y etc.
- *
- * @private
- * @function Highcharts.Tooltip#defaultFormatter
- *
- * @param {Highcharts.Tooltip} tooltip
- *
- * @return {Array<string>}
- */
- defaultFormatter: function (tooltip) {
- var items = this.points || splat(this),
- s;
- // Build the header
- s = [tooltip.tooltipFooterHeaderFormatter(items[0])];
- // build the values
- s = s.concat(tooltip.bodyFormatter(items));
- // footer
- s.push(tooltip.tooltipFooterHeaderFormatter(items[0], true));
- return s;
- },
- /**
- * Refresh the tooltip's text and position.
- *
- * @function Highcharts.Tooltip#refresh
- *
- * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
- * Either a point or an array of points.
- *
- * @param {global.Event} [mouseEvent]
- * Mouse event, that is responsible for the refresh and should be
- * used for the tooltip update.
- */
- refresh: function (pointOrPoints, mouseEvent) {
- var tooltip = this,
- label,
- options = tooltip.options,
- x,
- y,
- point = pointOrPoints,
- anchor,
- textConfig = {},
- text,
- pointConfig = [],
- formatter = options.formatter || tooltip.defaultFormatter,
- shared = tooltip.shared,
- currentSeries,
- styledMode = this.chart.styledMode;
- if (!options.enabled) {
- return;
- }
- H.clearTimeout(this.hideTimer);
- // get the reference point coordinates (pie charts use tooltipPos)
- tooltip.followPointer = splat(point)[0].series.tooltipOptions
- .followPointer;
- anchor = tooltip.getAnchor(point, mouseEvent);
- x = anchor[0];
- y = anchor[1];
- // shared tooltip, array is sent over
- if (shared && !(point.series && point.series.noSharedTooltip)) {
- point.forEach(function (item) {
- item.setState('hover');
- pointConfig.push(item.getLabelConfig());
- });
- textConfig = {
- x: point[0].category,
- y: point[0].y
- };
- textConfig.points = pointConfig;
- point = point[0];
- // single point tooltip
- } else {
- textConfig = point.getLabelConfig();
- }
- this.len = pointConfig.length; // #6128
- text = formatter.call(textConfig, tooltip);
- // register the current series
- currentSeries = point.series;
- this.distance = pick(currentSeries.tooltipOptions.distance, 16);
- // update the inner HTML
- if (text === false) {
- this.hide();
- } else {
- label = tooltip.getLabel();
- // show it
- if (tooltip.isHidden) {
- label.attr({
- opacity: 1
- }).show();
- }
- // update text
- if (tooltip.split) {
- this.renderSplit(text, splat(pointOrPoints));
- } else {
- // Prevent the tooltip from flowing over the chart box (#6659)
- if (!options.style.width || styledMode) {
- label.css({
- width: this.chart.spacingBox.width
- });
- }
- label.attr({
- text: text && text.join ? text.join('') : text
- });
- // Set the stroke color of the box to reflect the point
- label.removeClass(/highcharts-color-[\d]+/g)
- .addClass(
- 'highcharts-color-' +
- pick(point.colorIndex, currentSeries.colorIndex)
- );
- if (!styledMode) {
- label.attr({
- stroke: (
- options.borderColor ||
- point.color ||
- currentSeries.color ||
- '#666666'
- )
- });
- }
- tooltip.updatePosition({
- plotX: x,
- plotY: y,
- negative: point.negative,
- ttBelow: point.ttBelow,
- h: anchor[2] || 0
- });
- }
- this.isHidden = false;
- }
- },
- /**
- * Render the split tooltip. Loops over each point's text and adds
- * a label next to the point, then uses the distribute function to
- * find best non-overlapping positions.
- *
- * @private
- * @function Highcharts.Tooltip#renderSplit
- *
- * @param {Array<Highcharts.Label>} labels
- *
- * @param {Array<Highcharts.Point>} points
- */
- renderSplit: function (labels, points) {
- var tooltip = this,
- boxes = [],
- chart = this.chart,
- ren = chart.renderer,
- rightAligned = true,
- options = this.options,
- headerHeight = 0,
- headerTop,
- tooltipLabel = this.getLabel(),
- distributionBoxTop = chart.plotTop;
- // Graceful degradation for legacy formatters
- if (H.isString(labels)) {
- labels = [false, labels];
- }
- // Create the individual labels for header and points, ignore footer
- labels.slice(0, points.length + 1).forEach(function (str, i) {
- if (str !== false && str !== '') {
- var point = points[i - 1] ||
- {
- // Item 0 is the header. Instead of this, we could also
- // use the crosshair label
- isHeader: true,
- plotX: points[0].plotX,
- plotY: chart.plotHeight
- },
- owner = point.series || tooltip,
- tt = owner.tt,
- series = point.series || {},
- colorClass = 'highcharts-color-' + pick(
- point.colorIndex,
- series.colorIndex,
- 'none'
- ),
- target,
- x,
- bBox,
- boxWidth,
- attribs;
- // Store the tooltip referance on the series
- if (!tt) {
- attribs = {
- padding: options.padding,
- r: options.borderRadius
- };
- if (!chart.styledMode) {
- attribs.fill = options.backgroundColor;
- attribs.stroke = (
- options.borderColor ||
- point.color ||
- series.color ||
- '#333333'
- );
- attribs['stroke-width'] = options.borderWidth;
- }
- owner.tt = tt = ren
- .label(
- null,
- null,
- null,
- (
- point.isHeader ? options.headerShape :
- options.shape
- ) || 'callout',
- null,
- null,
- options.useHTML
- )
- .addClass('highcharts-tooltip-box ' + colorClass)
- .attr(attribs)
- .add(tooltipLabel);
- }
- tt.isActive = true;
- tt.attr({
- text: str
- });
- if (!chart.styledMode) {
- tt.css(options.style)
- .shadow(options.shadow);
- }
- // Get X position now, so we can move all to the other side in
- // case of overflow
- bBox = tt.getBBox();
- boxWidth = bBox.width + tt.strokeWidth();
- if (point.isHeader) {
- headerHeight = bBox.height;
- if (chart.xAxis[0].opposite) {
- headerTop = true;
- distributionBoxTop -= headerHeight;
- }
- x = Math.max(
- 0, // No left overflow
- Math.min(
- point.plotX + chart.plotLeft - boxWidth / 2,
- // No right overflow (#5794)
- chart.chartWidth +
- (
- // Scrollable plot area
- chart.scrollablePixels ?
- chart.scrollablePixels - chart.marginRight :
- 0
- ) -
- boxWidth
- )
- );
- } else {
- x = point.plotX + chart.plotLeft -
- pick(options.distance, 16) - boxWidth;
- }
- // If overflow left, we don't use this x in the next loop
- if (x < 0) {
- rightAligned = false;
- }
- // Prepare for distribution
- target = (point.series && point.series.yAxis &&
- point.series.yAxis.pos) + (point.plotY || 0);
- target -= distributionBoxTop;
- if (point.isHeader) {
- target = headerTop ?
- -headerHeight :
- chart.plotHeight + headerHeight;
- }
- boxes.push({
- target: target,
- rank: point.isHeader ? 1 : 0,
- size: owner.tt.getBBox().height + 1,
- point: point,
- x: x,
- tt: tt
- });
- }
- });
- // Clean previous run (for missing points)
- this.cleanSplit();
- if (options.positioner) {
- boxes.forEach(function (box) {
- var boxPosition = options.positioner.call(
- tooltip,
- box.tt.getBBox().width,
- box.size,
- box.point
- );
- box.x = boxPosition.x;
- box.align = 0; // 0-align to the top, 1-align to the bottom
- box.target = boxPosition.y;
- box.rank = pick(boxPosition.rank, box.rank);
- });
- }
- // Distribute and put in place
- H.distribute(boxes, chart.plotHeight + headerHeight);
- boxes.forEach(function (box) {
- var point = box.point,
- series = point.series;
- // Put the label in place
- box.tt.attr({
- visibility: box.pos === undefined ? 'hidden' : 'inherit',
- x: (rightAligned || point.isHeader || options.positioner ?
- box.x :
- point.plotX + chart.plotLeft + tooltip.distance),
- y: box.pos + distributionBoxTop,
- anchorX: point.isHeader ?
- point.plotX + chart.plotLeft :
- point.plotX + series.xAxis.pos,
- anchorY: point.isHeader ?
- chart.plotTop + chart.plotHeight / 2 :
- point.plotY + series.yAxis.pos
- });
- });
- },
- /**
- * Find the new position and perform the move
- *
- * @private
- * @function Highcharts.Tooltip#updatePosition
- *
- * @param {Highcharts.Point} point
- */
- updatePosition: function (point) {
- var chart = this.chart,
- label = this.getLabel(),
- pos = (this.options.positioner || this.getPosition).call(
- this,
- label.width,
- label.height,
- point
- ),
- anchorX = point.plotX + chart.plotLeft,
- anchorY = point.plotY + chart.plotTop,
- pad;
- // Set the renderer size dynamically to prevent document size to change
- if (this.outside) {
- pad = (this.options.borderWidth || 0) + 2 * this.distance;
- this.renderer.setSize(
- label.width + pad,
- label.height + pad,
- false
- );
- anchorX += chart.pointer.chartPosition.left - pos.x;
- anchorY += chart.pointer.chartPosition.top - pos.y;
- }
- // do the move
- this.move(
- Math.round(pos.x),
- Math.round(pos.y || 0), // can be undefined (#3977)
- anchorX,
- anchorY
- );
- },
- /**
- * Get the optimal date format for a point, based on a range.
- *
- * @private
- * @function Highcharts.Tooltip#getDateFormat
- *
- * @param {number} range
- * The time range
- *
- * @param {number|Date} date
- * The date of the point in question
- *
- * @param {number} startOfWeek
- * An integer representing the first day of the week, where 0 is
- * Sunday.
- *
- * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
- * A map of time units to formats.
- *
- * @return {string}
- * The optimal date format for a point.
- */
- getDateFormat: function (range, date, startOfWeek, dateTimeLabelFormats) {
- var time = this.chart.time,
- dateStr = time.dateFormat('%m-%d %H:%M:%S.%L', date),
- format,
- n,
- blank = '01-01 00:00:00.000',
- strpos = {
- millisecond: 15,
- second: 12,
- minute: 9,
- hour: 6,
- day: 3
- },
- lastN = 'millisecond'; // for sub-millisecond data, #4223
- for (n in timeUnits) {
- // If the range is exactly one week and we're looking at a
- // Sunday/Monday, go for the week format
- if (
- range === timeUnits.week &&
- +time.dateFormat('%w', date) === startOfWeek &&
- dateStr.substr(6) === blank.substr(6)
- ) {
- n = 'week';
- break;
- }
- // The first format that is too great for the range
- if (timeUnits[n] > range) {
- n = lastN;
- break;
- }
- // If the point is placed every day at 23:59, we need to show
- // the minutes as well. #2637.
- if (
- strpos[n] &&
- dateStr.substr(strpos[n]) !== blank.substr(strpos[n])
- ) {
- break;
- }
- // Weeks are outside the hierarchy, only apply them on
- // Mondays/Sundays like in the first condition
- if (n !== 'week') {
- lastN = n;
- }
- }
- if (n) {
- format = time.resolveDTLFormat(dateTimeLabelFormats[n]).main;
- }
- return format;
- },
- /**
- * Get the best X date format based on the closest point range on the axis.
- *
- * @private
- * @function Highcharts.Tooltip#getXDateFormat
- *
- * @param {Highcharts.Point} point
- *
- * @param {Highcharts.TooltipOptions} options
- *
- * @param {Highcharts.Axis} xAxis
- *
- * @return {string}
- */
- getXDateFormat: function (point, options, xAxis) {
- var xDateFormat,
- dateTimeLabelFormats = options.dateTimeLabelFormats,
- closestPointRange = xAxis && xAxis.closestPointRange;
- if (closestPointRange) {
- xDateFormat = this.getDateFormat(
- closestPointRange,
- point.x,
- xAxis.options.startOfWeek,
- dateTimeLabelFormats
- );
- } else {
- xDateFormat = dateTimeLabelFormats.day;
- }
- return xDateFormat || dateTimeLabelFormats.year; // #2546, 2581
- },
- /**
- * Format the footer/header of the tooltip
- * #3397: abstraction to enable formatting of footer and header
- *
- * @private
- * @function Highcharts.Tooltip#tooltipFooterHeaderFormatter
- *
- * @param {*} labelConfig
- *
- * @param {boolean} isFooter
- *
- * @return {string}
- */
- tooltipFooterHeaderFormatter: function (labelConfig, isFooter) {
- var footOrHead = isFooter ? 'footer' : 'header',
- series = labelConfig.series,
- tooltipOptions = series.tooltipOptions,
- xDateFormat = tooltipOptions.xDateFormat,
- xAxis = series.xAxis,
- isDateTime = (
- xAxis &&
- xAxis.options.type === 'datetime' &&
- isNumber(labelConfig.key)
- ),
- formatString = tooltipOptions[footOrHead + 'Format'],
- evt = { isFooter: isFooter, labelConfig: labelConfig };
- H.fireEvent(this, 'headerFormatter', evt, function (e) {
- // Guess the best date format based on the closest point distance
- // (#568, #3418)
- if (isDateTime && !xDateFormat) {
- xDateFormat = this.getXDateFormat(
- labelConfig,
- tooltipOptions,
- xAxis
- );
- }
- // Insert the footer date format if any
- if (isDateTime && xDateFormat) {
- ((labelConfig.point && labelConfig.point.tooltipDateKeys) ||
- ['key']).forEach(
- function (key) {
- formatString = formatString.replace(
- '{point.' + key + '}',
- '{point.' + key + ':' + xDateFormat + '}'
- );
- }
- );
- }
- // Replace default header style with class name
- if (series.chart.styledMode) {
- formatString = this.styledModeFormat(formatString);
- }
- e.text = format(formatString, {
- point: labelConfig,
- series: series
- }, this.chart.time);
- });
- return evt.text;
- },
- /**
- * Build the body (lines) of the tooltip by iterating over the items and
- * returning one entry for each item, abstracting this functionality allows
- * to easily overwrite and extend it.
- *
- * @private
- * @function Highcharts.Tooltip#bodyFormatter
- *
- * @param {Array<Highcharts.Point>} items
- *
- * @return {string}
- */
- bodyFormatter: function (items) {
- return items.map(function (item) {
- var tooltipOptions = item.series.tooltipOptions;
- return (
- tooltipOptions[
- (item.point.formatPrefix || 'point') + 'Formatter'
- ] ||
- item.point.tooltipFormatter
- ).call(
- item.point,
- tooltipOptions[
- (item.point.formatPrefix || 'point') + 'Format'
- ] || ''
- );
- });
- },
- styledModeFormat: function (formatString) {
- return formatString
- .replace(
- 'style="font-size: 10px"',
- 'class="highcharts-header"'
- )
- .replace(
- /style="color:{(point|series)\.color}"/g,
- 'class="highcharts-color-{$1.colorIndex}"'
- );
- }
- };
- }(Highcharts));
- (function (Highcharts) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * One position in relation to an axis.
- *
- * @interface Highcharts.PointerAxisCoordinateObject
- *//**
- * Related axis.
- *
- * @name Highcharts.PointerAxisCoordinateObject#axis
- * @type {Highcharts.Axis}
- *//**
- * Axis value.
- *
- * @name Highcharts.PointerAxisCoordinateObject#value
- * @type {number}
- */
- /**
- * Positions in terms of axis values.
- *
- * @interface Highcharts.PointerAxisCoordinatesObject
- *//**
- * Positions on the x-axis.
- * @name Highcharts.PointerAxisCoordinatesObject#xAxis
- * @type {Array<Highcharts.PointerAxisCoordinateObject>}
- *//**
- * Positions on the y-axis.
- * @name Highcharts.PointerAxisCoordinatesObject#yAxis
- * @type {Array<Highcharts.PointerAxisCoordinateObject>}
- */
- /**
- * Pointer coordinates.
- *
- * @interface Highcharts.PointerCoordinatesObject
- *//**
- * @name Highcharts.PointerCoordinatesObject#chartX
- * @type {number}
- *//**
- * @name Highcharts.PointerCoordinatesObject#chartY
- * @type {number}
- */
- /**
- * A native browser mouse or touch event, extended with position information
- * relative to the {@link Chart.container}.
- *
- * @interface Highcharts.PointerEventObject
- * @extends global.PointerEvent
- *//**
- * The X coordinate of the pointer interaction relative to the chart.
- *
- * @name Highcharts.PointerEventObject#chartX
- * @type {number}
- *//**
- * The Y coordinate of the pointer interaction relative to the chart.
- *
- * @name Highcharts.PointerEventObject#chartY
- * @type {number}
- */
- /**
- * Axis-specific data of a selection.
- *
- * @interface Highcharts.SelectDataObject
- *//**
- * @name Highcharts.SelectDataObject#axis
- * @type {Highcharts.Axis}
- *//**
- * @name Highcharts.SelectDataObject#min
- * @type {number}
- *//**
- * @name Highcharts.SelectDataObject#max
- * @type {number}
- */
- /**
- * Object for select events.
- *
- * @interface Highcharts.SelectEventObject
- *//**
- * @name Highcharts.SelectEventObject#originalEvent
- * @type {global.Event}
- *//**
- * @name Highcharts.SelectEventObject#xAxis
- * @type {Array<Highcharts.SelectDataObject>}
- *//**
- * @name Highcharts.SelectEventObject#yAxis
- * @type {Array<Highcharts.SelectDataObject>}
- */
- var H = Highcharts,
- addEvent = H.addEvent,
- attr = H.attr,
- charts = H.charts,
- color = H.color,
- css = H.css,
- defined = H.defined,
- extend = H.extend,
- find = H.find,
- fireEvent = H.fireEvent,
- isNumber = H.isNumber,
- isObject = H.isObject,
- offset = H.offset,
- pick = H.pick,
- splat = H.splat,
- Tooltip = H.Tooltip;
- /**
- * The mouse and touch tracker object. Each {@link Chart} item has one
- * assosiated Pointer item that can be accessed from the {@link Chart.pointer}
- * property.
- *
- * @class
- * @name Highcharts.Pointer
- *
- * @param {Highcharts.Chart} chart
- * The Chart instance.
- *
- * @param {Highcharts.Options} options
- * The root options object. The pointer uses options from the chart and
- * tooltip structures.
- */
- Highcharts.Pointer = function (chart, options) {
- this.init(chart, options);
- };
- Highcharts.Pointer.prototype = {
- /**
- * Initialize the Pointer.
- *
- * @private
- * @function Highcharts.Pointer#init
- *
- * @param {Highcharts.Chart} chart
- * The Chart instance.
- *
- * @param {Highcharts.Options} options
- * The root options object. The pointer uses options from the chart
- * and tooltip structures.
- */
- init: function (chart, options) {
- // Store references
- this.options = options;
- this.chart = chart;
- // Do we need to handle click on a touch device?
- this.runChartClick =
- options.chart.events && !!options.chart.events.click;
- this.pinchDown = [];
- this.lastValidTouch = {};
- if (Tooltip) {
- /**
- * Tooltip object for points of series.
- *
- * @name Highcharts.Chart#tooltip
- * @type {Highcharts.Tooltip}
- */
- chart.tooltip = new Tooltip(chart, options.tooltip);
- this.followTouchMove = pick(options.tooltip.followTouchMove, true);
- }
- this.setDOMEvents();
- },
- /**
- * Resolve the zoomType option, this is reset on all touch start and mouse
- * down events.
- *
- * @private
- * @function Highcharts.Pointer#zoomOption
- *
- * @param {global.Event} e
- * Event object.
- */
- zoomOption: function (e) {
- var chart = this.chart,
- options = chart.options.chart,
- zoomType = options.zoomType || '',
- inverted = chart.inverted,
- zoomX,
- zoomY;
- // Look for the pinchType option
- if (/touch/.test(e.type)) {
- zoomType = pick(options.pinchType, zoomType);
- }
- this.zoomX = zoomX = /x/.test(zoomType);
- this.zoomY = zoomY = /y/.test(zoomType);
- this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
- this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
- this.hasZoom = zoomX || zoomY;
- },
- /**
- * Takes a browser event object and extends it with custom Highcharts
- * properties `chartX` and `chartY` in order to work on the internal
- * coordinate system.
- *
- * @function Highcharts.Pointer#normalize
- *
- * @param {global.Event} e
- * Event object in standard browsers.
- *
- * @return {Highcharts.PointerEventObject}
- * A browser event with extended properties `chartX` and `chartY`.
- */
- normalize: function (e, chartPosition) {
- var ePos;
- // iOS (#2757)
- ePos = e.touches ?
- (e.touches.length ? e.touches.item(0) : e.changedTouches[0]) :
- e;
- // Get mouse position
- if (!chartPosition) {
- this.chartPosition = chartPosition = offset(this.chart.container);
- }
- return extend(e, {
- chartX: Math.round(ePos.pageX - chartPosition.left),
- chartY: Math.round(ePos.pageY - chartPosition.top)
- });
- },
- /**
- * Get the click position in terms of axis values.
- *
- * @function Highcharts.Pointer#getCoordinates
- *
- * @param {Highcharts.PointerEventObject} e
- * Pointer event, extended with `chartX` and `chartY` properties.
- *
- * @return {Highcharts.PointerAxisCoordinatesObject}
- */
- getCoordinates: function (e) {
- var coordinates = {
- xAxis: [],
- yAxis: []
- };
- this.chart.axes.forEach(function (axis) {
- coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
- axis: axis,
- value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
- });
- });
- return coordinates;
- },
- /**
- * Finds the closest point to a set of coordinates, using the k-d-tree
- * algorithm.
- *
- * @function Highcharts.Pointer#findNearestKDPoints
- *
- * @param {Array<Highcharts.Series>} series
- * All the series to search in.
- *
- * @param {boolean} shared
- * Whether it is a shared tooltip or not.
- *
- * @param {Highcharts.PointerEventObject} e
- * The pointer event object, containing chart coordinates of the
- * pointer.
- *
- * @return {Point|undefined}
- * The point closest to given coordinates.
- */
- findNearestKDPoint: function (series, shared, e) {
- var closest,
- sort = function (p1, p2) {
- var isCloserX = p1.distX - p2.distX,
- isCloser = p1.dist - p2.dist,
- isAbove =
- (p2.series.group && p2.series.group.zIndex) -
- (p1.series.group && p1.series.group.zIndex),
- result;
- // We have two points which are not in the same place on xAxis
- // and shared tooltip:
- if (isCloserX !== 0 && shared) { // #5721
- result = isCloserX;
- // Points are not exactly in the same place on x/yAxis:
- } else if (isCloser !== 0) {
- result = isCloser;
- // The same xAxis and yAxis position, sort by z-index:
- } else if (isAbove !== 0) {
- result = isAbove;
- // The same zIndex, sort by array index:
- } else {
- result = p1.series.index > p2.series.index ? -1 : 1;
- }
- return result;
- };
- series.forEach(function (s) {
- var noSharedTooltip = s.noSharedTooltip && shared,
- compareX = (
- !noSharedTooltip &&
- s.options.findNearestPointBy.indexOf('y') < 0
- ),
- point = s.searchPoint(
- e,
- compareX
- );
- if (
- // Check that we actually found a point on the series.
- isObject(point, true) &&
- // Use the new point if it is closer.
- (!isObject(closest, true) || (sort(closest, point) > 0))
- ) {
- closest = point;
- }
- });
- return closest;
- },
- /**
- * @private
- * @function Highcharts.Pointer#getPointFromEvent
- *
- * @param {global.Event} e
- *
- * @return {Highcharts.Point|undefined}
- */
- getPointFromEvent: function (e) {
- var target = e.target,
- point;
- while (target && !point) {
- point = target.point;
- target = target.parentNode;
- }
- return point;
- },
- /**
- * @private
- * @function Highcharts.Pointer#getChartCoordinatesFromPoint
- *
- * @param {Highcharts.Point} point
- *
- * @param {boolean} inverted
- *
- * @return {Highcharts.PointerCoordinatesObject}
- */
- getChartCoordinatesFromPoint: function (point, inverted) {
- var series = point.series,
- xAxis = series.xAxis,
- yAxis = series.yAxis,
- plotX = pick(point.clientX, point.plotX),
- shapeArgs = point.shapeArgs;
- if (xAxis && yAxis) {
- return inverted ? {
- chartX: xAxis.len + xAxis.pos - plotX,
- chartY: yAxis.len + yAxis.pos - point.plotY
- } : {
- chartX: plotX + xAxis.pos,
- chartY: point.plotY + yAxis.pos
- };
- }
- if (shapeArgs && shapeArgs.x && shapeArgs.y) {
- // E.g. pies do not have axes
- return {
- chartX: shapeArgs.x,
- chartY: shapeArgs.y
- };
- }
- },
- /**
- * Calculates what is the current hovered point/points and series.
- *
- * @private
- * @function Highcharts.Pointer#getHoverData
- *
- * @param {Highcharts.Point|undefined} existingHoverPoint
- * The point currrently beeing hovered.
- *
- * @param {Highcharts.Series|undefined} existingHoverSeries
- * The series currently beeing hovered.
- *
- * @param {Array<Highcharts.Series>} series
- * All the series in the chart.
- *
- * @param {boolean} isDirectTouch
- * Is the pointer directly hovering the point.
- *
- * @param {boolean} shared
- * Whether it is a shared tooltip or not.
- *
- * @param {Highcharts.PointerEventObject} e
- * The triggering event, containing chart coordinates of the pointer.
- *
- * @return {*}
- * Object containing resulting hover data: hoverPoint, hoverSeries,
- * and hoverPoints.
- */
- getHoverData: function (
- existingHoverPoint,
- existingHoverSeries,
- series,
- isDirectTouch,
- shared,
- e
- ) {
- var hoverPoint,
- hoverPoints = [],
- hoverSeries = existingHoverSeries,
- useExisting = !!(isDirectTouch && existingHoverPoint),
- notSticky = hoverSeries && !hoverSeries.stickyTracking,
- filter = function (s) {
- return (
- s.visible &&
- !(!shared && s.directTouch) && // #3821
- pick(s.options.enableMouseTracking, true)
- );
- },
- // Which series to look in for the hover point
- searchSeries = notSticky ?
- // Only search on hovered series if it has stickyTracking false
- [hoverSeries] :
- // Filter what series to look in.
- series.filter(function (s) {
- return filter(s) && s.stickyTracking;
- });
- // Use existing hovered point or find the one closest to coordinates.
- hoverPoint = useExisting ?
- existingHoverPoint :
- this.findNearestKDPoint(searchSeries, shared, e);
- // Assign hover series
- hoverSeries = hoverPoint && hoverPoint.series;
- // If we have a hoverPoint, assign hoverPoints.
- if (hoverPoint) {
- // When tooltip is shared, it displays more than one point
- if (shared && !hoverSeries.noSharedTooltip) {
- searchSeries = series.filter(function (s) {
- return filter(s) && !s.noSharedTooltip;
- });
- // Get all points with the same x value as the hoverPoint
- searchSeries.forEach(function (s) {
- var point = find(s.points, function (p) {
- return p.x === hoverPoint.x && !p.isNull;
- });
- if (isObject(point)) {
- /*
- * Boost returns a minimal point. Convert it to a usable
- * point for tooltip and states.
- */
- if (s.chart.isBoosting) {
- point = s.getPoint(point);
- }
- hoverPoints.push(point);
- }
- });
- } else {
- hoverPoints.push(hoverPoint);
- }
- }
- return {
- hoverPoint: hoverPoint,
- hoverSeries: hoverSeries,
- hoverPoints: hoverPoints
- };
- },
- /**
- * With line type charts with a single tracker, get the point closest to the
- * mouse. Run Point.onMouseOver and display tooltip for the point or points.
- *
- * @private
- * @function Highcharts.Pointer#runPointActions
- *
- * @param {global.Event} e
- *
- * @param {Highcharts.Point} p
- *
- * @fires Highcharts.Point#event:mouseOut
- * @fires Highcharts.Point#event:mouseOver
- */
- runPointActions: function (e, p) {
- var pointer = this,
- chart = pointer.chart,
- series = chart.series,
- tooltip = chart.tooltip && chart.tooltip.options.enabled ?
- chart.tooltip :
- undefined,
- shared = tooltip ? tooltip.shared : false,
- hoverPoint = p || chart.hoverPoint,
- hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries,
- // onMouseOver or already hovering a series with directTouch
- isDirectTouch = e.type !== 'touchmove' && (
- !!p || (
- (hoverSeries && hoverSeries.directTouch) &&
- pointer.isDirectTouch
- )
- ),
- hoverData = this.getHoverData(
- hoverPoint,
- hoverSeries,
- series,
- isDirectTouch,
- shared,
- e
- ),
- useSharedTooltip,
- followPointer,
- anchor,
- points;
- // Update variables from hoverData.
- hoverPoint = hoverData.hoverPoint;
- points = hoverData.hoverPoints;
- hoverSeries = hoverData.hoverSeries;
- followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
- useSharedTooltip = (
- shared &&
- hoverSeries &&
- !hoverSeries.noSharedTooltip
- );
- // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
- // #3926, #4200
- if (
- hoverPoint &&
- // !(hoverSeries && hoverSeries.directTouch) &&
- (hoverPoint !== chart.hoverPoint || (tooltip && tooltip.isHidden))
- ) {
- (chart.hoverPoints || []).forEach(function (p) {
- if (points.indexOf(p) === -1) {
- p.setState();
- }
- });
- // Do mouseover on all points (#3919, #3985, #4410, #5622)
- (points || []).forEach(function (p) {
- p.setState('hover');
- });
- // set normal state to previous series
- if (chart.hoverSeries !== hoverSeries) {
- hoverSeries.onMouseOver();
- }
- // If tracking is on series in stead of on each point,
- // fire mouseOver on hover point. // #4448
- if (chart.hoverPoint) {
- chart.hoverPoint.firePointEvent('mouseOut');
- }
- // Hover point may have been destroyed in the event handlers (#7127)
- if (!hoverPoint.series) {
- return;
- }
- hoverPoint.firePointEvent('mouseOver');
- chart.hoverPoints = points;
- chart.hoverPoint = hoverPoint;
- // Draw tooltip if necessary
- if (tooltip) {
- tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
- }
- // Update positions (regardless of kdpoint or hoverPoint)
- } else if (followPointer && tooltip && !tooltip.isHidden) {
- anchor = tooltip.getAnchor([{}], e);
- tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
- }
- // Start the event listener to pick up the tooltip and crosshairs
- if (!pointer.unDocMouseMove) {
- pointer.unDocMouseMove = addEvent(
- chart.container.ownerDocument,
- 'mousemove',
- function (e) {
- var chart = charts[H.hoverChartIndex];
- if (chart) {
- chart.pointer.onDocumentMouseMove(e);
- }
- }
- );
- }
- // Issues related to crosshair #4927, #5269 #5066, #5658
- chart.axes.forEach(function drawAxisCrosshair(axis) {
- var snap = pick(axis.crosshair.snap, true),
- point = !snap ?
- undefined :
- H.find(points, function (p) {
- return p.series[axis.coll] === axis;
- });
- // Axis has snapping crosshairs, and one of the hover points belongs
- // to axis. Always call drawCrosshair when it is not snap.
- if (point || !snap) {
- axis.drawCrosshair(e, point);
- // Axis has snapping crosshairs, but no hover point belongs to axis
- } else {
- axis.hideCrosshair();
- }
- });
- },
- /**
- * Reset the tracking by hiding the tooltip, the hover series state and the
- * hover point
- *
- * @function Highcharts.Pointer#reset
- *
- * @param {boolean} allowMove
- * Instead of destroying the tooltip altogether, allow moving it if
- * possible.
- *
- * @param {number} delay
- */
- reset: function (allowMove, delay) {
- var pointer = this,
- chart = pointer.chart,
- hoverSeries = chart.hoverSeries,
- hoverPoint = chart.hoverPoint,
- hoverPoints = chart.hoverPoints,
- tooltip = chart.tooltip,
- tooltipPoints = tooltip && tooltip.shared ?
- hoverPoints :
- hoverPoint;
- // Check if the points have moved outside the plot area (#1003, #4736,
- // #5101)
- if (allowMove && tooltipPoints) {
- splat(tooltipPoints).forEach(function (point) {
- if (point.series.isCartesian && point.plotX === undefined) {
- allowMove = false;
- }
- });
- }
- // Just move the tooltip, #349
- if (allowMove) {
- if (tooltip && tooltipPoints && tooltipPoints.length) {
- tooltip.refresh(tooltipPoints);
- if (tooltip.shared && hoverPoints) { // #8284
- hoverPoints.forEach(function (point) {
- point.setState(point.state, true);
- if (point.series.isCartesian) {
- if (point.series.xAxis.crosshair) {
- point.series.xAxis.drawCrosshair(null, point);
- }
- if (point.series.yAxis.crosshair) {
- point.series.yAxis.drawCrosshair(null, point);
- }
- }
- });
- } else if (hoverPoint) { // #2500
- hoverPoint.setState(hoverPoint.state, true);
- chart.axes.forEach(function (axis) {
- if (axis.crosshair) {
- axis.drawCrosshair(null, hoverPoint);
- }
- });
- }
- }
- // Full reset
- } else {
- if (hoverPoint) {
- hoverPoint.onMouseOut();
- }
- if (hoverPoints) {
- hoverPoints.forEach(function (point) {
- point.setState();
- });
- }
- if (hoverSeries) {
- hoverSeries.onMouseOut();
- }
- if (tooltip) {
- tooltip.hide(delay);
- }
- if (pointer.unDocMouseMove) {
- pointer.unDocMouseMove = pointer.unDocMouseMove();
- }
- // Remove crosshairs
- chart.axes.forEach(function (axis) {
- axis.hideCrosshair();
- });
- pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
- }
- },
- /**
- * Scale series groups to a certain scale and translation.
- *
- * @private
- * @function Highcharts.Pointer#scaleGroups
- *
- * @param {Highcharts.SeriesPlotBoxObject} attribs
- *
- * @param {boolean} clip
- */
- scaleGroups: function (attribs, clip) {
- var chart = this.chart,
- seriesAttribs;
- // Scale each series
- chart.series.forEach(function (series) {
- seriesAttribs = attribs || series.getPlotBox(); // #1701
- if (series.xAxis && series.xAxis.zoomEnabled && series.group) {
- series.group.attr(seriesAttribs);
- if (series.markerGroup) {
- series.markerGroup.attr(seriesAttribs);
- series.markerGroup.clip(clip ? chart.clipRect : null);
- }
- if (series.dataLabelsGroup) {
- series.dataLabelsGroup.attr(seriesAttribs);
- }
- }
- });
- // Clip
- chart.clipRect.attr(clip || chart.clipBox);
- },
- /**
- * Start a drag operation.
- *
- * @private
- * @function Highcharts.Pointer#dragStart
- *
- * @param {Highcharts.PointerEventObject} e
- */
- dragStart: function (e) {
- var chart = this.chart;
- // Record the start position
- chart.mouseIsDown = e.type;
- chart.cancelClick = false;
- chart.mouseDownX = this.mouseDownX = e.chartX;
- chart.mouseDownY = this.mouseDownY = e.chartY;
- },
- /**
- * Perform a drag operation in response to a mousemove event while the mouse
- * is down.
- *
- * @private
- * @function Highcharts.Pointer#drag
- *
- * @param {Highcharts.PointerEventObject} e
- */
- drag: function (e) {
- var chart = this.chart,
- chartOptions = chart.options.chart,
- chartX = e.chartX,
- chartY = e.chartY,
- zoomHor = this.zoomHor,
- zoomVert = this.zoomVert,
- plotLeft = chart.plotLeft,
- plotTop = chart.plotTop,
- plotWidth = chart.plotWidth,
- plotHeight = chart.plotHeight,
- clickedInside,
- size,
- selectionMarker = this.selectionMarker,
- mouseDownX = this.mouseDownX,
- mouseDownY = this.mouseDownY,
- panKey = chartOptions.panKey && e[chartOptions.panKey + 'Key'];
- // If the device supports both touch and mouse (like IE11), and we are
- // touch-dragging inside the plot area, don't handle the mouse event.
- // #4339.
- if (selectionMarker && selectionMarker.touch) {
- return;
- }
- // If the mouse is outside the plot area, adjust to cooordinates
- // inside to prevent the selection marker from going outside
- if (chartX < plotLeft) {
- chartX = plotLeft;
- } else if (chartX > plotLeft + plotWidth) {
- chartX = plotLeft + plotWidth;
- }
- if (chartY < plotTop) {
- chartY = plotTop;
- } else if (chartY > plotTop + plotHeight) {
- chartY = plotTop + plotHeight;
- }
- // determine if the mouse has moved more than 10px
- this.hasDragged = Math.sqrt(
- Math.pow(mouseDownX - chartX, 2) +
- Math.pow(mouseDownY - chartY, 2)
- );
- if (this.hasDragged > 10) {
- clickedInside = chart.isInsidePlot(
- mouseDownX - plotLeft,
- mouseDownY - plotTop
- );
- // make a selection
- if (
- chart.hasCartesianSeries &&
- (this.zoomX || this.zoomY) &&
- clickedInside &&
- !panKey
- ) {
- if (!selectionMarker) {
- this.selectionMarker = selectionMarker =
- chart.renderer.rect(
- plotLeft,
- plotTop,
- zoomHor ? 1 : plotWidth,
- zoomVert ? 1 : plotHeight,
- 0
- )
- .attr({
- 'class': 'highcharts-selection-marker',
- 'zIndex': 7
- })
- .add();
- if (!chart.styledMode) {
- selectionMarker.attr({
- fill: (
- chartOptions.selectionMarkerFill ||
- color('#335cad')
- .setOpacity(0.25).get()
- )
- });
- }
- }
- }
- // adjust the width of the selection marker
- if (selectionMarker && zoomHor) {
- size = chartX - mouseDownX;
- selectionMarker.attr({
- width: Math.abs(size),
- x: (size > 0 ? 0 : size) + mouseDownX
- });
- }
- // adjust the height of the selection marker
- if (selectionMarker && zoomVert) {
- size = chartY - mouseDownY;
- selectionMarker.attr({
- height: Math.abs(size),
- y: (size > 0 ? 0 : size) + mouseDownY
- });
- }
- // panning
- if (clickedInside && !selectionMarker && chartOptions.panning) {
- chart.pan(e, chartOptions.panning);
- }
- }
- },
- /**
- * On mouse up or touch end across the entire document, drop the selection.
- *
- * @private
- * @function Highcharts.Pointer#drop
- *
- * @param {global.Event} e
- */
- drop: function (e) {
- var pointer = this,
- chart = this.chart,
- hasPinched = this.hasPinched;
- if (this.selectionMarker) {
- var selectionData = {
- originalEvent: e, // #4890
- xAxis: [],
- yAxis: []
- },
- selectionBox = this.selectionMarker,
- selectionLeft = selectionBox.attr ?
- selectionBox.attr('x') :
- selectionBox.x,
- selectionTop = selectionBox.attr ?
- selectionBox.attr('y') :
- selectionBox.y,
- selectionWidth = selectionBox.attr ?
- selectionBox.attr('width') :
- selectionBox.width,
- selectionHeight = selectionBox.attr ?
- selectionBox.attr('height') :
- selectionBox.height,
- runZoom;
- // a selection has been made
- if (this.hasDragged || hasPinched) {
- // record each axis' min and max
- chart.axes.forEach(function (axis) {
- if (
- axis.zoomEnabled &&
- defined(axis.min) &&
- (
- hasPinched ||
- pointer[{
- xAxis: 'zoomX',
- yAxis: 'zoomY'
- }[axis.coll]]
- )
- ) { // #859, #3569
- var horiz = axis.horiz,
- minPixelPadding = e.type === 'touchend' ?
- axis.minPixelPadding :
- 0, // #1207, #3075
- selectionMin = axis.toValue(
- (horiz ? selectionLeft : selectionTop) +
- minPixelPadding
- ),
- selectionMax = axis.toValue(
- (
- horiz ?
- selectionLeft + selectionWidth :
- selectionTop + selectionHeight
- ) - minPixelPadding
- );
- selectionData[axis.coll].push({
- axis: axis,
- // Min/max for reversed axes
- min: Math.min(selectionMin, selectionMax),
- max: Math.max(selectionMin, selectionMax)
- });
- runZoom = true;
- }
- });
- if (runZoom) {
- fireEvent(
- chart,
- 'selection',
- selectionData,
- function (args) {
- chart.zoom(
- extend(
- args,
- hasPinched ? { animation: false } : null
- )
- );
- }
- );
- }
- }
- if (isNumber(chart.index)) {
- this.selectionMarker = this.selectionMarker.destroy();
- }
- // Reset scaling preview
- if (hasPinched) {
- this.scaleGroups();
- }
- }
- // Reset all. Check isNumber because it may be destroyed on mouse up
- // (#877)
- if (chart && isNumber(chart.index)) {
- css(chart.container, { cursor: chart._cursor });
- chart.cancelClick = this.hasDragged > 10; // #370
- chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
- this.pinchDown = [];
- }
- },
- /**
- * @private
- * @function Highcharts.Pointer#onContainerMouseDown
- *
- * @param {global.Event} e
- */
- onContainerMouseDown: function (e) {
- // Normalize before the 'if' for the legacy IE (#7850)
- e = this.normalize(e);
- if (e.button !== 2) {
- this.zoomOption(e);
- // issue #295, dragging not always working in Firefox
- if (e.preventDefault) {
- e.preventDefault();
- }
- this.dragStart(e);
- }
- },
- /**
- * @private
- * @function Highcharts.Pointer#onDocumentMouseUp
- *
- * @param {global.Event} e
- */
- onDocumentMouseUp: function (e) {
- if (charts[H.hoverChartIndex]) {
- charts[H.hoverChartIndex].pointer.drop(e);
- }
- },
- /**
- * Special handler for mouse move that will hide the tooltip when the mouse
- * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
- * always fire.
- *
- * @private
- * @function Highcharts.Pointer#onDocumentMouseMove
- *
- * @param {Highcharts.PointerEventObject} e
- */
- onDocumentMouseMove: function (e) {
- var chart = this.chart,
- chartPosition = this.chartPosition;
- e = this.normalize(e, chartPosition);
- // If we're outside, hide the tooltip
- if (
- chartPosition &&
- !this.inClass(e.target, 'highcharts-tracker') &&
- !chart.isInsidePlot(
- e.chartX - chart.plotLeft,
- e.chartY - chart.plotTop
- )
- ) {
- this.reset();
- }
- },
- /**
- * When mouse leaves the container, hide the tooltip.
- *
- * @private
- * @function Highcharts.Pointer#onContainerMouseLeave
- *
- * @param {global.Event} e
- */
- onContainerMouseLeave: function (e) {
- var chart = charts[H.hoverChartIndex];
- // #4886, MS Touch end fires mouseleave but with no related target
- if (chart && (e.relatedTarget || e.toElement)) {
- chart.pointer.reset();
- // Also reset the chart position, used in #149 fix
- chart.pointer.chartPosition = null;
- }
- },
- /**
- * The mousemove, touchmove and touchstart event handler
- *
- * @private
- * @function Highcharts.Pointer#onContainerMouseMove
- *
- * @param {Highcharts.PointerEventObject} e
- */
- onContainerMouseMove: function (e) {
- var chart = this.chart;
- if (
- !defined(H.hoverChartIndex) ||
- !charts[H.hoverChartIndex] ||
- !charts[H.hoverChartIndex].mouseIsDown
- ) {
- H.hoverChartIndex = chart.index;
- }
- e = this.normalize(e);
- e.returnValue = false; // #2251, #3224
- if (chart.mouseIsDown === 'mousedown') {
- this.drag(e);
- }
- // Show the tooltip and run mouse over events (#977)
- if (
- (
- this.inClass(e.target, 'highcharts-tracker') ||
- chart.isInsidePlot(
- e.chartX - chart.plotLeft,
- e.chartY - chart.plotTop
- )
- ) &&
- !chart.openMenu
- ) {
- this.runPointActions(e);
- }
- },
- /**
- * Utility to detect whether an element has, or has a parent with, a
- * specificclass name. Used on detection of tracker objects and on deciding
- * whether hovering the tooltip should cause the active series to mouse out.
- *
- * @function Highcharts.Pointer#inClass
- *
- * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
- * The element to investigate.
- *
- * @param {string} className
- * The class name to look for.
- *
- * @return {boolean}
- * True if either the element or one of its parents has the given
- * class name.
- */
- inClass: function (element, className) {
- var elemClassName;
- while (element) {
- elemClassName = attr(element, 'class');
- if (elemClassName) {
- if (elemClassName.indexOf(className) !== -1) {
- return true;
- }
- if (elemClassName.indexOf('highcharts-container') !== -1) {
- return false;
- }
- }
- element = element.parentNode;
- }
- },
- /**
- * @private
- * @function Highcharts.Pointer#onTrackerMouseOut
- *
- * @param {global.Event} e
- */
- onTrackerMouseOut: function (e) {
- var series = this.chart.hoverSeries,
- relatedTarget = e.relatedTarget || e.toElement;
- this.isDirectTouch = false;
- if (
- series &&
- relatedTarget &&
- !series.stickyTracking &&
- !this.inClass(relatedTarget, 'highcharts-tooltip') &&
- (
- !this.inClass(
- relatedTarget,
- 'highcharts-series-' + series.index
- ) || // #2499, #4465
- !this.inClass(relatedTarget, 'highcharts-tracker') // #5553
- )
- ) {
- series.onMouseOut();
- }
- },
- /**
- * @private
- * @function Highcharts.Pointer#onContainerClick
- *
- * @param {Highcharts.PointerEventObject} e
- */
- onContainerClick: function (e) {
- var chart = this.chart,
- hoverPoint = chart.hoverPoint,
- plotLeft = chart.plotLeft,
- plotTop = chart.plotTop;
- e = this.normalize(e);
- if (!chart.cancelClick) {
- // On tracker click, fire the series and point events. #783, #1583
- if (hoverPoint && this.inClass(e.target, 'highcharts-tracker')) {
- // the series click event
- fireEvent(hoverPoint.series, 'click', extend(e, {
- point: hoverPoint
- }));
- // the point click event
- if (chart.hoverPoint) { // it may be destroyed (#1844)
- hoverPoint.firePointEvent('click', e);
- }
- // When clicking outside a tracker, fire a chart event
- } else {
- extend(e, this.getCoordinates(e));
- // fire a click event in the chart
- if (
- chart.isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)
- ) {
- fireEvent(chart, 'click', e);
- }
- }
- }
- },
- /**
- * Set the JS DOM events on the container and document. This method should
- * contain a one-to-one assignment between methods and their handlers. Any
- * advanced logic should be moved to the handler reflecting the event's
- * name.
- *
- * @private
- * @function Highcharts.Pointer#setDOMEvents
- */
- setDOMEvents: function () {
- var pointer = this,
- container = pointer.chart.container,
- ownerDoc = container.ownerDocument;
- container.onmousedown = function (e) {
- pointer.onContainerMouseDown(e);
- };
- container.onmousemove = function (e) {
- pointer.onContainerMouseMove(e);
- };
- container.onclick = function (e) {
- pointer.onContainerClick(e);
- };
- this.unbindContainerMouseLeave = addEvent(
- container,
- 'mouseleave',
- pointer.onContainerMouseLeave
- );
- if (!H.unbindDocumentMouseUp) {
- H.unbindDocumentMouseUp = addEvent(
- ownerDoc,
- 'mouseup',
- pointer.onDocumentMouseUp
- );
- }
- if (H.hasTouch) {
- container.ontouchstart = function (e) {
- pointer.onContainerTouchStart(e);
- };
- container.ontouchmove = function (e) {
- pointer.onContainerTouchMove(e);
- };
- if (!H.unbindDocumentTouchEnd) {
- H.unbindDocumentTouchEnd = addEvent(
- ownerDoc,
- 'touchend',
- pointer.onDocumentTouchEnd
- );
- }
- }
- },
- /**
- * Destroys the Pointer object and disconnects DOM events.
- *
- * @function Highcharts.Pointer#destroy
- */
- destroy: function () {
- var pointer = this;
- if (pointer.unDocMouseMove) {
- pointer.unDocMouseMove();
- }
- this.unbindContainerMouseLeave();
- if (!H.chartCount) {
- if (H.unbindDocumentMouseUp) {
- H.unbindDocumentMouseUp = H.unbindDocumentMouseUp();
- }
- if (H.unbindDocumentTouchEnd) {
- H.unbindDocumentTouchEnd = H.unbindDocumentTouchEnd();
- }
- }
- // memory and CPU leak
- clearInterval(pointer.tooltipTimeout);
- H.objectEach(pointer, function (val, prop) {
- pointer[prop] = null;
- });
- }
- };
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var charts = H.charts,
- extend = H.extend,
- noop = H.noop,
- pick = H.pick,
- Pointer = H.Pointer;
- // Support for touch devices
- extend(Pointer.prototype, /** @lends Pointer.prototype */ {
- /**
- * Run translation operations
- *
- * @private
- * @function Highcharts.Pointer#pinchTranslate
- *
- * @param {Array<*>} pinchDown
- *
- * @param {Array<*>} touches
- *
- * @param {*} transform
- *
- * @param {*} selectionMarker
- *
- * @param {*} clip
- *
- * @param {*} lastValidTouch
- */
- pinchTranslate: function (
- pinchDown,
- touches,
- transform,
- selectionMarker,
- clip,
- lastValidTouch
- ) {
- if (this.zoomHor) {
- this.pinchTranslateDirection(
- true,
- pinchDown,
- touches,
- transform,
- selectionMarker,
- clip,
- lastValidTouch
- );
- }
- if (this.zoomVert) {
- this.pinchTranslateDirection(
- false,
- pinchDown,
- touches,
- transform,
- selectionMarker,
- clip,
- lastValidTouch
- );
- }
- },
- /**
- * Run translation operations for each direction (horizontal and vertical)
- * independently.
- *
- * @private
- * @function Highcharts.Pointer#pinchTranslateDirection
- *
- * @param {boolean} horiz
- *
- * @param {Array<*>} pinchDown
- *
- * @param {Array<*>} touches
- *
- * @param {*} transform
- *
- * @param {*} selectionMarker
- *
- * @param {*} clip
- *
- * @param {*} lastValidTouch
- *
- * @param {number|undefined} [forcedScale=1]
- */
- pinchTranslateDirection: function (
- horiz,
- pinchDown,
- touches,
- transform,
- selectionMarker,
- clip,
- lastValidTouch,
- forcedScale
- ) {
- var chart = this.chart,
- xy = horiz ? 'x' : 'y',
- XY = horiz ? 'X' : 'Y',
- sChartXY = 'chart' + XY,
- wh = horiz ? 'width' : 'height',
- plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')],
- selectionWH,
- selectionXY,
- clipXY,
- scale = forcedScale || 1,
- inverted = chart.inverted,
- bounds = chart.bounds[horiz ? 'h' : 'v'],
- singleTouch = pinchDown.length === 1,
- touch0Start = pinchDown[0][sChartXY],
- touch0Now = touches[0][sChartXY],
- touch1Start = !singleTouch && pinchDown[1][sChartXY],
- touch1Now = !singleTouch && touches[1][sChartXY],
- outOfBounds,
- transformScale,
- scaleKey,
- setScale = function () {
- // Don't zoom if fingers are too close on this axis
- if (!singleTouch && Math.abs(touch0Start - touch1Start) > 20) {
- scale = forcedScale ||
- Math.abs(touch0Now - touch1Now) /
- Math.abs(touch0Start - touch1Start);
- }
- clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
- selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] /
- scale;
- };
- // Set the scale, first pass
- setScale();
- // The clip position (x or y) is altered if out of bounds, the selection
- // position is not
- selectionXY = clipXY;
- // Out of bounds
- if (selectionXY < bounds.min) {
- selectionXY = bounds.min;
- outOfBounds = true;
- } else if (selectionXY + selectionWH > bounds.max) {
- selectionXY = bounds.max - selectionWH;
- outOfBounds = true;
- }
- // Is the chart dragged off its bounds, determined by dataMin and
- // dataMax?
- if (outOfBounds) {
- // Modify the touchNow position in order to create an elastic drag
- // movement. This indicates to the user that the chart is responsive
- // but can't be dragged further.
- touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
- if (!singleTouch) {
- touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
- }
- // Set the scale, second pass to adapt to the modified touchNow
- // positions
- setScale();
- } else {
- lastValidTouch[xy] = [touch0Now, touch1Now];
- }
- // Set geometry for clipping, selection and transformation
- if (!inverted) {
- clip[xy] = clipXY - plotLeftTop;
- clip[wh] = selectionWH;
- }
- scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
- transformScale = inverted ? 1 / scale : scale;
- selectionMarker[wh] = selectionWH;
- selectionMarker[xy] = selectionXY;
- transform[scaleKey] = scale;
- transform['translate' + XY] = (transformScale * plotLeftTop) +
- (touch0Now - (transformScale * touch0Start));
- },
- /**
- * Handle touch events with two touches
- *
- * @private
- * @function Highcharts.Pointer#pinch
- *
- * @param {Highcharts.PointerEvent} e
- */
- pinch: function (e) {
- var self = this,
- chart = self.chart,
- pinchDown = self.pinchDown,
- touches = e.touches,
- touchesLength = touches.length,
- lastValidTouch = self.lastValidTouch,
- hasZoom = self.hasZoom,
- selectionMarker = self.selectionMarker,
- transform = {},
- fireClickEvent = touchesLength === 1 && (
- (
- self.inClass(e.target, 'highcharts-tracker') &&
- chart.runTrackerClick
- ) ||
- self.runChartClick
- ),
- clip = {};
- // Don't initiate panning until the user has pinched. This prevents us
- // from blocking page scrolling as users scroll down a long page
- // (#4210).
- if (touchesLength > 1) {
- self.initiated = true;
- }
- // On touch devices, only proceed to trigger click if a handler is
- // defined
- if (hasZoom && self.initiated && !fireClickEvent) {
- e.preventDefault();
- }
- // Normalize each touch
- [].map.call(touches, function (e) {
- return self.normalize(e);
- });
- // Register the touch start position
- if (e.type === 'touchstart') {
- [].forEach.call(touches, function (e, i) {
- pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
- });
- lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
- pinchDown[1].chartX];
- lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
- pinchDown[1].chartY];
- // Identify the data bounds in pixels
- chart.axes.forEach(function (axis) {
- if (axis.zoomEnabled) {
- var bounds = chart.bounds[axis.horiz ? 'h' : 'v'],
- minPixelPadding = axis.minPixelPadding,
- min = axis.toPixels(
- pick(axis.options.min, axis.dataMin)
- ),
- max = axis.toPixels(
- pick(axis.options.max, axis.dataMax)
- ),
- absMin = Math.min(min, max),
- absMax = Math.max(min, max);
- // Store the bounds for use in the touchmove handler
- bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
- bounds.max = Math.max(
- axis.pos + axis.len,
- absMax + minPixelPadding
- );
- }
- });
- self.res = true; // reset on next move
- // Optionally move the tooltip on touchmove
- } else if (self.followTouchMove && touchesLength === 1) {
- this.runPointActions(self.normalize(e));
- // Event type is touchmove, handle panning and pinching
- } else if (pinchDown.length) { // can be 0 when releasing, if touchend
- // fires first
- // Set the marker
- if (!selectionMarker) {
- self.selectionMarker = selectionMarker = extend({
- destroy: noop,
- touch: true
- }, chart.plotBox);
- }
- self.pinchTranslate(
- pinchDown,
- touches,
- transform,
- selectionMarker,
- clip,
- lastValidTouch
- );
- self.hasPinched = hasZoom;
- // Scale and translate the groups to provide visual feedback during
- // pinching
- self.scaleGroups(transform, clip);
- if (self.res) {
- self.res = false;
- this.reset(false, 0);
- }
- }
- },
- /**
- * General touch handler shared by touchstart and touchmove.
- *
- * @private
- * @function Highcharts.Pointer#touch
- *
- * @param {Highcharts.PointerEvent} e
- *
- * @param {boolean} start
- */
- touch: function (e, start) {
- var chart = this.chart,
- hasMoved,
- pinchDown,
- isInside;
- if (chart.index !== H.hoverChartIndex) {
- this.onContainerMouseLeave({ relatedTarget: true });
- }
- H.hoverChartIndex = chart.index;
- if (e.touches.length === 1) {
- e = this.normalize(e);
- isInside = chart.isInsidePlot(
- e.chartX - chart.plotLeft,
- e.chartY - chart.plotTop
- );
- if (isInside && !chart.openMenu) {
- // Run mouse events and display tooltip etc
- if (start) {
- this.runPointActions(e);
- }
- // Android fires touchmove events after the touchstart even if
- // the finger hasn't moved, or moved only a pixel or two. In iOS
- // however, the touchmove doesn't fire unless the finger moves
- // more than ~4px. So we emulate this behaviour in Android by
- // checking how much it moved, and cancelling on small
- // distances. #3450.
- if (e.type === 'touchmove') {
- pinchDown = this.pinchDown;
- hasMoved = pinchDown[0] ? Math.sqrt( // #5266
- Math.pow(pinchDown[0].chartX - e.chartX, 2) +
- Math.pow(pinchDown[0].chartY - e.chartY, 2)
- ) >= 4 : false;
- }
- if (pick(hasMoved, true)) {
- this.pinch(e);
- }
- } else if (start) {
- // Hide the tooltip on touching outside the plot area (#1203)
- this.reset();
- }
- } else if (e.touches.length === 2) {
- this.pinch(e);
- }
- },
- /**
- * @private
- * @function Highcharts.Pointer#onContainerTouchStart
- *
- * @param {Highcharts.PointerEvent} e
- */
- onContainerTouchStart: function (e) {
- this.zoomOption(e);
- this.touch(e, true);
- },
- /**
- * @private
- * @function Highcharts.Pointer#onContainerTouchMove
- *
- * @param {Highcharts.PointerEvent} e
- */
- onContainerTouchMove: function (e) {
- this.touch(e);
- },
- /**
- * @private
- * @function Highcharts.Pointer#onDocumentTouchEnd
- *
- * @param {Highcharts.PointerEvent} e
- */
- onDocumentTouchEnd: function (e) {
- if (charts[H.hoverChartIndex]) {
- charts[H.hoverChartIndex].pointer.drop(e);
- }
- }
- });
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var addEvent = H.addEvent,
- charts = H.charts,
- css = H.css,
- doc = H.doc,
- extend = H.extend,
- hasTouch = H.hasTouch,
- noop = H.noop,
- Pointer = H.Pointer,
- removeEvent = H.removeEvent,
- win = H.win,
- wrap = H.wrap;
- if (!hasTouch && (win.PointerEvent || win.MSPointerEvent)) {
- // The touches object keeps track of the points being touched at all times
- var touches = {},
- hasPointerEvent = !!win.PointerEvent,
- getWebkitTouches = function () {
- var fake = [];
- fake.item = function (i) {
- return this[i];
- };
- H.objectEach(touches, function (touch) {
- fake.push({
- pageX: touch.pageX,
- pageY: touch.pageY,
- target: touch.target
- });
- });
- return fake;
- },
- translateMSPointer = function (e, method, wktype, func) {
- var p;
- if (
- (
- e.pointerType === 'touch' ||
- e.pointerType === e.MSPOINTER_TYPE_TOUCH
- ) && charts[H.hoverChartIndex]
- ) {
- func(e);
- p = charts[H.hoverChartIndex].pointer;
- p[method]({
- type: wktype,
- target: e.currentTarget,
- preventDefault: noop,
- touches: getWebkitTouches()
- });
- }
- };
- // Extend the Pointer prototype with methods for each event handler and more
- extend(Pointer.prototype, /** @lends Pointer.prototype */ {
- /**
- * @private
- * @function Highcharts.Pointer#onContainerPointerDown
- *
- * @param {Highcharts.PointerEventObject} e
- */
- onContainerPointerDown: function (e) {
- translateMSPointer(
- e,
- 'onContainerTouchStart',
- 'touchstart',
- function (e) {
- touches[e.pointerId] = {
- pageX: e.pageX,
- pageY: e.pageY,
- target: e.currentTarget
- };
- }
- );
- },
- /**
- * @private
- * @function Highcharts.Pointer#onContainerPointerMove
- *
- * @param {Highcharts.PointerEventObject} e
- */
- onContainerPointerMove: function (e) {
- translateMSPointer(
- e,
- 'onContainerTouchMove',
- 'touchmove',
- function (e) {
- touches[e.pointerId] = { pageX: e.pageX, pageY: e.pageY };
- if (!touches[e.pointerId].target) {
- touches[e.pointerId].target = e.currentTarget;
- }
- }
- );
- },
- /**
- * @private
- * @function Highcharts.Pointer#onDocumentPointerUp
- *
- * @param {Highcharts.PointerEventObject} e
- */
- onDocumentPointerUp: function (e) {
- translateMSPointer(
- e,
- 'onDocumentTouchEnd',
- 'touchend',
- function (e) {
- delete touches[e.pointerId];
- }
- );
- },
- /**
- * Add or remove the MS Pointer specific events
- *
- * @private
- * @function Highcharts.Pointer#batchMSEvents
- *
- * @param {Function} fn
- */
- batchMSEvents: function (fn) {
- fn(
- this.chart.container,
- hasPointerEvent ? 'pointerdown' : 'MSPointerDown',
- this.onContainerPointerDown
- );
- fn(
- this.chart.container,
- hasPointerEvent ? 'pointermove' : 'MSPointerMove',
- this.onContainerPointerMove
- );
- fn(
- doc,
- hasPointerEvent ? 'pointerup' : 'MSPointerUp',
- this.onDocumentPointerUp
- );
- }
- });
- // Disable default IE actions for pinch and such on chart element
- wrap(Pointer.prototype, 'init', function (proceed, chart, options) {
- proceed.call(this, chart, options);
- if (this.hasZoom) { // #4014
- css(chart.container, {
- '-ms-touch-action': 'none',
- 'touch-action': 'none'
- });
- }
- });
- // Add IE specific touch events to chart
- wrap(Pointer.prototype, 'setDOMEvents', function (proceed) {
- proceed.apply(this);
- if (this.hasZoom || this.followTouchMove) {
- this.batchMSEvents(addEvent);
- }
- });
- // Destroy MS events also
- wrap(Pointer.prototype, 'destroy', function (proceed) {
- this.batchMSEvents(removeEvent);
- proceed.call(this);
- });
- }
- }(Highcharts));
- (function (Highcharts) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var H = Highcharts,
- addEvent = H.addEvent,
- css = H.css,
- discardElement = H.discardElement,
- defined = H.defined,
- fireEvent = H.fireEvent,
- isFirefox = H.isFirefox,
- marginNames = H.marginNames,
- merge = H.merge,
- pick = H.pick,
- setAnimation = H.setAnimation,
- stableSort = H.stableSort,
- win = H.win,
- wrap = H.wrap;
- /**
- * The overview of the chart's series. The legend object is instanciated
- * internally in the chart constructor, and is available from the `chart.legend`
- * property. Each chart has only one legend.
- *
- * @class
- * @name Highcharts.Legend
- *
- * @param {Highcharts.Chart} chart
- * The chart instance.
- *
- * @param {Highcharts.LegendOptions} options
- * Legend options.
- */
- Highcharts.Legend = function (chart, options) {
- this.init(chart, options);
- };
- Highcharts.Legend.prototype = {
- /**
- * Initialize the legend.
- *
- * @private
- * @function Highcharts.Legend#init
- *
- * @param {Highcharts.Chart} chart
- * The chart instance.
- *
- * @param {Highcharts.LegendOptions} options
- * Legend options.
- */
- init: function (chart, options) {
- /**
- * Chart of this legend.
- *
- * @readonly
- * @name Highcharts.Legend#chart
- * @type {Highcharts.Chart}
- */
- this.chart = chart;
- this.setOptions(options);
- if (options.enabled) {
- // Render it
- this.render();
- // move checkboxes
- addEvent(this.chart, 'endResize', function () {
- this.legend.positionCheckboxes();
- });
- if (this.proximate) {
- this.unchartrender = addEvent(
- this.chart,
- 'render',
- function () {
- this.legend.proximatePositions();
- this.legend.positionItems();
- }
- );
- } else if (this.unchartrender) {
- this.unchartrender();
- }
- }
- },
- /**
- * @private
- * @function Highcharts.Legend#setOptions
- *
- * @param {Highcharts.LegendOptions} options
- */
- setOptions: function (options) {
- var padding = pick(options.padding, 8);
- /**
- * Legend options.
- *
- * @readonly
- * @name Highcharts.Legend#options
- * @type {Highcharts.LegendOptions}
- */
- this.options = options;
- if (!this.chart.styledMode) {
- this.itemStyle = options.itemStyle;
- this.itemHiddenStyle = merge(
- this.itemStyle,
- options.itemHiddenStyle
- );
- }
- this.itemMarginTop = options.itemMarginTop || 0;
- this.padding = padding;
- this.initialItemY = padding - 5; // 5 is pixels above the text
- this.symbolWidth = pick(options.symbolWidth, 16);
- this.pages = [];
- this.proximate = options.layout === 'proximate' && !this.chart.inverted;
- },
- /**
- * Update the legend with new options. Equivalent to running `chart.update`
- * with a legend configuration option.
- *
- * @sample highcharts/legend/legend-update/
- * Legend update
- *
- * @function Highcharts.Legend#update
- *
- * @param {Highcharts.LegendOptions} options
- * Legend options.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the axis is altered. If doing
- * more operations on the chart, it is a good idea to set redraw to
- * false and call {@link Chart#redraw} after.
- * Whether to redraw the chart.
- *
- * @fires Highcharts.Legends#event:afterUpdate
- */
- update: function (options, redraw) {
- var chart = this.chart;
- this.setOptions(merge(true, this.options, options));
- this.destroy();
- chart.isDirtyLegend = chart.isDirtyBox = true;
- if (pick(redraw, true)) {
- chart.redraw();
- }
- fireEvent(this, 'afterUpdate');
- },
- /**
- * Set the colors for the legend item.
- *
- * @private
- * @function Highcharts.Legend#colorizeItem
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- * A Series or Point instance
- *
- * @param {boolean} [visible=false]
- * Dimmed or colored
- *
- * @todo
- * Make events official: Fires the event `afterColorizeItem`.
- */
- colorizeItem: function (item, visible) {
- item.legendGroup[visible ? 'removeClass' : 'addClass'](
- 'highcharts-legend-item-hidden'
- );
- if (!this.chart.styledMode) {
- var legend = this,
- options = legend.options,
- legendItem = item.legendItem,
- legendLine = item.legendLine,
- legendSymbol = item.legendSymbol,
- hiddenColor = legend.itemHiddenStyle.color,
- textColor = visible ? options.itemStyle.color : hiddenColor,
- symbolColor = visible ?
- (item.color || hiddenColor) :
- hiddenColor,
- markerOptions = item.options && item.options.marker,
- symbolAttr = { fill: symbolColor };
- if (legendItem) {
- legendItem.css({
- fill: textColor,
- color: textColor // #1553, oldIE
- });
- }
- if (legendLine) {
- legendLine.attr({ stroke: symbolColor });
- }
- if (legendSymbol) {
- // Apply marker options
- if (markerOptions && legendSymbol.isMarker) { // #585
- symbolAttr = item.pointAttribs();
- if (!visible) {
- // #6769
- symbolAttr.stroke = symbolAttr.fill = hiddenColor;
- }
- }
- legendSymbol.attr(symbolAttr);
- }
- }
- fireEvent(this, 'afterColorizeItem', { item: item, visible: visible });
- },
- /**
- * @private
- * @function Highcharts.Legend#positionItems
- */
- positionItems: function () {
- // Now that the legend width and height are established, put the items
- // in the final position
- this.allItems.forEach(this.positionItem, this);
- if (!this.chart.isResizing) {
- this.positionCheckboxes();
- }
- },
- /**
- * Position the legend item.
- *
- * @private
- * @function Highcharts.Legend#positionItem
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- * The item to position
- */
- positionItem: function (item) {
- var legend = this,
- options = legend.options,
- symbolPadding = options.symbolPadding,
- ltr = !options.rtl,
- legendItemPos = item._legendItemPos,
- itemX = legendItemPos[0],
- itemY = legendItemPos[1],
- checkbox = item.checkbox,
- legendGroup = item.legendGroup;
- if (legendGroup && legendGroup.element) {
- legendGroup[defined(legendGroup.translateY) ? 'animate' : 'attr']({
- translateX: ltr ?
- itemX :
- legend.legendWidth - itemX - 2 * symbolPadding - 4,
- translateY: itemY
- });
- }
- if (checkbox) {
- checkbox.x = itemX;
- checkbox.y = itemY;
- }
- },
- /**
- * Destroy a single legend item, used internally on removing series items.
- *
- * @private
- * @function Highcharts.Legend#destroyItem
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- * The item to remove
- */
- destroyItem: function (item) {
- var checkbox = item.checkbox;
- // destroy SVG elements
- ['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'].forEach(
- function (key) {
- if (item[key]) {
- item[key] = item[key].destroy();
- }
- }
- );
- if (checkbox) {
- discardElement(item.checkbox);
- }
- },
- /**
- * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
- * must be called after destruction.
- *
- * @private
- * @function Highcharts.Legend#destroy
- */
- destroy: function () {
- function destroyItems(key) {
- if (this[key]) {
- this[key] = this[key].destroy();
- }
- }
- // Destroy items
- this.getAllItems().forEach(function (item) {
- ['legendItem', 'legendGroup'].forEach(destroyItems, item);
- });
- // Destroy legend elements
- [
- 'clipRect',
- 'up',
- 'down',
- 'pager',
- 'nav',
- 'box',
- 'title',
- 'group'
- ].forEach(destroyItems, this);
- this.display = null; // Reset in .render on update.
- },
- /**
- * Position the checkboxes after the width is determined.
- *
- * @private
- * @function Highcharts.Legend#positionCheckboxes
- */
- positionCheckboxes: function () {
- var alignAttr = this.group && this.group.alignAttr,
- translateY,
- clipHeight = this.clipHeight || this.legendHeight,
- titleHeight = this.titleHeight;
- if (alignAttr) {
- translateY = alignAttr.translateY;
- this.allItems.forEach(function (item) {
- var checkbox = item.checkbox,
- top;
- if (checkbox) {
- top = translateY + titleHeight + checkbox.y +
- (this.scrollOffset || 0) + 3;
- css(checkbox, {
- left: (alignAttr.translateX + item.checkboxOffset +
- checkbox.x - 20) + 'px',
- top: top + 'px',
- display: this.proximate || (
- top > translateY - 6 &&
- top < translateY + clipHeight - 6
- ) ?
- '' :
- 'none'
- });
- }
- }, this);
- }
- },
- /**
- * Render the legend title on top of the legend.
- *
- * @private
- * @function Highcharts.Legend#renderTitle
- */
- renderTitle: function () {
- var options = this.options,
- padding = this.padding,
- titleOptions = options.title,
- titleHeight = 0,
- bBox;
- if (titleOptions.text) {
- if (!this.title) {
- /**
- * SVG element of the legend title.
- *
- * @readonly
- * @name Highcharts.Legend#title
- * @type {Highcharts.SVGElement}
- */
- this.title = this.chart.renderer.label(
- titleOptions.text,
- padding - 3,
- padding - 4,
- null,
- null,
- null,
- options.useHTML,
- null,
- 'legend-title'
- )
- .attr({ zIndex: 1 });
- if (!this.chart.styledMode) {
- this.title.css(titleOptions.style);
- }
- this.title.add(this.group);
- }
- // Set the max title width (#7253)
- if (!titleOptions.width) {
- this.title.css({
- width: this.maxLegendWidth + 'px'
- });
- }
- bBox = this.title.getBBox();
- titleHeight = bBox.height;
- this.offsetWidth = bBox.width; // #1717
- this.contentGroup.attr({ translateY: titleHeight });
- }
- this.titleHeight = titleHeight;
- },
- /**
- * Set the legend item text.
- *
- * @function Highcharts.Legend#setText
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- * The item for which to update the text in the legend.
- */
- setText: function (item) {
- var options = this.options;
- item.legendItem.attr({
- text: options.labelFormat ?
- H.format(options.labelFormat, item, this.chart.time) :
- options.labelFormatter.call(item)
- });
- },
- /**
- * Render a single specific legend item. Called internally from the `render`
- * function.
- *
- * @private
- * @function Highcharts.Legend#renderItem
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- * The item to render.
- */
- renderItem: function (item) {
- var legend = this,
- chart = legend.chart,
- renderer = chart.renderer,
- options = legend.options,
- horizontal = options.layout === 'horizontal',
- symbolWidth = legend.symbolWidth,
- symbolPadding = options.symbolPadding,
- itemStyle = legend.itemStyle,
- itemHiddenStyle = legend.itemHiddenStyle,
- itemDistance = horizontal ? pick(options.itemDistance, 20) : 0,
- ltr = !options.rtl,
- bBox,
- li = item.legendItem,
- isSeries = !item.series,
- series = !isSeries && item.series.drawLegendSymbol ?
- item.series :
- item,
- seriesOptions = series.options,
- showCheckbox = legend.createCheckboxForItem &&
- seriesOptions &&
- seriesOptions.showCheckbox,
- // full width minus text width
- itemExtraWidth = symbolWidth + symbolPadding + itemDistance +
- (showCheckbox ? 20 : 0),
- useHTML = options.useHTML,
- itemClassName = item.options.className;
- if (!li) { // generate it once, later move it
- // Generate the group box, a group to hold the symbol and text. Text
- // is to be appended in Legend class.
- item.legendGroup = renderer.g('legend-item')
- .addClass(
- 'highcharts-' + series.type + '-series ' +
- 'highcharts-color-' + item.colorIndex +
- (itemClassName ? ' ' + itemClassName : '') +
- (isSeries ? ' highcharts-series-' + item.index : '')
- )
- .attr({ zIndex: 1 })
- .add(legend.scrollGroup);
- // Generate the list item text and add it to the group
- item.legendItem = li = renderer.text(
- '',
- ltr ? symbolWidth + symbolPadding : -symbolPadding,
- legend.baseline || 0,
- useHTML
- );
- if (!chart.styledMode) {
- // merge to prevent modifying original (#1021)
- li.css(merge(item.visible ? itemStyle : itemHiddenStyle));
- }
- li.attr({
- align: ltr ? 'left' : 'right',
- zIndex: 2
- })
- .add(item.legendGroup);
- // Get the baseline for the first item - the font size is equal for
- // all
- if (!legend.baseline) {
- legend.fontMetrics = renderer.fontMetrics(
- chart.styledMode ? 12 : itemStyle.fontSize,
- li
- );
- legend.baseline =
- legend.fontMetrics.f + 3 + legend.itemMarginTop;
- li.attr('y', legend.baseline);
- }
- // Draw the legend symbol inside the group box
- legend.symbolHeight = options.symbolHeight || legend.fontMetrics.f;
- series.drawLegendSymbol(legend, item);
- if (legend.setItemEvents) {
- legend.setItemEvents(item, li, useHTML);
- }
- // add the HTML checkbox on top
- if (showCheckbox) {
- legend.createCheckboxForItem(item);
- }
- }
- // Colorize the items
- legend.colorizeItem(item, item.visible);
- // Take care of max width and text overflow (#6659)
- if (chart.styledMode || !itemStyle.width) {
- li.css({
- width: (
- options.itemWidth ||
- legend.widthOption ||
- chart.spacingBox.width
- ) - itemExtraWidth
- });
- }
- // Always update the text
- legend.setText(item);
- // calculate the positions for the next line
- bBox = li.getBBox();
- item.itemWidth = item.checkboxOffset =
- options.itemWidth ||
- item.legendItemWidth ||
- bBox.width + itemExtraWidth;
- legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
- legend.totalItemWidth += item.itemWidth;
- legend.itemHeight = item.itemHeight = Math.round(
- item.legendItemHeight || bBox.height || legend.symbolHeight
- );
- },
- /**
- * Get the position of the item in the layout. We now know the
- * maxItemWidth from the previous loop.
- *
- * @private
- * @function Highcharts.Legend#layoutItem
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- */
- layoutItem: function (item) {
- var options = this.options,
- padding = this.padding,
- horizontal = options.layout === 'horizontal',
- itemHeight = item.itemHeight,
- itemMarginBottom = options.itemMarginBottom || 0,
- itemMarginTop = this.itemMarginTop,
- itemDistance = horizontal ? pick(options.itemDistance, 20) : 0,
- maxLegendWidth = this.maxLegendWidth,
- itemWidth = (
- options.alignColumns &&
- this.totalItemWidth > maxLegendWidth
- ) ?
- this.maxItemWidth :
- item.itemWidth;
- // If the item exceeds the width, start a new line
- if (
- horizontal &&
- this.itemX - padding + itemWidth > maxLegendWidth
- ) {
- this.itemX = padding;
- this.itemY += itemMarginTop + this.lastLineHeight +
- itemMarginBottom;
- this.lastLineHeight = 0; // reset for next line (#915, #3976)
- }
- // Set the edge positions
- this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
- this.lastLineHeight = Math.max( // #915
- itemHeight,
- this.lastLineHeight
- );
- // cache the position of the newly generated or reordered items
- item._legendItemPos = [this.itemX, this.itemY];
- // advance
- if (horizontal) {
- this.itemX += itemWidth;
- } else {
- this.itemY += itemMarginTop + itemHeight + itemMarginBottom;
- this.lastLineHeight = itemHeight;
- }
- // the width of the widest item
- this.offsetWidth = this.widthOption || Math.max(
- (
- horizontal ? this.itemX - padding - (item.checkbox ?
- // decrease by itemDistance only when no checkbox #4853
- 0 :
- itemDistance
- ) : itemWidth
- ) + padding,
- this.offsetWidth
- );
- },
- /**
- * Get all items, which is one item per series for most series and one
- * item per point for pie series and its derivatives.
- *
- * @private
- * @function Highcharts.Legend#getAllItems
- *
- * @return {Array<Highcharts.Point|Highcharts.Series>}
- * The current items in the legend.
- *
- * @fires Highcharts.Legend#event:afterGetAllItems
- *
- * @todo
- * Make events official: Fires the event `afterGetAllItems`.
- */
- getAllItems: function () {
- var allItems = [];
- this.chart.series.forEach(function (series) {
- var seriesOptions = series && series.options;
- // Handle showInLegend. If the series is linked to another series,
- // defaults to false.
- if (series && pick(
- seriesOptions.showInLegend,
- !defined(seriesOptions.linkedTo) ? undefined : false, true
- )) {
- // Use points or series for the legend item depending on
- // legendType
- allItems = allItems.concat(
- series.legendItems ||
- (
- seriesOptions.legendType === 'point' ?
- series.data :
- series
- )
- );
- }
- });
- fireEvent(this, 'afterGetAllItems', { allItems: allItems });
- return allItems;
- },
- /**
- * Get a short, three letter string reflecting the alignment and layout.
- *
- * @private
- * @function Highcharts.Legend#getAlignment
- *
- * @return {string}
- * The alignment, empty string if floating
- */
- getAlignment: function () {
- var options = this.options;
- // Use the first letter of each alignment option in order to detect
- // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
- if (this.proximate) {
- return options.align.charAt(0) + 'tv';
- }
- return options.floating ? '' : (
- options.align.charAt(0) +
- options.verticalAlign.charAt(0) +
- options.layout.charAt(0)
- );
- },
- /**
- * Adjust the chart margins by reserving space for the legend on only one
- * side of the chart. If the position is set to a corner, top or bottom is
- * reserved for horizontal legends and left or right for vertical ones.
- *
- * @private
- * @function Highcharts.Legend#adjustMargins
- *
- * @param {Array<number>} margin
- *
- * @param {number} spacing
- */
- adjustMargins: function (margin, spacing) {
- var chart = this.chart,
- options = this.options,
- alignment = this.getAlignment();
- if (alignment) {
- ([
- /(lth|ct|rth)/,
- /(rtv|rm|rbv)/,
- /(rbh|cb|lbh)/,
- /(lbv|lm|ltv)/
- ]).forEach(function (alignments, side) {
- if (alignments.test(alignment) && !defined(margin[side])) {
- // Now we have detected on which side of the chart we should
- // reserve space for the legend
- chart[marginNames[side]] = Math.max(
- chart[marginNames[side]],
- (
- chart.legend[
- (side + 1) % 2 ? 'legendHeight' : 'legendWidth'
- ] +
- [1, -1, -1, 1][side] * options[
- (side % 2) ? 'x' : 'y'
- ] +
- pick(options.margin, 12) +
- spacing[side] +
- (
- side === 0 &&
- chart.options.title.margin !== undefined ?
- chart.titleOffset +
- chart.options.title.margin :
- 0
- ) // #7428, #7894
- )
- );
- }
- });
- }
- },
- /**
- * @private
- * @function Highcharts.Legend#proximatePositions
- */
- proximatePositions: function () {
- var chart = this.chart,
- boxes = [],
- alignLeft = this.options.align === 'left';
- this.allItems.forEach(function (item) {
- var lastPoint,
- height,
- useFirstPoint = alignLeft;
- if (item.xAxis && item.points) {
- if (item.xAxis.options.reversed) {
- useFirstPoint = !useFirstPoint;
- }
- lastPoint = H.find(
- useFirstPoint ?
- item.points :
- item.points.slice(0).reverse(),
- function (item) {
- return H.isNumber(item.plotY);
- }
- );
- height = item.legendGroup.getBBox().height;
- boxes.push({
- target: item.visible ?
- (lastPoint ? lastPoint.plotY : item.xAxis.height) -
- 0.3 * height :
- chart.plotHeight,
- size: height,
- item: item
- });
- }
- }, this);
- H.distribute(boxes, chart.plotHeight);
- boxes.forEach(function (box) {
- box.item._legendItemPos[1] =
- chart.plotTop - chart.spacing[0] + box.pos;
- });
- },
- /**
- * Render the legend. This method can be called both before and after
- * `chart.render`. If called after, it will only rearrange items instead
- * of creating new ones. Called internally on initial render and after
- * redraws.
- *
- * @private
- * @function Highcharts.Legend#render
- */
- render: function () {
- var legend = this,
- chart = legend.chart,
- renderer = chart.renderer,
- legendGroup = legend.group,
- allItems,
- display,
- legendWidth,
- legendHeight,
- box = legend.box,
- options = legend.options,
- padding = legend.padding,
- alignTo,
- allowedWidth;
- legend.itemX = padding;
- legend.itemY = legend.initialItemY;
- legend.offsetWidth = 0;
- legend.lastItemY = 0;
- legend.widthOption = H.relativeLength(
- options.width,
- chart.spacingBox.width - padding
- );
- // Compute how wide the legend is allowed to be
- allowedWidth = chart.spacingBox.width - 2 * padding - options.x;
- if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
- allowedWidth /= 2;
- }
- legend.maxLegendWidth = legend.widthOption || allowedWidth;
- if (!legendGroup) {
- /**
- * SVG group of the legend.
- *
- * @readonly
- * @name Highcharts.Legend#group
- * @type {Highcharts.SVGElement}
- */
- legend.group = legendGroup = renderer.g('legend')
- .attr({ zIndex: 7 })
- .add();
- legend.contentGroup = renderer.g()
- .attr({ zIndex: 1 }) // above background
- .add(legendGroup);
- legend.scrollGroup = renderer.g()
- .add(legend.contentGroup);
- }
- legend.renderTitle();
- // add each series or point
- allItems = legend.getAllItems();
- // sort by legendIndex
- stableSort(allItems, function (a, b) {
- return ((a.options && a.options.legendIndex) || 0) -
- ((b.options && b.options.legendIndex) || 0);
- });
- // reversed legend
- if (options.reversed) {
- allItems.reverse();
- }
- /**
- * All items for the legend, which is an array of series for most series
- * and an array of points for pie series and its derivatives.
- *
- * @readonly
- * @name Highcharts.Legend#allItems
- * @type {Array<Highcharts.Point|Highcharts.Series>}
- */
- legend.allItems = allItems;
- legend.display = display = !!allItems.length;
- // Render the items. First we run a loop to set the text and properties
- // and read all the bounding boxes. The next loop computes the item
- // positions based on the bounding boxes.
- legend.lastLineHeight = 0;
- legend.maxItemWidth = 0;
- legend.totalItemWidth = 0;
- legend.itemHeight = 0;
- allItems.forEach(legend.renderItem, legend);
- allItems.forEach(legend.layoutItem, legend);
- // Get the box
- legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
- legendHeight = legend.lastItemY + legend.lastLineHeight +
- legend.titleHeight;
- legendHeight = legend.handleOverflow(legendHeight);
- legendHeight += padding;
- // Draw the border and/or background
- if (!box) {
- /**
- * SVG element of the legend box.
- *
- * @readonly
- * @name Highcharts.Legend#box
- * @type {Highcharts.SVGElement}
- */
- legend.box = box = renderer.rect()
- .addClass('highcharts-legend-box')
- .attr({
- r: options.borderRadius
- })
- .add(legendGroup);
- box.isNew = true;
- }
- // Presentational
- if (!chart.styledMode) {
- box
- .attr({
- stroke: options.borderColor,
- 'stroke-width': options.borderWidth || 0,
- fill: options.backgroundColor || 'none'
- })
- .shadow(options.shadow);
- }
- if (legendWidth > 0 && legendHeight > 0) {
- box[box.isNew ? 'attr' : 'animate'](
- box.crisp.call({}, { // #7260
- x: 0,
- y: 0,
- width: legendWidth,
- height: legendHeight
- }, box.strokeWidth())
- );
- box.isNew = false;
- }
- // hide the border if no items
- box[display ? 'show' : 'hide']();
- // Open for responsiveness
- if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
- legendWidth = legendHeight = 0;
- }
- legend.legendWidth = legendWidth;
- legend.legendHeight = legendHeight;
- if (display) {
- // If aligning to the top and the layout is horizontal, adjust for
- // the title (#7428)
- alignTo = chart.spacingBox;
- if (/(lth|ct|rth)/.test(legend.getAlignment())) {
- alignTo = merge(alignTo, {
- y: alignTo.y + chart.titleOffset +
- chart.options.title.margin
- });
- }
- legendGroup.align(merge(options, {
- width: legendWidth,
- height: legendHeight,
- verticalAlign: this.proximate ? 'top' : options.verticalAlign
- }), true, alignTo);
- }
- if (!this.proximate) {
- this.positionItems();
- }
- },
- /**
- * Set up the overflow handling by adding navigation with up and down arrows
- * below the legend.
- *
- * @private
- * @function Highcharts.Legend#handleOverflow
- *
- * @param {number} legendHeight
- *
- * @return {number}
- */
- handleOverflow: function (legendHeight) {
- var legend = this,
- chart = this.chart,
- renderer = chart.renderer,
- options = this.options,
- optionsY = options.y,
- alignTop = options.verticalAlign === 'top',
- padding = this.padding,
- spaceHeight = chart.spacingBox.height +
- (alignTop ? -optionsY : optionsY) - padding,
- maxHeight = options.maxHeight,
- clipHeight,
- clipRect = this.clipRect,
- navOptions = options.navigation,
- animation = pick(navOptions.animation, true),
- arrowSize = navOptions.arrowSize || 12,
- nav = this.nav,
- pages = this.pages,
- lastY,
- allItems = this.allItems,
- clipToHeight = function (height) {
- if (typeof height === 'number') {
- clipRect.attr({
- height: height
- });
- } else if (clipRect) { // Reset (#5912)
- legend.clipRect = clipRect.destroy();
- legend.contentGroup.clip();
- }
- // useHTML
- if (legend.contentGroup.div) {
- legend.contentGroup.div.style.clip = height ?
- 'rect(' + padding + 'px,9999px,' +
- (padding + height) + 'px,0)' :
- 'auto';
- }
- };
- // Adjust the height
- if (
- options.layout === 'horizontal' &&
- options.verticalAlign !== 'middle' &&
- !options.floating
- ) {
- spaceHeight /= 2;
- }
- if (maxHeight) {
- spaceHeight = Math.min(spaceHeight, maxHeight);
- }
- // Reset the legend height and adjust the clipping rectangle
- pages.length = 0;
- if (legendHeight > spaceHeight && navOptions.enabled !== false) {
- this.clipHeight = clipHeight =
- Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
- this.currentPage = pick(this.currentPage, 1);
- this.fullHeight = legendHeight;
- // Fill pages with Y positions so that the top of each a legend item
- // defines the scroll top for each page (#2098)
- allItems.forEach(function (item, i) {
- var y = item._legendItemPos[1],
- h = Math.round(item.legendItem.getBBox().height),
- len = pages.length;
- if (!len || (y - pages[len - 1] > clipHeight &&
- (lastY || y) !== pages[len - 1])) {
- pages.push(lastY || y);
- len++;
- }
- // Keep track of which page each item is on
- item.pageIx = len - 1;
- if (lastY) {
- allItems[i - 1].pageIx = len - 1;
- }
- if (
- i === allItems.length - 1 &&
- y + h - pages[len - 1] > clipHeight &&
- y !== lastY // #2617
- ) {
- pages.push(y);
- item.pageIx = len;
- }
- if (y !== lastY) {
- lastY = y;
- }
- });
- // Only apply clipping if needed. Clipping causes blurred legend in
- // PDF export (#1787)
- if (!clipRect) {
- clipRect = legend.clipRect =
- renderer.clipRect(0, padding, 9999, 0);
- legend.contentGroup.clip(clipRect);
- }
- clipToHeight(clipHeight);
- // Add navigation elements
- if (!nav) {
- this.nav = nav = renderer.g()
- .attr({ zIndex: 1 })
- .add(this.group);
- this.up = renderer
- .symbol(
- 'triangle',
- 0,
- 0,
- arrowSize,
- arrowSize
- )
- .on('click', function () {
- legend.scroll(-1, animation);
- })
- .add(nav);
- this.pager = renderer.text('', 15, 10)
- .addClass('highcharts-legend-navigation');
- if (!chart.styledMode) {
- this.pager.css(navOptions.style);
- }
- this.pager.add(nav);
- this.down = renderer
- .symbol(
- 'triangle-down',
- 0,
- 0,
- arrowSize,
- arrowSize
- )
- .on('click', function () {
- legend.scroll(1, animation);
- })
- .add(nav);
- }
- // Set initial position
- legend.scroll(0);
- legendHeight = spaceHeight;
- // Reset
- } else if (nav) {
- clipToHeight();
- this.nav = nav.destroy(); // #6322
- this.scrollGroup.attr({
- translateY: 1
- });
- this.clipHeight = 0; // #1379
- }
- return legendHeight;
- },
- /**
- * Scroll the legend by a number of pages.
- *
- * @private
- * @function Highcharts.Legend#scroll
- *
- * @param {number} scrollBy
- * The number of pages to scroll.
- *
- * @param {Highcharts.AnimationOptionsObject} animation
- * Whether and how to apply animation.
- */
- scroll: function (scrollBy, animation) {
- var pages = this.pages,
- pageCount = pages.length,
- currentPage = this.currentPage + scrollBy,
- clipHeight = this.clipHeight,
- navOptions = this.options.navigation,
- pager = this.pager,
- padding = this.padding;
- // When resizing while looking at the last page
- if (currentPage > pageCount) {
- currentPage = pageCount;
- }
- if (currentPage > 0) {
- if (animation !== undefined) {
- setAnimation(animation, this.chart);
- }
- this.nav.attr({
- translateX: padding,
- translateY: clipHeight + this.padding + 7 + this.titleHeight,
- visibility: 'visible'
- });
- this.up.attr({
- 'class': currentPage === 1 ?
- 'highcharts-legend-nav-inactive' :
- 'highcharts-legend-nav-active'
- });
- pager.attr({
- text: currentPage + '/' + pageCount
- });
- this.down.attr({
- 'x': 18 + this.pager.getBBox().width, // adjust to text width
- 'class': currentPage === pageCount ?
- 'highcharts-legend-nav-inactive' :
- 'highcharts-legend-nav-active'
- });
- if (!this.chart.styledMode) {
- this.up
- .attr({
- fill: currentPage === 1 ?
- navOptions.inactiveColor :
- navOptions.activeColor
- })
- .css({
- cursor: currentPage === 1 ? 'default' : 'pointer'
- });
- this.down
- .attr({
- fill: currentPage === pageCount ?
- navOptions.inactiveColor :
- navOptions.activeColor
- })
- .css({
- cursor: currentPage === pageCount ?
- 'default' :
- 'pointer'
- });
- }
- this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
- this.scrollGroup.animate({
- translateY: this.scrollOffset
- });
- this.currentPage = currentPage;
- this.positionCheckboxes();
- }
- }
- };
- /**
- * Legend symbol mixin.
- *
- * @private
- * @mixin Highcharts.LegendSymbolMixin
- */
- H.LegendSymbolMixin = {
- /**
- * Get the series' symbol in the legend
- *
- * @private
- * @function Highcharts.LegendSymbolMixin.drawRectangle
- *
- * @param {Highcharts.Legend} legend
- * The legend object
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- * The series (this) or point
- */
- drawRectangle: function (legend, item) {
- var options = legend.options,
- symbolHeight = legend.symbolHeight,
- square = options.squareSymbol,
- symbolWidth = square ? symbolHeight : legend.symbolWidth;
- item.legendSymbol = this.chart.renderer.rect(
- square ? (legend.symbolWidth - symbolHeight) / 2 : 0,
- legend.baseline - symbolHeight + 1, // #3988
- symbolWidth,
- symbolHeight,
- pick(legend.options.symbolRadius, symbolHeight / 2)
- )
- .addClass('highcharts-point')
- .attr({
- zIndex: 3
- }).add(item.legendGroup);
- },
- /**
- * Get the series' symbol in the legend. This method should be overridable
- * to create custom symbols through
- * Highcharts.seriesTypes[type].prototype.drawLegendSymbols.
- *
- * @private
- * @function Highcharts.LegendSymbolMixin.drawLineMarker
- *
- * @param {Highcharts.Legend} legend
- * The legend object.
- */
- drawLineMarker: function (legend) {
- var options = this.options,
- markerOptions = options.marker,
- radius,
- legendSymbol,
- symbolWidth = legend.symbolWidth,
- symbolHeight = legend.symbolHeight,
- generalRadius = symbolHeight / 2,
- renderer = this.chart.renderer,
- legendItemGroup = this.legendGroup,
- verticalCenter = legend.baseline -
- Math.round(legend.fontMetrics.b * 0.3),
- attr = {};
- // Draw the line
- if (!this.chart.styledMode) {
- attr = {
- 'stroke-width': options.lineWidth || 0
- };
- if (options.dashStyle) {
- attr.dashstyle = options.dashStyle;
- }
- }
- this.legendLine = renderer.path([
- 'M',
- 0,
- verticalCenter,
- 'L',
- symbolWidth,
- verticalCenter
- ])
- .addClass('highcharts-graph')
- .attr(attr)
- .add(legendItemGroup);
- // Draw the marker
- if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
- // Do not allow the marker to be larger than the symbolHeight
- radius = Math.min(
- pick(markerOptions.radius, generalRadius),
- generalRadius
- );
- // Restrict symbol markers size
- if (this.symbol.indexOf('url') === 0) {
- markerOptions = merge(markerOptions, {
- width: symbolHeight,
- height: symbolHeight
- });
- radius = 0;
- }
- this.legendSymbol = legendSymbol = renderer.symbol(
- this.symbol,
- (symbolWidth / 2) - radius,
- verticalCenter - radius,
- 2 * radius,
- 2 * radius,
- markerOptions
- )
- .addClass('highcharts-point')
- .add(legendItemGroup);
- legendSymbol.isMarker = true;
- }
- }
- };
- // Workaround for #2030, horizontal legend items not displaying in IE11 Preview,
- // and for #2580, a similar drawing flaw in Firefox 26.
- // Explore if there's a general cause for this. The problem may be related
- // to nested group elements, as the legend item texts are within 4 group
- // elements.
- if (/Trident\/7\.0/.test(win.navigator.userAgent) || isFirefox) {
- wrap(Highcharts.Legend.prototype, 'positionItem', function (proceed, item) {
- var legend = this,
- // If chart destroyed in sync, this is undefined (#2030)
- runPositionItem = function () {
- if (item._legendItemPos) {
- proceed.call(legend, item);
- }
- };
- // Do it now, for export and to get checkbox placement
- runPositionItem();
- // Do it after to work around the core issue
- if (!legend.bubbleLegend) {
- setTimeout(runPositionItem);
- }
- });
- }
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Callback for chart constructors.
- *
- * @callback Highcharts.ChartCallbackFunction
- *
- * @param {Highcharts.Chart} chart
- * Created chart.
- */
- /**
- * The chart title. The title has an `update` method that allows modifying the
- * options directly or indirectly via `chart.update`.
- *
- * @interface Highcharts.TitleObject
- * @extends Highcharts.SVGElement
- *//**
- * Modify options for the title.
- *
- * @function Highcharts.TitleObject#update
- *
- * @param {Highcharts.TitleOptions} titleOptions
- * Options to modify.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the title is altered. If doing more
- * operations on the chart, it is a good idea to set redraw to false and
- * call {@link Chart#redraw} after.
- */
- /**
- * The chart subtitle. The subtitle has an `update` method that
- * allows modifying the options directly or indirectly via
- * `chart.update`.
- *
- * @interface Highcharts.SubtitleObject
- * @extends Highcharts.SVGElement
- *//**
- * Modify options for the subtitle.
- *
- * @function Highcharts.SubtitleObject#update
- *
- * @param {Highcharts.SubtitleOptions} subtitleOptions
- * Options to modify.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the subtitle is altered. If doing
- * more operations on the chart, it is a good idea to set redraw to false
- * and call {@link Chart#redraw} after.
- */
- var addEvent = H.addEvent,
- animate = H.animate,
- animObject = H.animObject,
- attr = H.attr,
- doc = H.doc,
- Axis = H.Axis, // @todo add as requirement
- createElement = H.createElement,
- defaultOptions = H.defaultOptions,
- discardElement = H.discardElement,
- charts = H.charts,
- css = H.css,
- defined = H.defined,
- extend = H.extend,
- find = H.find,
- fireEvent = H.fireEvent,
- isNumber = H.isNumber,
- isObject = H.isObject,
- isString = H.isString,
- Legend = H.Legend, // @todo add as requirement
- marginNames = H.marginNames,
- merge = H.merge,
- objectEach = H.objectEach,
- Pointer = H.Pointer, // @todo add as requirement
- pick = H.pick,
- pInt = H.pInt,
- removeEvent = H.removeEvent,
- seriesTypes = H.seriesTypes,
- splat = H.splat,
- syncTimeout = H.syncTimeout,
- win = H.win;
- /**
- * The Chart class. The recommended constructor is {@link Highcharts#chart}.
- *
- * @example
- * var chart = Highcharts.chart('container', {
- * title: {
- * text: 'My chart'
- * },
- * series: [{
- * data: [1, 3, 2, 4]
- * }]
- * })
- *
- * @class
- * @name Highcharts.Chart
- *
- * @param {string|Highcharts.HTMLDOMElement} [renderTo]
- * The DOM element to render to, or its id.
- *
- * @param {Highcharts.Options} options
- * The chart options structure.
- *
- * @param {Highcharts.ChartCallbackFunction} [callback]
- * Function to run when the chart has loaded and and all external images
- * are loaded. Defining a
- * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
- * handler is equivalent.
- */
- var Chart = H.Chart = function () {
- this.getArgs.apply(this, arguments);
- };
- /**
- * Factory function for basic charts.
- *
- * @example
- * // Render a chart in to div#container
- * var chart = Highcharts.chart('container', {
- * title: {
- * text: 'My chart'
- * },
- * series: [{
- * data: [1, 3, 2, 4]
- * }]
- * });
- *
- * @function Highcharts.chart
- *
- * @param {string|Highcharts.HTMLDOMElement} [renderTo]
- * The DOM element to render to, or its id.
- *
- * @param {Highcharts.Options} options
- * The chart options structure.
- *
- * @param {Highcharts.ChartCallbackFunction} [callback]
- * Function to run when the chart has loaded and and all external images
- * are loaded. Defining a
- * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
- * handler is equivalent.
- *
- * @return {Highcharts.Chart}
- * Returns the Chart object.
- */
- H.chart = function (a, b, c) {
- return new Chart(a, b, c);
- };
- extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
- // Hook for adding callbacks in modules
- callbacks: [],
- /**
- * Handle the arguments passed to the constructor.
- *
- * @private
- * @function Highcharts.Chart#getArgs
- *
- * @param {...Array<*>} arguments
- * All arguments for the constructor.
- *
- * @return {Array<*>}
- * Passed arguments without renderTo.
- *
- * @fires Highcharts.Chart#event:init
- * @fires Highcharts.Chart#event:afterInit
- */
- getArgs: function () {
- var args = [].slice.call(arguments);
- // Remove the optional first argument, renderTo, and
- // set it on this.
- if (isString(args[0]) || args[0].nodeName) {
- this.renderTo = args.shift();
- }
- this.init(args[0], args[1]);
- },
- /**
- * Overridable function that initializes the chart. The constructor's
- * arguments are passed on directly.
- *
- * @function Highcharts.Chart#init
- *
- * @param {Highcharts.Options} userOptions
- * Custom options.
- *
- * @param {Function} [callback]
- * Function to run when the chart has loaded and and all external
- * images are loaded.
- *
- * @fires Highcharts.Chart#event:init
- * @fires Highcharts.Chart#event:afterInit
- */
- init: function (userOptions, callback) {
- // Handle regular options
- var options,
- type,
- // skip merging data points to increase performance
- seriesOptions = userOptions.series,
- userPlotOptions = userOptions.plotOptions || {};
- // Fire the event with a default function
- fireEvent(this, 'init', { args: arguments }, function () {
- userOptions.series = null;
- options = merge(defaultOptions, userOptions); // do the merge
- // Override (by copy of user options) or clear tooltip options
- // in chart.options.plotOptions (#6218)
- for (type in options.plotOptions) {
- options.plotOptions[type].tooltip = (
- userPlotOptions[type] &&
- merge(userPlotOptions[type].tooltip) // override by copy
- ) || undefined; // or clear
- }
- // User options have higher priority than default options
- // (#6218). In case of exporting: path is changed
- options.tooltip.userOptions = (
- userOptions.chart &&
- userOptions.chart.forExport &&
- userOptions.tooltip.userOptions
- ) || userOptions.tooltip;
- // set back the series data
- options.series = userOptions.series = seriesOptions;
- this.userOptions = userOptions;
- var optionsChart = options.chart;
- var chartEvents = optionsChart.events;
- this.margin = [];
- this.spacing = [];
- // Pixel data bounds for touch zoom
- this.bounds = { h: {}, v: {} };
- // An array of functions that returns labels that should be
- // considered for anti-collision
- this.labelCollectors = [];
- this.callback = callback;
- this.isResizing = 0;
- /**
- * The options structure for the chart. It contains members for
- * the sub elements like series, legend, tooltip etc.
- *
- * @name Highcharts.Chart#options
- * @type {Highcharts.Options}
- */
- this.options = options;
- /**
- * All the axes in the chart.
- *
- * @see Highcharts.Chart.xAxis
- * @see Highcharts.Chart.yAxis
- *
- * @name Highcharts.Chart#axes
- * @type {Array<Highcharts.Axis>}
- */
- this.axes = [];
- /**
- * All the current series in the chart.
- *
- * @name Highcharts.Chart#series
- * @type {Array<Highcharts.Series>}
- */
- this.series = [];
- /**
- * The `Time` object associated with the chart. Since v6.0.5,
- * time settings can be applied individually for each chart. If
- * no individual settings apply, the `Time` object is shared by
- * all instances.
- *
- * @name Highcharts.Chart#time
- * @type {Highcharts.Time}
- */
- this.time =
- userOptions.time && Object.keys(userOptions.time).length ?
- new H.Time(userOptions.time) :
- H.time;
- /**
- * Whether the chart is in styled mode, meaning all presentatinoal
- * attributes are avoided.
- *
- * @name Highcharts.Chart#styledMode
- * @type {boolean}
- */
- this.styledMode = optionsChart.styledMode;
- this.hasCartesianSeries = optionsChart.showAxes;
- var chart = this;
- // Add the chart to the global lookup
- chart.index = charts.length;
- charts.push(chart);
- H.chartCount++;
- // Chart event handlers
- if (chartEvents) {
- objectEach(chartEvents, function (event, eventType) {
- addEvent(chart, eventType, event);
- });
- }
- /**
- * A collection of the X axes in the chart.
- *
- * @name Highcharts.Chart#xAxis
- * @type {Array<Highcharts.Axis>}
- */
- chart.xAxis = [];
- /**
- * A collection of the Y axes in the chart.
- *
- * @name Highcharts.Chart#yAxis
- * @type {Array<Highcharts.Axis>}
- *
- * @todo
- * Make events official: Fire the event `afterInit`.
- */
- chart.yAxis = [];
- chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
- // Fire after init but before first render, before axes and series
- // have been initialized.
- fireEvent(chart, 'afterInit');
- chart.firstRender();
- });
- },
- /**
- * Internal function to unitialize an individual series.
- *
- * @private
- * @function Highcharts.Chart#initSeries
- *
- * @param {Highcharts.ChartOptions} options
- *
- * @return {Highcharts.Series}
- */
- initSeries: function (options) {
- var chart = this,
- optionsChart = chart.options.chart,
- type = (
- options.type ||
- optionsChart.type ||
- optionsChart.defaultSeriesType
- ),
- series,
- Constr = seriesTypes[type];
- // No such series type
- if (!Constr) {
- H.error(17, true, chart);
- }
- series = new Constr();
- series.init(this, options);
- return series;
- },
- /**
- * Order all series above a given index. When series are added and ordered
- * by configuration, only the last series is handled (#248, #1123, #2456,
- * #6112). This function is called on series initialization and destroy.
- *
- * @private
- * @function Highcharts.Series#orderSeries
- *
- * @param {number} fromIndex
- * If this is given, only the series above this index are handled.
- */
- orderSeries: function (fromIndex) {
- var series = this.series,
- i = fromIndex || 0;
- for (; i < series.length; i++) {
- if (series[i]) {
- series[i].index = i;
- series[i].name = series[i].getName();
- }
- }
- },
- /**
- * Check whether a given point is within the plot area.
- *
- * @function Highcharts.Chart#isInsidePlot
- *
- * @param {number} plotX
- * Pixel x relative to the plot area.
- *
- * @param {number} plotY
- * Pixel y relative to the plot area.
- *
- * @param {boolean} inverted
- * Whether the chart is inverted.
- *
- * @return {boolean}
- * Returns true if the given point is inside the plot area.
- */
- isInsidePlot: function (plotX, plotY, inverted) {
- var x = inverted ? plotY : plotX,
- y = inverted ? plotX : plotY;
- return x >= 0 &&
- x <= this.plotWidth &&
- y >= 0 &&
- y <= this.plotHeight;
- },
- /**
- * Redraw the chart after changes have been done to the data, axis extremes
- * chart size or chart elements. All methods for updating axes, series or
- * points have a parameter for redrawing the chart. This is `true` by
- * default. But in many cases you want to do more than one operation on the
- * chart before redrawing, for example add a number of points. In those
- * cases it is a waste of resources to redraw the chart for each new point
- * added. So you add the points and call `chart.redraw()` after.
- *
- * @function Highcharts.Chart#redraw
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
- * If or how to apply animation to the redraw.
- *
- * @fires Highcharts.Chart#event:afterSetExtremes
- * @fires Highcharts.Chart#event:beforeRedraw
- * @fires Highcharts.Chart#event:predraw
- * @fires Highcharts.Chart#event:redraw
- * @fires Highcharts.Chart#event:render
- * @fires Highcharts.Chart#event:updatedData
- */
- redraw: function (animation) {
- fireEvent(this, 'beforeRedraw');
- var chart = this,
- axes = chart.axes,
- series = chart.series,
- pointer = chart.pointer,
- legend = chart.legend,
- legendUserOptions = chart.userOptions.legend,
- redrawLegend = chart.isDirtyLegend,
- hasStackedSeries,
- hasDirtyStacks,
- hasCartesianSeries = chart.hasCartesianSeries,
- isDirtyBox = chart.isDirtyBox,
- i,
- serie,
- renderer = chart.renderer,
- isHiddenChart = renderer.isHidden(),
- afterRedraw = [];
- // Handle responsive rules, not only on resize (#6130)
- if (chart.setResponsive) {
- chart.setResponsive(false);
- }
- H.setAnimation(animation, chart);
- if (isHiddenChart) {
- chart.temporaryDisplay();
- }
- // Adjust title layout (reflow multiline text)
- chart.layOutTitles();
- // link stacked series
- i = series.length;
- while (i--) {
- serie = series[i];
- if (serie.options.stacking) {
- hasStackedSeries = true;
- if (serie.isDirty) {
- hasDirtyStacks = true;
- break;
- }
- }
- }
- if (hasDirtyStacks) { // mark others as dirty
- i = series.length;
- while (i--) {
- serie = series[i];
- if (serie.options.stacking) {
- serie.isDirty = true;
- }
- }
- }
- // Handle updated data in the series
- series.forEach(function (serie) {
- if (serie.isDirty) {
- if (serie.options.legendType === 'point') {
- if (serie.updateTotals) {
- serie.updateTotals();
- }
- redrawLegend = true;
- } else if (
- legendUserOptions &&
- (
- legendUserOptions.labelFormatter ||
- legendUserOptions.labelFormat
- )
- ) {
- redrawLegend = true; // #2165
- }
- }
- if (serie.isDirtyData) {
- fireEvent(serie, 'updatedData');
- }
- });
- // handle added or removed series
- if (redrawLegend && legend && legend.options.enabled) {
- // draw legend graphics
- legend.render();
- chart.isDirtyLegend = false;
- }
- // reset stacks
- if (hasStackedSeries) {
- chart.getStacks();
- }
- if (hasCartesianSeries) {
- // set axes scales
- axes.forEach(function (axis) {
- axis.updateNames();
- // Update categories in a Gantt chart
- if (axis.updateYNames) {
- axis.updateYNames();
- }
- axis.setScale();
- });
- }
- chart.getMargins(); // #3098
- if (hasCartesianSeries) {
- // If one axis is dirty, all axes must be redrawn (#792, #2169)
- axes.forEach(function (axis) {
- if (axis.isDirty) {
- isDirtyBox = true;
- }
- });
- // redraw axes
- axes.forEach(function (axis) {
- // Fire 'afterSetExtremes' only if extremes are set
- var key = axis.min + ',' + axis.max;
- if (axis.extKey !== key) { // #821, #4452
- axis.extKey = key;
- // prevent a recursive call to chart.redraw() (#1119)
- afterRedraw.push(function () {
- fireEvent(
- axis,
- 'afterSetExtremes',
- extend(axis.eventArgs, axis.getExtremes())
- ); // #747, #751
- delete axis.eventArgs;
- });
- }
- if (isDirtyBox || hasStackedSeries) {
- axis.redraw();
- }
- });
- }
- // the plot areas size has changed
- if (isDirtyBox) {
- chart.drawChartBox();
- }
- // Fire an event before redrawing series, used by the boost module to
- // clear previous series renderings.
- fireEvent(chart, 'predraw');
- // redraw affected series
- series.forEach(function (serie) {
- if ((isDirtyBox || serie.isDirty) && serie.visible) {
- serie.redraw();
- }
- // Set it here, otherwise we will have unlimited 'updatedData' calls
- // for a hidden series after setData(). Fixes #6012
- serie.isDirtyData = false;
- });
- // move tooltip or reset
- if (pointer) {
- pointer.reset(true);
- }
- // redraw if canvas
- renderer.draw();
- // Fire the events
- fireEvent(chart, 'redraw');
- fireEvent(chart, 'render');
- if (isHiddenChart) {
- chart.temporaryDisplay(true);
- }
- // Fire callbacks that are put on hold until after the redraw
- afterRedraw.forEach(function (callback) {
- callback.call();
- });
- },
- /**
- * Get an axis, series or point object by `id` as given in the configuration
- * options. Returns `undefined` if no item is found.
- *
- * @sample highcharts/plotoptions/series-id/
- * Get series by id
- *
- * @function Highcharts.Chart#get
- *
- * @param {string} id
- * The id as given in the configuration options.
- *
- * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
- * The retrieved item.
- */
- get: function (id) {
- var ret,
- series = this.series,
- i;
- function itemById(item) {
- return item.id === id || (item.options && item.options.id === id);
- }
- ret =
- // Search axes
- find(this.axes, itemById) ||
- // Search series
- find(this.series, itemById);
- // Search points
- for (i = 0; !ret && i < series.length; i++) {
- ret = find(series[i].points || [], itemById);
- }
- return ret;
- },
- /**
- * Create the Axis instances based on the config options.
- *
- * @private
- * @function Highcharts.Chart#getAxes
- *
- * @fires Highcharts.Chart#event:afterGetAxes
- * @fires Highcharts.Chart#event:getAxes
- */
- getAxes: function () {
- var chart = this,
- options = this.options,
- xAxisOptions = options.xAxis = splat(options.xAxis || {}),
- yAxisOptions = options.yAxis = splat(options.yAxis || {}),
- optionsArray;
- fireEvent(this, 'getAxes');
- // make sure the options are arrays and add some members
- xAxisOptions.forEach(function (axis, i) {
- axis.index = i;
- axis.isX = true;
- });
- yAxisOptions.forEach(function (axis, i) {
- axis.index = i;
- });
- // concatenate all axis options into one array
- optionsArray = xAxisOptions.concat(yAxisOptions);
- optionsArray.forEach(function (axisOptions) {
- new Axis(chart, axisOptions); // eslint-disable-line no-new
- });
- fireEvent(this, 'afterGetAxes');
- },
- /**
- * Returns an array of all currently selected points in the chart. Points
- * can be selected by clicking or programmatically by the
- * {@link Highcharts.Point#select}
- * function.
- *
- * @sample highcharts/plotoptions/series-allowpointselect-line/
- * Get selected points
- *
- * @function Highcharts.Chart#getSelectedPoints
- *
- * @return {Array<Highcharts.Point>}
- * The currently selected points.
- */
- getSelectedPoints: function () {
- var points = [];
- this.series.forEach(function (serie) {
- // For one-to-one points inspect series.data in order to retrieve
- // points outside the visible range (#6445). For grouped data,
- // inspect the generated series.points.
- points = points.concat(
- (serie[serie.hasGroupedData ? 'points' : 'data'] || []).filter(
- function (point) {
- return point.selected;
- }
- )
- );
- });
- return points;
- },
- /**
- * Returns an array of all currently selected series in the chart. Series
- * can be selected either programmatically by the
- * {@link Highcharts.Series#select}
- * function or by checking the checkbox next to the legend item if
- * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
- * is true.
- *
- * @sample highcharts/members/chart-getselectedseries/
- * Get selected series
- *
- * @function Highcharts.Chart#getSelectedSeries
- *
- * @return {Array<Highcharts.Series>}
- * The currently selected series.
- */
- getSelectedSeries: function () {
- return this.series.filter(function (serie) {
- return serie.selected;
- });
- },
- /**
- * Set a new title or subtitle for the chart.
- *
- * @sample highcharts/members/chart-settitle/
- * Set title text and styles
- *
- * @function Highcharts.Chart#setTitle
- *
- * @param {Highcharts.TitleOptions} titleOptions
- * New title options. The title text itself is set by the
- * `titleOptions.text` property.
- *
- * @param {Highcharts.SubtitleOptions} subtitleOptions
- * New subtitle options. The subtitle text itself is set by the
- * `subtitleOptions.text` property.
- *
- * @param {boolean} redraw
- * Whether to redraw the chart or wait for a later call to
- * `chart.redraw()`.
- */
- setTitle: function (titleOptions, subtitleOptions, redraw) {
- var chart = this,
- options = chart.options,
- styledMode = chart.styledMode,
- chartTitleOptions,
- chartSubtitleOptions;
- chartTitleOptions = options.title = merge(
- // Default styles
- !styledMode && {
- style: {
- color: '#333333',
- fontSize: options.isStock ? '16px' : '18px' // #2944
- }
- },
- options.title,
- titleOptions
- );
- chartSubtitleOptions = options.subtitle = merge(
- // Default styles
- !styledMode && {
- style: {
- color: '#666666'
- }
- },
- options.subtitle,
- subtitleOptions
- );
- // add title and subtitle
- /**
- * The chart title. The title has an `update` method that allows
- * modifying the options directly or indirectly via
- * `chart.update`.
- *
- * @sample highcharts/members/title-update/
- * Updating titles
- *
- * @name Highcharts.Chart#title
- * @type {Highcharts.TitleObject}
- */
- /**
- * The chart subtitle. The subtitle has an `update` method that
- * allows modifying the options directly or indirectly via
- * `chart.update`.
- *
- * @name Highcharts.Chart#subtitle
- * @type {Highcharts.SubtitleObject}
- */
- [
- ['title', titleOptions, chartTitleOptions],
- ['subtitle', subtitleOptions, chartSubtitleOptions]
- ].forEach(function (arr, i) {
- var name = arr[0],
- title = chart[name],
- titleOptions = arr[1],
- chartTitleOptions = arr[2];
- if (title && titleOptions) {
- chart[name] = title = title.destroy(); // remove old
- }
- if (chartTitleOptions && !title) {
- chart[name] = chart.renderer.text(
- chartTitleOptions.text,
- 0,
- 0,
- chartTitleOptions.useHTML
- )
- .attr({
- align: chartTitleOptions.align,
- 'class': 'highcharts-' + name,
- zIndex: chartTitleOptions.zIndex || 4
- })
- .add();
- // Update methods, shortcut to Chart.setTitle
- chart[name].update = function (o) {
- chart.setTitle(!i && o, i && o);
- };
- // Presentational
- if (!styledMode) {
- chart[name].css(chartTitleOptions.style);
- }
- }
- });
- chart.layOutTitles(redraw);
- },
- /**
- * Internal function to lay out the chart titles and cache the full offset
- * height for use in `getMargins`. The result is stored in
- * `this.titleOffset`.
- *
- * @private
- * @function Highcharts.Chart#layOutTitles
- *
- * @param {boolean} [redraw=true]
- */
- layOutTitles: function (redraw) {
- var titleOffset = 0,
- requiresDirtyBox,
- renderer = this.renderer,
- spacingBox = this.spacingBox;
- // Lay out the title and the subtitle respectively
- ['title', 'subtitle'].forEach(function (key) {
- var title = this[key],
- titleOptions = this.options[key],
- offset = key === 'title' ? -3 :
- // Floating subtitle (#6574)
- titleOptions.verticalAlign ? 0 : titleOffset + 2,
- titleSize;
- if (title) {
- if (!this.styledMode) {
- titleSize = titleOptions.style.fontSize;
- }
- titleSize = renderer.fontMetrics(titleSize, title).b;
- title
- .css({
- width: (titleOptions.width ||
- spacingBox.width + titleOptions.widthAdjust) + 'px'
- })
- .align(extend({
- y: offset + titleSize
- }, titleOptions), false, 'spacingBox');
- if (!titleOptions.floating && !titleOptions.verticalAlign) {
- titleOffset = Math.ceil(
- titleOffset +
- // Skip the cache for HTML (#3481)
- title.getBBox(titleOptions.useHTML).height
- );
- }
- }
- }, this);
- requiresDirtyBox = this.titleOffset !== titleOffset;
- this.titleOffset = titleOffset; // used in getMargins
- if (!this.isDirtyBox && requiresDirtyBox) {
- this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
- // Redraw if necessary (#2719, #2744)
- if (this.hasRendered && pick(redraw, true) && this.isDirtyBox) {
- this.redraw();
- }
- }
- },
- /**
- * Internal function to get the chart width and height according to options
- * and container size. Sets
- * {@link Chart.chartWidth} and
- * {@link Chart.chartHeight}.
- *
- * @function Highcharts.Chart#getChartSize
- */
- getChartSize: function () {
- var chart = this,
- optionsChart = chart.options.chart,
- widthOption = optionsChart.width,
- heightOption = optionsChart.height,
- renderTo = chart.renderTo;
- // Get inner width and height
- if (!defined(widthOption)) {
- chart.containerWidth = H.getStyle(renderTo, 'width');
- }
- if (!defined(heightOption)) {
- chart.containerHeight = H.getStyle(renderTo, 'height');
- }
- /**
- * The current pixel width of the chart.
- *
- * @name Highcharts.Chart#chartWidth
- * @type {number}
- */
- chart.chartWidth = Math.max( // #1393
- 0,
- widthOption || chart.containerWidth || 600 // #1460
- );
- /**
- * The current pixel height of the chart.
- *
- * @name Highcharts.Chart#chartHeight
- * @type {number}
- */
- chart.chartHeight = Math.max(
- 0,
- H.relativeLength(
- heightOption,
- chart.chartWidth
- ) ||
- (chart.containerHeight > 1 ? chart.containerHeight : 400)
- );
- },
- /**
- * If the renderTo element has no offsetWidth, most likely one or more of
- * its parents are hidden. Loop up the DOM tree to temporarily display the
- * parents, then save the original display properties, and when the true
- * size is retrieved, reset them. Used on first render and on redraws.
- *
- * @private
- * @function Highcharts.Chart#temporaryDisplay
- *
- * @param {boolean} revert
- * Revert to the saved original styles.
- */
- temporaryDisplay: function (revert) {
- var node = this.renderTo,
- tempStyle;
- if (!revert) {
- while (node && node.style) {
- // When rendering to a detached node, it needs to be temporarily
- // attached in order to read styling and bounding boxes (#5783,
- // #7024).
- if (!doc.body.contains(node) && !node.parentNode) {
- node.hcOrigDetached = true;
- doc.body.appendChild(node);
- }
- if (
- H.getStyle(node, 'display', false) === 'none' ||
- node.hcOricDetached
- ) {
- node.hcOrigStyle = {
- display: node.style.display,
- height: node.style.height,
- overflow: node.style.overflow
- };
- tempStyle = {
- display: 'block',
- overflow: 'hidden'
- };
- if (node !== this.renderTo) {
- tempStyle.height = 0;
- }
- H.css(node, tempStyle);
- // If it still doesn't have an offset width after setting
- // display to block, it probably has an !important priority
- // #2631, 6803
- if (!node.offsetWidth) {
- node.style.setProperty('display', 'block', 'important');
- }
- }
- node = node.parentNode;
- if (node === doc.body) {
- break;
- }
- }
- } else {
- while (node && node.style) {
- if (node.hcOrigStyle) {
- H.css(node, node.hcOrigStyle);
- delete node.hcOrigStyle;
- }
- if (node.hcOrigDetached) {
- doc.body.removeChild(node);
- node.hcOrigDetached = false;
- }
- node = node.parentNode;
- }
- }
- },
- /**
- * Set the {@link Chart.container|chart container's} class name, in
- * addition to `highcharts-container`.
- *
- * @function Highcharts.Chart#setClassName
- *
- * @param {string} className
- */
- setClassName: function (className) {
- this.container.className = 'highcharts-container ' + (className || '');
- },
- /**
- * Get the containing element, determine the size and create the inner
- * container div to hold the chart.
- *
- * @private
- * @function Highcharts.Chart#afterGetContainer
- *
- * @fires Highcharts.Chart#event:afterGetContainer
- */
- getContainer: function () {
- var chart = this,
- container,
- options = chart.options,
- optionsChart = options.chart,
- chartWidth,
- chartHeight,
- renderTo = chart.renderTo,
- indexAttrName = 'data-highcharts-chart',
- oldChartIndex,
- Ren,
- containerId = H.uniqueKey(),
- containerStyle,
- key;
- if (!renderTo) {
- chart.renderTo = renderTo = optionsChart.renderTo;
- }
- if (isString(renderTo)) {
- chart.renderTo = renderTo = doc.getElementById(renderTo);
- }
- // Display an error if the renderTo is wrong
- if (!renderTo) {
- H.error(13, true, chart);
- }
- // If the container already holds a chart, destroy it. The check for
- // hasRendered is there because web pages that are saved to disk from
- // the browser, will preserve the data-highcharts-chart attribute and
- // the SVG contents, but not an interactive chart. So in this case,
- // charts[oldChartIndex] will point to the wrong chart if any (#2609).
- oldChartIndex = pInt(attr(renderTo, indexAttrName));
- if (
- isNumber(oldChartIndex) &&
- charts[oldChartIndex] &&
- charts[oldChartIndex].hasRendered
- ) {
- charts[oldChartIndex].destroy();
- }
- // Make a reference to the chart from the div
- attr(renderTo, indexAttrName, chart.index);
- // remove previous chart
- renderTo.innerHTML = '';
- // If the container doesn't have an offsetWidth, it has or is a child of
- // a node that has display:none. We need to temporarily move it out to a
- // visible state to determine the size, else the legend and tooltips
- // won't render properly. The skipClone option is used in sparklines as
- // a micro optimization, saving about 1-2 ms each chart.
- if (!optionsChart.skipClone && !renderTo.offsetWidth) {
- chart.temporaryDisplay();
- }
- // get the width and height
- chart.getChartSize();
- chartWidth = chart.chartWidth;
- chartHeight = chart.chartHeight;
- // Allow table cells and flex-boxes to shrink without the chart blocking
- // them out (#6427)
- css(renderTo, { overflow: 'hidden' });
- // Create the inner container
- if (!chart.styledMode) {
- containerStyle = extend({
- position: 'relative',
- // needed for context menu (avoidscrollbars) and content
- // overflow in IE
- overflow: 'hidden',
- width: chartWidth + 'px',
- height: chartHeight + 'px',
- textAlign: 'left',
- lineHeight: 'normal', // #427
- zIndex: 0, // #1072
- '-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
- }, optionsChart.style);
- }
- /**
- * The containing HTML element of the chart. The container is
- * dynamically inserted into the element given as the `renderTo`
- * parameter in the {@link Highcharts#chart} constructor.
- *
- * @name Highcharts.Chart#container
- * @type {Highcharts.HTMLDOMElement}
- */
- container = createElement(
- 'div',
- {
- id: containerId
- },
- containerStyle,
- renderTo
- );
- chart.container = container;
- // cache the cursor (#1650)
- chart._cursor = container.style.cursor;
- // Initialize the renderer
- Ren = H[optionsChart.renderer] || H.Renderer;
- /**
- * The renderer instance of the chart. Each chart instance has only one
- * associated renderer.
- *
- * @name Highcharts.Chart#renderer
- * @type {Highcharts.SVGRenderer}
- */
- chart.renderer = new Ren(
- container,
- chartWidth,
- chartHeight,
- null,
- optionsChart.forExport,
- options.exporting && options.exporting.allowHTML,
- chart.styledMode
- );
- chart.setClassName(optionsChart.className);
- if (!chart.styledMode) {
- chart.renderer.setStyle(optionsChart.style);
- } else {
- // Initialize definitions
- for (key in options.defs) {
- this.renderer.definition(options.defs[key]);
- }
- }
- // Add a reference to the charts index
- chart.renderer.chartIndex = chart.index;
- fireEvent(this, 'afterGetContainer');
- },
- /**
- * Calculate margins by rendering axis labels in a preliminary position.
- * Title, subtitle and legend have already been rendered at this stage, but
- * will be moved into their final positions.
- *
- * @private
- * @function Highcharts.Chart#getMargins
- *
- * @param {boolean} skipAxes
- *
- * @fires Highcharts.Chart#event:getMargins
- */
- getMargins: function (skipAxes) {
- var chart = this,
- spacing = chart.spacing,
- margin = chart.margin,
- titleOffset = chart.titleOffset;
- chart.resetMargins();
- // Adjust for title and subtitle
- if (titleOffset && !defined(margin[0])) {
- chart.plotTop = Math.max(
- chart.plotTop,
- titleOffset + chart.options.title.margin + spacing[0]
- );
- }
- // Adjust for legend
- if (chart.legend && chart.legend.display) {
- chart.legend.adjustMargins(margin, spacing);
- }
- fireEvent(this, 'getMargins');
- if (!skipAxes) {
- this.getAxisMargins();
- }
- },
- /**
- * @private
- * @function Highcharts.Chart#getAxisMargins
- */
- getAxisMargins: function () {
- var chart = this,
- // [top, right, bottom, left]
- axisOffset = chart.axisOffset = [0, 0, 0, 0],
- margin = chart.margin;
- // pre-render axes to get labels offset width
- if (chart.hasCartesianSeries) {
- chart.axes.forEach(function (axis) {
- if (axis.visible) {
- axis.getOffset();
- }
- });
- }
- // Add the axis offsets
- marginNames.forEach(function (m, side) {
- if (!defined(margin[side])) {
- chart[m] += axisOffset[side];
- }
- });
- chart.setChartSize();
- },
- /**
- * Reflows the chart to its container. By default, the chart reflows
- * automatically to its container following a `window.resize` event, as per
- * the [chart.reflow](https://api.highcharts/highcharts/chart.reflow)
- * option. However, there are no reliable events for div resize, so if the
- * container is resized without a window resize event, this must be called
- * explicitly.
- *
- * @sample highcharts/members/chart-reflow/
- * Resize div and reflow
- * @sample highcharts/chart/events-container/
- * Pop up and reflow
- *
- * @function Highcharts.Chart#reflow
- *
- * @param {global.Event} [e]
- * Event arguments. Used primarily when the function is called
- * internally as a response to window resize.
- */
- reflow: function (e) {
- var chart = this,
- optionsChart = chart.options.chart,
- renderTo = chart.renderTo,
- hasUserSize = (
- defined(optionsChart.width) &&
- defined(optionsChart.height)
- ),
- width = optionsChart.width || H.getStyle(renderTo, 'width'),
- height = optionsChart.height || H.getStyle(renderTo, 'height'),
- target = e ? e.target : win;
- // Width and height checks for display:none. Target is doc in IE8 and
- // Opera, win in Firefox, Chrome and IE9.
- if (
- !hasUserSize &&
- !chart.isPrinting &&
- width &&
- height &&
- (target === win || target === doc)
- ) {
- if (
- width !== chart.containerWidth ||
- height !== chart.containerHeight
- ) {
- H.clearTimeout(chart.reflowTimeout);
- // When called from window.resize, e is set, else it's called
- // directly (#2224)
- chart.reflowTimeout = syncTimeout(function () {
- // Set size, it may have been destroyed in the meantime
- // (#1257)
- if (chart.container) {
- chart.setSize(undefined, undefined, false);
- }
- }, e ? 100 : 0);
- }
- chart.containerWidth = width;
- chart.containerHeight = height;
- }
- },
- /**
- * Toggle the event handlers necessary for auto resizing, depending on the
- * `chart.reflow` option.
- *
- * @private
- * @function Highcharts.Chart#setReflow
- *
- * @param {boolean} reflow
- */
- setReflow: function (reflow) {
- var chart = this;
- if (reflow !== false && !this.unbindReflow) {
- this.unbindReflow = addEvent(win, 'resize', function (e) {
- chart.reflow(e);
- });
- addEvent(this, 'destroy', this.unbindReflow);
- } else if (reflow === false && this.unbindReflow) {
- // Unbind and unset
- this.unbindReflow = this.unbindReflow();
- }
- // The following will add listeners to re-fit the chart before and after
- // printing (#2284). However it only works in WebKit. Should have worked
- // in Firefox, but not supported in IE.
- /*
- if (win.matchMedia) {
- win.matchMedia('print').addListener(function reflow() {
- chart.reflow();
- });
- }
- //*/
- },
- /**
- * Resize the chart to a given width and height. In order to set the width
- * only, the height argument may be skipped. To set the height only, pass
- * `undefined` for the width.
- *
- * @sample highcharts/members/chart-setsize-button/
- * Test resizing from buttons
- * @sample highcharts/members/chart-setsize-jquery-resizable/
- * Add a jQuery UI resizable
- * @sample stock/members/chart-setsize/
- * Highstock with UI resizable
- *
- * @function Highcharts.Chart#setSize
- *
- * @param {number|null} [width]
- * The new pixel width of the chart. Since v4.2.6, the argument can
- * be `undefined` in order to preserve the current value (when
- * setting height only), or `null` to adapt to the width of the
- * containing element.
- *
- * @param {number|null} [height]
- * The new pixel height of the chart. Since v4.2.6, the argument can
- * be `undefined` in order to preserve the current value, or `null`
- * in order to adapt to the height of the containing element.
- *
- * @param {Highcharts.AnimationOptionsObject} [animation=true]
- * Whether and how to apply animation.
- *
- * @fires Highcharts.Chart#event:endResize
- * @fires Highcharts.Chart#event:resize
- */
- setSize: function (width, height, animation) {
- var chart = this,
- renderer = chart.renderer,
- globalAnimation;
- // Handle the isResizing counter
- chart.isResizing += 1;
- // set the animation for the current process
- H.setAnimation(animation, chart);
- chart.oldChartHeight = chart.chartHeight;
- chart.oldChartWidth = chart.chartWidth;
- if (width !== undefined) {
- chart.options.chart.width = width;
- }
- if (height !== undefined) {
- chart.options.chart.height = height;
- }
- chart.getChartSize();
- // Resize the container with the global animation applied if enabled
- // (#2503)
- if (!chart.styledMode) {
- globalAnimation = renderer.globalAnimation;
- (globalAnimation ? animate : css)(chart.container, {
- width: chart.chartWidth + 'px',
- height: chart.chartHeight + 'px'
- }, globalAnimation);
- }
- chart.setChartSize(true);
- renderer.setSize(chart.chartWidth, chart.chartHeight, animation);
- // handle axes
- chart.axes.forEach(function (axis) {
- axis.isDirty = true;
- axis.setScale();
- });
- chart.isDirtyLegend = true; // force legend redraw
- chart.isDirtyBox = true; // force redraw of plot and chart border
- chart.layOutTitles(); // #2857
- chart.getMargins();
- chart.redraw(animation);
- chart.oldChartHeight = null;
- fireEvent(chart, 'resize');
- // Fire endResize and set isResizing back. If animation is disabled,
- // fire without delay
- syncTimeout(function () {
- if (chart) {
- fireEvent(chart, 'endResize', null, function () {
- chart.isResizing -= 1;
- });
- }
- }, animObject(globalAnimation).duration);
- },
- /**
- * Set the public chart properties. This is done before and after the
- * pre-render to determine margin sizes.
- *
- * @private
- * @function Highcharts.Chart#setChartSize
- *
- * @param {boolean} skipAxes
- *
- * @fires Highcharts.Chart#event:afterSetChartSize
- */
- setChartSize: function (skipAxes) {
- var chart = this,
- inverted = chart.inverted,
- renderer = chart.renderer,
- chartWidth = chart.chartWidth,
- chartHeight = chart.chartHeight,
- optionsChart = chart.options.chart,
- spacing = chart.spacing,
- clipOffset = chart.clipOffset,
- clipX,
- clipY,
- plotLeft,
- plotTop,
- plotWidth,
- plotHeight,
- plotBorderWidth;
- /**
- * The current left position of the plot area in pixels.
- *
- * @name Highcharts.Chart#plotLeft
- * @type {number}
- */
- chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
- /**
- * The current top position of the plot area in pixels.
- *
- * @name Highcharts.Chart#plotTop
- * @type {number}
- */
- chart.plotTop = plotTop = Math.round(chart.plotTop);
- /**
- * The current width of the plot area in pixels.
- *
- * @name Highcharts.Chart#plotWidth
- * @type {number}
- */
- chart.plotWidth = plotWidth = Math.max(
- 0,
- Math.round(chartWidth - plotLeft - chart.marginRight)
- );
- /**
- * The current height of the plot area in pixels.
- *
- * @name Highcharts.Chart#plotHeight
- * @type {number}
- */
- chart.plotHeight = plotHeight = Math.max(
- 0,
- Math.round(chartHeight - plotTop - chart.marginBottom)
- );
- chart.plotSizeX = inverted ? plotHeight : plotWidth;
- chart.plotSizeY = inverted ? plotWidth : plotHeight;
- chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
- // Set boxes used for alignment
- chart.spacingBox = renderer.spacingBox = {
- x: spacing[3],
- y: spacing[0],
- width: chartWidth - spacing[3] - spacing[1],
- height: chartHeight - spacing[0] - spacing[2]
- };
- chart.plotBox = renderer.plotBox = {
- x: plotLeft,
- y: plotTop,
- width: plotWidth,
- height: plotHeight
- };
- plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2);
- clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2);
- clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
- chart.clipBox = {
- x: clipX,
- y: clipY,
- width: Math.floor(
- chart.plotSizeX -
- Math.max(plotBorderWidth, clipOffset[1]) / 2 -
- clipX
- ),
- height: Math.max(
- 0,
- Math.floor(
- chart.plotSizeY -
- Math.max(plotBorderWidth, clipOffset[2]) / 2 -
- clipY
- )
- )
- };
- if (!skipAxes) {
- chart.axes.forEach(function (axis) {
- axis.setAxisSize();
- axis.setAxisTranslation();
- });
- }
- fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
- },
- /**
- * Initial margins before auto size margins are applied.
- *
- * @private
- * @function Highcharts.Chart#resetMargins
- */
- resetMargins: function () {
- fireEvent(this, 'resetMargins');
- var chart = this,
- chartOptions = chart.options.chart;
- // Create margin and spacing array
- ['margin', 'spacing'].forEach(function splashArrays(target) {
- var value = chartOptions[target],
- values = isObject(value) ? value : [value, value, value, value];
- [
- 'Top',
- 'Right',
- 'Bottom',
- 'Left'
- ].forEach(function (sideName, side) {
- chart[target][side] = pick(
- chartOptions[target + sideName],
- values[side]
- );
- });
- });
- // Set margin names like chart.plotTop, chart.plotLeft,
- // chart.marginRight, chart.marginBottom.
- marginNames.forEach(function (m, side) {
- chart[m] = pick(chart.margin[side], chart.spacing[side]);
- });
- chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
- chart.clipOffset = [0, 0, 0, 0];
- },
- /**
- * Internal function to draw or redraw the borders and backgrounds for chart
- * and plot area.
- *
- * @private
- * @function Highcharts.Chart#drawChartBox
- *
- * @fires Highcharts.Chart#event:afterDrawChartBox
- */
- drawChartBox: function () {
- var chart = this,
- optionsChart = chart.options.chart,
- renderer = chart.renderer,
- chartWidth = chart.chartWidth,
- chartHeight = chart.chartHeight,
- chartBackground = chart.chartBackground,
- plotBackground = chart.plotBackground,
- plotBorder = chart.plotBorder,
- chartBorderWidth,
- styledMode = chart.styledMode,
- plotBGImage = chart.plotBGImage,
- chartBackgroundColor = optionsChart.backgroundColor,
- plotBackgroundColor = optionsChart.plotBackgroundColor,
- plotBackgroundImage = optionsChart.plotBackgroundImage,
- mgn,
- bgAttr,
- plotLeft = chart.plotLeft,
- plotTop = chart.plotTop,
- plotWidth = chart.plotWidth,
- plotHeight = chart.plotHeight,
- plotBox = chart.plotBox,
- clipRect = chart.clipRect,
- clipBox = chart.clipBox,
- verb = 'animate';
- // Chart area
- if (!chartBackground) {
- chart.chartBackground = chartBackground = renderer.rect()
- .addClass('highcharts-background')
- .add();
- verb = 'attr';
- }
- if (!styledMode) {
- // Presentational
- chartBorderWidth = optionsChart.borderWidth || 0;
- mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
- bgAttr = {
- fill: chartBackgroundColor || 'none'
- };
- if (chartBorderWidth || chartBackground['stroke-width']) { // #980
- bgAttr.stroke = optionsChart.borderColor;
- bgAttr['stroke-width'] = chartBorderWidth;
- }
- chartBackground
- .attr(bgAttr)
- .shadow(optionsChart.shadow);
- } else {
- chartBorderWidth = mgn = chartBackground.strokeWidth();
- }
- chartBackground[verb]({
- x: mgn / 2,
- y: mgn / 2,
- width: chartWidth - mgn - chartBorderWidth % 2,
- height: chartHeight - mgn - chartBorderWidth % 2,
- r: optionsChart.borderRadius
- });
- // Plot background
- verb = 'animate';
- if (!plotBackground) {
- verb = 'attr';
- chart.plotBackground = plotBackground = renderer.rect()
- .addClass('highcharts-plot-background')
- .add();
- }
- plotBackground[verb](plotBox);
- if (!styledMode) {
- // Presentational attributes for the background
- plotBackground
- .attr({
- fill: plotBackgroundColor || 'none'
- })
- .shadow(optionsChart.plotShadow);
- // Create the background image
- if (plotBackgroundImage) {
- if (!plotBGImage) {
- chart.plotBGImage = renderer.image(
- plotBackgroundImage,
- plotLeft,
- plotTop,
- plotWidth,
- plotHeight
- ).add();
- } else {
- plotBGImage.animate(plotBox);
- }
- }
- }
- // Plot clip
- if (!clipRect) {
- chart.clipRect = renderer.clipRect(clipBox);
- } else {
- clipRect.animate({
- width: clipBox.width,
- height: clipBox.height
- });
- }
- // Plot area border
- verb = 'animate';
- if (!plotBorder) {
- verb = 'attr';
- chart.plotBorder = plotBorder = renderer.rect()
- .addClass('highcharts-plot-border')
- .attr({
- zIndex: 1 // Above the grid
- })
- .add();
- }
- if (!styledMode) {
- // Presentational
- plotBorder.attr({
- stroke: optionsChart.plotBorderColor,
- 'stroke-width': optionsChart.plotBorderWidth || 0,
- fill: 'none'
- });
- }
- plotBorder[verb](plotBorder.crisp({
- x: plotLeft,
- y: plotTop,
- width: plotWidth,
- height: plotHeight
- }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
- // reset
- chart.isDirtyBox = false;
- fireEvent(this, 'afterDrawChartBox');
- },
- /**
- * Detect whether a certain chart property is needed based on inspecting its
- * options and series. This mainly applies to the chart.inverted property,
- * and in extensions to the chart.angular and chart.polar properties.
- *
- * @private
- * @function Highcharts.Chart#propFromSeries
- */
- propFromSeries: function () {
- var chart = this,
- optionsChart = chart.options.chart,
- klass,
- seriesOptions = chart.options.series,
- i,
- value;
- ['inverted', 'angular', 'polar'].forEach(function (key) {
- // The default series type's class
- klass = seriesTypes[optionsChart.type ||
- optionsChart.defaultSeriesType];
- // Get the value from available chart-wide properties
- value =
- optionsChart[key] || // It is set in the options
- (klass && klass.prototype[key]); // The default series class
- // requires it
- // 4. Check if any the chart's series require it
- i = seriesOptions && seriesOptions.length;
- while (!value && i--) {
- klass = seriesTypes[seriesOptions[i].type];
- if (klass && klass.prototype[key]) {
- value = true;
- }
- }
- // Set the chart property
- chart[key] = value;
- });
- },
- /**
- * Internal function to link two or more series together, based on the
- * `linkedTo` option. This is done from `Chart.render`, and after
- * `Chart.addSeries` and `Series.remove`.
- *
- * @private
- * @function Highcharts.Chart#linkSeries
- *
- * @fires Highcharts.Chart#event:afterLinkSeries
- */
- linkSeries: function () {
- var chart = this,
- chartSeries = chart.series;
- // Reset links
- chartSeries.forEach(function (series) {
- series.linkedSeries.length = 0;
- });
- // Apply new links
- chartSeries.forEach(function (series) {
- var linkedTo = series.options.linkedTo;
- if (isString(linkedTo)) {
- if (linkedTo === ':previous') {
- linkedTo = chart.series[series.index - 1];
- } else {
- linkedTo = chart.get(linkedTo);
- }
- // #3341 avoid mutual linking
- if (linkedTo && linkedTo.linkedParent !== series) {
- linkedTo.linkedSeries.push(series);
- series.linkedParent = linkedTo;
- series.visible = pick(
- series.options.visible,
- linkedTo.options.visible,
- series.visible
- ); // #3879
- }
- }
- });
- fireEvent(this, 'afterLinkSeries');
- },
- /**
- * Render series for the chart.
- *
- * @private
- * @function Highcharts.Chart#renderSeries
- */
- renderSeries: function () {
- this.series.forEach(function (serie) {
- serie.translate();
- serie.render();
- });
- },
- /**
- * Render labels for the chart.
- *
- * @private
- * @function Highcharts.Chart#renderLabels
- */
- renderLabels: function () {
- var chart = this,
- labels = chart.options.labels;
- if (labels.items) {
- labels.items.forEach(function (label) {
- var style = extend(labels.style, label.style),
- x = pInt(style.left) + chart.plotLeft,
- y = pInt(style.top) + chart.plotTop + 12;
- // delete to prevent rewriting in IE
- delete style.left;
- delete style.top;
- chart.renderer.text(
- label.html,
- x,
- y
- )
- .attr({ zIndex: 2 })
- .css(style)
- .add();
- });
- }
- },
- /**
- * Render all graphics for the chart. Runs internally on initialization.
- *
- * @private
- * @function Highcharts.Chart#render
- */
- render: function () {
- var chart = this,
- axes = chart.axes,
- renderer = chart.renderer,
- options = chart.options,
- correction = 0, // correction for X axis labels
- tempWidth,
- tempHeight,
- redoHorizontal,
- redoVertical;
- // Title
- chart.setTitle();
- /**
- * The overview of the chart's series.
- *
- * @name Highcharts.Chart#legend
- * @type {Highcharts.Legend}
- */
- chart.legend = new Legend(chart, options.legend);
- // Get stacks
- if (chart.getStacks) {
- chart.getStacks();
- }
- // Get chart margins
- chart.getMargins(true);
- chart.setChartSize();
- // Record preliminary dimensions for later comparison
- tempWidth = chart.plotWidth;
- axes.some(function (axis) {
- if (
- axis.horiz &&
- axis.visible &&
- axis.options.labels.enabled &&
- axis.series.length
- ) {
- // 21 is the most common correction for X axis labels
- correction = 21;
- return true;
- }
- });
- // use Math.max to prevent negative plotHeight
- chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
- tempHeight = chart.plotHeight;
- // Get margins by pre-rendering axes
- axes.forEach(function (axis) {
- axis.setScale();
- });
- chart.getAxisMargins();
- // If the plot area size has changed significantly, calculate tick
- // positions again
- redoHorizontal = tempWidth / chart.plotWidth > 1.1;
- // Height is more sensitive, use lower threshold
- redoVertical = tempHeight / chart.plotHeight > 1.05;
- if (redoHorizontal || redoVertical) {
- axes.forEach(function (axis) {
- if (
- (axis.horiz && redoHorizontal) ||
- (!axis.horiz && redoVertical)
- ) {
- // update to reflect the new margins
- axis.setTickInterval(true);
- }
- });
- chart.getMargins(); // second pass to check for new labels
- }
- // Draw the borders and backgrounds
- chart.drawChartBox();
- // Axes
- if (chart.hasCartesianSeries) {
- axes.forEach(function (axis) {
- if (axis.visible) {
- axis.render();
- }
- });
- }
- // The series
- if (!chart.seriesGroup) {
- chart.seriesGroup = renderer.g('series-group')
- .attr({ zIndex: 3 })
- .add();
- }
- chart.renderSeries();
- // Labels
- chart.renderLabels();
- // Credits
- chart.addCredits();
- // Handle responsiveness
- if (chart.setResponsive) {
- chart.setResponsive();
- }
- // Set flag
- chart.hasRendered = true;
- },
- /**
- * Set a new credits label for the chart.
- *
- * @sample highcharts/credits/credits-update/
- * Add and update credits
- *
- * @function Highcharts.Chart#addCredits
- *
- * @param {Highcharts.CreditsOptions} options
- * A configuration object for the new credits.
- */
- addCredits: function (credits) {
- var chart = this;
- credits = merge(true, this.options.credits, credits);
- if (credits.enabled && !this.credits) {
- /**
- * The chart's credits label. The label has an `update` method that
- * allows setting new options as per the
- * [credits options set](https://api.highcharts.com/highcharts/credits).
- *
- * @name Highcharts.Chart#credits
- * @type {Highcharts.SVGElement}
- */
- this.credits = this.renderer.text(
- credits.text + (this.mapCredits || ''),
- 0,
- 0
- )
- .addClass('highcharts-credits')
- .on('click', function () {
- if (credits.href) {
- win.location.href = credits.href;
- }
- })
- .attr({
- align: credits.position.align,
- zIndex: 8
- });
- if (!chart.styledMode) {
- this.credits.css(credits.style);
- }
- this.credits
- .add()
- .align(credits.position);
- // Dynamically update
- this.credits.update = function (options) {
- chart.credits = chart.credits.destroy();
- chart.addCredits(options);
- };
- }
- },
- /**
- * Remove the chart and purge memory. This method is called internally
- * before adding a second chart into the same container, as well as on
- * window unload to prevent leaks.
- *
- * @sample highcharts/members/chart-destroy/
- * Destroy the chart from a button
- * @sample stock/members/chart-destroy/
- * Destroy with Highstock
- *
- * @function Highcharts.Chart#destroy
- *
- * @fires Highcharts.Chart#event:destroy
- */
- destroy: function () {
- var chart = this,
- axes = chart.axes,
- series = chart.series,
- container = chart.container,
- i,
- parentNode = container && container.parentNode;
- // fire the chart.destoy event
- fireEvent(chart, 'destroy');
- // Delete the chart from charts lookup array
- if (chart.renderer.forExport) {
- H.erase(charts, chart); // #6569
- } else {
- charts[chart.index] = undefined;
- }
- H.chartCount--;
- chart.renderTo.removeAttribute('data-highcharts-chart');
- // remove events
- removeEvent(chart);
- // ==== Destroy collections:
- // Destroy axes
- i = axes.length;
- while (i--) {
- axes[i] = axes[i].destroy();
- }
- // Destroy scroller & scroller series before destroying base series
- if (this.scroller && this.scroller.destroy) {
- this.scroller.destroy();
- }
- // Destroy each series
- i = series.length;
- while (i--) {
- series[i] = series[i].destroy();
- }
- // ==== Destroy chart properties:
- [
- 'title', 'subtitle', 'chartBackground', 'plotBackground',
- 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
- 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
- 'renderer'
- ].forEach(function (name) {
- var prop = chart[name];
- if (prop && prop.destroy) {
- chart[name] = prop.destroy();
- }
- });
- // Remove container and all SVG, check container as it can break in IE
- // when destroyed before finished loading
- if (container) {
- container.innerHTML = '';
- removeEvent(container);
- if (parentNode) {
- discardElement(container);
- }
- }
- // clean it all up
- objectEach(chart, function (val, key) {
- delete chart[key];
- });
- },
- /**
- * Prepare for first rendering after all data are loaded.
- *
- * @private
- * @function Highcharts.Chart#firstRender
- *
- * @fires Highcharts.Chart#event:beforeRender
- */
- firstRender: function () {
- var chart = this,
- options = chart.options;
- // Hook for oldIE to check whether the chart is ready to render
- if (chart.isReadyToRender && !chart.isReadyToRender()) {
- return;
- }
- // Create the container
- chart.getContainer();
- chart.resetMargins();
- chart.setChartSize();
- // Set the common chart properties (mainly invert) from the given series
- chart.propFromSeries();
- // get axes
- chart.getAxes();
- // Initialize the series
- (H.isArray(options.series) ? options.series : []).forEach( // #9680
- function (serieOptions) {
- chart.initSeries(serieOptions);
- }
- );
- chart.linkSeries();
- // Run an event after axes and series are initialized, but before
- // render. At this stage, the series data is indexed and cached in the
- // xData and yData arrays, so we can access those before rendering. Used
- // in Highstock.
- fireEvent(chart, 'beforeRender');
- // depends on inverted and on margins being set
- if (Pointer) {
- /**
- * The Pointer that keeps track of mouse and touch interaction.
- *
- * @memberof Highcharts.Chart
- * @name pointer
- * @type {Highcharts.Pointer}
- * @instance
- */
- chart.pointer = new Pointer(chart, options);
- }
- chart.render();
- // Fire the load event if there are no external images
- if (!chart.renderer.imgCount && chart.onload) {
- chart.onload();
- }
- // If the chart was rendered outside the top container, put it back in
- // (#3679)
- chart.temporaryDisplay(true);
- },
- /**
- * Internal function that runs on chart load, async if any images are loaded
- * in the chart. Runs the callbacks and triggers the `load` and `render`
- * events.
- *
- * @private
- * @function Highcharts.Chart#onload
- *
- * @fires Highcharts.Chart#event:load
- * @fires Highcharts.Chart#event:render
- */
- onload: function () {
- // Run callbacks
- [this.callback].concat(this.callbacks).forEach(function (fn) {
- // Chart destroyed in its own callback (#3600)
- if (fn && this.index !== undefined) {
- fn.apply(this, [this]);
- }
- }, this);
- fireEvent(this, 'load');
- fireEvent(this, 'render');
- // Set up auto resize, check for not destroyed (#6068)
- if (defined(this.index)) {
- this.setReflow(this.options.chart.reflow);
- }
- // Don't run again
- this.onload = null;
- }
- }); // end Chart
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- *
- * Highcharts feature to make the Y axis stay fixed when scrolling the chart
- * horizontally on mobile devices. Supports left and right side axes.
- */
- var addEvent = H.addEvent,
- Chart = H.Chart;
- /**
- * Options for a scrollable plot area. This feature provides a minimum width for
- * the plot area of the chart. If the width gets smaller than this, typically
- * on mobile devices, a native browser scrollbar is presented below the chart.
- * This scrollbar provides smooth scrolling for the contents of the plot area,
- * whereas the title, legend and axes are fixed.
- *
- * @sample {highcharts} highcharts/chart/scrollable-plotarea
- * Scrollable plot area
- *
- * @since 6.1.0
- * @product highcharts gantt
- * @apioption chart.scrollablePlotArea
- */
- /**
- * The minimum width for the plot area. If it gets smaller than this, the plot
- * area will become scrollable.
- *
- * @type {number}
- * @apioption chart.scrollablePlotArea.minWidth
- */
- /**
- * The initial scrolling position of the scrollable plot area. Ranges from 0 to
- * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
- * Typically we would use 1 if the chart has right aligned Y axes.
- *
- * @type {number}
- * @apioption chart.scrollablePlotArea.scrollPositionX
- */
- addEvent(Chart, 'afterSetChartSize', function (e) {
- var scrollablePlotArea = this.options.chart.scrollablePlotArea,
- scrollableMinWidth =
- scrollablePlotArea && scrollablePlotArea.minWidth,
- scrollablePixels;
- if (scrollableMinWidth && !this.renderer.forExport) {
- // The amount of pixels to scroll, the difference between chart
- // width and scrollable width
- this.scrollablePixels = scrollablePixels = Math.max(
- 0,
- scrollableMinWidth - this.chartWidth
- );
- if (scrollablePixels) {
- this.plotWidth += scrollablePixels;
- this.clipBox.width += scrollablePixels;
- if (!e.skipAxes) {
- this.axes.forEach(function (axis) {
- if (axis.side === 1) {
- // Get the plot lines right in getPlotLinePath,
- // temporarily set it to the adjusted plot width.
- axis.getPlotLinePath = function () {
- var right = this.right,
- path;
- this.right = right - axis.chart.scrollablePixels;
- path = H.Axis.prototype.getPlotLinePath.apply(
- this,
- arguments
- );
- this.right = right;
- return path;
- };
- } else {
- // Apply the corrected plotWidth
- axis.setAxisSize();
- axis.setAxisTranslation();
- }
- });
- }
- }
- }
- });
- addEvent(Chart, 'render', function () {
- if (this.scrollablePixels) {
- if (this.setUpScrolling) {
- this.setUpScrolling();
- }
- this.applyFixed();
- } else if (this.fixedDiv) { // Has been in scrollable mode
- this.applyFixed();
- }
- });
- /**
- * @private
- * @function Highcharts.Chart#setUpScrolling
- */
- Chart.prototype.setUpScrolling = function () {
- // Add the necessary divs to provide scrolling
- this.scrollingContainer = H.createElement('div', {
- 'className': 'highcharts-scrolling'
- }, {
- overflowX: 'auto',
- WebkitOverflowScrolling: 'touch'
- }, this.renderTo);
- this.innerContainer = H.createElement('div', {
- 'className': 'highcharts-inner-container'
- }, null, this.scrollingContainer);
- // Now move the container inside
- this.innerContainer.appendChild(this.container);
- // Don't run again
- this.setUpScrolling = null;
- };
- /**
- * @private
- * @function Highcharts.Chart#applyFixed
- */
- Chart.prototype.applyFixed = function () {
- var container = this.container,
- fixedRenderer,
- scrollableWidth,
- firstTime = !this.fixedDiv;
- // First render
- if (firstTime) {
- this.fixedDiv = H.createElement(
- 'div',
- {
- className: 'highcharts-fixed'
- },
- {
- position: 'absolute',
- overflow: 'hidden',
- pointerEvents: 'none',
- zIndex: 2
- },
- null,
- true
- );
- this.renderTo.insertBefore(
- this.fixedDiv,
- this.renderTo.firstChild
- );
- this.renderTo.style.overflow = 'visible';
- this.fixedRenderer = fixedRenderer = new H.Renderer(
- this.fixedDiv,
- 0,
- 0
- );
- // Mask
- this.scrollableMask = fixedRenderer.path()
- .attr({
- fill: H.color(
- this.options.chart.backgroundColor || '#fff'
- ).setOpacity(0.85).get(),
- zIndex: -1
- })
- .addClass('highcharts-scrollable-mask')
- .add();
- // These elements are moved over to the fixed renderer and stay fixed
- // when the user scrolls the chart.
- ([
- this.inverted ?
- '.highcharts-xaxis' :
- '.highcharts-yaxis',
- this.inverted ?
- '.highcharts-xaxis-labels' :
- '.highcharts-yaxis-labels',
- '.highcharts-contextbutton',
- '.highcharts-credits',
- '.highcharts-legend',
- '.highcharts-subtitle',
- '.highcharts-title',
- '.highcharts-legend-checkbox'
- ]).forEach(function (className) {
- [].forEach.call(
- container.querySelectorAll(className),
- function (elem) {
- (
- elem.namespaceURI === fixedRenderer.SVG_NS ?
- fixedRenderer.box :
- fixedRenderer.box.parentNode
- ).appendChild(elem);
- elem.style.pointerEvents = 'auto';
- }
- );
- });
- }
- // Set the size of the fixed renderer to the visible width
- this.fixedRenderer.setSize(
- this.chartWidth,
- this.chartHeight
- );
- // Increase the size of the scrollable renderer and background
- scrollableWidth = this.chartWidth + this.scrollablePixels;
- H.stop(this.container);
- this.container.style.width = scrollableWidth + 'px';
- this.renderer.boxWrapper.attr({
- width: scrollableWidth,
- height: this.chartHeight,
- viewBox: [0, 0, scrollableWidth, this.chartHeight].join(' ')
- });
- this.chartBackground.attr({ width: scrollableWidth });
- // Set scroll position
- if (firstTime) {
- var options = this.options.chart.scrollablePlotArea;
- if (options.scrollPositionX) {
- this.scrollingContainer.scrollLeft =
- this.scrollablePixels * options.scrollPositionX;
- }
- }
- // Mask behind the left and right side
- var axisOffset = this.axisOffset,
- maskTop = this.plotTop - axisOffset[0] - 1,
- maskBottom = this.plotTop + this.plotHeight + axisOffset[2],
- maskPlotRight = this.plotLeft + this.plotWidth -
- this.scrollablePixels;
- this.scrollableMask.attr({
- d: this.scrollablePixels ? [
- // Left side
- 'M', 0, maskTop,
- 'L', this.plotLeft - 1, maskTop,
- 'L', this.plotLeft - 1, maskBottom,
- 'L', 0, maskBottom,
- 'Z',
- // Right side
- 'M', maskPlotRight, maskTop,
- 'L', this.chartWidth, maskTop,
- 'L', this.chartWidth, maskBottom,
- 'L', maskPlotRight, maskBottom,
- 'Z'
- ] : ['M', 0, 0]
- });
- };
- }(Highcharts));
- (function (Highcharts) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Configuration hash for the data label and tooltip formatters.
- *
- * @interface Highcharts.PointLabelObject
- *//**
- * The point's current color.
- * @name Highcharts.PointLabelObject#color
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- *//**
- * The point's current color index, used in styled mode instead of `color`. The
- * color index is inserted in class names used for styling.
- * @name Highcharts.PointLabelObject#colorIndex
- * @type {number}
- *//**
- * The name of the related point.
- * @name Highcharts.PointLabelObject#key
- * @type {number|string}
- *//**
- * The percentage for related points in a stacked series or pies.
- * @name Highcharts.PointLabelObject#percentage
- * @type {number}
- *//**
- * The related point.
- * @name Highcharts.PointLabelObject#point
- * @type {Highcharts.Point}
- *//**
- * The related series.
- * @name Highcharts.PointLabelObject#series
- * @type {Highcharts.Series}
- *//**
- * The total of values in either a stack for stacked series, or a pie in a pie
- * series.
- * @name Highcharts.PointLabelObject#total
- * @type {number}
- *//**
- * For categorized axes this property holds the category name for the point. For
- * other axes it holds the X value.
- * @name Highcharts.PointLabelObject#x
- * @type {number|string}
- *//**
- * The y value of the point.
- * @name Highcharts.PointLabelObject#y
- * @type {number|undefined}
- */
- var Point,
- H = Highcharts,
- extend = H.extend,
- erase = H.erase,
- fireEvent = H.fireEvent,
- format = H.format,
- isArray = H.isArray,
- isNumber = H.isNumber,
- pick = H.pick,
- uniqueKey = H.uniqueKey,
- defined = H.defined,
- removeEvent = H.removeEvent;
- /**
- * The Point object. The point objects are generated from the `series.data`
- * configuration objects or raw numbers. They can be accessed from the
- * `Series.points` array. Other ways to instantiate points are through {@link
- * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
- *
- * @class
- * @name Highcharts.Point
- */
- Highcharts.Point = Point = function () {};
- Highcharts.Point.prototype = {
- /**
- * Initialize the point. Called internally based on the `series.data`
- * option.
- *
- * @function Highcharts.Point#init
- *
- * @param {Highcharts.Series} series
- * The series object containing this point.
- *
- * @param {number|object|Array<number|string>|null} options
- * The data in either number, array or object format.
- *
- * @param {number} [x]
- * Optionally, the X value of the point.
- *
- * @return {Highcharts.Point}
- * The Point instance.
- *
- * @fires Highcharts.Point#event:afterInit
- */
- init: function (series, options, x) {
- var point = this,
- colors,
- optionsChart = series.chart.options.chart,
- colorCount = optionsChart.colorCount,
- styledMode = series.chart.styledMode,
- colorIndex;
- /**
- * The series object associated with the point.
- *
- * @name Highcharts.Point#series
- * @type {Highcharts.Series}
- */
- point.series = series;
- /**
- * The point's current color.
- *
- * @name Highcharts.Point#color
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- */
- if (!styledMode) {
- point.color = series.color; // #3445
- }
- point.applyOptions(options, x);
- // Add a unique ID to the point if none is assigned
- point.id = defined(point.id) ? point.id : uniqueKey();
- if (series.options.colorByPoint) {
- if (!styledMode) {
- colors = series.options.colors || series.chart.options.colors;
- point.color = point.color || colors[series.colorCounter];
- colorCount = colors.length;
- }
- colorIndex = series.colorCounter;
- series.colorCounter++;
- // loop back to zero
- if (series.colorCounter === colorCount) {
- series.colorCounter = 0;
- }
- } else {
- colorIndex = series.colorIndex;
- }
- /**
- * The point's current color index, used in styled mode instead of
- * `color`. The color index is inserted in class names used for styling.
- *
- * @name Highcharts.Point#colorIndex
- * @type {number}
- */
- point.colorIndex = pick(point.colorIndex, colorIndex);
- series.chart.pointCount++;
- fireEvent(point, 'afterInit');
- return point;
- },
- /**
- * Apply the options containing the x and y data and possible some extra
- * properties. Called on point init or from point.update.
- *
- * @private
- * @function Highcharts.Point#applyOptions
- *
- * @param {number|object|Array<number|string>|null} options
- * The point options as defined in series.data.
- *
- * @param {number} [x]
- * Optionally, the x value.
- *
- * @return {Highcharts.Point}
- * The Point instance.
- */
- applyOptions: function (options, x) {
- var point = this,
- series = point.series,
- pointValKey = series.options.pointValKey || series.pointValKey;
- options = Point.prototype.optionsToObject.call(this, options);
- // copy options directly to point
- extend(point, options);
- /**
- * The point's options as applied in the initial configuration, or
- * extended through `Point.update`.
- * @name Highcharts.Point#options
- * @type {object}
- */
- point.options = point.options ?
- extend(point.options, options) :
- options;
- // Since options are copied into the Point instance, some accidental
- // options must be shielded (#5681)
- if (options.group) {
- delete point.group;
- }
- if (options.dataLabels) {
- delete point.dataLabels;
- }
- /**
- * The y value of the point.
- * @name Highcharts.Point#y
- * @type {number|undefined}
- */
- // For higher dimension series types. For instance, for ranges, point.y
- // is mapped to point.low.
- if (pointValKey) {
- point.y = point[pointValKey];
- }
- point.isNull = pick(
- point.isValid && !point.isValid(),
- point.x === null || !isNumber(point.y, true)
- ); // #3571, check for NaN
- // The point is initially selected by options (#5777)
- if (point.selected) {
- point.state = 'select';
- }
- /**
- * The x value of the point.
- * @name Highcharts.Point#x
- * @type {number}
- */
- // If no x is set by now, get auto incremented value. All points must
- // have an x value, however the y value can be null to create a gap in
- // the series
- if (
- 'name' in point &&
- x === undefined &&
- series.xAxis &&
- series.xAxis.hasNames
- ) {
- point.x = series.xAxis.nameToX(point);
- }
- if (point.x === undefined && series) {
- if (x === undefined) {
- point.x = series.autoIncrement(point);
- } else {
- point.x = x;
- }
- }
- return point;
- },
- /**
- * Set a value in an object, on the property defined by key. The key
- * supports nested properties using dot notation. The function modifies the
- * input object and does not make a copy.
- *
- * @function Highcharts.Point#setNestedProperty
- *
- * @param {object} object
- * The object to set the value on.
- *
- * @param {*} value
- * The value to set.
- *
- * @param {string} key
- * Key to the property to set.
- *
- * @return {object}
- * The modified object.
- */
- setNestedProperty: function (object, value, key) {
- var nestedKeys = key.split('.');
- nestedKeys.reduce(function (result, key, i, arr) {
- var isLastKey = arr.length - 1 === i;
- result[key] = (
- isLastKey ?
- value :
- (H.isObject(result[key], true) ? result[key] : {})
- );
- return result[key];
- }, object);
- return object;
- },
- /**
- * Transform number or array configs into objects. Used internally to unify
- * the different configuration formats for points. For example, a simple
- * number `10` in a line series will be transformed to `{ y: 10 }`, and an
- * array config like `[1, 10]` in a scatter series will be transformed to
- * `{ x: 1, y: 10 }`.
- *
- * @function Highcharts.Point#optionsToObject
- *
- * @param {number|object|Array<number|string>|null} options
- * The input option.
- *
- * @return {object}
- * Transformed options.
- */
- optionsToObject: function (options) {
- var ret = {},
- series = this.series,
- keys = series.options.keys,
- pointArrayMap = keys || series.pointArrayMap || ['y'],
- valueCount = pointArrayMap.length,
- firstItemType,
- i = 0,
- j = 0;
- if (isNumber(options) || options === null) {
- ret[pointArrayMap[0]] = options;
- } else if (isArray(options)) {
- // with leading x value
- if (!keys && options.length > valueCount) {
- firstItemType = typeof options[0];
- if (firstItemType === 'string') {
- ret.name = options[0];
- } else if (firstItemType === 'number') {
- ret.x = options[0];
- }
- i++;
- }
- while (j < valueCount) {
- // Skip undefined positions for keys
- if (!keys || options[i] !== undefined) {
- if (pointArrayMap[j].indexOf('.') > 0) {
- // Handle nested keys, e.g. ['color.pattern.image']
- // Avoid function call unless necessary.
- H.Point.prototype.setNestedProperty(
- ret, options[i], pointArrayMap[j]
- );
- } else {
- ret[pointArrayMap[j]] = options[i];
- }
- }
- i++;
- j++;
- }
- } else if (typeof options === 'object') {
- ret = options;
- // This is the fastest way to detect if there are individual point
- // dataLabels that need to be considered in drawDataLabels. These
- // can only occur in object configs.
- if (options.dataLabels) {
- series._hasPointLabels = true;
- }
- // Same approach as above for markers
- if (options.marker) {
- series._hasPointMarkers = true;
- }
- }
- return ret;
- },
- /**
- * Get the CSS class names for individual points. Used internally where the
- * returned value is set on every point.
- *
- * @function Highcharts.Point#getClassName
- *
- * @return {string}
- * The class names.
- */
- getClassName: function () {
- return 'highcharts-point' +
- (this.selected ? ' highcharts-point-select' : '') +
- (this.negative ? ' highcharts-negative' : '') +
- (this.isNull ? ' highcharts-null-point' : '') +
- (this.colorIndex !== undefined ? ' highcharts-color-' +
- this.colorIndex : '') +
- (this.options.className ? ' ' + this.options.className : '') +
- (this.zone && this.zone.className ? ' ' +
- this.zone.className.replace('highcharts-negative', '') : '');
- },
- /**
- * In a series with `zones`, return the zone that the point belongs to.
- *
- * @function Highcharts.Point#getZone
- *
- * @return {Highcharts.PlotSeriesZonesOptions}
- * The zone item.
- */
- getZone: function () {
- var series = this.series,
- zones = series.zones,
- zoneAxis = series.zoneAxis || 'y',
- i = 0,
- zone;
- zone = zones[i];
- while (this[zoneAxis] >= zone.value) {
- zone = zones[++i];
- }
- // For resetting or reusing the point (#8100)
- if (!this.nonZonedColor) {
- this.nonZonedColor = this.color;
- }
- if (zone && zone.color && !this.options.color) {
- this.color = zone.color;
- } else {
- this.color = this.nonZonedColor;
- }
- return zone;
- },
- /**
- * Destroy a point to clear memory. Its reference still stays in
- * `series.data`.
- *
- * @private
- * @function Highcharts.Point#destroy
- */
- destroy: function () {
- var point = this,
- series = point.series,
- chart = series.chart,
- hoverPoints = chart.hoverPoints,
- prop;
- chart.pointCount--;
- if (hoverPoints) {
- point.setState();
- erase(hoverPoints, point);
- if (!hoverPoints.length) {
- chart.hoverPoints = null;
- }
- }
- if (point === chart.hoverPoint) {
- point.onMouseOut();
- }
- // Remove all events and elements
- if (point.graphic || point.dataLabel || point.dataLabels) {
- removeEvent(point);
- point.destroyElements();
- }
- if (point.legendItem) { // pies have legend items
- chart.legend.destroyItem(point);
- }
- for (prop in point) {
- point[prop] = null;
- }
- },
- /**
- * Destroy SVG elements associated with the point.
- *
- * @private
- * @function Highcharts.Point#destroyElements
- */
- destroyElements: function () {
- var point = this,
- props = [
- 'graphic',
- 'dataLabel',
- 'dataLabelUpper',
- 'connector',
- 'shadowGroup'
- ],
- prop,
- i = 6;
- while (i--) {
- prop = props[i];
- if (point[prop]) {
- point[prop] = point[prop].destroy();
- }
- }
- // Handle point.dataLabels and point.connectors
- if (point.dataLabels) {
- point.dataLabels.forEach(function (label) {
- if (label.element) {
- label.destroy();
- }
- });
- delete point.dataLabels;
- }
- if (point.connectors) {
- point.connectors.forEach(function (connector) {
- if (connector.element) {
- connector.destroy();
- }
- });
- delete point.connectors;
- }
- },
- /**
- * Return the configuration hash needed for the data label and tooltip
- * formatters.
- *
- * @function Highcharts.Point#getLabelConfig
- *
- * @return {Highcharts.PointLabelObject}
- * Abstract object used in formatters and formats.
- */
- getLabelConfig: function () {
- return {
- x: this.category,
- y: this.y,
- color: this.color,
- colorIndex: this.colorIndex,
- key: this.name || this.category,
- series: this.series,
- point: this,
- percentage: this.percentage,
- total: this.total || this.stackTotal
- };
- },
- /**
- * Extendable method for formatting each point's tooltip line.
- *
- * @function Highcharts.Point#tooltipFormatter
- *
- * @param {string} pointFormat
- * The point format.
- *
- * @return {string}
- * A string to be concatenated in to the common tooltip text.
- */
- tooltipFormatter: function (pointFormat) {
- // Insert options for valueDecimals, valuePrefix, and valueSuffix
- var series = this.series,
- seriesTooltipOptions = series.tooltipOptions,
- valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''),
- valuePrefix = seriesTooltipOptions.valuePrefix || '',
- valueSuffix = seriesTooltipOptions.valueSuffix || '';
- // Replace default point style with class name
- if (series.chart.styledMode) {
- pointFormat = series.chart.tooltip.styledModeFormat(pointFormat);
- }
- // Loop over the point array map and replace unformatted values with
- // sprintf formatting markup
- (series.pointArrayMap || ['y']).forEach(function (key) {
- key = '{point.' + key; // without the closing bracket
- if (valuePrefix || valueSuffix) {
- pointFormat = pointFormat.replace(
- RegExp(key + '}', 'g'),
- valuePrefix + key + '}' + valueSuffix
- );
- }
- pointFormat = pointFormat.replace(
- RegExp(key + '}', 'g'),
- key + ':,.' + valueDecimals + 'f}'
- );
- });
- return format(pointFormat, {
- point: this,
- series: this.series
- }, series.chart.time);
- },
- /**
- * Fire an event on the Point object.
- *
- * @private
- * @function Highcharts.Point#firePointEvent
- *
- * @param {string} eventType
- * Type of the event.
- *
- * @param {object} eventArgs
- * Additional event arguments.
- *
- * @param {Function} defaultFunction
- * Default event handler.
- *
- * @fires Highcharts.Point#event:*
- */
- firePointEvent: function (eventType, eventArgs, defaultFunction) {
- var point = this,
- series = this.series,
- seriesOptions = series.options;
- // load event handlers on demand to save time on mouseover/out
- if (
- seriesOptions.point.events[eventType] ||
- (
- point.options &&
- point.options.events &&
- point.options.events[eventType]
- )
- ) {
- this.importEvents();
- }
- // add default handler if in selection mode
- if (eventType === 'click' && seriesOptions.allowPointSelect) {
- defaultFunction = function (event) {
- // Control key is for Windows, meta (= Cmd key) for Mac, Shift
- // for Opera.
- if (point.select) { // #2911
- point.select(
- null,
- event.ctrlKey || event.metaKey || event.shiftKey
- );
- }
- };
- }
- fireEvent(this, eventType, eventArgs, defaultFunction);
- },
- /**
- * For categorized axes this property holds the category name for the
- * point. For other axes it holds the X value.
- *
- * @name Highcharts.Point#category
- * @type {number|string}
- */
- /**
- * The name of the point. The name can be given as the first position of the
- * point configuration array, or as a `name` property in the configuration:
- *
- * @example
- * // Array config
- * data: [
- * ['John', 1],
- * ['Jane', 2]
- * ]
- *
- * // Object config
- * data: [{
- * name: 'John',
- * y: 1
- * }, {
- * name: 'Jane',
- * y: 2
- * }]
- *
- * @name Highcharts.Point#name
- * @type {string}
- */
- /**
- * The percentage for points in a stacked series or pies.
- *
- * @name Highcharts.Point#percentage
- * @type {number}
- */
- /**
- * The total of values in either a stack for stacked series, or a pie in a
- * pie series.
- *
- * @name Highcharts.Point#total
- * @type {number}
- */
- /**
- * For certain series types, like pie charts, where individual points can
- * be shown or hidden.
- *
- * @name Highcharts.Point#visible
- * @type {boolean}
- */
- visible: true
- };
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Function callback when a series has been animated.
- *
- * @callback Highcharts.SeriesAfterAnimateCallbackFunction
- *
- * @param {Highcharts.Series} this
- * The series where the event occured.
- *
- * @param {Highcharts.SeriesAfterAnimateEventObject} event
- * Event arguments.
- */
- /**
- * Event information regarding completed animation of a series.
- *
- * @interface Highcharts.SeriesAfterAnimateEventObject
- *//**
- * Animated series.
- * @name Highcharts.SeriesAfterAnimateEventObject#target
- * @type {Highcharts.Series}
- *//**
- * Event type.
- * @name Highcharts.SeriesAfterAnimateEventObject#type
- * @type {"afterAnimate"}
- */
- /**
- * Function callback when the checkbox next to the series' name in the legend is
- * clicked.
- *
- * @callback Highcharts.SeriesCheckboxClickCallbackFunction
- *
- * @param {Highcharts.Series} this
- * The series where the event occured.
- *
- * @param {Highcharts.SeriesCheckboxClickEventObject} event
- * Event arguments.
- */
- /**
- * Event information regarding check of a series box.
- *
- * @interface Highcharts.SeriesCheckboxClickEventObject
- *//**
- * Whether the box has been checked.
- * @name Highcharts.SeriesCheckboxClickEventObject#checked
- * @type {boolean}
- *//**
- * Related series.
- * @name Highcharts.SeriesCheckboxClickEventObject#item
- * @type {Highcharts.Series}
- *//**
- * Related series.
- * @name Highcharts.SeriesCheckboxClickEventObject#target
- * @type {Highcharts.Series}
- *//**
- * Event type.
- * @name Highcharts.SeriesCheckboxClickEventObject#type
- * @type {"checkboxClick"}
- */
- /**
- * Function callback when a series is clicked. Return false to cancel toogle
- * actions.
- *
- * @callback Highcharts.SeriesClickCallbackFunction
- *
- * @param {Highcharts.Series} this
- * The series where the event occured.
- *
- * @param {Highcharts.SeriesClickEventObject} event
- * Event arguments.
- */
- /**
- * Common information for a click event on a series.
- *
- * @interface Highcharts.SeriesClickEventObject
- * @implements {global.Event}
- *//**
- * Nearest point on the graph.
- * @name Highcharts.SeriesClickEventObject#point
- * @type {Highcharts.Point}
- */
- /**
- * @interface Highcharts.SeriesDataLabelsFormatterContextObject
- *//**
- * @name Highcharts.SeriesDataLabelsFormatterContextObject#point
- * @type {Highcharts.Point}
- */
- /**
- * Gets fired when the series is hidden after chart generation time, either by
- * clicking the legend item or by calling `.hide()`.
- *
- * @callback Highcharts.SeriesHideCallbackFunction
- *
- * @param {Highcharts.Series} this
- * The series where the event occured.
- *
- * @param {global.Event} event
- * The event that occured.
- */
- /**
- * Gets fired when the legend item belonging to the series is clicked. The
- * default action is to toggle the visibility of the series. This can be
- * prevented by returning `false` or calling `event.preventDefault()`.
- *
- * @callback Highcharts.SeriesLegendItemClickCallbackFunction
- *
- * @param {Highcharts.Series} this
- * The series where the event occured.
- *
- * @param {Highcharts.SeriesLegendItemClickEventObject} event
- * The event that occured.
- */
- /**
- * Information about the event.
- *
- * @interface Highcharts.SeriesLegendItemClickEventObject
- *//**
- * Related browser event.
- * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
- * @type {Highcharts.PointerEvent}
- *//**
- * Prevent the default action of toggle the visibility of the series.
- * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
- * @type {Function}
- *//**
- * Related series.
- * @name Highcharts.SeriesCheckboxClickEventObject#target
- * @type {Highcharts.Series}
- *//**
- * Event type.
- * @name Highcharts.SeriesCheckboxClickEventObject#type
- * @type {"checkboxClick"}
- */
- /**
- * Gets fired when the mouse leaves the graph.
- *
- * @callback Highcharts.SeriesMouseOutCallbackFunction
- *
- * @param {Highcharts.Series} this
- * Series where the event occured.
- *
- * @param {global.Event} event
- * Event that occured.
- */
- /**
- * Gets fired when the mouse enters the graph.
- *
- * @callback Highcharts.SeriesMouseOverCallbackFunction
- *
- * @param {Highcharts.Series} this
- * Series where the event occured.
- *
- * @param {global.Event} event
- * Event that occured.
- */
- /**
- * Translation and scale for the plot area of a series.
- *
- * @interface Highcharts.SeriesPlotBoxObject
- *//**
- * @name Highcharts.SeriesPlotBoxObject#translateX
- * @type {number}
- *//**
- * @name Highcharts.SeriesPlotBoxObject#translateY
- * @type {number}
- *//**
- * @name Highcharts.SeriesPlotBoxObject#scaleX
- * @type {number}
- *//**
- * @name Highcharts.SeriesPlotBoxObject#scaleY
- * @type {number}
- */
- /**
- * Function callback when a series point is clicked. Return false to cancel the
- * action.
- *
- * @callback Highcharts.SeriesPointClickCallbackFunction
- *
- * @param {Highcharts.Point} this
- * The point where the event occured.
- *
- * @param {Highcharts.SeriesPointClickEventObject} event
- * Event arguments.
- */
- /**
- * Common information for a click event on a series point.
- *
- * @interface Highcharts.SeriesPointClickEventObject
- * @implements {Highcharts.PointerEventObject}
- *//**
- * Clicked point.
- * @name Highcharts.SeriesPointClickEventObject#point
- * @type {Highcharts.Point}
- */
- /**
- * Gets fired when the mouse leaves the area close to the point.
- *
- * @callback Highcharts.SeriesPointMouseOutCallbackFunction
- *
- * @param {Highcharts.Point} this
- * Point where the event occured.
- *
- * @param {global.Event} event
- * Event that occured.
- */
- /**
- * Gets fired when the mouse enters the area close to the point.
- *
- * @callback Highcharts.SeriesPointMouseOverCallbackFunction
- *
- * @param {Highcharts.Point} this
- * Point where the event occured.
- *
- * @param {global.Event} event
- * Event that occured.
- */
- /**
- * Gets fired when the point is removed using the `.remove()` method.
- *
- * @callback Highcharts.SeriesPointRemoveCallbackFunction
- *
- * @param {Highcharts.Point} this
- * Point where the event occured.
- *
- * @param {global.Event} event
- * Event that occured.
- */
- /**
- * Gets fired when the point is selected either programmatically or following a
- * click on the point.
- *
- * @callback Highcharts.SeriesPointSelectCallbackFunction
- *
- * @param {Highcharts.Point} this
- * Point where the event occured.
- *
- * @param {Highcharts.SeriesPointSelectEventObject} event
- * Event that occured.
- */
- /**
- * Information about the select event.
- *
- * @interface Highcharts.SeriesPointSelectEventObject
- * @implements {global.Event}
- *//**
- * @name Highcharts.SeriesPointSelectEventObject#accumulate
- * @type {boolean}
- */
- /**
- * Fires when the point is unselected either programmatically or following a
- * click on the point.
- *
- * @callback Highcharts.SeriesPointUnselectCallbackFunction
- *
- * @param {Highcharts.Point} this
- * Point where the event occured.
- *
- * @param {Highcharts.SeriesPointUnselectEventObject} event
- * Event that occured.
- */
- /**
- * Information about the unselect event.
- *
- * @interface Highcharts.SeriesPointUnselectEventObject
- * @implements {global.Event}
- *//**
- * @name Highcharts.SeriesPointUnselectEventObject#accumulate
- * @type {boolean}
- */
- /**
- * Gets fired when the point is updated programmatically through the `.update()`
- * method.
- *
- * @callback Highcharts.SeriesPointUpdateCallbackFunction
- *
- * @param {Highcharts.Point} this
- * Point where the event occured.
- *
- * @param {Highcharts.SeriesPointUpdateEventObject} event
- * Event that occured.
- */
- /**
- * Information about the update event.
- *
- * @interface Highcharts.SeriesPointUpdateEventObject
- * @implements {global.Event}
- *//**
- * Options data of the update event.
- * @name Highcharts.SeriesPointUpdateEventObject#options
- * @type {number|object|Array<(number|string)>|null}
- */
- /**
- * Gets fired when the series is shown after chart generation time, either by
- * clicking the legend item or by calling `.show()`.
- *
- * @callback Highcharts.SeriesShowCallbackFunction
- *
- * @param {Highcharts.Series} this
- * Series where the event occured.
- *
- * @param {global.Event} event
- * Event that occured.
- */
- var addEvent = H.addEvent,
- animObject = H.animObject,
- arrayMax = H.arrayMax,
- arrayMin = H.arrayMin,
- correctFloat = H.correctFloat,
- defaultOptions = H.defaultOptions,
- defaultPlotOptions = H.defaultPlotOptions,
- defined = H.defined,
- erase = H.erase,
- extend = H.extend,
- fireEvent = H.fireEvent,
- isArray = H.isArray,
- isNumber = H.isNumber,
- isString = H.isString,
- LegendSymbolMixin = H.LegendSymbolMixin, // @todo add as a requirement
- merge = H.merge,
- objectEach = H.objectEach,
- pick = H.pick,
- Point = H.Point, // @todo add as a requirement
- removeEvent = H.removeEvent,
- splat = H.splat,
- SVGElement = H.SVGElement,
- syncTimeout = H.syncTimeout,
- win = H.win;
- /**
- * This is the base series prototype that all other series types inherit from.
- * A new series is initialized either through the
- * [series](https://api.highcharts.com/highcharts/series)
- * option structure, or after the chart is initialized, through
- * {@link Highcharts.Chart#addSeries}.
- *
- * The object can be accessed in a number of ways. All series and point event
- * handlers give a reference to the `series` object. The chart object has a
- * {@link Highcharts.Chart#series|series} property that is a collection of all
- * the chart's series. The point objects and axis objects also have the same
- * reference.
- *
- * Another way to reference the series programmatically is by `id`. Add an id
- * in the series configuration options, and get the series object by
- * {@link Highcharts.Chart#get}.
- *
- * Configuration options for the series are given in three levels. Options for
- * all series in a chart are given in the
- * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
- * object. Then options for all series of a specific type
- * are given in the plotOptions of that type, for example `plotOptions.line`.
- * Next, options for one single series are given in the series array, or as
- * arguments to `chart.addSeries`.
- *
- * The data in the series is stored in various arrays.
- *
- * - First, `series.options.data` contains all the original config options for
- * each point whether added by options or methods like `series.addPoint`.
- *
- * - Next, `series.data` contains those values converted to points, but in case
- * the series data length exceeds the `cropThreshold`, or if the data is
- * grouped, `series.data` doesn't contain all the points. It only contains the
- * points that have been created on demand.
- *
- * - Then there's `series.points` that contains all currently visible point
- * objects. In case of cropping, the cropped-away points are not part of this
- * array. The `series.points` array starts at `series.cropStart` compared to
- * `series.data` and `series.options.data`. If however the series data is
- * grouped, these can't be correlated one to one.
- *
- * - `series.xData` and `series.processedXData` contain clean x values,
- * equivalent to `series.data` and `series.points`.
- *
- * - `series.yData` and `series.processedYData` contain clean y values,
- * equivalent to `series.data` and `series.points`.
- *
- * @class
- * @name Highcharts.Series
- *
- * @param {Highcharts.Chart} chart
- * The chart instance.
- *
- * @param {Highcharts.SeriesOptionsType|object} options
- * The series options.
- *//**
- * The line series is the base type and is therefor the series base prototype.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.line
- *
- * @augments Highcharts.Series
- */
- H.Series = H.seriesType(
- 'line',
- /**
- * Series options for specific data and the data itself. In TypeScript you
- * have to cast the series options to specific series types, to get all
- * possible options for a series.
- *
- * @example
- * // TypeScript example
- * Highcharts.chart('container', {
- * series: [{
- * color: '#06C',
- * data: [[0, 1], [2, 3]]
- * } as Highcharts.SeriesLineOptions ]
- * });
- *
- * @type {Array<*>}
- * @apioption series
- */
- /**
- * An id for the series. This can be used after render time to get a pointer
- * to the series object through `chart.get()`.
- *
- * @sample {highcharts} highcharts/plotoptions/series-id/
- * Get series by id
- *
- * @type {string}
- * @since 1.2.0
- * @apioption series.id
- */
- /**
- * The index of the series in the chart, affecting the internal index in the
- * `chart.series` array, the visible Z index as well as the order in the
- * legend.
- *
- * @type {number}
- * @since 2.3.0
- * @apioption series.index
- */
- /**
- * The sequential index of the series in the legend.
- *
- * @see [legend.reversed](#legend.reversed),
- * [yAxis.reversedStacks](#yAxis.reversedStacks)
- *
- * @sample {highcharts|highstock} highcharts/series/legendindex/
- * Legend in opposite order
- *
- * @type {number}
- * @apioption series.legendIndex
- */
- /**
- * The name of the series as shown in the legend, tooltip etc.
- *
- * @sample {highcharts} highcharts/series/name/
- * Series name
- * @sample {highmaps} maps/demo/category-map/
- * Series name
- *
- * @type {string}
- * @apioption series.name
- */
- /**
- * This option allows grouping series in a stacked chart. The stack option
- * can be a string or anything else, as long as the grouped series' stack
- * options match each other after conversion into a string.
- *
- * @sample {highcharts} highcharts/series/stack/
- * Stacked and grouped columns
- *
- * @type {string|object}
- * @since 2.1
- * @product highcharts highstock
- * @apioption series.stack
- */
- /**
- * The type of series, for example `line` or `column`. By default, the
- * series type is inherited from [chart.type](#chart.type), so unless the
- * chart is a combination of series types, there is no need to set it on the
- * series level.
- *
- * In TypeScript instead the `type` option must always be set.
- *
- * @sample {highcharts} highcharts/series/type/
- * Line and column in the same chart
- * @sample {highmaps} maps/demo/mapline-mappoint/
- * Multiple types in the same map
- *
- * @type {string}
- * @apioption series.type
- */
- /**
- * When using dual or multiple x axes, this number defines which xAxis the
- * particular series is connected to. It refers to either the
- * {@link #xAxis.id|axis id}
- * or the index of the axis in the xAxis array, with 0 being the first.
- *
- * @type {number|string}
- * @default 0
- * @product highcharts highstock
- * @apioption series.xAxis
- */
- /**
- * When using dual or multiple y axes, this number defines which yAxis the
- * particular series is connected to. It refers to either the
- * {@link #yAxis.id|axis id}
- * or the index of the axis in the yAxis array, with 0 being the first.
- *
- * @sample {highcharts} highcharts/series/yaxis/
- * Apply the column series to the secondary Y axis
- *
- * @type {number|string}
- * @default 0
- * @product highcharts highstock
- * @apioption series.yAxis
- */
- /**
- * Define the visual z index of the series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
- * With no z index, the series defined last are on top
- * @sample {highcharts} highcharts/plotoptions/series-zindex/
- * With a z index, the series with the highest z index is on top
- * @sample {highstock} highcharts/plotoptions/series-zindex-default/
- * With no z index, the series defined last are on top
- * @sample {highstock} highcharts/plotoptions/series-zindex/
- * With a z index, the series with the highest z index is on top
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption series.zIndex
- */
- null,
- /**
- * General options for all series types.
- *
- * @optionparent plotOptions.series
- */
- { // base series options
- /**
- * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
- * of a line graph. Round means that lines are rounded in the ends and
- * bends.
- *
- * @type {string}
- * @validvalue ["round", "butt", "square"]
- * @default round
- * @since 3.0.7
- * @apioption plotOptions.line.linecap
- */
- /**
- * Pixel width of the graph line.
- *
- * @see In styled mode, the line stroke-width can be set with the
- * `.highcharts-graph` class name.
- *
- * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
- * On all series
- * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
- * On one single series
- *
- * @product highcharts highstock
- */
- lineWidth: 2,
- /**
- * For some series, there is a limit that shuts down initial animation
- * by default when the total number of points in the chart is too high.
- * For example, for a column chart and its derivatives, animation does
- * not run if there is more than 250 points totally. To disable this
- * cap, set `animationLimit` to `Infinity`.
- *
- * @type {number}
- * @apioption plotOptions.series.animationLimit
- */
- /**
- * Allow this series' points to be selected by clicking on the graphic
- * (columns, point markers, pie slices, map areas etc).
- *
- * @see {@link Highcharts.Chart#getSelectedPoints}.
- *
- * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
- * Line
- * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
- * Column
- * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
- * Pie
- * @sample {highmaps} maps/plotoptions/series-allowpointselect/
- * Map area
- * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
- * Map bubble
- *
- * @since 1.2.0
- */
- allowPointSelect: false,
- /**
- * If true, a checkbox is displayed next to the legend item to allow
- * selecting the series. The state of the checkbox is determined by
- * the `selected` option.
- *
- * @productdesc {highmaps}
- * Note that if a `colorAxis` is defined, the color axis is represented
- * in the legend, not the series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
- * Show select box
- *
- * @since 1.2.0
- */
- showCheckbox: false,
- /**
- * Enable or disable the initial animation when a series is displayed.
- * The animation can also be set as a configuration object. Please
- * note that this option only applies to the initial animation of the
- * series itself. For other animations, see [chart.animation](
- * #chart.animation) and the animation parameter under the API methods.
- * The following properties are supported:
- *
- * - `duration`: The duration of the animation in milliseconds.
- *
- * - `easing`: Can be a string reference to an easing function set on
- * the `Math` object or a function. See the _Custom easing function_
- * demo below.
- *
- * Due to poor performance, animation is disabled in old IE browsers
- * for several chart types.
- *
- * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
- * Animation disabled
- * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
- * Slower animation
- * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
- * Custom easing function
- * @sample {highstock} stock/plotoptions/animation-slower/
- * Slower animation
- * @sample {highstock} stock/plotoptions/animation-easing/
- * Custom easing function
- * @sample {highmaps} maps/plotoptions/series-animation-true/
- * Animation enabled on map series
- * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
- * Disabled on mapbubble series
- *
- * @type {boolean|Highcharts.AnimationOptionsObject}
- * @default {highcharts} true
- * @default {highstock} true
- * @default {highmaps} false
- */
- animation: {
- /**
- * @type {number}
- * @default 1000
- * @apioption plotOptions.series.animation.duration
- */
- duration: 1000
- },
- /**
- * An additional class name to apply to the series' graphical elements.
- * This option does not replace default class names of the graphical
- * element.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption plotOptions.series.className
- */
- /**
- * Disable this option to allow series rendering in the whole plotting
- * area.
- *
- * **Note:** Clipping should be always enabled when
- * [chart.zoomType](#chart.zoomType) is set
- *
- * @sample {highcharts} highcharts/plotoptions/series-clip/
- * Disabled clipping
- *
- * @default true
- * @type {boolean}
- * @since 3.0.0
- * @apioption plotOptions.series.clip
- */
- /**
- * The main color of the series. In line type series it applies to the
- * line and the point markers unless otherwise specified. In bar type
- * series it applies to the bars unless a color is specified per point.
- * The default value is pulled from the `options.colors` array.
- *
- * In styled mode, the color can be defined by the
- * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
- * color can be set with the `.highcharts-series`,
- * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
- * `.highcharts-series-{n}` class, or individual classes given by the
- * `className` option.
- *
- * @productdesc {highmaps}
- * In maps, the series color is rarely used, as most choropleth maps use
- * the color to denote the value of each point. The series color can
- * however be used in a map with multiple series holding categorized
- * data.
- *
- * @sample {highcharts} highcharts/plotoptions/series-color-general/
- * General plot option
- * @sample {highcharts} highcharts/plotoptions/series-color-specific/
- * One specific series
- * @sample {highcharts} highcharts/plotoptions/series-color-area/
- * Area color
- * @sample {highcharts} highcharts/series/infographic/
- * Pattern fill
- * @sample {highmaps} maps/demo/category-map/
- * Category map by multiple series
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @apioption plotOptions.series.color
- */
- /**
- * Styled mode only. A specific color index to use for the series, so
- * its graphic representations are given the class name
- * `highcharts-color-{n}`.
- *
- * @type {number}
- * @since 5.0.0
- * @apioption plotOptions.series.colorIndex
- */
- /**
- * Whether to connect a graph line across null points, or render a gap
- * between the two points on either side of the null.
- *
- * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
- * False by default
- * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
- * True
- *
- * @type {boolean}
- * @default false
- * @product highcharts highstock
- * @apioption plotOptions.series.connectNulls
- */
- /**
- * You can set the cursor to "pointer" if you have click events attached
- * to the series, to signal to the user that the points and lines can
- * be clicked.
- *
- * In styled mode, the series cursor can be set with the same classes
- * as listed under [series.color](#plotOptions.series.color).
- *
- * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
- * On line graph
- * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
- * On columns
- * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
- * On scatter markers
- * @sample {highstock} stock/plotoptions/cursor/
- * Pointer on a line graph
- * @sample {highmaps} maps/plotoptions/series-allowpointselect/
- * Map area
- * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
- * Map bubble
- *
- * @type {string|Highcharts.CursorType}
- * @apioption plotOptions.series.cursor
- */
- /**
- * A name for the dash style to use for the graph, or for some series
- * types the outline of each shape.
- *
- * In styled mode, the
- * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
- * can be set with the same classes as listed under
- * [series.color](#plotOptions.series.color).
- *
- * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
- * Possible values demonstrated
- * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
- * Chart suitable for printing in black and white
- * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
- * Possible values demonstrated
- * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
- * Possible values demonstrated
- * @sample {highmaps} maps/plotoptions/series-dashstyle/
- * Dotted borders on a map
- *
- * @type {Highcharts.DashStyleType}
- * @default Solid
- * @since 2.1
- * @apioption plotOptions.series.dashStyle
- */
- /**
- * Requires the Accessibility module.
- *
- * A description of the series to add to the screen reader information
- * about the series.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption plotOptions.series.description
- */
- /**
- * Enable or disable the mouse tracking for a specific series. This
- * includes point tooltips and click events on graphs and points. For
- * large datasets it improves performance.
- *
- * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
- * No mouse tracking
- * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
- * No mouse tracking
- *
- * @type {boolean}
- * @default true
- * @apioption plotOptions.series.enableMouseTracking
- */
- /**
- * By default, series are exposed to screen readers as regions. By
- * enabling this option, the series element itself will be exposed in
- * the same way as the data points. This is useful if the series is not
- * used as a grouping entity in the chart, but you still want to attach
- * a description to the series.
- *
- * Requires the Accessibility module.
- *
- * @sample highcharts/accessibility/art-grants/
- * Accessible data visualization
- *
- * @type {boolean}
- * @since 5.0.12
- * @apioption plotOptions.series.exposeElementToA11y
- */
- /**
- * Whether to use the Y extremes of the total chart width or only the
- * zoomed area when zooming in on parts of the X axis. By default, the
- * Y axis adjusts to the min and max of the visible data. Cartesian
- * series only.
- *
- * @type {boolean}
- * @default false
- * @since 4.1.6
- * @product highcharts highstock gantt
- * @apioption plotOptions.series.getExtremesFromAll
- */
- /**
- * An array specifying which option maps to which key in the data point
- * array. This makes it convenient to work with unstructured data arrays
- * from different sources.
- *
- * @see [series.data](#series.line.data)
- *
- * @sample {highcharts|highstock} highcharts/series/data-keys/
- * An extended data array with keys
- * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
- * Nested keys used to access object properties
- *
- * @type {Array<string>}
- * @since 4.1.6
- * @apioption plotOptions.series.keys
- */
- /**
- * The line cap used for line ends and line joins on the graph.
- *
- * @type {string}
- * @default round
- * @product highcharts highstock
- * @validvalue ["round", "square"]
- * @apioption plotOptions.series.linecap
- */
- /**
- * The [id](#series.id) of another series to link to. Additionally,
- * the value can be ":previous" to link to the previous series. When
- * two series are linked, only the first one appears in the legend.
- * Toggling the visibility of this also toggles the linked series.
- *
- * @sample {highcharts|highstock} highcharts/demo/arearange-line/
- * Linked series
- *
- * @type {string}
- * @since 3.0
- * @product highcharts highstock gantt
- * @apioption plotOptions.series.linkedTo
- */
- /**
- * Options for the corresponding navigator series if `showInNavigator`
- * is `true` for this series. Available options are the same as any
- * series, documented at [plotOptions](#plotOptions.series) and
- * [series](#series).
- *
- * These options are merged with options in [navigator.series](
- * #navigator.series), and will take precedence if the same option is
- * defined both places.
- *
- * @see [navigator.series](#navigator.series)
- *
- * @type {Highcharts.PlotSeriesOptions}
- * @since 5.0.0
- * @product highstock
- * @apioption plotOptions.series.navigatorOptions
- */
- /**
- * The color for the parts of the graph or points that are below the
- * [threshold](#plotOptions.series.threshold).
- *
- * @see In styled mode, a negative color is applied by setting this option
- * to `true` combined with the `.highcharts-negative` class name.
- *
- * @sample {highcharts} highcharts/plotoptions/series-negative-color/
- * Spline, area and column
- * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
- * Arearange
- * @sample {highcharts} highcharts/css/series-negative-color/
- * Styled mode
- * @sample {highstock} highcharts/plotoptions/series-negative-color/
- * Spline, area and column
- * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
- * Arearange
- * @sample {highmaps} highcharts/plotoptions/series-negative-color/
- * Spline, area and column
- * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
- * Arearange
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @since 3.0
- * @apioption plotOptions.series.negativeColor
- */
- /**
- * Same as
- * [accessibility.pointDescriptionFormatter](#accessibility.pointDescriptionFormatter),
- * but for an individual series. Overrides the chart wide configuration.
- *
- * @type {Function}
- * @since 5.0.12
- * @apioption plotOptions.series.pointDescriptionFormatter
- */
- /**
- * If no x values are given for the points in a series, `pointInterval`
- * defines the interval of the x values. For example, if a series
- * contains one value every decade starting from year 0, set
- * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
- * is set in milliseconds.
- *
- * It can be also be combined with `pointIntervalUnit` to draw irregular
- * time intervals.
- *
- * Please note that this options applies to the _series data_, not the
- * interval of the axis ticks, which is independent.
- *
- * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
- * Datetime X axis
- * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
- * Using pointStart and pointInterval
- *
- * @type {number}
- * @default 1
- * @product highcharts highstock gantt
- * @apioption plotOptions.series.pointInterval
- */
- /**
- * On datetime series, this allows for setting the
- * [pointInterval](#plotOptions.series.pointInterval) to irregular time
- * units, `day`, `month` and `year`. A day is usually the same as 24
- * hours, but `pointIntervalUnit` also takes the DST crossover into
- * consideration when dealing with local time. Combine this option with
- * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
- *
- * Please note that this options applies to the _series data_, not the
- * interval of the axis ticks, which is independent.
- *
- * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
- * One point a month
- * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
- * One point a month
- *
- * @type {string}
- * @since 4.1.0
- * @product highcharts highstock gantt
- * @validvalue ["day", "month", "year"]
- * @apioption plotOptions.series.pointIntervalUnit
- */
- /**
- * Possible values: `"on"`, `"between"`, `number`.
- *
- * In a column chart, when pointPlacement is `"on"`, the point will not
- * create any padding of the X axis. In a polar column chart this means
- * that the first column points directly north. If the pointPlacement is
- * `"between"`, the columns will be laid out between ticks. This is
- * useful for example for visualising an amount between two points in
- * time or in a certain sector of a polar chart.
- *
- * Since Highcharts 3.0.2, the point placement can also be numeric,
- * where 0 is on the axis value, -0.5 is between this value and the
- * previous, and 0.5 is between this value and the next. Unlike the
- * textual options, numeric point placement options won't affect axis
- * padding.
- *
- * Note that pointPlacement needs a [pointRange](
- * #plotOptions.series.pointRange) to work. For column series this is
- * computed, but for line-type series it needs to be set.
- *
- * For the `xrange` series type and gantt charts, if the Y axis is a
- * category axis, the `pointPlacement` applies to the Y axis rather than
- * the (typically datetime) X axis.
- *
- * Defaults to `undefined` in cartesian charts, `"between"` in polar
- * charts.
- *
- * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
- *
- * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
- * Between in a column chart
- * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
- * Numeric placement for custom layout
- * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
- * Placement in heatmap
- *
- * @type {string|number}
- * @since 2.3.0
- * @product highcharts highstock gantt
- * @apioption plotOptions.series.pointPlacement
- */
- /**
- * If no x values are given for the points in a series, pointStart
- * defines on what value to start. For example, if a series contains one
- * yearly value starting from 1945, set pointStart to 1945.
- *
- * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
- * Linear
- * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
- * Datetime
- * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
- * Using pointStart and pointInterval
- *
- * @type {number}
- * @default 0
- * @product highcharts highstock gantt
- * @apioption plotOptions.series.pointStart
- */
- /**
- * Whether to select the series initially. If `showCheckbox` is true,
- * the checkbox next to the series name in the legend will be checked
- * for a selected series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-selected/
- * One out of two series selected
- *
- * @type {boolean}
- * @default false
- * @since 1.2.0
- * @apioption plotOptions.series.selected
- */
- /**
- * Whether to apply a drop shadow to the graph line. Since 2.3 the
- * shadow can be an object configuration containing `color`, `offsetX`,
- * `offsetY`, `opacity` and `width`.
- *
- * @sample {highcharts} highcharts/plotoptions/series-shadow/
- * Shadow enabled
- *
- * @type {boolean|Highcharts.ShadowOptionsObject}
- * @default false
- * @apioption plotOptions.series.shadow
- */
- /**
- * Whether to display this particular series or series type in the
- * legend. The default value is `true` for standalone series, `false`
- * for linked series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
- * One series in the legend, one hidden
- *
- * @type {boolean}
- * @default true
- * @apioption plotOptions.series.showInLegend
- */
- /**
- * Whether or not to show the series in the navigator. Takes precedence
- * over [navigator.baseSeries](#navigator.baseSeries) if defined.
- *
- * @type {boolean}
- * @since 5.0.0
- * @product highstock
- * @apioption plotOptions.series.showInNavigator
- */
- /**
- * If set to `true`, the accessibility module will skip past the points
- * in this series for keyboard navigation.
- *
- * @type {boolean}
- * @since 5.0.12
- * @apioption plotOptions.series.skipKeyboardNavigation
- */
- /**
- * Whether to stack the values of each series on top of each other.
- * Possible values are `undefined` to disable, `"normal"` to stack by
- * value or `"percent"`. When stacking is enabled, data must be sorted
- * in ascending X order. A special stacking option is with the
- * streamgraph series type, where the stacking option is set to
- * `"stream"`. The second one is `"overlap"`, which only applies to
- * waterfall series.
- *
- * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
- *
- * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
- * Line
- * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
- * Column
- * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
- * Bar
- * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
- * Area
- * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
- * Line
- * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
- * Column
- * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
- * Bar
- * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
- * Area
- * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
- * Waterfall with normal stacking
- * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
- * Waterfall with overlap stacking
- * @sample {highstock} stock/plotoptions/stacking/
- * Area
- *
- * @type {string}
- * @product highcharts highstock
- * @validvalue ["normal", "percent"]
- * @apioption plotOptions.series.stacking
- */
- /**
- * Whether to apply steps to the line. Possible values are `left`,
- * `center` and `right`.
- *
- * @sample {highcharts} highcharts/plotoptions/line-step/
- * Different step line options
- * @sample {highcharts} highcharts/plotoptions/area-step/
- * Stepped, stacked area
- * @sample {highstock} stock/plotoptions/line-step/
- * Step line
- *
- * @type {string}
- * @since 1.2.5
- * @product highcharts highstock
- * @validvalue ["left", "center", "right"]
- * @apioption plotOptions.series.step
- */
- /**
- * The threshold, also called zero level or base level. For line type
- * series this is only used in conjunction with
- * [negativeColor](#plotOptions.series.negativeColor).
- *
- * @see [softThreshold](#plotOptions.series.softThreshold).
- *
- * @type {number}
- * @default 0
- * @since 3.0
- * @product highcharts highstock
- * @apioption plotOptions.series.threshold
- */
- /**
- * Set the initial visibility of the series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-visible/
- * Two series, one hidden and one visible
- * @sample {highstock} stock/plotoptions/series-visibility/
- * Hidden series
- *
- * @type {boolean}
- * @default true
- * @apioption plotOptions.series.visible
- */
- /**
- * Defines the Axis on which the zones are applied.
- *
- * @see [zones](#plotOptions.series.zones)
- *
- * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
- * Zones on the X-Axis
- * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
- * Zones on the X-Axis
- *
- * @type {string}
- * @default y
- * @since 4.1.0
- * @product highcharts highstock
- * @apioption plotOptions.series.zoneAxis
- */
- /**
- * General event handlers for the series items. These event hooks can
- * also be attached to the series at run time using the
- * `Highcharts.addEvent` function.
- */
- events: {},
- /**
- * Fires after the series has finished its initial animation, or in case
- * animation is disabled, immediately as the series is displayed.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
- * Show label after animate
- * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
- * Show label after animate
- *
- * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
- * @since 4.0
- * @product highcharts highstock gantt
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.afterAnimate
- */
- /**
- * Fires when the checkbox next to the series' name in the legend is
- * clicked. One parameter, `event`, is passed to the function. The state
- * of the checkbox is found by `event.checked`. The checked item is
- * found by `event.item`. Return `false` to prevent the default action
- * which is to toggle the select state of the series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
- * Alert checkbox status
- *
- * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.checkboxClick
- */
- /**
- * Fires when the series is clicked. One parameter, `event`, is passed
- * to the function, containing common event information. Additionally,
- * `event.point` holds a pointer to the nearest point on the graph.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-click/
- * Alert click info
- * @sample {highstock} stock/plotoptions/series-events-click/
- * Alert click info
- * @sample {highmaps} maps/plotoptions/series-events-click/
- * Display click info in subtitle
- *
- * @type {Highcharts.SeriesClickCallbackFunction}
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.click
- */
- /**
- * Fires when the series is hidden after chart generation time, either
- * by clicking the legend item or by calling `.hide()`.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-hide/
- * Alert when the series is hidden by clicking the legend item
- *
- * @type {Highcharts.SeriesHideCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.hide
- */
- /**
- * Fires when the legend item belonging to the series is clicked. One
- * parameter, `event`, is passed to the function. The default action
- * is to toggle the visibility of the series. This can be prevented
- * by returning `false` or calling `event.preventDefault()`.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
- * Confirm hiding and showing
- *
- * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.legendItemClick
- */
- /**
- * Fires when the mouse leaves the graph. One parameter, `event`, is
- * passed to the function, containing common event information. If the
- * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
- * doesn't happen before the mouse enters another graph or leaves the
- * plot area.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
- * With sticky tracking by default
- * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
- * Without sticky tracking
- *
- * @type {Highcharts.SeriesMouseOutCallbackFunction}
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.mouseOut
- */
- /**
- * Fires when the mouse enters the graph. One parameter, `event`, is
- * passed to the function, containing common event information.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
- * With sticky tracking by default
- * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
- * Without sticky tracking
- *
- * @type {Highcharts.SeriesMouseOverCallbackFunction}
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.mouseOver
- */
- /**
- * Fires when the series is shown after chart generation time, either
- * by clicking the legend item or by calling `.show()`.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-show/
- * Alert when the series is shown by clicking the legend item.
- *
- * @type {Highcharts.SeriesShowCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Series
- * @apioption plotOptions.series.events.show
- */
- /**
- * Options for the point markers of line-like series. Properties like
- * `fillColor`, `lineColor` and `lineWidth` define the visual appearance
- * of the markers. Other series types, like column series, don't have
- * markers, but have visual options on the series level instead.
- *
- * In styled mode, the markers can be styled with the
- * `.highcharts-point`, `.highcharts-point-hover` and
- * `.highcharts-point-select` class names.
- */
- marker: {
- /**
- * The width of the point marker's outline.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
- * 2px blue marker
- */
- lineWidth: 0,
- /**
- * The color of the point marker's outline. When `undefined`, the
- * series' or point's color is used.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
- * Inherit from series color (undefined)
- *
- * @type {Highcharts.ColorString}
- */
- lineColor: '#ffffff',
- /**
- * The fill color of the point marker. When `undefined`, the series'
- * or point's color is used.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
- * White fill
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @apioption plotOptions.series.marker.fillColor
- */
- /**
- * Enable or disable the point marker. If `undefined`, the markers
- * are hidden when the data is dense, and shown for more widespread
- * data points.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
- * Disabled markers
- * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
- * Disabled in normal state but enabled on hover
- * @sample {highstock} stock/plotoptions/series-marker/
- * Enabled markers
- *
- * @type {boolean}
- * @default {highcharts} undefined
- * @default {highstock} false
- * @apioption plotOptions.series.marker.enabled
- */
- /**
- * Image markers only. Set the image width explicitly. When using
- * this option, a `width` must also be set.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
- * Fixed width and height
- * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
- * Fixed width and height
- *
- * @type {number}
- * @since 4.0.4
- * @apioption plotOptions.series.marker.height
- */
- /**
- * A predefined shape or symbol for the marker. When undefined, the
- * symbol is pulled from options.symbols. Other possible values are
- * "circle", "square", "diamond", "triangle" and "triangle-down".
- *
- * Additionally, the URL to a graphic can be given on this form:
- * "url(graphic.png)". Note that for the image to be applied to
- * exported charts, its URL needs to be accessible by the export
- * server.
- *
- * Custom callbacks for symbol path generation can also be added to
- * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
- * used by its method name, as shown in the demo.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
- * Predefined, graphic and custom markers
- * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
- * Predefined, graphic and custom markers
- *
- * @type {string}
- * @apioption plotOptions.series.marker.symbol
- */
- /**
- * The threshold for how dense the point markers should be before
- * they are hidden, given that `enabled` is not defined. The number
- * indicates the horizontal distance between the two closest points
- * in the series, as multiples of the `marker.radius`. In other
- * words, the default value of 2 means points are hidden if
- * overlapping horizontally.
- *
- * @sample highcharts/plotoptions/series-marker-enabledthreshold
- * A higher threshold
- *
- * @since 6.0.5
- */
- enabledThreshold: 2,
- /**
- * The radius of the point marker.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
- * Bigger markers
- *
- * @default {highstock} 2
- */
- radius: 4,
- /**
- * Image markers only. Set the image width explicitly. When using
- * this option, a `height` must also be set.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
- * Fixed width and height
- * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
- * Fixed width and height
- *
- * @type {number}
- * @since 4.0.4
- * @apioption plotOptions.series.marker.width
- */
- /**
- * States for a single point marker.
- */
- states: {
- /**
- * The normal state of a single point marker. Currently only
- * used for setting animation when returning to normal state
- * from hover.
- */
- normal: {
- /**
- * Animation when returning to normal state after hovering.
- *
- * @type {boolean|Highcharts.AnimationOptionsObject}
- */
- animation: true
- },
- /**
- * The hover state for a single point marker.
- */
- hover: {
- /**
- * Animation when hovering over the marker.
- *
- * @type {boolean|Highcharts.AnimationOptionsObject}
- */
- animation: {
- duration: 50
- },
- /**
- * Enable or disable the point marker.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
- * Disabled hover state
- */
- enabled: true,
- /**
- * The fill color of the marker in hover state. When
- * `undefined`, the series' or point's fillColor for normal
- * state is used.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @apioption plotOptions.series.marker.states.hover.fillColor
- */
- /**
- * The color of the point marker's outline. When
- * `undefined`, the series' or point's lineColor for normal
- * state is used.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
- * White fill color, black line color
- *
- * @type {Highcharts.ColorString}
- * @apioption plotOptions.series.marker.states.hover.lineColor
- */
- /**
- * The width of the point marker's outline. When
- * `undefined`, the series' or point's lineWidth for normal
- * state is used.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
- * 3px line width
- *
- * @type {number}
- * @apioption plotOptions.series.marker.states.hover.lineWidth
- */
- /**
- * The radius of the point marker. In hover state, it
- * defaults to the normal state's radius + 2 as per the
- * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
- * option.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
- * 10px radius
- *
- * @type {number}
- * @apioption plotOptions.series.marker.states.hover.radius
- */
- /**
- * The number of pixels to increase the radius of the
- * hovered point.
- *
- * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
- * 5 pixels greater radius on hover
- * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
- * 5 pixels greater radius on hover
- *
- * @since 4.0.3
- */
- radiusPlus: 2,
- /**
- * The additional line width for a hovered point.
- *
- * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
- * 2 pixels wider on hover
- * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
- * 2 pixels wider on hover
- *
- * @since 4.0.3
- */
- lineWidthPlus: 1
- },
- /**
- * The appearance of the point marker when selected. In order to
- * allow a point to be selected, set the
- * `series.allowPointSelect` option to true.
- */
- select: {
- /**
- * The radius of the point marker. In hover state, it
- * defaults to the normal state's radius + 2.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
- * 10px radius for selected points
- *
- * @type {number}
- * @apioption plotOptions.series.marker.states.select.radius
- */
- /**
- * Enable or disable visible feedback for selection.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
- * Disabled select state
- *
- * @type {boolean}
- * @default true
- * @apioption plotOptions.series.marker.states.select.enabled
- */
- /**
- * The fill color of the point marker.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
- * Solid red discs for selected points
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- */
- fillColor: '#cccccc',
- /**
- * The color of the point marker's outline. When
- * `undefined`, the series' or point's color is used.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
- * Red line color for selected points
- *
- * @type {Highcharts.ColorString}
- */
- lineColor: '#000000',
- /**
- * The width of the point marker's outline.
- *
- * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
- * 3px line width for selected points
- */
- lineWidth: 2
- }
- }
- },
- /**
- * Properties for each single point.
- */
- point: {
- /**
- * Fires when a point is clicked. One parameter, `event`, is passed
- * to the function, containing common event information.
- *
- * If the `series.allowPointSelect` option is true, the default
- * action for the point's click event is to toggle the point's
- * select state. Returning `false` cancels this action.
- *
- * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
- * Click marker to alert values
- * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
- * Click column
- * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
- * Go to URL
- * @sample {highmaps} maps/plotoptions/series-point-events-click/
- * Click marker to display values
- * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
- * Go to URL
- *
- * @type {Highcharts.SeriesPointClickCallbackFunction}
- * @context Highcharts.Point
- * @apioption plotOptions.series.point.events.click
- */
- /**
- * Fires when the mouse leaves the area close to the point. One
- * parameter, `event`, is passed to the function, containing common
- * event information.
- *
- * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
- * Show values in the chart's corner on mouse over
- *
- * @type {Highcharts.SeriesPointMouseOutCallbackFunction}
- * @context Highcharts.Point
- * @apioption plotOptions.series.point.events.mouseOut
- */
- /**
- * Fires when the mouse enters the area close to the point. One
- * parameter, `event`, is passed to the function, containing common
- * event information.
- *
- * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
- * Show values in the chart's corner on mouse over
- *
- * @type {Highcharts.SeriesPointMouseOverCallbackFunction}
- * @context Highcharts.Point
- * @apioption plotOptions.series.point.events.mouseOver
- */
- /**
- * Fires when the point is removed using the `.remove()` method. One
- * parameter, `event`, is passed to the function. Returning `false`
- * cancels the operation.
- *
- * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
- * Remove point and confirm
- *
- * @type {Highcharts.SeriesPointRemoveCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Point
- * @apioption plotOptions.series.point.events.remove
- */
- /**
- * Fires when the point is selected either programmatically or
- * following a click on the point. One parameter, `event`, is passed
- * to the function. Returning `false` cancels the operation.
- *
- * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
- * Report the last selected point
- * @sample {highmaps} maps/plotoptions/series-allowpointselect/
- * Report select and unselect
- *
- * @type {Highcharts.SeriesPointSelectCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Point
- * @apioption plotOptions.series.point.events.select
- */
- /**
- * Fires when the point is unselected either programmatically or
- * following a click on the point. One parameter, `event`, is passed
- * to the function.
- * Returning `false` cancels the operation.
- *
- * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
- * Report the last unselected point
- * @sample {highmaps} maps/plotoptions/series-allowpointselect/
- * Report select and unselect
- *
- * @type {Highcharts.SeriesPointUnselectCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Point
- * @apioption plotOptions.series.point.events.unselect
- */
- /**
- * Fires when the point is updated programmatically through the
- * `.update()` method. One parameter, `event`, is passed to the
- * function. The new point options can be accessed through
- * `event.options`. Returning `false` cancels the operation.
- *
- * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
- * Confirm point updating
- *
- * @type {Highcharts.SeriesPointUpdateCallbackFunction}
- * @since 1.2.0
- * @context Highcharts.Point
- * @apioption plotOptions.series.point.events.update
- */
- /**
- * Events for each single point.
- */
- events: {}
- },
- /**
- * Options for the series data labels, appearing next to each data
- * point.
- *
- * Since v6.2.0, multiple data labels can be applied to each single
- * point by defining them as an array of configs.
- *
- * In styled mode, the data labels can be styled with the
- * `.highcharts-data-label-box` and `.highcharts-data-label` class names
- * ([see example](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-datalabels)).
- *
- * @sample highcharts/plotoptions/series-datalabels-enabled
- * Data labels enabled
- * @sample highcharts/plotoptions/series-datalabels-multiple
- * Multiple data labels on a bar series
- */
- dataLabels: {
- /**
- * The alignment of the data label compared to the point. If
- * `right`, the right side of the label should be touching the
- * point. For points with an extent, like columns, the alignments
- * also dictates how to align it inside the box, as given with the
- * [inside](#plotOptions.column.dataLabels.inside) option. Can be
- * one of `left`, `center` or `right`.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-align-left/
- * Left aligned
- *
- * @type {Highcharts.AlignType}
- */
- align: 'center',
- /**
- * Whether to allow data labels to overlap. To make the labels less
- * sensitive for overlapping, the [dataLabels.padding](
- * #plotOptions.series.dataLabels.padding) can be set to 0.
- *
- * @sample highcharts/plotoptions/series-datalabels-allowoverlap-false/
- * Don't allow overlap
- *
- * @type {boolean}
- * @default false
- * @since 4.1.0
- * @apioption plotOptions.series.dataLabels.allowOverlap
- */
- /**
- * The border radius in pixels for the data label.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- * @sample {highstock} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- * @sample {highmaps} maps/plotoptions/series-datalabels-box/
- * Data labels box options
- *
- * @type {number}
- * @default 0
- * @since 2.2.1
- * @apioption plotOptions.series.dataLabels.borderRadius
- */
- /**
- * The border width in pixels for the data label.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- * @sample {highstock} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- *
- * @type {number}
- * @default 0
- * @since 2.2.1
- * @apioption plotOptions.series.dataLabels.borderWidth
- */
- /**
- * A class name for the data label. Particularly in styled mode,
- * this can be used to give each series' or point's data label
- * unique styling. In addition to this option, a default color class
- * name is added so that we can give the labels a
- * [contrast text shadow](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/data-label-contrast/).
- *
- * @sample {highcharts} highcharts/css/series-datalabels/
- * Styling by CSS
- * @sample {highstock} highcharts/css/series-datalabels/
- * Styling by CSS
- * @sample {highmaps} highcharts/css/series-datalabels/
- * Styling by CSS
- *
- * @type {string}
- * @since 5.0.0
- * @apioption plotOptions.series.dataLabels.className
- */
- /**
- * The text color for the data labels. Defaults to `undefined`. For
- * certain series types, like column or map, the data labels can be
- * drawn inside the points. In this case the data label will be
- * drawn with maximum contrast by default. Additionally, it will be
- * given a `text-outline` style with the opposite color, to further
- * increase the contrast. This can be overridden by setting the
- * `text-outline` style to `none` in the `dataLabels.style` option.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
- * Red data labels
- * @sample {highmaps} maps/demo/color-axis/
- * White data labels
- *
- * @type {Highcharts.ColorString}
- * @apioption plotOptions.series.dataLabels.color
- */
- /**
- * Whether to hide data labels that are outside the plot area. By
- * default, the data label is moved inside the plot area according
- * to the [overflow](#plotOptions.series.dataLabels.overflow)
- * option.
- *
- * @type {boolean}
- * @default true
- * @since 2.3.3
- * @apioption plotOptions.series.dataLabels.crop
- */
- /**
- * Whether to defer displaying the data labels until the initial
- * series animation has finished.
- *
- * @type {boolean}
- * @default true
- * @since 4.0
- * @product highcharts highstock gantt
- * @apioption plotOptions.series.dataLabels.defer
- */
- /**
- * Enable or disable the data labels.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
- * Data labels enabled
- * @sample {highmaps} maps/demo/color-axis/
- * Data labels enabled
- *
- * @type {boolean}
- * @default false
- * @apioption plotOptions.series.dataLabels.enabled
- */
- /**
- * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
- * for the data label. Available variables are the same as for
- * `formatter`.
- *
- * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-format/
- * Add a unit
- * @sample {highmaps} maps/plotoptions/series-datalabels-format/
- * Formatted value in the data label
- *
- * @type {string}
- * @default {highcharts} {y}
- * @default {highstock} {y}
- * @default {highmaps} {point.value}
- * @since 3.0
- * @apioption plotOptions.series.dataLabels.format
- */
- /**
- * Callback JavaScript function to format the data label. Note that
- * if a `format` is defined, the format takes precedence and the
- * formatter is ignored. Available data are:
- *
- * - `this.percentage`: Stacked series and pies only. The point's
- * percentage of the total.
- *
- * - `this.point`: The point object. The point name, if defined, is
- * available through `this.point.name`.
- *
- * - `this.series`: The series object. The series name is available
- * through`this.series.name`.
- *
- * - `this.total`: Stacked series only. The total value at this
- * point's x value.
- *
- * - `this.x`: The x value.
- *
- * - `this.y`: The y value.
- *
- * @sample {highmaps} maps/plotoptions/series-datalabels-format/
- * Formatted value
- *
- * @type {Highcharts.FormatterCallbackFunction<Highcharts.SeriesDataLabelsFormatterContextObject>}
- * @default function () { return this.y; }
- */
- formatter: function () {
- return this.y === null ? '' : H.numberFormat(this.y, -1);
- },
- /**
- * Styles for the label. The default `color` setting is
- * `"contrast"`, which is a pseudo color that Highcharts picks up
- * and applies the maximum contrast to the underlying point item,
- * for example the bar in a bar chart.
- *
- * The `textOutline` is a pseudo property that
- * applies an outline of the given width with the given color, which
- * by default is the maximum contrast to the text. So a bright text
- * color will result in a black text outline for maximum readability
- * on a mixed background. In some cases, especially with grayscale
- * text, the text outline doesn't work well, in which cases it can
- * be disabled by setting it to `"none"`. When `useHTML` is true,
- * the `textOutline` will not be picked up. In this, case, the same
- * effect can be acheived through the `text-shadow` CSS property.
- *
- * For some series types, where each point has an extent, like for
- * example tree maps, the data label may overflow the point. There
- * are two strategies for handling overflow. By default, the text
- * will wrap to multiple lines. The other strategy is to set
- * `style.textOverflow` to `ellipsis`, which will keep the text on
- * one line plus it will break inside long words.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
- * Bold labels
- * @sample {highmaps} maps/demo/color-axis/
- * Bold labels
- *
- * @type {Highcharts.CSSObject}
- * @default {"color": "contrast", "fontSize": "11px", "fontWeight": "bold", "textOutline": "1px contrast" }
- * @since 4.1.0
- */
- style: {
- /** @ignore */
- fontSize: '11px',
- /** @ignore */
- fontWeight: 'bold',
- /** @ignore */
- color: 'contrast',
- /** @ignore */
- textOutline: '1px contrast'
- },
- /**
- * The name of a symbol to use for the border around the label.
- * Symbols are predefined functions on the Renderer object.
- *
- * @sample highcharts/plotoptions/series-datalabels-shape/
- * A callout for annotations
- *
- * @type {string}
- * @default square
- * @since 4.1.2
- * @apioption plotOptions.series.dataLabels.shape
- */
- /**
- * The Z index of the data labels. The default Z index puts it above
- * the series. Use a Z index of 2 to display it behind the series.
- *
- * @type {number}
- * @default 6
- * @since 2.3.5
- * @apioption plotOptions.series.dataLabels.zIndex
- */
- /**
- * A declarative filter for which data labels to display. The
- * declarative filter is designed for use when callback functions
- * are not available, like when the chart options require a pure
- * JSON structure or for use with graphical editors. For
- * programmatic control, use the `formatter` instead, and return
- * `undefined` to disable a single data label.
- *
- * @example
- * filter: {
- * property: 'percentage',
- * operator: '>',
- * value: 4
- * }
- *
- * @sample highcharts/demo/pie-monochrome
- * Data labels filtered by percentage
- *
- * @since 6.0.3
- * @apioption plotOptions.series.dataLabels.filter
- */
- /**
- * The point property to filter by. Point options are passed
- * directly to properties, additionally there are `y` value,
- * `percentage` and others listed under
- * [Point](https://api.highcharts.com/class-reference/Highcharts.Point)
- * members.
- *
- * @type {string}
- * @apioption plotOptions.series.dataLabels.filter.property
- */
- /**
- * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
- * `==`, and `===`.
- *
- * @type {string}
- * @validvalue [">", "<", ">=", "<=", "==", "==="]
- * @apioption plotOptions.series.dataLabels.filter.operator
- */
- /**
- * The value to compare against.
- *
- * @type {*}
- * @apioption plotOptions.series.dataLabels.filter.value
- */
- /**
- * The background color or gradient for the data label.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- * @sample {highmaps} maps/plotoptions/series-datalabels-box/
- * Data labels box options
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @since 2.2.1
- * @apioption plotOptions.series.dataLabels.backgroundColor
- */
- /**
- * The border color for the data label. Defaults to `undefined`.
- *
- * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- *
- * @type {Highcharts.ColorString}
- * @since 2.2.1
- * @apioption plotOptions.series.dataLabels.borderColor
- */
- /**
- * The shadow of the box. Works best with `borderWidth` or
- * `backgroundColor`. Since 2.3 the shadow can be an object
- * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
- * and `width`.
- *
- * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- *
- * @type {boolean|Highcharts.ShadowOptionsObject}
- * @default false
- * @since 2.2.1
- * @apioption plotOptions.series.dataLabels.shadow
- */
- /**
- * For points with an extent, like columns or map areas, whether to
- * align the data label inside the box or to the actual value point.
- * Defaults to `false` in most cases, `true` in stacked columns.
- *
- * @type {boolean}
- * @since 3.0
- * @apioption plotOptions.series.dataLabels.inside
- */
- /**
- * How to handle data labels that flow outside the plot area. The
- * default is `"justify"`, which aligns them inside the plot area.
- * For columns and bars, this means it will be moved inside the bar.
- * To display data labels outside the plot area, set `crop` to
- * `false` and `overflow` to `"allow"`.
- *
- * @type {string}
- * @default justify
- * @since 3.0.6
- * @validvalue ["allow", "justify"]
- * @apioption plotOptions.series.dataLabels.overflow
- */
- /**
- * Text rotation in degrees. Note that due to a more complex
- * structure, backgrounds, borders and padding will be lost on a
- * rotated data label.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
- * Vertical labels
- *
- * @type {number}
- * @default 0
- * @apioption plotOptions.series.dataLabels.rotation
- */
- /**
- * Whether to
- * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
- * to render the labels.
- *
- * @type {boolean}
- * @default false
- * @apioption plotOptions.series.dataLabels.useHTML
- */
- /**
- * The vertical alignment of a data label. Can be one of `top`,
- * `middle` or `bottom`. The default value depends on the data, for
- * instance in a column chart, the label is above positive values
- * and below negative values.
- *
- * @type {Highcharts.VerticalAlignType}
- * @since 2.3.3
- */
- verticalAlign: 'bottom', // above singular point
- /**
- * The x position offset of the label relative to the point in
- * pixels.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
- * Vertical and positioned
- */
- x: 0,
- /**
- * The y position offset of the label relative to the point in
- * pixels.
- *
- * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
- * Vertical and positioned
- */
- y: 0,
- /**
- * When either the `borderWidth` or the `backgroundColor` is set,
- * this is the padding within the box.
- *
- * @sample {highcharts|highstock} highcharts/plotoptions/series-datalabels-box/
- * Data labels box options
- * @sample {highmaps} maps/plotoptions/series-datalabels-box/
- * Data labels box options
- *
- * @default {highcharts} 5
- * @default {highstock} 5
- * @default {highmaps} 0
- * @since 2.2.1
- */
- padding: 5
- },
- /**
- * When the series contains less points than the crop threshold, all
- * points are drawn, even if the points fall outside the visible plot
- * area at the current zoom. The advantage of drawing all points
- * (including markers and columns), is that animation is performed on
- * updates. On the other hand, when the series contains more points than
- * the crop threshold, the series data is cropped to only contain points
- * that fall within the plot area. The advantage of cropping away
- * invisible points is to increase performance on large series.
- *
- * @since 2.2
- * @product highcharts highstock
- */
- cropThreshold: 300,
- /**
- * The width of each point on the x axis. For example in a column chart
- * with one value each day, the pointRange would be 1 day (= 24 * 3600
- * * 1000 milliseconds). This is normally computed automatically, but
- * this option can be used to override the automatic value.
- *
- * @product highstock
- */
- pointRange: 0,
- /**
- * When this is true, the series will not cause the Y axis to cross
- * the zero plane (or [threshold](#plotOptions.series.threshold) option)
- * unless the data actually crosses the plane.
- *
- * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
- * 3 will make the Y axis show negative values according to the
- * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
- * at 0.
- *
- * @since 4.1.9
- * @product highcharts highstock
- */
- softThreshold: true,
- /**
- * A wrapper object for all the series options in specific states.
- */
- states: {
- /**
- * The normal state of a series, or for point items in column, pie
- * and similar series. Currently only used for setting animation
- * when returning to normal state from hover.
- */
- normal: {
- /**
- * Animation when returning to normal state after hovering.
- *
- * @type {boolean|Highcharts.AnimationOptionsObject}
- */
- animation: true
- },
- /**
- * Options for the hovered series. These settings override the
- * normal state options when a series is moused over or touched.
- */
- hover: {
- /**
- * Enable separate styles for the hovered series to visualize
- * that the user hovers either the series itself or the legend.
- *
- * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
- * Line
- * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
- * Column
- * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
- * Pie
- *
- * @type {boolean}
- * @default true
- * @since 1.2
- * @apioption plotOptions.series.states.hover.enabled
- */
- /**
- * Animation setting for hovering the graph in line-type series.
- *
- * @type {boolean|Highcharts.AnimationOptionsObject}
- * @since 5.0.8
- * @product highcharts
- */
- animation: {
- /**
- * The duration of the hover animation in milliseconds. By
- * default the hover state animates quickly in, and slowly
- * back to normal.
- */
- duration: 50
- },
- /**
- * Pixel width of the graph line. By default this property is
- * undefined, and the `lineWidthPlus` property dictates how much
- * to increase the linewidth from normal state.
- *
- * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
- * 5px line on hover
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption plotOptions.series.states.hover.lineWidth
- */
- /**
- * The additional line width for the graph of a hovered series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
- * 5 pixels wider
- * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
- * 5 pixels wider
- *
- * @since 4.0.3
- * @product highcharts highstock
- */
- lineWidthPlus: 1,
- /**
- * In Highcharts 1.0, the appearance of all markers belonging
- * to the hovered series. For settings on the hover state of the
- * individual point, see
- * [marker.states.hover](#plotOptions.series.marker.states.hover).
- *
- * @deprecated
- *
- * @extends plotOptions.series.marker
- * @product highcharts highstock
- */
- marker: {
- // lineWidth: base + 1,
- // radius: base + 1
- },
- /**
- * Options for the halo appearing around the hovered point in
- * line-type series as well as outside the hovered slice in pie
- * charts. By default the halo is filled by the current point or
- * series color with an opacity of 0.25\. The halo can be
- * disabled by setting the `halo` option to `false`.
- *
- * In styled mode, the halo is styled with the
- * `.highcharts-halo` class, with colors inherited from
- * `.highcharts-color-{n}`.
- *
- * @sample {highcharts} highcharts/plotoptions/halo/
- * Halo options
- * @sample {highstock} highcharts/plotoptions/halo/
- * Halo options
- *
- * @since 4.0
- * @product highcharts highstock
- */
- halo: {
- /**
- * A collection of SVG attributes to override the appearance
- * of the halo, for example `fill`, `stroke` and
- * `stroke-width`.
- *
- * @type {Highcharts.SVGAttributes}
- * @since 4.0
- * @product highcharts highstock
- * @apioption plotOptions.series.states.hover.halo.attributes
- */
- /**
- * The pixel size of the halo. For point markers this is the
- * radius of the halo. For pie slices it is the width of the
- * halo outside the slice. For bubbles it defaults to 5 and
- * is the width of the halo outside the bubble.
- *
- * @since 4.0
- * @product highcharts highstock
- */
- size: 10,
- /**
- * Opacity for the halo unless a specific fill is overridden
- * using the `attributes` setting. Note that Highcharts is
- * only able to apply opacity to colors of hex or rgb(a)
- * formats.
- *
- * @since 4.0
- * @product highcharts highstock
- */
- opacity: 0.25
- }
- },
- /**
- * Specific options for point in selected states, after being
- * selected by
- * [allowPointSelect](#plotOptions.series.allowPointSelect) or
- * programmatically.
- *
- * @sample {highmaps} maps/plotoptions/series-allowpointselect/
- * Allow point select demo
- *
- * @extends plotOptions.series.states.hover
- * @excluding brightness
- * @product highmaps
- */
- select: {
- animation: {
- duration: 0
- }
- }
- },
- /**
- * Sticky tracking of mouse events. When true, the `mouseOut` event on a
- * series isn't triggered until the mouse moves over another series, or
- * out of the plot area. When false, the `mouseOut` event on a series is
- * triggered when the mouse leaves the area around the series' graph or
- * markers. This also implies the tooltip when not shared. When
- * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
- * will be hidden when moving the mouse between series. Defaults to true
- * for line and area type series, but to false for columns, pies etc.
- *
- * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
- * True by default
- * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
- * False
- *
- * @default {highcharts} true
- * @default {highstock} true
- * @default {highmaps} false
- * @since 2.0
- */
- stickyTracking: true,
- /**
- * A configuration object for the tooltip rendering of each single
- * series. Properties are inherited from [tooltip](#tooltip), but only
- * the following properties can be defined on a series level.
- *
- * @since 2.3
- * @extends tooltip
- * @excluding animation, backgroundColor, borderColor, borderRadius,
- * borderWidth, crosshairs, enabled, formatter, positioner,
- * shadow, shape, shared, snap, style, useHTML
- * @apioption plotOptions.series.tooltip
- */
- /**
- * When a series contains a data array that is longer than this, only
- * one dimensional arrays of numbers, or two dimensional arrays with
- * x and y values are allowed. Also, only the first point is tested,
- * and the rest are assumed to be the same format. This saves expensive
- * data checking and indexing in long series. Set it to `0` disable.
- *
- * @since 2.2
- * @product highcharts highstock gantt
- */
- turboThreshold: 1000,
- /**
- * An array defining zones within a series. Zones can be applied to the
- * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
- * option. The zone definitions have to be in ascending order regarding
- * to the value.
- *
- * In styled mode, the color zones are styled with the
- * `.highcharts-zone-{n}` class, or custom classed from the `className`
- * option
- * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
- *
- * @see [zoneAxis](#plotOptions.series.zoneAxis)
- *
- * @sample {highcharts} highcharts/series/color-zones-simple/
- * Color zones
- * @sample {highstock} highcharts/series/color-zones-simple/
- * Color zones
- *
- * @type {Array<*>}
- * @since 4.1.0
- * @product highcharts highstock
- * @apioption plotOptions.series.zones
- */
- /**
- * Styled mode only. A custom class name for the zone.
- *
- * @sample highcharts/css/color-zones/
- * Zones styled by class name
- *
- * @type {string}
- * @since 5.0.0
- * @apioption plotOptions.series.zones.className
- */
- /**
- * Defines the color of the series.
- *
- * @see [series color](#plotOptions.series.color)
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @since 4.1.0
- * @product highcharts highstock
- * @apioption plotOptions.series.zones.color
- */
- /**
- * A name for the dash style to use for the graph.
- *
- * @see [series.dashStyle](#plotOptions.series.dashStyle)
- *
- * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
- * Dashed line indicates prognosis
- *
- * @type {Highcharts.DashStyleType}
- * @since 4.1.0
- * @product highcharts highstock
- * @apioption plotOptions.series.zones.dashStyle
- */
- /**
- * Defines the fill color for the series (in area type series)
- *
- * @see [fillColor](#plotOptions.area.fillColor)
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @since 4.1.0
- * @product highcharts highstock
- * @apioption plotOptions.series.zones.fillColor
- */
- /**
- * The value up to where the zone extends, if undefined the zones
- * stretches to the last value in the series.
- *
- * @type {number}
- * @since 4.1.0
- * @product highcharts highstock
- * @apioption plotOptions.series.zones.value
- */
- /**
- * Determines whether the series should look for the nearest point
- * in both dimensions or just the x-dimension when hovering the series.
- * Defaults to `'xy'` for scatter series and `'x'` for most other
- * series. If the data has duplicate x-values, it is recommended to
- * set this to `'xy'` to allow hovering over all points.
- *
- * Applies only to series types using nearest neighbor search (not
- * direct hover) for tooltip.
- *
- * @sample {highcharts} highcharts/series/findnearestpointby/
- * Different hover behaviors
- * @sample {highstock} highcharts/series/findnearestpointby/
- * Different hover behaviors
- * @sample {highmaps} highcharts/series/findnearestpointby/
- * Different hover behaviors
- *
- * @since 5.0.10
- * @validvalue ["x", "xy"]
- */
- findNearestPointBy: 'x'
- },
- /** @lends Highcharts.Series.prototype */
- {
- isCartesian: true,
- pointClass: Point,
- sorted: true, // requires the data to be sorted
- requireSorting: true,
- directTouch: false,
- axisTypes: ['xAxis', 'yAxis'],
- colorCounter: 0,
- // each point's x and y values are stored in this.xData and this.yData
- parallelArrays: ['x', 'y'],
- coll: 'series',
- init: function (chart, options) {
- fireEvent(this, 'init', { options: options });
- var series = this,
- events,
- chartSeries = chart.series,
- lastSeries;
- /**
- * Read only. The chart that the series belongs to.
- *
- * @name Highcharts.Series#chart
- * @type {Highcharts.Chart}
- */
- series.chart = chart;
- /**
- * Read only. The series' type, like "line", "area", "column" etc.
- * The type in the series options anc can be altered using
- * {@link Series#update}.
- *
- * @name Highcharts.Series#type
- * @type {string}
- */
- /**
- * Read only. The series' current options. To update, use
- * {@link Series#update}.
- *
- * @name Highcharts.Series#options
- * @type {Highcharts.SeriesOptionsType}
- */
- series.options = options = series.setOptions(options);
- series.linkedSeries = [];
- // bind the axes
- series.bindAxes();
- // set some variables
- extend(series, {
- /**
- * The series name as given in the options. Defaults to
- * "Series {n}".
- *
- * @name Highcharts.Series#name
- * @type {string}
- */
- name: options.name,
- state: '',
- /**
- * Read only. The series' visibility state as set by {@link
- * Series#show}, {@link Series#hide}, or in the initial
- * configuration.
- *
- * @name Highcharts.Series#visible
- * @type {boolean}
- */
- visible: options.visible !== false, // true by default
- /**
- * Read only. The series' selected state as set by {@link
- * Highcharts.Series#select}.
- *
- * @name Highcharts.Series#selected
- * @type {boolean}
- */
- selected: options.selected === true // false by default
- });
- // register event listeners
- events = options.events;
- objectEach(events, function (event, eventType) {
- addEvent(series, eventType, event);
- });
- if (
- (events && events.click) ||
- (
- options.point &&
- options.point.events &&
- options.point.events.click
- ) ||
- options.allowPointSelect
- ) {
- chart.runTrackerClick = true;
- }
- series.getColor();
- series.getSymbol();
- // Set the data
- series.parallelArrays.forEach(function (key) {
- series[key + 'Data'] = [];
- });
- series.setData(options.data, false);
- // Mark cartesian
- if (series.isCartesian) {
- chart.hasCartesianSeries = true;
- }
- // Get the index and register the series in the chart. The index is
- // one more than the current latest series index (#5960).
- if (chartSeries.length) {
- lastSeries = chartSeries[chartSeries.length - 1];
- }
- series._i = pick(lastSeries && lastSeries._i, -1) + 1;
- // Insert the series and re-order all series above the insertion
- // point.
- chart.orderSeries(this.insert(chartSeries));
- fireEvent(this, 'afterInit');
- },
- /**
- * Insert the series in a collection with other series, either the chart
- * series or yAxis series, in the correct order according to the index
- * option. Used internally when adding series.
- *
- * @private
- * @function Highcharts.Series#insert
- *
- * @param {Array<Highcharts.Series>} collection
- * A collection of series, like `chart.series` or `xAxis.series`.
- *
- * @return {number}
- * The index of the series in the collection.
- */
- insert: function (collection) {
- var indexOption = this.options.index,
- i;
- // Insert by index option
- if (isNumber(indexOption)) {
- i = collection.length;
- while (i--) {
- // Loop down until the interted element has higher index
- if (indexOption >=
- pick(collection[i].options.index, collection[i]._i)
- ) {
- collection.splice(i + 1, 0, this);
- break;
- }
- }
- if (i === -1) {
- collection.unshift(this);
- }
- i = i + 1;
- // Or just push it to the end
- } else {
- collection.push(this);
- }
- return pick(i, collection.length - 1);
- },
- /**
- * Set the xAxis and yAxis properties of cartesian series, and register
- * the series in the `axis.series` array.
- *
- * @private
- * @function Highcharts.Series#bindAxes
- *
- * @exception 18
- */
- bindAxes: function () {
- var series = this,
- seriesOptions = series.options,
- chart = series.chart,
- axisOptions;
- fireEvent(this, 'bindAxes', null, function () {
- // repeat for xAxis and yAxis
- (series.axisTypes || []).forEach(function (AXIS) {
- // loop through the chart's axis objects
- chart[AXIS].forEach(function (axis) {
- axisOptions = axis.options;
- // apply if the series xAxis or yAxis option mathches
- // the number of the axis, or if undefined, use the
- // first axis
- if (
- seriesOptions[AXIS] === axisOptions.index ||
- (
- seriesOptions[AXIS] !== undefined &&
- seriesOptions[AXIS] === axisOptions.id
- ) ||
- (
- seriesOptions[AXIS] === undefined &&
- axisOptions.index === 0
- )
- ) {
- // register this series in the axis.series lookup
- series.insert(axis.series);
- // set this series.xAxis or series.yAxis reference
- /**
- * Read only. The unique xAxis object associated
- * with the series.
- *
- * @name Highcharts.Series#xAxis
- * @type {Highcharts.Axis}
- */
- /**
- * Read only. The unique yAxis object associated
- * with the series.
- *
- * @name Highcharts.Series#yAxis
- * @type {Highcharts.Axis}
- */
- series[AXIS] = axis;
- // mark dirty for redraw
- axis.isDirty = true;
- }
- });
- // The series needs an X and an Y axis
- if (!series[AXIS] && series.optionalAxis !== AXIS) {
- H.error(18, true, chart);
- }
- });
- });
- },
- /**
- * For simple series types like line and column, the data values are
- * held in arrays like xData and yData for quick lookup to find extremes
- * and more. For multidimensional series like bubble and map, this can
- * be extended with arrays like zData and valueData by adding to the
- * `series.parallelArrays` array.
- *
- * @private
- * @function Highcharts.Series#updateParallelArrays
- *
- * @param {Highcharts.Point} point
- *
- * @param {number|string} i
- */
- updateParallelArrays: function (point, i) {
- var series = point.series,
- args = arguments,
- fn = isNumber(i) ?
- // Insert the value in the given position
- function (key) {
- var val = key === 'y' && series.toYData ?
- series.toYData(point) :
- point[key];
- series[key + 'Data'][i] = val;
- } :
- // Apply the method specified in i with the following
- // arguments as arguments
- function (key) {
- Array.prototype[i].apply(
- series[key + 'Data'],
- Array.prototype.slice.call(args, 2)
- );
- };
- series.parallelArrays.forEach(fn);
- },
- /**
- * Return an auto incremented x value based on the pointStart and
- * pointInterval options. This is only used if an x value is not given
- * for the point that calls autoIncrement.
- *
- * @private
- * @function Highcharts.Series#autoIncrement
- *
- * @return {number}
- */
- autoIncrement: function () {
- var options = this.options,
- xIncrement = this.xIncrement,
- date,
- pointInterval,
- pointIntervalUnit = options.pointIntervalUnit,
- time = this.chart.time;
- xIncrement = pick(xIncrement, options.pointStart, 0);
- this.pointInterval = pointInterval = pick(
- this.pointInterval,
- options.pointInterval,
- 1
- );
- // Added code for pointInterval strings
- if (pointIntervalUnit) {
- date = new time.Date(xIncrement);
- if (pointIntervalUnit === 'day') {
- time.set(
- 'Date',
- date,
- time.get('Date', date) + pointInterval
- );
- } else if (pointIntervalUnit === 'month') {
- time.set(
- 'Month',
- date,
- time.get('Month', date) + pointInterval
- );
- } else if (pointIntervalUnit === 'year') {
- time.set(
- 'FullYear',
- date,
- time.get('FullYear', date) + pointInterval
- );
- }
- pointInterval = date.getTime() - xIncrement;
- }
- this.xIncrement = xIncrement + pointInterval;
- return xIncrement;
- },
- /**
- * Set the series options by merging from the options tree. Called
- * internally on initiating and updating series. This function will not
- * redraw the series. For API usage, use {@link Series#update}.
- *
- * @function Highcharts.Series#setOptions
- *
- * @param {Highcharts.SeriesOptionsType} itemOptions
- * The series options.
- *
- * @return {Highcharts.SeriesOptionsType}
- *
- * @fires Highcharts.Series#event:afterSetOptions
- */
- setOptions: function (itemOptions) {
- var chart = this.chart,
- chartOptions = chart.options,
- plotOptions = chartOptions.plotOptions,
- userOptions = chart.userOptions || {},
- userPlotOptions = userOptions.plotOptions || {},
- typeOptions = plotOptions[this.type],
- options,
- zones,
- zone,
- styledMode = chart.styledMode;
- // use copy to prevent undetected changes (#9762)
- this.userOptions = merge(itemOptions);
- // General series options take precedence over type options because
- // otherwise, default type options like column.animation would be
- // overwritten by the general option. But issues have been raised
- // here (#3881), and the solution may be to distinguish between
- // default option and userOptions like in the tooltip below.
- options = merge(
- typeOptions,
- plotOptions.series,
- itemOptions
- );
- // The tooltip options are merged between global and series specific
- // options. Importance order asscendingly:
- // globals: (1)tooltip, (2)plotOptions.series,
- // (3)plotOptions[this.type]
- // init userOptions with possible later updates: 4-6 like 1-3 and
- // (7)this series options
- this.tooltipOptions = merge(
- defaultOptions.tooltip, // 1
- defaultOptions.plotOptions.series &&
- defaultOptions.plotOptions.series.tooltip, // 2
- defaultOptions.plotOptions[this.type].tooltip, // 3
- chartOptions.tooltip.userOptions, // 4
- plotOptions.series && plotOptions.series.tooltip, // 5
- plotOptions[this.type].tooltip, // 6
- itemOptions.tooltip // 7
- );
- // When shared tooltip, stickyTracking is true by default,
- // unless user says otherwise.
- this.stickyTracking = pick(
- itemOptions.stickyTracking,
- userPlotOptions[this.type] &&
- userPlotOptions[this.type].stickyTracking,
- userPlotOptions.series && userPlotOptions.series.stickyTracking,
- (
- this.tooltipOptions.shared && !this.noSharedTooltip ?
- true :
- options.stickyTracking
- )
- );
- // Delete marker object if not allowed (#1125)
- if (typeOptions.marker === null) {
- delete options.marker;
- }
- // Handle color zones
- this.zoneAxis = options.zoneAxis;
- zones = this.zones = (options.zones || []).slice();
- if (
- (options.negativeColor || options.negativeFillColor) &&
- !options.zones
- ) {
- zone = {
- value:
- options[this.zoneAxis + 'Threshold'] ||
- options.threshold ||
- 0,
- className: 'highcharts-negative'
- };
- if (!styledMode) {
- zone.color = options.negativeColor;
- zone.fillColor = options.negativeFillColor;
- }
- zones.push(zone);
- }
- if (zones.length) { // Push one extra zone for the rest
- if (defined(zones[zones.length - 1].value)) {
- zones.push(styledMode ? {} : {
- color: this.color,
- fillColor: this.fillColor
- });
- }
- }
- fireEvent(this, 'afterSetOptions', { options: options });
- return options;
- },
- /**
- * Return series name in "Series {Number}" format or the one defined by
- * a user. This method can be simply overridden as series name format
- * can vary (e.g. technical indicators).
- *
- * @function Highcharts.Series#getName
- *
- * @return {string}
- * The series name.
- */
- getName: function () {
- // #4119
- return pick(this.options.name, 'Series ' + (this.index + 1));
- },
- /**
- * @private
- * @function Highcharts.Series#getCyclic
- *
- * @param {string} prop
- *
- * @param {*} value
- *
- * @param {*} [defaults]
- */
- getCyclic: function (prop, value, defaults) {
- var i,
- chart = this.chart,
- userOptions = this.userOptions,
- indexName = prop + 'Index',
- counterName = prop + 'Counter',
- len = defaults ? defaults.length : pick(
- chart.options.chart[prop + 'Count'],
- chart[prop + 'Count']
- ),
- setting;
- if (!value) {
- // Pick up either the colorIndex option, or the _colorIndex
- // after Series.update()
- setting = pick(
- userOptions[indexName],
- userOptions['_' + indexName]
- );
- if (defined(setting)) { // after Series.update()
- i = setting;
- } else {
- // #6138
- if (!chart.series.length) {
- chart[counterName] = 0;
- }
- userOptions['_' + indexName] = i = chart[counterName] % len;
- chart[counterName] += 1;
- }
- if (defaults) {
- value = defaults[i];
- }
- }
- // Set the colorIndex
- if (i !== undefined) {
- this[indexName] = i;
- }
- this[prop] = value;
- },
- /**
- * Get the series' color based on either the options or pulled from
- * global options.
- *
- * @function Highcharts.Series#getColor
- */
- getColor: function () {
- if (this.chart.styledMode) {
- this.getCyclic('color');
- } else if (this.options.colorByPoint) {
- // #4359, selected slice got series.color even when colorByPoint
- // was set.
- this.options.color = null;
- } else {
- this.getCyclic(
- 'color',
- this.options.color || defaultPlotOptions[this.type].color,
- this.chart.options.colors
- );
- }
- },
- /**
- * Get the series' symbol based on either the options or pulled from
- * global options.
- *
- * @function Highcharts.Series#getSymbol
- */
- getSymbol: function () {
- var seriesMarkerOption = this.options.marker;
- this.getCyclic(
- 'symbol',
- seriesMarkerOption.symbol,
- this.chart.options.symbols
- );
- },
- /**
- * @private
- * @borrows LegendSymbolMixin.drawLineMarker as Highcharts.Series#drawLegendSymbol
- */
- drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
- /**
- * Internal function called from setData. If the point count is the same
- * as is was, or if there are overlapping X values, just run
- * Point.update which is cheaper, allows animation, and keeps references
- * to points. This also allows adding or removing points if the X-es
- * don't match.
- *
- * @private
- * @function Highcharts.Series#updateData
- *
- * @param {Array<*>} data
- *
- * @return {boolean}
- */
- updateData: function (data) {
- var options = this.options,
- oldData = this.points,
- pointsToAdd = [],
- hasUpdatedByKey,
- i,
- point,
- lastIndex,
- requireSorting = this.requireSorting;
- this.xIncrement = null;
- // Iterate the new data
- data.forEach(function (pointOptions) {
- var id,
- matchingPoint,
- x,
- pointIndex,
- optionsObject = (
- H.defined(pointOptions) &&
- this.pointClass.prototype.optionsToObject.call(
- { series: this },
- pointOptions
- )
- ) || {};
- // Get the x of the new data point
- x = optionsObject.x;
- id = optionsObject.id;
- if (id || isNumber(x)) {
- if (id) {
- matchingPoint = this.chart.get(id);
- pointIndex = matchingPoint && matchingPoint.index;
- }
- // Search for the same X in the existing data set
- if (pointIndex === undefined && isNumber(x)) {
- pointIndex = this.xData.indexOf(x, lastIndex);
- }
- // Matching X not found
- // or used already due to ununique x values (#8995),
- // add point (but later)
- if (
- pointIndex === -1 ||
- pointIndex === undefined ||
- oldData[pointIndex].touched
- ) {
- pointsToAdd.push(pointOptions);
- // Matching X found, update
- } else if (pointOptions !== options.data[pointIndex]) {
- oldData[pointIndex].update(
- pointOptions,
- false,
- null,
- false
- );
- // Mark it touched, below we will remove all points that
- // are not touched.
- oldData[pointIndex].touched = true;
- // Speed optimize by only searching after last known
- // index. Performs ~20% bettor on large data sets.
- if (requireSorting) {
- lastIndex = pointIndex + 1;
- }
- // Point exists, no changes, don't remove it
- } else if (oldData[pointIndex]) {
- oldData[pointIndex].touched = true;
- }
- hasUpdatedByKey = true;
- }
- }, this);
- // Remove points that don't exist in the updated data set
- if (hasUpdatedByKey) {
- i = oldData.length;
- while (i--) {
- point = oldData[i];
- if (!point.touched) {
- point.remove(false);
- }
- point.touched = false;
- }
- // If we did not find keys (x-values), and the length is the same,
- // update one-to-one
- } else if (data.length === oldData.length) {
- data.forEach(function (point, i) {
- // .update doesn't exist on a linked, hidden series (#3709)
- if (oldData[i].update && point !== options.data[i]) {
- oldData[i].update(point, false, null, false);
- }
- });
- // Did not succeed in updating data
- } else {
- return false;
- }
- // Add new points
- pointsToAdd.forEach(function (point) {
- this.addPoint(point, false);
- }, this);
- return true;
- },
- /**
- * Apply a new set of data to the series and optionally redraw it. The
- * new data array is passed by reference (except in case of
- * `updatePoints`), and may later be mutated when updating the chart
- * data.
- *
- * Note the difference in behaviour when setting the same amount of
- * points, or a different amount of points, as handled by the
- * `updatePoints` parameter.
- *
- * @sample highcharts/members/series-setdata/
- * Set new data from a button
- * @sample highcharts/members/series-setdata-pie/
- * Set data in a pie
- * @sample stock/members/series-setdata/
- * Set new data in Highstock
- * @sample maps/members/series-setdata/
- * Set new data in Highmaps
- *
- * @function Highcharts.Series#setData
- *
- * @param {Array<*>} data
- * Takes an array of data in the same format as described under
- * `series.{type}.data` for the given series type, for example a
- * line series would take data in the form described under
- * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the series is altered. If
- * doing more operations on the chart, it is a good idea to set
- * redraw to false and call {@link Chart#redraw} after.
- *
- * @param {Highcharts.AnimationOptionsObject} [animation]
- * When the updated data is the same length as the existing data,
- * points will be updated by default, and animation visualizes
- * how the points are changed. Set false to disable animation, or
- * a configuration object to set duration or easing.
- *
- * @param {boolean} [updatePoints=true]
- * When this is true, points will be updated instead of replaced
- * whenever possible. This occurs a) when the updated data is the
- * same length as the existing data, b) when points are matched
- * by their id's, or c) when points can be matched by X values.
- * This allows updating with animation and performs better. In
- * this case, the original array is not passed by reference. Set
- * `false` to prevent.
- */
- setData: function (data, redraw, animation, updatePoints) {
- var series = this,
- oldData = series.points,
- oldDataLength = (oldData && oldData.length) || 0,
- dataLength,
- options = series.options,
- chart = series.chart,
- firstPoint = null,
- xAxis = series.xAxis,
- i,
- turboThreshold = options.turboThreshold,
- pt,
- xData = this.xData,
- yData = this.yData,
- pointArrayMap = series.pointArrayMap,
- valueCount = pointArrayMap && pointArrayMap.length,
- keys = options.keys,
- indexOfX = 0,
- indexOfY = 1,
- updatedData;
- data = data || [];
- dataLength = data.length;
- redraw = pick(redraw, true);
- // If the point count is the same as is was, just run Point.update
- // which is cheaper, allows animation, and keeps references to
- // points.
- if (
- updatePoints !== false &&
- dataLength &&
- oldDataLength &&
- !series.cropped &&
- !series.hasGroupedData &&
- series.visible &&
- // Soft updating has no benefit in boost, and causes JS error
- // (#8355)
- !series.isSeriesBoosting
- ) {
- updatedData = this.updateData(data);
- }
- if (!updatedData) {
- // Reset properties
- series.xIncrement = null;
- series.colorCounter = 0; // for series with colorByPoint (#1547)
- // Update parallel arrays
- this.parallelArrays.forEach(function (key) {
- series[key + 'Data'].length = 0;
- });
- // In turbo mode, only one- or twodimensional arrays of numbers
- // are allowed. The first value is tested, and we assume that
- // all the rest are defined the same way. Although the 'for'
- // loops are similar, they are repeated inside each if-else
- // conditional for max performance.
- if (turboThreshold && dataLength > turboThreshold) {
- // find the first non-null point
- i = 0;
- while (firstPoint === null && i < dataLength) {
- firstPoint = data[i];
- i++;
- }
- if (isNumber(firstPoint)) { // assume all points are numbers
- for (i = 0; i < dataLength; i++) {
- xData[i] = this.autoIncrement();
- yData[i] = data[i];
- }
- // Assume all points are arrays when first point is
- } else if (isArray(firstPoint)) {
- if (valueCount) { // [x, low, high] or [x, o, h, l, c]
- for (i = 0; i < dataLength; i++) {
- pt = data[i];
- xData[i] = pt[0];
- yData[i] = pt.slice(1, valueCount + 1);
- }
- } else { // [x, y]
- if (keys) {
- indexOfX = keys.indexOf('x');
- indexOfY = keys.indexOf('y');
- indexOfX = indexOfX >= 0 ? indexOfX : 0;
- indexOfY = indexOfY >= 0 ? indexOfY : 1;
- }
- for (i = 0; i < dataLength; i++) {
- pt = data[i];
- xData[i] = pt[indexOfX];
- yData[i] = pt[indexOfY];
- }
- }
- } else {
- // Highcharts expects configs to be numbers or arrays in
- // turbo mode
- H.error(12, false, chart);
- }
- } else {
- for (i = 0; i < dataLength; i++) {
- if (data[i] !== undefined) { // stray commas in oldIE
- pt = { series: series };
- series.pointClass.prototype.applyOptions.apply(
- pt,
- [data[i]]
- );
- series.updateParallelArrays(pt, i);
- }
- }
- }
- // Forgetting to cast strings to numbers is a common caveat when
- // handling CSV or JSON
- if (yData && isString(yData[0])) {
- H.error(14, true, chart);
- }
- series.data = [];
- series.options.data = series.userOptions.data = data;
- // destroy old points
- i = oldDataLength;
- while (i--) {
- if (oldData[i] && oldData[i].destroy) {
- oldData[i].destroy();
- }
- }
- // reset minRange (#878)
- if (xAxis) {
- xAxis.minRange = xAxis.userMinRange;
- }
- // redraw
- series.isDirty = chart.isDirtyBox = true;
- series.isDirtyData = !!oldData;
- animation = false;
- }
- // Typically for pie series, points need to be processed and
- // generated prior to rendering the legend
- if (options.legendType === 'point') {
- this.processData();
- this.generatePoints();
- }
- if (redraw) {
- chart.redraw(animation);
- }
- },
- /**
- * Internal function to process the data by cropping away unused data
- * points if the series is longer than the crop threshold. This saves
- * computing time for large series. In Highstock, this function is
- * extended to provide data grouping.
- *
- * @private
- * @function Highcharts.Series#processData
- *
- * @param {boolean} force
- * Force data grouping.
- *
- * @return {boolean|undefined}
- */
- processData: function (force) {
- var series = this,
- processedXData = series.xData, // copied during slice operation
- processedYData = series.yData,
- dataLength = processedXData.length,
- croppedData,
- cropStart = 0,
- cropped,
- distance,
- closestPointRange,
- xAxis = series.xAxis,
- i, // loop variable
- options = series.options,
- cropThreshold = options.cropThreshold,
- getExtremesFromAll =
- series.getExtremesFromAll ||
- options.getExtremesFromAll, // #4599
- isCartesian = series.isCartesian,
- xExtremes,
- val2lin = xAxis && xAxis.val2lin,
- isLog = xAxis && xAxis.isLog,
- throwOnUnsorted = series.requireSorting,
- min,
- max;
- // If the series data or axes haven't changed, don't go through
- // this. Return false to pass the message on to override methods
- // like in data grouping.
- if (
- isCartesian &&
- !series.isDirty &&
- !xAxis.isDirty &&
- !series.yAxis.isDirty &&
- !force
- ) {
- return false;
- }
- if (xAxis) {
- // corrected for log axis (#3053)
- xExtremes = xAxis.getExtremes();
- min = xExtremes.min;
- max = xExtremes.max;
- }
- // optionally filter out points outside the plot area
- if (
- isCartesian &&
- series.sorted &&
- !getExtremesFromAll &&
- (
- !cropThreshold ||
- dataLength > cropThreshold ||
- series.forceCrop
- )
- ) {
- // it's outside current extremes
- if (
- processedXData[dataLength - 1] < min ||
- processedXData[0] > max
- ) {
- processedXData = [];
- processedYData = [];
- // only crop if it's actually spilling out
- } else if (
- series.yData && (
- processedXData[0] < min ||
- processedXData[dataLength - 1] > max
- )
- ) {
- croppedData = this.cropData(
- series.xData,
- series.yData,
- min,
- max
- );
- processedXData = croppedData.xData;
- processedYData = croppedData.yData;
- cropStart = croppedData.start;
- cropped = true;
- }
- }
- // Find the closest distance between processed points
- i = processedXData.length || 1;
- while (--i) {
- distance = (
- isLog ?
- (
- val2lin(processedXData[i]) -
- val2lin(processedXData[i - 1])
- ) :
- processedXData[i] - processedXData[i - 1]
- );
- if (
- distance > 0 &&
- (
- closestPointRange === undefined ||
- distance < closestPointRange
- )
- ) {
- closestPointRange = distance;
- // Unsorted data is not supported by the line tooltip, as well
- // as data grouping and navigation in Stock charts (#725) and
- // width calculation of columns (#1900)
- } else if (distance < 0 && throwOnUnsorted) {
- H.error(15, false, series.chart);
- throwOnUnsorted = false; // Only once
- }
- }
- // Record the properties
- series.cropped = cropped; // undefined or true
- series.cropStart = cropStart;
- series.processedXData = processedXData;
- series.processedYData = processedYData;
- series.closestPointRange = closestPointRange;
- },
- /**
- * Iterate over xData and crop values between min and max. Returns
- * object containing crop start/end cropped xData with corresponding
- * part of yData, dataMin and dataMax within the cropped range.
- *
- * @private
- * @function Highcharts.Series#cropData
- *
- * @param {Array<number>} xData
- *
- * @param {Array<number>} yData
- *
- * @param {number} min
- *
- * @param {number} max
- *
- * @param {number} [cropShoulder]
- *
- * @return {*}
- */
- cropData: function (xData, yData, min, max, cropShoulder) {
- var dataLength = xData.length,
- cropStart = 0,
- cropEnd = dataLength,
- i,
- j;
- // line-type series need one point outside
- cropShoulder = pick(cropShoulder, this.cropShoulder, 1);
- // iterate up to find slice start
- for (i = 0; i < dataLength; i++) {
- if (xData[i] >= min) {
- cropStart = Math.max(0, i - cropShoulder);
- break;
- }
- }
- // proceed to find slice end
- for (j = i; j < dataLength; j++) {
- if (xData[j] > max) {
- cropEnd = j + cropShoulder;
- break;
- }
- }
- return {
- xData: xData.slice(cropStart, cropEnd),
- yData: yData.slice(cropStart, cropEnd),
- start: cropStart,
- end: cropEnd
- };
- },
- /**
- * Generate the data point after the data has been processed by cropping
- * away unused points and optionally grouped in Highcharts Stock.
- *
- * @private
- * @function Highcharts.Series#generatePoints
- */
- generatePoints: function () {
- var series = this,
- options = series.options,
- dataOptions = options.data,
- data = series.data,
- dataLength,
- processedXData = series.processedXData,
- processedYData = series.processedYData,
- PointClass = series.pointClass,
- processedDataLength = processedXData.length,
- cropStart = series.cropStart || 0,
- cursor,
- hasGroupedData = series.hasGroupedData,
- keys = options.keys,
- point,
- points = [],
- i;
- if (!data && !hasGroupedData) {
- var arr = [];
- arr.length = dataOptions.length;
- data = series.data = arr;
- }
- if (keys && hasGroupedData) {
- // grouped data has already applied keys (#6590)
- series.options.keys = false;
- }
- for (i = 0; i < processedDataLength; i++) {
- cursor = cropStart + i;
- if (!hasGroupedData) {
- point = data[cursor];
- if (!point && dataOptions[cursor] !== undefined) { // #970
- data[cursor] = point = (new PointClass()).init(
- series,
- dataOptions[cursor],
- processedXData[i]
- );
- }
- } else {
- // splat the y data in case of ohlc data array
- point = (new PointClass()).init(
- series,
- [processedXData[i]].concat(splat(processedYData[i]))
- );
- /**
- * Highstock only. If a point object is created by data
- * grouping, it doesn't reflect actual points in the raw
- * data. In this case, the `dataGroup` property holds
- * information that points back to the raw data.
- *
- * - `dataGroup.start` is the index of the first raw data
- * point in the group.
- *
- * - `dataGroup.length` is the amount of points in the
- * group.
- *
- * @product highstock
- *
- * @name Highcharts.Point#dataGroup
- * @type {Highcharts.SVGElement|undefined}
- */
- point.dataGroup = series.groupMap[i];
- if (point.dataGroup.options) {
- point.options = point.dataGroup.options;
- extend(point, point.dataGroup.options);
- // Collision of props and options (#9770)
- delete point.dataLabels;
- }
- }
- if (point) { // #6279
- point.index = cursor; // For faster access in Point.update
- points[i] = point;
- }
- }
- // restore keys options (#6590)
- series.options.keys = keys;
- // Hide cropped-away points - this only runs when the number of
- // points is above cropThreshold, or when swithching view from
- // non-grouped data to grouped data (#637)
- if (
- data &&
- (
- processedDataLength !== (dataLength = data.length) ||
- hasGroupedData
- )
- ) {
- for (i = 0; i < dataLength; i++) {
- // when has grouped data, clear all points
- if (i === cropStart && !hasGroupedData) {
- i += processedDataLength;
- }
- if (data[i]) {
- data[i].destroyElements();
- data[i].plotX = undefined; // #1003
- }
- }
- }
- /**
- * Read only. An array containing those values converted to points.
- * In case the series data length exceeds the `cropThreshold`, or if
- * the data is grouped, `series.data` doesn't contain all the
- * points. Also, in case a series is hidden, the `data` array may be
- * empty. To access raw values, `series.options.data` will always be
- * up to date. `Series.data` only contains the points that have been
- * created on demand. To modify the data, use
- * {@link Highcharts.Series#setData} or
- * {@link Highcharts.Point#update}.
- *
- * @see Series.points
- *
- * @name Highcharts.Series#data
- * @type {Array<Highcharts.Point>}
- */
- series.data = data;
- /**
- * An array containing all currently visible point objects. In case
- * of cropping, the cropped-away points are not part of this array.
- * The `series.points` array starts at `series.cropStart` compared
- * to `series.data` and `series.options.data`. If however the series
- * data is grouped, these can't be correlated one to one. To modify
- * the data, use {@link Highcharts.Series#setData} or
- * {@link Highcharts.Point#update}.
- *
- * @name Highcharts.Series#points
- * @type {Array<Highcharts.Point>}
- */
- series.points = points;
- fireEvent(this, 'afterGeneratePoints');
- },
- /**
- * Calculate Y extremes for the visible data. The result is set as
- * `dataMin` and `dataMax` on the Series item.
- *
- * @function Highcharts.Series#getExtremes
- *
- * @param {Array<number>} [yData]
- * The data to inspect. Defaults to the current data within the
- * visible range.
- */
- getExtremes: function (yData) {
- var xAxis = this.xAxis,
- yAxis = this.yAxis,
- xData = this.processedXData,
- yDataLength,
- activeYData = [],
- activeCounter = 0,
- // #2117, need to compensate for log X axis
- xExtremes = xAxis.getExtremes(),
- xMin = xExtremes.min,
- xMax = xExtremes.max,
- validValue,
- withinRange,
- // Handle X outside the viewed area. This does not work with
- // non-sorted data like scatter (#7639).
- shoulder = this.requireSorting ? 1 : 0,
- x,
- y,
- i,
- j;
- yData = yData || this.stackedYData || this.processedYData || [];
- yDataLength = yData.length;
- for (i = 0; i < yDataLength; i++) {
- x = xData[i];
- y = yData[i];
- // For points within the visible range, including the first
- // point outside the visible range (#7061), consider y extremes.
- validValue = (
- (isNumber(y, true) || isArray(y)) &&
- (!yAxis.positiveValuesOnly || (y.length || y > 0))
- );
- withinRange = (
- this.getExtremesFromAll ||
- this.options.getExtremesFromAll ||
- this.cropped ||
- (
- (xData[i + shoulder] || x) >= xMin &&
- (xData[i - shoulder] || x) <= xMax
- )
- );
- if (validValue && withinRange) {
- j = y.length;
- if (j) { // array, like ohlc or range data
- while (j--) {
- if (typeof y[j] === 'number') { // #7380
- activeYData[activeCounter++] = y[j];
- }
- }
- } else {
- activeYData[activeCounter++] = y;
- }
- }
- }
- this.dataMin = arrayMin(activeYData);
- this.dataMax = arrayMax(activeYData);
- fireEvent(this, 'afterGetExtremes');
- },
- /**
- * Translate data points from raw data values to chart specific
- * positioning data needed later in the `drawPoints` and `drawGraph`
- * functions. This function can be overridden in plugins and custom
- * series type implementations.
- *
- * @function Highcharts.Series#translate
- *
- * @fires Highcharts.Series#events:translate
- */
- translate: function () {
- if (!this.processedXData) { // hidden series
- this.processData();
- }
- this.generatePoints();
- var series = this,
- options = series.options,
- stacking = options.stacking,
- xAxis = series.xAxis,
- categories = xAxis.categories,
- yAxis = series.yAxis,
- points = series.points,
- dataLength = points.length,
- hasModifyValue = !!series.modifyValue,
- i,
- pointPlacement = series.pointPlacementToXValue(), // #7860
- dynamicallyPlaced = isNumber(pointPlacement),
- threshold = options.threshold,
- stackThreshold = options.startFromThreshold ? threshold : 0,
- plotX,
- plotY,
- lastPlotX,
- stackIndicator,
- zoneAxis = this.zoneAxis || 'y',
- closestPointRangePx = Number.MAX_VALUE;
- // Plotted coordinates need to be within a limited range. Drawing
- // too far outside the viewport causes various rendering issues
- // (#3201, #3923, #7555).
- function limitedRange(val) {
- return Math.min(Math.max(-1e5, val), 1e5);
- }
- // Translate each point
- for (i = 0; i < dataLength; i++) {
- var point = points[i],
- xValue = point.x,
- yValue = point.y,
- yBottom = point.low,
- stack = stacking && yAxis.stacks[(
- series.negStacks &&
- yValue < (stackThreshold ? 0 : threshold) ? '-' : ''
- ) + series.stackKey],
- pointStack,
- stackValues;
- // Discard disallowed y values for log axes (#3434)
- if (yAxis.positiveValuesOnly &&
- yValue !== null &&
- yValue <= 0
- ) {
- point.isNull = true;
- }
- // Get the plotX translation
- point.plotX = plotX = correctFloat( // #5236
- limitedRange(xAxis.translate( // #3923
- xValue,
- 0,
- 0,
- 0,
- 1,
- pointPlacement,
- this.type === 'flags'
- )) // #3923
- );
- // Calculate the bottom y value for stacked series
- if (
- stacking &&
- series.visible &&
- !point.isNull &&
- stack &&
- stack[xValue]
- ) {
- stackIndicator = series.getStackIndicator(
- stackIndicator,
- xValue,
- series.index
- );
- pointStack = stack[xValue];
- stackValues = pointStack.points[stackIndicator.key];
- yBottom = stackValues[0];
- yValue = stackValues[1];
- if (
- yBottom === stackThreshold &&
- stackIndicator.key === stack[xValue].base
- ) {
- yBottom = (
- pick(isNumber(threshold) && threshold, yAxis.min)
- );
- }
- // #1200, #1232
- if (yAxis.positiveValuesOnly && yBottom <= 0) {
- yBottom = null;
- }
- point.total = point.stackTotal = pointStack.total;
- point.percentage =
- pointStack.total &&
- (point.y / pointStack.total * 100);
- point.stackY = yValue;
- // Place the stack label
- pointStack.setOffset(
- series.pointXOffset || 0,
- series.barW || 0
- );
- }
- // Set translated yBottom or remove it
- point.yBottom = defined(yBottom) ?
- limitedRange(yAxis.translate(yBottom, 0, 1, 0, 1)) :
- null;
- // general hook, used for Highstock compare mode
- if (hasModifyValue) {
- yValue = series.modifyValue(yValue, point);
- }
- // Set the the plotY value, reset it for redraws
- // #3201
- point.plotY = plotY = (
- (typeof yValue === 'number' && yValue !== Infinity) ?
- limitedRange(yAxis.translate(yValue, 0, 1, 0, 1)) :
- undefined
- );
- point.isInside =
- plotY !== undefined &&
- plotY >= 0 &&
- plotY <= yAxis.len && // #3519
- plotX >= 0 &&
- plotX <= xAxis.len;
- // Set client related positions for mouse tracking
- point.clientX = dynamicallyPlaced ?
- correctFloat(
- xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)
- ) :
- plotX; // #1514, #5383, #5518
- // Negative points. For bubble charts, this means negative z
- // values (#9728)
- point.negative = point[zoneAxis] < (
- options[zoneAxis + 'Threshold'] ||
- threshold ||
- 0
- );
- // some API data
- point.category = (
- categories &&
- categories[point.x] !== undefined ?
- categories[point.x] :
- point.x
- );
- // Determine auto enabling of markers (#3635, #5099)
- if (!point.isNull) {
- if (lastPlotX !== undefined) {
- closestPointRangePx = Math.min(
- closestPointRangePx,
- Math.abs(plotX - lastPlotX)
- );
- }
- lastPlotX = plotX;
- }
- // Find point zone
- point.zone = this.zones.length && point.getZone();
- }
- series.closestPointRangePx = closestPointRangePx;
- fireEvent(this, 'afterTranslate');
- },
- /**
- * Return the series points with null points filtered out.
- *
- * @param {Array<Highcharts.Point>} [points]
- * The points to inspect, defaults to {@link Series.points}.
- *
- * @param {boolean} [insideOnly=false]
- * Whether to inspect only the points that are inside the visible
- * view.
- *
- * @return {Array<Highcharts.Point>}
- * The valid points.
- */
- getValidPoints: function (points, insideOnly) {
- var chart = this.chart;
- // #3916, #5029, #5085
- return (points || this.points || []).filter(
- function isValidPoint(point) {
- if (insideOnly && !chart.isInsidePlot(
- point.plotX,
- point.plotY,
- chart.inverted
- )) {
- return false;
- }
- return !point.isNull;
- }
- );
- },
- /**
- * Set the clipping for the series. For animated series it is called
- * twice, first to initiate animating the clip then the second time
- * without the animation to set the final clip.
- *
- * @private
- * @function Highcharts.Series#setClip
- *
- * @param {boolean} [animation]
- */
- setClip: function (animation) {
- var chart = this.chart,
- options = this.options,
- renderer = chart.renderer,
- inverted = chart.inverted,
- seriesClipBox = this.clipBox,
- clipBox = seriesClipBox || chart.clipBox,
- sharedClipKey =
- this.sharedClipKey ||
- [
- '_sharedClip',
- animation && animation.duration,
- animation && animation.easing,
- clipBox.height,
- options.xAxis,
- options.yAxis
- ].join(','), // #4526
- clipRect = chart[sharedClipKey],
- markerClipRect = chart[sharedClipKey + 'm'];
- // If a clipping rectangle with the same properties is currently
- // present in the chart, use that.
- if (!clipRect) {
- // When animation is set, prepare the initial positions
- if (animation) {
- clipBox.width = 0;
- if (inverted) {
- clipBox.x = chart.plotSizeX;
- }
- chart[sharedClipKey + 'm'] = markerClipRect = renderer
- .clipRect(
- // include the width of the first marker
- inverted ? chart.plotSizeX + 99 : -99,
- inverted ? -chart.plotLeft : -chart.plotTop,
- 99,
- inverted ? chart.chartWidth : chart.chartHeight
- );
- }
- chart[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
- // Create hashmap for series indexes
- clipRect.count = { length: 0 };
- }
- if (animation) {
- if (!clipRect.count[this.index]) {
- clipRect.count[this.index] = true;
- clipRect.count.length += 1;
- }
- }
- if (options.clip !== false) {
- this.group.clip(
- animation || seriesClipBox ? clipRect : chart.clipRect
- );
- this.markerGroup.clip(markerClipRect);
- this.sharedClipKey = sharedClipKey;
- }
- // Remove the shared clipping rectangle when all series are shown
- if (!animation) {
- if (clipRect.count[this.index]) {
- delete clipRect.count[this.index];
- clipRect.count.length -= 1;
- }
- if (
- clipRect.count.length === 0 &&
- sharedClipKey &&
- chart[sharedClipKey]
- ) {
- if (!seriesClipBox) {
- chart[sharedClipKey] = chart[sharedClipKey].destroy();
- }
- if (chart[sharedClipKey + 'm']) {
- chart[sharedClipKey + 'm'] =
- chart[sharedClipKey + 'm'].destroy();
- }
- }
- }
- },
- /**
- * Animate in the series. Called internally twice. First with the `init`
- * parameter set to true, which sets up the initial state of the
- * animation. Then when ready, it is called with the `init` parameter
- * undefined, in order to perform the actual animation. After the
- * second run, the function is removed.
- *
- * @function Highcharts.Series#animate
- *
- * @param {boolean} init
- * Initialize the animation.
- */
- animate: function (init) {
- var series = this,
- chart = series.chart,
- clipRect,
- animation = animObject(series.options.animation),
- sharedClipKey;
- // Initialize the animation. Set up the clipping rectangle.
- if (init) {
- series.setClip(animation);
- // Run the animation
- } else {
- sharedClipKey = this.sharedClipKey;
- clipRect = chart[sharedClipKey];
- if (clipRect) {
- clipRect.animate({
- width: chart.plotSizeX,
- x: 0
- }, animation);
- }
- if (chart[sharedClipKey + 'm']) {
- chart[sharedClipKey + 'm'].animate({
- width: chart.plotSizeX + 99,
- x: 0
- }, animation);
- }
- // Delete this function to allow it only once
- series.animate = null;
- }
- },
- /**
- * This runs after animation to land on the final plot clipping.
- *
- * @private
- * @function Highcharts.Series#afterAnimate
- *
- * @fires Highcharts.Series#event:afterAnimate
- */
- afterAnimate: function () {
- this.setClip();
- fireEvent(this, 'afterAnimate');
- this.finishedAnimating = true;
- },
- /**
- * Draw the markers for line-like series types, and columns or other
- * graphical representation for {@link Point} objects for other series
- * types. The resulting element is typically stored as
- * {@link Point.graphic}, and is created on the first call and updated
- * and moved on subsequent calls.
- *
- * @function Highcharts.Series#drawPoints
- */
- drawPoints: function () {
- var series = this,
- points = series.points,
- chart = series.chart,
- i,
- point,
- symbol,
- graphic,
- options = series.options,
- seriesMarkerOptions = options.marker,
- pointMarkerOptions,
- hasPointMarker,
- enabled,
- isInside,
- markerGroup = series[series.specialGroup] || series.markerGroup,
- xAxis = series.xAxis,
- markerAttribs,
- globallyEnabled = pick(
- seriesMarkerOptions.enabled,
- !xAxis || xAxis.isRadial ? true : null,
- // Use larger or equal as radius is null in bubbles (#6321)
- series.closestPointRangePx >= (
- seriesMarkerOptions.enabledThreshold *
- seriesMarkerOptions.radius
- )
- );
- if (seriesMarkerOptions.enabled !== false ||
- series._hasPointMarkers
- ) {
- for (i = 0; i < points.length; i++) {
- point = points[i];
- graphic = point.graphic;
- pointMarkerOptions = point.marker || {};
- hasPointMarker = !!point.marker;
- enabled = (
- globallyEnabled &&
- pointMarkerOptions.enabled === undefined
- ) || pointMarkerOptions.enabled;
- isInside = point.isInside !== false;
- // only draw the point if y is defined
- if (enabled && !point.isNull) {
- // Shortcuts
- symbol = pick(pointMarkerOptions.symbol, series.symbol);
- markerAttribs = series.markerAttribs(
- point,
- point.selected && 'select'
- );
- if (graphic) { // update
- // Since the marker group isn't clipped, each
- // individual marker must be toggled
- graphic[isInside ? 'show' : 'hide'](true)
- .animate(markerAttribs);
- } else if (
- isInside &&
- (markerAttribs.width > 0 || point.hasImage)
- ) {
- /**
- * The graphic representation of the point.
- * Typically this is a simple shape, like a `rect`
- * for column charts or `path` for line markers, but
- * for some complex series types like boxplot or 3D
- * charts, the graphic may be a `g` element
- * containing other shapes. The graphic is generated
- * the first time {@link Series#drawPoints} runs,
- * and updated and moved on subsequent runs.
- *
- * @name Point#graphic
- * @type {SVGElement}
- */
- point.graphic = graphic = chart.renderer
- .symbol(
- symbol,
- markerAttribs.x,
- markerAttribs.y,
- markerAttribs.width,
- markerAttribs.height,
- hasPointMarker ?
- pointMarkerOptions :
- seriesMarkerOptions
- )
- .add(markerGroup);
- }
- // Presentational attributes
- if (graphic && !chart.styledMode) {
- graphic.attr(
- series.pointAttribs(
- point,
- point.selected && 'select'
- )
- );
- }
- if (graphic) {
- graphic.addClass(point.getClassName(), true);
- }
- } else if (graphic) {
- point.graphic = graphic.destroy(); // #1269
- }
- }
- }
- },
- /**
- * Get non-presentational attributes for a point. Used internally for
- * both styled mode and classic. Can be overridden for different series
- * types.
- *
- * @see Series#pointAttribs
- *
- * @function Highcharts.Series#markerAttribs
- *
- * @param {Highcharts.Point} point
- * The Point to inspect.
- *
- * @param {string} [state]
- * The state, can be either `hover`, `select` or undefined.
- *
- * @return {Highcharts.SVGAttributes}
- * A hash containing those attributes that are not settable from
- * CSS.
- */
- markerAttribs: function (point, state) {
- var seriesMarkerOptions = this.options.marker,
- seriesStateOptions,
- pointMarkerOptions = point.marker || {},
- symbol = (
- pointMarkerOptions.symbol || seriesMarkerOptions.symbol
- ),
- pointStateOptions,
- radius = pick(
- pointMarkerOptions.radius,
- seriesMarkerOptions.radius
- ),
- attribs;
- // Handle hover and select states
- if (state) {
- seriesStateOptions = seriesMarkerOptions.states[state];
- pointStateOptions = pointMarkerOptions.states &&
- pointMarkerOptions.states[state];
- radius = pick(
- pointStateOptions && pointStateOptions.radius,
- seriesStateOptions && seriesStateOptions.radius,
- radius + (
- seriesStateOptions && seriesStateOptions.radiusPlus ||
- 0
- )
- );
- }
- point.hasImage = symbol && symbol.indexOf('url') === 0;
- if (point.hasImage) {
- radius = 0; // and subsequently width and height is not set
- }
- attribs = {
- x: Math.floor(point.plotX) - radius, // Math.floor for #1843
- y: point.plotY - radius
- };
- if (radius) {
- attribs.width = attribs.height = 2 * radius;
- }
- return attribs;
- },
- /**
- * Internal function to get presentational attributes for each point.
- * Unlike {@link Series#markerAttribs}, this function should return
- * those attributes that can also be set in CSS. In styled mode,
- * `pointAttribs` won't be called.
- *
- * @private
- * @function Highcharts.Series#pointAttribs
- *
- * @param {Highcharts.Point} point
- * The point instance to inspect.
- *
- * @param {string} [state]
- * The point state, can be either `hover`, `select` or undefined
- * for normal state.
- *
- * @return {Highcharts.SVGAttributes}
- * The presentational attributes to be set on the point.
- */
- pointAttribs: function (point, state) {
- var seriesMarkerOptions = this.options.marker,
- seriesStateOptions,
- pointOptions = point && point.options,
- pointMarkerOptions = (
- (pointOptions && pointOptions.marker) || {}
- ),
- pointStateOptions,
- color = this.color,
- pointColorOption = pointOptions && pointOptions.color,
- pointColor = point && point.color,
- strokeWidth = pick(
- pointMarkerOptions.lineWidth,
- seriesMarkerOptions.lineWidth
- ),
- zoneColor = point && point.zone && point.zone.color,
- fill,
- stroke;
- color = (
- pointColorOption ||
- zoneColor ||
- pointColor ||
- color
- );
- fill = (
- pointMarkerOptions.fillColor ||
- seriesMarkerOptions.fillColor ||
- color
- );
- stroke = (
- pointMarkerOptions.lineColor ||
- seriesMarkerOptions.lineColor ||
- color
- );
- // Handle hover and select states
- if (state) {
- seriesStateOptions = seriesMarkerOptions.states[state];
- pointStateOptions = (
- pointMarkerOptions.states &&
- pointMarkerOptions.states[state]
- ) || {};
- strokeWidth = pick(
- pointStateOptions.lineWidth,
- seriesStateOptions.lineWidth,
- strokeWidth + pick(
- pointStateOptions.lineWidthPlus,
- seriesStateOptions.lineWidthPlus,
- 0
- )
- );
- fill = (
- pointStateOptions.fillColor ||
- seriesStateOptions.fillColor ||
- fill
- );
- stroke = (
- pointStateOptions.lineColor ||
- seriesStateOptions.lineColor ||
- stroke
- );
- }
- return {
- 'stroke': stroke,
- 'stroke-width': strokeWidth,
- 'fill': fill
- };
- },
- /**
- * Clear DOM objects and free up memory.
- *
- * @private
- * @function Highcharts.Series#destroy
- *
- * @fires Highcharts.Series#event:destroy
- */
- destroy: function () {
- var series = this,
- chart = series.chart,
- issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent),
- destroy,
- i,
- data = series.data || [],
- point,
- axis;
- // add event hook
- fireEvent(series, 'destroy');
- // remove all events
- removeEvent(series);
- // erase from axes
- (series.axisTypes || []).forEach(function (AXIS) {
- axis = series[AXIS];
- if (axis && axis.series) {
- erase(axis.series, series);
- axis.isDirty = axis.forceRedraw = true;
- }
- });
- // remove legend items
- if (series.legendItem) {
- series.chart.legend.destroyItem(series);
- }
- // destroy all points with their elements
- i = data.length;
- while (i--) {
- point = data[i];
- if (point && point.destroy) {
- point.destroy();
- }
- }
- series.points = null;
- // Clear the animation timeout if we are destroying the series
- // during initial animation
- H.clearTimeout(series.animationTimeout);
- // Destroy all SVGElements associated to the series
- objectEach(series, function (val, prop) {
- // Survive provides a hook for not destroying
- if (val instanceof SVGElement && !val.survive) {
- // issue 134 workaround
- destroy = issue134 && prop === 'group' ?
- 'hide' :
- 'destroy';
- val[destroy]();
- }
- });
- // remove from hoverSeries
- if (chart.hoverSeries === series) {
- chart.hoverSeries = null;
- }
- erase(chart.series, series);
- chart.orderSeries();
- // clear all members
- objectEach(series, function (val, prop) {
- delete series[prop];
- });
- },
- /**
- * Get the graph path.
- *
- * @private
- * @function Highcharts.Series#getGraphPath
- *
- * @param {Array<*>} points
- *
- * @param {boolean} nullsAsZeroes
- *
- * @param {boolean} connectCliffs
- *
- * @return {Array<number|string>}
- */
- getGraphPath: function (points, nullsAsZeroes, connectCliffs) {
- var series = this,
- options = series.options,
- step = options.step,
- reversed,
- graphPath = [],
- xMap = [],
- gap;
- points = points || series.points;
- // Bottom of a stack is reversed
- reversed = points.reversed;
- if (reversed) {
- points.reverse();
- }
- // Reverse the steps (#5004)
- step = { right: 1, center: 2 }[step] || (step && 3);
- if (step && reversed) {
- step = 4 - step;
- }
- // Remove invalid points, especially in spline (#5015)
- if (options.connectNulls && !nullsAsZeroes && !connectCliffs) {
- points = this.getValidPoints(points);
- }
- // Build the line
- points.forEach(function (point, i) {
- var plotX = point.plotX,
- plotY = point.plotY,
- lastPoint = points[i - 1],
- pathToPoint; // the path to this point from the previous
- if (
- (point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
- !connectCliffs
- ) {
- gap = true; // ... and continue
- }
- // Line series, nullsAsZeroes is not handled
- if (point.isNull && !defined(nullsAsZeroes) && i > 0) {
- gap = !options.connectNulls;
- // Area series, nullsAsZeroes is set
- } else if (point.isNull && !nullsAsZeroes) {
- gap = true;
- } else {
- if (i === 0 || gap) {
- pathToPoint = ['M', point.plotX, point.plotY];
- // Generate the spline as defined in the SplineSeries object
- } else if (series.getPointSpline) {
- pathToPoint = series.getPointSpline(points, point, i);
- } else if (step) {
- if (step === 1) { // right
- pathToPoint = [
- 'L',
- lastPoint.plotX,
- plotY
- ];
- } else if (step === 2) { // center
- pathToPoint = [
- 'L',
- (lastPoint.plotX + plotX) / 2,
- lastPoint.plotY,
- 'L',
- (lastPoint.plotX + plotX) / 2,
- plotY
- ];
- } else {
- pathToPoint = [
- 'L',
- plotX,
- lastPoint.plotY
- ];
- }
- pathToPoint.push('L', plotX, plotY);
- } else {
- // normal line to next point
- pathToPoint = [
- 'L',
- plotX,
- plotY
- ];
- }
- // Prepare for animation. When step is enabled, there are
- // two path nodes for each x value.
- xMap.push(point.x);
- if (step) {
- xMap.push(point.x);
- if (step === 2) { // step = center (#8073)
- xMap.push(point.x);
- }
- }
- graphPath.push.apply(graphPath, pathToPoint);
- gap = false;
- }
- });
- graphPath.xMap = xMap;
- series.graphPath = graphPath;
- return graphPath;
- },
- /**
- * Draw the graph. Called internally when rendering line-like series
- * types. The first time it generates the `series.graph` item and
- * optionally other series-wide items like `series.area` for area
- * charts. On subsequent calls these items are updated with new
- * positions and attributes.
- *
- * @function Highcharts.Series#drawGraph
- */
- drawGraph: function () {
- var series = this,
- options = this.options,
- graphPath = (this.gappedPath || this.getGraphPath).call(this),
- styledMode = this.chart.styledMode,
- props = [[
- 'graph',
- 'highcharts-graph'
- ]];
- // Presentational properties
- if (!styledMode) {
- props[0].push(
- options.lineColor || this.color,
- options.dashStyle
- );
- }
- props = series.getZonesGraphs(props);
- // Draw the graph
- props.forEach(function (prop, i) {
- var graphKey = prop[0],
- graph = series[graphKey],
- attribs;
- if (graph) {
- graph.endX = series.preventGraphAnimation ?
- null :
- graphPath.xMap;
- graph.animate({ d: graphPath });
- } else if (graphPath.length) { // #1487
- series[graphKey] = series.chart.renderer.path(graphPath)
- .addClass(prop[1])
- .attr({ zIndex: 1 }) // #1069
- .add(series.group);
- if (!styledMode) {
- attribs = {
- 'stroke': prop[2],
- 'stroke-width': options.lineWidth,
- // Polygon series use filled graph
- 'fill': (series.fillGraph && series.color) || 'none'
- };
- if (prop[3]) {
- attribs.dashstyle = prop[3];
- } else if (options.linecap !== 'square') {
- attribs['stroke-linecap'] =
- attribs['stroke-linejoin'] = 'round';
- }
- graph = series[graphKey]
- .attr(attribs)
- // Add shadow to normal series (0) or to first
- // zone (1) #3932
- .shadow((i < 2) && options.shadow);
- }
- }
- // Helpers for animation
- if (graph) {
- graph.startX = graphPath.xMap;
- graph.isArea = graphPath.isArea; // For arearange animation
- }
- });
- },
- /**
- * Get zones properties for building graphs. Extendable by series with
- * multiple lines within one series.
- *
- * @private
- * @function Highcharts.Series#getZonesGraphs
- *
- * @param {Array<Array<string>>} props
- *
- * @return {Array<Array<string>>}
- */
- getZonesGraphs: function (props) {
- // Add the zone properties if any
- this.zones.forEach(function (zone, i) {
- var propset = [
- 'zone-graph-' + i,
- 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
- (zone.className || '')
- ];
- if (!this.chart.styledMode) {
- propset.push(
- zone.color || this.color,
- zone.dashStyle || this.options.dashStyle
- );
- }
- props.push(propset);
- }, this);
- return props;
- },
- /**
- * Clip the graphs into zones for colors and styling.
- *
- * @private
- * @function Highcharts.Series#applyZones
- */
- applyZones: function () {
- var series = this,
- chart = this.chart,
- renderer = chart.renderer,
- zones = this.zones,
- translatedFrom,
- translatedTo,
- clips = this.clips || [],
- clipAttr,
- graph = this.graph,
- area = this.area,
- chartSizeMax = Math.max(chart.chartWidth, chart.chartHeight),
- axis = this[(this.zoneAxis || 'y') + 'Axis'],
- extremes,
- reversed,
- inverted = chart.inverted,
- horiz,
- pxRange,
- pxPosMin,
- pxPosMax,
- ignoreZones = false;
- if (zones.length &&
- (graph || area) &&
- axis &&
- axis.min !== undefined
- ) {
- reversed = axis.reversed;
- horiz = axis.horiz;
- // The use of the Color Threshold assumes there are no gaps
- // so it is safe to hide the original graph and area
- // unless it is not waterfall series, then use showLine property
- // to set lines between columns to be visible (#7862)
- if (graph && !this.showLine) {
- graph.hide();
- }
- if (area) {
- area.hide();
- }
- // Create the clips
- extremes = axis.getExtremes();
- zones.forEach(function (threshold, i) {
- translatedFrom = reversed ?
- (horiz ? chart.plotWidth : 0) :
- (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
- translatedFrom = Math.min(
- Math.max(
- pick(translatedTo, translatedFrom), 0
- ),
- chartSizeMax
- );
- translatedTo = Math.min(
- Math.max(
- Math.round(
- axis.toPixels(
- pick(threshold.value, extremes.max),
- true
- ) || 0
- ),
- 0
- ),
- chartSizeMax
- );
- if (ignoreZones) {
- translatedFrom = translatedTo =
- axis.toPixels(extremes.max);
- }
- pxRange = Math.abs(translatedFrom - translatedTo);
- pxPosMin = Math.min(translatedFrom, translatedTo);
- pxPosMax = Math.max(translatedFrom, translatedTo);
- if (axis.isXAxis) {
- clipAttr = {
- x: inverted ? pxPosMax : pxPosMin,
- y: 0,
- width: pxRange,
- height: chartSizeMax
- };
- if (!horiz) {
- clipAttr.x = chart.plotHeight - clipAttr.x;
- }
- } else {
- clipAttr = {
- x: 0,
- y: inverted ? pxPosMax : pxPosMin,
- width: chartSizeMax,
- height: pxRange
- };
- if (horiz) {
- clipAttr.y = chart.plotWidth - clipAttr.y;
- }
- }
- // VML SUPPPORT
- if (inverted && renderer.isVML) {
- if (axis.isXAxis) {
- clipAttr = {
- x: 0,
- y: reversed ? pxPosMin : pxPosMax,
- height: clipAttr.width,
- width: chart.chartWidth
- };
- } else {
- clipAttr = {
- x: (
- clipAttr.y -
- chart.plotLeft -
- chart.spacingBox.x
- ),
- y: 0,
- width: clipAttr.height,
- height: chart.chartHeight
- };
- }
- }
- // END OF VML SUPPORT
- if (clips[i]) {
- clips[i].animate(clipAttr);
- } else {
- clips[i] = renderer.clipRect(clipAttr);
- if (graph) {
- series['zone-graph-' + i].clip(clips[i]);
- }
- if (area) {
- series['zone-area-' + i].clip(clips[i]);
- }
- }
- // if this zone extends out of the axis, ignore the others
- ignoreZones = threshold.value > extremes.max;
- // Clear translatedTo for indicators
- if (series.resetZones && translatedTo === 0) {
- translatedTo = undefined;
- }
- });
- this.clips = clips;
- }
- },
- /**
- * Initialize and perform group inversion on series.group and
- * series.markerGroup.
- *
- * @private
- * @function Highcharts.Series#invertGroups
- *
- * @param {boolean} inverted
- */
- invertGroups: function (inverted) {
- var series = this,
- chart = series.chart,
- remover;
- function setInvert() {
- ['group', 'markerGroup'].forEach(function (groupName) {
- if (series[groupName]) {
- // VML/HTML needs explicit attributes for flipping
- if (chart.renderer.isVML) {
- series[groupName].attr({
- width: series.yAxis.len,
- height: series.xAxis.len
- });
- }
- series[groupName].width = series.yAxis.len;
- series[groupName].height = series.xAxis.len;
- series[groupName].invert(inverted);
- }
- });
- }
- // Pie, go away (#1736)
- if (!series.xAxis) {
- return;
- }
- // A fixed size is needed for inversion to work
- remover = addEvent(chart, 'resize', setInvert);
- addEvent(series, 'destroy', remover);
- // Do it now
- setInvert(inverted); // do it now
- // On subsequent render and redraw, just do setInvert without
- // setting up events again
- series.invertGroups = setInvert;
- },
- /**
- * General abstraction for creating plot groups like series.group,
- * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
- * the group will only be adjusted to the updated plot size.
- *
- * @private
- * @function Highcharts.Series#plotGroup
- *
- * @param {string} prop
- *
- * @param {string} name
- *
- * @param {string} visibility
- *
- * @param {number} zIndex
- *
- * @param {Highcharts.SVGElement} parent
- *
- * @return {Highcharts.SVGElement}
- */
- plotGroup: function (prop, name, visibility, zIndex, parent) {
- var group = this[prop],
- isNew = !group;
- // Generate it on first call
- if (isNew) {
- this[prop] = group = this.chart.renderer.g()
- .attr({
- zIndex: zIndex || 0.1 // IE8 and pointer logic use this
- })
- .add(parent);
- }
- // Add the class names, and replace existing ones as response to
- // Series.update (#6660)
- group.addClass(
- (
- 'highcharts-' + name +
- ' highcharts-series-' + this.index +
- ' highcharts-' + this.type + '-series ' +
- (
- defined(this.colorIndex) ?
- 'highcharts-color-' + this.colorIndex + ' ' :
- ''
- ) +
- (this.options.className || '') +
- (
- group.hasClass('highcharts-tracker') ?
- ' highcharts-tracker' :
- ''
- )
- ),
- true
- );
- // Place it on first and subsequent (redraw) calls
- group.attr({ visibility: visibility })[isNew ? 'attr' : 'animate'](
- this.getPlotBox()
- );
- return group;
- },
- /**
- * Get the translation and scale for the plot area of this series.
- *
- * @function Highcharts.Series#getPlotBox
- *
- * @return {Highcharts.SeriesPlotBoxObject}
- */
- getPlotBox: function () {
- var chart = this.chart,
- xAxis = this.xAxis,
- yAxis = this.yAxis;
- // Swap axes for inverted (#2339)
- if (chart.inverted) {
- xAxis = yAxis;
- yAxis = this.xAxis;
- }
- return {
- translateX: xAxis ? xAxis.left : chart.plotLeft,
- translateY: yAxis ? yAxis.top : chart.plotTop,
- scaleX: 1, // #1623
- scaleY: 1
- };
- },
- /**
- * Render the graph and markers. Called internally when first rendering
- * and later when redrawing the chart. This function can be extended in
- * plugins, but normally shouldn't be called directly.
- *
- * @function Highcharts.Series#render
- *
- * @fires Highcharts.Series#event:afterRender
- */
- render: function () {
- var series = this,
- chart = series.chart,
- group,
- options = series.options,
- // Animation doesn't work in IE8 quirks when the group div is
- // hidden, and looks bad in other oldIE
- animDuration = (
- !!series.animate &&
- chart.renderer.isSVG &&
- animObject(options.animation).duration
- ),
- visibility = series.visible ? 'inherit' : 'hidden', // #2597
- zIndex = options.zIndex,
- hasRendered = series.hasRendered,
- chartSeriesGroup = chart.seriesGroup,
- inverted = chart.inverted;
- fireEvent(this, 'render');
- // the group
- group = series.plotGroup(
- 'group',
- 'series',
- visibility,
- zIndex,
- chartSeriesGroup
- );
- series.markerGroup = series.plotGroup(
- 'markerGroup',
- 'markers',
- visibility,
- zIndex,
- chartSeriesGroup
- );
- // initiate the animation
- if (animDuration) {
- series.animate(true);
- }
- // SVGRenderer needs to know this before drawing elements (#1089,
- // #1795)
- group.inverted = series.isCartesian ? inverted : false;
- // draw the graph if any
- if (series.drawGraph) {
- series.drawGraph();
- series.applyZones();
- }
- /* series.points.forEach(function (point) {
- if (point.redraw) {
- point.redraw();
- }
- }); */
- // draw the data labels (inn pies they go before the points)
- if (series.drawDataLabels) {
- series.drawDataLabels();
- }
- // draw the points
- if (series.visible) {
- series.drawPoints();
- }
- // draw the mouse tracking area
- if (
- series.drawTracker &&
- series.options.enableMouseTracking !== false
- ) {
- series.drawTracker();
- }
- // Handle inverted series and tracker groups
- series.invertGroups(inverted);
- // Initial clipping, must be defined after inverting groups for VML.
- // Applies to columns etc. (#3839).
- if (
- options.clip !== false &&
- !series.sharedClipKey &&
- !hasRendered
- ) {
- group.clip(chart.clipRect);
- }
- // Run the animation
- if (animDuration) {
- series.animate();
- }
- // Call the afterAnimate function on animation complete (but don't
- // overwrite the animation.complete option which should be available
- // to the user).
- if (!hasRendered) {
- series.animationTimeout = syncTimeout(function () {
- series.afterAnimate();
- }, animDuration);
- }
- // Means data is in accordance with what you see
- series.isDirty = false;
- // (See #322) series.isDirty = series.isDirtyData = false; // means
- // data is in accordance with what you see
- series.hasRendered = true;
- fireEvent(series, 'afterRender');
- },
- /**
- * Redraw the series. This function is called internally from
- * `chart.redraw` and normally shouldn't be called directly.
- *
- * @private
- * @function Highcharts.Series#redraw
- */
- redraw: function () {
- var series = this,
- chart = series.chart,
- // cache it here as it is set to false in render, but used after
- wasDirty = series.isDirty || series.isDirtyData,
- group = series.group,
- xAxis = series.xAxis,
- yAxis = series.yAxis;
- // reposition on resize
- if (group) {
- if (chart.inverted) {
- group.attr({
- width: chart.plotWidth,
- height: chart.plotHeight
- });
- }
- group.animate({
- translateX: pick(xAxis && xAxis.left, chart.plotLeft),
- translateY: pick(yAxis && yAxis.top, chart.plotTop)
- });
- }
- series.translate();
- series.render();
- if (wasDirty) { // #3868, #3945
- delete this.kdTree;
- }
- },
- kdAxisArray: ['clientX', 'plotY'],
- /**
- * @private
- * @function Highcharts.Series#searchPoint
- *
- * @param {object} e
- *
- * @param {boolean} [compareX]
- *
- * @return {Highcharts.Point}
- */
- searchPoint: function (e, compareX) {
- var series = this,
- xAxis = series.xAxis,
- yAxis = series.yAxis,
- inverted = series.chart.inverted;
- return this.searchKDTree({
- clientX: inverted ?
- xAxis.len - e.chartY + xAxis.pos :
- e.chartX - xAxis.pos,
- plotY: inverted ?
- yAxis.len - e.chartX + yAxis.pos :
- e.chartY - yAxis.pos
- }, compareX, e);
- },
- /**
- * Build the k-d-tree that is used by mouse and touch interaction to get
- * the closest point. Line-like series typically have a one-dimensional
- * tree where points are searched along the X axis, while scatter-like
- * series typically search in two dimensions, X and Y.
- *
- * @private
- * @function Highcharts.Series#buildKDTree
- */
- buildKDTree: function (e) {
- // Prevent multiple k-d-trees from being built simultaneously
- // (#6235)
- this.buildingKdTree = true;
- var series = this,
- dimensions = (
- series.options.findNearestPointBy.indexOf('y') > -1 ? 2 : 1
- );
- // Internal function
- function _kdtree(points, depth, dimensions) {
- var axis,
- median,
- length = points && points.length;
- if (length) {
- // alternate between the axis
- axis = series.kdAxisArray[depth % dimensions];
- // sort point array
- points.sort(function (a, b) {
- return a[axis] - b[axis];
- });
- median = Math.floor(length / 2);
- // build and return nod
- return {
- point: points[median],
- left: _kdtree(
- points.slice(0, median), depth + 1, dimensions
- ),
- right: _kdtree(
- points.slice(median + 1), depth + 1, dimensions
- )
- };
- }
- }
- // Start the recursive build process with a clone of the points
- // array and null points filtered out (#3873)
- function startRecursive() {
- series.kdTree = _kdtree(
- series.getValidPoints(
- null,
- // For line-type series restrict to plot area, but
- // column-type series not (#3916, #4511)
- !series.directTouch
- ),
- dimensions,
- dimensions
- );
- series.buildingKdTree = false;
- }
- delete series.kdTree;
- // For testing tooltips, don't build async. Also if touchstart, we
- // may be dealing with click events on mobile, so don't delay
- // (#6817).
- syncTimeout(
- startRecursive,
- series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1
- );
- },
- /**
- * @private
- * @function Highcharts.Series#searchKDTree
- *
- * @param {object} point
- *
- * @param {boolean} [compareX]
- *
- * @return {Highcharts.Point}
- */
- searchKDTree: function (point, compareX, e) {
- var series = this,
- kdX = this.kdAxisArray[0],
- kdY = this.kdAxisArray[1],
- kdComparer = compareX ? 'distX' : 'dist',
- kdDimensions = series.options.findNearestPointBy
- .indexOf('y') > -1 ? 2 : 1;
- // Set the one and two dimensional distance on the point object
- function setDistance(p1, p2) {
- var x = (defined(p1[kdX]) && defined(p2[kdX])) ?
- Math.pow(p1[kdX] - p2[kdX], 2) :
- null,
- y = (defined(p1[kdY]) && defined(p2[kdY])) ?
- Math.pow(p1[kdY] - p2[kdY], 2) :
- null,
- r = (x || 0) + (y || 0);
- p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
- p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
- }
- function _search(search, tree, depth, dimensions) {
- var point = tree.point,
- axis = series.kdAxisArray[depth % dimensions],
- tdist,
- sideA,
- sideB,
- ret = point,
- nPoint1,
- nPoint2;
- setDistance(search, point);
- // Pick side based on distance to splitting point
- tdist = search[axis] - point[axis];
- sideA = tdist < 0 ? 'left' : 'right';
- sideB = tdist < 0 ? 'right' : 'left';
- // End of tree
- if (tree[sideA]) {
- nPoint1 = _search(
- search, tree[sideA], depth + 1, dimensions
- );
- ret = (
- nPoint1[kdComparer] < ret[kdComparer] ? nPoint1 : point
- );
- }
- if (tree[sideB]) {
- // compare distance to current best to splitting point to
- // decide wether to check side B or not
- if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
- nPoint2 = _search(
- search,
- tree[sideB],
- depth + 1,
- dimensions
- );
- ret = nPoint2[kdComparer] < ret[kdComparer] ?
- nPoint2 :
- ret;
- }
- }
- return ret;
- }
- if (!this.kdTree && !this.buildingKdTree) {
- this.buildKDTree(e);
- }
- if (this.kdTree) {
- return _search(point, this.kdTree, kdDimensions, kdDimensions);
- }
- },
- /**
- * @private
- * @function Highcharts.Series#pointPlacementToXValue
- *
- * @return {number}
- */
- pointPlacementToXValue: function () {
- var series = this,
- pointPlacement = series.options.pointPlacement;
- // Point placement is relative to each series pointRange (#5889)
- if (pointPlacement === 'between') {
- pointPlacement = 0.5;
- }
- if (isNumber(pointPlacement)) {
- pointPlacement *=
- pick(series.options.pointRange || series.xAxis.pointRange);
- }
- return pointPlacement;
- }
- }
- ); // end Series prototype
- /**
- * A line series displays information as a series of data points connected by
- * straight line segments.
- *
- * @sample {highcharts} highcharts/demo/line-basic/
- * Line chart
- * @sample {highstock} stock/demo/basic-line/
- * Line chart
- *
- * @extends plotOptions.series
- * @product highcharts highstock
- * @apioption plotOptions.line
- */
- /**
- * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
- * of a line graph. Round means that lines are rounded in the ends and
- * bends.
- *
- * @type {string}
- * @validvalue ["round", "butt", "square"]
- * @default round
- * @since 3.0.7
- * @apioption plotOptions.line.linecap
- */
- /**
- * A `line` series. If the [type](#series.line.type) option is not
- * specified, it is inherited from [chart.type](#chart.type).
- *
- * In TypeScript instead the `type` option must always be set.
- *
- * @extends series,plotOptions.line
- * @excluding dataParser,dataURL
- * @product highcharts highstock
- * @apioption series.line
- */
- /**
- * An array of data points for the series. For the `line` series type,
- * points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `y` options. The `x` values will be automatically
- * calculated, either starting at 0 and incremented by 1, or from
- * `pointStart` and `pointInterval` given in the series options. If the axis
- * has categories, these will be used. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `x,y`. If the first value is a string, it is applied as the name of the
- * point, and the `x` value is inferred.
- * ```js
- * data: [
- * [0, 1],
- * [1, 2],
- * [2, 8]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.line.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * y: 9,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * y: 6,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<(number|string),number>|*>}
- * @apioption series.line.data
- */
- /**
- * An additional, individual class name for the data point's graphic
- * representation.
- *
- * @type {string}
- * @since 5.0.0
- * @product highcharts gantt
- * @apioption series.line.data.className
- */
- /**
- * Individual color for the point. By default the color is pulled from
- * the global `colors` array.
- *
- * In styled mode, the `color` option doesn't take effect. Instead, use
- * `colorIndex`.
- *
- * @sample {highcharts} highcharts/point/color/
- * Mark the highest point
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highcharts highstock gantt
- * @apioption series.line.data.color
- */
- /**
- * A specific color index to use for the point, so its graphic representations
- * are given the class name `highcharts-color-{n}`. In styled mode this will
- * change the color of the graphic. In non-styled mode, the color by is set by
- * the `fill` attribute, so the change in class name won't have a visual effect
- * by default.
- *
- * @type {number}
- * @since 5.0.0
- * @product highcharts gantt
- * @apioption series.line.data.colorIndex
- */
- /**
- * Individual data label for each point. The options are the same as
- * the ones for [plotOptions.series.dataLabels](
- * #plotOptions.series.dataLabels).
- *
- * @sample highcharts/point/datalabels/
- * Show a label for the last value
- *
- * @type {Highcharts.PlotSeriesDataLabelsOptions}
- * @product highcharts highstock gantt
- * @apioption series.line.data.dataLabels
- */
- /**
- * A description of the point to add to the screen reader information
- * about the point. Requires the Accessibility module.
- *
- * @type {string}
- * @since 5.0.0
- * @apioption series.line.data.description
- */
- /**
- * An id for the point. This can be used after render time to get a
- * pointer to the point object through `chart.get()`.
- *
- * @sample {highcharts} highcharts/point/id/
- * Remove an id'd point
- *
- * @type {string}
- * @since 1.2.0
- * @product highcharts highstock gantt
- * @apioption series.line.data.id
- */
- /**
- * The rank for this point's data label in case of collision. If two
- * data labels are about to overlap, only the one with the highest `labelrank`
- * will be drawn.
- *
- * @type {number}
- * @apioption series.line.data.labelrank
- */
- /**
- * The name of the point as shown in the legend, tooltip, dataLabel
- * etc.
- *
- * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
- *
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Point names
- *
- * @type {string}
- * @apioption series.line.data.name
- */
- /**
- * Whether the data point is selected initially.
- *
- * @type {boolean}
- * @default false
- * @product highcharts highstock gantt
- * @apioption series.line.data.selected
- */
- /**
- * The x value of the point. For datetime axes, the X value is the timestamp
- * in milliseconds since 1970.
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption series.line.data.x
- */
- /**
- * The y value of the point.
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption series.line.data.y
- */
- /**
- * Individual point events
- *
- * @extends plotOptions.series.point.events
- * @product highcharts highstock gantt
- * @apioption series.line.data.events
- */
- /**
- * @extends plotOptions.series.marker
- * @product highcharts highstock
- * @apioption series.line.data.marker
- */
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var Axis = H.Axis,
- Chart = H.Chart,
- correctFloat = H.correctFloat,
- defined = H.defined,
- destroyObjectProperties = H.destroyObjectProperties,
- format = H.format,
- objectEach = H.objectEach,
- pick = H.pick,
- Series = H.Series;
- /**
- * The class for stacks. Each stack, on a specific X value and either negative
- * or positive, has its own stack item.
- *
- * @private
- * @class
- * @name Highcharts.StackItem
- *
- * @param {Highcharts.Axis} axis
- *
- * @param {Highcharts.Options} options
- *
- * @param {boolean} isNegative
- *
- * @param {number} x
- *
- * @param {string|*} stackOption
- */
- H.StackItem = function (axis, options, isNegative, x, stackOption) {
- var inverted = axis.chart.inverted;
- this.axis = axis;
- // Tells if the stack is negative
- this.isNegative = isNegative;
- // Save the options to be able to style the label
- this.options = options;
- // Save the x value to be able to position the label later
- this.x = x;
- // Initialize total value
- this.total = null;
- // This will keep each points' extremes stored by series.index and point
- // index
- this.points = {};
- // Save the stack option on the series configuration object, and whether to
- // treat it as percent
- this.stack = stackOption;
- this.leftCliff = 0;
- this.rightCliff = 0;
- // The align options and text align varies on whether the stack is negative
- // and if the chart is inverted or not.
- // First test the user supplied value, then use the dynamic.
- this.alignOptions = {
- align: options.align ||
- (inverted ? (isNegative ? 'left' : 'right') : 'center'),
- verticalAlign: options.verticalAlign ||
- (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
- y: pick(options.y, inverted ? 4 : (isNegative ? 14 : -6)),
- x: pick(options.x, inverted ? (isNegative ? -6 : 6) : 0)
- };
- this.textAlign = options.textAlign ||
- (inverted ? (isNegative ? 'right' : 'left') : 'center');
- };
- H.StackItem.prototype = {
- /**
- * @private
- * @function Highcharts.StackItem#destroy
- */
- destroy: function () {
- destroyObjectProperties(this, this.axis);
- },
- /**
- * Renders the stack total label and adds it to the stack label group.
- *
- * @private
- * @function Highcharts.StackItem#render
- *
- * @param {Highcharts.SVGElement} group
- */
- render: function (group) {
- var chart = this.axis.chart,
- options = this.options,
- formatOption = options.format,
- str = formatOption ?
- format(formatOption, this, chart.time) :
- options.formatter.call(this); // format the text in the label
- // Change the text to reflect the new total and set visibility to hidden
- // in case the serie is hidden
- if (this.label) {
- this.label.attr({ text: str, visibility: 'hidden' });
- // Create new label
- } else {
- this.label =
- chart.renderer.text(str, null, null, options.useHTML)
- .css(options.style)
- .attr({
- align: this.textAlign,
- rotation: options.rotation,
- visibility: 'hidden' // hidden until setOffset is called
- })
- .add(group); // add to the labels-group
- }
- // Rank it higher than data labels (#8742)
- this.label.labelrank = chart.plotHeight;
- },
- /**
- * Sets the offset that the stack has from the x value and repositions the
- * label.
- *
- * @private
- * @function Highcarts.StackItem#setOffset
- *
- * @param {number} xOffset
- *
- * @param {number} xWidth
- */
- setOffset: function (xOffset, xWidth) {
- var stackItem = this,
- axis = stackItem.axis,
- chart = axis.chart,
- // stack value translated mapped to chart coordinates
- y = axis.translate(
- axis.usePercentage ? 100 : stackItem.total,
- 0,
- 0,
- 0,
- 1
- ),
- yZero = axis.translate(0), // stack origin
- h = defined(y) && Math.abs(y - yZero), // stack height
- x = chart.xAxis[0].translate(stackItem.x) + xOffset, // x position
- stackBox = defined(y) && stackItem.getStackBox(
- chart,
- stackItem,
- x,
- y,
- xWidth,
- h,
- axis
- ),
- label = stackItem.label,
- alignAttr;
- if (label && stackBox) {
- // Align the label to the box
- label.align(stackItem.alignOptions, null, stackBox);
- // Set visibility (#678)
- alignAttr = label.alignAttr;
- label[
- stackItem.options.crop === false || chart.isInsidePlot(
- alignAttr.x,
- alignAttr.y
- ) ? 'show' : 'hide'](true);
- }
- },
- /**
- * @private
- * @function Highcharts.StackItem#getStackBox
- *
- * @param {Highcharts.Chart} chart
- *
- * @param {Highcharts.StackItem} stackItem
- *
- * @param {number} x
- *
- * @param {number} y
- *
- * @param {number} xWidth
- *
- * @param {number} h
- *
- * @param {Highcharts.Axis} axis
- *
- * @return {*}
- */
- getStackBox: function (chart, stackItem, x, y, xWidth, h, axis) {
- var reversed = stackItem.axis.reversed,
- inverted = chart.inverted,
- axisPos = axis.height + axis.pos - (inverted ? chart.plotLeft :
- chart.plotTop),
- neg = (stackItem.isNegative && !reversed) ||
- (!stackItem.isNegative && reversed); // #4056
- return { // this is the box for the complete stack
- x: inverted ? (neg ? y : y - h) : x,
- y: inverted ?
- axisPos - x - xWidth :
- (neg ?
- (axisPos - y - h) :
- axisPos - y
- ),
- width: inverted ? h : xWidth,
- height: inverted ? xWidth : h
- };
- }
- };
- /**
- * Generate stacks for each series and calculate stacks total values
- *
- * @private
- * @function Highcharts.Chart#getStacks
- */
- Chart.prototype.getStacks = function () {
- var chart = this;
- // reset stacks for each yAxis
- chart.yAxis.forEach(function (axis) {
- if (axis.stacks && axis.hasVisibleSeries) {
- axis.oldStacks = axis.stacks;
- }
- });
- chart.series.forEach(function (series) {
- if (series.options.stacking && (series.visible === true ||
- chart.options.chart.ignoreHiddenSeries === false)) {
- series.stackKey = series.type + pick(series.options.stack, '');
- }
- });
- };
- // Stacking methods defined on the Axis prototype
- /**
- * Build the stacks from top down
- *
- * @private
- * @function Highcharts.Axis#buildStacks
- */
- Axis.prototype.buildStacks = function () {
- var axisSeries = this.series,
- reversedStacks = pick(this.options.reversedStacks, true),
- len = axisSeries.length,
- i;
- if (!this.isXAxis) {
- this.usePercentage = false;
- i = len;
- while (i--) {
- axisSeries[reversedStacks ? i : len - i - 1].setStackedPoints();
- }
- // Loop up again to compute percent and stream stack
- for (i = 0; i < len; i++) {
- axisSeries[i].modifyStacks();
- }
- }
- };
- /**
- * @private
- * @function Highcharts.Axis#renderStackTotals
- */
- Axis.prototype.renderStackTotals = function () {
- var axis = this,
- chart = axis.chart,
- renderer = chart.renderer,
- stacks = axis.stacks,
- stackTotalGroup = axis.stackTotalGroup;
- // Create a separate group for the stack total labels
- if (!stackTotalGroup) {
- axis.stackTotalGroup = stackTotalGroup =
- renderer.g('stack-labels')
- .attr({
- visibility: 'visible',
- zIndex: 6
- })
- .add();
- }
- // plotLeft/Top will change when y axis gets wider so we need to translate
- // the stackTotalGroup at every render call. See bug #506 and #516
- stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
- // Render each stack total
- objectEach(stacks, function (type) {
- objectEach(type, function (stack) {
- stack.render(stackTotalGroup);
- });
- });
- };
- /**
- * Set all the stacks to initial states and destroy unused ones.
- *
- * @private
- * @function Highcharts.Axis#resetStacks
- */
- Axis.prototype.resetStacks = function () {
- var axis = this,
- stacks = axis.stacks;
- if (!axis.isXAxis) {
- objectEach(stacks, function (type) {
- objectEach(type, function (stack, key) {
- // Clean up memory after point deletion (#1044, #4320)
- if (stack.touched < axis.stacksTouched) {
- stack.destroy();
- delete type[key];
- // Reset stacks
- } else {
- stack.total = null;
- stack.cumulative = null;
- }
- });
- });
- }
- };
- /**
- * @private
- * @function Highcharts.Axis#cleanStacks
- */
- Axis.prototype.cleanStacks = function () {
- var stacks;
- if (!this.isXAxis) {
- if (this.oldStacks) {
- stacks = this.stacks = this.oldStacks;
- }
- // reset stacks
- objectEach(stacks, function (type) {
- objectEach(type, function (stack) {
- stack.cumulative = stack.total;
- });
- });
- }
- };
- // Stacking methods defnied for Series prototype
- /**
- * Adds series' points value to corresponding stack
- *
- * @private
- * @function Highcharts.Series#setStackedPoints
- */
- Series.prototype.setStackedPoints = function () {
- if (!this.options.stacking || (this.visible !== true &&
- this.chart.options.chart.ignoreHiddenSeries !== false)) {
- return;
- }
- var series = this,
- xData = series.processedXData,
- yData = series.processedYData,
- stackedYData = [],
- yDataLength = yData.length,
- seriesOptions = series.options,
- threshold = seriesOptions.threshold,
- stackThreshold = pick(seriesOptions.startFromThreshold && threshold, 0),
- stackOption = seriesOptions.stack,
- stacking = seriesOptions.stacking,
- stackKey = series.stackKey,
- negKey = '-' + stackKey,
- negStacks = series.negStacks,
- yAxis = series.yAxis,
- stacks = yAxis.stacks,
- oldStacks = yAxis.oldStacks,
- stackIndicator,
- isNegative,
- stack,
- other,
- key,
- pointKey,
- i,
- x,
- y;
- yAxis.stacksTouched += 1;
- // loop over the non-null y values and read them into a local array
- for (i = 0; i < yDataLength; i++) {
- x = xData[i];
- y = yData[i];
- stackIndicator = series.getStackIndicator(
- stackIndicator,
- x,
- series.index
- );
- pointKey = stackIndicator.key;
- // Read stacked values into a stack based on the x value,
- // the sign of y and the stack key. Stacking is also handled for null
- // values (#739)
- isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
- key = isNegative ? negKey : stackKey;
- // Create empty object for this stack if it doesn't exist yet
- if (!stacks[key]) {
- stacks[key] = {};
- }
- // Initialize StackItem for this x
- if (!stacks[key][x]) {
- if (oldStacks[key] && oldStacks[key][x]) {
- stacks[key][x] = oldStacks[key][x];
- stacks[key][x].total = null;
- } else {
- stacks[key][x] = new H.StackItem(
- yAxis,
- yAxis.options.stackLabels,
- isNegative,
- x,
- stackOption
- );
- }
- }
- // If the StackItem doesn't exist, create it first
- stack = stacks[key][x];
- if (y !== null) {
- stack.points[pointKey] = stack.points[series.index] =
- [pick(stack.cumulative, stackThreshold)];
- // Record the base of the stack
- if (!defined(stack.cumulative)) {
- stack.base = pointKey;
- }
- stack.touched = yAxis.stacksTouched;
- // In area charts, if there are multiple points on the same X value,
- // let the area fill the full span of those points
- if (stackIndicator.index > 0 && series.singleStacks === false) {
- stack.points[pointKey][0] =
- stack.points[series.index + ',' + x + ',0'][0];
- }
- // When updating to null, reset the point stack (#7493)
- } else {
- stack.points[pointKey] = stack.points[series.index] = null;
- }
- // Add value to the stack total
- if (stacking === 'percent') {
- // Percent stacked column, totals are the same for the positive and
- // negative stacks
- other = isNegative ? stackKey : negKey;
- if (negStacks && stacks[other] && stacks[other][x]) {
- other = stacks[other][x];
- stack.total = other.total =
- Math.max(other.total, stack.total) + Math.abs(y) || 0;
- // Percent stacked areas
- } else {
- stack.total = correctFloat(stack.total + (Math.abs(y) || 0));
- }
- } else {
- stack.total = correctFloat(stack.total + (y || 0));
- }
- stack.cumulative = pick(stack.cumulative, stackThreshold) + (y || 0);
- if (y !== null) {
- stack.points[pointKey].push(stack.cumulative);
- stackedYData[i] = stack.cumulative;
- }
- }
- if (stacking === 'percent') {
- yAxis.usePercentage = true;
- }
- this.stackedYData = stackedYData; // To be used in getExtremes
- // Reset old stacks
- yAxis.oldStacks = {};
- };
- /**
- * Iterate over all stacks and compute the absolute values to percent
- *
- * @private
- * @function Highcharts.Series#modifyStacks
- */
- Series.prototype.modifyStacks = function () {
- var series = this,
- stackKey = series.stackKey,
- stacks = series.yAxis.stacks,
- processedXData = series.processedXData,
- stackIndicator,
- stacking = series.options.stacking;
- if (series[stacking + 'Stacker']) { // Modifier function exists
- [stackKey, '-' + stackKey].forEach(function (key) {
- var i = processedXData.length,
- x,
- stack,
- pointExtremes;
- while (i--) {
- x = processedXData[i];
- stackIndicator = series.getStackIndicator(
- stackIndicator,
- x,
- series.index,
- key
- );
- stack = stacks[key] && stacks[key][x];
- pointExtremes = stack && stack.points[stackIndicator.key];
- if (pointExtremes) {
- series[stacking + 'Stacker'](pointExtremes, stack, i);
- }
- }
- });
- }
- };
- /**
- * Modifier function for percent stacks. Blows up the stack to 100%.
- *
- * @private
- * @function Highcharts.Series#percentStacker
- *
- * @param {Array<number>} pointExtremes
- *
- * @param {Highcharts.StackItem} stack
- *
- * @param {number} i
- */
- Series.prototype.percentStacker = function (pointExtremes, stack, i) {
- var totalFactor = stack.total ? 100 / stack.total : 0;
- // Y bottom value
- pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
- // Y value
- pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
- this.stackedYData[i] = pointExtremes[1];
- };
- /**
- * Get stack indicator, according to it's x-value, to determine points with the
- * same x-value
- *
- * @private
- * @function Highcharts.Series#getStackIndicator
- *
- * @param {*} stackIndicator
- *
- * @param {number} x
- *
- * @param {number} index
- *
- * @param {string} key
- *
- * @return {*}
- */
- Series.prototype.getStackIndicator = function (stackIndicator, x, index, key) {
- // Update stack indicator, when:
- // first point in a stack || x changed || stack type (negative vs positive)
- // changed:
- if (!defined(stackIndicator) || stackIndicator.x !== x ||
- (key && stackIndicator.key !== key)) {
- stackIndicator = {
- x: x,
- index: 0,
- key: key
- };
- } else {
- stackIndicator.index++;
- }
- stackIndicator.key = [index, x, stackIndicator.index].join(',');
- return stackIndicator;
- };
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var addEvent = H.addEvent,
- animate = H.animate,
- Axis = H.Axis,
- Chart = H.Chart,
- createElement = H.createElement,
- css = H.css,
- defined = H.defined,
- erase = H.erase,
- extend = H.extend,
- fireEvent = H.fireEvent,
- isNumber = H.isNumber,
- isObject = H.isObject,
- isArray = H.isArray,
- merge = H.merge,
- objectEach = H.objectEach,
- pick = H.pick,
- Point = H.Point,
- Series = H.Series,
- seriesTypes = H.seriesTypes,
- setAnimation = H.setAnimation,
- splat = H.splat;
- // Remove settings that have not changed, to avoid unnecessary rendering or
- // computing (#9197)
- H.cleanRecursively = function (newer, older) {
- var result = {};
- objectEach(newer, function (val, key) {
- var ob;
- // Dive into objects
- if (isObject(newer[key], true) && older[key]) {
- ob = H.cleanRecursively(newer[key], older[key]);
- if (Object.keys(ob).length) {
- result[key] = ob;
- }
- // Arrays or primitives are copied directly
- } else if (isObject(newer[key]) || newer[key] !== older[key]) {
- result[key] = newer[key];
- }
- });
- return result;
- };
- // Extend the Chart prototype for dynamic methods
- extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
- /**
- * Add a series to the chart after render time. Note that this method should
- * never be used when adding data synchronously at chart render time, as it
- * adds expense to the calculations and rendering. When adding data at the
- * same time as the chart is initialized, add the series as a configuration
- * option instead. With multiple axes, the `offset` is dynamically adjusted.
- *
- * @sample highcharts/members/chart-addseries/
- * Add a series from a button
- * @sample stock/members/chart-addseries/
- * Add a series in Highstock
- *
- * @function Highcharts.Chart#addSeries
- *
- * @param {Highcharts.SeriesOptionsType} options
- * The config options for the series.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after adding.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
- * Whether to apply animation, and optionally animation
- * configuration.
- *
- * @return {Highcharts.Series}
- * The newly created series object.
- *
- * @fires Highcharts.Chart#event:addSeries
- * @fires Highcharts.Chart#event:afterAddSeries
- */
- addSeries: function (options, redraw, animation) {
- var series,
- chart = this;
- if (options) {
- redraw = pick(redraw, true); // defaults to true
- fireEvent(chart, 'addSeries', { options: options }, function () {
- series = chart.initSeries(options);
- chart.isDirtyLegend = true;
- chart.linkSeries();
- fireEvent(chart, 'afterAddSeries');
- if (redraw) {
- chart.redraw(animation);
- }
- });
- }
- return series;
- },
- /**
- * Add an axis to the chart after render time. Note that this method should
- * never be used when adding data synchronously at chart render time, as it
- * adds expense to the calculations and rendering. When adding data at the
- * same time as the chart is initialized, add the axis as a configuration
- * option instead.
- *
- * @sample highcharts/members/chart-addaxis/
- * Add and remove axes
- *
- * @function Highcharts.Chart#addAxis
- *
- * @param {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} options
- * The axis options.
- *
- * @param {boolean} [isX=false]
- * Whether it is an X axis or a value axis.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after adding.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
- * Whether and how to apply animation in the redraw.
- *
- * @return {Highcharts.Axis}
- * The newly generated Axis object.
- */
- addAxis: function (options, isX, redraw, animation) {
- var key = isX ? 'xAxis' : 'yAxis',
- chartOptions = this.options,
- userOptions = merge(options, {
- index: this[key].length,
- isX: isX
- }),
- axis;
- axis = new Axis(this, userOptions);
- // Push the new axis options to the chart options
- chartOptions[key] = splat(chartOptions[key] || {});
- chartOptions[key].push(userOptions);
- if (pick(redraw, true)) {
- this.redraw(animation);
- }
- return axis;
- },
- /**
- * Dim the chart and show a loading text or symbol. Options for the loading
- * screen are defined in {@link
- * https://api.highcharts.com/highcharts/loading|the loading options}.
- *
- * @sample highcharts/members/chart-hideloading/
- * Show and hide loading from a button
- * @sample highcharts/members/chart-showloading/
- * Apply different text labels
- * @sample stock/members/chart-show-hide-loading/
- * Toggle loading in Highstock
- *
- * @function Highcharts.Chart#showLoading
- *
- * @param {string} [str]
- * An optional text to show in the loading label instead of the
- * default one. The default text is set in
- * [lang.loading](http://api.highcharts.com/highcharts/lang.loading).
- */
- showLoading: function (str) {
- var chart = this,
- options = chart.options,
- loadingDiv = chart.loadingDiv,
- loadingOptions = options.loading,
- setLoadingSize = function () {
- if (loadingDiv) {
- css(loadingDiv, {
- left: chart.plotLeft + 'px',
- top: chart.plotTop + 'px',
- width: chart.plotWidth + 'px',
- height: chart.plotHeight + 'px'
- });
- }
- };
- // create the layer at the first call
- if (!loadingDiv) {
- chart.loadingDiv = loadingDiv = createElement('div', {
- className: 'highcharts-loading highcharts-loading-hidden'
- }, null, chart.container);
- chart.loadingSpan = createElement(
- 'span',
- { className: 'highcharts-loading-inner' },
- null,
- loadingDiv
- );
- addEvent(chart, 'redraw', setLoadingSize); // #1080
- }
- loadingDiv.className = 'highcharts-loading';
- // Update text
- chart.loadingSpan.innerHTML = str || options.lang.loading;
- if (!chart.styledMode) {
- // Update visuals
- css(loadingDiv, extend(loadingOptions.style, {
- zIndex: 10
- }));
- css(chart.loadingSpan, loadingOptions.labelStyle);
- // Show it
- if (!chart.loadingShown) {
- css(loadingDiv, {
- opacity: 0,
- display: ''
- });
- animate(loadingDiv, {
- opacity: loadingOptions.style.opacity || 0.5
- }, {
- duration: loadingOptions.showDuration || 0
- });
- }
- }
- chart.loadingShown = true;
- setLoadingSize();
- },
- /**
- * Hide the loading layer.
- *
- * @see Highcharts.Chart#showLoading
- *
- * @sample highcharts/members/chart-hideloading/
- * Show and hide loading from a button
- * @sample stock/members/chart-show-hide-loading/
- * Toggle loading in Highstock
- *
- * @function Highcharts.Chart#hideLoading
- */
- hideLoading: function () {
- var options = this.options,
- loadingDiv = this.loadingDiv;
- if (loadingDiv) {
- loadingDiv.className =
- 'highcharts-loading highcharts-loading-hidden';
- if (!this.styledMode) {
- animate(loadingDiv, {
- opacity: 0
- }, {
- duration: options.loading.hideDuration || 100,
- complete: function () {
- css(loadingDiv, { display: 'none' });
- }
- });
- }
- }
- this.loadingShown = false;
- },
- /**
- * These properties cause isDirtyBox to be set to true when updating. Can be
- * extended from plugins.
- */
- propsRequireDirtyBox: [
- 'backgroundColor',
- 'borderColor',
- 'borderWidth',
- 'margin',
- 'marginTop',
- 'marginRight',
- 'marginBottom',
- 'marginLeft',
- 'spacing',
- 'spacingTop',
- 'spacingRight',
- 'spacingBottom',
- 'spacingLeft',
- 'borderRadius',
- 'plotBackgroundColor',
- 'plotBackgroundImage',
- 'plotBorderColor',
- 'plotBorderWidth',
- 'plotShadow',
- 'shadow'
- ],
- /**
- * These properties cause all series to be updated when updating. Can be
- * extended from plugins.
- */
- propsRequireUpdateSeries: [
- 'chart.inverted',
- 'chart.polar',
- 'chart.ignoreHiddenSeries',
- 'chart.type',
- 'colors',
- 'plotOptions',
- 'time',
- 'tooltip'
- ],
- /**
- * These collections (arrays) implement update() methods with support for
- * one-to-one option.
- */
- collectionsWithUpdate: [
- 'xAxis',
- 'yAxis',
- 'zAxis',
- 'series',
- 'colorAxis',
- 'pane'
- ],
- /**
- * A generic function to update any element of the chart. Elements can be
- * enabled and disabled, moved, re-styled, re-formatted etc.
- *
- * A special case is configuration objects that take arrays, for example
- * [xAxis](https://api.highcharts.com/highcharts/xAxis),
- * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
- * [series](https://api.highcharts.com/highcharts/series). For these
- * collections, an `id` option is used to map the new option set to an
- * existing object. If an existing object of the same id is not found, the
- * corresponding item is updated. So for example, running `chart.update`
- * with a series item without an id, will cause the existing chart's series
- * with the same index in the series array to be updated. When the
- * `oneToOne` parameter is true, `chart.update` will also take care of
- * adding and removing items from the collection. Read more under the
- * parameter description below.
- *
- * Note that when changing series data, `chart.update` may mutate the passed
- * data options.
- *
- * See also the
- * [responsive option set](https://api.highcharts.com/highcharts/responsive).
- * Switching between `responsive.rules` basically runs `chart.update` under
- * the hood.
- *
- * @sample highcharts/members/chart-update/
- * Update chart geometry
- *
- * @function Highcharts.Chart#update
- *
- * @param {Highcharts.Options} options
- * A configuration object for the new chart options.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart.
- *
- * @param {boolean} [oneToOne=false]
- * When `true`, the `series`, `xAxis` and `yAxis` collections will
- * be updated one to one, and items will be either added or removed
- * to match the new updated options. For example, if the chart has
- * two series and we call `chart.update` with a configuration
- * containing three series, one will be added. If we call
- * `chart.update` with one series, one will be removed. Setting an
- * empty `series` array will remove all series, but leaving out the
- * `series` property will leave all series untouched. If the series
- * have id's, the new series options will be matched by id, and the
- * remaining ones removed.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
- * Whether to apply animation, and optionally animation
- * configuration.
- *
- * @fires Highcharts.Chart#event:update
- * @fires Highcharts.Chart#event:afterUpdate
- */
- update: function (options, redraw, oneToOne, animation) {
- var chart = this,
- adders = {
- credits: 'addCredits',
- title: 'setTitle',
- subtitle: 'setSubtitle'
- },
- optionsChart,
- updateAllAxes,
- updateAllSeries,
- newWidth,
- newHeight,
- itemsForRemoval = [];
- fireEvent(chart, 'update', { options: options });
- // If there are responsive rules in action, undo the responsive rules
- // before we apply the updated options and replay the responsive rules
- // on top from the chart.redraw function (#9617).
- if (!options.isResponsiveOptions) {
- chart.setResponsive(false, true);
- }
- options = H.cleanRecursively(options, chart.options);
- // If the top-level chart option is present, some special updates are
- // required
- optionsChart = options.chart;
- if (optionsChart) {
- merge(true, chart.options.chart, optionsChart);
- // Setter function
- if ('className' in optionsChart) {
- chart.setClassName(optionsChart.className);
- }
- if ('reflow' in optionsChart) {
- chart.setReflow(optionsChart.reflow);
- }
- if (
- 'inverted' in optionsChart ||
- 'polar' in optionsChart ||
- 'type' in optionsChart
- ) {
- // Parse options.chart.inverted and options.chart.polar together
- // with the available series.
- chart.propFromSeries();
- updateAllAxes = true;
- }
- if ('alignTicks' in optionsChart) { // #6452
- updateAllAxes = true;
- }
- objectEach(optionsChart, function (val, key) {
- if (
- chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
- -1
- ) {
- updateAllSeries = true;
- }
- // Only dirty box
- if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
- chart.isDirtyBox = true;
- }
- });
- if (!chart.styledMode && 'style' in optionsChart) {
- chart.renderer.setStyle(optionsChart.style);
- }
- }
- // Moved up, because tooltip needs updated plotOptions (#6218)
- if (!chart.styledMode && options.colors) {
- this.options.colors = options.colors;
- }
- if (options.plotOptions) {
- merge(true, this.options.plotOptions, options.plotOptions);
- }
- // Some option stuctures correspond one-to-one to chart objects that
- // have update methods, for example
- // options.credits => chart.credits
- // options.legend => chart.legend
- // options.title => chart.title
- // options.tooltip => chart.tooltip
- // options.subtitle => chart.subtitle
- // options.mapNavigation => chart.mapNavigation
- // options.navigator => chart.navigator
- // options.scrollbar => chart.scrollbar
- objectEach(options, function (val, key) {
- if (chart[key] && typeof chart[key].update === 'function') {
- chart[key].update(val, false);
- // If a one-to-one object does not exist, look for an adder function
- } else if (typeof chart[adders[key]] === 'function') {
- chart[adders[key]](val);
- }
- if (
- key !== 'chart' &&
- chart.propsRequireUpdateSeries.indexOf(key) !== -1
- ) {
- updateAllSeries = true;
- }
- });
- // Setters for collections. For axes and series, each item is referred
- // by an id. If the id is not found, it defaults to the corresponding
- // item in the collection, so setting one series without an id, will
- // update the first series in the chart. Setting two series without
- // an id will update the first and the second respectively (#6019)
- // chart.update and responsive.
- this.collectionsWithUpdate.forEach(function (coll) {
- var indexMap;
- if (options[coll]) {
- // In stock charts, the navigator series are also part of the
- // chart.series array, but those series should not be handled
- // here (#8196).
- if (coll === 'series') {
- indexMap = [];
- chart[coll].forEach(function (s, i) {
- if (!s.options.isInternal) {
- indexMap.push(pick(s.options.index, i));
- }
- });
- }
- splat(options[coll]).forEach(function (newOptions, i) {
- var item = (
- defined(newOptions.id) &&
- chart.get(newOptions.id)
- ) || chart[coll][indexMap ? indexMap[i] : i];
- if (item && item.coll === coll) {
- item.update(newOptions, false);
- if (oneToOne) {
- item.touched = true;
- }
- }
- // If oneToOne and no matching item is found, add one
- if (!item && oneToOne) {
- if (coll === 'series') {
- chart.addSeries(newOptions, false)
- .touched = true;
- } else if (coll === 'xAxis' || coll === 'yAxis') {
- chart.addAxis(newOptions, coll === 'xAxis', false)
- .touched = true;
- }
- }
- });
- // Add items for removal
- if (oneToOne) {
- chart[coll].forEach(function (item) {
- if (!item.touched && !item.options.isInternal) {
- itemsForRemoval.push(item);
- } else {
- delete item.touched;
- }
- });
- }
- }
- });
- itemsForRemoval.forEach(function (item) {
- if (item.remove) {
- item.remove(false);
- }
- });
- if (updateAllAxes) {
- chart.axes.forEach(function (axis) {
- axis.update({}, false);
- });
- }
- // Certain options require the whole series structure to be thrown away
- // and rebuilt
- if (updateAllSeries) {
- chart.series.forEach(function (series) {
- series.update({}, false);
- });
- }
- // For loading, just update the options, do not redraw
- if (options.loading) {
- merge(true, chart.options.loading, options.loading);
- }
- // Update size. Redraw is forced.
- newWidth = optionsChart && optionsChart.width;
- newHeight = optionsChart && optionsChart.height;
- if ((isNumber(newWidth) && newWidth !== chart.chartWidth) ||
- (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
- chart.setSize(newWidth, newHeight, animation);
- } else if (pick(redraw, true)) {
- chart.redraw(animation);
- }
- fireEvent(chart, 'afterUpdate', { options: options });
- },
- /**
- * Shortcut to set the subtitle options. This can also be done from {@link
- * Chart#update} or {@link Chart#setTitle}.
- *
- * @function Highcharts.Chart#setSubtitle
- *
- * @param {Highcharts.SubtitleOptions} options
- * New subtitle options. The subtitle text itself is set by the
- * `options.text` property.
- */
- setSubtitle: function (options) {
- this.setTitle(undefined, options);
- }
- });
- // extend the Point prototype for dynamic methods
- extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
- /**
- * Update point with new options (typically x/y data) and optionally redraw
- * the series.
- *
- * @sample highcharts/members/point-update-column/
- * Update column value
- * @sample highcharts/members/point-update-pie/
- * Update pie slice
- * @sample maps/members/point-update/
- * Update map area value in Highmaps
- *
- * @function Highcharts.Point#update
- *
- * @param {number|object|Array<number|string>|null} options
- * The point options. Point options are handled as described under
- * the `series.type.data` item for each series type. For example
- * for a line series, if options is a single number, the point will
- * be given that number as the marin y value. If it is an array, it
- * will be interpreted as x and y values respectively. If it is an
- * object, advanced options are applied.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the point is updated. If doing
- * more operations on the chart, it is best practice to set
- * `redraw` to false and call `chart.redraw()` after.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
- * Whether to apply animation, and optionally animation
- * configuration.
- *
- * @fires Highcharts.Point#event:update
- */
- update: function (options, redraw, animation, runEvent) {
- var point = this,
- series = point.series,
- graphic = point.graphic,
- i,
- chart = series.chart,
- seriesOptions = series.options;
- redraw = pick(redraw, true);
- function update() {
- point.applyOptions(options);
- // Update visuals
- if (point.y === null && graphic) { // #4146
- point.graphic = graphic.destroy();
- }
- if (isObject(options, true)) {
- // Destroy so we can get new elements
- if (graphic && graphic.element) {
- // "null" is also a valid symbol
- if (
- options &&
- options.marker &&
- options.marker.symbol !== undefined
- ) {
- point.graphic = graphic.destroy();
- }
- }
- if (options && options.dataLabels && point.dataLabel) { // #2468
- point.dataLabel = point.dataLabel.destroy();
- }
- if (point.connector) {
- point.connector = point.connector.destroy(); // #7243
- }
- }
- // record changes in the parallel arrays
- i = point.index;
- series.updateParallelArrays(point, i);
- // Record the options to options.data. If the old or the new config
- // is an object, use point options, otherwise use raw options
- // (#4701, #4916).
- seriesOptions.data[i] = (
- isObject(seriesOptions.data[i], true) ||
- isObject(options, true)
- ) ?
- point.options :
- pick(options, seriesOptions.data[i]);
- // redraw
- series.isDirty = series.isDirtyData = true;
- if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
- chart.isDirtyBox = true;
- }
- if (seriesOptions.legendType === 'point') { // #1831, #1885
- chart.isDirtyLegend = true;
- }
- if (redraw) {
- chart.redraw(animation);
- }
- }
- // Fire the event with a default handler of doing the update
- if (runEvent === false) { // When called from setData
- update();
- } else {
- point.firePointEvent('update', { options: options }, update);
- }
- },
- /**
- * Remove a point and optionally redraw the series and if necessary the axes
- *
- * @sample highcharts/plotoptions/series-point-events-remove/
- * Remove point and confirm
- * @sample highcharts/members/point-remove/
- * Remove pie slice
- * @sample maps/members/point-remove/
- * Remove selected points in Highmaps
- *
- * @function Highcharts.Point#remove
- *
- * @param {boolean} redraw
- * Whether to redraw the chart or wait for an explicit call. When
- * doing more operations on the chart, for example running
- * `point.remove()` in a loop, it is best practice to set `redraw`
- * to false and call `chart.redraw()` after.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation=false]
- * Whether to apply animation, and optionally animation
- * configuration.
- */
- remove: function (redraw, animation) {
- this.series.removePoint(
- this.series.data.indexOf(this),
- redraw,
- animation
- );
- }
- });
- // Extend the series prototype for dynamic methods
- extend(Series.prototype, /** @lends Series.prototype */ {
- /**
- * Add a point to the series after render time. The point can be added at
- * the end, or by giving it an X value, to the start or in the middle of the
- * series.
- *
- * @sample highcharts/members/series-addpoint-append/
- * Append point
- * @sample highcharts/members/series-addpoint-append-and-shift/
- * Append and shift
- * @sample highcharts/members/series-addpoint-x-and-y/
- * Both X and Y values given
- * @sample highcharts/members/series-addpoint-pie/
- * Append pie slice
- * @sample stock/members/series-addpoint/
- * Append 100 points in Highstock
- * @sample stock/members/series-addpoint-shift/
- * Append and shift in Highstock
- * @sample maps/members/series-addpoint/
- * Add a point in Highmaps
- *
- * @function Highcharts.Series#addPoint
- *
- * @param {number|object|Array<number|string>|null} options
- * The point options. If options is a single number, a point with
- * that y value is appended to the series. If it is an array, it will
- * be interpreted as x and y values respectively. If it is an
- * object, advanced options as outlined under `series.data` are
- * applied.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the point is added. When adding
- * more than one point, it is highly recommended that the redraw
- * option be set to false, and instead {@link Chart#redraw} is
- * explicitly called after the adding of points is finished.
- * Otherwise, the chart will redraw after adding each point.
- *
- * @param {boolean} [shift=false]
- * If true, a point is shifted off the start of the series as one is
- * appended to the end.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
- * Whether to apply animation, and optionally animation
- * configuration.
- */
- addPoint: function (options, redraw, shift, animation) {
- var series = this,
- seriesOptions = series.options,
- data = series.data,
- chart = series.chart,
- xAxis = series.xAxis,
- names = xAxis && xAxis.hasNames && xAxis.names,
- dataOptions = seriesOptions.data,
- point,
- isInTheMiddle,
- xData = series.xData,
- i,
- x;
- // Optional redraw, defaults to true
- redraw = pick(redraw, true);
- // Get options and push the point to xData, yData and series.options. In
- // series.generatePoints the Point instance will be created on demand
- // and pushed to the series.data array.
- point = { series: series };
- series.pointClass.prototype.applyOptions.apply(point, [options]);
- x = point.x;
- // Get the insertion point
- i = xData.length;
- if (series.requireSorting && x < xData[i - 1]) {
- isInTheMiddle = true;
- while (i && xData[i - 1] > x) {
- i--;
- }
- }
- // Insert undefined item
- series.updateParallelArrays(point, 'splice', i, 0, 0);
- // Update it
- series.updateParallelArrays(point, i);
- if (names && point.name) {
- names[x] = point.name;
- }
- dataOptions.splice(i, 0, options);
- if (isInTheMiddle) {
- series.data.splice(i, 0, null);
- series.processData();
- }
- // Generate points to be added to the legend (#1329)
- if (seriesOptions.legendType === 'point') {
- series.generatePoints();
- }
- // Shift the first point off the parallel arrays
- if (shift) {
- if (data[0] && data[0].remove) {
- data[0].remove(false);
- } else {
- data.shift();
- series.updateParallelArrays(point, 'shift');
- dataOptions.shift();
- }
- }
- // redraw
- series.isDirty = true;
- series.isDirtyData = true;
- if (redraw) {
- chart.redraw(animation); // Animation is set anyway on redraw, #5665
- }
- },
- /**
- * Remove a point from the series. Unlike the
- * {@link Highcharts.Point#remove} method, this can also be done on a point
- * that is not instanciated because it is outside the view or subject to
- * Highstock data grouping.
- *
- * @sample highcharts/members/series-removepoint/
- * Remove cropped point
- *
- * @function Highcharts.Series#removePoint
- *
- * @param {number} i
- * The index of the point in the {@link Highcharts.Series.data|data}
- * array.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the point is added. When
- * removing more than one point, it is highly recommended that the
- * `redraw` option be set to `false`, and instead {@link
- * Highcharts.Chart#redraw} is explicitly called after the adding of
- * points is finished.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
- * Whether and optionally how the series should be animated.
- *
- * @fires Highcharts.Point#event:remove
- */
- removePoint: function (i, redraw, animation) {
- var series = this,
- data = series.data,
- point = data[i],
- points = series.points,
- chart = series.chart,
- remove = function () {
- if (points && points.length === data.length) { // #4935
- points.splice(i, 1);
- }
- data.splice(i, 1);
- series.options.data.splice(i, 1);
- series.updateParallelArrays(
- point || { series: series },
- 'splice',
- i,
- 1
- );
- if (point) {
- point.destroy();
- }
- // redraw
- series.isDirty = true;
- series.isDirtyData = true;
- if (redraw) {
- chart.redraw();
- }
- };
- setAnimation(animation, chart);
- redraw = pick(redraw, true);
- // Fire the event with a default handler of removing the point
- if (point) {
- point.firePointEvent('remove', null, remove);
- } else {
- remove();
- }
- },
- /**
- * Remove a series and optionally redraw the chart.
- *
- * @sample highcharts/members/series-remove/
- * Remove first series from a button
- *
- * @function Highcharts.Series#remove
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart or wait for an explicit call to
- * {@link Highcharts.Chart#redraw}.
- *
- * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
- * Whether to apply animation, and optionally animation
- * configuration.
- *
- * @param {boolean} [withEvent=true]
- * Used internally, whether to fire the series `remove` event.
- *
- * @fires Highcharts.Series#event:remove
- */
- remove: function (redraw, animation, withEvent) {
- var series = this,
- chart = series.chart;
- function remove() {
- // Destroy elements
- series.destroy();
- series.remove = null; // Prevent from doing again (#9097)
- // Redraw
- chart.isDirtyLegend = chart.isDirtyBox = true;
- chart.linkSeries();
- if (pick(redraw, true)) {
- chart.redraw(animation);
- }
- }
- // Fire the event with a default handler of removing the point
- if (withEvent !== false) {
- fireEvent(series, 'remove', null, remove);
- } else {
- remove();
- }
- },
- /**
- * Update the series with a new set of options. For a clean and precise
- * handling of new options, all methods and elements from the series are
- * removed, and it is initiated from scratch. Therefore, this method is more
- * performance expensive than some other utility methods like {@link
- * Series#setData} or {@link Series#setVisible}.
- *
- * Note that `Series.update` may mutate the passed `data` options.
- *
- * @sample highcharts/members/series-update/
- * Updating series options
- * @sample maps/members/series-update/
- * Update series options in Highmaps
- *
- * @function Highcharts.Series#update
- *
- * @param {Highcharts.SeriesOptionsType} options
- * New options that will be merged with the series' existing options.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the series is altered. If doing
- * more operations on the chart, it is a good idea to set redraw to
- * false and call {@link Chart#redraw} after.
- *
- * @fires Highcharts.Series#event:afterUpdate
- */
- update: function (newOptions, redraw) {
- newOptions = H.cleanRecursively(newOptions, this.userOptions);
- var series = this,
- chart = series.chart,
- // must use user options when changing type because series.options
- // is merged in with type specific plotOptions
- oldOptions = series.userOptions,
- initialType = series.initialType || series.type,
- newType = (
- newOptions.type ||
- oldOptions.type ||
- chart.options.chart.type
- ),
- initialSeriesProto = seriesTypes[initialType].prototype,
- n,
- groups = [
- 'group',
- 'markerGroup',
- 'dataLabelsGroup'
- ],
- preserve = [
- 'navigatorSeries',
- 'baseSeries'
- ],
- // Animation must be enabled when calling update before the initial
- // animation has first run. This happens when calling update
- // directly after chart initialization, or when applying responsive
- // rules (#6912).
- animation = series.finishedAnimating && { animation: false },
- allowSoftUpdate = [
- 'data',
- 'name',
- 'turboThreshold'
- ],
- keys = Object.keys(newOptions),
- doSoftUpdate = keys.length > 0;
- // Running Series.update to update the data only is an intuitive usage,
- // so we want to make sure that when used like this, we run the
- // cheaper setData function and allow animation instead of completely
- // recreating the series instance. This includes sideways animation when
- // adding points to the data set. The `name` should also support soft
- // update because the data module sets name and data when setting new
- // data by `chart.update`.
- keys.forEach(function (key) {
- if (allowSoftUpdate.indexOf(key) === -1) {
- doSoftUpdate = false;
- }
- });
- if (doSoftUpdate) {
- if (newOptions.data) {
- this.setData(newOptions.data, false);
- }
- if (newOptions.name) {
- this.setName(newOptions.name, false);
- }
- } else {
- // Make sure preserved properties are not destroyed (#3094)
- preserve = groups.concat(preserve);
- preserve.forEach(function (prop) {
- preserve[prop] = series[prop];
- delete series[prop];
- });
- // Do the merge, with some forced options
- newOptions = merge(oldOptions, animation, {
- index: series.index,
- pointStart: pick(
- oldOptions.pointStart, // when updating from blank (#7933)
- series.xData[0] // when updating after addPoint
- )
- }, { data: series.options.data }, newOptions);
- // Destroy the series and delete all properties. Reinsert all
- // methods and properties from the new type prototype (#2270,
- // #3719).
- series.remove(false, null, false);
- for (n in initialSeriesProto) {
- series[n] = undefined;
- }
- if (seriesTypes[newType || initialType]) {
- extend(series, seriesTypes[newType || initialType].prototype);
- } else {
- H.error(17, true, chart);
- }
- // Re-register groups (#3094) and other preserved properties
- preserve.forEach(function (prop) {
- series[prop] = preserve[prop];
- });
- series.init(chart, newOptions);
- // Update the Z index of groups (#3380, #7397)
- if (newOptions.zIndex !== oldOptions.zIndex) {
- groups.forEach(function (groupName) {
- if (series[groupName]) {
- series[groupName].attr({
- zIndex: newOptions.zIndex
- });
- }
- });
- }
- series.initialType = initialType;
- chart.linkSeries(); // Links are lost in series.remove (#3028)
- }
- fireEvent(this, 'afterUpdate');
- if (pick(redraw, true)) {
- chart.redraw(doSoftUpdate ? undefined : false);
- }
- },
- /**
- * Used from within series.update
- *
- * @private
- * @function Highcharts.Series#setName
- *
- * @param {string} name
- */
- setName: function (name) {
- this.name = this.options.name = this.userOptions.name = name;
- this.chart.isDirtyLegend = true;
- }
- });
- // Extend the Axis.prototype for dynamic methods
- extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
- /**
- * Update an axis object with a new set of options. The options are merged
- * with the existing options, so only new or altered options need to be
- * specified.
- *
- * @sample highcharts/members/axis-update/
- * Axis update demo
- *
- * @function Highcharts.Axis#update
- *
- * @param {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} options
- * The new options that will be merged in with existing options on
- * the axis.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the axis is altered. If doing
- * more operations on the chart, it is a good idea to set redraw to
- * false and call {@link Chart#redraw} after.
- */
- update: function (options, redraw) {
- var chart = this.chart,
- newEvents = ((options && options.events) || {});
- options = merge(this.userOptions, options);
- // Color Axis is not an array,
- // This change is applied in the ColorAxis wrapper
- if (chart.options[this.coll].indexOf) {
- // Don't use this.options.index,
- // StockChart has Axes in navigator too
- chart.options[this.coll][
- chart.options[this.coll].indexOf(this.userOptions)
- ] = options;
- }
- // Remove old events, if no new exist (#8161)
- objectEach(chart.options[this.coll].events, function (fn, ev) {
- if (typeof newEvents[ev] === 'undefined') {
- newEvents[ev] = undefined;
- }
- });
- this.destroy(true);
- this.init(chart, extend(options, { events: newEvents }));
- chart.isDirtyBox = true;
- if (pick(redraw, true)) {
- chart.redraw();
- }
- },
- /**
- * Remove the axis from the chart.
- *
- * @sample highcharts/members/chart-addaxis/
- * Add and remove axes
- *
- * @function Highcharts.Axis#remove
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart following the remove.
- */
- remove: function (redraw) {
- var chart = this.chart,
- key = this.coll, // xAxis or yAxis
- axisSeries = this.series,
- i = axisSeries.length;
- // Remove associated series (#2687)
- while (i--) {
- if (axisSeries[i]) {
- axisSeries[i].remove(false);
- }
- }
- // Remove the axis
- erase(chart.axes, this);
- erase(chart[key], this);
- if (isArray(chart.options[key])) {
- chart.options[key].splice(this.options.index, 1);
- } else { // color axis, #6488
- delete chart.options[key];
- }
- chart[key].forEach(function (axis, i) { // Re-index, #1706, #8075
- axis.options.index = axis.userOptions.index = i;
- });
- this.destroy();
- chart.isDirtyBox = true;
- if (pick(redraw, true)) {
- chart.redraw();
- }
- },
- /**
- * Update the axis title by options after render time.
- *
- * @sample highcharts/members/axis-settitle/
- * Set a new Y axis title
- *
- * @function Highcharts.Axis#setTitle
- *
- * @param {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} titleOptions
- * The additional title options.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after setting the title.
- */
- setTitle: function (titleOptions, redraw) {
- this.update({ title: titleOptions }, redraw);
- },
- /**
- * Set new axis categories and optionally redraw.
- *
- * @sample highcharts/members/axis-setcategories/
- * Set categories by click on a button
- *
- * @function Highcharts.Axis#setCategories
- *
- * @param {Array<string>} categories
- * The new categories.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart.
- */
- setCategories: function (categories, redraw) {
- this.update({ categories: categories }, redraw);
- }
- });
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var color = H.color,
- LegendSymbolMixin = H.LegendSymbolMixin,
- pick = H.pick,
- Series = H.Series,
- seriesType = H.seriesType;
- /**
- * Area series type.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.area
- *
- * @augments Highcharts.Series
- */
- seriesType('area', 'line',
- /**
- * The area series type.
- *
- * @sample {highcharts} highcharts/demo/area-basic/
- * Area chart
- * @sample {highstock} stock/demo/area/
- * Area chart
- *
- * @extends plotOptions.line
- * @excluding useOhlcData
- * @product highcharts highstock
- * @optionparent plotOptions.area
- */
- {
- /**
- * Fill color or gradient for the area. When `null`, the series' `color`
- * is used with the series' `fillOpacity`.
- *
- * In styled mode, the fill color can be set with the `.highcharts-area`
- * class name.
- *
- * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
- * Null by default
- * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
- * Gradient
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highcharts highstock
- * @apioption plotOptions.area.fillColor
- */
- /**
- * Fill opacity for the area. When you set an explicit `fillColor`,
- * the `fillOpacity` is not applied. Instead, you should define the
- * opacity in the `fillColor` with an rgba color definition. The
- * `fillOpacity` setting, also the default setting, overrides the alpha
- * component of the `color` setting.
- *
- * In styled mode, the fill opacity can be set with the
- * `.highcharts-area` class name.
- *
- * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
- * Automatic fill color and fill opacity of 0.1
- *
- * @type {number}
- * @default {highcharts} 0.75
- * @default {highstock} 0.75
- * @product highcharts highstock
- * @apioption plotOptions.area.fillOpacity
- */
- /**
- * A separate color for the graph line. By default the line takes the
- * `color` of the series, but the lineColor setting allows setting a
- * separate color for the line without altering the `fillColor`.
- *
- * In styled mode, the line stroke can be set with the
- * `.highcharts-graph` class name.
- *
- * @sample {highcharts} highcharts/plotoptions/area-linecolor/
- * Dark gray line
- *
- * @type {Highcharts.ColorString}
- * @product highcharts highstock
- * @apioption plotOptions.area.lineColor
- */
- /**
- * A separate color for the negative part of the area.
- *
- * In styled mode, a negative color is set with the `.highcharts-negative`
- * class name.
- *
- * @see [negativeColor](#plotOptions.area.negativeColor)
- *
- * @sample {highcharts} highcharts/css/series-negative-color/
- * Negative color in styled mode
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @since 3.0
- * @product highcharts
- * @apioption plotOptions.area.negativeFillColor
- */
- /**
- * Whether the whole area or just the line should respond to mouseover
- * tooltips and other mouse or touch events.
- *
- * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
- * Display the tooltip when the area is hovered
- *
- * @type {boolean}
- * @default false
- * @since 1.1.6
- * @product highcharts highstock
- * @apioption plotOptions.area.trackByArea
- */
- /**
- * When this is true, the series will not cause the Y axis to cross
- * the zero plane (or [threshold](#plotOptions.series.threshold) option)
- * unless the data actually crosses the plane.
- *
- * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
- * 3 will make the Y axis show negative values according to the `minPadding`
- * option. If `softThreshold` is `true`, the Y axis starts at 0.
- *
- * @since 4.1.9
- * @product highcharts highstock
- */
- softThreshold: false,
- /**
- * The Y axis value to serve as the base for the area, for distinguishing
- * between values above and below a threshold. The area between the graph
- * and the threshold is filled.
- *
- * * If a number is given, the Y axis will scale to the threshold.
- * * If `null`, the scaling behaves like a line series with fill between the
- * graph and the Y axis minimum.
- * * If `Infinity` or `-Infinity`, the area between the graph and the
- * corresponing Y axis extreme is filled (since v6.1.0).
- *
- * @sample {highcharts} highcharts/plotoptions/area-threshold/
- * A threshold of 100
- * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
- * A threshold of Infinity
- *
- * @since 2.0
- * @product highcharts highstock
- */
- threshold: 0
- }, /** @lends seriesTypes.area.prototype */ {
- singleStacks: false,
- // Return an array of stacked points, where null and missing points are
- // replaced by dummy points in order for gaps to be drawn correctly in
- // stacks.
- getStackPoints: function (points) {
- var series = this,
- segment = [],
- keys = [],
- xAxis = this.xAxis,
- yAxis = this.yAxis,
- stack = yAxis.stacks[this.stackKey],
- pointMap = {},
- seriesIndex = series.index,
- yAxisSeries = yAxis.series,
- seriesLength = yAxisSeries.length,
- visibleSeries,
- upOrDown = pick(yAxis.options.reversedStacks, true) ? 1 : -1,
- i;
- points = points || this.points;
- if (this.options.stacking) {
- for (i = 0; i < points.length; i++) {
- // Reset after point update (#7326)
- points[i].leftNull = points[i].rightNull = null;
- // Create a map where we can quickly look up the points by
- // their X values.
- pointMap[points[i].x] = points[i];
- }
- // Sort the keys (#1651)
- H.objectEach(stack, function (stackX, x) {
- // nulled after switching between
- // grouping and not (#1651, #2336)
- if (stackX.total !== null) {
- keys.push(x);
- }
- });
- keys.sort(function (a, b) {
- return a - b;
- });
- visibleSeries = yAxisSeries.map(function (s) {
- return s.visible;
- });
- keys.forEach(function (x, idx) {
- var y = 0,
- stackPoint,
- stackedValues;
- if (pointMap[x] && !pointMap[x].isNull) {
- segment.push(pointMap[x]);
- // Find left and right cliff. -1 goes left, 1 goes
- // right.
- [-1, 1].forEach(function (direction) {
- var nullName = direction === 1 ?
- 'rightNull' :
- 'leftNull',
- cliffName = direction === 1 ?
- 'rightCliff' :
- 'leftCliff',
- cliff = 0,
- otherStack = stack[keys[idx + direction]];
- // If there is a stack next to this one,
- // to the left or to the right...
- if (otherStack) {
- i = seriesIndex;
- // Can go either up or down,
- // depending on reversedStacks
- while (i >= 0 && i < seriesLength) {
- stackPoint = otherStack.points[i];
- if (!stackPoint) {
- // If the next point in this series
- // is missing, mark the point
- // with point.leftNull or
- // point.rightNull = true.
- if (i === seriesIndex) {
- pointMap[x][nullName] = true;
- // If there are missing points in
- // the next stack in any of the
- // series below this one, we need
- // to substract the missing values
- // and add a hiatus to the left or
- // right.
- } else if (visibleSeries[i]) {
- stackedValues = stack[x].points[i];
- if (stackedValues) {
- cliff -= stackedValues[1] -
- stackedValues[0];
- }
- }
- }
- // When reversedStacks is true, loop up,
- // else loop down
- i += upOrDown;
- }
- }
- pointMap[x][cliffName] = cliff;
- });
- // There is no point for this X value in this series, so we
- // insert a dummy point in order for the areas to be drawn
- // correctly.
- } else {
- // Loop down the stack to find the series below this
- // one that has a value (#1991)
- i = seriesIndex;
- while (i >= 0 && i < seriesLength) {
- stackPoint = stack[x].points[i];
- if (stackPoint) {
- y = stackPoint[1];
- break;
- }
- // When reversedStacks is true, loop up, else loop
- // down
- i += upOrDown;
- }
- y = yAxis.translate(y, 0, 1, 0, 1); // #6272
- segment.push({
- isNull: true,
- plotX: xAxis.translate(x, 0, 0, 0, 1), // #6272
- x: x,
- plotY: y,
- yBottom: y
- });
- }
- });
- }
- return segment;
- },
- getGraphPath: function (points) {
- var getGraphPath = Series.prototype.getGraphPath,
- graphPath,
- options = this.options,
- stacking = options.stacking,
- yAxis = this.yAxis,
- topPath,
- bottomPath,
- bottomPoints = [],
- graphPoints = [],
- seriesIndex = this.index,
- i,
- areaPath,
- plotX,
- stacks = yAxis.stacks[this.stackKey],
- threshold = options.threshold,
- translatedThreshold = yAxis.getThreshold(options.threshold),
- isNull,
- yBottom,
- connectNulls = options.connectNulls || stacking === 'percent',
- // To display null points in underlying stacked series, this
- // series graph must be broken, and the area also fall down to
- // fill the gap left by the null point. #2069
- addDummyPoints = function (i, otherI, side) {
- var point = points[i],
- stackedValues = stacking &&
- stacks[point.x].points[seriesIndex],
- nullVal = point[side + 'Null'] || 0,
- cliffVal = point[side + 'Cliff'] || 0,
- top,
- bottom,
- isNull = true;
- if (cliffVal || nullVal) {
- top = (nullVal ? stackedValues[0] : stackedValues[1]) +
- cliffVal;
- bottom = stackedValues[0] + cliffVal;
- isNull = !!nullVal;
- } else if (
- !stacking &&
- points[otherI] &&
- points[otherI].isNull
- ) {
- top = bottom = threshold;
- }
- // Add to the top and bottom line of the area
- if (top !== undefined) {
- graphPoints.push({
- plotX: plotX,
- plotY: top === null ?
- translatedThreshold :
- yAxis.getThreshold(top),
- isNull: isNull,
- isCliff: true
- });
- bottomPoints.push({
- plotX: plotX,
- plotY: bottom === null ?
- translatedThreshold :
- yAxis.getThreshold(bottom),
- doCurve: false // #1041, gaps in areaspline areas
- });
- }
- };
- // Find what points to use
- points = points || this.points;
- // Fill in missing points
- if (stacking) {
- points = this.getStackPoints(points);
- }
- for (i = 0; i < points.length; i++) {
- isNull = points[i].isNull;
- plotX = pick(points[i].rectPlotX, points[i].plotX);
- yBottom = pick(points[i].yBottom, translatedThreshold);
- if (!isNull || connectNulls) {
- if (!connectNulls) {
- addDummyPoints(i, i - 1, 'left');
- }
- // Skip null point when stacking is false and connectNulls
- // true
- if (!(isNull && !stacking && connectNulls)) {
- graphPoints.push(points[i]);
- bottomPoints.push({
- x: i,
- plotX: plotX,
- plotY: yBottom
- });
- }
- if (!connectNulls) {
- addDummyPoints(i, i + 1, 'right');
- }
- }
- }
- topPath = getGraphPath.call(this, graphPoints, true, true);
- bottomPoints.reversed = true;
- bottomPath = getGraphPath.call(this, bottomPoints, true, true);
- if (bottomPath.length) {
- bottomPath[0] = 'L';
- }
- areaPath = topPath.concat(bottomPath);
- // TODO: don't set leftCliff and rightCliff when connectNulls?
- graphPath = getGraphPath
- .call(this, graphPoints, false, connectNulls);
- areaPath.xMap = topPath.xMap;
- this.areaPath = areaPath;
- return graphPath;
- },
- // Draw the graph and the underlying area. This method calls the Series
- // base function and adds the area. The areaPath is calculated in the
- // getSegmentPath method called from Series.prototype.drawGraph.
- drawGraph: function () {
- // Define or reset areaPath
- this.areaPath = [];
- // Call the base method
- Series.prototype.drawGraph.apply(this);
- // Define local variables
- var series = this,
- areaPath = this.areaPath,
- options = this.options,
- zones = this.zones,
- props = [[
- 'area',
- 'highcharts-area',
- this.color,
- options.fillColor
- ]]; // area name, main color, fill color
- zones.forEach(function (zone, i) {
- props.push([
- 'zone-area-' + i,
- 'highcharts-area highcharts-zone-area-' + i + ' ' +
- zone.className,
- zone.color || series.color,
- zone.fillColor || options.fillColor
- ]);
- });
- props.forEach(function (prop) {
- var areaKey = prop[0],
- area = series[areaKey],
- attribs;
- // Create or update the area
- if (area) { // update
- area.endX = series.preventGraphAnimation ?
- null :
- areaPath.xMap;
- area.animate({ d: areaPath });
- } else { // create
- attribs = {
- zIndex: 0 // #1069
- };
- if (!series.chart.styledMode) {
- attribs.fill = pick(
- prop[3],
- color(prop[2])
- .setOpacity(pick(options.fillOpacity, 0.75))
- .get()
- );
- }
- area = series[areaKey] = series.chart.renderer
- .path(areaPath)
- .addClass(prop[1])
- .attr(attribs)
- .add(series.group);
- area.isArea = true;
- }
- area.startX = areaPath.xMap;
- area.shiftUnit = options.step ? 2 : 1;
- });
- },
- drawLegendSymbol: LegendSymbolMixin.drawRectangle
- });
- /**
- * A `area` series. If the [type](#series.area.type) option is not
- * specified, it is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.area
- * @excluding dataParser, dataURL, useOhlcData
- * @product highcharts highstock
- * @apioption series.area
- */
- /**
- * An array of data points for the series. For the `area` series type,
- * points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `y` options. The `x` values will be automatically
- * calculated, either starting at 0 and incremented by 1, or from
- * `pointStart` * and `pointInterval` given in the series options. If the
- * axis has categories, these will be used. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `x,y`. If the first value is a string, it is applied as the name of the
- * point, and the `x` value is inferred.
- * ```js
- * data: [
- * [0, 9],
- * [1, 7],
- * [2, 6]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.area.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * y: 9,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * y: 6,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<(number|string),number>|*>}
- * @extends series.line.data
- * @product highcharts highstock
- * @apioption series.area.data
- */
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var pick = H.pick,
- seriesType = H.seriesType;
- /**
- * Spline series type.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.spline
- *
- * @augments Highcarts.Series
- */
- seriesType(
- 'spline',
- 'line',
- /**
- * A spline series is a special type of line series, where the segments
- * between the data points are smoothed.
- *
- * @sample {highcharts} highcharts/demo/spline-irregular-time/
- * Spline chart
- * @sample {highstock} stock/demo/spline/
- * Spline chart
- *
- * @extends plotOptions.series
- * @excluding step
- * @product highcharts highstock
- * @optionparent plotOptions.spline
- */
- {
- },
- /** @lends seriesTypes.spline.prototype */ {
- /**
- * Get the spline segment from a given point's previous neighbour to the
- * given point.
- *
- * @private
- * @function Highcharts.seriesTypes.spline#getPointSpline
- *
- * @param {Array<Highcharts.Point>}
- *
- * @param {Highcharts.Point} point
- *
- * @param {number} i
- *
- * @return {Highcharts.SVGPathArray}
- */
- getPointSpline: function (points, point, i) {
- var
- // 1 means control points midway between points, 2 means 1/3
- // from the point, 3 is 1/4 etc
- smoothing = 1.5,
- denom = smoothing + 1,
- plotX = point.plotX,
- plotY = point.plotY,
- lastPoint = points[i - 1],
- nextPoint = points[i + 1],
- leftContX,
- leftContY,
- rightContX,
- rightContY,
- ret;
- function doCurve(otherPoint) {
- return otherPoint &&
- !otherPoint.isNull &&
- otherPoint.doCurve !== false &&
- !point.isCliff; // #6387, area splines next to null
- }
- // Find control points
- if (doCurve(lastPoint) && doCurve(nextPoint)) {
- var lastX = lastPoint.plotX,
- lastY = lastPoint.plotY,
- nextX = nextPoint.plotX,
- nextY = nextPoint.plotY,
- correction = 0;
- leftContX = (smoothing * plotX + lastX) / denom;
- leftContY = (smoothing * plotY + lastY) / denom;
- rightContX = (smoothing * plotX + nextX) / denom;
- rightContY = (smoothing * plotY + nextY) / denom;
- // Have the two control points make a straight line through main
- // point
- if (rightContX !== leftContX) { // #5016, division by zero
- correction = (
- ((rightContY - leftContY) * (rightContX - plotX)) /
- (rightContX - leftContX) + plotY - rightContY
- );
- }
- leftContY += correction;
- rightContY += correction;
- // to prevent false extremes, check that control points are
- // between neighbouring points' y values
- if (leftContY > lastY && leftContY > plotY) {
- leftContY = Math.max(lastY, plotY);
- // mirror of left control point
- rightContY = 2 * plotY - leftContY;
- } else if (leftContY < lastY && leftContY < plotY) {
- leftContY = Math.min(lastY, plotY);
- rightContY = 2 * plotY - leftContY;
- }
- if (rightContY > nextY && rightContY > plotY) {
- rightContY = Math.max(nextY, plotY);
- leftContY = 2 * plotY - rightContY;
- } else if (rightContY < nextY && rightContY < plotY) {
- rightContY = Math.min(nextY, plotY);
- leftContY = 2 * plotY - rightContY;
- }
- // record for drawing in next point
- point.rightContX = rightContX;
- point.rightContY = rightContY;
- }
- // Visualize control points for debugging
- /*
- if (leftContX) {
- this.chart.renderer.circle(
- leftContX + this.chart.plotLeft,
- leftContY + this.chart.plotTop,
- 2
- )
- .attr({
- stroke: 'red',
- 'stroke-width': 2,
- fill: 'none',
- zIndex: 9
- })
- .add();
- this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
- leftContY + this.chart.plotTop,
- 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
- .attr({
- stroke: 'red',
- 'stroke-width': 2,
- zIndex: 9
- })
- .add();
- }
- if (rightContX) {
- this.chart.renderer.circle(
- rightContX + this.chart.plotLeft,
- rightContY + this.chart.plotTop,
- 2
- )
- .attr({
- stroke: 'green',
- 'stroke-width': 2,
- fill: 'none',
- zIndex: 9
- })
- .add();
- this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
- rightContY + this.chart.plotTop,
- 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
- .attr({
- stroke: 'green',
- 'stroke-width': 2,
- zIndex: 9
- })
- .add();
- }
- // */
- ret = [
- 'C',
- pick(lastPoint.rightContX, lastPoint.plotX),
- pick(lastPoint.rightContY, lastPoint.plotY),
- pick(leftContX, plotX),
- pick(leftContY, plotY),
- plotX,
- plotY
- ];
- // reset for updating series later
- lastPoint.rightContX = lastPoint.rightContY = null;
- return ret;
- }
- }
- );
- /**
- * A `spline` series. If the [type](#series.spline.type) option is
- * not specified, it is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.spline
- * @excluding dataParser, dataURL, step
- * @product highcharts highstock
- * @apioption series.spline
- */
- /**
- * An array of data points for the series. For the `spline` series type,
- * points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `y` options. The `x` values will be automatically
- * calculated, either starting at 0 and incremented by 1, or from
- * `pointStart` and `pointInterval` given in the series options. If the axis
- * has categories, these will be used. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `x,y`. If the first value is a string, it is applied as the name of the
- * point, and the `x` value is inferred.
- * ```js
- * data: [
- * [0, 9],
- * [1, 2],
- * [2, 8]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.spline.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * y: 9,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * y: 0,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<(number|string),number>|*>}
- * @extends series.line.data
- * @product highcharts highstock
- * @apioption series.spline.data
- */
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var areaProto = H.seriesTypes.area.prototype,
- defaultPlotOptions = H.defaultPlotOptions,
- LegendSymbolMixin = H.LegendSymbolMixin,
- seriesType = H.seriesType;
- /**
- * AreaSpline series type.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.areaspline
- *
- * @augments Highcharts.Series
- */
- seriesType('areaspline', 'spline',
- /**
- * The area spline series is an area series where the graph between the
- * points is smoothed into a spline.
- *
- * @sample {highcharts} highcharts/demo/areaspline/
- * Area spline chart
- * @sample {highstock} stock/demo/areaspline/
- * Area spline chart
- *
- * @extends plotOptions.area
- * @excluding step
- * @product highcharts highstock
- * @apioption plotOptions.areaspline
- */
- defaultPlotOptions.area
- , {
- getStackPoints: areaProto.getStackPoints,
- getGraphPath: areaProto.getGraphPath,
- drawGraph: areaProto.drawGraph,
- drawLegendSymbol: LegendSymbolMixin.drawRectangle
- });
- /**
- * A `areaspline` series. If the [type](#series.areaspline.type) option
- * is not specified, it is inherited from [chart.type](#chart.type).
- *
- *
- * @extends series,plotOptions.areaspline
- * @excluding dataParser, dataURL
- * @product highcharts highstock
- * @apioption series.areaspline
- */
- /**
- * An array of data points for the series. For the `areaspline` series
- * type, points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `y` options. The `x` values will be automatically
- * calculated, either starting at 0 and incremented by 1, or from
- * `pointStart` and `pointInterval` given in the series options. If the axis
- * has categories, these will be used. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `x,y`. If the first value is a string, it is applied as the name of the
- * point, and the `x` value is inferred.
- * ```js
- * data: [
- * [0, 10],
- * [1, 9],
- * [2, 3]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.areaspline.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * y: 4,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * y: 4,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<(number|string),number>|*>}
- * @extends series.line.data
- * @product highcharts highstock
- * @apioption series.areaspline.data
- */
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Adjusted width and x offset of the columns for grouping.
- *
- * @private
- * @interface Highcharts.ColumnMetricsObject
- *//**
- * Width of the columns.
- *
- * @name Highcharts.ColumnMetricsObject#width
- * @type {number}
- *//**
- * Offset of the columns.
- *
- * @name Highcharts.ColumnMetricsObject#offset
- * @type {number}
- */
- var animObject = H.animObject,
- color = H.color,
- extend = H.extend,
- defined = H.defined,
- isNumber = H.isNumber,
- LegendSymbolMixin = H.LegendSymbolMixin,
- merge = H.merge,
- noop = H.noop,
- pick = H.pick,
- Series = H.Series,
- seriesType = H.seriesType,
- svg = H.svg;
- /**
- * The column series type.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.column
- *
- * @augments Highcharts.Series
- */
- seriesType('column', 'line'
- /**
- * Column series display one column per value along an X axis.
- *
- * @sample {highcharts} highcharts/demo/column-basic/
- * Column chart
- * @sample {highstock} stock/demo/column/
- * Column chart
- *
- * @extends plotOptions.line
- * @excluding connectNulls, dashStyle, gapSize, gapUnit, linecap,
- * lineWidth, marker, connectEnds, step, useOhlcData
- * @product highcharts highstock
- * @optionparent plotOptions.column
- */
- , {
- /**
- * The corner radius of the border surrounding each column or bar.
- *
- * @sample {highcharts} highcharts/plotoptions/column-borderradius/
- * Rounded columns
- *
- * @product highcharts highstock gantt
- */
- borderRadius: 0,
- /**
- * When using automatic point colors pulled from the global
- * [colors](colors) or series-specific
- * [plotOptions.column.colors](series.colors) collections, this option
- * determines whether the chart should receive one color per series or
- * one color per point.
- *
- * In styled mode, the `colors` or `series.colors` arrays are not
- * supported, and instead this option gives the points individual color
- * class names on the form `highcharts-color-{n}`.
- *
- * @see [series colors](#plotOptions.column.colors)
- *
- * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
- * False by default
- * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
- * True
- *
- * @type {boolean}
- * @default false
- * @since 2.0
- * @product highcharts highstock gantt
- * @apioption plotOptions.column.colorByPoint
- */
- /**
- * A series specific or series type specific color set to apply instead
- * of the global [colors](#colors) when [colorByPoint](
- * #plotOptions.column.colorByPoint) is true.
- *
- * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
- * @since 3.0
- * @product highcharts highstock gantt
- * @apioption plotOptions.column.colors
- */
- /**
- * When true, each column edge is rounded to its nearest pixel in order
- * to render sharp on screen. In some cases, when there are a lot of
- * densely packed columns, this leads to visible difference in column
- * widths or distance between columns. In these cases, setting `crisp`
- * to `false` may look better, even though each column is rendered
- * blurry.
- *
- * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
- * Crisp is false
- *
- * @since 5.0.10
- * @product highcharts highstock gantt
- */
- crisp: true,
- /**
- * Padding between each value groups, in x axis units.
- *
- * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
- * 0.2 by default
- * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
- * No group padding - all columns are evenly spaced
- *
- * @product highcharts highstock gantt
- */
- groupPadding: 0.2,
- /**
- * Whether to group non-stacked columns or to let them render
- * independent of each other. Non-grouped columns will be laid out
- * individually and overlap each other.
- *
- * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
- * Grouping disabled
- * @sample {highstock} highcharts/plotoptions/column-grouping-false/
- * Grouping disabled
- *
- * @type {boolean}
- * @default true
- * @since 2.3.0
- * @product highcharts highstock gantt
- * @apioption plotOptions.column.grouping
- */
- /**
- * @ignore-option
- */
- marker: null, // point options are specified in the base options
- /**
- * The maximum allowed pixel width for a column, translated to the
- * height of a bar in a bar chart. This prevents the columns from
- * becoming too wide when there is a small number of points in the
- * chart.
- *
- * @see [pointWidth](#plotOptions.column.pointWidth)
- *
- * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
- * Limited to 50
- * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
- * Limited to 50
- *
- * @type {number}
- * @since 4.1.8
- * @product highcharts highstock gantt
- * @apioption plotOptions.column.maxPointWidth
- */
- /**
- * Padding between each column or bar, in x axis units.
- *
- * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
- * 0.1 by default
- * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
- * 0.25
- * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
- * 0 for tightly packed columns
- *
- * @product highcharts highstock gantt
- */
- pointPadding: 0.1,
- /**
- * A pixel value specifying a fixed width for each column or bar. When
- * `null`, the width is calculated from the `pointPadding` and
- * `groupPadding`.
- *
- * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
- *
- * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
- * 20px wide columns regardless of chart width or the amount of
- * data points
- *
- * @type {number}
- * @since 1.2.5
- * @product highcharts highstock gantt
- * @apioption plotOptions.column.pointWidth
- */
- /**
- * A pixel value specifying a fixed width for the column or bar.
- * Overrides pointWidth on the series.
- *
- * @see [series.pointWidth](#plotOptions.column.pointWidth)
- *
- * @type {number}
- * @default undefined
- * @since 7.0.0
- * @product highcharts highstock gantt
- * @apioption series.column.data.pointWidth
- */
- /**
- * The minimal height for a column or width for a bar. By default,
- * 0 values are not shown. To visualize a 0 (or close to zero) point,
- * set the minimal point length to a pixel value like 3\. In stacked
- * column charts, minPointLength might not be respected for tightly
- * packed values.
- *
- * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
- * Zero base value
- * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
- * Positive and negative close to zero values
- *
- * @product highcharts highstock gantt
- */
- minPointLength: 0,
- /**
- * When the series contains less points than the crop threshold, all
- * points are drawn, event if the points fall outside the visible plot
- * area at the current zoom. The advantage of drawing all points
- * (including markers and columns), is that animation is performed on
- * updates. On the other hand, when the series contains more points than
- * the crop threshold, the series data is cropped to only contain points
- * that fall within the plot area. The advantage of cropping away
- * invisible points is to increase performance on large series.
- *
- * @product highcharts highstock gantt
- */
- cropThreshold: 50,
- /**
- * The X axis range that each point is valid for. This determines the
- * width of the column. On a categorized axis, the range will be 1
- * by default (one category unit). On linear and datetime axes, the
- * range will be computed as the distance between the two closest data
- * points.
- *
- * The default `null` means it is computed automatically, but this
- * option can be used to override the automatic value.
- *
- * @sample {highcharts} highcharts/plotoptions/column-pointrange/
- * Set the point range to one day on a data set with one week
- * between the points
- *
- * @type {number|null}
- * @since 2.3
- * @product highcharts highstock gantt
- */
- pointRange: null,
- states: {
- /**
- * Options for the hovered point. These settings override the normal
- * state options when a point is moused over or touched.
- *
- * @extends plotOptions.series.states.hover
- * @excluding halo, lineWidth, lineWidthPlus, marker
- * @product highcharts highstock gantt
- */
- hover: {
- /** @ignore-option */
- halo: false,
- /**
- * A specific border color for the hovered point. Defaults to
- * inherit the normal state border color.
- *
- * @type {Highcharts.ColorString}
- * @product highcharts gantt
- * @apioption plotOptions.column.states.hover.borderColor
- */
- /**
- * A specific color for the hovered point.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highcharts gantt
- * @apioption plotOptions.column.states.hover.color
- */
- /**
- * How much to brighten the point on interaction. Requires the
- * main color to be defined in hex or rgb(a) format.
- *
- * In styled mode, the hover brightening is by default replaced
- * with a fill-opacity set in the `.highcharts-point:hover`
- * rule.
- *
- * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
- * Brighten by 0.5
- *
- * @product highcharts highstock gantt
- */
- brightness: 0.1
- },
- /**
- * Options for the selected point. These settings override the
- * normal state options when a point is selected.
- *
- * @extends plotOptions.series.states.select
- * @excluding halo, lineWidth, lineWidthPlus, marker
- * @product highcharts highstock gantt
- */
- select: {
- /**
- * A specific color for the selected point.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @default #cccccc
- * @product highcharts highstock gantt
- */
- color: '#cccccc',
- /**
- * A specific border color for the selected point.
- *
- * @type {Highcharts.ColorString}
- * @default #000000
- * @product highcharts highstock gantt
- */
- borderColor: '#000000'
- }
- },
- dataLabels: {
- /**
- * @type {Highcharts.AlignType|null}
- */
- align: null, // auto
- /**
- * @type {Highcharts.VerticalAlignType|null}
- */
- verticalAlign: null, // auto
- /**
- * @type {number|null}
- */
- y: null
- },
- /**
- * When this is true, the series will not cause the Y axis to cross
- * the zero plane (or [threshold](#plotOptions.series.threshold) option)
- * unless the data actually crosses the plane.
- *
- * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
- * 3 will make the Y axis show negative values according to the
- * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
- * at 0.
- *
- * @since 4.1.9
- * @product highcharts highstock
- */
- softThreshold: false,
- // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
- /** @ignore-option */
- startFromThreshold: true,
- stickyTracking: false,
- tooltip: {
- distance: 6
- },
- /**
- * The Y axis value to serve as the base for the columns, for
- * distinguishing between values above and below a threshold. If `null`,
- * the columns extend from the padding Y axis minimum.
- *
- * @since 2.0
- * @product highcharts
- */
- threshold: 0,
- /**
- * The width of the border surrounding each column or bar. Defaults to
- * `1` when there is room for a border, but to `0` when the columns are
- * so dense that a border would cover the next column.
- *
- * In styled mode, the stroke width can be set with the
- * `.highcharts-point` rule.
- *
- * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
- * 2px black border
- *
- * @type {number}
- * @default undefined
- * @product highcharts highstock gantt
- * @apioption plotOptions.column.borderWidth
- */
- /**
- * The color of the border surrounding each column or bar.
- *
- * In styled mode, the border stroke can be set with the
- * `.highcharts-point` rule.
- *
- * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
- * Dark gray border
- *
- * @type {Highcharts.ColorString}
- * @default #ffffff
- * @product highcharts highstock gantt
- */
- borderColor: '#ffffff'
- }, /** @lends seriesTypes.column.prototype */ {
- cropShoulder: 0,
- // When tooltip is not shared, this series (and derivatives) requires
- // direct touch/hover. KD-tree does not apply.
- directTouch: true,
- trackerGroups: ['group', 'dataLabelsGroup'],
- // use separate negative stacks, unlike area stacks where a negative
- // point is substracted from previous (#1910)
- negStacks: true,
- /**
- * Initialize the series. Extends the basic Series.init method by
- * marking other series of the same type as dirty.
- *
- * @private
- * @function Highcharts.seriesTypes.column#init
- */
- init: function () {
- Series.prototype.init.apply(this, arguments);
- var series = this,
- chart = series.chart;
- // if the series is added dynamically, force redraw of other
- // series affected by a new column
- if (chart.hasRendered) {
- chart.series.forEach(function (otherSeries) {
- if (otherSeries.type === series.type) {
- otherSeries.isDirty = true;
- }
- });
- }
- },
- /**
- * Return the width and x offset of the columns adjusted for grouping,
- * groupPadding, pointPadding, pointWidth etc.
- *
- * @private
- * @function Highcharts.seriesTypes.column#getColumnMetrics
- *
- * @return {Highcharts.ColumnMetricsObject}
- */
- getColumnMetrics: function () {
- var series = this,
- options = series.options,
- xAxis = series.xAxis,
- yAxis = series.yAxis,
- reversedStacks = xAxis.options.reversedStacks,
- // Keep backward compatibility: reversed xAxis had reversed
- // stacks
- reverseStacks = (xAxis.reversed && !reversedStacks) ||
- (!xAxis.reversed && reversedStacks),
- stackKey,
- stackGroups = {},
- columnCount = 0;
- // Get the total number of column type series. This is called on
- // every series. Consider moving this logic to a chart.orderStacks()
- // function and call it on init, addSeries and removeSeries
- if (options.grouping === false) {
- columnCount = 1;
- } else {
- series.chart.series.forEach(function (otherSeries) {
- var otherOptions = otherSeries.options,
- otherYAxis = otherSeries.yAxis,
- columnIndex;
- if (
- otherSeries.type === series.type &&
- (
- otherSeries.visible ||
- !series.chart.options.chart.ignoreHiddenSeries
- ) &&
- yAxis.len === otherYAxis.len &&
- yAxis.pos === otherYAxis.pos
- ) { // #642, #2086
- if (otherOptions.stacking) {
- stackKey = otherSeries.stackKey;
- if (stackGroups[stackKey] === undefined) {
- stackGroups[stackKey] = columnCount++;
- }
- columnIndex = stackGroups[stackKey];
- } else if (otherOptions.grouping !== false) { // #1162
- columnIndex = columnCount++;
- }
- otherSeries.columnIndex = columnIndex;
- }
- });
- }
- var categoryWidth = Math.min(
- Math.abs(xAxis.transA) * (
- xAxis.ordinalSlope ||
- options.pointRange ||
- xAxis.closestPointRange ||
- xAxis.tickInterval ||
- 1
- ), // #2610
- xAxis.len // #1535
- ),
- groupPadding = categoryWidth * options.groupPadding,
- groupWidth = categoryWidth - 2 * groupPadding,
- pointOffsetWidth = groupWidth / (columnCount || 1),
- pointWidth = Math.min(
- options.maxPointWidth || xAxis.len,
- pick(
- options.pointWidth,
- pointOffsetWidth * (1 - 2 * options.pointPadding)
- )
- ),
- pointPadding = (pointOffsetWidth - pointWidth) / 2,
- // #1251, #3737
- colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0),
- pointXOffset =
- pointPadding +
- (
- groupPadding +
- colIndex * pointOffsetWidth -
- (categoryWidth / 2)
- ) * (reverseStacks ? -1 : 1);
- // Save it for reading in linked series (Error bars particularly)
- series.columnMetrics = {
- width: pointWidth,
- offset: pointXOffset
- };
- return series.columnMetrics;
- },
- /**
- * Make the columns crisp. The edges are rounded to the nearest full
- * pixel.
- *
- * @private
- * @function Highcharts.seriesTypes.column#crispCol
- *
- * @param {number} x
- *
- * @param {number} y
- *
- * @param {number} w
- *
- * @param {number} h
- *
- * @return {*}
- */
- crispCol: function (x, y, w, h) {
- var chart = this.chart,
- borderWidth = this.borderWidth,
- xCrisp = -(borderWidth % 2 ? 0.5 : 0),
- yCrisp = borderWidth % 2 ? 0.5 : 1,
- right,
- bottom,
- fromTop;
- if (chart.inverted && chart.renderer.isVML) {
- yCrisp += 1;
- }
- // Horizontal. We need to first compute the exact right edge, then
- // round it and compute the width from there.
- if (this.options.crisp) {
- right = Math.round(x + w) + xCrisp;
- x = Math.round(x) + xCrisp;
- w = right - x;
- }
- // Vertical
- bottom = Math.round(y + h) + yCrisp;
- fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
- y = Math.round(y) + yCrisp;
- h = bottom - y;
- // Top edges are exceptions
- if (fromTop && h) { // #5146
- y -= 1;
- h += 1;
- }
- return {
- x: x,
- y: y,
- width: w,
- height: h
- };
- },
- /**
- * Translate each point to the plot area coordinate system and find
- * shape positions
- *
- * @private
- * @function Highcharts.seriesTypes.column#translate
- */
- translate: function () {
- var series = this,
- chart = series.chart,
- options = series.options,
- dense = series.dense =
- series.closestPointRange * series.xAxis.transA < 2,
- borderWidth = series.borderWidth = pick(
- options.borderWidth,
- dense ? 0 : 1 // #3635
- ),
- yAxis = series.yAxis,
- threshold = options.threshold,
- translatedThreshold = series.translatedThreshold =
- yAxis.getThreshold(threshold),
- minPointLength = pick(options.minPointLength, 5),
- metrics = series.getColumnMetrics(),
- seriesPointWidth = metrics.width,
- // postprocessed for border width
- seriesBarW = series.barW =
- Math.max(seriesPointWidth, 1 + 2 * borderWidth),
- seriesXOffset = series.pointXOffset = metrics.offset;
- if (chart.inverted) {
- translatedThreshold -= 0.5; // #3355
- }
- // When the pointPadding is 0, we want the columns to be packed
- // tightly, so we allow individual columns to have individual sizes.
- // When pointPadding is greater, we strive for equal-width columns
- // (#2694).
- if (options.pointPadding) {
- seriesBarW = Math.ceil(seriesBarW);
- }
- Series.prototype.translate.apply(series);
- // Record the new values
- series.points.forEach(function (point) {
- var yBottom = pick(point.yBottom, translatedThreshold),
- safeDistance = 999 + Math.abs(yBottom),
- pointWidth = seriesPointWidth,
- // Don't draw too far outside plot area (#1303, #2241,
- // #4264)
- plotY = Math.min(
- Math.max(-safeDistance, point.plotY),
- yAxis.len + safeDistance
- ),
- barX = point.plotX + seriesXOffset,
- barW = seriesBarW,
- barY = Math.min(plotY, yBottom),
- up,
- barH = Math.max(plotY, yBottom) - barY;
- // Handle options.minPointLength
- if (minPointLength && Math.abs(barH) < minPointLength) {
- barH = minPointLength;
- up = (!yAxis.reversed && !point.negative) ||
- (yAxis.reversed && point.negative);
- // Reverse zeros if there's no positive value in the series
- // in visible range (#7046)
- if (
- point.y === threshold &&
- series.dataMax <= threshold &&
- yAxis.min < threshold // and if there's room for it (#7311)
- ) {
- up = !up;
- }
- // If stacked...
- barY = (
- Math.abs(barY - translatedThreshold) > minPointLength ?
- // ...keep position
- yBottom - minPointLength :
- // #1485, #4051
- translatedThreshold - (up ? minPointLength : 0)
- );
- }
- // Handle point.options.pointWidth
- // @todo Handle grouping/stacking too. Calculate offset properly
- if (defined(point.options.pointWidth)) {
- pointWidth = barW = Math.ceil(point.options.pointWidth);
- barX -= Math.round((pointWidth - seriesPointWidth) / 2);
- }
- // Cache for access in polar
- point.barX = barX;
- point.pointWidth = pointWidth;
- // Fix the tooltip on center of grouped columns (#1216, #424,
- // #3648)
- point.tooltipPos = chart.inverted ?
- [
- yAxis.len + yAxis.pos - chart.plotLeft - plotY,
- series.xAxis.len - barX - barW / 2, barH
- ] :
- [barX + barW / 2, plotY + yAxis.pos - chart.plotTop, barH];
- // Register shape type and arguments to be used in drawPoints
- // Allow shapeType defined on pointClass level
- point.shapeType = point.shapeType || 'rect';
- point.shapeArgs = series.crispCol.apply(
- series,
- point.isNull ?
- // #3169, drilldown from null must have a position to work
- // from #6585, dataLabel should be placed on xAxis, not
- // floating in the middle of the chart
- [barX, translatedThreshold, barW, 0] :
- [barX, barY, barW, barH]
- );
- });
- },
- getSymbol: noop,
- /**
- * Use a solid rectangle like the area series types
- *
- * @private
- * @function Highcharts.seriesTypes.column#drawLegendSymbol
- *
- * @param {Highcharts.Legend} legend
- * The legend object
- *
- * @param {Highcharts.Series|Highcharts.Point} item
- * The series (this) or point
- */
- drawLegendSymbol: LegendSymbolMixin.drawRectangle,
- /**
- * Columns have no graph
- *
- * @private
- * @function Highcharts.seriesTypes.column#drawGraph
- */
- drawGraph: function () {
- this.group[
- this.dense ? 'addClass' : 'removeClass'
- ]('highcharts-dense-data');
- },
- /**
- * Get presentational attributes
- *
- * @private
- * @function Highcharts.seriesTypes.column#pointAttribs
- *
- * @param {Highcharts.Point} point
- *
- * @param {string} state
- *
- * @return {Highcharts.Dictionary<any>}
- */
- pointAttribs: function (point, state) {
- var options = this.options,
- stateOptions,
- ret,
- p2o = this.pointAttrToOptions || {},
- strokeOption = p2o.stroke || 'borderColor',
- strokeWidthOption = p2o['stroke-width'] || 'borderWidth',
- fill = (point && point.color) || this.color,
- // set to fill when borderColor null:
- stroke = (
- (point && point[strokeOption]) ||
- options[strokeOption] ||
- this.color ||
- fill
- ),
- strokeWidth = (point && point[strokeWidthOption]) ||
- options[strokeWidthOption] || this[strokeWidthOption] || 0,
- dashstyle = options.dashStyle,
- zone,
- brightness;
- // Handle zone colors
- if (point && this.zones.length) {
- zone = point.getZone();
- // When zones are present, don't use point.color (#4267).
- // Changed order (#6527)
- fill = (
- point.options.color || (zone && zone.color) || this.color
- );
- }
- // Select or hover states
- if (state) {
- stateOptions = merge(
- options.states[state],
- // #6401
- point.options.states && point.options.states[state] || {}
- );
- brightness = stateOptions.brightness;
- fill = stateOptions.color ||
- (
- brightness !== undefined &&
- color(fill).brighten(stateOptions.brightness).get()
- ) ||
- fill;
- stroke = stateOptions[strokeOption] || stroke;
- strokeWidth = stateOptions[strokeWidthOption] || strokeWidth;
- dashstyle = stateOptions.dashStyle || dashstyle;
- }
- ret = {
- 'fill': fill,
- 'stroke': stroke,
- 'stroke-width': strokeWidth
- };
- if (dashstyle) {
- ret.dashstyle = dashstyle;
- }
- return ret;
- },
- /**
- * Draw the columns. For bars, the series.group is rotated, so the same
- * coordinates apply for columns and bars. This method is inherited by
- * scatter series.
- *
- * @private
- * @function Highcharts.seriesTypes.column#drawPoints
- */
- drawPoints: function () {
- var series = this,
- chart = this.chart,
- options = series.options,
- renderer = chart.renderer,
- animationLimit = options.animationLimit || 250,
- shapeArgs;
- // draw the columns
- series.points.forEach(function (point) {
- var plotY = point.plotY,
- graphic = point.graphic,
- verb = graphic && chart.pointCount < animationLimit ?
- 'animate' : 'attr';
- if (isNumber(plotY) && point.y !== null) {
- shapeArgs = point.shapeArgs;
- if (graphic) { // update
- graphic[verb](
- merge(shapeArgs)
- );
- } else {
- point.graphic = graphic =
- renderer[point.shapeType](shapeArgs)
- .add(point.group || series.group);
- }
- // Border radius is not stylable (#6900)
- if (options.borderRadius) {
- graphic.attr({
- r: options.borderRadius
- });
- }
- // Presentational
- if (!chart.styledMode) {
- graphic[verb](series.pointAttribs(
- point,
- point.selected && 'select'
- ))
- .shadow(
- options.shadow,
- null,
- options.stacking && !options.borderRadius
- );
- }
- graphic.addClass(point.getClassName(), true);
- } else if (graphic) {
- point.graphic = graphic.destroy(); // #1269
- }
- });
- },
- /**
- * Animate the column heights one by one from zero.
- *
- * @private
- * @function Highcharts.seriesTypes.column#animate
- *
- * @param {boolean} init
- * Whether to initialize the animation or run it
- */
- animate: function (init) {
- var series = this,
- yAxis = this.yAxis,
- options = series.options,
- inverted = this.chart.inverted,
- attr = {},
- translateProp = inverted ? 'translateX' : 'translateY',
- translateStart,
- translatedThreshold;
- if (svg) { // VML is too slow anyway
- if (init) {
- attr.scaleY = 0.001;
- translatedThreshold = Math.min(
- yAxis.pos + yAxis.len,
- Math.max(yAxis.pos, yAxis.toPixels(options.threshold))
- );
- if (inverted) {
- attr.translateX = translatedThreshold - yAxis.len;
- } else {
- attr.translateY = translatedThreshold;
- }
- // apply finnal clipping (used in Highstock) (#7083)
- // animation is done by scaleY, so cliping is for panes
- if (series.clipBox) {
- series.setClip();
- }
- series.group.attr(attr);
- } else { // run the animation
- translateStart = series.group.attr(translateProp);
- series.group.animate(
- { scaleY: 1 },
- extend(animObject(series.options.animation), {
- // Do the scale synchronously to ensure smooth
- // updating (#5030, #7228)
- step: function (val, fx) {
- attr[translateProp] =
- translateStart +
- fx.pos * (yAxis.pos - translateStart);
- series.group.attr(attr);
- }
- })
- );
- // delete this function to allow it only once
- series.animate = null;
- }
- }
- },
- /**
- * Remove this series from the chart
- *
- * @private
- * @function Highcharts.seriesTypes.column#remove
- */
- remove: function () {
- var series = this,
- chart = series.chart;
- // column and bar series affects other series of the same type
- // as they are either stacked or grouped
- if (chart.hasRendered) {
- chart.series.forEach(function (otherSeries) {
- if (otherSeries.type === series.type) {
- otherSeries.isDirty = true;
- }
- });
- }
- Series.prototype.remove.apply(series, arguments);
- }
- });
- /**
- * A `column` series. If the [type](#series.column.type) option is
- * not specified, it is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.column
- * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
- * linecap, lineWidth, marker, connectEnds, step
- * @product highcharts highstock
- * @apioption series.column
- */
- /**
- * An array of data points for the series. For the `column` series type,
- * points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `y` options. The `x` values will be automatically
- * calculated, either starting at 0 and incremented by 1, or from
- * `pointStart` and `pointInterval` given in the series options. If the axis
- * has categories, these will be used. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `x,y`. If the first value is a string, it is applied as the name of the
- * point, and the `x` value is inferred.
- * ```js
- * data: [
- * [0, 6],
- * [1, 2],
- * [2, 6]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.column.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * y: 9,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * y: 6,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<(number|string),number>|*>}
- * @extends series.line.data
- * @excluding marker
- * @product highcharts highstock
- * @apioption series.column.data
- */
- /**
- * The color of the border surrounding the column or bar.
- *
- * In styled mode, the border stroke can be set with the `.highcharts-point`
- * rule.
- *
- * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
- * Dark gray border
- *
- * @type {Highcharts.ColorString}
- * @product highcharts highstock
- * @apioption series.column.data.borderColor
- */
- /**
- * The width of the border surrounding the column or bar.
- *
- * In styled mode, the stroke width can be set with the `.highcharts-point`
- * rule.
- *
- * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
- * 2px black border
- *
- * @type {number}
- * @product highcharts highstock
- * @apioption series.column.data.borderWidth
- */
- /**
- * @excluding halo, lineWidth, lineWidthPlus, marker
- * @product highcharts highstock
- * @apioption series.column.states.hover
- */
- /**
- * @excluding halo, lineWidth, lineWidthPlus, marker
- * @product highcharts highstock
- * @apioption series.column.states.select
- */
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var seriesType = H.seriesType;
- /**
- * Bar series type.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.bar
- *
- * @augments Highcharts.Series
- */
- seriesType('bar', 'column',
- /**
- * A bar series is a special type of column series where the columns are
- * horizontal.
- *
- * @sample highcharts/demo/bar-basic/
- * Bar chart
- *
- * @extends plotOptions.column
- * @product highcharts
- * @apioption plotOptions.bar
- */
- /**
- * Alignment of the data label relative to the data point.
- *
- * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
- * Data labels inside the bar
- *
- * @type {string}
- * @default left
- * @product highcharts
- * @apioption plotOptions.bar.dataLabels.align
- */
- /**
- * The x position of the data label relative to the data point.
- *
- * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
- * Data labels inside the bar
- *
- * @type {number}
- * @default 5
- * @product highcharts
- * @apioption plotOptions.bar.dataLabels.x
- */
- /**
- * @ignore
- */
- null
- , {
- inverted: true
- });
- /**
- * A `bar` series. If the [type](#series.bar.type) option is not specified,
- * it is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.bar
- * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
- * linecap, lineWidth, marker, connectEnds, step
- * @product highcharts
- * @apioption series.bar
- */
- /**
- * An array of data points for the series. For the `bar` series type,
- * points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `y` options. The `x` values will be automatically
- * calculated, either starting at 0 and incremented by 1, or from
- * `pointStart` and `pointInterval` given in the series options. If the axis
- * has categories, these will be used. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `x,y`. If the first value is a string, it is applied as the name of the
- * point, and the `x` value is inferred.
- * ```js
- * data: [
- * [0, 5],
- * [1, 10],
- * [2, 3]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.bar.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * y: 1,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * y: 10,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<(number|string),number>|*>}
- * @extends series.column.data
- * @product highcharts
- * @apioption series.bar.data
- */
- /**
- * @excluding halo,lineWidth,lineWidthPlus,marker
- * @product highcharts highstock
- * @apioption series.bar.states.hover
- */
- /**
- * @excluding halo,lineWidth,lineWidthPlus,marker
- * @product highcharts highstock
- * @apioption series.bar.states.select
- */
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var Series = H.Series,
- seriesType = H.seriesType;
- /**
- * Scatter series type.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.scatter
- *
- * @augments Highcharts.Series
- */
- seriesType(
- 'scatter',
- 'line',
- /**
- * A scatter plot uses cartesian coordinates to display values for two
- * variables for a set of data.
- *
- * @sample {highcharts} highcharts/demo/scatter/
- * Scatter plot
- *
- * @extends plotOptions.line
- * @excluding pointPlacement, shadow, useOhlcData
- * @product highcharts highstock
- * @optionparent plotOptions.scatter
- */
- {
- /**
- * The width of the line connecting the data points.
- *
- * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
- * 0 by default
- * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
- * 1px
- *
- * @product highcharts highstock
- */
- lineWidth: 0,
- findNearestPointBy: 'xy',
- /**
- * Apply a jitter effect for the rendered markers. When plotting
- * discrete values, a little random noise may help telling the points
- * apart. The jitter setting applies a random displacement of up to `n`
- * axis units in either direction. So for example on a horizontal X
- * axis, setting the `jitter.x` to 0.24 will render the point in a
- * random position between 0.24 units to the left and 0.24 units to the
- * right of the true axis position. On a category axis, setting it to
- * 0.5 will fill up the bin and make the data appear continuous.
- *
- * When rendered on top of a box plot or a column series, a jitter value
- * of 0.24 will correspond to the underlying series' default
- * [groupPadding](
- * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
- * and [pointPadding](
- * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
- * settings.
- *
- * @sample {highcharts} highcharts/series-scatter/jitter
- * Jitter on a scatter plot
- *
- * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
- * Jittered scatter plot on top of a box plot
- *
- * @product highcharts highstock
- * @since 7.0.2
- */
- jitter: {
- /**
- * The maximal X offset for the random jitter effect.
- */
- x: 0,
- /**
- * The maximal Y offset for the random jitter effect.
- */
- y: 0
- },
- marker: {
- enabled: true // Overrides auto-enabling in line series (#3647)
- },
- /**
- * Sticky tracking of mouse events. When true, the `mouseOut` event
- * on a series isn't triggered until the mouse moves over another
- * series, or out of the plot area. When false, the `mouseOut` event on
- * a series is triggered when the mouse leaves the area around the
- * series' graph or markers. This also implies the tooltip. When
- * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
- * will be hidden when moving the mouse between series.
- *
- * @type {boolean}
- * @default false
- * @product highcharts highstock
- * @apioption plotOptions.scatter.stickyTracking
- */
- /**
- * A configuration object for the tooltip rendering of each single
- * series. Properties are inherited from [tooltip](#tooltip).
- * Overridable properties are `headerFormat`, `pointFormat`,
- * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
- * series, in a scatter plot the series.name by default shows in the
- * headerFormat and point.x and point.y in the pointFormat.
- *
- * @product highcharts highstock
- */
- tooltip: {
- headerFormat:
- '<span style="color:{point.color}">\u25CF</span> ' +
- '<span style="font-size: 10px"> {series.name}</span><br/>',
- pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
- }
- // Prototype members
- }, {
- sorted: false,
- requireSorting: false,
- noSharedTooltip: true,
- trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
- takeOrdinalPosition: false, // #2342
- /**
- * @private
- * @function Highcharts.seriesTypes.scatter#drawGraph
- */
- drawGraph: function () {
- if (this.options.lineWidth) {
- Series.prototype.drawGraph.call(this);
- }
- },
- // Optionally add the jitter effect
- applyJitter: function () {
- var series = this,
- jitter = this.options.jitter,
- len = this.points.length;
- // Return a repeatable, pseudo-random number based on an integer
- // seed
- function unrandom(seed) {
- var rand = Math.sin(seed) * 10000;
- return rand - Math.floor(rand);
- }
- if (jitter) {
- this.points.forEach(function (point, i) {
- ['x', 'y'].forEach(function (dim, j) {
- var axis,
- plotProp = 'plot' + dim.toUpperCase(),
- min,
- max,
- translatedJitter;
- if (jitter[dim] && !point.isNull) {
- axis = series[dim + 'Axis'];
- translatedJitter = jitter[dim] * axis.transA;
- if (axis && !axis.isLog) {
- // Identify the outer bounds of the jitter range
- min = Math.max(
- 0,
- point[plotProp] - translatedJitter
- );
- max = Math.min(
- axis.len,
- point[plotProp] + translatedJitter
- );
- // Find a random position within this range
- point[plotProp] = min +
- (max - min) * unrandom(i + j * len);
- // Update clientX for the tooltip k-d-tree
- if (dim === 'x') {
- point.clientX = point.plotX;
- }
- }
- }
- });
- });
- }
- }
- }
- );
- H.addEvent(Series, 'afterTranslate', function () {
- if (this.applyJitter) {
- this.applyJitter();
- }
- });
- /**
- * A `scatter` series. If the [type](#series.scatter.type) option is
- * not specified, it is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.scatter
- * @excluding dataParser, dataURL, useOhlcData
- * @product highcharts highstock
- * @apioption series.scatter
- */
- /**
- * An array of data points for the series. For the `scatter` series
- * type, points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `y` options. The `x` values will be automatically
- * calculated, either starting at 0 and incremented by 1, or from
- * `pointStart` and `pointInterval` given in the series options. If the axis
- * has categories, these will be used. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `x,y`. If the first value is a string, it is applied as the name of the
- * point, and the `x` value is inferred.
- * ```js
- * data: [
- * [0, 0],
- * [1, 8],
- * [2, 9]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.scatter.turboThreshold), this option is not
- * available.
- * ```js
- * data: [{
- * x: 1,
- * y: 2,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * x: 1,
- * y: 4,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<(number|string),number>|*>}
- * @extends series.line.data
- * @product highcharts highstock
- * @apioption series.scatter.data
- */
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * @private
- * @typedef Highcharts.RadianAngles
- *
- * @property {number} start
- *
- * @property {number} end
- */
- var deg2rad = H.deg2rad,
- isNumber = H.isNumber,
- pick = H.pick,
- relativeLength = H.relativeLength;
- /**
- * @private
- * @mixin Highcharts.CenteredSeriesMixin
- */
- H.CenteredSeriesMixin = {
- /**
- * Get the center of the pie based on the size and center options relative
- * to the plot area. Borrowed by the polar and gauge series types.
- *
- * @private
- * @function Highcharts.CenteredSeriesMixin.getCenter
- *
- * @return {Array<number>}
- */
- getCenter: function () {
- var options = this.options,
- chart = this.chart,
- slicingRoom = 2 * (options.slicedOffset || 0),
- handleSlicingRoom,
- plotWidth = chart.plotWidth - 2 * slicingRoom,
- plotHeight = chart.plotHeight - 2 * slicingRoom,
- centerOption = options.center,
- positions = [
- pick(centerOption[0], '50%'),
- pick(centerOption[1], '50%'),
- options.size || '100%',
- options.innerSize || 0
- ],
- smallestSize = Math.min(plotWidth, plotHeight),
- i,
- value;
- for (i = 0; i < 4; ++i) {
- value = positions[i];
- handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
- // i == 0: centerX, relative to width
- // i == 1: centerY, relative to height
- // i == 2: size, relative to smallestSize
- // i == 3: innerSize, relative to size
- positions[i] = relativeLength(
- value,
- [plotWidth, plotHeight, smallestSize, positions[2]][i]
- ) + (handleSlicingRoom ? slicingRoom : 0);
- }
- // innerSize cannot be larger than size (#3632)
- if (positions[3] > positions[2]) {
- positions[3] = positions[2];
- }
- return positions;
- },
- /**
- * getStartAndEndRadians - Calculates start and end angles in radians.
- * Used in series types such as pie and sunburst.
- *
- * @private
- * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
- *
- * @param {number} start
- * Start angle in degrees.
- *
- * @param {number} end
- * Start angle in degrees.
- *
- * @return {Highcharts.RadianAngles}
- * Returns an object containing start and end angles as radians.
- */
- getStartAndEndRadians: function getStartAndEndRadians(start, end) {
- var startAngle = isNumber(start) ? start : 0, // must be a number
- endAngle = (
- (
- isNumber(end) && // must be a number
- end > startAngle && // must be larger than the start angle
- // difference must be less than 360 degrees
- (end - startAngle) < 360
- ) ?
- end :
- startAngle + 360
- ),
- correction = -90;
- return {
- start: deg2rad * (startAngle + correction),
- end: deg2rad * (endAngle + correction)
- };
- }
- };
- }(Highcharts));
- (function (H) {
- /* *
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var addEvent = H.addEvent,
- CenteredSeriesMixin = H.CenteredSeriesMixin,
- defined = H.defined,
- extend = H.extend,
- getStartAndEndRadians = CenteredSeriesMixin.getStartAndEndRadians,
- LegendSymbolMixin = H.LegendSymbolMixin,
- noop = H.noop,
- pick = H.pick,
- Point = H.Point,
- Series = H.Series,
- seriesType = H.seriesType,
- seriesTypes = H.seriesTypes,
- setAnimation = H.setAnimation;
- /**
- * Pie series type.
- *
- * @private
- * @class
- * @name Highcharts.seriesTypes.pie
- *
- * @augments Highcharts.Series
- */
- seriesType('pie', 'line',
- /**
- * A pie chart is a circular graphic which is divided into slices to
- * illustrate numerical proportion.
- *
- * @sample highcharts/demo/pie-basic/
- * Pie chart
- *
- * @extends plotOptions.line
- * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
- * cropThreshold, dashStyle, findNearestPointBy,
- * getExtremesFromAll, lineWidth, marker, negativeColor,
- * pointInterval, pointIntervalUnit, pointPlacement,
- * pointStart, softThreshold, stacking, step, threshold,
- * turboThreshold, zoneAxis, zones
- * @product highcharts
- * @optionparent plotOptions.pie
- */
- {
- /**
- * @excluding legendItemClick
- * @apioption plotOptions.pie.events
- */
- /**
- * Fires when the checkbox next to the point name in the legend is
- * clicked. One parameter, event, is passed to the function. The state
- * of the checkbox is found by event.checked. The checked item is found
- * by event.item. Return false to prevent the default action which is to
- * toggle the select state of the series.
- *
- * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
- * Alert checkbox status
- *
- * @type {Function}
- * @since 1.2.0
- * @product highcharts
- * @context Highcharts.Point
- * @apioption plotOptions.pie.events.checkboxClick
- */
- /**
- * The center of the pie chart relative to the plot area. Can be
- * percentages or pixel values. The default behaviour (as of 3.0) is to
- * center the pie so that all slices and data labels are within the plot
- * area. As a consequence, the pie may actually jump around in a chart
- * with dynamic values, as the data labels move. In that case, the
- * center should be explicitly set, for example to `["50%", "50%"]`.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-center/
- * Centered at 100, 100
- *
- * @type {Array<number|string|null>}
- * @default [null, null]
- * @product highcharts
- */
- center: [null, null],
- /**
- * @product highcharts
- */
- clip: false,
- /** @ignore */
- colorByPoint: true, // always true for pies
- /**
- * A series specific or series type specific color set to use instead
- * of the global [colors](#colors).
- *
- * @sample {highcharts} highcharts/demo/pie-monochrome/
- * Set default colors for all pies
- *
- * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
- * @since 3.0
- * @product highcharts
- * @apioption plotOptions.pie.colors
- */
- /**
- * @extends plotOptions.series.dataLabels
- * @excluding align, allowOverlap, staggerLines, step
- * @product highcharts
- */
- dataLabels: {
- allowOverlap: true,
- /**
- * The color of the line connecting the data label to the pie slice.
- * The default color is the same as the point's color.
- *
- * In styled mode, the connector stroke is given in the
- * `.highcharts-data-label-connector` class.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
- * Blue connectors
- * @sample {highcharts} highcharts/css/pie-point/
- * Styled connectors
- *
- * @type {Highcharts.ColorString}
- * @since 2.1
- * @product highcharts
- * @apioption plotOptions.pie.dataLabels.connectorColor
- */
- /**
- * The distance from the data label to the connector. Note that data
- * labels also have a default `padding`, so in order for the
- * connector to touch the text, the `padding` must also be 0.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
- * No padding
- *
- * @since 2.1
- * @product highcharts
- * @apioption plotOptions.pie.dataLabels.connectorPadding
- */
- connectorPadding: 5,
- /**
- * The width of the line connecting the data label to the pie slice.
- *
- *
- * In styled mode, the connector stroke width is given in the
- * `.highcharts-data-label-connector` class.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
- * Disable the connector
- * @sample {highcharts} highcharts/css/pie-point/
- * Styled connectors
- *
- * @type {number}
- * @default 1
- * @since 2.1
- * @product highcharts
- * @apioption plotOptions.pie.dataLabels.connectorWidth
- */
- /**
- * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow
- * Long labels truncated with an ellipsis
- * @sample {highcharts} highcharts/plotOptions/pie-datalabels-overflow-wrap
- * Long labels are wrapped
- *
- * @type {Highcharts.CSSObject}
- * @apioption plotOptions.pie.dataLabels.style
- */
- /**
- * The distance of the data label from the pie's edge. Negative
- * numbers put the data label on top of the pie slices. Connectors
- * are only shown for data labels outside the pie.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
- * Data labels on top of the pie
- *
- * @since 2.1
- * @product highcharts
- */
- distance: 30,
- /**
- * Enable or disable the data labels.
- *
- * @since 2.1
- * @product highcharts
- */
- enabled: true,
- /**
- * @type {Highcharts.FormatterCallbackFunction<Highcharts.SeriesDataLabelsFormatterContextObject>}
- * @default function () { return this.point.name; }
- * @apioption plotOptions.pie.dataLabels.formatter
- */
- formatter: function () { // #2945
- return this.point.isNull ? undefined : this.point.name;
- },
- /**
- * Whether to render the connector as a soft arc or a line with
- * sharp break. Works only if `connectorShape` equals to
- * `fixedOffset`.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-true/
- * Soft
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-false/
- * Non soft
- *
- * @type {number}
- * @since 2.1.7
- * @product highcharts
- * @apioption plotOptions.pie.dataLabels.softConnector
- */
- softConnector: true,
- /**
- * Alignment method for data labels. Possible values are:
- * `'toPlotEdges'` (each label touches the nearest vertical edge of
- * the plot area) or `'connectors'` (connectors have the same x
- * position and the widest label of each half (left & right) touches
- * the nearest vertical edge of the plot area).
- *
- * @type {String}
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/ alignTo: connectors
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/ alignTo: plotEdges
- * @since 7.0.0
- * @default undefined
- * @product highcharts
- * @apioption plotOptions.pie.dataLabels.alignTo
- */
- x: 0,
- /**
- * Specifies the method that is used to generate the connector path.
- * Highcharts provides 3 built-in connector shapes: `'fixedOffset'`
- * (default), `'straight'` and `'crookedLine'`. Using
- * `'crookedLine'` has the most sense (in most of the cases) when
- * `'alignTo'` is set.
- *
- * Users can provide their own method by passing a function instead
- * of a String. 3 arguments are passed to the callback:
- *
- * - Object that holds the information about the coordinates of the
- * label (`x` & `y` properties) and how the label is located in
- * relation to the pie (`alignment` property). `alignment` can by
- * one of the following:
- * `'left'` (pie on the left side of the data label),
- * `'right'` (pie on the right side of the data label) or
- * `'center'` (data label overlaps the pie).
- * - Object that holds the information about the position of the
- * connector. Its `touchingSliceAt` porperty tells the position
- * of the place where the connector touches the slice.
- * - Data label options
- *
- * The function has to return an SVG path definition in array form
- * (see the example).
- *
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-string/
- * connectorShape is a String
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-function/
- * connectorShape is a function
- *
- * @type {string|Function}
- * @since 7.0.0
- * @product highcharts
- * @apioption plotOptions.pie.dataLabels.connectorShape
- */
- connectorShape: 'fixedOffset',
- /**
- * Works only if `connectorShape` is `'crookedLine'`. It defines how
- * far from the vertical plot edge the coonnector path should be
- * crooked.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
- * crookDistance set to 90%
- *
- * @type {string}
- * @since 7.0.0
- * @product highcharts
- * @apioption plotOptions.pie.dataLabels.crookDistance
- */
- crookDistance: '70%'
- },
- /**
- * The end angle of the pie in degrees where 0 is top and 90 is right.
- * Defaults to `startAngle` plus 360.
- *
- * @sample {highcharts} highcharts/demo/pie-semi-circle/
- * Semi-circle donut
- *
- * @type {number}
- * @since 1.3.6
- * @product highcharts
- * @apioption plotOptions.pie.endAngle
- */
- /**
- * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
- * this option tells whether the series shall be redrawn as if the
- * hidden point were `null`.
- *
- * The default value changed from `false` to `true` with Highcharts
- * 3.0.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
- * True, the hiddden point is ignored
- *
- * @since 2.3.0
- * @product highcharts
- */
- ignoreHiddenPoint: true,
- /**
- * The size of the inner diameter for the pie. A size greater than 0
- * renders a donut chart. Can be a percentage or pixel value.
- * Percentages are relative to the pie size. Pixel values are given as
- * integers.
- *
- *
- * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
- * area, not the pie size.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
- * 80px inner size
- * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
- * 50% of the plot area
- * @sample {highcharts} highcharts/demo/3d-pie-donut/
- * 3D donut
- *
- * @type {number|string}
- * @default 0
- * @since 2.0
- * @product highcharts
- * @apioption plotOptions.pie.innerSize
- */
- /** @ignore-option */
- legendType: 'point',
- /** @ignore-option */
- marker: null, // point options are specified in the base options
- /**
- * The minimum size for a pie in response to auto margins. The pie will
- * try to shrink to make room for data labels in side the plot area,
- * but only to this size.
- *
- * @type {number}
- * @default 80
- * @since 3.0
- * @product highcharts
- * @apioption plotOptions.pie.minSize
- */
- /**
- * The diameter of the pie relative to the plot area. Can be a
- * percentage or pixel value. Pixel values are given as integers. The
- * default behaviour (as of 3.0) is to scale to the plot area and give
- * room for data labels within the plot area.
- * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
- * default size calculation. As a consequence, the size of the pie may
- * vary when points are updated and data labels more around. In that
- * case it is best to set a fixed value, for example `"75%"`.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-size/
- * Smaller pie
- *
- * @type {number|string|null}
- * @product highcharts
- */
- size: null,
- /**
- * Whether to display this particular series or series type in the
- * legend. Since 2.1, pies are not shown in the legend by default.
- *
- * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
- * One series in the legend, one hidden
- *
- * @product highcharts
- */
- showInLegend: false,
- /**
- * If a point is sliced, moved out from the center, how many pixels
- * should it be moved?.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
- * 20px offset
- *
- * @product highcharts
- */
- slicedOffset: 10,
- /**
- * The start angle of the pie slices in degrees where 0 is top and 90
- * right.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
- * Start from right
- *
- * @type {number}
- * @default 0
- * @since 2.3.4
- * @product highcharts
- * @apioption plotOptions.pie.startAngle
- */
- /**
- * Sticky tracking of mouse events. When true, the `mouseOut` event
- * on a series isn't triggered until the mouse moves over another
- * series, or out of the plot area. When false, the `mouseOut` event on
- * a series is triggered when the mouse leaves the area around the
- * series' graph or markers. This also implies the tooltip. When
- * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
- * will be hidden when moving the mouse between series.
- *
- * @product highcharts
- */
- stickyTracking: false,
- tooltip: {
- followPointer: true
- },
- /**
- * The color of the border surrounding each slice. When `null`, the
- * border takes the same color as the slice fill. This can be used
- * together with a `borderWidth` to fill drawing gaps created by
- * antialiazing artefacts in borderless pies.
- *
- * In styled mode, the border stroke is given in the `.highcharts-point`
- * class.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
- * Black border
- *
- * @type {Highcharts.ColorString}
- * @default #ffffff
- * @product highcharts
- */
- borderColor: '#ffffff',
- /**
- * The width of the border surrounding each slice.
- *
- * When setting the border width to 0, there may be small gaps between
- * the slices due to SVG antialiasing artefacts. To work around this,
- * keep the border width at 0.5 or 1, but set the `borderColor` to
- * `null` instead.
- *
- * In styled mode, the border stroke width is given in the
- * `.highcharts-point` class.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
- * 3px border
- *
- * @product highcharts
- */
- borderWidth: 1,
- states: {
- /**
- * @extends plotOptions.series.states.hover
- * @excluding marker, lineWidth, lineWidthPlus
- * @product highcharts
- */
- hover: {
- /**
- * How much to brighten the point on interaction. Requires the main
- * color to be defined in hex or rgb(a) format.
- *
- * In styled mode, the hover brightness is by default replaced
- * by a fill-opacity given in the `.highcharts-point-hover` class.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
- * Brightened by 0.5
- *
- * @product highcharts
- */
- brightness: 0.1
- }
- }
- }, /** @lends seriesTypes.pie.prototype */ {
- isCartesian: false,
- requireSorting: false,
- directTouch: true,
- noSharedTooltip: true,
- trackerGroups: ['group', 'dataLabelsGroup'],
- axisTypes: [],
- pointAttribs: seriesTypes.column.prototype.pointAttribs,
- /**
- * Animate the pies in
- *
- * @private
- * @function Highcharts.seriesTypes.pie#animate
- *
- * @param {boolean} [init=false]
- */
- animate: function (init) {
- var series = this,
- points = series.points,
- startAngleRad = series.startAngleRad;
- if (!init) {
- points.forEach(function (point) {
- var graphic = point.graphic,
- args = point.shapeArgs;
- if (graphic) {
- // start values
- graphic.attr({
- // animate from inner radius (#779)
- r: point.startR || (series.center[3] / 2),
- start: startAngleRad,
- end: startAngleRad
- });
- // animate
- graphic.animate({
- r: args.r,
- start: args.start,
- end: args.end
- }, series.options.animation);
- }
- });
- // delete this function to allow it only once
- series.animate = null;
- }
- },
- /**
- * Recompute total chart sum and update percentages of points.
- *
- * @private
- * @function Highcharts.seriesTypes.pie#updateTotals
- */
- updateTotals: function () {
- var i,
- total = 0,
- points = this.points,
- len = points.length,
- point,
- ignoreHiddenPoint = this.options.ignoreHiddenPoint;
- // Get the total sum
- for (i = 0; i < len; i++) {
- point = points[i];
- total += (ignoreHiddenPoint && !point.visible) ?
- 0 :
- point.isNull ? 0 : point.y;
- }
- this.total = total;
- // Set each point's properties
- for (i = 0; i < len; i++) {
- point = points[i];
- point.percentage =
- (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
- point.y / total * 100 :
- 0;
- point.total = total;
- }
- },
- /**
- * Extend the generatePoints method by adding total and percentage
- * properties to each point
- *
- * @private
- * @function Highcharts.seriesTypes.pie#generatePoints
- */
- generatePoints: function () {
- Series.prototype.generatePoints.call(this);
- this.updateTotals();
- },
- // Utility for getting the x value from a given y, used for
- // anticollision logic in data labels. Added point for using specific
- // points' label distance.
- getX: function (y, left, point) {
- var center = this.center,
- // Variable pie has individual radius
- radius = this.radii ? this.radii[point.index] : center[2] / 2,
- angle,
- x;
- angle = Math.asin(
- Math.max(
- Math.min(
- (
- (y - center[1]) /
- (radius + point.labelDistance)
- ),
- 1
- ),
- -1
- )
- );
- x = center[0] +
- (left ? -1 : 1) *
- (Math.cos(angle) * (radius + point.labelDistance)) +
- (
- point.labelDistance > 0 ?
- (left ? -1 : 1) * this.options.dataLabels.padding :
- 0
- );
- return x;
- },
- /**
- * Do translation for pie slices
- *
- * @private
- * @function Highcharts.seriesTypes.pie#translate
- *
- * @param {Array<number>} positions
- */
- translate: function (positions) {
- this.generatePoints();
- var series = this,
- cumulative = 0,
- precision = 1000, // issue #172
- options = series.options,
- slicedOffset = options.slicedOffset,
- connectorOffset = slicedOffset + (options.borderWidth || 0),
- finalConnectorOffset,
- start,
- end,
- angle,
- radians = getStartAndEndRadians(
- options.startAngle,
- options.endAngle
- ),
- startAngleRad = series.startAngleRad = radians.start,
- endAngleRad = series.endAngleRad = radians.end,
- circ = endAngleRad - startAngleRad, // 2 * Math.PI,
- points = series.points,
- // the x component of the radius vector for a given point
- radiusX,
- radiusY,
- labelDistance = options.dataLabels.distance,
- ignoreHiddenPoint = options.ignoreHiddenPoint,
- i,
- len = points.length,
- point;
- // Get positions - either an integer or a percentage string must be
- // given. If positions are passed as a parameter, we're in a
- // recursive loop for adjusting space for data labels.
- if (!positions) {
- series.center = positions = series.getCenter();
- }
- // Calculate the geometry for each point
- for (i = 0; i < len; i++) {
- point = points[i];
- // Used for distance calculation for specific point.
- point.labelDistance = pick(
- (
- point.options.dataLabels &&
- point.options.dataLabels.distance
- ),
- labelDistance
- );
- // Saved for later dataLabels distance calculation.
- series.maxLabelDistance = Math.max(
- series.maxLabelDistance || 0,
- point.labelDistance
- );
- // set start and end angle
- start = startAngleRad + (cumulative * circ);
- if (!ignoreHiddenPoint || point.visible) {
- cumulative += point.percentage / 100;
- }
- end = startAngleRad + (cumulative * circ);
- // set the shape
- point.shapeType = 'arc';
- point.shapeArgs = {
- x: positions[0],
- y: positions[1],
- r: positions[2] / 2,
- innerR: positions[3] / 2,
- start: Math.round(start * precision) / precision,
- end: Math.round(end * precision) / precision
- };
- // The angle must stay within -90 and 270 (#2645)
- angle = (end + start) / 2;
- if (angle > 1.5 * Math.PI) {
- angle -= 2 * Math.PI;
- } else if (angle < -Math.PI / 2) {
- angle += 2 * Math.PI;
- }
- // Center for the sliced out slice
- point.slicedTranslation = {
- translateX: Math.round(Math.cos(angle) * slicedOffset),
- translateY: Math.round(Math.sin(angle) * slicedOffset)
- };
- // set the anchor point for tooltips
- radiusX = Math.cos(angle) * positions[2] / 2;
- radiusY = Math.sin(angle) * positions[2] / 2;
- point.tooltipPos = [
- positions[0] + radiusX * 0.7,
- positions[1] + radiusY * 0.7
- ];
- point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
- 1 :
- 0;
- point.angle = angle;
- // Set the anchor point for data labels. Use point.labelDistance
- // instead of labelDistance // #1174
- // finalConnectorOffset - not override connectorOffset value.
- finalConnectorOffset = Math.min(
- connectorOffset,
- point.labelDistance / 5
- ); // #1678
- point.labelPosition = {
- natural: {
- // initial position of the data label - it's utilized for
- // finding the final position for the label
- x: positions[0] + radiusX + Math.cos(angle) *
- point.labelDistance,
- y: positions[1] + radiusY + Math.sin(angle) *
- point.labelDistance
- },
- 'final': {
- // used for generating connector path -
- // initialized later in drawDataLabels function
- // x: undefined,
- // y: undefined
- },
- // left - pie on the left side of the data label
- // right - pie on the right side of the data label
- // center - data label overlaps the pie
- alignment: point.labelDistance < 0 ?
- 'center' : point.half ? 'right' : 'left',
- connectorPosition: {
- breakAt: { // used in connectorShapes.fixedOffset
- x: positions[0] + radiusX + Math.cos(angle) *
- finalConnectorOffset,
- y: positions[1] + radiusY + Math.sin(angle) *
- finalConnectorOffset
- },
- touchingSliceAt: { // middle of the arc
- x: positions[0] + radiusX,
- y: positions[1] + radiusY
- }
- }
- };
- }
- },
- /**
- * @private
- * @deprecated
- * @name Highcharts.seriesTypes.pie#drawGraph
- * @type {null}
- */
- drawGraph: null,
- /**
- * Draw the data points
- *
- * @private
- * @function Highcharts.seriesTypes.pie#drawPoints
- */
- drawPoints: function () {
- var series = this,
- chart = series.chart,
- renderer = chart.renderer,
- groupTranslation,
- graphic,
- pointAttr,
- shapeArgs,
- shadow = series.options.shadow;
- if (shadow && !series.shadowGroup && !chart.styledMode) {
- series.shadowGroup = renderer.g('shadow')
- .add(series.group);
- }
- // draw the slices
- series.points.forEach(function (point) {
- graphic = point.graphic;
- if (!point.isNull) {
- shapeArgs = point.shapeArgs;
- // If the point is sliced, use special translation, else use
- // plot area traslation
- groupTranslation = point.getTranslate();
- if (!chart.styledMode) {
- // Put the shadow behind all points
- var shadowGroup = point.shadowGroup;
- if (shadow && !shadowGroup) {
- shadowGroup = point.shadowGroup = renderer
- .g('shadow')
- .add(series.shadowGroup);
- }
- if (shadowGroup) {
- shadowGroup.attr(groupTranslation);
- }
- pointAttr = series.pointAttribs(
- point,
- point.selected && 'select'
- );
- }
- // Draw the slice
- if (graphic) {
- graphic
- .setRadialReference(series.center);
- if (!chart.styledMode) {
- graphic.attr(pointAttr);
- }
- graphic.animate(extend(shapeArgs, groupTranslation));
- } else {
- point.graphic = graphic = renderer[point.shapeType](
- shapeArgs
- )
- .setRadialReference(series.center)
- .attr(groupTranslation)
- .add(series.group);
- if (!chart.styledMode) {
- graphic
- .attr(pointAttr)
- .attr({ 'stroke-linejoin': 'round' })
- .shadow(shadow, shadowGroup);
- }
- }
- graphic.attr({
- visibility: point.visible ? 'inherit' : 'hidden'
- });
- graphic.addClass(point.getClassName());
- } else if (graphic) {
- point.graphic = graphic.destroy();
- }
- });
- },
- /**
- * @private
- * @deprecated
- * @function Highcharts.seriesTypes.pie#searchPoint
- */
- searchPoint: noop,
- /**
- * Utility for sorting data labels
- *
- * @private
- * @function Highcharts.seriesTypes.pie#sortByAngle
- *
- * @param {Array<Highcharts.Point>} points
- *
- * @param {number} sign
- */
- sortByAngle: function (points, sign) {
- points.sort(function (a, b) {
- return a.angle !== undefined && (b.angle - a.angle) * sign;
- });
- },
- /**
- * Use a simple symbol from LegendSymbolMixin.
- *
- * @private
- * @borrows Highcharts.LegendSymbolMixin.drawRectangle as Highcharts.seriesTypes.pie#drawLegendSymbol
- */
- drawLegendSymbol: LegendSymbolMixin.drawRectangle,
- /**
- * Use the getCenter method from drawLegendSymbol.
- *
- * @private
- * @borrows Highcharts.CenteredSeriesMixin.getCenter as Highcharts.seriesTypes.pie#getCenter
- */
- getCenter: CenteredSeriesMixin.getCenter,
- /**
- * Pies don't have point marker symbols.
- *
- * @deprecated
- * @private
- * @function Highcharts.seriesTypes.pie#getSymbol
- */
- getSymbol: noop
- }, /** @lends seriesTypes.pie.prototype.pointClass.prototype */ {
- /**
- * Initiate the pie slice
- *
- * @private
- * @function Highcharts.seriesTypes.pie#pointClass#init
- *
- * @return {Highcharts.Point}
- */
- init: function () {
- Point.prototype.init.apply(this, arguments);
- var point = this,
- toggleSlice;
- point.name = pick(point.name, 'Slice');
- // add event listener for select
- toggleSlice = function (e) {
- point.slice(e.type === 'select');
- };
- addEvent(point, 'select', toggleSlice);
- addEvent(point, 'unselect', toggleSlice);
- return point;
- },
- /**
- * Negative points are not valid (#1530, #3623, #5322)
- *
- * @private
- * @function Highcharts.seriesTypes.pie#pointClass#isValid
- *
- * @return {boolean}
- */
- isValid: function () {
- return H.isNumber(this.y, true) && this.y >= 0;
- },
- /**
- * Toggle the visibility of the pie slice
- *
- * @private
- * @function Highcharts.seriesTypes.pie#pointClass#setVisible
- *
- * @param {boolean} vis
- * Whether to show the slice or not. If undefined, the visibility is
- * toggled.
- *
- * @param {boolean} [redraw=false]
- */
- setVisible: function (vis, redraw) {
- var point = this,
- series = point.series,
- chart = series.chart,
- ignoreHiddenPoint = series.options.ignoreHiddenPoint;
- redraw = pick(redraw, ignoreHiddenPoint);
- if (vis !== point.visible) {
- // If called without an argument, toggle visibility
- point.visible = point.options.visible = vis =
- vis === undefined ? !point.visible : vis;
- // update userOptions.data
- series.options.data[series.data.indexOf(point)] = point.options;
- // Show and hide associated elements. This is performed
- // regardless of redraw or not, because chart.redraw only
- // handles full series.
- ['graphic', 'dataLabel', 'connector', 'shadowGroup'].forEach(
- function (key) {
- if (point[key]) {
- point[key][vis ? 'show' : 'hide'](true);
- }
- }
- );
- if (point.legendItem) {
- chart.legend.colorizeItem(point, vis);
- }
- // #4170, hide halo after hiding point
- if (!vis && point.state === 'hover') {
- point.setState('');
- }
- // Handle ignore hidden slices
- if (ignoreHiddenPoint) {
- series.isDirty = true;
- }
- if (redraw) {
- chart.redraw();
- }
- }
- },
- /**
- * Set or toggle whether the slice is cut out from the pie
- *
- * @private
- * @function Highcharts.seriesTypes.pie#pointClass#slice
- *
- * @param {boolean} sliced
- * When undefined, the slice state is toggled.
- *
- * @param {boolean} redraw
- * Whether to redraw the chart. True by default.
- */
- slice: function (sliced, redraw, animation) {
- var point = this,
- series = point.series,
- chart = series.chart;
- setAnimation(animation, chart);
- // redraw is true by default
- redraw = pick(redraw, true);
- // if called without an argument, toggle
- point.sliced = point.options.sliced = sliced =
- defined(sliced) ? sliced : !point.sliced;
- // update userOptions.data
- series.options.data[series.data.indexOf(point)] = point.options;
- point.graphic.animate(this.getTranslate());
- if (point.shadowGroup) {
- point.shadowGroup.animate(this.getTranslate());
- }
- },
- /**
- * @private
- * @function Highcharts.seriesTypes.pie#pointClass#getTranslate
- *
- * @return {*}
- */
- getTranslate: function () {
- return this.sliced ? this.slicedTranslation : {
- translateX: 0,
- translateY: 0
- };
- },
- /**
- * @private
- * @function Highcharts.seriesTypes.pie#pointClass#haloPath
- *
- * @param {number} size
- *
- * @return {Highcharts.SVGPathArray}
- */
- haloPath: function (size) {
- var shapeArgs = this.shapeArgs;
- return this.sliced || !this.visible ?
- [] :
- this.series.chart.renderer.symbols.arc(
- shapeArgs.x,
- shapeArgs.y,
- shapeArgs.r + size,
- shapeArgs.r + size, {
- // Substract 1px to ensure the background is not bleeding
- // through between the halo and the slice (#7495).
- innerR: this.shapeArgs.r - 1,
- start: shapeArgs.start,
- end: shapeArgs.end
- }
- );
- },
- connectorShapes: {
- // only one available before v7.0.0
- fixedOffset: function (labelPosition, connectorPosition, options) {
- var breakAt = connectorPosition.breakAt,
- touchingSliceAt = connectorPosition.touchingSliceAt,
- linePath = options.softConnector ? [
- 'C', // soft break
- // 1st control point (of the curve)
- labelPosition.x +
- // 5 gives the connector a little horizontal bend
- (labelPosition.alignment === 'left' ? -5 : 5),
- labelPosition.y, //
- 2 * breakAt.x - touchingSliceAt.x, // 2nd control point
- 2 * breakAt.y - touchingSliceAt.y, //
- breakAt.x, // end of the curve
- breakAt.y //
- ] : [
- 'L', // pointy break
- breakAt.x,
- breakAt.y
- ];
- // assemble the path
- return [
- 'M',
- labelPosition.x,
- labelPosition.y
- ].concat(linePath).concat([
- 'L',
- touchingSliceAt.x,
- touchingSliceAt.y
- ]);
- },
- straight: function (labelPosition, connectorPosition) {
- var touchingSliceAt = connectorPosition.touchingSliceAt;
- // direct line to the slice
- return [
- 'M',
- labelPosition.x,
- labelPosition.y,
- 'L',
- touchingSliceAt.x,
- touchingSliceAt.y
- ];
- },
- crookedLine: function (labelPosition, connectorPosition,
- options) {
- var touchingSliceAt = connectorPosition.touchingSliceAt,
- series = this.series,
- pieCenterX = series.center[0],
- plotWidth = series.chart.plotWidth,
- plotLeft = series.chart.plotLeft,
- alignment = labelPosition.alignment,
- radius = this.shapeArgs.r,
- crookDistance =
- H.relativeLength(options.crookDistance, 1), // % to fraction
- crookX = alignment === 'left' ?
- pieCenterX + radius + (plotWidth + plotLeft -
- pieCenterX - radius) * (1 - crookDistance) :
- plotLeft + (pieCenterX - radius) * crookDistance,
- segmentWithCrook = ['L',
- crookX,
- labelPosition.y];
- // crookedLine formula doesn't make sense if the path overlaps
- // the label - use straight line instead in that case
- if (alignment === 'left' ?
- (crookX > labelPosition.x || crookX < touchingSliceAt.x) :
- (crookX < labelPosition.x || crookX > touchingSliceAt.x)) {
- segmentWithCrook = []; // remove the crook
- }
- // assemble the path
- return [
- 'M',
- labelPosition.x,
- labelPosition.y
- ].concat(segmentWithCrook).concat(['L',
- touchingSliceAt.x,
- touchingSliceAt.y
- ]);
- }
- },
- /**
- * Extendable method for getting the path of the connector between the data
- * label and the pie slice.
- */
- getConnectorPath: function () {
- var labelPosition = this.labelPosition,
- options = this.series.options.dataLabels,
- connectorShape = options.connectorShape,
- predefinedShapes = this.connectorShapes;
- // find out whether to use the predefined shape
- if (predefinedShapes[connectorShape]) {
- connectorShape = predefinedShapes[connectorShape];
- }
- return connectorShape.call(this, {
- // pass simplified label position object for user's convenience
- x: labelPosition.final.x,
- y: labelPosition.final.y,
- alignment: labelPosition.alignment
- }, labelPosition.connectorPosition, options);
- }
- });
- /**
- * A `pie` series. If the [type](#series.pie.type) option is not specified,
- * it is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.pie
- * @excluding dataParser, dataURL, stack, xAxis, yAxis
- * @product highcharts
- * @apioption series.pie
- */
- /**
- * An array of data points for the series. For the `pie` series type,
- * points can be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values
- * will be interpreted as `y` options. Example:
- *
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of data
- * points exceeds the series' [turboThreshold](#series.pie.turboThreshold),
- * this option is not available.
- *
- * ```js
- * data: [{
- * y: 1,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * y: 7,
- * name: "Point1",
- * color: "#FF00FF"
- * }]</pre>
- *
- * @sample {highcharts} highcharts/chart/reflow-true/
- * Numerical values
- * @sample {highcharts} highcharts/series/data-array-of-arrays/
- * Arrays of numeric x and y
- * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
- * Arrays of datetime x and y
- * @sample {highcharts} highcharts/series/data-array-of-name-value/
- * Arrays of point.name and y
- * @sample {highcharts} highcharts/series/data-array-of-objects/
- * Config objects
- *
- * @type {Array<number|Array<string,number>|*>}
- * @extends series.line.data
- * @excluding marker, x
- * @product highcharts
- * @apioption series.pie.data
- */
- /**
- * The sequential index of the data point in the legend.
- *
- * @type {number}
- * @product highcharts
- * @apioption series.pie.data.legendIndex
- */
- /**
- * Fires when the legend item belonging to the pie point (slice) is
- * clicked. The `this` keyword refers to the point itself. One parameter,
- * `event`, is passed to the function, containing common event information. The
- * default action is to toggle the visibility of the point. This can be
- * prevented by calling `event.preventDefault()`.
- *
- * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
- * Confirm toggle visibility
- *
- * @type {Function}
- * @since 1.2.0
- * @product highcharts
- * @apioption plotOptions.pie.point.events.legendItemClick
- */
- /**
- * Whether to display a slice offset from the center.
- *
- * @sample {highcharts} highcharts/point/sliced/
- * One sliced point
- *
- * @type {boolean}
- * @product highcharts
- * @apioption series.pie.data.sliced
- */
- /**
- * @excluding legendItemClick
- * @apioption series.pie.events
- */
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var addEvent = H.addEvent,
- arrayMax = H.arrayMax,
- defined = H.defined,
- extend = H.extend,
- format = H.format,
- merge = H.merge,
- noop = H.noop,
- pick = H.pick,
- relativeLength = H.relativeLength,
- Series = H.Series,
- seriesTypes = H.seriesTypes,
- stableSort = H.stableSort,
- isArray = H.isArray,
- splat = H.splat;
- /**
- * General distribution algorithm for distributing labels of differing size
- * along a confined length in two dimensions. The algorithm takes an array of
- * objects containing a size, a target and a rank. It will place the labels as
- * close as possible to their targets, skipping the lowest ranked labels if
- * necessary.
- *
- * @private
- * @function Highcharts.distribute
- *
- * @param {Array<object>} boxes
- *
- * @param {number} len
- *
- * @param {number} maxDistance
- */
- H.distribute = function (boxes, len, maxDistance) {
- var i,
- overlapping = true,
- origBoxes = boxes, // Original array will be altered with added .pos
- restBoxes = [], // The outranked overshoot
- box,
- target,
- total = 0,
- reducedLen = origBoxes.reducedLen || len;
- function sortByTarget(a, b) {
- return a.target - b.target;
- }
- // If the total size exceeds the len, remove those boxes with the lowest
- // rank
- i = boxes.length;
- while (i--) {
- total += boxes[i].size;
- }
- // Sort by rank, then slice away overshoot
- if (total > reducedLen) {
- stableSort(boxes, function (a, b) {
- return (b.rank || 0) - (a.rank || 0);
- });
- i = 0;
- total = 0;
- while (total <= reducedLen) {
- total += boxes[i].size;
- i++;
- }
- restBoxes = boxes.splice(i - 1, boxes.length);
- }
- // Order by target
- stableSort(boxes, sortByTarget);
- // So far we have been mutating the original array. Now
- // create a copy with target arrays
- boxes = boxes.map(function (box) {
- return {
- size: box.size,
- targets: [box.target],
- align: pick(box.align, 0.5)
- };
- });
- while (overlapping) {
- // Initial positions: target centered in box
- i = boxes.length;
- while (i--) {
- box = boxes[i];
- // Composite box, average of targets
- target = (
- Math.min.apply(0, box.targets) +
- Math.max.apply(0, box.targets)
- ) / 2;
- box.pos = Math.min(
- Math.max(0, target - box.size * box.align),
- len - box.size
- );
- }
- // Detect overlap and join boxes
- i = boxes.length;
- overlapping = false;
- while (i--) {
- // Overlap
- if (i > 0 && boxes[i - 1].pos + boxes[i - 1].size > boxes[i].pos) {
- // Add this size to the previous box
- boxes[i - 1].size += boxes[i].size;
- boxes[i - 1].targets = boxes[i - 1]
- .targets
- .concat(boxes[i].targets);
- boxes[i - 1].align = 0.5;
- // Overlapping right, push left
- if (boxes[i - 1].pos + boxes[i - 1].size > len) {
- boxes[i - 1].pos = len - boxes[i - 1].size;
- }
- boxes.splice(i, 1); // Remove this item
- overlapping = true;
- }
- }
- }
- // Add the rest (hidden boxes)
- origBoxes.push.apply(origBoxes, restBoxes);
- // Now the composite boxes are placed, we need to put the original boxes
- // within them
- i = 0;
- boxes.some(function (box) {
- var posInCompositeBox = 0;
- if (box.targets.some(function () {
- origBoxes[i].pos = box.pos + posInCompositeBox;
- // If the distance between the position and the target exceeds
- // maxDistance, abort the loop and decrease the length in increments
- // of 10% to recursively reduce the number of visible boxes by
- // rank. Once all boxes are within the maxDistance, we're good.
- if (
- Math.abs(origBoxes[i].pos - origBoxes[i].target) >
- maxDistance
- ) {
- // Reset the positions that are already set
- origBoxes.slice(0, i + 1).forEach(function (box) {
- delete box.pos;
- });
- // Try with a smaller length
- origBoxes.reducedLen =
- (origBoxes.reducedLen || len) - (len * 0.1);
- // Recurse
- if (origBoxes.reducedLen > len * 0.1) {
- H.distribute(origBoxes, len, maxDistance);
- }
- // Exceeded maxDistance => abort
- return true;
- }
- posInCompositeBox += origBoxes[i].size;
- i++;
- })) {
- // Exceeded maxDistance => abort
- return true;
- }
- });
- // Add the rest (hidden) boxes and sort by target
- stableSort(origBoxes, sortByTarget);
- };
- /**
- * Draw the data labels
- *
- * @private
- * @function Highcharts.Series#drawDataLabels
- *
- * @fires Highcharts.Series#event:afterDrawDataLabels
- */
- Series.prototype.drawDataLabels = function () {
- var series = this,
- chart = series.chart,
- seriesOptions = series.options,
- seriesDlOptions = seriesOptions.dataLabels,
- points = series.points,
- pointOptions,
- hasRendered = series.hasRendered || 0,
- dataLabelsGroup,
- defer = pick(seriesDlOptions.defer, !!seriesOptions.animation),
- renderer = chart.renderer;
- /*
- * Handle the dataLabels.filter option.
- */
- function applyFilter(point, options) {
- var filter = options.filter,
- op,
- prop,
- val;
- if (filter) {
- op = filter.operator;
- prop = point[filter.property];
- val = filter.value;
- if (
- (op === '>' && prop > val) ||
- (op === '<' && prop < val) ||
- (op === '>=' && prop >= val) ||
- (op === '<=' && prop <= val) ||
- (op === '==' && prop == val) || // eslint-disable-line eqeqeq
- (op === '===' && prop === val)
- ) {
- return true;
- }
- return false;
- }
- return true;
- }
- /*
- * Merge two objects that can be arrays. If one of them is an array, the
- * other is merged into each element. If both are arrays, each element is
- * merged by index. If neither are arrays, we use normal merge.
- */
- function mergeArrays(one, two) {
- var res = [],
- i;
- if (isArray(one) && !isArray(two)) {
- res = one.map(function (el) {
- return merge(el, two);
- });
- } else if (isArray(two) && !isArray(one)) {
- res = two.map(function (el) {
- return merge(one, el);
- });
- } else if (!isArray(one) && !isArray(two)) {
- res = merge(one, two);
- } else {
- i = Math.max(one.length, two.length);
- while (i--) {
- res[i] = merge(one[i], two[i]);
- }
- }
- return res;
- }
- // Merge in plotOptions.dataLabels for series
- seriesDlOptions = mergeArrays(
- mergeArrays(
- chart.options.plotOptions &&
- chart.options.plotOptions.series &&
- chart.options.plotOptions.series.dataLabels,
- chart.options.plotOptions &&
- chart.options.plotOptions[series.type] &&
- chart.options.plotOptions[series.type].dataLabels
- ),
- seriesDlOptions
- );
- H.fireEvent(this, 'drawDataLabels');
- if (
- isArray(seriesDlOptions) ||
- seriesDlOptions.enabled ||
- series._hasPointLabels
- ) {
- // Create a separate group for the data labels to avoid rotation
- dataLabelsGroup = series.plotGroup(
- 'dataLabelsGroup',
- 'data-labels',
- defer && !hasRendered ? 'hidden' : 'visible', // #5133
- seriesDlOptions.zIndex || 6
- );
- if (defer) {
- dataLabelsGroup.attr({ opacity: +hasRendered }); // #3300
- if (!hasRendered) {
- addEvent(series, 'afterAnimate', function () {
- if (series.visible) { // #2597, #3023, #3024
- dataLabelsGroup.show(true);
- }
- dataLabelsGroup[
- seriesOptions.animation ? 'animate' : 'attr'
- ]({ opacity: 1 }, { duration: 200 });
- });
- }
- }
- // Make the labels for each point
- points.forEach(function (point) {
- // Merge in series options for the point.
- // @note dataLabelAttribs (like pointAttribs) would eradicate
- // the need for dlOptions, and simplify the section below.
- pointOptions = splat(
- mergeArrays(
- seriesDlOptions,
- point.dlOptions || // dlOptions is used in treemaps
- (point.options && point.options.dataLabels)
- )
- );
- // Handle each individual data label for this point
- pointOptions.forEach(function (labelOptions, i) {
- // Options for one datalabel
- var labelEnabled = labelOptions.enabled &&
- !point.isNull && // #2282, #4641, #7112
- applyFilter(point, labelOptions),
- labelConfig,
- formatString,
- labelText,
- style,
- rotation,
- attr,
- dataLabel = point.dataLabels ? point.dataLabels[i] :
- point.dataLabel,
- connector = point.connectors ? point.connectors[i] :
- point.connector,
- isNew = !dataLabel;
- if (labelEnabled) {
- // Create individual options structure that can be extended
- // without affecting others
- labelConfig = point.getLabelConfig();
- formatString = (
- labelOptions[point.formatPrefix + 'Format'] ||
- labelOptions.format
- );
- labelText = defined(formatString) ?
- format(formatString, labelConfig, chart.time) :
- (
- labelOptions[point.formatPrefix + 'Formatter'] ||
- labelOptions.formatter
- ).call(labelConfig, labelOptions);
- style = labelOptions.style;
- rotation = labelOptions.rotation;
- if (!chart.styledMode) {
- // Determine the color
- style.color = pick(
- labelOptions.color,
- style.color,
- series.color,
- '#000000'
- );
- // Get automated contrast color
- if (style.color === 'contrast') {
- point.contrastColor = renderer.getContrast(
- point.color || series.color
- );
- style.color = labelOptions.inside ||
- pick(
- labelOptions.distance,
- point.labelDistance
- ) < 0 ||
- !!seriesOptions.stacking ?
- point.contrastColor :
- '#000000';
- }
- if (seriesOptions.cursor) {
- style.cursor = seriesOptions.cursor;
- }
- }
- attr = {
- r: labelOptions.borderRadius || 0,
- rotation: rotation,
- padding: labelOptions.padding,
- zIndex: 1
- };
- if (!chart.styledMode) {
- attr.fill = labelOptions.backgroundColor;
- attr.stroke = labelOptions.borderColor;
- attr['stroke-width'] = labelOptions.borderWidth;
- }
- // Remove unused attributes (#947)
- H.objectEach(attr, function (val, name) {
- if (val === undefined) {
- delete attr[name];
- }
- });
- }
- // If the point is outside the plot area, destroy it. #678, #820
- if (dataLabel && (!labelEnabled || !defined(labelText))) {
- point.dataLabel =
- point.dataLabel && point.dataLabel.destroy();
- if (point.dataLabels) {
- // Remove point.dataLabels if this was the last one
- if (point.dataLabels.length === 1) {
- delete point.dataLabels;
- } else {
- delete point.dataLabels[i];
- }
- }
- if (!i) {
- delete point.dataLabel;
- }
- if (connector) {
- point.connector = point.connector.destroy();
- if (point.connectors) {
- // Remove point.connectors if this was the last one
- if (point.connectors.length === 1) {
- delete point.connectors;
- } else {
- delete point.connectors[i];
- }
- }
- }
- // Individual labels are disabled if the are explicitly disabled
- // in the point options, or if they fall outside the plot area.
- } else if (labelEnabled && defined(labelText)) {
- if (!dataLabel) {
- // Create new label element
- point.dataLabels = point.dataLabels || [];
- dataLabel = point.dataLabels[i] = rotation ?
- // Labels don't rotate, use text element
- renderer.text(labelText, 0, -9999)
- .addClass('highcharts-data-label') :
- // We can use label
- renderer.label(
- labelText,
- 0,
- -9999,
- labelOptions.shape,
- null,
- null,
- labelOptions.useHTML,
- null,
- 'data-label'
- );
- // Store for backwards compatibility
- if (!i) {
- point.dataLabel = dataLabel;
- }
- dataLabel.addClass(
- ' highcharts-data-label-color-' + point.colorIndex +
- ' ' + (labelOptions.className || '') +
- ( // #3398
- labelOptions.useHTML ?
- ' highcharts-tracker' :
- ''
- )
- );
- } else {
- // Use old element and just update text
- attr.text = labelText;
- }
- // Store data label options for later access
- dataLabel.options = labelOptions;
- dataLabel.attr(attr);
- if (!chart.styledMode) {
- // Styles must be applied before add in order to read
- // text bounding box
- dataLabel.css(style).shadow(labelOptions.shadow);
- }
- if (!dataLabel.added) {
- dataLabel.add(dataLabelsGroup);
- }
- // Now the data label is created and placed at 0,0, so we
- // need to align it
- series.alignDataLabel(
- point, dataLabel, labelOptions, null, isNew
- );
- }
- });
- });
- }
- H.fireEvent(this, 'afterDrawDataLabels');
- };
- /**
- * Align each individual data label.
- *
- * @private
- * @function Highcharts.Series#alignDataLabel
- *
- * @param {Highcharts.Point} point
- *
- * @param {Highcharts.SVGElement} dataLabel
- *
- * @param {Highcharts.PlotSeriesDataLabelsOptions} options
- *
- * @param {Highcharts.BBoxObject} alignTo
- *
- * @param {boolean} isNew
- */
- Series.prototype.alignDataLabel = function (
- point,
- dataLabel,
- options,
- alignTo,
- isNew
- ) {
- var chart = this.chart,
- inverted = this.isCartesian && chart.inverted,
- plotX = pick(point.dlBox && point.dlBox.centerX, point.plotX, -9999),
- plotY = pick(point.plotY, -9999),
- bBox = dataLabel.getBBox(),
- baseline,
- rotation = options.rotation,
- normRotation,
- negRotation,
- align = options.align,
- rotCorr, // rotation correction
- // Math.round for rounding errors (#2683), alignTo to allow column
- // labels (#2700)
- visible =
- this.visible &&
- (
- point.series.forceDL ||
- chart.isInsidePlot(plotX, Math.round(plotY), inverted) ||
- (
- alignTo && chart.isInsidePlot(
- plotX,
- inverted ?
- alignTo.x + 1 :
- alignTo.y + alignTo.height - 1,
- inverted
- )
- )
- ),
- alignAttr, // the final position;
- justify = pick(options.overflow, 'justify') === 'justify';
- if (visible) {
- baseline = chart.renderer.fontMetrics(
- chart.styledMode ? undefined : options.style.fontSize,
- dataLabel
- ).b;
- // The alignment box is a singular point
- alignTo = extend({
- x: inverted ? this.yAxis.len - plotY : plotX,
- y: Math.round(inverted ? this.xAxis.len - plotX : plotY),
- width: 0,
- height: 0
- }, alignTo);
- // Add the text size for alignment calculation
- extend(options, {
- width: bBox.width,
- height: bBox.height
- });
- // Allow a hook for changing alignment in the last moment, then do the
- // alignment
- if (rotation) {
- justify = false; // Not supported for rotated text
- rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
- alignAttr = {
- x: alignTo.x + options.x + alignTo.width / 2 + rotCorr.x,
- y: (
- alignTo.y +
- options.y +
- { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
- alignTo.height
- )
- };
- dataLabel[isNew ? 'attr' : 'animate'](alignAttr)
- .attr({ // #3003
- align: align
- });
- // Compensate for the rotated label sticking out on the sides
- normRotation = (rotation + 720) % 360;
- negRotation = normRotation > 180 && normRotation < 360;
- if (align === 'left') {
- alignAttr.y -= negRotation ? bBox.height : 0;
- } else if (align === 'center') {
- alignAttr.x -= bBox.width / 2;
- alignAttr.y -= bBox.height / 2;
- } else if (align === 'right') {
- alignAttr.x -= bBox.width;
- alignAttr.y -= negRotation ? 0 : bBox.height;
- }
- dataLabel.placed = true;
- dataLabel.alignAttr = alignAttr;
- } else {
- dataLabel.align(options, null, alignTo);
- alignAttr = dataLabel.alignAttr;
- }
- // Handle justify or crop
- if (justify && alignTo.height >= 0) { // #8830
- point.isLabelJustified = this.justifyDataLabel(
- dataLabel,
- options,
- alignAttr,
- bBox,
- alignTo,
- isNew
- );
- // Now check that the data label is within the plot area
- } else if (pick(options.crop, true)) {
- visible =
- chart.isInsidePlot(
- alignAttr.x,
- alignAttr.y
- ) &&
- chart.isInsidePlot(
- alignAttr.x + bBox.width,
- alignAttr.y + bBox.height
- );
- }
- // When we're using a shape, make it possible with a connector or an
- // arrow pointing to thie point
- if (options.shape && !rotation) {
- dataLabel[isNew ? 'attr' : 'animate']({
- anchorX: inverted ? chart.plotWidth - point.plotY : point.plotX,
- anchorY: inverted ? chart.plotHeight - point.plotX : point.plotY
- });
- }
- }
- // Show or hide based on the final aligned position
- if (!visible) {
- dataLabel.attr({ y: -9999 });
- dataLabel.placed = false; // don't animate back in
- }
- };
- /**
- * If data labels fall partly outside the plot area, align them back in, in a
- * way that doesn't hide the point.
- *
- * @private
- * @function Highcharts.Series#justifyDataLabel
- *
- * @param {Highcharts.SVGElement} dataLabel
- *
- * @param {Highcharts.PlotSeriesDataLabelsOptions} options
- *
- * @param {*} alignAttr
- *
- * @param {Highcharts.BBoxObject} bBox
- *
- * @param {boolean} isNew
- *
- * @return {boolean}
- */
- Series.prototype.justifyDataLabel = function (
- dataLabel,
- options,
- alignAttr,
- bBox,
- alignTo,
- isNew
- ) {
- var chart = this.chart,
- align = options.align,
- verticalAlign = options.verticalAlign,
- off,
- justified,
- padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
- // Off left
- off = alignAttr.x + padding;
- if (off < 0) {
- if (align === 'right') {
- options.align = 'left';
- } else {
- options.x = -off;
- }
- justified = true;
- }
- // Off right
- off = alignAttr.x + bBox.width - padding;
- if (off > chart.plotWidth) {
- if (align === 'left') {
- options.align = 'right';
- } else {
- options.x = chart.plotWidth - off;
- }
- justified = true;
- }
- // Off top
- off = alignAttr.y + padding;
- if (off < 0) {
- if (verticalAlign === 'bottom') {
- options.verticalAlign = 'top';
- } else {
- options.y = -off;
- }
- justified = true;
- }
- // Off bottom
- off = alignAttr.y + bBox.height - padding;
- if (off > chart.plotHeight) {
- if (verticalAlign === 'top') {
- options.verticalAlign = 'bottom';
- } else {
- options.y = chart.plotHeight - off;
- }
- justified = true;
- }
- if (justified) {
- dataLabel.placed = !isNew;
- dataLabel.align(options, null, alignTo);
- }
- return justified;
- };
- if (seriesTypes.pie) {
- seriesTypes.pie.prototype.dataLabelPositioners = {
- // Based on the value computed in Highcharts' distribute algorithm.
- radialDistributionY: function (point) {
- return point.top + point.distributeBox.pos;
- },
- // get the x - use the natural x position for labels near the
- // top and bottom, to prevent the top and botton slice
- // connectors from touching each other on either side
- // Based on the value computed in Highcharts' distribute algorithm.
- radialDistributionX: function (series, point, y, naturalY) {
- return series.getX(
- y < point.top + 2 || y > point.bottom - 2 ?
- naturalY :
- y,
- point.half,
- point
- );
- },
- // dataLabels.distance determines the x position of the label
- justify: function (point, radius, seriesCenter) {
- return seriesCenter[0] + (point.half ? -1 : 1) *
- (radius + point.labelDistance);
- },
- // Left edges of the left-half labels touch the left edge of the plot
- // area. Right edges of the right-half labels touch the right edge of
- // the plot area.
- alignToPlotEdges: function (
- dataLabel,
- half,
- plotWidth,
- plotLeft
- ) {
- var dataLabelWidth = dataLabel.getBBox().width;
- return half ? dataLabelWidth + plotLeft :
- plotWidth - dataLabelWidth - plotLeft;
- },
- // Connectors of each side end in the same x position. Labels are
- // aligned to them. Left edge of the widest left-half label touches the
- // left edge of the plot area. Right edge of the widest right-half label
- // touches the right edge of the plot area.
- alignToConnectors: function (
- points,
- half,
- plotWidth,
- plotLeft
- ) {
- var maxDataLabelWidth = 0,
- dataLabelWidth;
- // find widest data label
- points.forEach(function (point) {
- dataLabelWidth = point.dataLabel.getBBox().width;
- if (dataLabelWidth > maxDataLabelWidth) {
- maxDataLabelWidth = dataLabelWidth;
- }
- });
- return half ? maxDataLabelWidth + plotLeft :
- plotWidth - maxDataLabelWidth - plotLeft;
- }
- };
- /**
- * Override the base drawDataLabels method by pie specific functionality
- *
- * @private
- * @function Highcharts.seriesTypes.pie#drawDataLabels
- */
- seriesTypes.pie.prototype.drawDataLabels = function () {
- var series = this,
- data = series.data,
- point,
- chart = series.chart,
- options = series.options.dataLabels,
- connectorPadding = options.connectorPadding,
- connectorWidth = pick(options.connectorWidth, 1),
- plotWidth = chart.plotWidth,
- plotHeight = chart.plotHeight,
- plotLeft = chart.plotLeft,
- maxWidth = Math.round(chart.chartWidth / 3),
- connector,
- seriesCenter = series.center,
- radius = seriesCenter[2] / 2,
- centerY = seriesCenter[1],
- dataLabel,
- dataLabelWidth,
- // labelPos,
- labelPosition,
- labelHeight,
- // divide the points into right and left halves for anti collision
- halves = [
- [], // right
- [] // left
- ],
- x,
- y,
- visibility,
- j,
- overflow = [0, 0, 0, 0], // top, right, bottom, left
- dataLabelPositioners = series.dataLabelPositioners;
- // get out if not enabled
- if (!series.visible || (!options.enabled && !series._hasPointLabels)) {
- return;
- }
- // Reset all labels that have been shortened
- data.forEach(function (point) {
- if (point.dataLabel && point.visible && point.dataLabel.shortened) {
- point.dataLabel
- .attr({
- width: 'auto'
- }).css({
- width: 'auto',
- textOverflow: 'clip'
- });
- point.dataLabel.shortened = false;
- }
- });
- // run parent method
- Series.prototype.drawDataLabels.apply(series);
- data.forEach(function (point) {
- if (point.dataLabel) {
- if (point.visible) { // #407, #2510
- // Arrange points for detection collision
- halves[point.half].push(point);
- // Reset positions (#4905)
- point.dataLabel._pos = null;
- // Avoid long labels squeezing the pie size too far down
- if (
- !defined(options.style.width) &&
- !defined(
- point.options.dataLabels &&
- point.options.dataLabels.style &&
- point.options.dataLabels.style.width
- )
- ) {
- if (point.dataLabel.getBBox().width > maxWidth) {
- point.dataLabel.css({
- // Use a fraction of the maxWidth to avoid
- // wrapping close to the end of the string.
- width: maxWidth * 0.7
- });
- point.dataLabel.shortened = true;
- }
- }
- } else {
- point.dataLabel = point.dataLabel.destroy();
- // Workaround to make pies destroy multiple datalabels
- // correctly. This logic needs rewriting to support multiple
- // datalabels fully.
- if (point.dataLabels && point.dataLabels.length === 1) {
- delete point.dataLabels;
- }
- }
- }
- });
- /* Loop over the points in each half, starting from the top and bottom
- * of the pie to detect overlapping labels.
- */
- halves.forEach(function (points, i) {
- var top,
- bottom,
- length = points.length,
- positions = [],
- naturalY,
- sideOverflow,
- size,
- distributionLength;
- if (!length) {
- return;
- }
- // Sort by angle
- series.sortByAngle(points, i - 0.5);
- // Only do anti-collision when we have dataLabels outside the pie
- // and have connectors. (#856)
- if (series.maxLabelDistance > 0) {
- top = Math.max(
- 0,
- centerY - radius - series.maxLabelDistance
- );
- bottom = Math.min(
- centerY + radius + series.maxLabelDistance,
- chart.plotHeight
- );
- points.forEach(function (point) {
- // check if specific points' label is outside the pie
- if (point.labelDistance > 0 && point.dataLabel) {
- // point.top depends on point.labelDistance value
- // Used for calculation of y value in getX method
- point.top = Math.max(
- 0,
- centerY - radius - point.labelDistance
- );
- point.bottom = Math.min(
- centerY + radius + point.labelDistance,
- chart.plotHeight
- );
- size = point.dataLabel.getBBox().height || 21;
- // point.positionsIndex is needed for getting index of
- // parameter related to specific point inside positions
- // array - not every point is in positions array.
- point.distributeBox = {
- target: point.labelPosition.natural.y -
- point.top + size / 2,
- size: size,
- rank: point.y
- };
- positions.push(point.distributeBox);
- }
- });
- distributionLength = bottom + size - top;
- H.distribute(
- positions,
- distributionLength,
- distributionLength / 5
- );
- }
- // Now the used slots are sorted, fill them up sequentially
- for (j = 0; j < length; j++) {
- point = points[j];
- // labelPos = point.labelPos;
- labelPosition = point.labelPosition;
- dataLabel = point.dataLabel;
- visibility = point.visible === false ? 'hidden' : 'inherit';
- naturalY = labelPosition.natural.y;
- y = naturalY;
- if (positions && defined(point.distributeBox)) {
- if (point.distributeBox.pos === undefined) {
- visibility = 'hidden';
- } else {
- labelHeight = point.distributeBox.size;
- // Find label's y position
- y = dataLabelPositioners.radialDistributionY(point);
- }
- }
- // It is needed to delete point.positionIndex for
- // dynamically added points etc.
- delete point.positionIndex;
- // Find label's x position
- // justify is undocumented in the API - preserve support for it
- if (options.justify) {
- x = dataLabelPositioners.justify(point, radius,
- seriesCenter);
- } else {
- switch (options.alignTo) {
- case 'connectors':
- x = dataLabelPositioners.alignToConnectors(points,
- i, plotWidth, plotLeft);
- break;
- case 'plotEdges':
- x = dataLabelPositioners.alignToPlotEdges(dataLabel,
- i, plotWidth, plotLeft);
- break;
- default:
- x = dataLabelPositioners.radialDistributionX(series,
- point, y, naturalY);
- }
- }
- // Record the placement and visibility
- dataLabel._attr = {
- visibility: visibility,
- align: labelPosition.alignment
- };
- dataLabel._pos = {
- x: (
- x +
- options.x +
- ({
- left: connectorPadding,
- right: -connectorPadding
- }[labelPosition.alignment] || 0)
- ),
- // 10 is for the baseline (label vs text)
- y: y + options.y - 10
- };
- // labelPos.x = x;
- // labelPos.y = y;
- labelPosition.final.x = x;
- labelPosition.final.y = y;
- // Detect overflowing data labels
- if (pick(options.crop, true)) {
- dataLabelWidth = dataLabel.getBBox().width;
- sideOverflow = null;
- // Overflow left
- if (
- x - dataLabelWidth < connectorPadding &&
- i === 1 // left half
- ) {
- sideOverflow = Math.round(
- dataLabelWidth - x + connectorPadding
- );
- overflow[3] = Math.max(sideOverflow, overflow[3]);
- // Overflow right
- } else if (
- x + dataLabelWidth > plotWidth - connectorPadding &&
- i === 0 // right half
- ) {
- sideOverflow = Math.round(
- x + dataLabelWidth - plotWidth + connectorPadding
- );
- overflow[1] = Math.max(sideOverflow, overflow[1]);
- }
- // Overflow top
- if (y - labelHeight / 2 < 0) {
- overflow[0] = Math.max(
- Math.round(-y + labelHeight / 2),
- overflow[0]
- );
- // Overflow left
- } else if (y + labelHeight / 2 > plotHeight) {
- overflow[2] = Math.max(
- Math.round(y + labelHeight / 2 - plotHeight),
- overflow[2]
- );
- }
- dataLabel.sideOverflow = sideOverflow;
- }
- } // for each point
- }); // for each half
- // Do not apply the final placement and draw the connectors until we
- // have verified that labels are not spilling over.
- if (
- arrayMax(overflow) === 0 ||
- this.verifyDataLabelOverflow(overflow)
- ) {
- // Place the labels in the final position
- this.placeDataLabels();
- // Draw the connectors
- if (connectorWidth) {
- this.points.forEach(function (point) {
- var isNew;
- connector = point.connector;
- dataLabel = point.dataLabel;
- if (
- dataLabel &&
- dataLabel._pos &&
- point.visible &&
- point.labelDistance > 0
- ) {
- visibility = dataLabel._attr.visibility;
- isNew = !connector;
- if (isNew) {
- point.connector = connector = chart.renderer.path()
- .addClass(
- 'highcharts-data-label-connector ' +
- ' highcharts-color-' + point.colorIndex +
- (
- point.className ?
- ' ' + point.className :
- ''
- )
- )
- .add(series.dataLabelsGroup);
- if (!chart.styledMode) {
- connector.attr({
- 'stroke-width': connectorWidth,
- 'stroke': (
- options.connectorColor ||
- point.color ||
- '#666666'
- )
- });
- }
- }
- connector[isNew ? 'attr' : 'animate']({
- d: point.getConnectorPath()
- });
- connector.attr('visibility', visibility);
- } else if (connector) {
- point.connector = connector.destroy();
- }
- });
- }
- }
- };
- /**
- * Extendable method for getting the path of the connector between the data
- * label and the pie slice.
- *
- * @private
- * @function Highcharts.seriesTypes.pie#connectorPath
- *
- * @param {*} labelPos
- *
- * @return {Highcharts.PathObject}
- */
- // TODO: depracated - remove it
- /*
- seriesTypes.pie.prototype.connectorPath = function (labelPos) {
- var x = labelPos.x,
- y = labelPos.y;
- return pick(this.options.dataLabels.softConnector, true) ? [
- 'M',
- // end of the string at the label
- x + (labelPos[6] === 'left' ? 5 : -5), y,
- 'C',
- x, y, // first break, next to the label
- 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
- labelPos[2], labelPos[3], // second break
- 'L',
- labelPos[4], labelPos[5] // base
- ] : [
- 'M',
- // end of the string at the label
- x + (labelPos[6] === 'left' ? 5 : -5), y,
- 'L',
- labelPos[2], labelPos[3], // second break
- 'L',
- labelPos[4], labelPos[5] // base
- ];
- };
- */
- /**
- * Perform the final placement of the data labels after we have verified
- * that they fall within the plot area.
- *
- * @private
- * @function Highcharts.seriesTypes.pie#placeDataLabels
- */
- seriesTypes.pie.prototype.placeDataLabels = function () {
- this.points.forEach(function (point) {
- var dataLabel = point.dataLabel,
- _pos;
- if (dataLabel && point.visible) {
- _pos = dataLabel._pos;
- if (_pos) {
- // Shorten data labels with ellipsis if they still overflow
- // after the pie has reached minSize (#223).
- if (dataLabel.sideOverflow) {
- dataLabel._attr.width =
- dataLabel.getBBox().width - dataLabel.sideOverflow;
- dataLabel.css({
- width: dataLabel._attr.width + 'px',
- textOverflow: (
- (this.options.dataLabels.style || {})
- .textOverflow ||
- 'ellipsis'
- )
- });
- dataLabel.shortened = true;
- }
- dataLabel.attr(dataLabel._attr);
- dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
- dataLabel.moved = true;
- } else if (dataLabel) {
- dataLabel.attr({ y: -9999 });
- }
- }
- }, this);
- };
- seriesTypes.pie.prototype.alignDataLabel = noop;
- /**
- * Verify whether the data labels are allowed to draw, or we should run more
- * translation and data label positioning to keep them inside the plot area.
- * Returns true when data labels are ready to draw.
- *
- * @private
- * @function Highcharts.seriesTypes.pie#verifyDataLabelOverflow
- *
- * @param {boolean} overflow
- *
- * @return {boolean}
- */
- seriesTypes.pie.prototype.verifyDataLabelOverflow = function (overflow) {
- var center = this.center,
- options = this.options,
- centerOption = options.center,
- minSize = options.minSize || 80,
- newSize = minSize,
- // If a size is set, return true and don't try to shrink the pie
- // to fit the labels.
- ret = options.size !== null;
- if (!ret) {
- // Handle horizontal size and center
- if (centerOption[0] !== null) { // Fixed center
- newSize = Math.max(center[2] -
- Math.max(overflow[1], overflow[3]), minSize);
- } else { // Auto center
- newSize = Math.max(
- // horizontal overflow
- center[2] - overflow[1] - overflow[3],
- minSize
- );
- // horizontal center
- center[0] += (overflow[3] - overflow[1]) / 2;
- }
- // Handle vertical size and center
- if (centerOption[1] !== null) { // Fixed center
- newSize = Math.max(Math.min(newSize, center[2] -
- Math.max(overflow[0], overflow[2])), minSize);
- } else { // Auto center
- newSize = Math.max(
- Math.min(
- newSize,
- // vertical overflow
- center[2] - overflow[0] - overflow[2]
- ),
- minSize
- );
- // vertical center
- center[1] += (overflow[0] - overflow[2]) / 2;
- }
- // If the size must be decreased, we need to run translate and
- // drawDataLabels again
- if (newSize < center[2]) {
- center[2] = newSize;
- center[3] = Math.min( // #3632
- relativeLength(options.innerSize || 0, newSize),
- newSize
- );
- this.translate(center);
- if (this.drawDataLabels) {
- this.drawDataLabels();
- }
- // Else, return true to indicate that the pie and its labels is
- // within the plot area
- } else {
- ret = true;
- }
- }
- return ret;
- };
- }
- if (seriesTypes.column) {
- /**
- * Override the basic data label alignment by adjusting for the position of
- * the column.
- *
- * @private
- * @function Highcharts.seriesTypes.column#alignDataLabel
- *
- * @param {Highcharts.Point} point
- *
- * @param {Highcharts.SVGElement} dataLabel
- *
- * @param {Highcharts.PlotSeriesDataLabelsOptions} options
- *
- * @param {Highcharts.BBoxObject} alignTo
- *
- * @param {boolean} isNew
- */
- seriesTypes.column.prototype.alignDataLabel = function (
- point,
- dataLabel,
- options,
- alignTo,
- isNew
- ) {
- var inverted = this.chart.inverted,
- series = point.series,
- // data label box for alignment
- dlBox = point.dlBox || point.shapeArgs,
- below = pick(
- point.below, // range series
- point.plotY > pick(this.translatedThreshold, series.yAxis.len)
- ),
- // draw it inside the box?
- inside = pick(options.inside, !!this.options.stacking),
- overshoot;
- // Align to the column itself, or the top of it
- if (dlBox) { // Area range uses this method but not alignTo
- alignTo = merge(dlBox);
- if (alignTo.y < 0) {
- alignTo.height += alignTo.y;
- alignTo.y = 0;
- }
- overshoot = alignTo.y + alignTo.height - series.yAxis.len;
- if (overshoot > 0) {
- alignTo.height -= overshoot;
- }
- if (inverted) {
- alignTo = {
- x: series.yAxis.len - alignTo.y - alignTo.height,
- y: series.xAxis.len - alignTo.x - alignTo.width,
- width: alignTo.height,
- height: alignTo.width
- };
- }
- // Compute the alignment box
- if (!inside) {
- if (inverted) {
- alignTo.x += below ? 0 : alignTo.width;
- alignTo.width = 0;
- } else {
- alignTo.y += below ? alignTo.height : 0;
- alignTo.height = 0;
- }
- }
- }
- // When alignment is undefined (typically columns and bars), display the
- // individual point below or above the point depending on the threshold
- options.align = pick(
- options.align,
- !inverted || inside ? 'center' : below ? 'right' : 'left'
- );
- options.verticalAlign = pick(
- options.verticalAlign,
- inverted || inside ? 'middle' : below ? 'top' : 'bottom'
- );
- // Call the parent method
- Series.prototype.alignDataLabel.call(
- this,
- point,
- dataLabel,
- options,
- alignTo,
- isNew
- );
- // If label was justified and we have contrast, set it:
- if (point.isLabelJustified && point.contrastColor) {
- dataLabel.css({
- color: point.contrastColor
- });
- }
- };
- }
- }(Highcharts));
- (function (H) {
- /* *
- * Highcharts module to hide overlapping data labels. This module is included in
- * Highcharts.
- *
- * (c) 2009-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var Chart = H.Chart,
- isArray = H.isArray,
- objectEach = H.objectEach,
- pick = H.pick,
- addEvent = H.addEvent,
- fireEvent = H.fireEvent;
- // Collect potensial overlapping data labels. Stack labels probably don't need
- // to be considered because they are usually accompanied by data labels that lie
- // inside the columns.
- addEvent(Chart, 'render', function collectAndHide() {
- var labels = [];
- // Consider external label collectors
- (this.labelCollectors || []).forEach(function (collector) {
- labels = labels.concat(collector());
- });
- (this.yAxis || []).forEach(function (yAxis) {
- if (
- yAxis.options.stackLabels &&
- !yAxis.options.stackLabels.allowOverlap
- ) {
- objectEach(yAxis.stacks, function (stack) {
- objectEach(stack, function (stackItem) {
- labels.push(stackItem.label);
- });
- });
- }
- });
- (this.series || []).forEach(function (series) {
- var dlOptions = series.options.dataLabels;
- if (
- series.visible &&
- !(dlOptions.enabled === false && !series._hasPointLabels)
- ) { // #3866
- series.points.forEach(function (point) {
- if (point.visible) {
- var dataLabels = (
- isArray(point.dataLabels) ?
- point.dataLabels :
- (point.dataLabel ? [point.dataLabel] : [])
- );
- dataLabels.forEach(function (label) {
- var options = label.options;
- label.labelrank = pick(
- options.labelrank,
- point.labelrank,
- point.shapeArgs && point.shapeArgs.height
- ); // #4118
- if (!options.allowOverlap) {
- labels.push(label);
- }
- });
- }
- });
- }
- });
- this.hideOverlappingLabels(labels);
- });
- /**
- * Hide overlapping labels. Labels are moved and faded in and out on zoom to
- * provide a smooth visual imression.
- *
- * @private
- * @function Highcharts.Chart#hideOverlappingLabels
- *
- * @param {Array<Highcharts.SVGElement>} labels
- */
- Chart.prototype.hideOverlappingLabels = function (labels) {
- var chart = this,
- len = labels.length,
- ren = chart.renderer,
- label,
- i,
- j,
- label1,
- label2,
- isIntersecting,
- box1,
- box2,
- intersectRect = function (x1, y1, w1, h1, x2, y2, w2, h2) {
- return !(
- x2 > x1 + w1 ||
- x2 + w2 < x1 ||
- y2 > y1 + h1 ||
- y2 + h2 < y1
- );
- },
- // Get the box with its position inside the chart, as opposed to getBBox
- // that only reports the position relative to the parent.
- getAbsoluteBox = function (label) {
- var pos,
- parent,
- bBox,
- // Substract the padding if no background or border (#4333)
- padding = label.box ? 0 : (label.padding || 0),
- lineHeightCorrection = 0;
- if (
- label &&
- (!label.alignAttr || label.placed)
- ) {
- pos = label.alignAttr || {
- x: label.attr('x'),
- y: label.attr('y')
- };
- parent = label.parentGroup;
- // Get width and height if pure text nodes (stack labels)
- if (!label.width) {
- bBox = label.getBBox();
- label.width = bBox.width;
- label.height = bBox.height;
- // Labels positions are computed from top left corner, so
- // we need to substract the text height from text nodes too.
- lineHeightCorrection = ren
- .fontMetrics(null, label.element).h;
- }
- return {
- x: pos.x + (parent.translateX || 0) + padding,
- y: pos.y + (parent.translateY || 0) + padding -
- lineHeightCorrection,
- width: label.width - 2 * padding,
- height: label.height - 2 * padding
- };
- }
- };
- for (i = 0; i < len; i++) {
- label = labels[i];
- if (label) {
- // Mark with initial opacity
- label.oldOpacity = label.opacity;
- label.newOpacity = 1;
- label.absoluteBox = getAbsoluteBox(label);
- }
- }
- // Prevent a situation in a gradually rising slope, that each label will
- // hide the previous one because the previous one always has lower rank.
- labels.sort(function (a, b) {
- return (b.labelrank || 0) - (a.labelrank || 0);
- });
- // Detect overlapping labels
- for (i = 0; i < len; i++) {
- label1 = labels[i];
- box1 = label1 && label1.absoluteBox;
- for (j = i + 1; j < len; ++j) {
- label2 = labels[j];
- box2 = label2 && label2.absoluteBox;
- if (
- box1 &&
- box2 &&
- label1 !== label2 && // #6465, polar chart with connectEnds
- label1.newOpacity !== 0 &&
- label2.newOpacity !== 0
- ) {
- isIntersecting = intersectRect(
- box1.x,
- box1.y,
- box1.width,
- box1.height,
- box2.x,
- box2.y,
- box2.width,
- box2.height
- );
- if (isIntersecting) {
- (label1.labelrank < label2.labelrank ? label1 : label2)
- .newOpacity = 0;
- }
- }
- }
- }
- // Hide or show
- labels.forEach(function (label) {
- var complete,
- newOpacity;
- if (label) {
- newOpacity = label.newOpacity;
- if (label.oldOpacity !== newOpacity) {
- // Make sure the label is completely hidden to avoid catching
- // clicks (#4362)
- if (label.alignAttr && label.placed) { // data labels
- if (newOpacity) {
- label.show(true);
- } else {
- complete = function () {
- label.hide();
- };
- }
- // Animate or set the opacity
- label.alignAttr.opacity = newOpacity;
- label[label.isOld ? 'animate' : 'attr'](
- label.alignAttr,
- null,
- complete
- );
- fireEvent(chart, 'afterHideOverlappingLabels');
- } else { // other labels, tick labels
- label.attr({
- opacity: newOpacity
- });
- }
- }
- label.isOld = true;
- }
- });
- };
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- var addEvent = H.addEvent,
- Chart = H.Chart,
- createElement = H.createElement,
- css = H.css,
- defaultOptions = H.defaultOptions,
- defaultPlotOptions = H.defaultPlotOptions,
- extend = H.extend,
- fireEvent = H.fireEvent,
- hasTouch = H.hasTouch,
- isObject = H.isObject,
- Legend = H.Legend,
- merge = H.merge,
- pick = H.pick,
- Point = H.Point,
- Series = H.Series,
- seriesTypes = H.seriesTypes,
- svg = H.svg,
- TrackerMixin;
- /**
- * TrackerMixin for points and graphs.
- *
- * @private
- * @mixin Highcharts.TrackerMixin
- */
- TrackerMixin = H.TrackerMixin = {
- /**
- * Draw the tracker for a point.
- *
- * @private
- * @function Highcharts.TrackerMixin.drawTrackerPoint
- *
- * @fires Highcharts.Series#event:afterDrawTracker
- */
- drawTrackerPoint: function () {
- var series = this,
- chart = series.chart,
- pointer = chart.pointer,
- onMouseOver = function (e) {
- var point = pointer.getPointFromEvent(e);
- // undefined on graph in scatterchart
- if (point !== undefined) {
- pointer.isDirectTouch = true;
- point.onMouseOver(e);
- }
- };
- // Add reference to the point
- series.points.forEach(function (point) {
- if (point.graphic) {
- point.graphic.element.point = point;
- }
- if (point.dataLabel) {
- if (point.dataLabel.div) {
- point.dataLabel.div.point = point;
- } else {
- point.dataLabel.element.point = point;
- }
- }
- });
- // Add the event listeners, we need to do this only once
- if (!series._hasTracking) {
- series.trackerGroups.forEach(function (key) {
- if (series[key]) { // we don't always have dataLabelsGroup
- series[key]
- .addClass('highcharts-tracker')
- .on('mouseover', onMouseOver)
- .on('mouseout', function (e) {
- pointer.onTrackerMouseOut(e);
- });
- if (hasTouch) {
- series[key].on('touchstart', onMouseOver);
- }
- if (!chart.styledMode && series.options.cursor) {
- series[key]
- .css(css)
- .css({ cursor: series.options.cursor });
- }
- }
- });
- series._hasTracking = true;
- }
- fireEvent(this, 'afterDrawTracker');
- },
- /**
- * Draw the tracker object that sits above all data labels and markers to
- * track mouse events on the graph or points. For the line type charts
- * the tracker uses the same graphPath, but with a greater stroke width
- * for better control.
- *
- * @private
- * @function Highcharts.TrackerMixin.drawTrackerGraph
- *
- * @fires Highcharts.Series#event:afterDrawTracker
- */
- drawTrackerGraph: function () {
- var series = this,
- options = series.options,
- trackByArea = options.trackByArea,
- trackerPath = [].concat(
- trackByArea ? series.areaPath : series.graphPath
- ),
- trackerPathLength = trackerPath.length,
- chart = series.chart,
- pointer = chart.pointer,
- renderer = chart.renderer,
- snap = chart.options.tooltip.snap,
- tracker = series.tracker,
- i,
- onMouseOver = function () {
- if (chart.hoverSeries !== series) {
- series.onMouseOver();
- }
- },
- /*
- * Empirical lowest possible opacities for TRACKER_FILL for an
- * element to stay invisible but clickable
- * IE6: 0.002
- * IE7: 0.002
- * IE8: 0.002
- * IE9: 0.00000000001 (unlimited)
- * IE10: 0.0001 (exporting only)
- * FF: 0.00000000001 (unlimited)
- * Chrome: 0.000001
- * Safari: 0.000001
- * Opera: 0.00000000001 (unlimited)
- */
- TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
- // Extend end points. A better way would be to use round linecaps,
- // but those are not clickable in VML.
- if (trackerPathLength && !trackByArea) {
- i = trackerPathLength + 1;
- while (i--) {
- if (trackerPath[i] === 'M') { // extend left side
- trackerPath.splice(
- i + 1, 0,
- trackerPath[i + 1] - snap,
- trackerPath[i + 2],
- 'L'
- );
- }
- if (
- (i && trackerPath[i] === 'M') ||
- i === trackerPathLength
- ) { // extend right side
- trackerPath.splice(
- i,
- 0,
- 'L',
- trackerPath[i - 2] + snap,
- trackerPath[i - 1]
- );
- }
- }
- }
- // draw the tracker
- if (tracker) {
- tracker.attr({ d: trackerPath });
- } else if (series.graph) { // create
- series.tracker = renderer.path(trackerPath)
- .attr({
- visibility: series.visible ? 'visible' : 'hidden',
- zIndex: 2
- })
- .addClass(
- trackByArea ?
- 'highcharts-tracker-area' :
- 'highcharts-tracker-line'
- )
- .add(series.group);
- if (!chart.styledMode) {
- series.tracker.attr({
- 'stroke-linejoin': 'round', // #1225
- stroke: TRACKER_FILL,
- fill: trackByArea ? TRACKER_FILL : 'none',
- 'stroke-width': series.graph.strokeWidth() +
- (trackByArea ? 0 : 2 * snap)
- });
- }
- // The tracker is added to the series group, which is clipped, but
- // is covered by the marker group. So the marker group also needs to
- // capture events.
- [series.tracker, series.markerGroup].forEach(function (tracker) {
- tracker.addClass('highcharts-tracker')
- .on('mouseover', onMouseOver)
- .on('mouseout', function (e) {
- pointer.onTrackerMouseOut(e);
- });
- if (options.cursor && !chart.styledMode) {
- tracker.css({ cursor: options.cursor });
- }
- if (hasTouch) {
- tracker.on('touchstart', onMouseOver);
- }
- });
- }
- fireEvent(this, 'afterDrawTracker');
- }
- };
- /* End TrackerMixin */
- /*
- * Add tracking event listener to the series group, so the point graphics
- * themselves act as trackers
- */
- if (seriesTypes.column) {
- /**
- * @private
- * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.column#drawTracker
- */
- seriesTypes.column.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
- }
- if (seriesTypes.pie) {
- /**
- * @private
- * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.pie#drawTracker
- */
- seriesTypes.pie.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
- }
- if (seriesTypes.scatter) {
- /**
- * @private
- * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.scatter#drawTracker
- */
- seriesTypes.scatter.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
- }
- // Extend Legend for item events.
- extend(Legend.prototype, {
- /**
- * @private
- * @function Highcharts.Legend#setItemEvents
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- *
- * @param {Highcharts.SVGElement} legendItem
- *
- * @param {boolean} [useHTML=false]
- *
- * @fires Highcharts.Point#event:legendItemClick
- * @fires Highcharts.Series#event:legendItemClick
- */
- setItemEvents: function (item, legendItem, useHTML) {
- var legend = this,
- boxWrapper = legend.chart.renderer.boxWrapper,
- activeClass = 'highcharts-legend-' +
- (item instanceof Point ? 'point' : 'series') + '-active',
- styledMode = legend.chart.styledMode;
- // Set the events on the item group, or in case of useHTML, the item
- // itself (#1249)
- (useHTML ? legendItem : item.legendGroup).on('mouseover', function () {
- item.setState('hover');
- // A CSS class to dim or hide other than the hovered series
- boxWrapper.addClass(activeClass);
- if (!styledMode) {
- legendItem.css(legend.options.itemHoverStyle);
- }
- })
- .on('mouseout', function () {
- if (!legend.styledMode) {
- legendItem.css(
- merge(
- item.visible ?
- legend.itemStyle :
- legend.itemHiddenStyle
- )
- );
- }
- // A CSS class to dim or hide other than the hovered series
- boxWrapper.removeClass(activeClass);
- item.setState();
- })
- .on('click', function (event) {
- var strLegendItemClick = 'legendItemClick',
- fnLegendItemClick = function () {
- if (item.setVisible) {
- item.setVisible();
- }
- };
- // A CSS class to dim or hide other than the hovered series.
- // Event handling in iOS causes the activeClass to be added
- // prior to click in some cases (#7418).
- boxWrapper.removeClass(activeClass);
- // Pass over the click/touch event. #4.
- event = {
- browserEvent: event
- };
- // click the name or symbol
- if (item.firePointEvent) { // point
- item.firePointEvent(
- strLegendItemClick,
- event,
- fnLegendItemClick
- );
- } else {
- fireEvent(
- item, strLegendItemClick, event, fnLegendItemClick
- );
- }
- });
- },
- /**
- * @private
- * @function Highcharts.Legend#createCheckboxForItem
- *
- * @param {Highcharts.Point|Highcharts.Series} item
- *
- * @fires Highcharts.Series#event:checkboxClick
- */
- createCheckboxForItem: function (item) {
- var legend = this;
- item.checkbox = createElement('input', {
- type: 'checkbox',
- className: 'highcharts-legend-checkbox',
- checked: item.selected,
- defaultChecked: item.selected // required by IE7
- }, legend.options.itemCheckboxStyle, legend.chart.container);
- addEvent(item.checkbox, 'click', function (event) {
- var target = event.target;
- fireEvent(
- item.series || item,
- 'checkboxClick',
- { // #3712
- checked: target.checked,
- item: item
- },
- function () {
- item.select();
- }
- );
- });
- }
- });
- /*
- * Extend the Chart object with interaction
- */
- extend(Chart.prototype, /** @lends Chart.prototype */ {
- /**
- * Display the zoom button.
- *
- * @private
- * @function Highcharts.Chart#showResetZoom
- *
- * @fires Highcharts.Chart#event:beforeShowResetZoom
- */
- showResetZoom: function () {
- var chart = this,
- lang = defaultOptions.lang,
- btnOptions = chart.options.chart.resetZoomButton,
- theme = btnOptions.theme,
- states = theme.states,
- alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox';
- function zoomOut() {
- chart.zoomOut();
- }
- fireEvent(this, 'beforeShowResetZoom', null, function () {
- chart.resetZoomButton = chart.renderer.button(
- lang.resetZoom,
- null,
- null,
- zoomOut,
- theme,
- states && states.hover
- )
- .attr({
- align: btnOptions.position.align,
- title: lang.resetZoomTitle
- })
- .addClass('highcharts-reset-zoom')
- .add()
- .align(btnOptions.position, false, alignTo);
- });
- },
- /**
- * Zoom the chart out after a user has zoomed in. See also
- * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
- *
- * @function Highcharts.Chart#zoomOut
- *
- * @fires Highcharts.Chart#event:selection
- */
- zoomOut: function () {
- fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
- },
- /**
- * Zoom into a given portion of the chart given by axis coordinates.
- *
- * @private
- * @function Highcharts.Chart#zoom
- *
- * @param {Highcharts.SelectEventObject} event
- */
- zoom: function (event) {
- var chart = this,
- hasZoomed,
- pointer = chart.pointer,
- displayButton = false,
- resetZoomButton;
- // If zoom is called with no arguments, reset the axes
- if (!event || event.resetSelection) {
- chart.axes.forEach(function (axis) {
- hasZoomed = axis.zoom();
- });
- pointer.initiated = false; // #6804
- } else { // else, zoom in on all axes
- event.xAxis.concat(event.yAxis).forEach(function (axisData) {
- var axis = axisData.axis,
- isXAxis = axis.isXAxis;
- // don't zoom more than minRange
- if (pointer[isXAxis ? 'zoomX' : 'zoomY']) {
- hasZoomed = axis.zoom(axisData.min, axisData.max);
- if (axis.displayBtn) {
- displayButton = true;
- }
- }
- });
- }
- // Show or hide the Reset zoom button
- resetZoomButton = chart.resetZoomButton;
- if (displayButton && !resetZoomButton) {
- chart.showResetZoom();
- } else if (!displayButton && isObject(resetZoomButton)) {
- chart.resetZoomButton = resetZoomButton.destroy();
- }
- // Redraw
- if (hasZoomed) {
- chart.redraw(
- pick(
- chart.options.chart.animation,
- event && event.animation,
- chart.pointCount < 100
- )
- );
- }
- },
- /**
- * Pan the chart by dragging the mouse across the pane. This function is
- * called on mouse move, and the distance to pan is computed from chartX
- * compared to the first chartX position in the dragging operation.
- *
- * @private
- * @function Highcharts.Chart#pan
- *
- * @param {Highcharts.PointerEventObject} e
- *
- * @param {string} panning
- */
- pan: function (e, panning) {
- var chart = this,
- hoverPoints = chart.hoverPoints,
- doRedraw;
- fireEvent(this, 'pan', { originalEvent: e }, function () {
- // remove active points for shared tooltip
- if (hoverPoints) {
- hoverPoints.forEach(function (point) {
- point.setState();
- });
- }
- // xy is used in maps
- (panning === 'xy' ? [1, 0] : [1]).forEach(function (isX) {
- var axis = chart[isX ? 'xAxis' : 'yAxis'][0],
- horiz = axis.horiz,
- mousePos = e[horiz ? 'chartX' : 'chartY'],
- mouseDown = horiz ? 'mouseDownX' : 'mouseDownY',
- startPos = chart[mouseDown],
- halfPointRange = (axis.pointRange || 0) / 2,
- pointRangeDirection =
- (axis.reversed && !chart.inverted) ||
- (!axis.reversed && chart.inverted) ?
- -1 :
- 1,
- extremes = axis.getExtremes(),
- panMin = axis.toValue(startPos - mousePos, true) +
- halfPointRange * pointRangeDirection,
- panMax =
- axis.toValue(
- startPos + axis.len - mousePos, true
- ) -
- halfPointRange * pointRangeDirection,
- flipped = panMax < panMin,
- newMin = flipped ? panMax : panMin,
- newMax = flipped ? panMin : panMax,
- paddedMin = Math.min(
- extremes.dataMin,
- halfPointRange ?
- extremes.min :
- axis.toValue(
- axis.toPixels(extremes.min) -
- axis.minPixelPadding
- )
- ),
- paddedMax = Math.max(
- extremes.dataMax,
- halfPointRange ?
- extremes.max :
- axis.toValue(
- axis.toPixels(extremes.max) +
- axis.minPixelPadding
- )
- ),
- spill;
- // If the new range spills over, either to the min or max,
- // adjust the new range.
- spill = paddedMin - newMin;
- if (spill > 0) {
- newMax += spill;
- newMin = paddedMin;
- }
- spill = newMax - paddedMax;
- if (spill > 0) {
- newMax = paddedMax;
- newMin -= spill;
- }
- // Set new extremes if they are actually new
- if (
- axis.series.length &&
- newMin !== extremes.min &&
- newMax !== extremes.max
- ) {
- axis.setExtremes(
- newMin,
- newMax,
- false,
- false,
- { trigger: 'pan' }
- );
- doRedraw = true;
- }
- chart[mouseDown] = mousePos; // set new reference for next run
- });
- if (doRedraw) {
- chart.redraw(false);
- }
- css(chart.container, { cursor: 'move' });
- });
- }
- });
- // Extend the Point object with interaction
- extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
- /**
- * Toggle the selection status of a point.
- *
- * @see Highcharts.Chart#getSelectedPoints
- *
- * @sample highcharts/members/point-select/
- * Select a point from a button
- * @sample highcharts/chart/events-selection-points/
- * Select a range of points through a drag selection
- * @sample maps/series/data-id/
- * Select a point in Highmaps
- *
- * @function Highcharts.Point#select
- *
- * @param {boolean} [selected]
- * When `true`, the point is selected. When `false`, the point is
- * unselected. When `null` or `undefined`, the selection state is
- * toggled.
- *
- * @param {boolean} [accumulate=false]
- * When `true`, the selection is added to other selected points.
- * When `false`, other selected points are deselected. Internally in
- * Highcharts, when
- * [allowPointSelect](http://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
- * is `true`, selected points are accumulated on Control, Shift or
- * Cmd clicking the point.
- *
- * @fires Highcharts.Point#event:select
- * @fires Highcharts.Point#event:unselect
- */
- select: function (selected, accumulate) {
- var point = this,
- series = point.series,
- chart = series.chart;
- selected = pick(selected, !point.selected);
- // fire the event with the default handler
- point.firePointEvent(
- selected ? 'select' : 'unselect',
- { accumulate: accumulate },
- function () {
- /**
- * Whether the point is selected or not.
- *
- * @see Point#select
- * @see Chart#getSelectedPoints
- *
- * @name Highcharts.Point#selected
- * @type {boolean}
- */
- point.selected = point.options.selected = selected;
- series.options.data[series.data.indexOf(point)] =
- point.options;
- point.setState(selected && 'select');
- // unselect all other points unless Ctrl or Cmd + click
- if (!accumulate) {
- chart.getSelectedPoints().forEach(function (loopPoint) {
- if (loopPoint.selected && loopPoint !== point) {
- loopPoint.selected = loopPoint.options.selected =
- false;
- series.options.data[
- series.data.indexOf(loopPoint)
- ] = loopPoint.options;
- loopPoint.setState('');
- loopPoint.firePointEvent('unselect');
- }
- });
- }
- }
- );
- },
- /**
- * Runs on mouse over the point. Called internally from mouse and touch
- * events.
- *
- * @function Highcharts.Point#onMouseOver
- *
- * @param {Highcharts.PointerEventObject} e
- * The event arguments.
- */
- onMouseOver: function (e) {
- var point = this,
- series = point.series,
- chart = series.chart,
- pointer = chart.pointer;
- e = e ?
- pointer.normalize(e) :
- // In cases where onMouseOver is called directly without an event
- pointer.getChartCoordinatesFromPoint(point, chart.inverted);
- pointer.runPointActions(e, point);
- },
- /**
- * Runs on mouse out from the point. Called internally from mouse and touch
- * events.
- *
- * @function Highcharts.Point#onMouseOut
- *
- * @fires Highcharts.Point#event:mouseOut
- */
- onMouseOut: function () {
- var point = this,
- chart = point.series.chart;
- point.firePointEvent('mouseOut');
- (chart.hoverPoints || []).forEach(function (p) {
- p.setState();
- });
- chart.hoverPoints = chart.hoverPoint = null;
- },
- /**
- * Import events from the series' and point's options. Only do it on
- * demand, to save processing time on hovering.
- *
- * @private
- * @function Highcharts.Point#importEvents
- */
- importEvents: function () {
- if (!this.hasImportedEvents) {
- var point = this,
- options = merge(point.series.options.point, point.options),
- events = options.events;
- point.events = events;
- H.objectEach(events, function (event, eventType) {
- addEvent(point, eventType, event);
- });
- this.hasImportedEvents = true;
- }
- },
- /**
- * Set the point's state.
- *
- * @function Highcharts.Point#setState
- *
- * @param {string} [state]
- * The new state, can be one of `''` (an empty string), `hover` or
- * `select`.
- *
- * @param {boolean} [move]
- * State for animation.
- *
- * @fires Highcharts.Point#event:afterSetState
- */
- setState: function (state, move) {
- var point = this,
- plotX = Math.floor(point.plotX), // #4586
- plotY = point.plotY,
- series = point.series,
- stateOptions = series.options.states[state || 'normal'] || {},
- markerOptions = defaultPlotOptions[series.type].marker &&
- series.options.marker,
- normalDisabled = markerOptions && markerOptions.enabled === false,
- markerStateOptions = (
- markerOptions &&
- markerOptions.states &&
- markerOptions.states[state || 'normal']
- ) || {},
- stateDisabled = markerStateOptions.enabled === false,
- stateMarkerGraphic = series.stateMarkerGraphic,
- pointMarker = point.marker || {},
- chart = series.chart,
- halo = series.halo,
- haloOptions,
- markerAttribs,
- hasMarkers = markerOptions && series.markerAttribs,
- newSymbol;
- state = state || ''; // empty string
- if (
- // already has this state
- (state === point.state && !move) ||
- // selected points don't respond to hover
- (point.selected && state !== 'select') ||
- // series' state options is disabled
- (stateOptions.enabled === false) ||
- // general point marker's state options is disabled
- (state && (
- stateDisabled ||
- (normalDisabled && markerStateOptions.enabled === false)
- )) ||
- // individual point marker's state options is disabled
- (
- state &&
- pointMarker.states &&
- pointMarker.states[state] &&
- pointMarker.states[state].enabled === false
- ) // #1610
- ) {
- return;
- }
- if (hasMarkers) {
- markerAttribs = series.markerAttribs(point, state);
- }
- // Apply hover styles to the existing point
- if (point.graphic) {
- if (point.state) {
- point.graphic.removeClass('highcharts-point-' + point.state);
- }
- if (state) {
- point.graphic.addClass('highcharts-point-' + state);
- }
- if (!chart.styledMode) {
- point.graphic.animate(
- series.pointAttribs(point, state),
- pick(
- chart.options.chart.animation,
- stateOptions.animation
- )
- );
- }
- if (markerAttribs) {
- point.graphic.animate(
- markerAttribs,
- pick(
- chart.options.chart.animation, // Turn off globally
- markerStateOptions.animation,
- markerOptions.animation
- )
- );
- }
- // Zooming in from a range with no markers to a range with markers
- if (stateMarkerGraphic) {
- stateMarkerGraphic.hide();
- }
- } else {
- // if a graphic is not applied to each point in the normal state,
- // create a shared graphic for the hover state
- if (state && markerStateOptions) {
- newSymbol = pointMarker.symbol || series.symbol;
- // If the point has another symbol than the previous one, throw
- // away the state marker graphic and force a new one (#1459)
- if (
- stateMarkerGraphic &&
- stateMarkerGraphic.currentSymbol !== newSymbol
- ) {
- stateMarkerGraphic = stateMarkerGraphic.destroy();
- }
- // Add a new state marker graphic
- if (!stateMarkerGraphic) {
- if (newSymbol) {
- series.stateMarkerGraphic = stateMarkerGraphic =
- chart.renderer.symbol(
- newSymbol,
- markerAttribs.x,
- markerAttribs.y,
- markerAttribs.width,
- markerAttribs.height
- )
- .add(series.markerGroup);
- stateMarkerGraphic.currentSymbol = newSymbol;
- }
- // Move the existing graphic
- } else {
- stateMarkerGraphic[move ? 'animate' : 'attr']({ // #1054
- x: markerAttribs.x,
- y: markerAttribs.y
- });
- }
- if (!chart.styledMode && stateMarkerGraphic) {
- stateMarkerGraphic.attr(series.pointAttribs(point, state));
- }
- }
- if (stateMarkerGraphic) {
- stateMarkerGraphic[
- state && chart.isInsidePlot(plotX, plotY, chart.inverted) ?
- 'show' :
- 'hide'
- ](); // #2450
- stateMarkerGraphic.element.point = point; // #4310
- }
- }
- // Show me your halo
- haloOptions = stateOptions.halo;
- if (haloOptions && haloOptions.size) {
- if (!halo) {
- series.halo = halo = chart.renderer.path()
- // #5818, #5903, #6705
- .add((point.graphic || stateMarkerGraphic).parentGroup);
- }
- halo.show()[move ? 'animate' : 'attr']({
- d: point.haloPath(haloOptions.size)
- });
- halo.attr({
- 'class': 'highcharts-halo highcharts-color-' +
- pick(point.colorIndex, series.colorIndex) +
- (point.className ? ' ' + point.className : ''),
- 'zIndex': -1 // #4929, #8276
- });
- halo.point = point; // #6055
- if (!chart.styledMode) {
- halo.attr(extend({
- 'fill': point.color || series.color,
- 'fill-opacity': haloOptions.opacity
- }, haloOptions.attributes));
- }
- } else if (halo && halo.point && halo.point.haloPath) {
- // Animate back to 0 on the current halo point (#6055)
- halo.animate(
- { d: halo.point.haloPath(0) },
- null,
- // Hide after unhovering. The `complete` callback runs in the
- // halo's context (#7681).
- halo.hide
- );
- }
- point.state = state;
- fireEvent(point, 'afterSetState');
- },
- /**
- * Get the path definition for the halo, which is usually a shadow-like
- * circle around the currently hovered point.
- *
- * @function Highcharts.Point#haloPath
- *
- * @param {number} size
- * The radius of the circular halo.
- *
- * @return {Highcharts.SVGPathArray}
- * The path definition.
- */
- haloPath: function (size) {
- var series = this.series,
- chart = series.chart;
- return chart.renderer.symbols.circle(
- Math.floor(this.plotX) - size,
- this.plotY - size,
- size * 2,
- size * 2
- );
- }
- });
- // Extend the Series object with interaction
- extend(Series.prototype, /** @lends Highcharts.Series.prototype */ {
- /**
- * Runs on mouse over the series graphical items.
- *
- * @function Highcharts.Series#onMouseOver
- *
- * @fires Highcharts.Series#event:mouseOver
- */
- onMouseOver: function () {
- var series = this,
- chart = series.chart,
- hoverSeries = chart.hoverSeries;
- // set normal state to previous series
- if (hoverSeries && hoverSeries !== series) {
- hoverSeries.onMouseOut();
- }
- // trigger the event, but to save processing time,
- // only if defined
- if (series.options.events.mouseOver) {
- fireEvent(series, 'mouseOver');
- }
- // hover this
- series.setState('hover');
- chart.hoverSeries = series;
- },
- /**
- * Runs on mouse out of the series graphical items.
- *
- * @function Highcharts.Series#onMouseOut
- *
- * @fires Highcharts.Series#event:mouseOut
- */
- onMouseOut: function () {
- // trigger the event only if listeners exist
- var series = this,
- options = series.options,
- chart = series.chart,
- tooltip = chart.tooltip,
- hoverPoint = chart.hoverPoint;
- // #182, set to null before the mouseOut event fires
- chart.hoverSeries = null;
- // trigger mouse out on the point, which must be in this series
- if (hoverPoint) {
- hoverPoint.onMouseOut();
- }
- // fire the mouse out event
- if (series && options.events.mouseOut) {
- fireEvent(series, 'mouseOut');
- }
- // hide the tooltip
- if (
- tooltip &&
- !series.stickyTracking &&
- (!tooltip.shared || series.noSharedTooltip)
- ) {
- tooltip.hide();
- }
- // set normal state
- series.setState();
- },
- /**
- * Set the state of the series. Called internally on mouse interaction
- * operations, but it can also be called directly to visually
- * highlight a series.
- *
- * @function Highcharts.Series#setState
- *
- * @param {string} [state]
- * Can be either `hover` or undefined to set to normal state.
- */
- setState: function (state) {
- var series = this,
- options = series.options,
- graph = series.graph,
- stateOptions = options.states,
- lineWidth = options.lineWidth,
- attribs,
- i = 0;
- state = state || '';
- if (series.state !== state) {
- // Toggle class names
- [
- series.group,
- series.markerGroup,
- series.dataLabelsGroup
- ].forEach(function (group) {
- if (group) {
- // Old state
- if (series.state) {
- group.removeClass('highcharts-series-' + series.state);
- }
- // New state
- if (state) {
- group.addClass('highcharts-series-' + state);
- }
- }
- });
- series.state = state;
- if (!series.chart.styledMode) {
- if (
- stateOptions[state] &&
- stateOptions[state].enabled === false
- ) {
- return;
- }
- if (state) {
- lineWidth = (
- stateOptions[state].lineWidth ||
- lineWidth + (stateOptions[state].lineWidthPlus || 0)
- ); // #4035
- }
- if (graph && !graph.dashstyle) {
- attribs = {
- 'stroke-width': lineWidth
- };
- // Animate the graph stroke-width. By default a quick
- // animation to hover, slower to un-hover.
- graph.animate(
- attribs,
- pick(
- (
- stateOptions[state || 'normal'] &&
- stateOptions[state || 'normal'].animation
- ),
- series.chart.options.chart.animation
- )
- );
- while (series['zone-graph-' + i]) {
- series['zone-graph-' + i].attr(attribs);
- i = i + 1;
- }
- }
- }
- }
- },
- /**
- * Show or hide the series.
- *
- * @function Highcharts.Series#setVisible
- *
- * @param {boolean} [visible]
- * True to show the series, false to hide. If undefined, the
- * visibility is toggled.
- *
- * @param {boolean} [redraw=true]
- * Whether to redraw the chart after the series is altered. If doing
- * more operations on the chart, it is a good idea to set redraw to
- * false and call {@link Chart#redraw|chart.redraw()} after.
- *
- * @fires Highcharts.Series#event:hide
- * @fires Highcharts.Series#event:show
- */
- setVisible: function (vis, redraw) {
- var series = this,
- chart = series.chart,
- legendItem = series.legendItem,
- showOrHide,
- ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
- oldVisibility = series.visible;
- // if called without an argument, toggle visibility
- series.visible =
- vis =
- series.options.visible =
- series.userOptions.visible =
- vis === undefined ? !oldVisibility : vis; // #5618
- showOrHide = vis ? 'show' : 'hide';
- // show or hide elements
- [
- 'group',
- 'dataLabelsGroup',
- 'markerGroup',
- 'tracker',
- 'tt'
- ].forEach(function (key) {
- if (series[key]) {
- series[key][showOrHide]();
- }
- });
- // hide tooltip (#1361)
- if (
- chart.hoverSeries === series ||
- (chart.hoverPoint && chart.hoverPoint.series) === series
- ) {
- series.onMouseOut();
- }
- if (legendItem) {
- chart.legend.colorizeItem(series, vis);
- }
- // rescale or adapt to resized chart
- series.isDirty = true;
- // in a stack, all other series are affected
- if (series.options.stacking) {
- chart.series.forEach(function (otherSeries) {
- if (otherSeries.options.stacking && otherSeries.visible) {
- otherSeries.isDirty = true;
- }
- });
- }
- // show or hide linked series
- series.linkedSeries.forEach(function (otherSeries) {
- otherSeries.setVisible(vis, false);
- });
- if (ignoreHiddenSeries) {
- chart.isDirtyBox = true;
- }
- fireEvent(series, showOrHide);
- if (redraw !== false) {
- chart.redraw();
- }
- },
- /**
- * Show the series if hidden.
- *
- * @sample highcharts/members/series-hide/
- * Toggle visibility from a button
- *
- * @function Highcharts.Series#show
- *
- * @fires Highcharts.Series#event:show
- */
- show: function () {
- this.setVisible(true);
- },
- /**
- * Hide the series if visible. If the {@link
- * https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries|
- * chart.ignoreHiddenSeries} option is true, the chart is redrawn without
- * this series.
- *
- * @sample highcharts/members/series-hide/
- * Toggle visibility from a button
- *
- * @function Highcharts.Series#hide
- *
- * @fires Highcharts.Series#event:hide
- */
- hide: function () {
- this.setVisible(false);
- },
- /**
- * Select or unselect the series. This means its
- * {@link Highcharts.Series.selected|selected}
- * property is set, the checkbox in the legend is toggled and when selected,
- * the series is returned by the
- * {@link Highcharts.Chart#getSelectedSeries}
- * function.
- *
- * @sample highcharts/members/series-select/
- * Select a series from a button
- *
- * @function Highcharts.Series#select
- *
- * @param {boolean} [selected]
- * True to select the series, false to unselect. If undefined, the
- * selection state is toggled.
- *
- * @fires Highcharts.Series#event:select
- * @fires Highcharts.Series#event:unselect
- */
- select: function (selected) {
- var series = this;
- series.selected =
- selected =
- this.options.selected = (
- selected === undefined ?
- !series.selected :
- selected
- );
- if (series.checkbox) {
- series.checkbox.checked = selected;
- }
- fireEvent(series, selected ? 'select' : 'unselect');
- },
- /**
- * @private
- * @borrows Highcharts.TrackerMixin.drawTrackerGraph as Highcharts.Series#drawTracker
- */
- drawTracker: TrackerMixin.drawTrackerGraph
- });
- }(Highcharts));
- (function (H) {
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * A callback function to gain complete control on when the responsive rule
- * applies.
- *
- * @callback Highcharts.ResponsiveCallbackFunction
- *
- * @return {boolean}
- * Return `true` if it applies.
- */
- var Chart = H.Chart,
- isArray = H.isArray,
- isObject = H.isObject,
- pick = H.pick,
- splat = H.splat;
- /**
- * Allows setting a set of rules to apply for different screen or chart
- * sizes. Each rule specifies additional chart options.
- *
- * @sample {highstock} stock/demo/responsive/
- * Stock chart
- * @sample highcharts/responsive/axis/
- * Axis
- * @sample highcharts/responsive/legend/
- * Legend
- * @sample highcharts/responsive/classname/
- * Class name
- *
- * @since 5.0.0
- * @apioption responsive
- */
- /**
- * A set of rules for responsive settings. The rules are executed from
- * the top down.
- *
- * @sample {highcharts} highcharts/responsive/axis/
- * Axis changes
- * @sample {highstock} highcharts/responsive/axis/
- * Axis changes
- * @sample {highmaps} highcharts/responsive/axis/
- * Axis changes
- *
- * @type {Array<*>}
- * @since 5.0.0
- * @apioption responsive.rules
- */
- /**
- * A full set of chart options to apply as overrides to the general
- * chart options. The chart options are applied when the given rule
- * is active.
- *
- * A special case is configuration objects that take arrays, for example
- * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
- * collections, an `id` option is used to map the new option set to
- * an existing object. If an existing object of the same id is not found,
- * the item of the same indexupdated. So for example, setting `chartOptions`
- * with two series items without an `id`, will cause the existing chart's
- * two series to be updated with respective options.
- *
- * @sample {highstock} stock/demo/responsive/
- * Stock chart
- * @sample highcharts/responsive/axis/
- * Axis
- * @sample highcharts/responsive/legend/
- * Legend
- * @sample highcharts/responsive/classname/
- * Class name
- *
- * @type {Highcharts.Options}
- * @since 5.0.0
- * @apioption responsive.rules.chartOptions
- */
- /**
- * Under which conditions the rule applies.
- *
- * @since 5.0.0
- * @apioption responsive.rules.condition
- */
- /**
- * A callback function to gain complete control on when the responsive
- * rule applies. Return `true` if it applies. This opens for checking
- * against other metrics than the chart size, or example the document
- * size or other elements.
- *
- * @type {Highcharts.ResponsiveCallbackFunction}
- * @since 5.0.0
- * @context Highcharts.Chart
- * @apioption responsive.rules.condition.callback
- */
- /**
- * The responsive rule applies if the chart height is less than this.
- *
- * @type {number}
- * @since 5.0.0
- * @apioption responsive.rules.condition.maxHeight
- */
- /**
- * The responsive rule applies if the chart width is less than this.
- *
- * @sample highcharts/responsive/axis/
- * Max width is 500
- *
- * @type {number}
- * @since 5.0.0
- * @apioption responsive.rules.condition.maxWidth
- */
- /**
- * The responsive rule applies if the chart height is greater than this.
- *
- * @type {number}
- * @default 0
- * @since 5.0.0
- * @apioption responsive.rules.condition.minHeight
- */
- /**
- * The responsive rule applies if the chart width is greater than this.
- *
- * @type {number}
- * @default 0
- * @since 5.0.0
- * @apioption responsive.rules.condition.minWidth
- */
- /**
- * Update the chart based on the current chart/document size and options for
- * responsiveness.
- *
- * @private
- * @function Highcharts.Chart#setResponsive
- *
- * @param {boolean} [redraw=true]
- * @param {Array} [reset=false]
- * Reset by un-applying all rules. Chart.update resets all rules before
- * applying updated options.
- */
- Chart.prototype.setResponsive = function (redraw, reset) {
- var options = this.options.responsive,
- ruleIds = [],
- currentResponsive = this.currentResponsive,
- currentRuleIds,
- undoOptions;
- if (!reset && options && options.rules) {
- options.rules.forEach(function (rule) {
- if (rule._id === undefined) {
- rule._id = H.uniqueKey();
- }
- this.matchResponsiveRule(rule, ruleIds, redraw);
- }, this);
- }
- // Merge matching rules
- var mergedOptions = H.merge.apply(0, ruleIds.map(function (ruleId) {
- return H.find(options.rules, function (rule) {
- return rule._id === ruleId;
- }).chartOptions;
- }));
- mergedOptions.isResponsiveOptions = true;
- // Stringified key for the rules that currently apply.
- ruleIds = ruleIds.toString() || undefined;
- currentRuleIds = currentResponsive && currentResponsive.ruleIds;
- // Changes in what rules apply
- if (ruleIds !== currentRuleIds) {
- // Undo previous rules. Before we apply a new set of rules, we need to
- // roll back completely to base options (#6291).
- if (currentResponsive) {
- this.update(currentResponsive.undoOptions, redraw);
- }
- if (ruleIds) {
- // Get undo-options for matching rules
- undoOptions = this.currentOptions(mergedOptions);
- undoOptions.isResponsiveOptions = true;
- this.currentResponsive = {
- ruleIds: ruleIds,
- mergedOptions: mergedOptions,
- undoOptions: undoOptions
- };
- this.update(mergedOptions, redraw);
- } else {
- this.currentResponsive = undefined;
- }
- }
- };
- /**
- * Handle a single responsiveness rule.
- *
- * @private
- * @function Highcharts.Chart#matchResponsiveRule
- *
- * @param {Highcharts.ResponsiveRulesConditionOptions} rule
- *
- * @param {Array<number>} matches
- */
- Chart.prototype.matchResponsiveRule = function (rule, matches) {
- var condition = rule.condition,
- fn = condition.callback || function () {
- return (
- this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
- this.chartHeight <=
- pick(condition.maxHeight, Number.MAX_VALUE) &&
- this.chartWidth >= pick(condition.minWidth, 0) &&
- this.chartHeight >= pick(condition.minHeight, 0)
- );
- };
- if (fn.call(this)) {
- matches.push(rule._id);
- }
- };
- /**
- * Get the current values for a given set of options. Used before we update
- * the chart with a new responsiveness rule.
- * TODO: Restore axis options (by id?)
- *
- * @private
- * @function Highcharts.Chart#currentOptions
- *
- * @param {Highcharts.Options} options
- *
- * @return {Highcharts.Options}
- */
- Chart.prototype.currentOptions = function (options) {
- var ret = {};
- /**
- * Recurse over a set of options and its current values,
- * and store the current values in the ret object.
- */
- function getCurrent(options, curr, ret, depth) {
- var i;
- H.objectEach(options, function (val, key) {
- if (!depth && ['series', 'xAxis', 'yAxis'].indexOf(key) > -1) {
- val = splat(val);
- ret[key] = [];
- // Iterate over collections like series, xAxis or yAxis and map
- // the items by index.
- for (i = 0; i < val.length; i++) {
- if (curr[key][i]) { // Item exists in current data (#6347)
- ret[key][i] = {};
- getCurrent(
- val[i],
- curr[key][i],
- ret[key][i],
- depth + 1
- );
- }
- }
- } else if (isObject(val)) {
- ret[key] = isArray(val) ? [] : {};
- getCurrent(val, curr[key] || {}, ret[key], depth + 1);
- } else {
- ret[key] = curr[key] || null;
- }
- });
- }
- getCurrent(options, this.options, ret, 0);
- return ret;
- };
- }(Highcharts));
- return (function (Highcharts) {
- return Highcharts;
- }(Highcharts));
- }));
|