gantt.src.js 568 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187
  1. /**
  2. * @license Highcharts Gantt JS v9.0.1 (2021-02-16)
  3. *
  4. * Gantt series
  5. *
  6. * (c) 2016-2021 Lars A. V. Cabrera
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/gantt', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'Series/XRange/XRangePoint.js', [_modules['Core/Series/Point.js'], _modules['Core/Series/SeriesRegistry.js']], function (Point, SeriesRegistry) {
  32. /* *
  33. *
  34. * X-range series module
  35. *
  36. * (c) 2010-2021 Torstein Honsi, Lars A. V. Cabrera
  37. *
  38. * License: www.highcharts.com/license
  39. *
  40. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41. *
  42. * */
  43. var __extends = (this && this.__extends) || (function () {
  44. var extendStatics = function (d,
  45. b) {
  46. extendStatics = Object.setPrototypeOf ||
  47. ({ __proto__: [] } instanceof Array && function (d,
  48. b) { d.__proto__ = b; }) ||
  49. function (d,
  50. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  51. return extendStatics(d, b);
  52. };
  53. return function (d, b) {
  54. extendStatics(d, b);
  55. function __() { this.constructor = d; }
  56. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  57. };
  58. })();
  59. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  60. /* *
  61. *
  62. * Declarations
  63. *
  64. * */
  65. var XRangePoint = /** @class */ (function (_super) {
  66. __extends(XRangePoint, _super);
  67. function XRangePoint() {
  68. var _this = _super !== null && _super.apply(this,
  69. arguments) || this;
  70. /* *
  71. *
  72. * Properties
  73. *
  74. * */
  75. _this.options = void 0;
  76. _this.series = void 0;
  77. _this.tooltipDateKeys = ['x', 'x2'];
  78. return _this;
  79. /* eslint-enable valid-jsdoc */
  80. }
  81. /* *
  82. *
  83. * Static properties
  84. *
  85. * */
  86. /**
  87. * Return color of a point based on its category.
  88. *
  89. * @private
  90. * @function getColorByCategory
  91. *
  92. * @param {object} series
  93. * The series which the point belongs to.
  94. *
  95. * @param {object} point
  96. * The point to calculate its color for.
  97. *
  98. * @return {object}
  99. * Returns an object containing the properties color and colorIndex.
  100. */
  101. XRangePoint.getColorByCategory = function (series, point) {
  102. var colors = series.options.colors || series.chart.options.colors,
  103. colorCount = colors ?
  104. colors.length :
  105. series.chart.options.chart.colorCount,
  106. colorIndex = point.y % colorCount,
  107. color = colors && colors[colorIndex];
  108. return {
  109. colorIndex: colorIndex,
  110. color: color
  111. };
  112. };
  113. /* *
  114. *
  115. * Functions
  116. *
  117. * */
  118. /**
  119. * The ending X value of the range point.
  120. * @name Highcharts.Point#x2
  121. * @type {number|undefined}
  122. * @requires modules/xrange
  123. */
  124. /**
  125. * Extend applyOptions so that `colorByPoint` for x-range means that one
  126. * color is applied per Y axis category.
  127. *
  128. * @private
  129. * @function Highcharts.Point#applyOptions
  130. *
  131. * @return {Highcharts.Series}
  132. */
  133. /* eslint-disable valid-jsdoc */
  134. /**
  135. * @private
  136. */
  137. XRangePoint.prototype.resolveColor = function () {
  138. var series = this.series,
  139. colorByPoint;
  140. if (series.options.colorByPoint && !this.options.color) {
  141. colorByPoint = XRangePoint.getColorByCategory(series, this);
  142. if (!series.chart.styledMode) {
  143. this.color = colorByPoint.color;
  144. }
  145. if (!this.options.colorIndex) {
  146. this.colorIndex = colorByPoint.colorIndex;
  147. }
  148. }
  149. else if (!this.color) {
  150. this.color = series.color;
  151. }
  152. };
  153. /**
  154. * Extend init to have y default to 0.
  155. *
  156. * @private
  157. * @function Highcharts.Point#init
  158. *
  159. * @return {Highcharts.Point}
  160. */
  161. XRangePoint.prototype.init = function () {
  162. Point.prototype.init.apply(this, arguments);
  163. if (!this.y) {
  164. this.y = 0;
  165. }
  166. return this;
  167. };
  168. /**
  169. * @private
  170. * @function Highcharts.Point#setState
  171. */
  172. XRangePoint.prototype.setState = function () {
  173. Point.prototype.setState.apply(this, arguments);
  174. this.series.drawPoint(this, this.series.getAnimationVerb());
  175. };
  176. /**
  177. * @private
  178. * @function Highcharts.Point#getLabelConfig
  179. *
  180. * @return {Highcharts.PointLabelObject}
  181. */
  182. // Add x2 and yCategory to the available properties for tooltip formats
  183. XRangePoint.prototype.getLabelConfig = function () {
  184. var point = this,
  185. cfg = Point.prototype.getLabelConfig.call(point),
  186. yCats = point.series.yAxis.categories;
  187. cfg.x2 = point.x2;
  188. cfg.yCategory = point.yCategory = yCats && yCats[point.y];
  189. return cfg;
  190. };
  191. /**
  192. * @private
  193. * @function Highcharts.Point#isValid
  194. *
  195. * @return {boolean}
  196. */
  197. XRangePoint.prototype.isValid = function () {
  198. return typeof this.x === 'number' &&
  199. typeof this.x2 === 'number';
  200. };
  201. return XRangePoint;
  202. }(ColumnSeries.prototype.pointClass));
  203. /* *
  204. *
  205. * Default Export
  206. *
  207. * */
  208. return XRangePoint;
  209. });
  210. _registerModule(_modules, 'Series/XRange/XRangeComposition.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Utilities.js']], function (Axis, U) {
  211. /* *
  212. *
  213. * X-range series module
  214. *
  215. * (c) 2010-2021 Torstein Honsi, Lars A. V. Cabrera
  216. *
  217. * License: www.highcharts.com/license
  218. *
  219. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  220. *
  221. * */
  222. /* *
  223. *
  224. * Imports
  225. *
  226. * */
  227. /* *
  228. *
  229. * Imports
  230. *
  231. * */
  232. var addEvent = U.addEvent,
  233. pick = U.pick;
  234. /**
  235. * Max x2 should be considered in xAxis extremes
  236. */
  237. addEvent(Axis, 'afterGetSeriesExtremes', function () {
  238. var axis = this, // eslint-disable-line no-invalid-this
  239. axisSeries = axis.series,
  240. dataMax,
  241. modMax;
  242. if (axis.isXAxis) {
  243. dataMax = pick(axis.dataMax, -Number.MAX_VALUE);
  244. axisSeries.forEach(function (series) {
  245. if (series.x2Data) {
  246. series.x2Data
  247. .forEach(function (val) {
  248. if (val > dataMax) {
  249. dataMax = val;
  250. modMax = true;
  251. }
  252. });
  253. }
  254. });
  255. if (modMax) {
  256. axis.dataMax = dataMax;
  257. }
  258. }
  259. });
  260. });
  261. _registerModule(_modules, 'Series/XRange/XRangeSeries.js', [_modules['Core/Globals.js'], _modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js'], _modules['Series/XRange/XRangePoint.js']], function (H, Color, SeriesRegistry, U, XRangePoint) {
  262. /* *
  263. *
  264. * X-range series module
  265. *
  266. * (c) 2010-2021 Torstein Honsi, Lars A. V. Cabrera
  267. *
  268. * License: www.highcharts.com/license
  269. *
  270. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  271. *
  272. * */
  273. var __extends = (this && this.__extends) || (function () {
  274. var extendStatics = function (d,
  275. b) {
  276. extendStatics = Object.setPrototypeOf ||
  277. ({ __proto__: [] } instanceof Array && function (d,
  278. b) { d.__proto__ = b; }) ||
  279. function (d,
  280. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  281. return extendStatics(d, b);
  282. };
  283. return function (d, b) {
  284. extendStatics(d, b);
  285. function __() { this.constructor = d; }
  286. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  287. };
  288. })();
  289. var color = Color.parse;
  290. var Series = SeriesRegistry.series,
  291. ColumnSeries = SeriesRegistry.seriesTypes.column;
  292. var columnProto = ColumnSeries.prototype;
  293. var clamp = U.clamp,
  294. correctFloat = U.correctFloat,
  295. defined = U.defined,
  296. extend = U.extend,
  297. find = U.find,
  298. isNumber = U.isNumber,
  299. isObject = U.isObject,
  300. merge = U.merge,
  301. pick = U.pick;
  302. /* *
  303. * @interface Highcharts.PointOptionsObject in parts/Point.ts
  304. */ /**
  305. * The ending X value of the range point.
  306. * @name Highcharts.PointOptionsObject#x2
  307. * @type {number|undefined}
  308. * @requires modules/xrange
  309. */
  310. /**
  311. * @private
  312. * @class
  313. * @name Highcharts.seriesTypes.xrange
  314. *
  315. * @augments Highcharts.Series
  316. */
  317. var XRangeSeries = /** @class */ (function (_super) {
  318. __extends(XRangeSeries, _super);
  319. function XRangeSeries() {
  320. var _this = _super !== null && _super.apply(this,
  321. arguments) || this;
  322. /* *
  323. *
  324. * Properties
  325. *
  326. * */
  327. _this.data = void 0;
  328. _this.options = void 0;
  329. _this.points = void 0;
  330. return _this;
  331. /*
  332. // Override to remove stroke from points. For partial fill.
  333. pointAttribs: function () {
  334. var series = this,
  335. retVal = columnType.prototype.pointAttribs
  336. .apply(series,
  337. arguments);
  338. //retVal['stroke-width'] = 0;
  339. return retVal;
  340. }
  341. //*/
  342. /* eslint-enable valid-jsdoc */
  343. }
  344. /* *
  345. *
  346. * Functions
  347. *
  348. * */
  349. /* eslint-disable valid-jsdoc */
  350. /**
  351. * @private
  352. * @function Highcarts.seriesTypes.xrange#init
  353. * @return {void}
  354. */
  355. XRangeSeries.prototype.init = function () {
  356. ColumnSeries.prototype.init.apply(this, arguments);
  357. this.options.stacking = void 0; // #13161
  358. };
  359. /**
  360. * Borrow the column series metrics, but with swapped axes. This gives
  361. * free access to features like groupPadding, grouping, pointWidth etc.
  362. *
  363. * @private
  364. * @function Highcharts.Series#getColumnMetrics
  365. *
  366. * @return {Highcharts.ColumnMetricsObject}
  367. */
  368. XRangeSeries.prototype.getColumnMetrics = function () {
  369. var metrics,
  370. chart = this.chart;
  371. /**
  372. * @private
  373. */
  374. function swapAxes() {
  375. chart.series.forEach(function (s) {
  376. var xAxis = s.xAxis;
  377. s.xAxis = s.yAxis;
  378. s.yAxis = xAxis;
  379. });
  380. }
  381. swapAxes();
  382. metrics = columnProto.getColumnMetrics.call(this);
  383. swapAxes();
  384. return metrics;
  385. };
  386. /**
  387. * Override cropData to show a point where x or x2 is outside visible
  388. * range, but one of them is inside.
  389. *
  390. * @private
  391. * @function Highcharts.Series#cropData
  392. *
  393. * @param {Array<number>} xData
  394. *
  395. * @param {Array<number>} yData
  396. *
  397. * @param {number} min
  398. *
  399. * @param {number} max
  400. *
  401. * @param {number} [cropShoulder]
  402. *
  403. * @return {*}
  404. */
  405. XRangeSeries.prototype.cropData = function (xData, yData, min, max) {
  406. // Replace xData with x2Data to find the appropriate cropStart
  407. var cropData = Series.prototype.cropData,
  408. crop = cropData.call(this,
  409. this.x2Data,
  410. yData,
  411. min,
  412. max);
  413. // Re-insert the cropped xData
  414. crop.xData = xData.slice(crop.start, crop.end);
  415. return crop;
  416. };
  417. /**
  418. * Finds the index of an existing point that matches the given point
  419. * options.
  420. *
  421. * @private
  422. * @function Highcharts.Series#findPointIndex
  423. * @param {object} options The options of the point.
  424. * @returns {number|undefined} Returns index of a matching point,
  425. * returns undefined if no match is found.
  426. */
  427. XRangeSeries.prototype.findPointIndex = function (options) {
  428. var _a = this,
  429. cropped = _a.cropped,
  430. cropStart = _a.cropStart,
  431. points = _a.points;
  432. var id = options.id;
  433. var pointIndex;
  434. if (id) {
  435. var point = find(points,
  436. function (point) {
  437. return point.id === id;
  438. });
  439. pointIndex = point ? point.index : void 0;
  440. }
  441. if (typeof pointIndex === 'undefined') {
  442. var point = find(points,
  443. function (point) {
  444. return (point.x === options.x &&
  445. point.x2 === options.x2 &&
  446. !point.touched);
  447. });
  448. pointIndex = point ? point.index : void 0;
  449. }
  450. // Reduce pointIndex if data is cropped
  451. if (cropped &&
  452. isNumber(pointIndex) &&
  453. isNumber(cropStart) &&
  454. pointIndex >= cropStart) {
  455. pointIndex -= cropStart;
  456. }
  457. return pointIndex;
  458. };
  459. /**
  460. * @private
  461. * @function Highcharts.Series#translatePoint
  462. *
  463. * @param {Highcharts.Point} point
  464. */
  465. XRangeSeries.prototype.translatePoint = function (point) {
  466. var _a,
  467. _b;
  468. var series = this,
  469. xAxis = series.xAxis,
  470. yAxis = series.yAxis,
  471. metrics = series.columnMetrics,
  472. options = series.options,
  473. minPointLength = options.minPointLength || 0,
  474. oldColWidth = ((_a = point.shapeArgs) === null || _a === void 0 ? void 0 : _a.width) / 2,
  475. seriesXOffset = series.pointXOffset = metrics.offset,
  476. plotX = point.plotX,
  477. posX = pick(point.x2,
  478. point.x + (point.len || 0)),
  479. plotX2 = xAxis.translate(posX, 0, 0, 0, 1),
  480. length = Math.abs(plotX2 - plotX),
  481. widthDifference,
  482. shapeArgs,
  483. partialFill,
  484. inverted = this.chart.inverted,
  485. borderWidth = pick(options.borderWidth, 1),
  486. crisper = borderWidth % 2 / 2,
  487. yOffset = metrics.offset,
  488. pointHeight = Math.round(metrics.width),
  489. dlLeft,
  490. dlRight,
  491. dlWidth,
  492. clipRectWidth,
  493. tooltipYOffset;
  494. if (minPointLength) {
  495. widthDifference = minPointLength - length;
  496. if (widthDifference < 0) {
  497. widthDifference = 0;
  498. }
  499. plotX -= widthDifference / 2;
  500. plotX2 += widthDifference / 2;
  501. }
  502. plotX = Math.max(plotX, -10);
  503. plotX2 = clamp(plotX2, -10, xAxis.len + 10);
  504. // Handle individual pointWidth
  505. if (defined(point.options.pointWidth)) {
  506. yOffset -= ((Math.ceil(point.options.pointWidth) - pointHeight) / 2);
  507. pointHeight = Math.ceil(point.options.pointWidth);
  508. }
  509. // Apply pointPlacement to the Y axis
  510. if (options.pointPlacement &&
  511. isNumber(point.plotY) &&
  512. yAxis.categories) {
  513. point.plotY = yAxis.translate(point.y, 0, 1, 0, 1, options.pointPlacement);
  514. }
  515. point.shapeArgs = {
  516. x: Math.floor(Math.min(plotX, plotX2)) + crisper,
  517. y: Math.floor(point.plotY + yOffset) + crisper,
  518. width: Math.round(Math.abs(plotX2 - plotX)),
  519. height: pointHeight,
  520. r: series.options.borderRadius
  521. };
  522. // Move tooltip to default position
  523. if (!inverted) {
  524. point.tooltipPos[0] -= oldColWidth +
  525. seriesXOffset -
  526. ((_b = point.shapeArgs) === null || _b === void 0 ? void 0 : _b.width) / 2;
  527. }
  528. else {
  529. point.tooltipPos[1] += seriesXOffset +
  530. oldColWidth;
  531. }
  532. // Align data labels inside the shape and inside the plot area
  533. dlLeft = point.shapeArgs.x;
  534. dlRight = dlLeft + point.shapeArgs.width;
  535. if (dlLeft < 0 || dlRight > xAxis.len) {
  536. dlLeft = clamp(dlLeft, 0, xAxis.len);
  537. dlRight = clamp(dlRight, 0, xAxis.len);
  538. dlWidth = dlRight - dlLeft;
  539. point.dlBox = merge(point.shapeArgs, {
  540. x: dlLeft,
  541. width: dlRight - dlLeft,
  542. centerX: dlWidth ? dlWidth / 2 : null
  543. });
  544. }
  545. else {
  546. point.dlBox = null;
  547. }
  548. // Tooltip position
  549. var tooltipPos = point.tooltipPos;
  550. var xIndex = !inverted ? 0 : 1;
  551. var yIndex = !inverted ? 1 : 0;
  552. tooltipYOffset = series.columnMetrics ?
  553. series.columnMetrics.offset : -metrics.width / 2;
  554. // Centering tooltip position (#14147)
  555. if (!inverted) {
  556. tooltipPos[xIndex] += (xAxis.reversed ? -1 : 0) * point.shapeArgs.width;
  557. }
  558. else {
  559. tooltipPos[xIndex] += point.shapeArgs.width / 2;
  560. }
  561. tooltipPos[yIndex] = clamp(tooltipPos[yIndex] + ((inverted ? -1 : 1) * tooltipYOffset), 0, yAxis.len - 1);
  562. // Add a partShapeArgs to the point, based on the shapeArgs property
  563. partialFill = point.partialFill;
  564. if (partialFill) {
  565. // Get the partial fill amount
  566. if (isObject(partialFill)) {
  567. partialFill = partialFill.amount;
  568. }
  569. // If it was not a number, assume 0
  570. if (!isNumber(partialFill)) {
  571. partialFill = 0;
  572. }
  573. shapeArgs = point.shapeArgs;
  574. point.partShapeArgs = {
  575. x: shapeArgs.x,
  576. y: shapeArgs.y,
  577. width: shapeArgs.width,
  578. height: shapeArgs.height,
  579. r: series.options.borderRadius
  580. };
  581. clipRectWidth = Math.max(Math.round(length * partialFill + point.plotX -
  582. plotX), 0);
  583. point.clipRectArgs = {
  584. x: xAxis.reversed ? // #10717
  585. shapeArgs.x + length - clipRectWidth :
  586. shapeArgs.x,
  587. y: shapeArgs.y,
  588. width: clipRectWidth,
  589. height: shapeArgs.height
  590. };
  591. }
  592. };
  593. /**
  594. * @private
  595. * @function Highcharts.Series#translate
  596. */
  597. XRangeSeries.prototype.translate = function () {
  598. columnProto.translate.apply(this, arguments);
  599. this.points.forEach(function (point) {
  600. this.translatePoint(point);
  601. }, this);
  602. };
  603. /**
  604. * Draws a single point in the series. Needed for partial fill.
  605. *
  606. * This override turns point.graphic into a group containing the
  607. * original graphic and an overlay displaying the partial fill.
  608. *
  609. * @private
  610. * @function Highcharts.Series#drawPoint
  611. *
  612. * @param {Highcharts.Point} point
  613. * An instance of Point in the series.
  614. *
  615. * @param {"animate"|"attr"} verb
  616. * 'animate' (animates changes) or 'attr' (sets options)
  617. */
  618. XRangeSeries.prototype.drawPoint = function (point, verb) {
  619. var series = this,
  620. seriesOpts = series.options,
  621. renderer = series.chart.renderer,
  622. graphic = point.graphic,
  623. type = point.shapeType,
  624. shapeArgs = point.shapeArgs,
  625. partShapeArgs = point.partShapeArgs,
  626. clipRectArgs = point.clipRectArgs,
  627. pfOptions = point.partialFill,
  628. cutOff = seriesOpts.stacking && !seriesOpts.borderRadius,
  629. pointState = point.state,
  630. stateOpts = (seriesOpts.states[pointState || 'normal'] ||
  631. {}),
  632. pointStateVerb = typeof pointState === 'undefined' ?
  633. 'attr' : verb,
  634. pointAttr = series.pointAttribs(point,
  635. pointState),
  636. animation = pick(series.chart.options.chart.animation,
  637. stateOpts.animation),
  638. fill;
  639. if (!point.isNull && point.visible !== false) {
  640. // Original graphic
  641. if (graphic) { // update
  642. graphic.rect[verb](shapeArgs);
  643. }
  644. else {
  645. point.graphic = graphic = renderer.g('point')
  646. .addClass(point.getClassName())
  647. .add(point.group || series.group);
  648. graphic.rect = renderer[type](merge(shapeArgs))
  649. .addClass(point.getClassName())
  650. .addClass('highcharts-partfill-original')
  651. .add(graphic);
  652. }
  653. // Partial fill graphic
  654. if (partShapeArgs) {
  655. if (graphic.partRect) {
  656. graphic.partRect[verb](merge(partShapeArgs));
  657. graphic.partialClipRect[verb](merge(clipRectArgs));
  658. }
  659. else {
  660. graphic.partialClipRect = renderer.clipRect(clipRectArgs.x, clipRectArgs.y, clipRectArgs.width, clipRectArgs.height);
  661. graphic.partRect =
  662. renderer[type](partShapeArgs)
  663. .addClass('highcharts-partfill-overlay')
  664. .add(graphic)
  665. .clip(graphic.partialClipRect);
  666. }
  667. }
  668. // Presentational
  669. if (!series.chart.styledMode) {
  670. graphic
  671. .rect[verb](pointAttr, animation)
  672. .shadow(seriesOpts.shadow, null, cutOff);
  673. if (partShapeArgs) {
  674. // Ensure pfOptions is an object
  675. if (!isObject(pfOptions)) {
  676. pfOptions = {};
  677. }
  678. if (isObject(seriesOpts.partialFill)) {
  679. pfOptions = merge(seriesOpts.partialFill, pfOptions);
  680. }
  681. fill = (pfOptions.fill ||
  682. color(pointAttr.fill).brighten(-0.3).get() ||
  683. color(point.color || series.color)
  684. .brighten(-0.3).get());
  685. pointAttr.fill = fill;
  686. graphic
  687. .partRect[pointStateVerb](pointAttr, animation)
  688. .shadow(seriesOpts.shadow, null, cutOff);
  689. }
  690. }
  691. }
  692. else if (graphic) {
  693. point.graphic = graphic.destroy(); // #1269
  694. }
  695. };
  696. /**
  697. * @private
  698. * @function Highcharts.Series#drawPoints
  699. */
  700. XRangeSeries.prototype.drawPoints = function () {
  701. var series = this,
  702. verb = series.getAnimationVerb();
  703. // Draw the columns
  704. series.points.forEach(function (point) {
  705. series.drawPoint(point, verb);
  706. });
  707. };
  708. /**
  709. * Returns "animate", or "attr" if the number of points is above the
  710. * animation limit.
  711. *
  712. * @private
  713. * @function Highcharts.Series#getAnimationVerb
  714. *
  715. * @return {string}
  716. */
  717. XRangeSeries.prototype.getAnimationVerb = function () {
  718. return (this.chart.pointCount < (this.options.animationLimit || 250) ?
  719. 'animate' :
  720. 'attr');
  721. };
  722. /**
  723. * @private
  724. * @function Highcharts.XRangeSeries#isPointInside
  725. */
  726. XRangeSeries.prototype.isPointInside = function (point) {
  727. var shapeArgs = point.shapeArgs,
  728. plotX = point.plotX,
  729. plotY = point.plotY;
  730. if (!shapeArgs) {
  731. return _super.prototype.isPointInside.apply(this, arguments);
  732. }
  733. var isInside = typeof plotX !== 'undefined' &&
  734. typeof plotY !== 'undefined' &&
  735. plotY >= 0 &&
  736. plotY <= this.yAxis.len &&
  737. shapeArgs.x + shapeArgs.width >= 0 &&
  738. plotX <= this.xAxis.len;
  739. return isInside;
  740. };
  741. /* *
  742. *
  743. * Static properties
  744. *
  745. * */
  746. /**
  747. * The X-range series displays ranges on the X axis, typically time
  748. * intervals with a start and end date.
  749. *
  750. * @sample {highcharts} highcharts/demo/x-range/
  751. * X-range
  752. * @sample {highcharts} highcharts/css/x-range/
  753. * Styled mode X-range
  754. * @sample {highcharts} highcharts/chart/inverted-xrange/
  755. * Inverted X-range
  756. *
  757. * @extends plotOptions.column
  758. * @since 6.0.0
  759. * @product highcharts highstock gantt
  760. * @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor,
  761. * edgeWidth, findNearestPointBy, getExtremesFromAll,
  762. * negativeColor, pointInterval, pointIntervalUnit,
  763. * pointPlacement, pointRange, pointStart, softThreshold,
  764. * stacking, threshold, data, dataSorting, boostBlending
  765. * @requires modules/xrange
  766. * @optionparent plotOptions.xrange
  767. */
  768. XRangeSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  769. /**
  770. * A partial fill for each point, typically used to visualize how much
  771. * of a task is performed. The partial fill object can be set either on
  772. * series or point level.
  773. *
  774. * @sample {highcharts} highcharts/demo/x-range
  775. * X-range with partial fill
  776. *
  777. * @product highcharts highstock gantt
  778. * @apioption plotOptions.xrange.partialFill
  779. */
  780. /**
  781. * The fill color to be used for partial fills. Defaults to a darker
  782. * shade of the point color.
  783. *
  784. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  785. * @product highcharts highstock gantt
  786. * @apioption plotOptions.xrange.partialFill.fill
  787. */
  788. /**
  789. * A partial fill for each point, typically used to visualize how much
  790. * of a task is performed. See [completed](series.gantt.data.completed).
  791. *
  792. * @sample gantt/demo/progress-indicator
  793. * Gantt with progress indicator
  794. *
  795. * @product gantt
  796. * @apioption plotOptions.gantt.partialFill
  797. */
  798. /**
  799. * In an X-range series, this option makes all points of the same Y-axis
  800. * category the same color.
  801. */
  802. colorByPoint: true,
  803. dataLabels: {
  804. formatter: function () {
  805. var point = this.point,
  806. amount = point.partialFill;
  807. if (isObject(amount)) {
  808. amount = amount.amount;
  809. }
  810. if (isNumber(amount) && amount > 0) {
  811. return correctFloat(amount * 100) + '%';
  812. }
  813. },
  814. inside: true,
  815. verticalAlign: 'middle'
  816. },
  817. tooltip: {
  818. headerFormat: '<span style="font-size: 10px">{point.x} - {point.x2}</span><br/>',
  819. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.yCategory}</b><br/>'
  820. },
  821. borderRadius: 3,
  822. pointRange: 0
  823. });
  824. return XRangeSeries;
  825. }(ColumnSeries));
  826. extend(XRangeSeries.prototype, {
  827. type: 'xrange',
  828. parallelArrays: ['x', 'x2', 'y'],
  829. requireSorting: false,
  830. animate: Series.prototype.animate,
  831. cropShoulder: 1,
  832. getExtremesFromAll: true,
  833. autoIncrement: H.noop,
  834. buildKDTree: H.noop,
  835. pointClass: XRangePoint
  836. });
  837. SeriesRegistry.registerSeriesType('xrange', XRangeSeries);
  838. /* *
  839. *
  840. * Default Export
  841. *
  842. * */
  843. /* *
  844. *
  845. * API Options
  846. *
  847. * */
  848. /**
  849. * An `xrange` series. If the [type](#series.xrange.type) option is not
  850. * specified, it is inherited from [chart.type](#chart.type).
  851. *
  852. * @extends series,plotOptions.xrange
  853. * @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor, edgeWidth,
  854. * findNearestPointBy, getExtremesFromAll, negativeColor,
  855. * pointInterval, pointIntervalUnit, pointPlacement, pointRange,
  856. * pointStart, softThreshold, stacking, threshold, dataSorting,
  857. * boostBlending
  858. * @product highcharts highstock gantt
  859. * @requires modules/xrange
  860. * @apioption series.xrange
  861. */
  862. /**
  863. * An array of data points for the series. For the `xrange` series type,
  864. * points can be given in the following ways:
  865. *
  866. * 1. An array of objects with named values. The objects are point configuration
  867. * objects as seen below.
  868. * ```js
  869. * data: [{
  870. * x: Date.UTC(2017, 0, 1),
  871. * x2: Date.UTC(2017, 0, 3),
  872. * name: "Test",
  873. * y: 0,
  874. * color: "#00FF00"
  875. * }, {
  876. * x: Date.UTC(2017, 0, 4),
  877. * x2: Date.UTC(2017, 0, 5),
  878. * name: "Deploy",
  879. * y: 1,
  880. * color: "#FF0000"
  881. * }]
  882. * ```
  883. *
  884. * @sample {highcharts} highcharts/series/data-array-of-objects/
  885. * Config objects
  886. *
  887. * @declare Highcharts.XrangePointOptionsObject
  888. * @type {Array<*>}
  889. * @extends series.line.data
  890. * @product highcharts highstock gantt
  891. * @apioption series.xrange.data
  892. */
  893. /**
  894. * The starting X value of the range point.
  895. *
  896. * @sample {highcharts} highcharts/demo/x-range
  897. * X-range
  898. *
  899. * @type {number}
  900. * @product highcharts highstock gantt
  901. * @apioption series.xrange.data.x
  902. */
  903. /**
  904. * The ending X value of the range point.
  905. *
  906. * @sample {highcharts} highcharts/demo/x-range
  907. * X-range
  908. *
  909. * @type {number}
  910. * @product highcharts highstock gantt
  911. * @apioption series.xrange.data.x2
  912. */
  913. /**
  914. * The Y value of the range point.
  915. *
  916. * @sample {highcharts} highcharts/demo/x-range
  917. * X-range
  918. *
  919. * @type {number}
  920. * @product highcharts highstock gantt
  921. * @apioption series.xrange.data.y
  922. */
  923. /**
  924. * A partial fill for each point, typically used to visualize how much of
  925. * a task is performed. The partial fill object can be set either on series
  926. * or point level.
  927. *
  928. * @sample {highcharts} highcharts/demo/x-range
  929. * X-range with partial fill
  930. *
  931. * @declare Highcharts.XrangePointPartialFillOptionsObject
  932. * @product highcharts highstock gantt
  933. * @apioption series.xrange.data.partialFill
  934. */
  935. /**
  936. * The amount of the X-range point to be filled. Values can be 0-1 and are
  937. * converted to percentages in the default data label formatter.
  938. *
  939. * @type {number}
  940. * @product highcharts highstock gantt
  941. * @apioption series.xrange.data.partialFill.amount
  942. */
  943. /**
  944. * The fill color to be used for partial fills. Defaults to a darker shade
  945. * of the point color.
  946. *
  947. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  948. * @product highcharts highstock gantt
  949. * @apioption series.xrange.data.partialFill.fill
  950. */
  951. ''; // adds doclets above to transpiled file
  952. return XRangeSeries;
  953. });
  954. _registerModule(_modules, 'Series/Gantt/GanttPoint.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  955. /* *
  956. *
  957. * (c) 2016-2021 Highsoft AS
  958. *
  959. * Author: Lars A. V. Cabrera
  960. *
  961. * License: www.highcharts.com/license
  962. *
  963. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  964. *
  965. * */
  966. var __extends = (this && this.__extends) || (function () {
  967. var extendStatics = function (d,
  968. b) {
  969. extendStatics = Object.setPrototypeOf ||
  970. ({ __proto__: [] } instanceof Array && function (d,
  971. b) { d.__proto__ = b; }) ||
  972. function (d,
  973. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  974. return extendStatics(d, b);
  975. };
  976. return function (d, b) {
  977. extendStatics(d, b);
  978. function __() { this.constructor = d; }
  979. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  980. };
  981. })();
  982. var XRangePoint = SeriesRegistry.seriesTypes.xrange.prototype.pointClass;
  983. var pick = U.pick;
  984. /* *
  985. *
  986. * Class
  987. *
  988. * */
  989. var GanttPoint = /** @class */ (function (_super) {
  990. __extends(GanttPoint, _super);
  991. function GanttPoint() {
  992. /* *
  993. *
  994. * Static Functions
  995. *
  996. * */
  997. var _this = _super !== null && _super.apply(this,
  998. arguments) || this;
  999. _this.options = void 0;
  1000. _this.series = void 0;
  1001. return _this;
  1002. /* eslint-enable valid-jsdoc */
  1003. }
  1004. /* eslint-disable valid-jsdoc */
  1005. /**
  1006. * @private
  1007. */
  1008. GanttPoint.setGanttPointAliases = function (options) {
  1009. /**
  1010. * Add a value to options if the value exists.
  1011. * @private
  1012. */
  1013. function addIfExists(prop, val) {
  1014. if (typeof val !== 'undefined') {
  1015. options[prop] = val;
  1016. }
  1017. }
  1018. addIfExists('x', pick(options.start, options.x));
  1019. addIfExists('x2', pick(options.end, options.x2));
  1020. addIfExists('partialFill', pick(options.completed, options.partialFill));
  1021. };
  1022. /* *
  1023. *
  1024. * Functions
  1025. *
  1026. * */
  1027. /* eslint-disable valid-jsdoc */
  1028. /**
  1029. * Applies the options containing the x and y data and possible some
  1030. * extra properties. This is called on point init or from point.update.
  1031. *
  1032. * @private
  1033. * @function Highcharts.Point#applyOptions
  1034. *
  1035. * @param {object} options
  1036. * The point options
  1037. *
  1038. * @param {number} x
  1039. * The x value
  1040. *
  1041. * @return {Highcharts.Point}
  1042. * The Point instance
  1043. */
  1044. GanttPoint.prototype.applyOptions = function (options, x) {
  1045. var point = this,
  1046. ganttPoint;
  1047. ganttPoint = _super.prototype.applyOptions.call(point, options, x);
  1048. GanttPoint.setGanttPointAliases(ganttPoint);
  1049. return ganttPoint;
  1050. };
  1051. GanttPoint.prototype.isValid = function () {
  1052. return ((typeof this.start === 'number' ||
  1053. typeof this.x === 'number') &&
  1054. (typeof this.end === 'number' ||
  1055. typeof this.x2 === 'number' ||
  1056. this.milestone));
  1057. };
  1058. return GanttPoint;
  1059. }(XRangePoint));
  1060. /* *
  1061. *
  1062. * Default Export
  1063. *
  1064. * */
  1065. return GanttPoint;
  1066. });
  1067. _registerModule(_modules, 'Gantt/Tree.js', [_modules['Core/Utilities.js']], function (U) {
  1068. /* *
  1069. *
  1070. * (c) 2016-2021 Highsoft AS
  1071. *
  1072. * Authors: Jon Arild Nygard
  1073. *
  1074. * License: www.highcharts.com/license
  1075. *
  1076. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1077. *
  1078. * */
  1079. /* eslint no-console: 0 */
  1080. var extend = U.extend,
  1081. isNumber = U.isNumber,
  1082. pick = U.pick;
  1083. /**
  1084. * Creates an object map from parent id to childrens index.
  1085. *
  1086. * @private
  1087. * @function Highcharts.Tree#getListOfParents
  1088. *
  1089. * @param {Array<*>} data
  1090. * List of points set in options. `Array.parent` is parent id of point.
  1091. *
  1092. * @param {Array<string>} ids
  1093. * List of all point ids.
  1094. *
  1095. * @return {Highcharts.Dictionary<Array<*>>}
  1096. * Map from parent id to children index in data
  1097. */
  1098. var getListOfParents = function (data,
  1099. ids) {
  1100. var listOfParents = data.reduce(function (prev,
  1101. curr) {
  1102. var parent = pick(curr.parent, '');
  1103. if (typeof prev[parent] === 'undefined') {
  1104. prev[parent] = [];
  1105. }
  1106. prev[parent].push(curr);
  1107. return prev;
  1108. }, {}), parents = Object.keys(listOfParents);
  1109. // If parent does not exist, hoist parent to root of tree.
  1110. parents.forEach(function (parent, list) {
  1111. var children = listOfParents[parent];
  1112. if ((parent !== '') && (ids.indexOf(parent) === -1)) {
  1113. children.forEach(function (child) {
  1114. list[''].push(child);
  1115. });
  1116. delete list[parent];
  1117. }
  1118. });
  1119. return listOfParents;
  1120. };
  1121. var getNode = function (id,
  1122. parent,
  1123. level,
  1124. data,
  1125. mapOfIdToChildren,
  1126. options) {
  1127. var descendants = 0,
  1128. height = 0,
  1129. after = options && options.after,
  1130. before = options && options.before,
  1131. node = {
  1132. data: data,
  1133. depth: level - 1,
  1134. id: id,
  1135. level: level,
  1136. parent: parent
  1137. },
  1138. start,
  1139. end,
  1140. children;
  1141. // Allow custom logic before the children has been created.
  1142. if (typeof before === 'function') {
  1143. before(node, options);
  1144. }
  1145. // Call getNode recursively on the children. Calulate the height of the
  1146. // node, and the number of descendants.
  1147. children = ((mapOfIdToChildren[id] || [])).map(function (child) {
  1148. var node = getNode(child.id,
  1149. id, (level + 1),
  1150. child,
  1151. mapOfIdToChildren,
  1152. options),
  1153. childStart = child.start,
  1154. childEnd = (child.milestone === true ?
  1155. childStart :
  1156. child.end);
  1157. // Start should be the lowest child.start.
  1158. start = ((!isNumber(start) || childStart < start) ?
  1159. childStart :
  1160. start);
  1161. // End should be the largest child.end.
  1162. // If child is milestone, then use start as end.
  1163. end = ((!isNumber(end) || childEnd > end) ?
  1164. childEnd :
  1165. end);
  1166. descendants = descendants + 1 + node.descendants;
  1167. height = Math.max(node.height + 1, height);
  1168. return node;
  1169. });
  1170. // Calculate start and end for point if it is not already explicitly set.
  1171. if (data) {
  1172. data.start = pick(data.start, start);
  1173. data.end = pick(data.end, end);
  1174. }
  1175. extend(node, {
  1176. children: children,
  1177. descendants: descendants,
  1178. height: height
  1179. });
  1180. // Allow custom logic after the children has been created.
  1181. if (typeof after === 'function') {
  1182. after(node, options);
  1183. }
  1184. return node;
  1185. };
  1186. var getTree = function (data,
  1187. options) {
  1188. var ids = data.map(function (d) {
  1189. return d.id;
  1190. }), mapOfIdToChildren = getListOfParents(data, ids);
  1191. return getNode('', null, 1, null, mapOfIdToChildren, options);
  1192. };
  1193. var Tree = {
  1194. getListOfParents: getListOfParents,
  1195. getNode: getNode,
  1196. getTree: getTree
  1197. };
  1198. return Tree;
  1199. });
  1200. _registerModule(_modules, 'Core/Axis/TreeGridTick.js', [_modules['Core/Color/Palette.js'], _modules['Core/Utilities.js']], function (palette, U) {
  1201. /* *
  1202. *
  1203. * (c) 2016 Highsoft AS
  1204. * Authors: Jon Arild Nygard
  1205. *
  1206. * License: www.highcharts.com/license
  1207. *
  1208. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1209. *
  1210. * */
  1211. var addEvent = U.addEvent,
  1212. defined = U.defined,
  1213. isObject = U.isObject,
  1214. isNumber = U.isNumber,
  1215. pick = U.pick,
  1216. wrap = U.wrap;
  1217. /**
  1218. * @private
  1219. */
  1220. var TreeGridTick;
  1221. (function (TreeGridTick) {
  1222. /* *
  1223. *
  1224. * Interfaces
  1225. *
  1226. * */
  1227. /* *
  1228. *
  1229. * Variables
  1230. *
  1231. * */
  1232. var applied = false;
  1233. /* *
  1234. *
  1235. * Functions
  1236. *
  1237. * */
  1238. /**
  1239. * @private
  1240. */
  1241. function compose(TickClass) {
  1242. if (!applied) {
  1243. addEvent(TickClass, 'init', onInit);
  1244. wrap(TickClass.prototype, 'getLabelPosition', wrapGetLabelPosition);
  1245. wrap(TickClass.prototype, 'renderLabel', wrapRenderLabel);
  1246. // backwards compatibility
  1247. TickClass.prototype.collapse = function (redraw) {
  1248. this.treeGrid.collapse(redraw);
  1249. };
  1250. TickClass.prototype.expand = function (redraw) {
  1251. this.treeGrid.expand(redraw);
  1252. };
  1253. TickClass.prototype.toggleCollapse = function (redraw) {
  1254. this.treeGrid.toggleCollapse(redraw);
  1255. };
  1256. applied = true;
  1257. }
  1258. }
  1259. TreeGridTick.compose = compose;
  1260. /**
  1261. * @private
  1262. */
  1263. function onInit() {
  1264. var tick = this;
  1265. if (!tick.treeGrid) {
  1266. tick.treeGrid = new Additions(tick);
  1267. }
  1268. }
  1269. /**
  1270. * @private
  1271. */
  1272. function onTickHover(label) {
  1273. label.addClass('highcharts-treegrid-node-active');
  1274. if (!label.renderer.styledMode) {
  1275. label.css({
  1276. textDecoration: 'underline'
  1277. });
  1278. }
  1279. }
  1280. /**
  1281. * @private
  1282. */
  1283. function onTickHoverExit(label, options) {
  1284. var css = isObject(options.style) ? options.style : {};
  1285. label.removeClass('highcharts-treegrid-node-active');
  1286. if (!label.renderer.styledMode) {
  1287. label.css({ textDecoration: css.textDecoration });
  1288. }
  1289. }
  1290. /**
  1291. * @private
  1292. */
  1293. function renderLabelIcon(tick, params) {
  1294. var treeGrid = tick.treeGrid,
  1295. isNew = !treeGrid.labelIcon,
  1296. renderer = params.renderer,
  1297. labelBox = params.xy,
  1298. options = params.options,
  1299. width = options.width,
  1300. height = options.height,
  1301. iconCenter = {
  1302. x: labelBox.x - (width / 2) - options.padding,
  1303. y: labelBox.y - (height / 2)
  1304. },
  1305. rotation = params.collapsed ? 90 : 180,
  1306. shouldRender = params.show && isNumber(iconCenter.y);
  1307. var icon = treeGrid.labelIcon;
  1308. if (!icon) {
  1309. treeGrid.labelIcon = icon = renderer
  1310. .path(renderer.symbols[options.type](options.x, options.y, width, height))
  1311. .addClass('highcharts-label-icon')
  1312. .add(params.group);
  1313. }
  1314. // Set the new position, and show or hide
  1315. if (!shouldRender) {
  1316. icon.attr({ y: -9999 }); // #1338
  1317. }
  1318. // Presentational attributes
  1319. if (!renderer.styledMode) {
  1320. icon
  1321. .attr({
  1322. 'stroke-width': 1,
  1323. 'fill': pick(params.color, palette.neutralColor60)
  1324. })
  1325. .css({
  1326. cursor: 'pointer',
  1327. stroke: options.lineColor,
  1328. strokeWidth: options.lineWidth
  1329. });
  1330. }
  1331. // Update the icon positions
  1332. icon[isNew ? 'attr' : 'animate']({
  1333. translateX: iconCenter.x,
  1334. translateY: iconCenter.y,
  1335. rotation: rotation
  1336. });
  1337. }
  1338. /**
  1339. * @private
  1340. */
  1341. function wrapGetLabelPosition(proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  1342. var tick = this,
  1343. lbOptions = pick(tick.options && tick.options.labels,
  1344. labelOptions),
  1345. pos = tick.pos,
  1346. axis = tick.axis,
  1347. options = axis.options,
  1348. isTreeGrid = options.type === 'treegrid',
  1349. result = proceed.apply(tick,
  1350. [x,
  1351. y,
  1352. label,
  1353. horiz,
  1354. lbOptions,
  1355. tickmarkOffset,
  1356. index,
  1357. step]);
  1358. var symbolOptions,
  1359. indentation,
  1360. mapOfPosToGridNode,
  1361. node,
  1362. level;
  1363. if (isTreeGrid) {
  1364. symbolOptions = (lbOptions && isObject(lbOptions.symbol, true) ?
  1365. lbOptions.symbol :
  1366. {});
  1367. indentation = (lbOptions && isNumber(lbOptions.indentation) ?
  1368. lbOptions.indentation :
  1369. 0);
  1370. mapOfPosToGridNode = axis.treeGrid.mapOfPosToGridNode;
  1371. node = mapOfPosToGridNode && mapOfPosToGridNode[pos];
  1372. level = (node && node.depth) || 1;
  1373. result.x += (
  1374. // Add space for symbols
  1375. ((symbolOptions.width) + (symbolOptions.padding * 2)) +
  1376. // Apply indentation
  1377. ((level - 1) * indentation));
  1378. }
  1379. return result;
  1380. }
  1381. /**
  1382. * @private
  1383. */
  1384. function wrapRenderLabel(proceed) {
  1385. var tick = this, pos = tick.pos, axis = tick.axis, label = tick.label, mapOfPosToGridNode = axis.treeGrid.mapOfPosToGridNode, options = axis.options, labelOptions = pick(tick.options && tick.options.labels, options && options.labels), symbolOptions = (labelOptions && isObject(labelOptions.symbol, true) ?
  1386. labelOptions.symbol :
  1387. {}), node = mapOfPosToGridNode && mapOfPosToGridNode[pos], level = node && node.depth, isTreeGrid = options.type === 'treegrid', shouldRender = axis.tickPositions.indexOf(pos) > -1, prefixClassName = 'highcharts-treegrid-node-', styledMode = axis.chart.styledMode;
  1388. var collapsed,
  1389. addClassName,
  1390. removeClassName;
  1391. if (isTreeGrid && node) {
  1392. // Add class name for hierarchical styling.
  1393. if (label &&
  1394. label.element) {
  1395. label.addClass(prefixClassName + 'level-' + level);
  1396. }
  1397. }
  1398. proceed.apply(tick, Array.prototype.slice.call(arguments, 1));
  1399. if (isTreeGrid &&
  1400. label &&
  1401. label.element &&
  1402. node &&
  1403. node.descendants &&
  1404. node.descendants > 0) {
  1405. collapsed = axis.treeGrid.isCollapsed(node);
  1406. renderLabelIcon(tick, {
  1407. color: !styledMode && label.styles && label.styles.color || '',
  1408. collapsed: collapsed,
  1409. group: label.parentGroup,
  1410. options: symbolOptions,
  1411. renderer: label.renderer,
  1412. show: shouldRender,
  1413. xy: label.xy
  1414. });
  1415. // Add class name for the node.
  1416. addClassName = prefixClassName +
  1417. (collapsed ? 'collapsed' : 'expanded');
  1418. removeClassName = prefixClassName +
  1419. (collapsed ? 'expanded' : 'collapsed');
  1420. label
  1421. .addClass(addClassName)
  1422. .removeClass(removeClassName);
  1423. if (!styledMode) {
  1424. label.css({
  1425. cursor: 'pointer'
  1426. });
  1427. }
  1428. // Add events to both label text and icon
  1429. [label, tick.treeGrid.labelIcon].forEach(function (object) {
  1430. if (object && !object.attachedTreeGridEvents) {
  1431. // On hover
  1432. addEvent(object.element, 'mouseover', function () {
  1433. onTickHover(label);
  1434. });
  1435. // On hover out
  1436. addEvent(object.element, 'mouseout', function () {
  1437. onTickHoverExit(label, labelOptions);
  1438. });
  1439. addEvent(object.element, 'click', function () {
  1440. tick.treeGrid.toggleCollapse();
  1441. });
  1442. object.attachedTreeGridEvents = true;
  1443. }
  1444. });
  1445. }
  1446. }
  1447. /* *
  1448. *
  1449. * Classes
  1450. *
  1451. * */
  1452. /**
  1453. * @private
  1454. * @class
  1455. */
  1456. var Additions = /** @class */ (function () {
  1457. /* *
  1458. *
  1459. * Constructors
  1460. *
  1461. * */
  1462. /**
  1463. * @private
  1464. */
  1465. function Additions(tick) {
  1466. this.tick = tick;
  1467. }
  1468. /* *
  1469. *
  1470. * Functions
  1471. *
  1472. * */
  1473. /**
  1474. * Collapse the grid cell. Used when axis is of type treegrid.
  1475. *
  1476. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  1477. *
  1478. * @private
  1479. * @function Highcharts.Tick#collapse
  1480. *
  1481. * @param {boolean} [redraw=true]
  1482. * Whether to redraw the chart or wait for an explicit call to
  1483. * {@link Highcharts.Chart#redraw}
  1484. */
  1485. Additions.prototype.collapse = function (redraw) {
  1486. var tick = this.tick,
  1487. axis = tick.axis,
  1488. brokenAxis = axis.brokenAxis;
  1489. if (brokenAxis &&
  1490. axis.treeGrid.mapOfPosToGridNode) {
  1491. var pos = tick.pos,
  1492. node = axis.treeGrid.mapOfPosToGridNode[pos],
  1493. breaks = axis.treeGrid.collapse(node);
  1494. brokenAxis.setBreaks(breaks, pick(redraw, true));
  1495. }
  1496. };
  1497. /**
  1498. * Expand the grid cell. Used when axis is of type treegrid.
  1499. *
  1500. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  1501. *
  1502. * @private
  1503. * @function Highcharts.Tick#expand
  1504. *
  1505. * @param {boolean} [redraw=true]
  1506. * Whether to redraw the chart or wait for an explicit call to
  1507. * {@link Highcharts.Chart#redraw}
  1508. */
  1509. Additions.prototype.expand = function (redraw) {
  1510. var tick = this.tick,
  1511. axis = tick.axis,
  1512. brokenAxis = axis.brokenAxis;
  1513. if (brokenAxis &&
  1514. axis.treeGrid.mapOfPosToGridNode) {
  1515. var pos = tick.pos,
  1516. node = axis.treeGrid.mapOfPosToGridNode[pos],
  1517. breaks = axis.treeGrid.expand(node);
  1518. brokenAxis.setBreaks(breaks, pick(redraw, true));
  1519. }
  1520. };
  1521. /**
  1522. * Toggle the collapse/expand state of the grid cell. Used when axis is
  1523. * of type treegrid.
  1524. *
  1525. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  1526. *
  1527. * @private
  1528. * @function Highcharts.Tick#toggleCollapse
  1529. *
  1530. * @param {boolean} [redraw=true]
  1531. * Whether to redraw the chart or wait for an explicit call to
  1532. * {@link Highcharts.Chart#redraw}
  1533. */
  1534. Additions.prototype.toggleCollapse = function (redraw) {
  1535. var tick = this.tick,
  1536. axis = tick.axis,
  1537. brokenAxis = axis.brokenAxis;
  1538. if (brokenAxis &&
  1539. axis.treeGrid.mapOfPosToGridNode) {
  1540. var pos = tick.pos,
  1541. node = axis.treeGrid.mapOfPosToGridNode[pos],
  1542. breaks = axis.treeGrid.toggleCollapse(node);
  1543. brokenAxis.setBreaks(breaks, pick(redraw, true));
  1544. }
  1545. };
  1546. return Additions;
  1547. }());
  1548. TreeGridTick.Additions = Additions;
  1549. })(TreeGridTick || (TreeGridTick = {}));
  1550. return TreeGridTick;
  1551. });
  1552. _registerModule(_modules, 'Mixins/TreeSeries.js', [_modules['Core/Color/Color.js'], _modules['Core/Utilities.js']], function (Color, U) {
  1553. /* *
  1554. *
  1555. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1556. *
  1557. * */
  1558. var extend = U.extend,
  1559. isArray = U.isArray,
  1560. isNumber = U.isNumber,
  1561. isObject = U.isObject,
  1562. merge = U.merge,
  1563. pick = U.pick;
  1564. var isBoolean = function (x) {
  1565. return typeof x === 'boolean';
  1566. }, isFn = function (x) {
  1567. return typeof x === 'function';
  1568. };
  1569. /* eslint-disable valid-jsdoc */
  1570. /**
  1571. * @todo Combine buildTree and buildNode with setTreeValues
  1572. * @todo Remove logic from Treemap and make it utilize this mixin.
  1573. * @private
  1574. */
  1575. var setTreeValues = function setTreeValues(tree,
  1576. options) {
  1577. var before = options.before,
  1578. idRoot = options.idRoot,
  1579. mapIdToNode = options.mapIdToNode,
  1580. nodeRoot = mapIdToNode[idRoot],
  1581. levelIsConstant = (isBoolean(options.levelIsConstant) ?
  1582. options.levelIsConstant :
  1583. true),
  1584. points = options.points,
  1585. point = points[tree.i],
  1586. optionsPoint = point && point.options || {},
  1587. childrenTotal = 0,
  1588. children = [],
  1589. value;
  1590. extend(tree, {
  1591. levelDynamic: tree.level - (levelIsConstant ? 0 : nodeRoot.level),
  1592. name: pick(point && point.name, ''),
  1593. visible: (idRoot === tree.id ||
  1594. (isBoolean(options.visible) ? options.visible : false))
  1595. });
  1596. if (isFn(before)) {
  1597. tree = before(tree, options);
  1598. }
  1599. // First give the children some values
  1600. tree.children.forEach(function (child, i) {
  1601. var newOptions = extend({},
  1602. options);
  1603. extend(newOptions, {
  1604. index: i,
  1605. siblings: tree.children.length,
  1606. visible: tree.visible
  1607. });
  1608. child = setTreeValues(child, newOptions);
  1609. children.push(child);
  1610. if (child.visible) {
  1611. childrenTotal += child.val;
  1612. }
  1613. });
  1614. tree.visible = childrenTotal > 0 || tree.visible;
  1615. // Set the values
  1616. value = pick(optionsPoint.value, childrenTotal);
  1617. extend(tree, {
  1618. children: children,
  1619. childrenTotal: childrenTotal,
  1620. isLeaf: tree.visible && !childrenTotal,
  1621. val: value
  1622. });
  1623. return tree;
  1624. };
  1625. /**
  1626. * @private
  1627. */
  1628. var getColor = function getColor(node,
  1629. options) {
  1630. var index = options.index,
  1631. mapOptionsToLevel = options.mapOptionsToLevel,
  1632. parentColor = options.parentColor,
  1633. parentColorIndex = options.parentColorIndex,
  1634. series = options.series,
  1635. colors = options.colors,
  1636. siblings = options.siblings,
  1637. points = series.points,
  1638. getColorByPoint,
  1639. chartOptionsChart = series.chart.options.chart,
  1640. point,
  1641. level,
  1642. colorByPoint,
  1643. colorIndexByPoint,
  1644. color,
  1645. colorIndex;
  1646. /**
  1647. * @private
  1648. */
  1649. function variation(color) {
  1650. var colorVariation = level && level.colorVariation;
  1651. if (colorVariation) {
  1652. if (colorVariation.key === 'brightness') {
  1653. return Color.parse(color).brighten(colorVariation.to * (index / siblings)).get();
  1654. }
  1655. }
  1656. return color;
  1657. }
  1658. if (node) {
  1659. point = points[node.i];
  1660. level = mapOptionsToLevel[node.level] || {};
  1661. getColorByPoint = point && level.colorByPoint;
  1662. if (getColorByPoint) {
  1663. colorIndexByPoint = point.index % (colors ?
  1664. colors.length :
  1665. chartOptionsChart.colorCount);
  1666. colorByPoint = colors && colors[colorIndexByPoint];
  1667. }
  1668. // Select either point color, level color or inherited color.
  1669. if (!series.chart.styledMode) {
  1670. color = pick(point && point.options.color, level && level.color, colorByPoint, parentColor && variation(parentColor), series.color);
  1671. }
  1672. colorIndex = pick(point && point.options.colorIndex, level && level.colorIndex, colorIndexByPoint, parentColorIndex, options.colorIndex);
  1673. }
  1674. return {
  1675. color: color,
  1676. colorIndex: colorIndex
  1677. };
  1678. };
  1679. /**
  1680. * Creates a map from level number to its given options.
  1681. *
  1682. * @private
  1683. * @function getLevelOptions
  1684. * @param {object} params
  1685. * Object containing parameters.
  1686. * - `defaults` Object containing default options. The default options
  1687. * are merged with the userOptions to get the final options for a
  1688. * specific level.
  1689. * - `from` The lowest level number.
  1690. * - `levels` User options from series.levels.
  1691. * - `to` The highest level number.
  1692. * @return {Highcharts.Dictionary<object>|null}
  1693. * Returns a map from level number to its given options.
  1694. */
  1695. var getLevelOptions = function getLevelOptions(params) {
  1696. var result = null,
  1697. defaults,
  1698. converted,
  1699. i,
  1700. from,
  1701. to,
  1702. levels;
  1703. if (isObject(params)) {
  1704. result = {};
  1705. from = isNumber(params.from) ? params.from : 1;
  1706. levels = params.levels;
  1707. converted = {};
  1708. defaults = isObject(params.defaults) ? params.defaults : {};
  1709. if (isArray(levels)) {
  1710. converted = levels.reduce(function (obj, item) {
  1711. var level,
  1712. levelIsConstant,
  1713. options;
  1714. if (isObject(item) && isNumber(item.level)) {
  1715. options = merge({}, item);
  1716. levelIsConstant = (isBoolean(options.levelIsConstant) ?
  1717. options.levelIsConstant :
  1718. defaults.levelIsConstant);
  1719. // Delete redundant properties.
  1720. delete options.levelIsConstant;
  1721. delete options.level;
  1722. // Calculate which level these options apply to.
  1723. level = item.level + (levelIsConstant ? 0 : from - 1);
  1724. if (isObject(obj[level])) {
  1725. extend(obj[level], options);
  1726. }
  1727. else {
  1728. obj[level] = options;
  1729. }
  1730. }
  1731. return obj;
  1732. }, {});
  1733. }
  1734. to = isNumber(params.to) ? params.to : 1;
  1735. for (i = 0; i <= to; i++) {
  1736. result[i] = merge({}, defaults, isObject(converted[i]) ? converted[i] : {});
  1737. }
  1738. }
  1739. return result;
  1740. };
  1741. /**
  1742. * Update the rootId property on the series. Also makes sure that it is
  1743. * accessible to exporting.
  1744. *
  1745. * @private
  1746. * @function updateRootId
  1747. *
  1748. * @param {object} series
  1749. * The series to operate on.
  1750. *
  1751. * @return {string}
  1752. * Returns the resulting rootId after update.
  1753. */
  1754. var updateRootId = function (series) {
  1755. var rootId,
  1756. options;
  1757. if (isObject(series)) {
  1758. // Get the series options.
  1759. options = isObject(series.options) ? series.options : {};
  1760. // Calculate the rootId.
  1761. rootId = pick(series.rootNode, options.rootId, '');
  1762. // Set rootId on series.userOptions to pick it up in exporting.
  1763. if (isObject(series.userOptions)) {
  1764. series.userOptions.rootId = rootId;
  1765. }
  1766. // Set rootId on series to pick it up on next update.
  1767. series.rootNode = rootId;
  1768. }
  1769. return rootId;
  1770. };
  1771. var result = {
  1772. getColor: getColor,
  1773. getLevelOptions: getLevelOptions,
  1774. setTreeValues: setTreeValues,
  1775. updateRootId: updateRootId
  1776. };
  1777. return result;
  1778. });
  1779. _registerModule(_modules, 'Core/Axis/GridAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Utilities.js']], function (Axis, H, Tick, U) {
  1780. /* *
  1781. *
  1782. * (c) 2016 Highsoft AS
  1783. * Authors: Lars A. V. Cabrera
  1784. *
  1785. * License: www.highcharts.com/license
  1786. *
  1787. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1788. *
  1789. * */
  1790. var addEvent = U.addEvent,
  1791. defined = U.defined,
  1792. erase = U.erase,
  1793. find = U.find,
  1794. isArray = U.isArray,
  1795. isNumber = U.isNumber,
  1796. merge = U.merge,
  1797. pick = U.pick,
  1798. timeUnits = U.timeUnits,
  1799. wrap = U.wrap;
  1800. var argsToArray = function (args) {
  1801. return Array.prototype.slice.call(args, 1);
  1802. }, isObject = function (x) {
  1803. // Always use strict mode
  1804. return U.isObject(x, true);
  1805. }, Chart = H.Chart;
  1806. var applyGridOptions = function applyGridOptions(axis) {
  1807. var options = axis.options;
  1808. // Center-align by default
  1809. if (!options.labels) {
  1810. options.labels = {};
  1811. }
  1812. options.labels.align = pick(options.labels.align, 'center');
  1813. // @todo: Check against tickLabelPlacement between/on etc
  1814. /* Prevents adding the last tick label if the axis is not a category
  1815. axis.
  1816. Since numeric labels are normally placed at starts and ends of a
  1817. range of value, and this module makes the label point at the value,
  1818. an "extra" label would appear. */
  1819. if (!axis.categories) {
  1820. options.showLastLabel = false;
  1821. }
  1822. // Prevents rotation of labels when squished, as rotating them would not
  1823. // help.
  1824. axis.labelRotation = 0;
  1825. options.labels.rotation = 0;
  1826. };
  1827. /**
  1828. * @productdesc {gantt}
  1829. * For grid axes (like in Gantt charts),
  1830. * it is possible to declare as a list to provide different
  1831. * formats depending on available space.
  1832. *
  1833. * Defaults to:
  1834. * ```js
  1835. * {
  1836. * hour: { list: ['%H:%M', '%H'] },
  1837. * day: { list: ['%A, %e. %B', '%a, %e. %b', '%E'] },
  1838. * week: { list: ['Week %W', 'W%W'] },
  1839. * month: { list: ['%B', '%b', '%o'] }
  1840. * }
  1841. * ```
  1842. *
  1843. * @sample {gantt} gantt/grid-axis/date-time-label-formats
  1844. * Gantt chart with custom axis date format.
  1845. *
  1846. * @apioption xAxis.dateTimeLabelFormats
  1847. */
  1848. /**
  1849. * Set grid options for the axis labels. Requires Highcharts Gantt.
  1850. *
  1851. * @since 6.2.0
  1852. * @product gantt
  1853. * @apioption xAxis.grid
  1854. */
  1855. /**
  1856. * Enable grid on the axis labels. Defaults to true for Gantt charts.
  1857. *
  1858. * @type {boolean}
  1859. * @default true
  1860. * @since 6.2.0
  1861. * @product gantt
  1862. * @apioption xAxis.grid.enabled
  1863. */
  1864. /**
  1865. * Set specific options for each column (or row for horizontal axes) in the
  1866. * grid. Each extra column/row is its own axis, and the axis options can be set
  1867. * here.
  1868. *
  1869. * @sample gantt/demo/left-axis-table
  1870. * Left axis as a table
  1871. *
  1872. * @type {Array<Highcharts.XAxisOptions>}
  1873. * @apioption xAxis.grid.columns
  1874. */
  1875. /**
  1876. * Set border color for the label grid lines.
  1877. *
  1878. * @type {Highcharts.ColorString}
  1879. * @apioption xAxis.grid.borderColor
  1880. */
  1881. /**
  1882. * Set border width of the label grid lines.
  1883. *
  1884. * @type {number}
  1885. * @default 1
  1886. * @apioption xAxis.grid.borderWidth
  1887. */
  1888. /**
  1889. * Set cell height for grid axis labels. By default this is calculated from font
  1890. * size. This option only applies to horizontal axes.
  1891. *
  1892. * @sample gantt/grid-axis/cellheight
  1893. * Gant chart with custom cell height
  1894. * @type {number}
  1895. * @apioption xAxis.grid.cellHeight
  1896. */
  1897. ''; // detach doclets above
  1898. /**
  1899. * Get the largest label width and height.
  1900. *
  1901. * @private
  1902. * @function Highcharts.Axis#getMaxLabelDimensions
  1903. *
  1904. * @param {Highcharts.Dictionary<Highcharts.Tick>} ticks
  1905. * All the ticks on one axis.
  1906. *
  1907. * @param {Array<number|string>} tickPositions
  1908. * All the tick positions on one axis.
  1909. *
  1910. * @return {Highcharts.SizeObject}
  1911. * Object containing the properties height and width.
  1912. *
  1913. * @todo Move this to the generic axis implementation, as it is used there.
  1914. */
  1915. Axis.prototype.getMaxLabelDimensions = function (ticks, tickPositions) {
  1916. var dimensions = {
  1917. width: 0,
  1918. height: 0
  1919. };
  1920. tickPositions.forEach(function (pos) {
  1921. var tick = ticks[pos],
  1922. labelHeight = 0,
  1923. labelWidth = 0,
  1924. label;
  1925. if (isObject(tick)) {
  1926. label = isObject(tick.label) ? tick.label : {};
  1927. // Find width and height of label
  1928. labelHeight = label.getBBox ? label.getBBox().height : 0;
  1929. if (label.textStr && !isNumber(label.textPxLength)) {
  1930. label.textPxLength = label.getBBox().width;
  1931. }
  1932. labelWidth = isNumber(label.textPxLength) ?
  1933. // Math.round ensures crisp lines
  1934. Math.round(label.textPxLength) :
  1935. 0;
  1936. if (label.textStr) {
  1937. // Set the tickWidth same as the label width after ellipsis
  1938. // applied #10281
  1939. labelWidth = Math.round(label.getBBox().width);
  1940. }
  1941. // Update the result if width and/or height are larger
  1942. dimensions.height = Math.max(labelHeight, dimensions.height);
  1943. dimensions.width = Math.max(labelWidth, dimensions.width);
  1944. }
  1945. });
  1946. return dimensions;
  1947. };
  1948. // Adds week date format
  1949. H.dateFormats.W = function (timestamp) {
  1950. var d = new this.Date(timestamp);
  1951. var firstDay = (this.get('Day',
  1952. d) + 6) % 7;
  1953. var thursday = new this.Date(d.valueOf());
  1954. this.set('Date', thursday, this.get('Date', d) - firstDay + 3);
  1955. var firstThursday = new this.Date(this.get('FullYear',
  1956. thursday), 0, 1);
  1957. if (this.get('Day', firstThursday) !== 4) {
  1958. this.set('Month', d, 0);
  1959. this.set('Date', d, 1 + (11 - this.get('Day', firstThursday)) % 7);
  1960. }
  1961. return (1 +
  1962. Math.floor((thursday.valueOf() - firstThursday.valueOf()) / 604800000)).toString();
  1963. };
  1964. // First letter of the day of the week, e.g. 'M' for 'Monday'.
  1965. H.dateFormats.E = function (timestamp) {
  1966. return this.dateFormat('%a', timestamp, true).charAt(0);
  1967. };
  1968. /* eslint-disable no-invalid-this */
  1969. addEvent(Chart, 'afterSetChartSize', function () {
  1970. this.axes.forEach(function (axis) {
  1971. (axis.grid && axis.grid.columns || []).forEach(function (column) {
  1972. column.setAxisSize();
  1973. column.setAxisTranslation();
  1974. });
  1975. });
  1976. });
  1977. // Center tick labels in cells.
  1978. addEvent(Tick, 'afterGetLabelPosition', function (e) {
  1979. var tick = this,
  1980. label = tick.label,
  1981. axis = tick.axis,
  1982. reversed = axis.reversed,
  1983. chart = axis.chart,
  1984. options = axis.options,
  1985. gridOptions = options.grid || {},
  1986. labelOpts = axis.options.labels,
  1987. align = labelOpts.align,
  1988. // verticalAlign is currently not supported for axis.labels.
  1989. verticalAlign = 'middle', // labelOpts.verticalAlign,
  1990. side = GridAxis.Side[axis.side],
  1991. tickmarkOffset = e.tickmarkOffset,
  1992. tickPositions = axis.tickPositions,
  1993. tickPos = tick.pos - tickmarkOffset,
  1994. nextTickPos = (isNumber(tickPositions[e.index + 1]) ?
  1995. tickPositions[e.index + 1] - tickmarkOffset :
  1996. axis.max + tickmarkOffset),
  1997. tickSize = axis.tickSize('tick'),
  1998. tickWidth = tickSize ? tickSize[0] : 0,
  1999. crispCorr = tickSize ? tickSize[1] / 2 : 0,
  2000. labelHeight,
  2001. lblMetrics,
  2002. lines,
  2003. bottom,
  2004. top,
  2005. left,
  2006. right;
  2007. // Only center tick labels in grid axes
  2008. if (gridOptions.enabled === true) {
  2009. // Calculate top and bottom positions of the cell.
  2010. if (side === 'top') {
  2011. bottom = axis.top + axis.offset;
  2012. top = bottom - tickWidth;
  2013. }
  2014. else if (side === 'bottom') {
  2015. top = chart.chartHeight - axis.bottom + axis.offset;
  2016. bottom = top + tickWidth;
  2017. }
  2018. else {
  2019. bottom = axis.top + axis.len - axis.translate(reversed ? nextTickPos : tickPos);
  2020. top = axis.top + axis.len - axis.translate(reversed ? tickPos : nextTickPos);
  2021. }
  2022. // Calculate left and right positions of the cell.
  2023. if (side === 'right') {
  2024. left = chart.chartWidth - axis.right + axis.offset;
  2025. right = left + tickWidth;
  2026. }
  2027. else if (side === 'left') {
  2028. right = axis.left + axis.offset;
  2029. left = right - tickWidth;
  2030. }
  2031. else {
  2032. left = Math.round(axis.left + axis.translate(reversed ? nextTickPos : tickPos)) - crispCorr;
  2033. right = Math.round(axis.left + axis.translate(reversed ? tickPos : nextTickPos)) - crispCorr;
  2034. }
  2035. tick.slotWidth = right - left;
  2036. // Calculate the positioning of the label based on
  2037. // alignment.
  2038. e.pos.x = (align === 'left' ?
  2039. left :
  2040. align === 'right' ?
  2041. right :
  2042. left + ((right - left) / 2) // default to center
  2043. );
  2044. e.pos.y = (verticalAlign === 'top' ?
  2045. top :
  2046. verticalAlign === 'bottom' ?
  2047. bottom :
  2048. top + ((bottom - top) / 2) // default to middle
  2049. );
  2050. lblMetrics = chart.renderer.fontMetrics(labelOpts.style.fontSize, label.element);
  2051. labelHeight = label.getBBox().height;
  2052. // Adjustment to y position to align the label correctly.
  2053. // Would be better to have a setter or similar for this.
  2054. if (!labelOpts.useHTML) {
  2055. lines = Math.round(labelHeight / lblMetrics.h);
  2056. e.pos.y += (
  2057. // Center the label
  2058. // TODO: why does this actually center the label?
  2059. ((lblMetrics.b - (lblMetrics.h - lblMetrics.f)) / 2) +
  2060. // Adjust for height of additional lines.
  2061. -(((lines - 1) * lblMetrics.h) / 2));
  2062. }
  2063. else {
  2064. e.pos.y += (
  2065. // Readjust yCorr in htmlUpdateTransform
  2066. lblMetrics.b +
  2067. // Adjust for height of html label
  2068. -(labelHeight / 2));
  2069. }
  2070. e.pos.x += (axis.horiz && labelOpts.x || 0);
  2071. }
  2072. });
  2073. /* eslint-enable no-invalid-this */
  2074. /**
  2075. * Additions for grid axes.
  2076. * @private
  2077. * @class
  2078. */
  2079. var GridAxisAdditions = /** @class */ (function () {
  2080. /* *
  2081. *
  2082. * Constructors
  2083. *
  2084. * */
  2085. function GridAxisAdditions(axis) {
  2086. this.axis = axis;
  2087. }
  2088. /* *
  2089. *
  2090. * Functions
  2091. *
  2092. * */
  2093. /**
  2094. * Checks if an axis is the outer axis in its dimension. Since
  2095. * axes are placed outwards in order, the axis with the highest
  2096. * index is the outermost axis.
  2097. *
  2098. * Example: If there are multiple x-axes at the top of the chart,
  2099. * this function returns true if the axis supplied is the last
  2100. * of the x-axes.
  2101. *
  2102. * @private
  2103. *
  2104. * @return {boolean}
  2105. * True if the axis is the outermost axis in its dimension; false if
  2106. * not.
  2107. */
  2108. GridAxisAdditions.prototype.isOuterAxis = function () {
  2109. var axis = this.axis;
  2110. var chart = axis.chart;
  2111. var columnIndex = axis.grid.columnIndex;
  2112. var columns = (axis.linkedParent && axis.linkedParent.grid.columns ||
  2113. axis.grid.columns);
  2114. var parentAxis = columnIndex ? axis.linkedParent : axis;
  2115. var thisIndex = -1,
  2116. lastIndex = 0;
  2117. chart[axis.coll].forEach(function (otherAxis, index) {
  2118. if (otherAxis.side === axis.side && !otherAxis.options.isInternal) {
  2119. lastIndex = index;
  2120. if (otherAxis === parentAxis) {
  2121. // Get the index of the axis in question
  2122. thisIndex = index;
  2123. }
  2124. }
  2125. });
  2126. return (lastIndex === thisIndex &&
  2127. (isNumber(columnIndex) ? columns.length === columnIndex : true));
  2128. };
  2129. /**
  2130. * Add extra border based on the provided path.
  2131. * *
  2132. * @private
  2133. *
  2134. * @param {SVGPath} path
  2135. * The path of the border.
  2136. *
  2137. * @return {Highcharts.SVGElement}
  2138. */
  2139. GridAxisAdditions.prototype.renderBorder = function (path) {
  2140. var axis = this.axis,
  2141. renderer = axis.chart.renderer,
  2142. options = axis.options,
  2143. extraBorderLine = renderer.path(path)
  2144. .addClass('highcharts-axis-line')
  2145. .add(axis.axisBorder);
  2146. if (!renderer.styledMode) {
  2147. extraBorderLine.attr({
  2148. stroke: options.lineColor,
  2149. 'stroke-width': options.lineWidth,
  2150. zIndex: 7
  2151. });
  2152. }
  2153. return extraBorderLine;
  2154. };
  2155. return GridAxisAdditions;
  2156. }());
  2157. /**
  2158. * Axis with grid support.
  2159. * @private
  2160. * @class
  2161. */
  2162. var GridAxis = /** @class */ (function () {
  2163. function GridAxis() {
  2164. }
  2165. /* *
  2166. *
  2167. * Static Functions
  2168. *
  2169. * */
  2170. /* eslint-disable valid-jsdoc */
  2171. /**
  2172. * Extends axis class with grid support.
  2173. * @private
  2174. */
  2175. GridAxis.compose = function (AxisClass) {
  2176. Axis.keepProps.push('grid');
  2177. wrap(AxisClass.prototype, 'unsquish', GridAxis.wrapUnsquish);
  2178. // Add event handlers
  2179. addEvent(AxisClass, 'init', GridAxis.onInit);
  2180. addEvent(AxisClass, 'afterGetOffset', GridAxis.onAfterGetOffset);
  2181. addEvent(AxisClass, 'afterGetTitlePosition', GridAxis.onAfterGetTitlePosition);
  2182. addEvent(AxisClass, 'afterInit', GridAxis.onAfterInit);
  2183. addEvent(AxisClass, 'afterRender', GridAxis.onAfterRender);
  2184. addEvent(AxisClass, 'afterSetAxisTranslation', GridAxis.onAfterSetAxisTranslation);
  2185. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions);
  2186. addEvent(AxisClass, 'afterSetOptions', GridAxis.onAfterSetOptions2);
  2187. addEvent(AxisClass, 'afterSetScale', GridAxis.onAfterSetScale);
  2188. addEvent(AxisClass, 'afterTickSize', GridAxis.onAfterTickSize);
  2189. addEvent(AxisClass, 'trimTicks', GridAxis.onTrimTicks);
  2190. addEvent(AxisClass, 'destroy', GridAxis.onDestroy);
  2191. };
  2192. /**
  2193. * Handle columns and getOffset.
  2194. * @private
  2195. */
  2196. GridAxis.onAfterGetOffset = function () {
  2197. var grid = this.grid;
  2198. (grid && grid.columns || []).forEach(function (column) {
  2199. column.getOffset();
  2200. });
  2201. };
  2202. /**
  2203. * @private
  2204. */
  2205. GridAxis.onAfterGetTitlePosition = function (e) {
  2206. var axis = this;
  2207. var options = axis.options;
  2208. var gridOptions = options.grid || {};
  2209. if (gridOptions.enabled === true) {
  2210. // compute anchor points for each of the title align options
  2211. var title = axis.axisTitle,
  2212. axisHeight = axis.height,
  2213. horiz = axis.horiz,
  2214. axisLeft = axis.left,
  2215. offset = axis.offset,
  2216. opposite = axis.opposite,
  2217. _a = axis.options.title,
  2218. axisTitleOptions = _a === void 0 ? {} : _a,
  2219. axisTop = axis.top,
  2220. axisWidth = axis.width;
  2221. var tickSize = axis.tickSize();
  2222. var titleWidth = title && title.getBBox().width;
  2223. var xOption = axisTitleOptions.x || 0;
  2224. var yOption = axisTitleOptions.y || 0;
  2225. var titleMargin = pick(axisTitleOptions.margin,
  2226. horiz ? 5 : 10);
  2227. var titleFontSize = axis.chart.renderer.fontMetrics(axisTitleOptions.style &&
  2228. axisTitleOptions.style.fontSize,
  2229. title).f;
  2230. var crispCorr = tickSize ? tickSize[0] / 2 : 0;
  2231. // TODO account for alignment
  2232. // the position in the perpendicular direction of the axis
  2233. var offAxis = ((horiz ? axisTop + axisHeight : axisLeft) +
  2234. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  2235. (opposite ? -1 : 1) * // so does opposite axes
  2236. crispCorr +
  2237. (axis.side === GridAxis.Side.bottom ? titleFontSize : 0));
  2238. e.titlePosition.x = horiz ?
  2239. axisLeft - titleWidth / 2 - titleMargin + xOption :
  2240. offAxis + (opposite ? axisWidth : 0) + offset + xOption;
  2241. e.titlePosition.y = horiz ?
  2242. (offAxis -
  2243. (opposite ? axisHeight : 0) +
  2244. (opposite ? titleFontSize : -titleFontSize) / 2 +
  2245. offset +
  2246. yOption) :
  2247. axisTop - titleMargin + yOption;
  2248. }
  2249. };
  2250. /**
  2251. * @private
  2252. */
  2253. GridAxis.onAfterInit = function () {
  2254. var axis = this;
  2255. var chart = axis.chart,
  2256. _a = axis.options.grid,
  2257. gridOptions = _a === void 0 ? {} : _a,
  2258. userOptions = axis.userOptions;
  2259. if (gridOptions.enabled) {
  2260. applyGridOptions(axis);
  2261. /* eslint-disable no-invalid-this */
  2262. // TODO: wrap the axis instead
  2263. wrap(axis, 'labelFormatter', function (proceed) {
  2264. var _a = this,
  2265. axis = _a.axis,
  2266. value = _a.value;
  2267. var tickPos = axis.tickPositions;
  2268. var series = (axis.isLinked ?
  2269. axis.linkedParent :
  2270. axis).series[0];
  2271. var isFirst = value === tickPos[0];
  2272. var isLast = value === tickPos[tickPos.length - 1];
  2273. var point = series && find(series.options.data,
  2274. function (p) {
  2275. return p[axis.isXAxis ? 'x' : 'y'] === value;
  2276. });
  2277. var pointCopy;
  2278. if (point && series.is('gantt')) {
  2279. // For the Gantt set point aliases to the pointCopy
  2280. // to do not change the original point
  2281. pointCopy = merge(point);
  2282. H.seriesTypes.gantt.prototype.pointClass.setGanttPointAliases(pointCopy);
  2283. }
  2284. // Make additional properties available for the
  2285. // formatter
  2286. this.isFirst = isFirst;
  2287. this.isLast = isLast;
  2288. this.point = pointCopy;
  2289. // Call original labelFormatter
  2290. return proceed.call(this);
  2291. });
  2292. /* eslint-enable no-invalid-this */
  2293. }
  2294. if (gridOptions.columns) {
  2295. var columns = axis.grid.columns = [],
  2296. columnIndex = axis.grid.columnIndex = 0;
  2297. // Handle columns, each column is a grid axis
  2298. while (++columnIndex < gridOptions.columns.length) {
  2299. var columnOptions = merge(userOptions,
  2300. gridOptions.columns[gridOptions.columns.length - columnIndex - 1], {
  2301. linkedTo: 0,
  2302. // Force to behave like category axis
  2303. type: 'category',
  2304. // Disable by default the scrollbar on the grid axis
  2305. scrollbar: {
  2306. enabled: false
  2307. }
  2308. });
  2309. delete columnOptions.grid.columns; // Prevent recursion
  2310. var column = new Axis(axis.chart,
  2311. columnOptions);
  2312. column.grid.isColumn = true;
  2313. column.grid.columnIndex = columnIndex;
  2314. // Remove column axis from chart axes array, and place it
  2315. // in the columns array.
  2316. erase(chart.axes, column);
  2317. erase(chart[axis.coll], column);
  2318. columns.push(column);
  2319. }
  2320. }
  2321. };
  2322. /**
  2323. * Draw an extra line on the far side of the outermost axis,
  2324. * creating floor/roof/wall of a grid. And some padding.
  2325. * ```
  2326. * Make this:
  2327. * (axis.min) __________________________ (axis.max)
  2328. * | | | | |
  2329. * Into this:
  2330. * (axis.min) __________________________ (axis.max)
  2331. * ___|____|____|____|____|__
  2332. * ```
  2333. * @private
  2334. */
  2335. GridAxis.onAfterRender = function () {
  2336. var _a;
  2337. var axis = this,
  2338. grid = axis.grid,
  2339. options = axis.options,
  2340. gridOptions = options.grid || {};
  2341. if (gridOptions.enabled === true) {
  2342. // @todo acutual label padding (top, bottom, left, right)
  2343. axis.maxLabelDimensions = axis.getMaxLabelDimensions(axis.ticks, axis.tickPositions);
  2344. // Remove right wall before rendering if updating
  2345. if (axis.rightWall) {
  2346. axis.rightWall.destroy();
  2347. }
  2348. /*
  2349. Draw an extra axis line on outer axes
  2350. >
  2351. Make this: |______|______|______|___
  2352. > _________________________
  2353. Into this: |______|______|______|__|
  2354. */
  2355. if (axis.grid && axis.grid.isOuterAxis() && axis.axisLine) {
  2356. var lineWidth = options.lineWidth;
  2357. if (lineWidth) {
  2358. var linePath = axis.getLinePath(lineWidth),
  2359. startPoint = linePath[0],
  2360. endPoint = linePath[1],
  2361. // Negate distance if top or left axis
  2362. // Subtract 1px to draw the line at the end of the tick
  2363. tickLength = (axis.tickSize('tick') || [1])[0],
  2364. distance = (tickLength - 1) * ((axis.side === GridAxis.Side.top ||
  2365. axis.side === GridAxis.Side.left) ? -1 : 1);
  2366. // If axis is horizontal, reposition line path vertically
  2367. if (startPoint[0] === 'M' && endPoint[0] === 'L') {
  2368. if (axis.horiz) {
  2369. startPoint[2] += distance;
  2370. endPoint[2] += distance;
  2371. }
  2372. else {
  2373. startPoint[1] += distance;
  2374. endPoint[1] += distance;
  2375. }
  2376. }
  2377. // If it doesn't exist, add an upper and lower border
  2378. // for the vertical grid axis.
  2379. if (!axis.horiz && axis.chart.marginRight) {
  2380. var upperBorderStartPoint = startPoint, upperBorderEndPoint = ['L', axis.left, startPoint[2]], upperBorderPath = [upperBorderStartPoint, upperBorderEndPoint], lowerBorderEndPoint = ['L', axis.chart.chartWidth - axis.chart.marginRight, axis.toPixels(axis.max + axis.tickmarkOffset)], lowerBorderStartPoint = ['M', endPoint[1], axis.toPixels(axis.max + axis.tickmarkOffset)], lowerBorderPath = [lowerBorderStartPoint, lowerBorderEndPoint];
  2381. if (!axis.grid.upperBorder && axis.min % 1 !== 0) {
  2382. axis.grid.upperBorder = axis.grid.renderBorder(upperBorderPath);
  2383. }
  2384. if (axis.grid.upperBorder) {
  2385. axis.grid.upperBorder.animate({
  2386. d: upperBorderPath
  2387. });
  2388. }
  2389. if (!axis.grid.lowerBorder && axis.max % 1 !== 0) {
  2390. axis.grid.lowerBorder = axis.grid.renderBorder(lowerBorderPath);
  2391. }
  2392. if (axis.grid.lowerBorder) {
  2393. axis.grid.lowerBorder.animate({
  2394. d: lowerBorderPath
  2395. });
  2396. }
  2397. }
  2398. // Render an extra line parallel to the existing axes,
  2399. // to close the grid.
  2400. if (!axis.grid.axisLineExtra) {
  2401. axis.grid.axisLineExtra = axis.grid.renderBorder(linePath);
  2402. }
  2403. else {
  2404. axis.grid.axisLineExtra.animate({
  2405. d: linePath
  2406. });
  2407. }
  2408. // show or hide the line depending on
  2409. // options.showEmpty
  2410. axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
  2411. }
  2412. }
  2413. (grid && grid.columns || []).forEach(function (column) {
  2414. column.render();
  2415. });
  2416. // Manipulate the tick mark visibility
  2417. // based on the axis.max- allows smooth scrolling.
  2418. if (!axis.horiz && axis.chart.hasRendered && (axis.scrollbar || ((_a = axis.linkedParent) === null || _a === void 0 ? void 0 : _a.scrollbar))) {
  2419. var max = axis.max,
  2420. min = axis.min,
  2421. tickmarkOffset = axis.tickmarkOffset,
  2422. lastTick = axis.tickPositions[axis.tickPositions.length - 1],
  2423. firstTick = axis.tickPositions[0];
  2424. // Hide/show firts tick label.
  2425. if (min - firstTick > tickmarkOffset) {
  2426. axis.ticks[firstTick].label.hide();
  2427. }
  2428. else {
  2429. axis.ticks[firstTick].label.show();
  2430. }
  2431. // Hide/show last tick mark/label.
  2432. if (lastTick - max > tickmarkOffset) {
  2433. axis.ticks[lastTick].label.hide();
  2434. }
  2435. else {
  2436. axis.ticks[lastTick].label.show();
  2437. }
  2438. if (lastTick - max < tickmarkOffset && lastTick - max > 0 && axis.ticks[lastTick].isLast) {
  2439. axis.ticks[lastTick].mark.hide();
  2440. }
  2441. else if (axis.ticks[lastTick - 1]) {
  2442. axis.ticks[lastTick - 1].mark.show();
  2443. }
  2444. }
  2445. }
  2446. };
  2447. /**
  2448. * @private
  2449. */
  2450. GridAxis.onAfterSetAxisTranslation = function () {
  2451. var _a;
  2452. var axis = this;
  2453. var tickInfo = axis.tickPositions && axis.tickPositions.info;
  2454. var options = axis.options;
  2455. var gridOptions = options.grid || {};
  2456. var userLabels = axis.userOptions.labels || {};
  2457. // Fire this only for the Gantt type chart, #14868.
  2458. if (gridOptions.enabled) {
  2459. if (axis.horiz) {
  2460. axis.series.forEach(function (series) {
  2461. series.options.pointRange = 0;
  2462. });
  2463. // Lower level time ticks, like hours or minutes, represent
  2464. // points in time and not ranges. These should be aligned
  2465. // left in the grid cell by default. The same applies to
  2466. // years of higher order.
  2467. if (tickInfo &&
  2468. options.dateTimeLabelFormats &&
  2469. options.labels &&
  2470. !defined(userLabels.align) &&
  2471. (options.dateTimeLabelFormats[tickInfo.unitName].range === false ||
  2472. tickInfo.count > 1 // years
  2473. )) {
  2474. options.labels.align = 'left';
  2475. if (!defined(userLabels.x)) {
  2476. options.labels.x = 3;
  2477. }
  2478. }
  2479. }
  2480. else {
  2481. // Don't trim ticks which not in min/max range but
  2482. // they are still in the min/max plus tickInterval.
  2483. if (this.options.type !== 'treegrid' && ((_a = axis.grid) === null || _a === void 0 ? void 0 : _a.columns)) {
  2484. this.minPointOffset = this.tickInterval;
  2485. }
  2486. }
  2487. }
  2488. };
  2489. /**
  2490. * Creates a left and right wall on horizontal axes:
  2491. * - Places leftmost tick at the start of the axis, to create a left
  2492. * wall
  2493. * - Ensures that the rightmost tick is at the end of the axis, to
  2494. * create a right wall.
  2495. * @private
  2496. */
  2497. GridAxis.onAfterSetOptions = function (e) {
  2498. var options = this.options,
  2499. userOptions = e.userOptions,
  2500. gridAxisOptions,
  2501. gridOptions = ((options && isObject(options.grid)) ? options.grid : {});
  2502. if (gridOptions.enabled === true) {
  2503. // Merge the user options into default grid axis options so
  2504. // that when a user option is set, it takes presedence.
  2505. gridAxisOptions = merge(true, {
  2506. className: ('highcharts-grid-axis ' + (userOptions.className || '')),
  2507. dateTimeLabelFormats: {
  2508. hour: {
  2509. list: ['%H:%M', '%H']
  2510. },
  2511. day: {
  2512. list: ['%A, %e. %B', '%a, %e. %b', '%E']
  2513. },
  2514. week: {
  2515. list: ['Week %W', 'W%W']
  2516. },
  2517. month: {
  2518. list: ['%B', '%b', '%o']
  2519. }
  2520. },
  2521. grid: {
  2522. borderWidth: 1
  2523. },
  2524. labels: {
  2525. padding: 2,
  2526. style: {
  2527. fontSize: '13px'
  2528. }
  2529. },
  2530. margin: 0,
  2531. title: {
  2532. text: null,
  2533. reserveSpace: false,
  2534. rotation: 0
  2535. },
  2536. // In a grid axis, only allow one unit of certain types,
  2537. // for example we shouln't have one grid cell spanning
  2538. // two days.
  2539. units: [[
  2540. 'millisecond',
  2541. [1, 10, 100]
  2542. ], [
  2543. 'second',
  2544. [1, 10]
  2545. ], [
  2546. 'minute',
  2547. [1, 5, 15]
  2548. ], [
  2549. 'hour',
  2550. [1, 6]
  2551. ], [
  2552. 'day',
  2553. [1]
  2554. ], [
  2555. 'week',
  2556. [1]
  2557. ], [
  2558. 'month',
  2559. [1]
  2560. ], [
  2561. 'year',
  2562. null
  2563. ]]
  2564. }, userOptions);
  2565. // X-axis specific options
  2566. if (this.coll === 'xAxis') {
  2567. // For linked axes, tickPixelInterval is used only if
  2568. // the tickPositioner below doesn't run or returns
  2569. // undefined (like multiple years)
  2570. if (defined(userOptions.linkedTo) &&
  2571. !defined(userOptions.tickPixelInterval)) {
  2572. gridAxisOptions.tickPixelInterval = 350;
  2573. }
  2574. // For the secondary grid axis, use the primary axis'
  2575. // tick intervals and return ticks one level higher.
  2576. if (
  2577. // Check for tick pixel interval in options
  2578. !defined(userOptions.tickPixelInterval) &&
  2579. // Only for linked axes
  2580. defined(userOptions.linkedTo) &&
  2581. !defined(userOptions.tickPositioner) &&
  2582. !defined(userOptions.tickInterval)) {
  2583. gridAxisOptions.tickPositioner = function (min, max) {
  2584. var parentInfo = (this.linkedParent &&
  2585. this.linkedParent.tickPositions &&
  2586. this.linkedParent.tickPositions.info);
  2587. if (parentInfo) {
  2588. var unitIdx,
  2589. count,
  2590. unitName,
  2591. i,
  2592. units = gridAxisOptions.units,
  2593. unitRange;
  2594. for (i = 0; i < units.length; i++) {
  2595. if (units[i][0] ===
  2596. parentInfo.unitName) {
  2597. unitIdx = i;
  2598. break;
  2599. }
  2600. }
  2601. // Get the first allowed count on the next
  2602. // unit.
  2603. if (units[unitIdx + 1]) {
  2604. unitName = units[unitIdx + 1][0];
  2605. count =
  2606. (units[unitIdx + 1][1] || [1])[0];
  2607. // In case the base X axis shows years, make
  2608. // the secondary axis show ten times the
  2609. // years (#11427)
  2610. }
  2611. else if (parentInfo.unitName === 'year') {
  2612. unitName = 'year';
  2613. count = parentInfo.count * 10;
  2614. }
  2615. unitRange = timeUnits[unitName];
  2616. this.tickInterval = unitRange * count;
  2617. return this.getTimeTicks({
  2618. unitRange: unitRange,
  2619. count: count,
  2620. unitName: unitName
  2621. }, min, max, this.options.startOfWeek);
  2622. }
  2623. };
  2624. }
  2625. }
  2626. // Now merge the combined options into the axis options
  2627. merge(true, this.options, gridAxisOptions);
  2628. if (this.horiz) {
  2629. /* _________________________
  2630. Make this: ___|_____|_____|_____|__|
  2631. ^ ^
  2632. _________________________
  2633. Into this: |_____|_____|_____|_____|
  2634. ^ ^ */
  2635. options.minPadding = pick(userOptions.minPadding, 0);
  2636. options.maxPadding = pick(userOptions.maxPadding, 0);
  2637. }
  2638. // If borderWidth is set, then use its value for tick and
  2639. // line width.
  2640. if (isNumber(options.grid.borderWidth)) {
  2641. options.tickWidth = options.lineWidth = gridOptions.borderWidth;
  2642. }
  2643. }
  2644. };
  2645. /**
  2646. * @private
  2647. */
  2648. GridAxis.onAfterSetOptions2 = function (e) {
  2649. var axis = this;
  2650. var userOptions = e.userOptions;
  2651. var gridOptions = userOptions && userOptions.grid || {};
  2652. var columns = gridOptions.columns;
  2653. // Add column options to the parent axis. Children has their column
  2654. // options set on init in onGridAxisAfterInit.
  2655. if (gridOptions.enabled && columns) {
  2656. merge(true, axis.options, columns[columns.length - 1]);
  2657. }
  2658. };
  2659. /**
  2660. * Handle columns and setScale.
  2661. * @private
  2662. */
  2663. GridAxis.onAfterSetScale = function () {
  2664. var axis = this;
  2665. (axis.grid.columns || []).forEach(function (column) {
  2666. column.setScale();
  2667. });
  2668. };
  2669. /**
  2670. * Draw vertical axis ticks extra long to create cell floors and roofs.
  2671. * Overrides the tickLength for vertical axes.
  2672. * @private
  2673. */
  2674. GridAxis.onAfterTickSize = function (e) {
  2675. var defaultLeftAxisOptions = Axis.defaultLeftAxisOptions;
  2676. var _a = this,
  2677. horiz = _a.horiz,
  2678. maxLabelDimensions = _a.maxLabelDimensions,
  2679. _b = _a.options.grid,
  2680. gridOptions = _b === void 0 ? {} : _b;
  2681. if (gridOptions.enabled && maxLabelDimensions) {
  2682. var labelPadding = (Math.abs(defaultLeftAxisOptions.labels.x) * 2);
  2683. var distance = horiz ?
  2684. gridOptions.cellHeight || labelPadding + maxLabelDimensions.height :
  2685. labelPadding + maxLabelDimensions.width;
  2686. if (isArray(e.tickSize)) {
  2687. e.tickSize[0] = distance;
  2688. }
  2689. else {
  2690. e.tickSize = [distance, 0];
  2691. }
  2692. }
  2693. };
  2694. /**
  2695. * @private
  2696. */
  2697. GridAxis.onDestroy = function (e) {
  2698. var grid = this.grid;
  2699. (grid.columns || []).forEach(function (column) {
  2700. column.destroy(e.keepEvents);
  2701. });
  2702. grid.columns = void 0;
  2703. };
  2704. /**
  2705. * Wraps axis init to draw cell walls on vertical axes.
  2706. * @private
  2707. */
  2708. GridAxis.onInit = function (e) {
  2709. var axis = this;
  2710. var userOptions = e.userOptions || {};
  2711. var gridOptions = userOptions.grid || {};
  2712. if (gridOptions.enabled && defined(gridOptions.borderColor)) {
  2713. userOptions.tickColor = userOptions.lineColor = gridOptions.borderColor;
  2714. }
  2715. if (!axis.grid) {
  2716. axis.grid = new GridAxisAdditions(axis);
  2717. }
  2718. };
  2719. /**
  2720. * Makes tick labels which are usually ignored in a linked axis
  2721. * displayed if they are within range of linkedParent.min.
  2722. * ```
  2723. * _____________________________
  2724. * | | | | |
  2725. * Make this: | | 2 | 3 | 4 |
  2726. * |___|_______|_______|_______|
  2727. * ^
  2728. * _____________________________
  2729. * | | | | |
  2730. * Into this: | 1 | 2 | 3 | 4 |
  2731. * |___|_______|_______|_______|
  2732. * ^
  2733. * ```
  2734. * @private
  2735. * @todo Does this function do what the drawing says? Seems to affect
  2736. * ticks and not the labels directly?
  2737. */
  2738. GridAxis.onTrimTicks = function () {
  2739. var axis = this;
  2740. var options = axis.options;
  2741. var gridOptions = options.grid || {};
  2742. var categoryAxis = axis.categories;
  2743. var tickPositions = axis.tickPositions;
  2744. var firstPos = tickPositions[0];
  2745. var lastPos = tickPositions[tickPositions.length - 1];
  2746. var linkedMin = axis.linkedParent && axis.linkedParent.min;
  2747. var linkedMax = axis.linkedParent && axis.linkedParent.max;
  2748. var min = linkedMin || axis.min;
  2749. var max = linkedMax || axis.max;
  2750. var tickInterval = axis.tickInterval;
  2751. var endMoreThanMin = (firstPos < min &&
  2752. firstPos + tickInterval > min);
  2753. var startLessThanMax = (lastPos > max &&
  2754. lastPos - tickInterval < max);
  2755. if (gridOptions.enabled === true &&
  2756. !categoryAxis &&
  2757. (axis.horiz || axis.isLinked)) {
  2758. if (endMoreThanMin && !options.startOnTick) {
  2759. tickPositions[0] = min;
  2760. }
  2761. if (startLessThanMax && !options.endOnTick) {
  2762. tickPositions[tickPositions.length - 1] = max;
  2763. }
  2764. }
  2765. };
  2766. /**
  2767. * Avoid altering tickInterval when reserving space.
  2768. * @private
  2769. */
  2770. GridAxis.wrapUnsquish = function (proceed) {
  2771. var axis = this;
  2772. var _a = axis.options.grid,
  2773. gridOptions = _a === void 0 ? {} : _a;
  2774. if (gridOptions.enabled === true && axis.categories) {
  2775. return axis.tickInterval;
  2776. }
  2777. return proceed.apply(axis, argsToArray(arguments));
  2778. };
  2779. return GridAxis;
  2780. }());
  2781. (function (GridAxis) {
  2782. /**
  2783. * Enum for which side the axis is on. Maps to axis.side.
  2784. * @private
  2785. */
  2786. var Side;
  2787. (function (Side) {
  2788. Side[Side["top"] = 0] = "top";
  2789. Side[Side["right"] = 1] = "right";
  2790. Side[Side["bottom"] = 2] = "bottom";
  2791. Side[Side["left"] = 3] = "left";
  2792. })(Side = GridAxis.Side || (GridAxis.Side = {}));
  2793. })(GridAxis || (GridAxis = {}));
  2794. GridAxis.compose(Axis);
  2795. return GridAxis;
  2796. });
  2797. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Series/Series.js'], _modules['Extensions/Stacking.js'], _modules['Core/Utilities.js']], function (Axis, Series, StackItem, U) {
  2798. /* *
  2799. *
  2800. * (c) 2009-2021 Torstein Honsi
  2801. *
  2802. * License: www.highcharts.com/license
  2803. *
  2804. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2805. *
  2806. * */
  2807. var addEvent = U.addEvent,
  2808. find = U.find,
  2809. fireEvent = U.fireEvent,
  2810. isArray = U.isArray,
  2811. isNumber = U.isNumber,
  2812. pick = U.pick;
  2813. /* eslint-disable valid-jsdoc */
  2814. /**
  2815. * Provides support for broken axes.
  2816. * @private
  2817. * @class
  2818. */
  2819. var BrokenAxisAdditions = /** @class */ (function () {
  2820. /* *
  2821. *
  2822. * Constructors
  2823. *
  2824. * */
  2825. function BrokenAxisAdditions(axis) {
  2826. this.hasBreaks = false;
  2827. this.axis = axis;
  2828. }
  2829. /* *
  2830. *
  2831. * Static Functions
  2832. *
  2833. * */
  2834. /**
  2835. * @private
  2836. */
  2837. BrokenAxisAdditions.isInBreak = function (brk, val) {
  2838. var ret,
  2839. repeat = brk.repeat || Infinity,
  2840. from = brk.from,
  2841. length = brk.to - brk.from,
  2842. test = (val >= from ?
  2843. (val - from) % repeat :
  2844. repeat - ((from - val) % repeat));
  2845. if (!brk.inclusive) {
  2846. ret = test < length && test !== 0;
  2847. }
  2848. else {
  2849. ret = test <= length;
  2850. }
  2851. return ret;
  2852. };
  2853. /**
  2854. * @private
  2855. */
  2856. BrokenAxisAdditions.lin2Val = function (val) {
  2857. var axis = this;
  2858. var brokenAxis = axis.brokenAxis;
  2859. var breakArray = brokenAxis && brokenAxis.breakArray;
  2860. if (!breakArray) {
  2861. return val;
  2862. }
  2863. var nval = val,
  2864. brk,
  2865. i;
  2866. for (i = 0; i < breakArray.length; i++) {
  2867. brk = breakArray[i];
  2868. if (brk.from >= nval) {
  2869. break;
  2870. }
  2871. else if (brk.to < nval) {
  2872. nval += brk.len;
  2873. }
  2874. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  2875. nval += brk.len;
  2876. }
  2877. }
  2878. return nval;
  2879. };
  2880. /**
  2881. * @private
  2882. */
  2883. BrokenAxisAdditions.val2Lin = function (val) {
  2884. var axis = this;
  2885. var brokenAxis = axis.brokenAxis;
  2886. var breakArray = brokenAxis && brokenAxis.breakArray;
  2887. if (!breakArray) {
  2888. return val;
  2889. }
  2890. var nval = val,
  2891. brk,
  2892. i;
  2893. for (i = 0; i < breakArray.length; i++) {
  2894. brk = breakArray[i];
  2895. if (brk.to <= val) {
  2896. nval -= brk.len;
  2897. }
  2898. else if (brk.from >= val) {
  2899. break;
  2900. }
  2901. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  2902. nval -= (val - brk.from);
  2903. break;
  2904. }
  2905. }
  2906. return nval;
  2907. };
  2908. /* *
  2909. *
  2910. * Functions
  2911. *
  2912. * */
  2913. /**
  2914. * Returns the first break found where the x is larger then break.from and
  2915. * smaller then break.to.
  2916. *
  2917. * @param {number} x
  2918. * The number which should be within a break.
  2919. *
  2920. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  2921. * The array of breaks to search within.
  2922. *
  2923. * @return {Highcharts.XAxisBreaksOptions|undefined}
  2924. * Returns the first break found that matches, returns false if no break is
  2925. * found.
  2926. */
  2927. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  2928. return find(breaks, function (b) {
  2929. return b.from < x && x < b.to;
  2930. });
  2931. };
  2932. /**
  2933. * @private
  2934. */
  2935. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  2936. var brokenAxis = this;
  2937. var axis = brokenAxis.axis;
  2938. var breaks = axis.options.breaks,
  2939. i = breaks && breaks.length,
  2940. inbrk,
  2941. keep,
  2942. ret;
  2943. if (i) {
  2944. while (i--) {
  2945. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  2946. inbrk = true;
  2947. if (!keep) {
  2948. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  2949. }
  2950. }
  2951. }
  2952. if (inbrk && testKeep) {
  2953. ret = inbrk && !keep;
  2954. }
  2955. else {
  2956. ret = inbrk;
  2957. }
  2958. }
  2959. return ret;
  2960. };
  2961. /**
  2962. * Dynamically set or unset breaks in an axis. This function in lighter than
  2963. * usin Axis.update, and it also preserves animation.
  2964. *
  2965. * @private
  2966. * @function Highcharts.Axis#setBreaks
  2967. *
  2968. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  2969. * The breaks to add. When `undefined` it removes existing breaks.
  2970. *
  2971. * @param {boolean} [redraw=true]
  2972. * Whether to redraw the chart immediately.
  2973. *
  2974. * @return {void}
  2975. */
  2976. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  2977. var brokenAxis = this;
  2978. var axis = brokenAxis.axis;
  2979. var hasBreaks = (isArray(breaks) && !!breaks.length);
  2980. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  2981. brokenAxis.hasBreaks = hasBreaks;
  2982. axis.options.breaks = axis.userOptions.breaks = breaks;
  2983. axis.forceRedraw = true; // Force recalculation in setScale
  2984. // Recalculate series related to the axis.
  2985. axis.series.forEach(function (series) {
  2986. series.isDirty = true;
  2987. });
  2988. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  2989. // Revert to prototype functions
  2990. delete axis.val2lin;
  2991. delete axis.lin2val;
  2992. }
  2993. if (hasBreaks) {
  2994. axis.userOptions.ordinal = false;
  2995. axis.lin2val = BrokenAxisAdditions.lin2Val;
  2996. axis.val2lin = BrokenAxisAdditions.val2Lin;
  2997. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  2998. // If trying to set extremes inside a break, extend min to
  2999. // after, and max to before the break ( #3857 )
  3000. if (brokenAxis.hasBreaks) {
  3001. var axisBreak,
  3002. breaks = this.options.breaks;
  3003. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  3004. newMin = axisBreak.to;
  3005. }
  3006. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  3007. newMax = axisBreak.from;
  3008. }
  3009. // If both min and max is within the same break.
  3010. if (newMax < newMin) {
  3011. newMax = newMin;
  3012. }
  3013. }
  3014. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  3015. };
  3016. axis.setAxisTranslation = function () {
  3017. Axis.prototype.setAxisTranslation.call(this);
  3018. brokenAxis.unitLength = null;
  3019. if (brokenAxis.hasBreaks) {
  3020. var breaks = axis.options.breaks || [],
  3021. // Temporary one:
  3022. breakArrayT = [],
  3023. breakArray = [],
  3024. length = 0,
  3025. inBrk,
  3026. repeat,
  3027. min = axis.userMin || axis.min,
  3028. max = axis.userMax || axis.max,
  3029. pointRangePadding = pick(axis.pointRangePadding, 0),
  3030. start,
  3031. i;
  3032. // Min & max check (#4247)
  3033. breaks.forEach(function (brk) {
  3034. repeat = brk.repeat || Infinity;
  3035. if (BrokenAxisAdditions.isInBreak(brk, min)) {
  3036. min +=
  3037. (brk.to % repeat) -
  3038. (min % repeat);
  3039. }
  3040. if (BrokenAxisAdditions.isInBreak(brk, max)) {
  3041. max -=
  3042. (max % repeat) -
  3043. (brk.from % repeat);
  3044. }
  3045. });
  3046. // Construct an array holding all breaks in the axis
  3047. breaks.forEach(function (brk) {
  3048. start = brk.from;
  3049. repeat = brk.repeat || Infinity;
  3050. while (start - repeat > min) {
  3051. start -= repeat;
  3052. }
  3053. while (start < min) {
  3054. start += repeat;
  3055. }
  3056. for (i = start; i < max; i += repeat) {
  3057. breakArrayT.push({
  3058. value: i,
  3059. move: 'in'
  3060. });
  3061. breakArrayT.push({
  3062. value: i + (brk.to - brk.from),
  3063. move: 'out',
  3064. size: brk.breakSize
  3065. });
  3066. }
  3067. });
  3068. breakArrayT.sort(function (a, b) {
  3069. return ((a.value === b.value) ?
  3070. ((a.move === 'in' ? 0 : 1) -
  3071. (b.move === 'in' ? 0 : 1)) :
  3072. a.value - b.value);
  3073. });
  3074. // Simplify the breaks
  3075. inBrk = 0;
  3076. start = min;
  3077. breakArrayT.forEach(function (brk) {
  3078. inBrk += (brk.move === 'in' ? 1 : -1);
  3079. if (inBrk === 1 && brk.move === 'in') {
  3080. start = brk.value;
  3081. }
  3082. if (inBrk === 0) {
  3083. breakArray.push({
  3084. from: start,
  3085. to: brk.value,
  3086. len: brk.value - start - (brk.size || 0)
  3087. });
  3088. length += brk.value - start - (brk.size || 0);
  3089. }
  3090. });
  3091. /**
  3092. * HC <= 8 backwards compatibility, used by demo samples.
  3093. * @deprecated
  3094. * @private
  3095. * @requires modules/broken-axis
  3096. */
  3097. axis.breakArray = brokenAxis.breakArray = breakArray;
  3098. // Used with staticScale, and below the actual axis length,
  3099. // when breaks are substracted.
  3100. brokenAxis.unitLength = max - min - length + pointRangePadding;
  3101. fireEvent(axis, 'afterBreaks');
  3102. if (axis.staticScale) {
  3103. axis.transA = axis.staticScale;
  3104. }
  3105. else if (brokenAxis.unitLength) {
  3106. axis.transA *=
  3107. (max - axis.min + pointRangePadding) /
  3108. brokenAxis.unitLength;
  3109. }
  3110. if (pointRangePadding) {
  3111. axis.minPixelPadding =
  3112. axis.transA * axis.minPointOffset;
  3113. }
  3114. axis.min = min;
  3115. axis.max = max;
  3116. }
  3117. };
  3118. }
  3119. if (pick(redraw, true)) {
  3120. axis.chart.redraw();
  3121. }
  3122. };
  3123. return BrokenAxisAdditions;
  3124. }());
  3125. /**
  3126. * Axis with support of broken data rows.
  3127. * @private
  3128. * @class
  3129. */
  3130. var BrokenAxis = /** @class */ (function () {
  3131. function BrokenAxis() {
  3132. }
  3133. /**
  3134. * Adds support for broken axes.
  3135. * @private
  3136. */
  3137. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  3138. AxisClass.keepProps.push('brokenAxis');
  3139. var seriesProto = Series.prototype;
  3140. /**
  3141. * @private
  3142. */
  3143. seriesProto.drawBreaks = function (axis, keys) {
  3144. var series = this,
  3145. points = series.points,
  3146. breaks,
  3147. threshold,
  3148. eventName,
  3149. y;
  3150. if (axis && // #5950
  3151. axis.brokenAxis &&
  3152. axis.brokenAxis.hasBreaks) {
  3153. var brokenAxis_1 = axis.brokenAxis;
  3154. keys.forEach(function (key) {
  3155. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  3156. threshold = axis.isXAxis ?
  3157. axis.min :
  3158. pick(series.options.threshold, axis.min);
  3159. points.forEach(function (point) {
  3160. y = pick(point['stack' + key.toUpperCase()], point[key]);
  3161. breaks.forEach(function (brk) {
  3162. if (isNumber(threshold) && isNumber(y)) {
  3163. eventName = false;
  3164. if ((threshold < brk.from && y > brk.to) ||
  3165. (threshold > brk.from && y < brk.from)) {
  3166. eventName = 'pointBreak';
  3167. }
  3168. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  3169. (threshold > brk.from && y > brk.to && y < brk.from)) {
  3170. eventName = 'pointInBreak';
  3171. }
  3172. if (eventName) {
  3173. fireEvent(axis, eventName, { point: point, brk: brk });
  3174. }
  3175. }
  3176. });
  3177. });
  3178. });
  3179. }
  3180. };
  3181. /**
  3182. * Extend getGraphPath by identifying gaps in the data so that we can
  3183. * draw a gap in the line or area. This was moved from ordinal axis
  3184. * module to broken axis module as of #5045.
  3185. *
  3186. * @private
  3187. * @function Highcharts.Series#gappedPath
  3188. *
  3189. * @return {Highcharts.SVGPathArray}
  3190. * Gapped path
  3191. */
  3192. seriesProto.gappedPath = function () {
  3193. var currentDataGrouping = this.currentDataGrouping,
  3194. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  3195. gapSize = this.options.gapSize,
  3196. points = this.points.slice(),
  3197. i = points.length - 1,
  3198. yAxis = this.yAxis,
  3199. stack;
  3200. /**
  3201. * Defines when to display a gap in the graph, together with the
  3202. * [gapUnit](plotOptions.series.gapUnit) option.
  3203. *
  3204. * In case when `dataGrouping` is enabled, points can be grouped
  3205. * into a larger time span. This can make the grouped points to have
  3206. * a greater distance than the absolute value of `gapSize` property,
  3207. * which will result in disappearing graph completely. To prevent
  3208. * this situation the mentioned distance between grouped points is
  3209. * used instead of previously defined `gapSize`.
  3210. *
  3211. * In practice, this option is most often used to visualize gaps in
  3212. * time series. In a stock chart, intraday data is available for
  3213. * daytime hours, while gaps will appear in nights and weekends.
  3214. *
  3215. * @see [gapUnit](plotOptions.series.gapUnit)
  3216. * @see [xAxis.breaks](#xAxis.breaks)
  3217. *
  3218. * @sample {highstock} stock/plotoptions/series-gapsize/
  3219. * Setting the gap size to 2 introduces gaps for weekends
  3220. * in daily datasets.
  3221. *
  3222. * @type {number}
  3223. * @default 0
  3224. * @product highstock
  3225. * @requires modules/broken-axis
  3226. * @apioption plotOptions.series.gapSize
  3227. */
  3228. /**
  3229. * Together with [gapSize](plotOptions.series.gapSize), this option
  3230. * defines where to draw gaps in the graph.
  3231. *
  3232. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  3233. * means that if the distance between two points is greater than
  3234. * 5 times that of the two closest points, the graph will be broken.
  3235. *
  3236. * When the `gapUnit` is `"value"`, the gap is based on absolute
  3237. * axis values, which on a datetime axis is milliseconds. This also
  3238. * applies to the navigator series that inherits gap options from
  3239. * the base series.
  3240. *
  3241. * @see [gapSize](plotOptions.series.gapSize)
  3242. *
  3243. * @type {string}
  3244. * @default relative
  3245. * @since 5.0.13
  3246. * @product highstock
  3247. * @validvalue ["relative", "value"]
  3248. * @requires modules/broken-axis
  3249. * @apioption plotOptions.series.gapUnit
  3250. */
  3251. if (gapSize && i > 0) { // #5008
  3252. // Gap unit is relative
  3253. if (this.options.gapUnit !== 'value') {
  3254. gapSize *= this.basePointRange;
  3255. }
  3256. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  3257. if (groupingSize &&
  3258. groupingSize > gapSize &&
  3259. // Except when DG is forced (e.g. from other series)
  3260. // and has lower granularity than actual points (#11351)
  3261. groupingSize >= this.basePointRange) {
  3262. gapSize = groupingSize;
  3263. }
  3264. // extension for ordinal breaks
  3265. var current = void 0,
  3266. next = void 0;
  3267. while (i--) {
  3268. // Reassign next if it is not visible
  3269. if (!(next && next.visible !== false)) {
  3270. next = points[i + 1];
  3271. }
  3272. current = points[i];
  3273. // Skip iteration if one of the points is not visible
  3274. if (next.visible === false || current.visible === false) {
  3275. continue;
  3276. }
  3277. if (next.x - current.x > gapSize) {
  3278. var xRange = (current.x + next.x) / 2;
  3279. points.splice(// insert after this one
  3280. i + 1, 0, {
  3281. isNull: true,
  3282. x: xRange
  3283. });
  3284. // For stacked chart generate empty stack items, #6546
  3285. if (yAxis.stacking && this.options.stacking) {
  3286. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  3287. new StackItem(yAxis, yAxis.options
  3288. .stackLabels, false, xRange, this.stack);
  3289. stack.total = 0;
  3290. }
  3291. }
  3292. // Assign current to next for the upcoming iteration
  3293. next = current;
  3294. }
  3295. }
  3296. // Call base method
  3297. return this.getGraphPath(points);
  3298. };
  3299. /* eslint-disable no-invalid-this */
  3300. addEvent(AxisClass, 'init', function () {
  3301. var axis = this;
  3302. if (!axis.brokenAxis) {
  3303. axis.brokenAxis = new BrokenAxisAdditions(axis);
  3304. }
  3305. });
  3306. addEvent(AxisClass, 'afterInit', function () {
  3307. if (typeof this.brokenAxis !== 'undefined') {
  3308. this.brokenAxis.setBreaks(this.options.breaks, false);
  3309. }
  3310. });
  3311. addEvent(AxisClass, 'afterSetTickPositions', function () {
  3312. var axis = this;
  3313. var brokenAxis = axis.brokenAxis;
  3314. if (brokenAxis &&
  3315. brokenAxis.hasBreaks) {
  3316. var tickPositions = this.tickPositions,
  3317. info = this.tickPositions.info,
  3318. newPositions = [],
  3319. i;
  3320. for (i = 0; i < tickPositions.length; i++) {
  3321. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  3322. newPositions.push(tickPositions[i]);
  3323. }
  3324. }
  3325. this.tickPositions = newPositions;
  3326. this.tickPositions.info = info;
  3327. }
  3328. });
  3329. // Force Axis to be not-ordinal when breaks are defined
  3330. addEvent(AxisClass, 'afterSetOptions', function () {
  3331. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  3332. this.options.ordinal = false;
  3333. }
  3334. });
  3335. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  3336. var _a = this,
  3337. isDirty = _a.isDirty,
  3338. connectNulls = _a.options.connectNulls,
  3339. points = _a.points,
  3340. xAxis = _a.xAxis,
  3341. yAxis = _a.yAxis;
  3342. // Set, or reset visibility of the points. Axis.setBreaks marks the
  3343. // series as isDirty
  3344. if (isDirty) {
  3345. var i = points.length;
  3346. while (i--) {
  3347. var point = points[i];
  3348. // Respect nulls inside the break (#4275)
  3349. var nullGap = point.y === null && connectNulls === false;
  3350. var isPointInBreak = (!nullGap && ((xAxis &&
  3351. xAxis.brokenAxis &&
  3352. xAxis.brokenAxis.isInAnyBreak(point.x,
  3353. true)) || (yAxis &&
  3354. yAxis.brokenAxis &&
  3355. yAxis.brokenAxis.isInAnyBreak(point.y,
  3356. true))));
  3357. // Set point.visible if in any break.
  3358. // If not in break, reset visible to original value.
  3359. point.visible = isPointInBreak ?
  3360. false :
  3361. point.options.visible !== false;
  3362. }
  3363. }
  3364. });
  3365. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  3366. this.drawBreaks(this.xAxis, ['x']);
  3367. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  3368. });
  3369. };
  3370. return BrokenAxis;
  3371. }());
  3372. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  3373. return BrokenAxis;
  3374. });
  3375. _registerModule(_modules, 'Core/Axis/TreeGridAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Axis/Tick.js'], _modules['Gantt/Tree.js'], _modules['Core/Axis/TreeGridTick.js'], _modules['Mixins/TreeSeries.js'], _modules['Core/Utilities.js']], function (Axis, Tick, Tree, TreeGridTick, mixinTreeSeries, U) {
  3376. /* *
  3377. *
  3378. * (c) 2016 Highsoft AS
  3379. * Authors: Jon Arild Nygard
  3380. *
  3381. * License: www.highcharts.com/license
  3382. *
  3383. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3384. *
  3385. * */
  3386. var getLevelOptions = mixinTreeSeries.getLevelOptions;
  3387. var addEvent = U.addEvent,
  3388. find = U.find,
  3389. fireEvent = U.fireEvent,
  3390. isArray = U.isArray,
  3391. isNumber = U.isNumber,
  3392. isObject = U.isObject,
  3393. isString = U.isString,
  3394. merge = U.merge,
  3395. pick = U.pick,
  3396. wrap = U.wrap;
  3397. /**
  3398. * @private
  3399. */
  3400. var TreeGridAxis;
  3401. (function (TreeGridAxis) {
  3402. /* *
  3403. *
  3404. * Interfaces
  3405. *
  3406. * */
  3407. /* *
  3408. *
  3409. * Variables
  3410. *
  3411. * */
  3412. var applied = false;
  3413. /* *
  3414. *
  3415. * Functions
  3416. *
  3417. * */
  3418. /**
  3419. * @private
  3420. */
  3421. function compose(AxisClass) {
  3422. if (!applied) {
  3423. wrap(AxisClass.prototype, 'generateTick', wrapGenerateTick);
  3424. wrap(AxisClass.prototype, 'getMaxLabelDimensions', wrapGetMaxLabelDimensions);
  3425. wrap(AxisClass.prototype, 'init', wrapInit);
  3426. wrap(AxisClass.prototype, 'setTickInterval', wrapSetTickInterval);
  3427. TreeGridTick.compose(Tick);
  3428. applied = true;
  3429. }
  3430. }
  3431. TreeGridAxis.compose = compose;
  3432. /**
  3433. * @private
  3434. */
  3435. function getBreakFromNode(node, max) {
  3436. var from = node.collapseStart || 0,
  3437. to = node.collapseEnd || 0;
  3438. // In broken-axis, the axis.max is minimized until it is not within a
  3439. // break. Therefore, if break.to is larger than axis.max, the axis.to
  3440. // should not add the 0.5 axis.tickMarkOffset, to avoid adding a break
  3441. // larger than axis.max.
  3442. // TODO consider simplifying broken-axis and this might solve itself
  3443. if (to >= max) {
  3444. from -= 0.5;
  3445. }
  3446. return {
  3447. from: from,
  3448. to: to,
  3449. showPoints: false
  3450. };
  3451. }
  3452. /**
  3453. * Creates a tree structure of the data, and the treegrid. Calculates
  3454. * categories, and y-values of points based on the tree.
  3455. *
  3456. * @private
  3457. * @function getTreeGridFromData
  3458. *
  3459. * @param {Array<Highcharts.GanttPointOptions>} data
  3460. * All the data points to display in the axis.
  3461. *
  3462. * @param {boolean} uniqueNames
  3463. * Wether or not the data node with the same name should share grid cell. If
  3464. * true they do share cell. False by default.
  3465. *
  3466. * @param {number} numberOfSeries
  3467. *
  3468. * @return {object}
  3469. * Returns an object containing categories, mapOfIdToNode,
  3470. * mapOfPosToGridNode, and tree.
  3471. *
  3472. * @todo There should be only one point per line.
  3473. * @todo It should be optional to have one category per point, or merge
  3474. * cells
  3475. * @todo Add unit-tests.
  3476. */
  3477. function getTreeGridFromData(data, uniqueNames, numberOfSeries) {
  3478. var categories = [],
  3479. collapsedNodes = [],
  3480. mapOfIdToNode = {},
  3481. mapOfPosToGridNode = {},
  3482. posIterator = -1,
  3483. uniqueNamesEnabled = typeof uniqueNames === 'boolean' ? uniqueNames : false,
  3484. tree;
  3485. // Build the tree from the series data.
  3486. var treeParams = {
  3487. // After the children has been created.
  3488. after: function (node) {
  3489. var gridNode = mapOfPosToGridNode[node.pos],
  3490. height = 0,
  3491. descendants = 0;
  3492. gridNode.children.forEach(function (child) {
  3493. descendants += (child.descendants || 0) + 1;
  3494. height = Math.max((child.height || 0) + 1, height);
  3495. });
  3496. gridNode.descendants = descendants;
  3497. gridNode.height = height;
  3498. if (gridNode.collapsed) {
  3499. collapsedNodes.push(gridNode);
  3500. }
  3501. },
  3502. // Before the children has been created.
  3503. before: function (node) {
  3504. var data = isObject(node.data,
  3505. true) ? node.data : {},
  3506. name = isString(data.name) ? data.name : '',
  3507. parentNode = mapOfIdToNode[node.parent],
  3508. parentGridNode = (isObject(parentNode,
  3509. true) ?
  3510. mapOfPosToGridNode[parentNode.pos] :
  3511. null),
  3512. hasSameName = function (x) {
  3513. return x.name === name;
  3514. }, gridNode, pos;
  3515. // If not unique names, look for sibling node with the same name
  3516. if (uniqueNamesEnabled &&
  3517. isObject(parentGridNode, true) &&
  3518. !!(gridNode = find(parentGridNode.children, hasSameName))) {
  3519. // If there is a gridNode with the same name, reuse position
  3520. pos = gridNode.pos;
  3521. // Add data node to list of nodes in the grid node.
  3522. gridNode.nodes.push(node);
  3523. }
  3524. else {
  3525. // If it is a new grid node, increment position.
  3526. pos = posIterator++;
  3527. }
  3528. // Add new grid node to map.
  3529. if (!mapOfPosToGridNode[pos]) {
  3530. mapOfPosToGridNode[pos] = gridNode = {
  3531. depth: parentGridNode ? parentGridNode.depth + 1 : 0,
  3532. name: name,
  3533. id: data.id,
  3534. nodes: [node],
  3535. children: [],
  3536. pos: pos
  3537. };
  3538. // If not root, then add name to categories.
  3539. if (pos !== -1) {
  3540. categories.push(name);
  3541. }
  3542. // Add name to list of children.
  3543. if (isObject(parentGridNode, true)) {
  3544. parentGridNode.children.push(gridNode);
  3545. }
  3546. }
  3547. // Add data node to map
  3548. if (isString(node.id)) {
  3549. mapOfIdToNode[node.id] = node;
  3550. }
  3551. // If one of the points are collapsed, then start the grid node
  3552. // in collapsed state.
  3553. if (gridNode &&
  3554. data.collapsed === true) {
  3555. gridNode.collapsed = true;
  3556. }
  3557. // Assign pos to data node
  3558. node.pos = pos;
  3559. }
  3560. };
  3561. var updateYValuesAndTickPos = function (map,
  3562. numberOfSeries) {
  3563. var setValues = function (gridNode,
  3564. start,
  3565. result) {
  3566. var nodes = gridNode.nodes,
  3567. end = start + (start === -1 ? 0 : numberOfSeries - 1),
  3568. diff = (end - start) / 2,
  3569. padding = 0.5,
  3570. pos = start + diff;
  3571. nodes.forEach(function (node) {
  3572. var data = node.data;
  3573. if (isObject(data, true)) {
  3574. // Update point
  3575. data.y = start + (data.seriesIndex || 0);
  3576. // Remove the property once used
  3577. delete data.seriesIndex;
  3578. }
  3579. node.pos = pos;
  3580. });
  3581. result[pos] = gridNode;
  3582. gridNode.pos = pos;
  3583. gridNode.tickmarkOffset = diff + padding;
  3584. gridNode.collapseStart = end + padding;
  3585. gridNode.children.forEach(function (child) {
  3586. setValues(child, end + 1, result);
  3587. end = (child.collapseEnd || 0) - padding;
  3588. });
  3589. // Set collapseEnd to the end of the last child node.
  3590. gridNode.collapseEnd = end + padding;
  3591. return result;
  3592. };
  3593. return setValues(map['-1'], -1, {});
  3594. };
  3595. // Create tree from data
  3596. tree = Tree.getTree(data, treeParams);
  3597. // Update y values of data, and set calculate tick positions.
  3598. mapOfPosToGridNode = updateYValuesAndTickPos(mapOfPosToGridNode, numberOfSeries);
  3599. // Return the resulting data.
  3600. return {
  3601. categories: categories,
  3602. mapOfIdToNode: mapOfIdToNode,
  3603. mapOfPosToGridNode: mapOfPosToGridNode,
  3604. collapsedNodes: collapsedNodes,
  3605. tree: tree
  3606. };
  3607. }
  3608. /**
  3609. * Builds the tree of categories and calculates its positions.
  3610. * @private
  3611. * @param {object} e Event object
  3612. * @param {object} e.target The chart instance which the event was fired on.
  3613. * @param {object[]} e.target.axes The axes of the chart.
  3614. */
  3615. function onBeforeRender(e) {
  3616. var chart = e.target,
  3617. axes = chart.axes;
  3618. axes.filter(function (axis) {
  3619. return axis.options.type === 'treegrid';
  3620. }).forEach(function (axis) {
  3621. var options = axis.options || {},
  3622. labelOptions = options.labels,
  3623. uniqueNames = options.uniqueNames,
  3624. numberOfSeries = 0,
  3625. isDirty,
  3626. data,
  3627. treeGrid,
  3628. max = options.max;
  3629. // Check whether any of series is rendering for the first time,
  3630. // visibility has changed, or its data is dirty,
  3631. // and only then update. #10570, #10580
  3632. // Also check if mapOfPosToGridNode exists. #10887
  3633. isDirty = (!axis.treeGrid.mapOfPosToGridNode ||
  3634. axis.series.some(function (series) {
  3635. return !series.hasRendered ||
  3636. series.isDirtyData ||
  3637. series.isDirty;
  3638. }));
  3639. if (isDirty) {
  3640. // Concatenate data from all series assigned to this axis.
  3641. data = axis.series.reduce(function (arr, s) {
  3642. if (s.visible) {
  3643. // Push all data to array
  3644. (s.options.data || []).forEach(function (data) {
  3645. // For using keys - rebuild the data structure
  3646. if (s.options.keys && s.options.keys.length) {
  3647. data = s.pointClass.prototype.optionsToObject.call({ series: s }, data);
  3648. s.pointClass.setGanttPointAliases(data);
  3649. }
  3650. if (isObject(data, true)) {
  3651. // Set series index on data. Removed again
  3652. // after use.
  3653. data.seriesIndex = numberOfSeries;
  3654. arr.push(data);
  3655. }
  3656. });
  3657. // Increment series index
  3658. if (uniqueNames === true) {
  3659. numberOfSeries++;
  3660. }
  3661. }
  3662. return arr;
  3663. }, []);
  3664. // If max is higher than set data - add a
  3665. // dummy data to render categories #10779
  3666. if (max && data.length < max) {
  3667. for (var i = data.length; i <= max; i++) {
  3668. data.push({
  3669. // Use the zero-width character
  3670. // to avoid conflict with uniqueNames
  3671. name: i + '\u200B'
  3672. });
  3673. }
  3674. }
  3675. // setScale is fired after all the series is initialized,
  3676. // which is an ideal time to update the axis.categories.
  3677. treeGrid = getTreeGridFromData(data, uniqueNames || false, (uniqueNames === true) ? numberOfSeries : 1);
  3678. // Assign values to the axis.
  3679. axis.categories = treeGrid.categories;
  3680. axis.treeGrid.mapOfPosToGridNode = treeGrid.mapOfPosToGridNode;
  3681. axis.hasNames = true;
  3682. axis.treeGrid.tree = treeGrid.tree;
  3683. // Update yData now that we have calculated the y values
  3684. axis.series.forEach(function (series) {
  3685. var axisData = (series.options.data || []).map(function (d) {
  3686. if (isArray(d) && series.options.keys && series.options.keys.length) {
  3687. // Get the axisData from the data array used to
  3688. // build the treeGrid where has been modified
  3689. data.forEach(function (point) {
  3690. if (d.indexOf(point.x) >= 0 && d.indexOf(point.x2) >= 0) {
  3691. d = point;
  3692. }
  3693. });
  3694. }
  3695. return isObject(d, true) ? merge(d) : d;
  3696. });
  3697. // Avoid destroying points when series is not visible
  3698. if (series.visible) {
  3699. series.setData(axisData, false);
  3700. }
  3701. });
  3702. // Calculate the label options for each level in the tree.
  3703. axis.treeGrid.mapOptionsToLevel =
  3704. getLevelOptions({
  3705. defaults: labelOptions,
  3706. from: 1,
  3707. levels: labelOptions && labelOptions.levels,
  3708. to: axis.treeGrid.tree && axis.treeGrid.tree.height
  3709. });
  3710. // Setting initial collapsed nodes
  3711. if (e.type === 'beforeRender') {
  3712. axis.treeGrid.collapsedNodes = treeGrid.collapsedNodes;
  3713. }
  3714. }
  3715. });
  3716. }
  3717. /**
  3718. * Generates a tick for initial positioning.
  3719. *
  3720. * @private
  3721. * @function Highcharts.GridAxis#generateTick
  3722. *
  3723. * @param {Function} proceed
  3724. * The original generateTick function.
  3725. *
  3726. * @param {number} pos
  3727. * The tick position in axis values.
  3728. */
  3729. function wrapGenerateTick(proceed, pos) {
  3730. var axis = this,
  3731. mapOptionsToLevel = axis.treeGrid.mapOptionsToLevel || {},
  3732. isTreeGrid = axis.options.type === 'treegrid',
  3733. ticks = axis.ticks;
  3734. var tick = ticks[pos],
  3735. levelOptions,
  3736. options,
  3737. gridNode;
  3738. if (isTreeGrid &&
  3739. axis.treeGrid.mapOfPosToGridNode) {
  3740. gridNode = axis.treeGrid.mapOfPosToGridNode[pos];
  3741. levelOptions = mapOptionsToLevel[gridNode.depth];
  3742. if (levelOptions) {
  3743. options = {
  3744. labels: levelOptions
  3745. };
  3746. }
  3747. if (!tick) {
  3748. ticks[pos] = tick =
  3749. new Tick(axis, pos, void 0, void 0, {
  3750. category: gridNode.name,
  3751. tickmarkOffset: gridNode.tickmarkOffset,
  3752. options: options
  3753. });
  3754. }
  3755. else {
  3756. // update labels depending on tick interval
  3757. tick.parameters.category = gridNode.name;
  3758. tick.options = options;
  3759. tick.addLabel();
  3760. }
  3761. }
  3762. else {
  3763. proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
  3764. }
  3765. }
  3766. /**
  3767. * Override to add indentation to axis.maxLabelDimensions.
  3768. *
  3769. * @private
  3770. * @function Highcharts.GridAxis#getMaxLabelDimensions
  3771. *
  3772. * @param {Function} proceed
  3773. * The original function
  3774. */
  3775. function wrapGetMaxLabelDimensions(proceed) {
  3776. var axis = this,
  3777. options = axis.options,
  3778. labelOptions = options && options.labels,
  3779. indentation = (labelOptions && isNumber(labelOptions.indentation) ?
  3780. labelOptions.indentation :
  3781. 0),
  3782. retVal = proceed.apply(axis,
  3783. Array.prototype.slice.call(arguments, 1)),
  3784. isTreeGrid = axis.options.type === 'treegrid';
  3785. var treeDepth;
  3786. if (isTreeGrid && axis.treeGrid.mapOfPosToGridNode) {
  3787. treeDepth = axis.treeGrid.mapOfPosToGridNode[-1].height || 0;
  3788. retVal.width += indentation * (treeDepth - 1);
  3789. }
  3790. return retVal;
  3791. }
  3792. /**
  3793. * @private
  3794. */
  3795. function wrapInit(proceed, chart, userOptions) {
  3796. var axis = this,
  3797. isTreeGrid = userOptions.type === 'treegrid';
  3798. if (!axis.treeGrid) {
  3799. axis.treeGrid = new Additions(axis);
  3800. }
  3801. // Set default and forced options for TreeGrid
  3802. if (isTreeGrid) {
  3803. // Add event for updating the categories of a treegrid.
  3804. // NOTE Preferably these events should be set on the axis.
  3805. addEvent(chart, 'beforeRender', onBeforeRender);
  3806. addEvent(chart, 'beforeRedraw', onBeforeRender);
  3807. // Add new collapsed nodes on addseries
  3808. addEvent(chart, 'addSeries', function (e) {
  3809. if (e.options.data) {
  3810. var treeGrid = getTreeGridFromData(e.options.data,
  3811. userOptions.uniqueNames || false, 1);
  3812. axis.treeGrid.collapsedNodes = (axis.treeGrid.collapsedNodes || []).concat(treeGrid.collapsedNodes);
  3813. }
  3814. });
  3815. // Collapse all nodes in axis.treegrid.collapsednodes
  3816. // where collapsed equals true.
  3817. addEvent(axis, 'foundExtremes', function () {
  3818. if (axis.treeGrid.collapsedNodes) {
  3819. axis.treeGrid.collapsedNodes.forEach(function (node) {
  3820. var breaks = axis.treeGrid.collapse(node);
  3821. if (axis.brokenAxis) {
  3822. axis.brokenAxis.setBreaks(breaks, false);
  3823. // remove the node from the axis collapsedNodes
  3824. if (axis.treeGrid.collapsedNodes) {
  3825. axis.treeGrid.collapsedNodes = axis.treeGrid.collapsedNodes.filter(function (n) {
  3826. return node.collapseStart !== n.collapseStart ||
  3827. node.collapseEnd !== n.collapseEnd;
  3828. });
  3829. }
  3830. }
  3831. });
  3832. }
  3833. });
  3834. // If staticScale is not defined on the yAxis
  3835. // and chart height is set, set axis.isDirty
  3836. // to ensure collapsing works (#12012)
  3837. addEvent(axis, 'afterBreaks', function () {
  3838. var _a;
  3839. if (axis.coll === 'yAxis' && !axis.staticScale && ((_a = axis.chart.options.chart) === null || _a === void 0 ? void 0 : _a.height)) {
  3840. axis.isDirty = true;
  3841. }
  3842. });
  3843. userOptions = merge({
  3844. // Default options
  3845. grid: {
  3846. enabled: true
  3847. },
  3848. // TODO: add support for align in treegrid.
  3849. labels: {
  3850. align: 'left',
  3851. /**
  3852. * Set options on specific levels in a tree grid axis. Takes
  3853. * precedence over labels options.
  3854. *
  3855. * @sample {gantt} gantt/treegrid-axis/labels-levels
  3856. * Levels on TreeGrid Labels
  3857. *
  3858. * @type {Array<*>}
  3859. * @product gantt
  3860. * @apioption yAxis.labels.levels
  3861. *
  3862. * @private
  3863. */
  3864. levels: [{
  3865. /**
  3866. * Specify the level which the options within this object
  3867. * applies to.
  3868. *
  3869. * @type {number}
  3870. * @product gantt
  3871. * @apioption yAxis.labels.levels.level
  3872. *
  3873. * @private
  3874. */
  3875. level: void 0
  3876. }, {
  3877. level: 1,
  3878. /**
  3879. * @type {Highcharts.CSSObject}
  3880. * @product gantt
  3881. * @apioption yAxis.labels.levels.style
  3882. *
  3883. * @private
  3884. */
  3885. style: {
  3886. /** @ignore-option */
  3887. fontWeight: 'bold'
  3888. }
  3889. }],
  3890. /**
  3891. * The symbol for the collapse and expand icon in a
  3892. * treegrid.
  3893. *
  3894. * @product gantt
  3895. * @optionparent yAxis.labels.symbol
  3896. *
  3897. * @private
  3898. */
  3899. symbol: {
  3900. /**
  3901. * The symbol type. Points to a definition function in
  3902. * the `Highcharts.Renderer.symbols` collection.
  3903. *
  3904. * @type {Highcharts.SymbolKeyValue}
  3905. *
  3906. * @private
  3907. */
  3908. type: 'triangle',
  3909. x: -5,
  3910. y: -5,
  3911. height: 10,
  3912. width: 10,
  3913. padding: 5
  3914. }
  3915. },
  3916. uniqueNames: false
  3917. }, userOptions, {
  3918. // Forced options
  3919. reversed: true,
  3920. // grid.columns is not supported in treegrid
  3921. grid: {
  3922. columns: void 0
  3923. }
  3924. });
  3925. }
  3926. // Now apply the original function with the original arguments,
  3927. // which are sliced off this function's arguments
  3928. proceed.apply(axis, [chart, userOptions]);
  3929. if (isTreeGrid) {
  3930. axis.hasNames = true;
  3931. axis.options.showLastLabel = true;
  3932. }
  3933. }
  3934. /**
  3935. * Set the tick positions, tickInterval, axis min and max.
  3936. *
  3937. * @private
  3938. * @function Highcharts.GridAxis#setTickInterval
  3939. *
  3940. * @param {Function} proceed
  3941. * The original setTickInterval function.
  3942. */
  3943. function wrapSetTickInterval(proceed) {
  3944. var axis = this,
  3945. options = axis.options,
  3946. isTreeGrid = options.type === 'treegrid';
  3947. if (isTreeGrid) {
  3948. axis.min = pick(axis.userMin, options.min, axis.dataMin);
  3949. axis.max = pick(axis.userMax, options.max, axis.dataMax);
  3950. fireEvent(axis, 'foundExtremes');
  3951. // setAxisTranslation modifies the min and max according to
  3952. // axis breaks.
  3953. axis.setAxisTranslation();
  3954. axis.tickmarkOffset = 0.5;
  3955. axis.tickInterval = 1;
  3956. axis.tickPositions = axis.treeGrid.mapOfPosToGridNode ?
  3957. axis.treeGrid.getTickPositions() :
  3958. [];
  3959. }
  3960. else {
  3961. proceed.apply(axis, Array.prototype.slice.call(arguments, 1));
  3962. }
  3963. }
  3964. /* *
  3965. *
  3966. * Classes
  3967. *
  3968. * */
  3969. /**
  3970. * @private
  3971. * @class
  3972. */
  3973. var Additions = /** @class */ (function () {
  3974. /* *
  3975. *
  3976. * Constructors
  3977. *
  3978. * */
  3979. /**
  3980. * @private
  3981. */
  3982. function Additions(axis) {
  3983. this.axis = axis;
  3984. }
  3985. /* *
  3986. *
  3987. * Functions
  3988. *
  3989. * */
  3990. /**
  3991. * Set the collapse status.
  3992. *
  3993. * @private
  3994. *
  3995. * @param {Highcharts.Axis} axis
  3996. * The axis to check against.
  3997. *
  3998. * @param {Highcharts.GridNode} node
  3999. * The node to collapse.
  4000. */
  4001. Additions.prototype.setCollapsedStatus = function (node) {
  4002. var axis = this.axis,
  4003. chart = axis.chart;
  4004. axis.series.forEach(function (series) {
  4005. var data = series.options.data;
  4006. if (node.id && data) {
  4007. var point = chart.get(node.id),
  4008. dataPoint = data[series.data.indexOf(point)];
  4009. if (point && dataPoint) {
  4010. point.collapsed = node.collapsed;
  4011. dataPoint.collapsed = node.collapsed;
  4012. }
  4013. }
  4014. });
  4015. };
  4016. /**
  4017. * Calculates the new axis breaks to collapse a node.
  4018. *
  4019. * @private
  4020. *
  4021. * @param {Highcharts.Axis} axis
  4022. * The axis to check against.
  4023. *
  4024. * @param {Highcharts.GridNode} node
  4025. * The node to collapse.
  4026. *
  4027. * @param {number} pos
  4028. * The tick position to collapse.
  4029. *
  4030. * @return {Array<object>}
  4031. * Returns an array of the new breaks for the axis.
  4032. */
  4033. Additions.prototype.collapse = function (node) {
  4034. var axis = this.axis,
  4035. breaks = (axis.options.breaks || []),
  4036. obj = getBreakFromNode(node,
  4037. axis.max);
  4038. breaks.push(obj);
  4039. // Change the collapsed flag #13838
  4040. node.collapsed = true;
  4041. axis.treeGrid.setCollapsedStatus(node);
  4042. return breaks;
  4043. };
  4044. /**
  4045. * Calculates the new axis breaks to expand a node.
  4046. *
  4047. * @private
  4048. *
  4049. * @param {Highcharts.Axis} axis
  4050. * The axis to check against.
  4051. *
  4052. * @param {Highcharts.GridNode} node
  4053. * The node to expand.
  4054. *
  4055. * @param {number} pos
  4056. * The tick position to expand.
  4057. *
  4058. * @return {Array<object>}
  4059. * Returns an array of the new breaks for the axis.
  4060. */
  4061. Additions.prototype.expand = function (node) {
  4062. var axis = this.axis,
  4063. breaks = (axis.options.breaks || []),
  4064. obj = getBreakFromNode(node,
  4065. axis.max);
  4066. // Change the collapsed flag #13838
  4067. node.collapsed = false;
  4068. axis.treeGrid.setCollapsedStatus(node);
  4069. // Remove the break from the axis breaks array.
  4070. return breaks.reduce(function (arr, b) {
  4071. if (b.to !== obj.to || b.from !== obj.from) {
  4072. arr.push(b);
  4073. }
  4074. return arr;
  4075. }, []);
  4076. };
  4077. /**
  4078. * Creates a list of positions for the ticks on the axis. Filters out
  4079. * positions that are outside min and max, or is inside an axis break.
  4080. *
  4081. * @private
  4082. *
  4083. * @return {Array<number>}
  4084. * List of positions.
  4085. */
  4086. Additions.prototype.getTickPositions = function () {
  4087. var axis = this.axis,
  4088. roundedMin = Math.floor(axis.min / axis.tickInterval) * axis.tickInterval,
  4089. roundedMax = Math.ceil(axis.max / axis.tickInterval) * axis.tickInterval;
  4090. return Object.keys(axis.treeGrid.mapOfPosToGridNode || {}).reduce(function (arr, key) {
  4091. var pos = +key;
  4092. if (pos >= roundedMin &&
  4093. pos <= roundedMax &&
  4094. !(axis.brokenAxis && axis.brokenAxis.isInAnyBreak(pos))) {
  4095. arr.push(pos);
  4096. }
  4097. return arr;
  4098. }, []);
  4099. };
  4100. /**
  4101. * Check if a node is collapsed.
  4102. *
  4103. * @private
  4104. *
  4105. * @param {Highcharts.Axis} axis
  4106. * The axis to check against.
  4107. *
  4108. * @param {object} node
  4109. * The node to check if is collapsed.
  4110. *
  4111. * @param {number} pos
  4112. * The tick position to collapse.
  4113. *
  4114. * @return {boolean}
  4115. * Returns true if collapsed, false if expanded.
  4116. */
  4117. Additions.prototype.isCollapsed = function (node) {
  4118. var axis = this.axis,
  4119. breaks = (axis.options.breaks || []),
  4120. obj = getBreakFromNode(node,
  4121. axis.max);
  4122. return breaks.some(function (b) {
  4123. return b.from === obj.from && b.to === obj.to;
  4124. });
  4125. };
  4126. /**
  4127. * Calculates the new axis breaks after toggling the collapse/expand
  4128. * state of a node. If it is collapsed it will be expanded, and if it is
  4129. * exapended it will be collapsed.
  4130. *
  4131. * @private
  4132. *
  4133. * @param {Highcharts.Axis} axis
  4134. * The axis to check against.
  4135. *
  4136. * @param {Highcharts.GridNode} node
  4137. * The node to toggle.
  4138. *
  4139. * @return {Array<object>}
  4140. * Returns an array of the new breaks for the axis.
  4141. */
  4142. Additions.prototype.toggleCollapse = function (node) {
  4143. return (this.isCollapsed(node) ?
  4144. this.expand(node) :
  4145. this.collapse(node));
  4146. };
  4147. return Additions;
  4148. }());
  4149. TreeGridAxis.Additions = Additions;
  4150. })(TreeGridAxis || (TreeGridAxis = {}));
  4151. // Make utility functions available for testing.
  4152. Axis.prototype.utils = {
  4153. getNode: Tree.getNode
  4154. };
  4155. TreeGridAxis.compose(Axis);
  4156. return TreeGridAxis;
  4157. });
  4158. _registerModule(_modules, 'Extensions/CurrentDateIndication.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Color/Palette.js'], _modules['Core/Utilities.js'], _modules['Core/Axis/PlotLineOrBand.js']], function (Axis, palette, U, PlotLineOrBand) {
  4159. /* *
  4160. *
  4161. * (c) 2016-2021 Highsoft AS
  4162. *
  4163. * Author: Lars A. V. Cabrera
  4164. *
  4165. * License: www.highcharts.com/license
  4166. *
  4167. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4168. *
  4169. * */
  4170. var addEvent = U.addEvent,
  4171. merge = U.merge,
  4172. wrap = U.wrap;
  4173. var defaultConfig = {
  4174. /**
  4175. * Show an indicator on the axis for the current date and time. Can be a
  4176. * boolean or a configuration object similar to
  4177. * [xAxis.plotLines](#xAxis.plotLines).
  4178. *
  4179. * @sample gantt/current-date-indicator/demo
  4180. * Current date indicator enabled
  4181. * @sample gantt/current-date-indicator/object-config
  4182. * Current date indicator with custom options
  4183. *
  4184. * @declare Highcharts.AxisCurrentDateIndicatorOptions
  4185. * @type {boolean|*}
  4186. * @default true
  4187. * @extends xAxis.plotLines
  4188. * @excluding value
  4189. * @product gantt
  4190. * @apioption xAxis.currentDateIndicator
  4191. */
  4192. currentDateIndicator: true,
  4193. color: palette.highlightColor20,
  4194. width: 2,
  4195. /**
  4196. * @declare Highcharts.AxisCurrentDateIndicatorLabelOptions
  4197. */
  4198. label: {
  4199. /**
  4200. * Format of the label. This options is passed as the fist argument to
  4201. * [dateFormat](/class-reference/Highcharts#dateFormat) function.
  4202. *
  4203. * @type {string}
  4204. * @default %a, %b %d %Y, %H:%M
  4205. * @product gantt
  4206. * @apioption xAxis.currentDateIndicator.label.format
  4207. */
  4208. format: '%a, %b %d %Y, %H:%M',
  4209. formatter: function (value, format) {
  4210. return this.axis.chart.time.dateFormat(format, value);
  4211. },
  4212. rotation: 0,
  4213. /**
  4214. * @type {Highcharts.CSSObject}
  4215. */
  4216. style: {
  4217. /** @internal */
  4218. fontSize: '10px'
  4219. }
  4220. }
  4221. };
  4222. /* eslint-disable no-invalid-this */
  4223. addEvent(Axis, 'afterSetOptions', function () {
  4224. var options = this.options,
  4225. cdiOptions = options.currentDateIndicator;
  4226. if (cdiOptions) {
  4227. cdiOptions = typeof cdiOptions === 'object' ?
  4228. merge(defaultConfig, cdiOptions) : merge(defaultConfig);
  4229. cdiOptions.value = new Date();
  4230. if (!options.plotLines) {
  4231. options.plotLines = [];
  4232. }
  4233. options.plotLines.push(cdiOptions);
  4234. }
  4235. });
  4236. addEvent(PlotLineOrBand, 'render', function () {
  4237. // If the label already exists, update its text
  4238. if (this.label) {
  4239. this.label.attr({
  4240. text: this.getLabelText(this.options.label)
  4241. });
  4242. }
  4243. });
  4244. wrap(PlotLineOrBand.prototype, 'getLabelText', function (defaultMethod, defaultLabelOptions) {
  4245. var options = this.options;
  4246. if (options.currentDateIndicator && options.label &&
  4247. typeof options.label.formatter === 'function') {
  4248. options.value = new Date();
  4249. return options.label.formatter
  4250. .call(this, options.value, options.label.format);
  4251. }
  4252. return defaultMethod.call(this, defaultLabelOptions);
  4253. });
  4254. });
  4255. _registerModule(_modules, 'Extensions/StaticScale.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js']], function (Axis, Chart, U) {
  4256. /* *
  4257. *
  4258. * (c) 2016-2021 Torstein Honsi, Lars Cabrera
  4259. *
  4260. * License: www.highcharts.com/license
  4261. *
  4262. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4263. *
  4264. * */
  4265. var addEvent = U.addEvent,
  4266. defined = U.defined,
  4267. isNumber = U.isNumber,
  4268. pick = U.pick;
  4269. /* eslint-disable no-invalid-this */
  4270. /**
  4271. * For vertical axes only. Setting the static scale ensures that each tick unit
  4272. * is translated into a fixed pixel height. For example, setting the static
  4273. * scale to 24 results in each Y axis category taking up 24 pixels, and the
  4274. * height of the chart adjusts. Adding or removing items will make the chart
  4275. * resize.
  4276. *
  4277. * @sample gantt/xrange-series/demo/
  4278. * X-range series with static scale
  4279. *
  4280. * @type {number}
  4281. * @default 50
  4282. * @since 6.2.0
  4283. * @product gantt
  4284. * @apioption yAxis.staticScale
  4285. */
  4286. addEvent(Axis, 'afterSetOptions', function () {
  4287. var chartOptions = this.chart.options && this.chart.options.chart;
  4288. if (!this.horiz &&
  4289. isNumber(this.options.staticScale) &&
  4290. (!chartOptions.height ||
  4291. (chartOptions.scrollablePlotArea &&
  4292. chartOptions.scrollablePlotArea.minHeight))) {
  4293. this.staticScale = this.options.staticScale;
  4294. }
  4295. });
  4296. Chart.prototype.adjustHeight = function () {
  4297. if (this.redrawTrigger !== 'adjustHeight') {
  4298. (this.axes || []).forEach(function (axis) {
  4299. var chart = axis.chart,
  4300. animate = !!chart.initiatedScale &&
  4301. chart.options.animation,
  4302. staticScale = axis.options.staticScale,
  4303. height,
  4304. diff;
  4305. if (axis.staticScale && defined(axis.min)) {
  4306. height = pick(axis.brokenAxis && axis.brokenAxis.unitLength, axis.max + axis.tickInterval - axis.min) * staticScale;
  4307. // Minimum height is 1 x staticScale.
  4308. height = Math.max(height, staticScale);
  4309. diff = height - chart.plotHeight;
  4310. if (Math.abs(diff) >= 1) {
  4311. chart.plotHeight = height;
  4312. chart.redrawTrigger = 'adjustHeight';
  4313. chart.setSize(void 0, chart.chartHeight + diff, animate);
  4314. }
  4315. // Make sure clip rects have the right height before initial
  4316. // animation.
  4317. axis.series.forEach(function (series) {
  4318. var clipRect = series.sharedClipKey &&
  4319. chart[series.sharedClipKey];
  4320. if (clipRect) {
  4321. clipRect.attr({
  4322. height: chart.plotHeight
  4323. });
  4324. }
  4325. });
  4326. }
  4327. });
  4328. this.initiatedScale = true;
  4329. }
  4330. this.redrawTrigger = null;
  4331. };
  4332. addEvent(Chart, 'render', Chart.prototype.adjustHeight);
  4333. });
  4334. _registerModule(_modules, 'Extensions/ArrowSymbols.js', [_modules['Core/Renderer/SVG/SVGRenderer.js']], function (SVGRenderer) {
  4335. /* *
  4336. *
  4337. * (c) 2017 Highsoft AS
  4338. * Authors: Lars A. V. Cabrera
  4339. *
  4340. * License: www.highcharts.com/license
  4341. *
  4342. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4343. *
  4344. * */
  4345. /**
  4346. * Creates an arrow symbol. Like a triangle, except not filled.
  4347. * ```
  4348. * o
  4349. * o
  4350. * o
  4351. * o
  4352. * o
  4353. * o
  4354. * o
  4355. * ```
  4356. *
  4357. * @private
  4358. * @function
  4359. *
  4360. * @param {number} x
  4361. * x position of the arrow
  4362. *
  4363. * @param {number} y
  4364. * y position of the arrow
  4365. *
  4366. * @param {number} w
  4367. * width of the arrow
  4368. *
  4369. * @param {number} h
  4370. * height of the arrow
  4371. *
  4372. * @return {Highcharts.SVGPathArray}
  4373. * Path array
  4374. */
  4375. SVGRenderer.prototype.symbols.arrow = function (x, y, w, h) {
  4376. return [
  4377. ['M', x, y + h / 2],
  4378. ['L', x + w, y],
  4379. ['L', x, y + h / 2],
  4380. ['L', x + w, y + h]
  4381. ];
  4382. };
  4383. /**
  4384. * Creates a half-width arrow symbol. Like a triangle, except not filled.
  4385. * ```
  4386. * o
  4387. * o
  4388. * o
  4389. * o
  4390. * o
  4391. * ```
  4392. *
  4393. * @private
  4394. * @function
  4395. *
  4396. * @param {number} x
  4397. * x position of the arrow
  4398. *
  4399. * @param {number} y
  4400. * y position of the arrow
  4401. *
  4402. * @param {number} w
  4403. * width of the arrow
  4404. *
  4405. * @param {number} h
  4406. * height of the arrow
  4407. *
  4408. * @return {Highcharts.SVGPathArray}
  4409. * Path array
  4410. */
  4411. SVGRenderer.prototype.symbols['arrow-half'] = function (x, y, w, h) {
  4412. return SVGRenderer.prototype.symbols.arrow(x, y, w / 2, h);
  4413. };
  4414. /**
  4415. * Creates a left-oriented triangle.
  4416. * ```
  4417. * o
  4418. * ooooooo
  4419. * ooooooooooooo
  4420. * ooooooo
  4421. * o
  4422. * ```
  4423. *
  4424. * @private
  4425. * @function
  4426. *
  4427. * @param {number} x
  4428. * x position of the triangle
  4429. *
  4430. * @param {number} y
  4431. * y position of the triangle
  4432. *
  4433. * @param {number} w
  4434. * width of the triangle
  4435. *
  4436. * @param {number} h
  4437. * height of the triangle
  4438. *
  4439. * @return {Highcharts.SVGPathArray}
  4440. * Path array
  4441. */
  4442. SVGRenderer.prototype.symbols['triangle-left'] = function (x, y, w, h) {
  4443. return [
  4444. ['M', x + w, y],
  4445. ['L', x, y + h / 2],
  4446. ['L', x + w, y + h],
  4447. ['Z']
  4448. ];
  4449. };
  4450. /**
  4451. * Alias function for triangle-left.
  4452. *
  4453. * @private
  4454. * @function
  4455. *
  4456. * @param {number} x
  4457. * x position of the arrow
  4458. *
  4459. * @param {number} y
  4460. * y position of the arrow
  4461. *
  4462. * @param {number} w
  4463. * width of the arrow
  4464. *
  4465. * @param {number} h
  4466. * height of the arrow
  4467. *
  4468. * @return {Highcharts.SVGPathArray}
  4469. * Path array
  4470. */
  4471. SVGRenderer.prototype.symbols['arrow-filled'] = SVGRenderer.prototype.symbols['triangle-left'];
  4472. /**
  4473. * Creates a half-width, left-oriented triangle.
  4474. * ```
  4475. * o
  4476. * oooo
  4477. * ooooooo
  4478. * oooo
  4479. * o
  4480. * ```
  4481. *
  4482. * @private
  4483. * @function
  4484. *
  4485. * @param {number} x
  4486. * x position of the triangle
  4487. *
  4488. * @param {number} y
  4489. * y position of the triangle
  4490. *
  4491. * @param {number} w
  4492. * width of the triangle
  4493. *
  4494. * @param {number} h
  4495. * height of the triangle
  4496. *
  4497. * @return {Highcharts.SVGPathArray}
  4498. * Path array
  4499. */
  4500. SVGRenderer.prototype.symbols['triangle-left-half'] = function (x, y, w, h) {
  4501. return SVGRenderer.prototype.symbols['triangle-left'](x, y, w / 2, h);
  4502. };
  4503. /**
  4504. * Alias function for triangle-left-half.
  4505. *
  4506. * @private
  4507. * @function
  4508. *
  4509. * @param {number} x
  4510. * x position of the arrow
  4511. *
  4512. * @param {number} y
  4513. * y position of the arrow
  4514. *
  4515. * @param {number} w
  4516. * width of the arrow
  4517. *
  4518. * @param {number} h
  4519. * height of the arrow
  4520. *
  4521. * @return {Highcharts.SVGPathArray}
  4522. * Path array
  4523. */
  4524. SVGRenderer.prototype.symbols['arrow-filled-half'] = SVGRenderer.prototype.symbols['triangle-left-half'];
  4525. });
  4526. _registerModule(_modules, 'Gantt/Connection.js', [_modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (H, O, Point, U) {
  4527. /* *
  4528. *
  4529. * (c) 2016 Highsoft AS
  4530. * Authors: Øystein Moseng, Lars A. V. Cabrera
  4531. *
  4532. * License: www.highcharts.com/license
  4533. *
  4534. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4535. *
  4536. * */
  4537. /**
  4538. * The default pathfinder algorithm to use for a chart. It is possible to define
  4539. * your own algorithms by adding them to the
  4540. * `Highcharts.Pathfinder.prototype.algorithms`
  4541. * object before the chart has been created.
  4542. *
  4543. * The default algorithms are as follows:
  4544. *
  4545. * `straight`: Draws a straight line between the connecting
  4546. * points. Does not avoid other points when drawing.
  4547. *
  4548. * `simpleConnect`: Finds a path between the points using right angles
  4549. * only. Takes only starting/ending points into
  4550. * account, and will not avoid other points.
  4551. *
  4552. * `fastAvoid`: Finds a path between the points using right angles
  4553. * only. Will attempt to avoid other points, but its
  4554. * focus is performance over accuracy. Works well with
  4555. * less dense datasets.
  4556. *
  4557. * @typedef {"fastAvoid"|"simpleConnect"|"straight"|string} Highcharts.PathfinderTypeValue
  4558. */
  4559. ''; // detach doclets above
  4560. var defaultOptions = O.defaultOptions;
  4561. var addEvent = U.addEvent,
  4562. defined = U.defined,
  4563. error = U.error,
  4564. extend = U.extend,
  4565. merge = U.merge,
  4566. objectEach = U.objectEach,
  4567. pick = U.pick,
  4568. splat = U.splat;
  4569. var deg2rad = H.deg2rad,
  4570. max = Math.max,
  4571. min = Math.min;
  4572. /*
  4573. @todo:
  4574. - Document how to write your own algorithms
  4575. - Consider adding a Point.pathTo method that wraps creating a connection
  4576. and rendering it
  4577. */
  4578. // Set default Pathfinder options
  4579. extend(defaultOptions, {
  4580. /**
  4581. * The Pathfinder module allows you to define connections between any two
  4582. * points, represented as lines - optionally with markers for the start
  4583. * and/or end points. Multiple algorithms are available for calculating how
  4584. * the connecting lines are drawn.
  4585. *
  4586. * Connector functionality requires Highcharts Gantt to be loaded. In Gantt
  4587. * charts, the connectors are used to draw dependencies between tasks.
  4588. *
  4589. * @see [dependency](series.gantt.data.dependency)
  4590. *
  4591. * @sample gantt/pathfinder/demo
  4592. * Pathfinder connections
  4593. *
  4594. * @declare Highcharts.ConnectorsOptions
  4595. * @product gantt
  4596. * @optionparent connectors
  4597. */
  4598. connectors: {
  4599. /**
  4600. * Enable connectors for this chart. Requires Highcharts Gantt.
  4601. *
  4602. * @type {boolean}
  4603. * @default true
  4604. * @since 6.2.0
  4605. * @apioption connectors.enabled
  4606. */
  4607. /**
  4608. * Set the default dash style for this chart's connecting lines.
  4609. *
  4610. * @type {string}
  4611. * @default solid
  4612. * @since 6.2.0
  4613. * @apioption connectors.dashStyle
  4614. */
  4615. /**
  4616. * Set the default color for this chart's Pathfinder connecting lines.
  4617. * Defaults to the color of the point being connected.
  4618. *
  4619. * @type {Highcharts.ColorString}
  4620. * @since 6.2.0
  4621. * @apioption connectors.lineColor
  4622. */
  4623. /**
  4624. * Set the default pathfinder margin to use, in pixels. Some Pathfinder
  4625. * algorithms attempt to avoid obstacles, such as other points in the
  4626. * chart. These algorithms use this margin to determine how close lines
  4627. * can be to an obstacle. The default is to compute this automatically
  4628. * from the size of the obstacles in the chart.
  4629. *
  4630. * To draw connecting lines close to existing points, set this to a low
  4631. * number. For more space around existing points, set this number
  4632. * higher.
  4633. *
  4634. * @sample gantt/pathfinder/algorithm-margin
  4635. * Small algorithmMargin
  4636. *
  4637. * @type {number}
  4638. * @since 6.2.0
  4639. * @apioption connectors.algorithmMargin
  4640. */
  4641. /**
  4642. * Set the default pathfinder algorithm to use for this chart. It is
  4643. * possible to define your own algorithms by adding them to the
  4644. * Highcharts.Pathfinder.prototype.algorithms object before the chart
  4645. * has been created.
  4646. *
  4647. * The default algorithms are as follows:
  4648. *
  4649. * `straight`: Draws a straight line between the connecting
  4650. * points. Does not avoid other points when drawing.
  4651. *
  4652. * `simpleConnect`: Finds a path between the points using right angles
  4653. * only. Takes only starting/ending points into
  4654. * account, and will not avoid other points.
  4655. *
  4656. * `fastAvoid`: Finds a path between the points using right angles
  4657. * only. Will attempt to avoid other points, but its
  4658. * focus is performance over accuracy. Works well with
  4659. * less dense datasets.
  4660. *
  4661. * Default value: `straight` is used as default for most series types,
  4662. * while `simpleConnect` is used as default for Gantt series, to show
  4663. * dependencies between points.
  4664. *
  4665. * @sample gantt/pathfinder/demo
  4666. * Different types used
  4667. *
  4668. * @type {Highcharts.PathfinderTypeValue}
  4669. * @default undefined
  4670. * @since 6.2.0
  4671. */
  4672. type: 'straight',
  4673. /**
  4674. * Set the default pixel width for this chart's Pathfinder connecting
  4675. * lines.
  4676. *
  4677. * @since 6.2.0
  4678. */
  4679. lineWidth: 1,
  4680. /**
  4681. * Marker options for this chart's Pathfinder connectors. Note that
  4682. * this option is overridden by the `startMarker` and `endMarker`
  4683. * options.
  4684. *
  4685. * @declare Highcharts.ConnectorsMarkerOptions
  4686. * @since 6.2.0
  4687. */
  4688. marker: {
  4689. /**
  4690. * Set the radius of the connector markers. The default is
  4691. * automatically computed based on the algorithmMargin setting.
  4692. *
  4693. * Setting marker.width and marker.height will override this
  4694. * setting.
  4695. *
  4696. * @type {number}
  4697. * @since 6.2.0
  4698. * @apioption connectors.marker.radius
  4699. */
  4700. /**
  4701. * Set the width of the connector markers. If not supplied, this
  4702. * is inferred from the marker radius.
  4703. *
  4704. * @type {number}
  4705. * @since 6.2.0
  4706. * @apioption connectors.marker.width
  4707. */
  4708. /**
  4709. * Set the height of the connector markers. If not supplied, this
  4710. * is inferred from the marker radius.
  4711. *
  4712. * @type {number}
  4713. * @since 6.2.0
  4714. * @apioption connectors.marker.height
  4715. */
  4716. /**
  4717. * Set the color of the connector markers. By default this is the
  4718. * same as the connector color.
  4719. *
  4720. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  4721. * @since 6.2.0
  4722. * @apioption connectors.marker.color
  4723. */
  4724. /**
  4725. * Set the line/border color of the connector markers. By default
  4726. * this is the same as the marker color.
  4727. *
  4728. * @type {Highcharts.ColorString}
  4729. * @since 6.2.0
  4730. * @apioption connectors.marker.lineColor
  4731. */
  4732. /**
  4733. * Enable markers for the connectors.
  4734. */
  4735. enabled: false,
  4736. /**
  4737. * Horizontal alignment of the markers relative to the points.
  4738. *
  4739. * @type {Highcharts.AlignValue}
  4740. */
  4741. align: 'center',
  4742. /**
  4743. * Vertical alignment of the markers relative to the points.
  4744. *
  4745. * @type {Highcharts.VerticalAlignValue}
  4746. */
  4747. verticalAlign: 'middle',
  4748. /**
  4749. * Whether or not to draw the markers inside the points.
  4750. */
  4751. inside: false,
  4752. /**
  4753. * Set the line/border width of the pathfinder markers.
  4754. */
  4755. lineWidth: 1
  4756. },
  4757. /**
  4758. * Marker options specific to the start markers for this chart's
  4759. * Pathfinder connectors. Overrides the generic marker options.
  4760. *
  4761. * @declare Highcharts.ConnectorsStartMarkerOptions
  4762. * @extends connectors.marker
  4763. * @since 6.2.0
  4764. */
  4765. startMarker: {
  4766. /**
  4767. * Set the symbol of the connector start markers.
  4768. */
  4769. symbol: 'diamond'
  4770. },
  4771. /**
  4772. * Marker options specific to the end markers for this chart's
  4773. * Pathfinder connectors. Overrides the generic marker options.
  4774. *
  4775. * @declare Highcharts.ConnectorsEndMarkerOptions
  4776. * @extends connectors.marker
  4777. * @since 6.2.0
  4778. */
  4779. endMarker: {
  4780. /**
  4781. * Set the symbol of the connector end markers.
  4782. */
  4783. symbol: 'arrow-filled'
  4784. }
  4785. }
  4786. });
  4787. /**
  4788. * Override Pathfinder connector options for a series. Requires Highcharts Gantt
  4789. * to be loaded.
  4790. *
  4791. * @declare Highcharts.SeriesConnectorsOptionsObject
  4792. * @extends connectors
  4793. * @since 6.2.0
  4794. * @excluding enabled, algorithmMargin
  4795. * @product gantt
  4796. * @apioption plotOptions.series.connectors
  4797. */
  4798. /**
  4799. * Connect to a point. This option can be either a string, referring to the ID
  4800. * of another point, or an object, or an array of either. If the option is an
  4801. * array, each element defines a connection.
  4802. *
  4803. * @sample gantt/pathfinder/demo
  4804. * Different connection types
  4805. *
  4806. * @declare Highcharts.XrangePointConnectorsOptionsObject
  4807. * @type {string|Array<string|*>|*}
  4808. * @extends plotOptions.series.connectors
  4809. * @since 6.2.0
  4810. * @excluding enabled
  4811. * @product gantt
  4812. * @requires highcharts-gantt
  4813. * @apioption series.xrange.data.connect
  4814. */
  4815. /**
  4816. * The ID of the point to connect to.
  4817. *
  4818. * @type {string}
  4819. * @since 6.2.0
  4820. * @product gantt
  4821. * @apioption series.xrange.data.connect.to
  4822. */
  4823. /**
  4824. * Get point bounding box using plotX/plotY and shapeArgs. If using
  4825. * graphic.getBBox() directly, the bbox will be affected by animation.
  4826. *
  4827. * @private
  4828. * @function
  4829. *
  4830. * @param {Highcharts.Point} point
  4831. * The point to get BB of.
  4832. *
  4833. * @return {Highcharts.Dictionary<number>|null}
  4834. * Result xMax, xMin, yMax, yMin.
  4835. */
  4836. function getPointBB(point) {
  4837. var shapeArgs = point.shapeArgs,
  4838. bb;
  4839. // Prefer using shapeArgs (columns)
  4840. if (shapeArgs) {
  4841. return {
  4842. xMin: shapeArgs.x,
  4843. xMax: shapeArgs.x + shapeArgs.width,
  4844. yMin: shapeArgs.y,
  4845. yMax: shapeArgs.y + shapeArgs.height
  4846. };
  4847. }
  4848. // Otherwise use plotX/plotY and bb
  4849. bb = point.graphic && point.graphic.getBBox();
  4850. return bb ? {
  4851. xMin: point.plotX - bb.width / 2,
  4852. xMax: point.plotX + bb.width / 2,
  4853. yMin: point.plotY - bb.height / 2,
  4854. yMax: point.plotY + bb.height / 2
  4855. } : null;
  4856. }
  4857. /**
  4858. * Calculate margin to place around obstacles for the pathfinder in pixels.
  4859. * Returns a minimum of 1 pixel margin.
  4860. *
  4861. * @private
  4862. * @function
  4863. *
  4864. * @param {Array<object>} obstacles
  4865. * Obstacles to calculate margin from.
  4866. *
  4867. * @return {number}
  4868. * The calculated margin in pixels. At least 1.
  4869. */
  4870. function calculateObstacleMargin(obstacles) {
  4871. var len = obstacles.length,
  4872. i = 0,
  4873. j,
  4874. obstacleDistance,
  4875. distances = [],
  4876. // Compute smallest distance between two rectangles
  4877. distance = function (a,
  4878. b,
  4879. bbMargin) {
  4880. // Count the distance even if we are slightly off
  4881. var margin = pick(bbMargin, 10),
  4882. yOverlap = a.yMax + margin > b.yMin - margin &&
  4883. a.yMin - margin < b.yMax + margin,
  4884. xOverlap = a.xMax + margin > b.xMin - margin &&
  4885. a.xMin - margin < b.xMax + margin,
  4886. xDistance = yOverlap ? (a.xMin > b.xMax ? a.xMin - b.xMax : b.xMin - a.xMax) : Infinity,
  4887. yDistance = xOverlap ? (a.yMin > b.yMax ? a.yMin - b.yMax : b.yMin - a.yMax) : Infinity;
  4888. // If the rectangles collide, try recomputing with smaller margin.
  4889. // If they collide anyway, discard the obstacle.
  4890. if (xOverlap && yOverlap) {
  4891. return (margin ?
  4892. distance(a, b, Math.floor(margin / 2)) :
  4893. Infinity);
  4894. }
  4895. return min(xDistance, yDistance);
  4896. };
  4897. // Go over all obstacles and compare them to the others.
  4898. for (; i < len; ++i) {
  4899. // Compare to all obstacles ahead. We will already have compared this
  4900. // obstacle to the ones before.
  4901. for (j = i + 1; j < len; ++j) {
  4902. obstacleDistance = distance(obstacles[i], obstacles[j]);
  4903. // TODO: Magic number 80
  4904. if (obstacleDistance < 80) { // Ignore large distances
  4905. distances.push(obstacleDistance);
  4906. }
  4907. }
  4908. }
  4909. // Ensure we always have at least one value, even in very spaceous charts
  4910. distances.push(80);
  4911. return max(Math.floor(distances.sort(function (a, b) {
  4912. return (a - b);
  4913. })[
  4914. // Discard first 10% of the relevant distances, and then grab
  4915. // the smallest one.
  4916. Math.floor(distances.length / 10)] / 2 - 1 // Divide the distance by 2 and subtract 1.
  4917. ), 1 // 1 is the minimum margin
  4918. );
  4919. }
  4920. /* eslint-disable no-invalid-this, valid-jsdoc */
  4921. /**
  4922. * The Connection class. Used internally to represent a connection between two
  4923. * points.
  4924. *
  4925. * @private
  4926. * @class
  4927. * @name Highcharts.Connection
  4928. *
  4929. * @param {Highcharts.Point} from
  4930. * Connection runs from this Point.
  4931. *
  4932. * @param {Highcharts.Point} to
  4933. * Connection runs to this Point.
  4934. *
  4935. * @param {Highcharts.ConnectorsOptions} [options]
  4936. * Connection options.
  4937. */
  4938. var Connection = /** @class */ (function () {
  4939. function Connection(from, to, options) {
  4940. /* *
  4941. *
  4942. * Properties
  4943. *
  4944. * */
  4945. this.chart = void 0;
  4946. this.fromPoint = void 0;
  4947. this.graphics = void 0;
  4948. this.pathfinder = void 0;
  4949. this.toPoint = void 0;
  4950. this.init(from, to, options);
  4951. }
  4952. /**
  4953. * Initialize the Connection object. Used as constructor only.
  4954. *
  4955. * @function Highcharts.Connection#init
  4956. *
  4957. * @param {Highcharts.Point} from
  4958. * Connection runs from this Point.
  4959. *
  4960. * @param {Highcharts.Point} to
  4961. * Connection runs to this Point.
  4962. *
  4963. * @param {Highcharts.ConnectorsOptions} [options]
  4964. * Connection options.
  4965. */
  4966. Connection.prototype.init = function (from, to, options) {
  4967. this.fromPoint = from;
  4968. this.toPoint = to;
  4969. this.options = options;
  4970. this.chart = from.series.chart;
  4971. this.pathfinder = this.chart.pathfinder;
  4972. };
  4973. /**
  4974. * Add (or update) this connection's path on chart. Stores reference to the
  4975. * created element on this.graphics.path.
  4976. *
  4977. * @function Highcharts.Connection#renderPath
  4978. *
  4979. * @param {Highcharts.SVGPathArray} path
  4980. * Path to render, in array format. E.g. ['M', 0, 0, 'L', 10, 10]
  4981. *
  4982. * @param {Highcharts.SVGAttributes} [attribs]
  4983. * SVG attributes for the path.
  4984. *
  4985. * @param {Partial<Highcharts.AnimationOptionsObject>} [animation]
  4986. * Animation options for the rendering.
  4987. */
  4988. Connection.prototype.renderPath = function (path, attribs, animation) {
  4989. var connection = this,
  4990. chart = this.chart,
  4991. styledMode = chart.styledMode,
  4992. pathfinder = chart.pathfinder,
  4993. animate = !chart.options.chart.forExport && animation !== false,
  4994. pathGraphic = connection.graphics && connection.graphics.path,
  4995. anim;
  4996. // Add the SVG element of the pathfinder group if it doesn't exist
  4997. if (!pathfinder.group) {
  4998. pathfinder.group = chart.renderer.g()
  4999. .addClass('highcharts-pathfinder-group')
  5000. .attr({ zIndex: -1 })
  5001. .add(chart.seriesGroup);
  5002. }
  5003. // Shift the group to compensate for plot area.
  5004. // Note: Do this always (even when redrawing a path) to avoid issues
  5005. // when updating chart in a way that changes plot metrics.
  5006. pathfinder.group.translate(chart.plotLeft, chart.plotTop);
  5007. // Create path if does not exist
  5008. if (!(pathGraphic && pathGraphic.renderer)) {
  5009. pathGraphic = chart.renderer.path()
  5010. .add(pathfinder.group);
  5011. if (!styledMode) {
  5012. pathGraphic.attr({
  5013. opacity: 0
  5014. });
  5015. }
  5016. }
  5017. // Set path attribs and animate to the new path
  5018. pathGraphic.attr(attribs);
  5019. anim = { d: path };
  5020. if (!styledMode) {
  5021. anim.opacity = 1;
  5022. }
  5023. pathGraphic[animate ? 'animate' : 'attr'](anim, animation);
  5024. // Store reference on connection
  5025. this.graphics = this.graphics || {};
  5026. this.graphics.path = pathGraphic;
  5027. };
  5028. /**
  5029. * Calculate and add marker graphics for connection to the chart. The
  5030. * created/updated elements are stored on this.graphics.start and
  5031. * this.graphics.end.
  5032. *
  5033. * @function Highcharts.Connection#addMarker
  5034. *
  5035. * @param {string} type
  5036. * Marker type, either 'start' or 'end'.
  5037. *
  5038. * @param {Highcharts.ConnectorsMarkerOptions} options
  5039. * All options for this marker. Not calculated or merged with other
  5040. * options.
  5041. *
  5042. * @param {Highcharts.SVGPathArray} path
  5043. * Connection path in array format. This is used to calculate the
  5044. * rotation angle of the markers.
  5045. */
  5046. Connection.prototype.addMarker = function (type, options, path) {
  5047. var connection = this,
  5048. chart = connection.fromPoint.series.chart,
  5049. pathfinder = chart.pathfinder,
  5050. renderer = chart.renderer,
  5051. point = (type === 'start' ?
  5052. connection.fromPoint :
  5053. connection.toPoint),
  5054. anchor = point.getPathfinderAnchorPoint(options),
  5055. markerVector,
  5056. radians,
  5057. rotation,
  5058. box,
  5059. width,
  5060. height,
  5061. pathVector,
  5062. segment;
  5063. if (!options.enabled) {
  5064. return;
  5065. }
  5066. // Last vector before start/end of path, used to get angle
  5067. if (type === 'start') {
  5068. segment = path[1];
  5069. }
  5070. else { // 'end'
  5071. segment = path[path.length - 2];
  5072. }
  5073. if (segment && segment[0] === 'M' || segment[0] === 'L') {
  5074. pathVector = {
  5075. x: segment[1],
  5076. y: segment[2]
  5077. };
  5078. // Get angle between pathVector and anchor point and use it to
  5079. // create marker position.
  5080. radians = point.getRadiansToVector(pathVector, anchor);
  5081. markerVector = point.getMarkerVector(radians, options.radius, anchor);
  5082. // Rotation of marker is calculated from angle between pathVector
  5083. // and markerVector.
  5084. // (Note:
  5085. // Used to recalculate radians between markerVector and pathVector,
  5086. // but this should be the same as between pathVector and anchor.)
  5087. rotation = -radians / deg2rad;
  5088. if (options.width && options.height) {
  5089. width = options.width;
  5090. height = options.height;
  5091. }
  5092. else {
  5093. width = height = options.radius * 2;
  5094. }
  5095. // Add graphics object if it does not exist
  5096. connection.graphics = connection.graphics || {};
  5097. box = {
  5098. x: markerVector.x - (width / 2),
  5099. y: markerVector.y - (height / 2),
  5100. width: width,
  5101. height: height,
  5102. rotation: rotation,
  5103. rotationOriginX: markerVector.x,
  5104. rotationOriginY: markerVector.y
  5105. };
  5106. if (!connection.graphics[type]) {
  5107. // Create new marker element
  5108. connection.graphics[type] = renderer
  5109. .symbol(options.symbol)
  5110. .addClass('highcharts-point-connecting-path-' + type + '-marker')
  5111. .attr(box)
  5112. .add(pathfinder.group);
  5113. if (!renderer.styledMode) {
  5114. connection.graphics[type].attr({
  5115. fill: options.color || connection.fromPoint.color,
  5116. stroke: options.lineColor,
  5117. 'stroke-width': options.lineWidth,
  5118. opacity: 0
  5119. })
  5120. .animate({
  5121. opacity: 1
  5122. }, point.series.options.animation);
  5123. }
  5124. }
  5125. else {
  5126. connection.graphics[type].animate(box);
  5127. }
  5128. }
  5129. };
  5130. /**
  5131. * Calculate and return connection path.
  5132. * Note: Recalculates chart obstacles on demand if they aren't calculated.
  5133. *
  5134. * @function Highcharts.Connection#getPath
  5135. *
  5136. * @param {Highcharts.ConnectorsOptions} options
  5137. * Connector options. Not calculated or merged with other options.
  5138. *
  5139. * @return {object|undefined}
  5140. * Calculated SVG path data in array format.
  5141. */
  5142. Connection.prototype.getPath = function (options) {
  5143. var pathfinder = this.pathfinder,
  5144. chart = this.chart,
  5145. algorithm = pathfinder.algorithms[options.type],
  5146. chartObstacles = pathfinder.chartObstacles;
  5147. if (typeof algorithm !== 'function') {
  5148. error('"' + options.type + '" is not a Pathfinder algorithm.');
  5149. return {
  5150. path: [],
  5151. obstacles: []
  5152. };
  5153. }
  5154. // This function calculates obstacles on demand if they don't exist
  5155. if (algorithm.requiresObstacles && !chartObstacles) {
  5156. chartObstacles =
  5157. pathfinder.chartObstacles =
  5158. pathfinder.getChartObstacles(options);
  5159. // If the algorithmMargin was computed, store the result in default
  5160. // options.
  5161. chart.options.connectors.algorithmMargin =
  5162. options.algorithmMargin;
  5163. // Cache some metrics too
  5164. pathfinder.chartObstacleMetrics =
  5165. pathfinder.getObstacleMetrics(chartObstacles);
  5166. }
  5167. // Get the SVG path
  5168. return algorithm(
  5169. // From
  5170. this.fromPoint.getPathfinderAnchorPoint(options.startMarker),
  5171. // To
  5172. this.toPoint.getPathfinderAnchorPoint(options.endMarker), merge({
  5173. chartObstacles: chartObstacles,
  5174. lineObstacles: pathfinder.lineObstacles || [],
  5175. obstacleMetrics: pathfinder.chartObstacleMetrics,
  5176. hardBounds: {
  5177. xMin: 0,
  5178. xMax: chart.plotWidth,
  5179. yMin: 0,
  5180. yMax: chart.plotHeight
  5181. },
  5182. obstacleOptions: {
  5183. margin: options.algorithmMargin
  5184. },
  5185. startDirectionX: pathfinder.getAlgorithmStartDirection(options.startMarker)
  5186. }, options));
  5187. };
  5188. /**
  5189. * (re)Calculate and (re)draw the connection.
  5190. *
  5191. * @function Highcharts.Connection#render
  5192. */
  5193. Connection.prototype.render = function () {
  5194. var connection = this,
  5195. fromPoint = connection.fromPoint,
  5196. series = fromPoint.series,
  5197. chart = series.chart,
  5198. pathfinder = chart.pathfinder,
  5199. pathResult,
  5200. path,
  5201. options = merge(chart.options.connectors,
  5202. series.options.connectors,
  5203. fromPoint.options.connectors,
  5204. connection.options),
  5205. attribs = {};
  5206. // Set path attribs
  5207. if (!chart.styledMode) {
  5208. attribs.stroke = options.lineColor || fromPoint.color;
  5209. attribs['stroke-width'] = options.lineWidth;
  5210. if (options.dashStyle) {
  5211. attribs.dashstyle = options.dashStyle;
  5212. }
  5213. }
  5214. attribs['class'] = // eslint-disable-line dot-notation
  5215. 'highcharts-point-connecting-path ' +
  5216. 'highcharts-color-' + fromPoint.colorIndex;
  5217. options = merge(attribs, options);
  5218. // Set common marker options
  5219. if (!defined(options.marker.radius)) {
  5220. options.marker.radius = min(max(Math.ceil((options.algorithmMargin || 8) / 2) - 1, 1), 5);
  5221. }
  5222. // Get the path
  5223. pathResult = connection.getPath(options);
  5224. path = pathResult.path;
  5225. // Always update obstacle storage with obstacles from this path.
  5226. // We don't know if future calls will need this for their algorithm.
  5227. if (pathResult.obstacles) {
  5228. pathfinder.lineObstacles =
  5229. pathfinder.lineObstacles || [];
  5230. pathfinder.lineObstacles =
  5231. pathfinder.lineObstacles.concat(pathResult.obstacles);
  5232. }
  5233. // Add the calculated path to the pathfinder group
  5234. connection.renderPath(path, attribs, series.options.animation);
  5235. // Render the markers
  5236. connection.addMarker('start', merge(options.marker, options.startMarker), path);
  5237. connection.addMarker('end', merge(options.marker, options.endMarker), path);
  5238. };
  5239. /**
  5240. * Destroy connection by destroying the added graphics elements.
  5241. *
  5242. * @function Highcharts.Connection#destroy
  5243. */
  5244. Connection.prototype.destroy = function () {
  5245. if (this.graphics) {
  5246. objectEach(this.graphics, function (val) {
  5247. val.destroy();
  5248. });
  5249. delete this.graphics;
  5250. }
  5251. };
  5252. return Connection;
  5253. }());
  5254. // Add to Highcharts namespace
  5255. H.Connection = Connection;
  5256. // Add pathfinding capabilities to Points
  5257. extend(Point.prototype, /** @lends Point.prototype */ {
  5258. /**
  5259. * Get coordinates of anchor point for pathfinder connection.
  5260. *
  5261. * @private
  5262. * @function Highcharts.Point#getPathfinderAnchorPoint
  5263. *
  5264. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  5265. * Connection options for position on point.
  5266. *
  5267. * @return {Highcharts.PositionObject}
  5268. * An object with x/y properties for the position. Coordinates are
  5269. * in plot values, not relative to point.
  5270. */
  5271. getPathfinderAnchorPoint: function (markerOptions) {
  5272. var bb = getPointBB(this),
  5273. x,
  5274. y;
  5275. switch (markerOptions.align) { // eslint-disable-line default-case
  5276. case 'right':
  5277. x = 'xMax';
  5278. break;
  5279. case 'left':
  5280. x = 'xMin';
  5281. }
  5282. switch (markerOptions.verticalAlign) { // eslint-disable-line default-case
  5283. case 'top':
  5284. y = 'yMin';
  5285. break;
  5286. case 'bottom':
  5287. y = 'yMax';
  5288. }
  5289. return {
  5290. x: x ? bb[x] : (bb.xMin + bb.xMax) / 2,
  5291. y: y ? bb[y] : (bb.yMin + bb.yMax) / 2
  5292. };
  5293. },
  5294. /**
  5295. * Utility to get the angle from one point to another.
  5296. *
  5297. * @private
  5298. * @function Highcharts.Point#getRadiansToVector
  5299. *
  5300. * @param {Highcharts.PositionObject} v1
  5301. * The first vector, as an object with x/y properties.
  5302. *
  5303. * @param {Highcharts.PositionObject} v2
  5304. * The second vector, as an object with x/y properties.
  5305. *
  5306. * @return {number}
  5307. * The angle in degrees
  5308. */
  5309. getRadiansToVector: function (v1, v2) {
  5310. var box;
  5311. if (!defined(v2)) {
  5312. box = getPointBB(this);
  5313. if (box) {
  5314. v2 = {
  5315. x: (box.xMin + box.xMax) / 2,
  5316. y: (box.yMin + box.yMax) / 2
  5317. };
  5318. }
  5319. }
  5320. return Math.atan2(v2.y - v1.y, v1.x - v2.x);
  5321. },
  5322. /**
  5323. * Utility to get the position of the marker, based on the path angle and
  5324. * the marker's radius.
  5325. *
  5326. * @private
  5327. * @function Highcharts.Point#getMarkerVector
  5328. *
  5329. * @param {number} radians
  5330. * The angle in radians from the point center to another vector.
  5331. *
  5332. * @param {number} markerRadius
  5333. * The radius of the marker, to calculate the additional distance to
  5334. * the center of the marker.
  5335. *
  5336. * @param {object} anchor
  5337. * The anchor point of the path and marker as an object with x/y
  5338. * properties.
  5339. *
  5340. * @return {object}
  5341. * The marker vector as an object with x/y properties.
  5342. */
  5343. getMarkerVector: function (radians, markerRadius, anchor) {
  5344. var twoPI = Math.PI * 2.0,
  5345. theta = radians,
  5346. bb = getPointBB(this),
  5347. rectWidth = bb.xMax - bb.xMin,
  5348. rectHeight = bb.yMax - bb.yMin,
  5349. rAtan = Math.atan2(rectHeight,
  5350. rectWidth),
  5351. tanTheta = 1,
  5352. leftOrRightRegion = false,
  5353. rectHalfWidth = rectWidth / 2.0,
  5354. rectHalfHeight = rectHeight / 2.0,
  5355. rectHorizontalCenter = bb.xMin + rectHalfWidth,
  5356. rectVerticalCenter = bb.yMin + rectHalfHeight,
  5357. edgePoint = {
  5358. x: rectHorizontalCenter,
  5359. y: rectVerticalCenter
  5360. },
  5361. xFactor = 1,
  5362. yFactor = 1;
  5363. while (theta < -Math.PI) {
  5364. theta += twoPI;
  5365. }
  5366. while (theta > Math.PI) {
  5367. theta -= twoPI;
  5368. }
  5369. tanTheta = Math.tan(theta);
  5370. if ((theta > -rAtan) && (theta <= rAtan)) {
  5371. // Right side
  5372. yFactor = -1;
  5373. leftOrRightRegion = true;
  5374. }
  5375. else if (theta > rAtan && theta <= (Math.PI - rAtan)) {
  5376. // Top side
  5377. yFactor = -1;
  5378. }
  5379. else if (theta > (Math.PI - rAtan) || theta <= -(Math.PI - rAtan)) {
  5380. // Left side
  5381. xFactor = -1;
  5382. leftOrRightRegion = true;
  5383. }
  5384. else {
  5385. // Bottom side
  5386. xFactor = -1;
  5387. }
  5388. // Correct the edgePoint according to the placement of the marker
  5389. if (leftOrRightRegion) {
  5390. edgePoint.x += xFactor * (rectHalfWidth);
  5391. edgePoint.y += yFactor * (rectHalfWidth) * tanTheta;
  5392. }
  5393. else {
  5394. edgePoint.x += xFactor * (rectHeight / (2.0 * tanTheta));
  5395. edgePoint.y += yFactor * (rectHalfHeight);
  5396. }
  5397. if (anchor.x !== rectHorizontalCenter) {
  5398. edgePoint.x = anchor.x;
  5399. }
  5400. if (anchor.y !== rectVerticalCenter) {
  5401. edgePoint.y = anchor.y;
  5402. }
  5403. return {
  5404. x: edgePoint.x + (markerRadius * Math.cos(theta)),
  5405. y: edgePoint.y - (markerRadius * Math.sin(theta))
  5406. };
  5407. }
  5408. });
  5409. /**
  5410. * Warn if using legacy options. Copy the options over. Note that this will
  5411. * still break if using the legacy options in chart.update, addSeries etc.
  5412. * @private
  5413. */
  5414. function warnLegacy(chart) {
  5415. if (chart.options.pathfinder ||
  5416. chart.series.reduce(function (acc, series) {
  5417. if (series.options) {
  5418. merge(true, (series.options.connectors = series.options.connectors ||
  5419. {}), series.options.pathfinder);
  5420. }
  5421. return acc || series.options && series.options.pathfinder;
  5422. }, false)) {
  5423. merge(true, (chart.options.connectors = chart.options.connectors || {}), chart.options.pathfinder);
  5424. error('WARNING: Pathfinder options have been renamed. ' +
  5425. 'Use "chart.connectors" or "series.connectors" instead.');
  5426. }
  5427. }
  5428. return Connection;
  5429. });
  5430. _registerModule(_modules, 'Gantt/PathfinderAlgorithms.js', [_modules['Core/Utilities.js']], function (U) {
  5431. /* *
  5432. *
  5433. * (c) 2016 Highsoft AS
  5434. * Author: Øystein Moseng
  5435. *
  5436. * License: www.highcharts.com/license
  5437. *
  5438. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5439. *
  5440. * */
  5441. var extend = U.extend,
  5442. pick = U.pick;
  5443. var min = Math.min,
  5444. max = Math.max,
  5445. abs = Math.abs;
  5446. /**
  5447. * Get index of last obstacle before xMin. Employs a type of binary search, and
  5448. * thus requires that obstacles are sorted by xMin value.
  5449. *
  5450. * @private
  5451. * @function findLastObstacleBefore
  5452. *
  5453. * @param {Array<object>} obstacles
  5454. * Array of obstacles to search in.
  5455. *
  5456. * @param {number} xMin
  5457. * The xMin threshold.
  5458. *
  5459. * @param {number} [startIx]
  5460. * Starting index to search from. Must be within array range.
  5461. *
  5462. * @return {number}
  5463. * The index of the last obstacle element before xMin.
  5464. */
  5465. function findLastObstacleBefore(obstacles, xMin, startIx) {
  5466. var left = startIx || 0, // left limit
  5467. right = obstacles.length - 1, // right limit
  5468. min = xMin - 0.0000001, // Make sure we include all obstacles at xMin
  5469. cursor,
  5470. cmp;
  5471. while (left <= right) {
  5472. cursor = (right + left) >> 1;
  5473. cmp = min - obstacles[cursor].xMin;
  5474. if (cmp > 0) {
  5475. left = cursor + 1;
  5476. }
  5477. else if (cmp < 0) {
  5478. right = cursor - 1;
  5479. }
  5480. else {
  5481. return cursor;
  5482. }
  5483. }
  5484. return left > 0 ? left - 1 : 0;
  5485. }
  5486. /**
  5487. * Test if a point lays within an obstacle.
  5488. *
  5489. * @private
  5490. * @function pointWithinObstacle
  5491. *
  5492. * @param {object} obstacle
  5493. * Obstacle to test.
  5494. *
  5495. * @param {Highcharts.Point} point
  5496. * Point with x/y props.
  5497. *
  5498. * @return {boolean}
  5499. * Whether point is within the obstacle or not.
  5500. */
  5501. function pointWithinObstacle(obstacle, point) {
  5502. return (point.x <= obstacle.xMax &&
  5503. point.x >= obstacle.xMin &&
  5504. point.y <= obstacle.yMax &&
  5505. point.y >= obstacle.yMin);
  5506. }
  5507. /**
  5508. * Find the index of an obstacle that wraps around a point.
  5509. * Returns -1 if not found.
  5510. *
  5511. * @private
  5512. * @function findObstacleFromPoint
  5513. *
  5514. * @param {Array<object>} obstacles
  5515. * Obstacles to test.
  5516. *
  5517. * @param {Highcharts.Point} point
  5518. * Point with x/y props.
  5519. *
  5520. * @return {number}
  5521. * Ix of the obstacle in the array, or -1 if not found.
  5522. */
  5523. function findObstacleFromPoint(obstacles, point) {
  5524. var i = findLastObstacleBefore(obstacles,
  5525. point.x + 1) + 1;
  5526. while (i--) {
  5527. if (obstacles[i].xMax >= point.x &&
  5528. // optimization using lazy evaluation
  5529. pointWithinObstacle(obstacles[i], point)) {
  5530. return i;
  5531. }
  5532. }
  5533. return -1;
  5534. }
  5535. /**
  5536. * Get SVG path array from array of line segments.
  5537. *
  5538. * @private
  5539. * @function pathFromSegments
  5540. *
  5541. * @param {Array<object>} segments
  5542. * The segments to build the path from.
  5543. *
  5544. * @return {Highcharts.SVGPathArray}
  5545. * SVG path array as accepted by the SVG Renderer.
  5546. */
  5547. function pathFromSegments(segments) {
  5548. var path = [];
  5549. if (segments.length) {
  5550. path.push(['M', segments[0].start.x, segments[0].start.y]);
  5551. for (var i = 0; i < segments.length; ++i) {
  5552. path.push(['L', segments[i].end.x, segments[i].end.y]);
  5553. }
  5554. }
  5555. return path;
  5556. }
  5557. /**
  5558. * Limits obstacle max/mins in all directions to bounds. Modifies input
  5559. * obstacle.
  5560. *
  5561. * @private
  5562. * @function limitObstacleToBounds
  5563. *
  5564. * @param {object} obstacle
  5565. * Obstacle to limit.
  5566. *
  5567. * @param {object} bounds
  5568. * Bounds to use as limit.
  5569. *
  5570. * @return {void}
  5571. */
  5572. function limitObstacleToBounds(obstacle, bounds) {
  5573. obstacle.yMin = max(obstacle.yMin, bounds.yMin);
  5574. obstacle.yMax = min(obstacle.yMax, bounds.yMax);
  5575. obstacle.xMin = max(obstacle.xMin, bounds.xMin);
  5576. obstacle.xMax = min(obstacle.xMax, bounds.xMax);
  5577. }
  5578. /**
  5579. * Get an SVG path from a starting coordinate to an ending coordinate.
  5580. * Draws a straight line.
  5581. *
  5582. * @function Highcharts.Pathfinder.algorithms.straight
  5583. *
  5584. * @param {Highcharts.PositionObject} start
  5585. * Starting coordinate, object with x/y props.
  5586. *
  5587. * @param {Highcharts.PositionObject} end
  5588. * Ending coordinate, object with x/y props.
  5589. *
  5590. * @return {object}
  5591. * An object with the SVG path in Array form as accepted by the SVG
  5592. * renderer, as well as an array of new obstacles making up this
  5593. * path.
  5594. */
  5595. function straight(start, end) {
  5596. return {
  5597. path: [
  5598. ['M', start.x, start.y],
  5599. ['L', end.x, end.y]
  5600. ],
  5601. obstacles: [{ start: start, end: end }]
  5602. };
  5603. }
  5604. /**
  5605. * Find a path from a starting coordinate to an ending coordinate, using
  5606. * right angles only, and taking only starting/ending obstacle into
  5607. * consideration.
  5608. *
  5609. * @function Highcharts.Pathfinder.algorithms.simpleConnect
  5610. *
  5611. * @param {Highcharts.PositionObject} start
  5612. * Starting coordinate, object with x/y props.
  5613. *
  5614. * @param {Highcharts.PositionObject} end
  5615. * Ending coordinate, object with x/y props.
  5616. *
  5617. * @param {object} options
  5618. * Options for the algorithm:
  5619. * - chartObstacles: Array of chart obstacles to avoid
  5620. * - startDirectionX: Optional. True if starting in the X direction.
  5621. * If not provided, the algorithm starts in the direction that is
  5622. * the furthest between start/end.
  5623. *
  5624. * @return {object}
  5625. * An object with the SVG path in Array form as accepted by the SVG
  5626. * renderer, as well as an array of new obstacles making up this
  5627. * path.
  5628. */
  5629. var simpleConnect = extend(function (start,
  5630. end,
  5631. options) {
  5632. var segments = [],
  5633. endSegment,
  5634. dir = pick(options.startDirectionX,
  5635. abs(end.x - start.x) > abs(end.y - start.y)) ? 'x' : 'y',
  5636. chartObstacles = options.chartObstacles,
  5637. startObstacleIx = findObstacleFromPoint(chartObstacles,
  5638. start),
  5639. endObstacleIx = findObstacleFromPoint(chartObstacles,
  5640. end),
  5641. startObstacle,
  5642. endObstacle,
  5643. prevWaypoint,
  5644. waypoint,
  5645. waypoint2,
  5646. useMax,
  5647. endPoint;
  5648. // eslint-disable-next-line valid-jsdoc
  5649. /**
  5650. * Return a clone of a point with a property set from a target object,
  5651. * optionally with an offset
  5652. * @private
  5653. */
  5654. function copyFromPoint(from, fromKey, to, toKey, offset) {
  5655. var point = {
  5656. x: from.x,
  5657. y: from.y
  5658. };
  5659. point[fromKey] = to[toKey || fromKey] + (offset || 0);
  5660. return point;
  5661. }
  5662. // eslint-disable-next-line valid-jsdoc
  5663. /**
  5664. * Return waypoint outside obstacle.
  5665. * @private
  5666. */
  5667. function getMeOut(obstacle, point, direction) {
  5668. var useMax = abs(point[direction] - obstacle[direction + 'Min']) >
  5669. abs(point[direction] - obstacle[direction + 'Max']);
  5670. return copyFromPoint(point, direction, obstacle, direction + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1);
  5671. }
  5672. // Pull out end point
  5673. if (endObstacleIx > -1) {
  5674. endObstacle = chartObstacles[endObstacleIx];
  5675. waypoint = getMeOut(endObstacle, end, dir);
  5676. endSegment = {
  5677. start: waypoint,
  5678. end: end
  5679. };
  5680. endPoint = waypoint;
  5681. }
  5682. else {
  5683. endPoint = end;
  5684. }
  5685. // If an obstacle envelops the start point, add a segment to get out,
  5686. // and around it.
  5687. if (startObstacleIx > -1) {
  5688. startObstacle = chartObstacles[startObstacleIx];
  5689. waypoint = getMeOut(startObstacle, start, dir);
  5690. segments.push({
  5691. start: start,
  5692. end: waypoint
  5693. });
  5694. // If we are going back again, switch direction to get around start
  5695. // obstacle.
  5696. if (
  5697. // Going towards max from start:
  5698. waypoint[dir] >= start[dir] ===
  5699. // Going towards min to end:
  5700. waypoint[dir] >= endPoint[dir]) {
  5701. dir = dir === 'y' ? 'x' : 'y';
  5702. useMax = start[dir] < end[dir];
  5703. segments.push({
  5704. start: waypoint,
  5705. end: copyFromPoint(waypoint, dir, startObstacle, dir + (useMax ? 'Max' : 'Min'), useMax ? 1 : -1)
  5706. });
  5707. // Switch direction again
  5708. dir = dir === 'y' ? 'x' : 'y';
  5709. }
  5710. }
  5711. // We are around the start obstacle. Go towards the end in one
  5712. // direction.
  5713. prevWaypoint = segments.length ?
  5714. segments[segments.length - 1].end :
  5715. start;
  5716. waypoint = copyFromPoint(prevWaypoint, dir, endPoint);
  5717. segments.push({
  5718. start: prevWaypoint,
  5719. end: waypoint
  5720. });
  5721. // Final run to end point in the other direction
  5722. dir = dir === 'y' ? 'x' : 'y';
  5723. waypoint2 = copyFromPoint(waypoint, dir, endPoint);
  5724. segments.push({
  5725. start: waypoint,
  5726. end: waypoint2
  5727. });
  5728. // Finally add the endSegment
  5729. segments.push(endSegment);
  5730. return {
  5731. path: pathFromSegments(segments),
  5732. obstacles: segments
  5733. };
  5734. }, {
  5735. requiresObstacles: true
  5736. });
  5737. /**
  5738. * Find a path from a starting coordinate to an ending coordinate, taking
  5739. * obstacles into consideration. Might not always find the optimal path,
  5740. * but is fast, and usually good enough.
  5741. *
  5742. * @function Highcharts.Pathfinder.algorithms.fastAvoid
  5743. *
  5744. * @param {Highcharts.PositionObject} start
  5745. * Starting coordinate, object with x/y props.
  5746. *
  5747. * @param {Highcharts.PositionObject} end
  5748. * Ending coordinate, object with x/y props.
  5749. *
  5750. * @param {object} options
  5751. * Options for the algorithm.
  5752. * - chartObstacles: Array of chart obstacles to avoid
  5753. * - lineObstacles: Array of line obstacles to jump over
  5754. * - obstacleMetrics: Object with metrics of chartObstacles cached
  5755. * - hardBounds: Hard boundaries to not cross
  5756. * - obstacleOptions: Options for the obstacles, including margin
  5757. * - startDirectionX: Optional. True if starting in the X direction.
  5758. * If not provided, the algorithm starts in the
  5759. * direction that is the furthest between
  5760. * start/end.
  5761. *
  5762. * @return {object}
  5763. * An object with the SVG path in Array form as accepted by the SVG
  5764. * renderer, as well as an array of new obstacles making up this
  5765. * path.
  5766. */
  5767. var fastAvoid = extend(function (start,
  5768. end,
  5769. options) {
  5770. /*
  5771. Algorithm rules/description
  5772. - Find initial direction
  5773. - Determine soft/hard max for each direction.
  5774. - Move along initial direction until obstacle.
  5775. - Change direction.
  5776. - If hitting obstacle,
  5777. first try to change length of previous line
  5778. before changing direction again.
  5779. Soft min/max x = start/destination x +/- widest obstacle + margin
  5780. Soft min/max y = start/destination y +/- tallest obstacle + margin
  5781. @todo:
  5782. - Make retrospective,
  5783. try changing prev segment to reduce
  5784. corners
  5785. - Fix logic for breaking out of end-points - not always picking
  5786. the best direction currently
  5787. - When going around the end obstacle we should not always go the
  5788. shortest route,
  5789. rather pick the one closer to the end point
  5790. */
  5791. var dirIsX = pick(options.startDirectionX,
  5792. abs(end.x - start.x) > abs(end.y - start.y)),
  5793. dir = dirIsX ? 'x' : 'y',
  5794. segments,
  5795. useMax,
  5796. extractedEndPoint,
  5797. endSegments = [],
  5798. forceObstacleBreak = false, // Used in clearPathTo to keep track of
  5799. // when to force break through an obstacle.
  5800. // Boundaries to stay within. If beyond soft boundary, prefer to
  5801. // change direction ASAP. If at hard max, always change immediately.
  5802. metrics = options.obstacleMetrics,
  5803. softMinX = min(start.x,
  5804. end.x) - metrics.maxWidth - 10,
  5805. softMaxX = max(start.x,
  5806. end.x) + metrics.maxWidth + 10,
  5807. softMinY = min(start.y,
  5808. end.y) - metrics.maxHeight - 10,
  5809. softMaxY = max(start.y,
  5810. end.y) + metrics.maxHeight + 10,
  5811. // Obstacles
  5812. chartObstacles = options.chartObstacles,
  5813. startObstacleIx = findLastObstacleBefore(chartObstacles,
  5814. softMinX),
  5815. endObstacleIx = findLastObstacleBefore(chartObstacles,
  5816. softMaxX);
  5817. // eslint-disable-next-line valid-jsdoc
  5818. /**
  5819. * How far can you go between two points before hitting an obstacle?
  5820. * Does not work for diagonal lines (because it doesn't have to).
  5821. * @private
  5822. */
  5823. function pivotPoint(fromPoint, toPoint, directionIsX) {
  5824. var firstPoint,
  5825. lastPoint,
  5826. highestPoint,
  5827. lowestPoint,
  5828. i,
  5829. searchDirection = fromPoint.x < toPoint.x ? 1 : -1;
  5830. if (fromPoint.x < toPoint.x) {
  5831. firstPoint = fromPoint;
  5832. lastPoint = toPoint;
  5833. }
  5834. else {
  5835. firstPoint = toPoint;
  5836. lastPoint = fromPoint;
  5837. }
  5838. if (fromPoint.y < toPoint.y) {
  5839. lowestPoint = fromPoint;
  5840. highestPoint = toPoint;
  5841. }
  5842. else {
  5843. lowestPoint = toPoint;
  5844. highestPoint = fromPoint;
  5845. }
  5846. // Go through obstacle range in reverse if toPoint is before
  5847. // fromPoint in the X-dimension.
  5848. i = searchDirection < 0 ?
  5849. // Searching backwards, start at last obstacle before last point
  5850. min(findLastObstacleBefore(chartObstacles, lastPoint.x), chartObstacles.length - 1) :
  5851. // Forwards. Since we're not sorted by xMax, we have to look
  5852. // at all obstacles.
  5853. 0;
  5854. // Go through obstacles in this X range
  5855. while (chartObstacles[i] && (searchDirection > 0 && chartObstacles[i].xMin <= lastPoint.x ||
  5856. searchDirection < 0 && chartObstacles[i].xMax >= firstPoint.x)) {
  5857. // If this obstacle is between from and to points in a straight
  5858. // line, pivot at the intersection.
  5859. if (chartObstacles[i].xMin <= lastPoint.x &&
  5860. chartObstacles[i].xMax >= firstPoint.x &&
  5861. chartObstacles[i].yMin <= highestPoint.y &&
  5862. chartObstacles[i].yMax >= lowestPoint.y) {
  5863. if (directionIsX) {
  5864. return {
  5865. y: fromPoint.y,
  5866. x: fromPoint.x < toPoint.x ?
  5867. chartObstacles[i].xMin - 1 :
  5868. chartObstacles[i].xMax + 1,
  5869. obstacle: chartObstacles[i]
  5870. };
  5871. }
  5872. // else ...
  5873. return {
  5874. x: fromPoint.x,
  5875. y: fromPoint.y < toPoint.y ?
  5876. chartObstacles[i].yMin - 1 :
  5877. chartObstacles[i].yMax + 1,
  5878. obstacle: chartObstacles[i]
  5879. };
  5880. }
  5881. i += searchDirection;
  5882. }
  5883. return toPoint;
  5884. }
  5885. /**
  5886. * Decide in which direction to dodge or get out of an obstacle.
  5887. * Considers desired direction, which way is shortest, soft and hard
  5888. * bounds.
  5889. *
  5890. * (? Returns a string, either xMin, xMax, yMin or yMax.)
  5891. *
  5892. * @private
  5893. * @function
  5894. *
  5895. * @param {object} obstacle
  5896. * Obstacle to dodge/escape.
  5897. *
  5898. * @param {object} fromPoint
  5899. * Point with x/y props that's dodging/escaping.
  5900. *
  5901. * @param {object} toPoint
  5902. * Goal point.
  5903. *
  5904. * @param {boolean} dirIsX
  5905. * Dodge in X dimension.
  5906. *
  5907. * @param {object} bounds
  5908. * Hard and soft boundaries.
  5909. *
  5910. * @return {boolean}
  5911. * Use max or not.
  5912. */
  5913. function getDodgeDirection(obstacle, fromPoint, toPoint, dirIsX, bounds) {
  5914. var softBounds = bounds.soft, hardBounds = bounds.hard, dir = dirIsX ? 'x' : 'y', toPointMax = { x: fromPoint.x, y: fromPoint.y }, toPointMin = { x: fromPoint.x, y: fromPoint.y }, minPivot, maxPivot, maxOutOfSoftBounds = obstacle[dir + 'Max'] >=
  5915. softBounds[dir + 'Max'], minOutOfSoftBounds = obstacle[dir + 'Min'] <=
  5916. softBounds[dir + 'Min'], maxOutOfHardBounds = obstacle[dir + 'Max'] >=
  5917. hardBounds[dir + 'Max'], minOutOfHardBounds = obstacle[dir + 'Min'] <=
  5918. hardBounds[dir + 'Min'],
  5919. // Find out if we should prefer one direction over the other if
  5920. // we can choose freely
  5921. minDistance = abs(obstacle[dir + 'Min'] - fromPoint[dir]), maxDistance = abs(obstacle[dir + 'Max'] - fromPoint[dir]),
  5922. // If it's a small difference, pick the one leading towards dest
  5923. // point. Otherwise pick the shortest distance
  5924. useMax = abs(minDistance - maxDistance) < 10 ?
  5925. fromPoint[dir] < toPoint[dir] :
  5926. maxDistance < minDistance;
  5927. // Check if we hit any obstacles trying to go around in either
  5928. // direction.
  5929. toPointMin[dir] = obstacle[dir + 'Min'];
  5930. toPointMax[dir] = obstacle[dir + 'Max'];
  5931. minPivot = pivotPoint(fromPoint, toPointMin, dirIsX)[dir] !==
  5932. toPointMin[dir];
  5933. maxPivot = pivotPoint(fromPoint, toPointMax, dirIsX)[dir] !==
  5934. toPointMax[dir];
  5935. useMax = minPivot ?
  5936. (maxPivot ? useMax : true) :
  5937. (maxPivot ? false : useMax);
  5938. // useMax now contains our preferred choice, bounds not taken into
  5939. // account. If both or neither direction is out of bounds we want to
  5940. // use this.
  5941. // Deal with soft bounds
  5942. useMax = minOutOfSoftBounds ?
  5943. (maxOutOfSoftBounds ? useMax : true) : // Out on min
  5944. (maxOutOfSoftBounds ? false : useMax); // Not out on min
  5945. // Deal with hard bounds
  5946. useMax = minOutOfHardBounds ?
  5947. (maxOutOfHardBounds ? useMax : true) : // Out on min
  5948. (maxOutOfHardBounds ? false : useMax); // Not out on min
  5949. return useMax;
  5950. }
  5951. // eslint-disable-next-line valid-jsdoc
  5952. /**
  5953. * Find a clear path between point.
  5954. * @private
  5955. */
  5956. function clearPathTo(fromPoint, toPoint, dirIsX) {
  5957. // Don't waste time if we've hit goal
  5958. if (fromPoint.x === toPoint.x && fromPoint.y === toPoint.y) {
  5959. return [];
  5960. }
  5961. var dir = dirIsX ? 'x' : 'y',
  5962. pivot,
  5963. segments,
  5964. waypoint,
  5965. waypointUseMax,
  5966. envelopingObstacle,
  5967. secondEnvelopingObstacle,
  5968. envelopWaypoint,
  5969. obstacleMargin = options.obstacleOptions.margin,
  5970. bounds = {
  5971. soft: {
  5972. xMin: softMinX,
  5973. xMax: softMaxX,
  5974. yMin: softMinY,
  5975. yMax: softMaxY
  5976. },
  5977. hard: options.hardBounds
  5978. };
  5979. // If fromPoint is inside an obstacle we have a problem. Break out
  5980. // by just going to the outside of this obstacle. We prefer to go to
  5981. // the nearest edge in the chosen direction.
  5982. envelopingObstacle =
  5983. findObstacleFromPoint(chartObstacles, fromPoint);
  5984. if (envelopingObstacle > -1) {
  5985. envelopingObstacle = chartObstacles[envelopingObstacle];
  5986. waypointUseMax = getDodgeDirection(envelopingObstacle, fromPoint, toPoint, dirIsX, bounds);
  5987. // Cut obstacle to hard bounds to make sure we stay within
  5988. limitObstacleToBounds(envelopingObstacle, options.hardBounds);
  5989. envelopWaypoint = dirIsX ? {
  5990. y: fromPoint.y,
  5991. x: envelopingObstacle[waypointUseMax ? 'xMax' : 'xMin'] +
  5992. (waypointUseMax ? 1 : -1)
  5993. } : {
  5994. x: fromPoint.x,
  5995. y: envelopingObstacle[waypointUseMax ? 'yMax' : 'yMin'] +
  5996. (waypointUseMax ? 1 : -1)
  5997. };
  5998. // If we crashed into another obstacle doing this, we put the
  5999. // waypoint between them instead
  6000. secondEnvelopingObstacle = findObstacleFromPoint(chartObstacles, envelopWaypoint);
  6001. if (secondEnvelopingObstacle > -1) {
  6002. secondEnvelopingObstacle = chartObstacles[secondEnvelopingObstacle];
  6003. // Cut obstacle to hard bounds
  6004. limitObstacleToBounds(secondEnvelopingObstacle, options.hardBounds);
  6005. // Modify waypoint to lay between obstacles
  6006. envelopWaypoint[dir] = waypointUseMax ? max(envelopingObstacle[dir + 'Max'] - obstacleMargin + 1, (secondEnvelopingObstacle[dir + 'Min'] +
  6007. envelopingObstacle[dir + 'Max']) / 2) :
  6008. min((envelopingObstacle[dir + 'Min'] + obstacleMargin - 1), ((secondEnvelopingObstacle[dir + 'Max'] +
  6009. envelopingObstacle[dir + 'Min']) / 2));
  6010. // We are not going anywhere. If this happens for the first
  6011. // time, do nothing. Otherwise, try to go to the extreme of
  6012. // the obstacle pair in the current direction.
  6013. if (fromPoint.x === envelopWaypoint.x &&
  6014. fromPoint.y === envelopWaypoint.y) {
  6015. if (forceObstacleBreak) {
  6016. envelopWaypoint[dir] = waypointUseMax ?
  6017. max(envelopingObstacle[dir + 'Max'], secondEnvelopingObstacle[dir + 'Max']) + 1 :
  6018. min(envelopingObstacle[dir + 'Min'], secondEnvelopingObstacle[dir + 'Min']) - 1;
  6019. }
  6020. // Toggle on if off, and the opposite
  6021. forceObstacleBreak = !forceObstacleBreak;
  6022. }
  6023. else {
  6024. // This point is not identical to previous.
  6025. // Clear break trigger.
  6026. forceObstacleBreak = false;
  6027. }
  6028. }
  6029. segments = [{
  6030. start: fromPoint,
  6031. end: envelopWaypoint
  6032. }];
  6033. }
  6034. else { // If not enveloping, use standard pivot calculation
  6035. pivot = pivotPoint(fromPoint, {
  6036. x: dirIsX ? toPoint.x : fromPoint.x,
  6037. y: dirIsX ? fromPoint.y : toPoint.y
  6038. }, dirIsX);
  6039. segments = [{
  6040. start: fromPoint,
  6041. end: {
  6042. x: pivot.x,
  6043. y: pivot.y
  6044. }
  6045. }];
  6046. // Pivot before goal, use a waypoint to dodge obstacle
  6047. if (pivot[dirIsX ? 'x' : 'y'] !== toPoint[dirIsX ? 'x' : 'y']) {
  6048. // Find direction of waypoint
  6049. waypointUseMax = getDodgeDirection(pivot.obstacle, pivot, toPoint, !dirIsX, bounds);
  6050. // Cut waypoint to hard bounds
  6051. limitObstacleToBounds(pivot.obstacle, options.hardBounds);
  6052. waypoint = {
  6053. x: dirIsX ?
  6054. pivot.x :
  6055. pivot.obstacle[waypointUseMax ? 'xMax' : 'xMin'] +
  6056. (waypointUseMax ? 1 : -1),
  6057. y: dirIsX ?
  6058. pivot.obstacle[waypointUseMax ? 'yMax' : 'yMin'] +
  6059. (waypointUseMax ? 1 : -1) :
  6060. pivot.y
  6061. };
  6062. // We're changing direction here, store that to make sure we
  6063. // also change direction when adding the last segment array
  6064. // after handling waypoint.
  6065. dirIsX = !dirIsX;
  6066. segments = segments.concat(clearPathTo({
  6067. x: pivot.x,
  6068. y: pivot.y
  6069. }, waypoint, dirIsX));
  6070. }
  6071. }
  6072. // Get segments for the other direction too
  6073. // Recursion is our friend
  6074. segments = segments.concat(clearPathTo(segments[segments.length - 1].end, toPoint, !dirIsX));
  6075. return segments;
  6076. }
  6077. // eslint-disable-next-line valid-jsdoc
  6078. /**
  6079. * Extract point to outside of obstacle in whichever direction is
  6080. * closest. Returns new point outside obstacle.
  6081. * @private
  6082. */
  6083. function extractFromObstacle(obstacle, point, goalPoint) {
  6084. var dirIsX = min(obstacle.xMax - point.x,
  6085. point.x - obstacle.xMin) <
  6086. min(obstacle.yMax - point.y,
  6087. point.y - obstacle.yMin),
  6088. bounds = {
  6089. soft: options.hardBounds,
  6090. hard: options.hardBounds
  6091. },
  6092. useMax = getDodgeDirection(obstacle,
  6093. point,
  6094. goalPoint,
  6095. dirIsX,
  6096. bounds);
  6097. return dirIsX ? {
  6098. y: point.y,
  6099. x: obstacle[useMax ? 'xMax' : 'xMin'] + (useMax ? 1 : -1)
  6100. } : {
  6101. x: point.x,
  6102. y: obstacle[useMax ? 'yMax' : 'yMin'] + (useMax ? 1 : -1)
  6103. };
  6104. }
  6105. // Cut the obstacle array to soft bounds for optimization in large
  6106. // datasets.
  6107. chartObstacles =
  6108. chartObstacles.slice(startObstacleIx, endObstacleIx + 1);
  6109. // If an obstacle envelops the end point, move it out of there and add
  6110. // a little segment to where it was.
  6111. if ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
  6112. extractedEndPoint = extractFromObstacle(chartObstacles[endObstacleIx], end, start);
  6113. endSegments.push({
  6114. end: end,
  6115. start: extractedEndPoint
  6116. });
  6117. end = extractedEndPoint;
  6118. }
  6119. // If it's still inside one or more obstacles, get out of there by
  6120. // force-moving towards the start point.
  6121. while ((endObstacleIx = findObstacleFromPoint(chartObstacles, end)) > -1) {
  6122. useMax = end[dir] - start[dir] < 0;
  6123. extractedEndPoint = {
  6124. x: end.x,
  6125. y: end.y
  6126. };
  6127. extractedEndPoint[dir] = chartObstacles[endObstacleIx][useMax ? dir + 'Max' : dir + 'Min'] + (useMax ? 1 : -1);
  6128. endSegments.push({
  6129. end: end,
  6130. start: extractedEndPoint
  6131. });
  6132. end = extractedEndPoint;
  6133. }
  6134. // Find the path
  6135. segments = clearPathTo(start, end, dirIsX);
  6136. // Add the end-point segments
  6137. segments = segments.concat(endSegments.reverse());
  6138. return {
  6139. path: pathFromSegments(segments),
  6140. obstacles: segments
  6141. };
  6142. }, {
  6143. requiresObstacles: true
  6144. });
  6145. // Define the available pathfinding algorithms.
  6146. // Algorithms take up to 3 arguments: starting point, ending point, and an
  6147. // options object.
  6148. var algorithms = {
  6149. fastAvoid: fastAvoid,
  6150. straight: straight,
  6151. simpleConnect: simpleConnect
  6152. };
  6153. return algorithms;
  6154. });
  6155. _registerModule(_modules, 'Gantt/Pathfinder.js', [_modules['Gantt/Connection.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js'], _modules['Gantt/PathfinderAlgorithms.js']], function (Connection, Chart, H, O, Point, U, pathfinderAlgorithms) {
  6156. /* *
  6157. *
  6158. * (c) 2016 Highsoft AS
  6159. * Authors: Øystein Moseng, Lars A. V. Cabrera
  6160. *
  6161. * License: www.highcharts.com/license
  6162. *
  6163. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6164. *
  6165. * */
  6166. /**
  6167. * The default pathfinder algorithm to use for a chart. It is possible to define
  6168. * your own algorithms by adding them to the
  6169. * `Highcharts.Pathfinder.prototype.algorithms`
  6170. * object before the chart has been created.
  6171. *
  6172. * The default algorithms are as follows:
  6173. *
  6174. * `straight`: Draws a straight line between the connecting
  6175. * points. Does not avoid other points when drawing.
  6176. *
  6177. * `simpleConnect`: Finds a path between the points using right angles
  6178. * only. Takes only starting/ending points into
  6179. * account, and will not avoid other points.
  6180. *
  6181. * `fastAvoid`: Finds a path between the points using right angles
  6182. * only. Will attempt to avoid other points, but its
  6183. * focus is performance over accuracy. Works well with
  6184. * less dense datasets.
  6185. *
  6186. * @typedef {"fastAvoid"|"simpleConnect"|"straight"|string} Highcharts.PathfinderTypeValue
  6187. */
  6188. ''; // detach doclets above
  6189. var defaultOptions = O.defaultOptions;
  6190. var addEvent = U.addEvent,
  6191. defined = U.defined,
  6192. error = U.error,
  6193. extend = U.extend,
  6194. merge = U.merge,
  6195. objectEach = U.objectEach,
  6196. pick = U.pick,
  6197. splat = U.splat;
  6198. var deg2rad = H.deg2rad,
  6199. max = Math.max,
  6200. min = Math.min;
  6201. /*
  6202. @todo:
  6203. - Document how to write your own algorithms
  6204. - Consider adding a Point.pathTo method that wraps creating a connection
  6205. and rendering it
  6206. */
  6207. // Set default Pathfinder options
  6208. extend(defaultOptions, {
  6209. /**
  6210. * The Pathfinder module allows you to define connections between any two
  6211. * points, represented as lines - optionally with markers for the start
  6212. * and/or end points. Multiple algorithms are available for calculating how
  6213. * the connecting lines are drawn.
  6214. *
  6215. * Connector functionality requires Highcharts Gantt to be loaded. In Gantt
  6216. * charts, the connectors are used to draw dependencies between tasks.
  6217. *
  6218. * @see [dependency](series.gantt.data.dependency)
  6219. *
  6220. * @sample gantt/pathfinder/demo
  6221. * Pathfinder connections
  6222. *
  6223. * @declare Highcharts.ConnectorsOptions
  6224. * @product gantt
  6225. * @optionparent connectors
  6226. */
  6227. connectors: {
  6228. /**
  6229. * Enable connectors for this chart. Requires Highcharts Gantt.
  6230. *
  6231. * @type {boolean}
  6232. * @default true
  6233. * @since 6.2.0
  6234. * @apioption connectors.enabled
  6235. */
  6236. /**
  6237. * Set the default dash style for this chart's connecting lines.
  6238. *
  6239. * @type {string}
  6240. * @default solid
  6241. * @since 6.2.0
  6242. * @apioption connectors.dashStyle
  6243. */
  6244. /**
  6245. * Set the default color for this chart's Pathfinder connecting lines.
  6246. * Defaults to the color of the point being connected.
  6247. *
  6248. * @type {Highcharts.ColorString}
  6249. * @since 6.2.0
  6250. * @apioption connectors.lineColor
  6251. */
  6252. /**
  6253. * Set the default pathfinder margin to use, in pixels. Some Pathfinder
  6254. * algorithms attempt to avoid obstacles, such as other points in the
  6255. * chart. These algorithms use this margin to determine how close lines
  6256. * can be to an obstacle. The default is to compute this automatically
  6257. * from the size of the obstacles in the chart.
  6258. *
  6259. * To draw connecting lines close to existing points, set this to a low
  6260. * number. For more space around existing points, set this number
  6261. * higher.
  6262. *
  6263. * @sample gantt/pathfinder/algorithm-margin
  6264. * Small algorithmMargin
  6265. *
  6266. * @type {number}
  6267. * @since 6.2.0
  6268. * @apioption connectors.algorithmMargin
  6269. */
  6270. /**
  6271. * Set the default pathfinder algorithm to use for this chart. It is
  6272. * possible to define your own algorithms by adding them to the
  6273. * Highcharts.Pathfinder.prototype.algorithms object before the chart
  6274. * has been created.
  6275. *
  6276. * The default algorithms are as follows:
  6277. *
  6278. * `straight`: Draws a straight line between the connecting
  6279. * points. Does not avoid other points when drawing.
  6280. *
  6281. * `simpleConnect`: Finds a path between the points using right angles
  6282. * only. Takes only starting/ending points into
  6283. * account, and will not avoid other points.
  6284. *
  6285. * `fastAvoid`: Finds a path between the points using right angles
  6286. * only. Will attempt to avoid other points, but its
  6287. * focus is performance over accuracy. Works well with
  6288. * less dense datasets.
  6289. *
  6290. * Default value: `straight` is used as default for most series types,
  6291. * while `simpleConnect` is used as default for Gantt series, to show
  6292. * dependencies between points.
  6293. *
  6294. * @sample gantt/pathfinder/demo
  6295. * Different types used
  6296. *
  6297. * @type {Highcharts.PathfinderTypeValue}
  6298. * @default undefined
  6299. * @since 6.2.0
  6300. */
  6301. type: 'straight',
  6302. /**
  6303. * Set the default pixel width for this chart's Pathfinder connecting
  6304. * lines.
  6305. *
  6306. * @since 6.2.0
  6307. */
  6308. lineWidth: 1,
  6309. /**
  6310. * Marker options for this chart's Pathfinder connectors. Note that
  6311. * this option is overridden by the `startMarker` and `endMarker`
  6312. * options.
  6313. *
  6314. * @declare Highcharts.ConnectorsMarkerOptions
  6315. * @since 6.2.0
  6316. */
  6317. marker: {
  6318. /**
  6319. * Set the radius of the connector markers. The default is
  6320. * automatically computed based on the algorithmMargin setting.
  6321. *
  6322. * Setting marker.width and marker.height will override this
  6323. * setting.
  6324. *
  6325. * @type {number}
  6326. * @since 6.2.0
  6327. * @apioption connectors.marker.radius
  6328. */
  6329. /**
  6330. * Set the width of the connector markers. If not supplied, this
  6331. * is inferred from the marker radius.
  6332. *
  6333. * @type {number}
  6334. * @since 6.2.0
  6335. * @apioption connectors.marker.width
  6336. */
  6337. /**
  6338. * Set the height of the connector markers. If not supplied, this
  6339. * is inferred from the marker radius.
  6340. *
  6341. * @type {number}
  6342. * @since 6.2.0
  6343. * @apioption connectors.marker.height
  6344. */
  6345. /**
  6346. * Set the color of the connector markers. By default this is the
  6347. * same as the connector color.
  6348. *
  6349. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6350. * @since 6.2.0
  6351. * @apioption connectors.marker.color
  6352. */
  6353. /**
  6354. * Set the line/border color of the connector markers. By default
  6355. * this is the same as the marker color.
  6356. *
  6357. * @type {Highcharts.ColorString}
  6358. * @since 6.2.0
  6359. * @apioption connectors.marker.lineColor
  6360. */
  6361. /**
  6362. * Enable markers for the connectors.
  6363. */
  6364. enabled: false,
  6365. /**
  6366. * Horizontal alignment of the markers relative to the points.
  6367. *
  6368. * @type {Highcharts.AlignValue}
  6369. */
  6370. align: 'center',
  6371. /**
  6372. * Vertical alignment of the markers relative to the points.
  6373. *
  6374. * @type {Highcharts.VerticalAlignValue}
  6375. */
  6376. verticalAlign: 'middle',
  6377. /**
  6378. * Whether or not to draw the markers inside the points.
  6379. */
  6380. inside: false,
  6381. /**
  6382. * Set the line/border width of the pathfinder markers.
  6383. */
  6384. lineWidth: 1
  6385. },
  6386. /**
  6387. * Marker options specific to the start markers for this chart's
  6388. * Pathfinder connectors. Overrides the generic marker options.
  6389. *
  6390. * @declare Highcharts.ConnectorsStartMarkerOptions
  6391. * @extends connectors.marker
  6392. * @since 6.2.0
  6393. */
  6394. startMarker: {
  6395. /**
  6396. * Set the symbol of the connector start markers.
  6397. */
  6398. symbol: 'diamond'
  6399. },
  6400. /**
  6401. * Marker options specific to the end markers for this chart's
  6402. * Pathfinder connectors. Overrides the generic marker options.
  6403. *
  6404. * @declare Highcharts.ConnectorsEndMarkerOptions
  6405. * @extends connectors.marker
  6406. * @since 6.2.0
  6407. */
  6408. endMarker: {
  6409. /**
  6410. * Set the symbol of the connector end markers.
  6411. */
  6412. symbol: 'arrow-filled'
  6413. }
  6414. }
  6415. });
  6416. /**
  6417. * Override Pathfinder connector options for a series. Requires Highcharts Gantt
  6418. * to be loaded.
  6419. *
  6420. * @declare Highcharts.SeriesConnectorsOptionsObject
  6421. * @extends connectors
  6422. * @since 6.2.0
  6423. * @excluding enabled, algorithmMargin
  6424. * @product gantt
  6425. * @apioption plotOptions.series.connectors
  6426. */
  6427. /**
  6428. * Connect to a point. This option can be either a string, referring to the ID
  6429. * of another point, or an object, or an array of either. If the option is an
  6430. * array, each element defines a connection.
  6431. *
  6432. * @sample gantt/pathfinder/demo
  6433. * Different connection types
  6434. *
  6435. * @declare Highcharts.XrangePointConnectorsOptionsObject
  6436. * @type {string|Array<string|*>|*}
  6437. * @extends plotOptions.series.connectors
  6438. * @since 6.2.0
  6439. * @excluding enabled
  6440. * @product gantt
  6441. * @requires highcharts-gantt
  6442. * @apioption series.xrange.data.connect
  6443. */
  6444. /**
  6445. * The ID of the point to connect to.
  6446. *
  6447. * @type {string}
  6448. * @since 6.2.0
  6449. * @product gantt
  6450. * @apioption series.xrange.data.connect.to
  6451. */
  6452. /**
  6453. * Get point bounding box using plotX/plotY and shapeArgs. If using
  6454. * graphic.getBBox() directly, the bbox will be affected by animation.
  6455. *
  6456. * @private
  6457. * @function
  6458. *
  6459. * @param {Highcharts.Point} point
  6460. * The point to get BB of.
  6461. *
  6462. * @return {Highcharts.Dictionary<number>|null}
  6463. * Result xMax, xMin, yMax, yMin.
  6464. */
  6465. function getPointBB(point) {
  6466. var shapeArgs = point.shapeArgs,
  6467. bb;
  6468. // Prefer using shapeArgs (columns)
  6469. if (shapeArgs) {
  6470. return {
  6471. xMin: shapeArgs.x,
  6472. xMax: shapeArgs.x + shapeArgs.width,
  6473. yMin: shapeArgs.y,
  6474. yMax: shapeArgs.y + shapeArgs.height
  6475. };
  6476. }
  6477. // Otherwise use plotX/plotY and bb
  6478. bb = point.graphic && point.graphic.getBBox();
  6479. return bb ? {
  6480. xMin: point.plotX - bb.width / 2,
  6481. xMax: point.plotX + bb.width / 2,
  6482. yMin: point.plotY - bb.height / 2,
  6483. yMax: point.plotY + bb.height / 2
  6484. } : null;
  6485. }
  6486. /**
  6487. * Calculate margin to place around obstacles for the pathfinder in pixels.
  6488. * Returns a minimum of 1 pixel margin.
  6489. *
  6490. * @private
  6491. * @function
  6492. *
  6493. * @param {Array<object>} obstacles
  6494. * Obstacles to calculate margin from.
  6495. *
  6496. * @return {number}
  6497. * The calculated margin in pixels. At least 1.
  6498. */
  6499. function calculateObstacleMargin(obstacles) {
  6500. var len = obstacles.length,
  6501. i = 0,
  6502. j,
  6503. obstacleDistance,
  6504. distances = [],
  6505. // Compute smallest distance between two rectangles
  6506. distance = function (a,
  6507. b,
  6508. bbMargin) {
  6509. // Count the distance even if we are slightly off
  6510. var margin = pick(bbMargin, 10),
  6511. yOverlap = a.yMax + margin > b.yMin - margin &&
  6512. a.yMin - margin < b.yMax + margin,
  6513. xOverlap = a.xMax + margin > b.xMin - margin &&
  6514. a.xMin - margin < b.xMax + margin,
  6515. xDistance = yOverlap ? (a.xMin > b.xMax ? a.xMin - b.xMax : b.xMin - a.xMax) : Infinity,
  6516. yDistance = xOverlap ? (a.yMin > b.yMax ? a.yMin - b.yMax : b.yMin - a.yMax) : Infinity;
  6517. // If the rectangles collide, try recomputing with smaller margin.
  6518. // If they collide anyway, discard the obstacle.
  6519. if (xOverlap && yOverlap) {
  6520. return (margin ?
  6521. distance(a, b, Math.floor(margin / 2)) :
  6522. Infinity);
  6523. }
  6524. return min(xDistance, yDistance);
  6525. };
  6526. // Go over all obstacles and compare them to the others.
  6527. for (; i < len; ++i) {
  6528. // Compare to all obstacles ahead. We will already have compared this
  6529. // obstacle to the ones before.
  6530. for (j = i + 1; j < len; ++j) {
  6531. obstacleDistance = distance(obstacles[i], obstacles[j]);
  6532. // TODO: Magic number 80
  6533. if (obstacleDistance < 80) { // Ignore large distances
  6534. distances.push(obstacleDistance);
  6535. }
  6536. }
  6537. }
  6538. // Ensure we always have at least one value, even in very spaceous charts
  6539. distances.push(80);
  6540. return max(Math.floor(distances.sort(function (a, b) {
  6541. return (a - b);
  6542. })[
  6543. // Discard first 10% of the relevant distances, and then grab
  6544. // the smallest one.
  6545. Math.floor(distances.length / 10)] / 2 - 1 // Divide the distance by 2 and subtract 1.
  6546. ), 1 // 1 is the minimum margin
  6547. );
  6548. }
  6549. /* eslint-disable no-invalid-this, valid-jsdoc */
  6550. /**
  6551. * The Pathfinder class.
  6552. *
  6553. * @private
  6554. * @class
  6555. * @name Highcharts.Pathfinder
  6556. *
  6557. * @param {Highcharts.Chart} chart
  6558. * The chart to operate on.
  6559. */
  6560. var Pathfinder = /** @class */ (function () {
  6561. function Pathfinder(chart) {
  6562. /* *
  6563. *
  6564. * Properties
  6565. *
  6566. * */
  6567. this.chart = void 0;
  6568. this.chartObstacles = void 0;
  6569. this.chartObstacleMetrics = void 0;
  6570. this.connections = void 0;
  6571. this.group = void 0;
  6572. this.lineObstacles = void 0;
  6573. this.init(chart);
  6574. }
  6575. /**
  6576. * @name Highcharts.Pathfinder#algorithms
  6577. * @type {Highcharts.Dictionary<Function>}
  6578. */
  6579. /**
  6580. * Initialize the Pathfinder object.
  6581. *
  6582. * @function Highcharts.Pathfinder#init
  6583. *
  6584. * @param {Highcharts.Chart} chart
  6585. * The chart context.
  6586. */
  6587. Pathfinder.prototype.init = function (chart) {
  6588. // Initialize pathfinder with chart context
  6589. this.chart = chart;
  6590. // Init connection reference list
  6591. this.connections = [];
  6592. // Recalculate paths/obstacles on chart redraw
  6593. addEvent(chart, 'redraw', function () {
  6594. this.pathfinder.update();
  6595. });
  6596. };
  6597. /**
  6598. * Update Pathfinder connections from scratch.
  6599. *
  6600. * @function Highcharts.Pathfinder#update
  6601. *
  6602. * @param {boolean} [deferRender]
  6603. * Whether or not to defer rendering of connections until
  6604. * series.afterAnimate event has fired. Used on first render.
  6605. */
  6606. Pathfinder.prototype.update = function (deferRender) {
  6607. var chart = this.chart,
  6608. pathfinder = this,
  6609. oldConnections = pathfinder.connections;
  6610. // Rebuild pathfinder connections from options
  6611. pathfinder.connections = [];
  6612. chart.series.forEach(function (series) {
  6613. if (series.visible && !series.options.isInternal) {
  6614. series.points.forEach(function (point) {
  6615. var ganttPointOptions = point.options;
  6616. // For Gantt series the connect could be
  6617. // defined as a dependency
  6618. if (ganttPointOptions && ganttPointOptions.dependency) {
  6619. ganttPointOptions.connect = ganttPointOptions.dependency;
  6620. }
  6621. var to,
  6622. connects = (point.options &&
  6623. point.options.connect &&
  6624. splat(point.options.connect));
  6625. if (point.visible && point.isInside !== false && connects) {
  6626. connects.forEach(function (connect) {
  6627. to = chart.get(typeof connect === 'string' ?
  6628. connect : connect.to);
  6629. if (to instanceof Point &&
  6630. to.series.visible &&
  6631. to.visible &&
  6632. to.isInside !== false) {
  6633. // Add new connection
  6634. pathfinder.connections.push(new Connection(point, // from
  6635. to, typeof connect === 'string' ?
  6636. {} :
  6637. connect));
  6638. }
  6639. });
  6640. }
  6641. });
  6642. }
  6643. });
  6644. // Clear connections that should not be updated, and move old info over
  6645. // to new connections.
  6646. for (var j = 0, k, found, lenOld = oldConnections.length, lenNew = pathfinder.connections.length; j < lenOld; ++j) {
  6647. found = false;
  6648. for (k = 0; k < lenNew; ++k) {
  6649. if (oldConnections[j].fromPoint ===
  6650. pathfinder.connections[k].fromPoint &&
  6651. oldConnections[j].toPoint ===
  6652. pathfinder.connections[k].toPoint) {
  6653. pathfinder.connections[k].graphics =
  6654. oldConnections[j].graphics;
  6655. found = true;
  6656. break;
  6657. }
  6658. }
  6659. if (!found) {
  6660. oldConnections[j].destroy();
  6661. }
  6662. }
  6663. // Clear obstacles to force recalculation. This must be done on every
  6664. // redraw in case positions have changed. Recalculation is handled in
  6665. // Connection.getPath on demand.
  6666. delete this.chartObstacles;
  6667. delete this.lineObstacles;
  6668. // Draw the pending connections
  6669. pathfinder.renderConnections(deferRender);
  6670. };
  6671. /**
  6672. * Draw the chart's connecting paths.
  6673. *
  6674. * @function Highcharts.Pathfinder#renderConnections
  6675. *
  6676. * @param {boolean} [deferRender]
  6677. * Whether or not to defer render until series animation is finished.
  6678. * Used on first render.
  6679. */
  6680. Pathfinder.prototype.renderConnections = function (deferRender) {
  6681. if (deferRender) {
  6682. // Render after series are done animating
  6683. this.chart.series.forEach(function (series) {
  6684. var render = function () {
  6685. // Find pathfinder connections belonging to this series
  6686. // that haven't rendered, and render them now.
  6687. var pathfinder = series.chart.pathfinder,
  6688. conns = pathfinder && pathfinder.connections || [];
  6689. conns.forEach(function (connection) {
  6690. if (connection.fromPoint &&
  6691. connection.fromPoint.series === series) {
  6692. connection.render();
  6693. }
  6694. });
  6695. if (series.pathfinderRemoveRenderEvent) {
  6696. series.pathfinderRemoveRenderEvent();
  6697. delete series.pathfinderRemoveRenderEvent;
  6698. }
  6699. };
  6700. if (series.options.animation === false) {
  6701. render();
  6702. }
  6703. else {
  6704. series.pathfinderRemoveRenderEvent = addEvent(series, 'afterAnimate', render);
  6705. }
  6706. });
  6707. }
  6708. else {
  6709. // Go through connections and render them
  6710. this.connections.forEach(function (connection) {
  6711. connection.render();
  6712. });
  6713. }
  6714. };
  6715. /**
  6716. * Get obstacles for the points in the chart. Does not include connecting
  6717. * lines from Pathfinder. Applies algorithmMargin to the obstacles.
  6718. *
  6719. * @function Highcharts.Pathfinder#getChartObstacles
  6720. *
  6721. * @param {object} options
  6722. * Options for the calculation. Currenlty only
  6723. * options.algorithmMargin.
  6724. *
  6725. * @return {Array<object>}
  6726. * An array of calculated obstacles. Each obstacle is defined as an
  6727. * object with xMin, xMax, yMin and yMax properties.
  6728. */
  6729. Pathfinder.prototype.getChartObstacles = function (options) {
  6730. var obstacles = [],
  6731. series = this.chart.series,
  6732. margin = pick(options.algorithmMargin, 0),
  6733. calculatedMargin;
  6734. for (var i = 0, sLen = series.length; i < sLen; ++i) {
  6735. if (series[i].visible && !series[i].options.isInternal) {
  6736. for (var j = 0, pLen = series[i].points.length, bb, point; j < pLen; ++j) {
  6737. point = series[i].points[j];
  6738. if (point.visible) {
  6739. bb = getPointBB(point);
  6740. if (bb) {
  6741. obstacles.push({
  6742. xMin: bb.xMin - margin,
  6743. xMax: bb.xMax + margin,
  6744. yMin: bb.yMin - margin,
  6745. yMax: bb.yMax + margin
  6746. });
  6747. }
  6748. }
  6749. }
  6750. }
  6751. }
  6752. // Sort obstacles by xMin for optimization
  6753. obstacles = obstacles.sort(function (a, b) {
  6754. return a.xMin - b.xMin;
  6755. });
  6756. // Add auto-calculated margin if the option is not defined
  6757. if (!defined(options.algorithmMargin)) {
  6758. calculatedMargin =
  6759. options.algorithmMargin =
  6760. calculateObstacleMargin(obstacles);
  6761. obstacles.forEach(function (obstacle) {
  6762. obstacle.xMin -= calculatedMargin;
  6763. obstacle.xMax += calculatedMargin;
  6764. obstacle.yMin -= calculatedMargin;
  6765. obstacle.yMax += calculatedMargin;
  6766. });
  6767. }
  6768. return obstacles;
  6769. };
  6770. /**
  6771. * Utility function to get metrics for obstacles:
  6772. * - Widest obstacle width
  6773. * - Tallest obstacle height
  6774. *
  6775. * @function Highcharts.Pathfinder#getObstacleMetrics
  6776. *
  6777. * @param {Array<object>} obstacles
  6778. * An array of obstacles to inspect.
  6779. *
  6780. * @return {object}
  6781. * The calculated metrics, as an object with maxHeight and maxWidth
  6782. * properties.
  6783. */
  6784. Pathfinder.prototype.getObstacleMetrics = function (obstacles) {
  6785. var maxWidth = 0,
  6786. maxHeight = 0,
  6787. width,
  6788. height,
  6789. i = obstacles.length;
  6790. while (i--) {
  6791. width = obstacles[i].xMax - obstacles[i].xMin;
  6792. height = obstacles[i].yMax - obstacles[i].yMin;
  6793. if (maxWidth < width) {
  6794. maxWidth = width;
  6795. }
  6796. if (maxHeight < height) {
  6797. maxHeight = height;
  6798. }
  6799. }
  6800. return {
  6801. maxHeight: maxHeight,
  6802. maxWidth: maxWidth
  6803. };
  6804. };
  6805. /**
  6806. * Utility to get which direction to start the pathfinding algorithm
  6807. * (X vs Y), calculated from a set of marker options.
  6808. *
  6809. * @function Highcharts.Pathfinder#getAlgorithmStartDirection
  6810. *
  6811. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  6812. * Marker options to calculate from.
  6813. *
  6814. * @return {boolean}
  6815. * Returns true for X, false for Y, and undefined for autocalculate.
  6816. */
  6817. Pathfinder.prototype.getAlgorithmStartDirection = function (markerOptions) {
  6818. var xCenter = markerOptions.align !== 'left' &&
  6819. markerOptions.align !== 'right', yCenter = markerOptions.verticalAlign !== 'top' &&
  6820. markerOptions.verticalAlign !== 'bottom', undef;
  6821. return xCenter ?
  6822. (yCenter ? undef : false) : // x is centered
  6823. (yCenter ? true : undef); // x is off-center
  6824. };
  6825. return Pathfinder;
  6826. }());
  6827. Pathfinder.prototype.algorithms = pathfinderAlgorithms;
  6828. // Add to Highcharts namespace
  6829. H.Pathfinder = Pathfinder;
  6830. // Add pathfinding capabilities to Points
  6831. extend(Point.prototype, /** @lends Point.prototype */ {
  6832. /**
  6833. * Get coordinates of anchor point for pathfinder connection.
  6834. *
  6835. * @private
  6836. * @function Highcharts.Point#getPathfinderAnchorPoint
  6837. *
  6838. * @param {Highcharts.ConnectorsMarkerOptions} markerOptions
  6839. * Connection options for position on point.
  6840. *
  6841. * @return {Highcharts.PositionObject}
  6842. * An object with x/y properties for the position. Coordinates are
  6843. * in plot values, not relative to point.
  6844. */
  6845. getPathfinderAnchorPoint: function (markerOptions) {
  6846. var bb = getPointBB(this),
  6847. x,
  6848. y;
  6849. switch (markerOptions.align) { // eslint-disable-line default-case
  6850. case 'right':
  6851. x = 'xMax';
  6852. break;
  6853. case 'left':
  6854. x = 'xMin';
  6855. }
  6856. switch (markerOptions.verticalAlign) { // eslint-disable-line default-case
  6857. case 'top':
  6858. y = 'yMin';
  6859. break;
  6860. case 'bottom':
  6861. y = 'yMax';
  6862. }
  6863. return {
  6864. x: x ? bb[x] : (bb.xMin + bb.xMax) / 2,
  6865. y: y ? bb[y] : (bb.yMin + bb.yMax) / 2
  6866. };
  6867. },
  6868. /**
  6869. * Utility to get the angle from one point to another.
  6870. *
  6871. * @private
  6872. * @function Highcharts.Point#getRadiansToVector
  6873. *
  6874. * @param {Highcharts.PositionObject} v1
  6875. * The first vector, as an object with x/y properties.
  6876. *
  6877. * @param {Highcharts.PositionObject} v2
  6878. * The second vector, as an object with x/y properties.
  6879. *
  6880. * @return {number}
  6881. * The angle in degrees
  6882. */
  6883. getRadiansToVector: function (v1, v2) {
  6884. var box;
  6885. if (!defined(v2)) {
  6886. box = getPointBB(this);
  6887. if (box) {
  6888. v2 = {
  6889. x: (box.xMin + box.xMax) / 2,
  6890. y: (box.yMin + box.yMax) / 2
  6891. };
  6892. }
  6893. }
  6894. return Math.atan2(v2.y - v1.y, v1.x - v2.x);
  6895. },
  6896. /**
  6897. * Utility to get the position of the marker, based on the path angle and
  6898. * the marker's radius.
  6899. *
  6900. * @private
  6901. * @function Highcharts.Point#getMarkerVector
  6902. *
  6903. * @param {number} radians
  6904. * The angle in radians from the point center to another vector.
  6905. *
  6906. * @param {number} markerRadius
  6907. * The radius of the marker, to calculate the additional distance to
  6908. * the center of the marker.
  6909. *
  6910. * @param {object} anchor
  6911. * The anchor point of the path and marker as an object with x/y
  6912. * properties.
  6913. *
  6914. * @return {object}
  6915. * The marker vector as an object with x/y properties.
  6916. */
  6917. getMarkerVector: function (radians, markerRadius, anchor) {
  6918. var twoPI = Math.PI * 2.0,
  6919. theta = radians,
  6920. bb = getPointBB(this),
  6921. rectWidth = bb.xMax - bb.xMin,
  6922. rectHeight = bb.yMax - bb.yMin,
  6923. rAtan = Math.atan2(rectHeight,
  6924. rectWidth),
  6925. tanTheta = 1,
  6926. leftOrRightRegion = false,
  6927. rectHalfWidth = rectWidth / 2.0,
  6928. rectHalfHeight = rectHeight / 2.0,
  6929. rectHorizontalCenter = bb.xMin + rectHalfWidth,
  6930. rectVerticalCenter = bb.yMin + rectHalfHeight,
  6931. edgePoint = {
  6932. x: rectHorizontalCenter,
  6933. y: rectVerticalCenter
  6934. },
  6935. xFactor = 1,
  6936. yFactor = 1;
  6937. while (theta < -Math.PI) {
  6938. theta += twoPI;
  6939. }
  6940. while (theta > Math.PI) {
  6941. theta -= twoPI;
  6942. }
  6943. tanTheta = Math.tan(theta);
  6944. if ((theta > -rAtan) && (theta <= rAtan)) {
  6945. // Right side
  6946. yFactor = -1;
  6947. leftOrRightRegion = true;
  6948. }
  6949. else if (theta > rAtan && theta <= (Math.PI - rAtan)) {
  6950. // Top side
  6951. yFactor = -1;
  6952. }
  6953. else if (theta > (Math.PI - rAtan) || theta <= -(Math.PI - rAtan)) {
  6954. // Left side
  6955. xFactor = -1;
  6956. leftOrRightRegion = true;
  6957. }
  6958. else {
  6959. // Bottom side
  6960. xFactor = -1;
  6961. }
  6962. // Correct the edgePoint according to the placement of the marker
  6963. if (leftOrRightRegion) {
  6964. edgePoint.x += xFactor * (rectHalfWidth);
  6965. edgePoint.y += yFactor * (rectHalfWidth) * tanTheta;
  6966. }
  6967. else {
  6968. edgePoint.x += xFactor * (rectHeight / (2.0 * tanTheta));
  6969. edgePoint.y += yFactor * (rectHalfHeight);
  6970. }
  6971. if (anchor.x !== rectHorizontalCenter) {
  6972. edgePoint.x = anchor.x;
  6973. }
  6974. if (anchor.y !== rectVerticalCenter) {
  6975. edgePoint.y = anchor.y;
  6976. }
  6977. return {
  6978. x: edgePoint.x + (markerRadius * Math.cos(theta)),
  6979. y: edgePoint.y - (markerRadius * Math.sin(theta))
  6980. };
  6981. }
  6982. });
  6983. /**
  6984. * Warn if using legacy options. Copy the options over. Note that this will
  6985. * still break if using the legacy options in chart.update, addSeries etc.
  6986. * @private
  6987. */
  6988. function warnLegacy(chart) {
  6989. if (chart.options.pathfinder ||
  6990. chart.series.reduce(function (acc, series) {
  6991. if (series.options) {
  6992. merge(true, (series.options.connectors = series.options.connectors ||
  6993. {}), series.options.pathfinder);
  6994. }
  6995. return acc || series.options && series.options.pathfinder;
  6996. }, false)) {
  6997. merge(true, (chart.options.connectors = chart.options.connectors || {}), chart.options.pathfinder);
  6998. error('WARNING: Pathfinder options have been renamed. ' +
  6999. 'Use "chart.connectors" or "series.connectors" instead.');
  7000. }
  7001. }
  7002. // Initialize Pathfinder for charts
  7003. Chart.prototype.callbacks.push(function (chart) {
  7004. var options = chart.options;
  7005. if (options.connectors.enabled !== false) {
  7006. warnLegacy(chart);
  7007. this.pathfinder = new Pathfinder(this);
  7008. this.pathfinder.update(true); // First draw, defer render
  7009. }
  7010. });
  7011. return Pathfinder;
  7012. });
  7013. _registerModule(_modules, 'Series/Gantt/GanttSeries.js', [_modules['Series/Gantt/GanttPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (GanttPoint, SeriesRegistry, U) {
  7014. /* *
  7015. *
  7016. * (c) 2016-2021 Highsoft AS
  7017. *
  7018. * Author: Lars A. V. Cabrera
  7019. *
  7020. * License: www.highcharts.com/license
  7021. *
  7022. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7023. *
  7024. * */
  7025. var __extends = (this && this.__extends) || (function () {
  7026. var extendStatics = function (d,
  7027. b) {
  7028. extendStatics = Object.setPrototypeOf ||
  7029. ({ __proto__: [] } instanceof Array && function (d,
  7030. b) { d.__proto__ = b; }) ||
  7031. function (d,
  7032. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  7033. return extendStatics(d, b);
  7034. };
  7035. return function (d, b) {
  7036. extendStatics(d, b);
  7037. function __() { this.constructor = d; }
  7038. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  7039. };
  7040. })();
  7041. var Series = SeriesRegistry.series,
  7042. XRangeSeries = SeriesRegistry.seriesTypes.xrange;
  7043. var extend = U.extend,
  7044. isNumber = U.isNumber,
  7045. merge = U.merge,
  7046. splat = U.splat;
  7047. /* *
  7048. *
  7049. * Class
  7050. *
  7051. * */
  7052. /**
  7053. * @private
  7054. * @class
  7055. * @name Highcharts.seriesTypes.gantt
  7056. *
  7057. * @augments Highcharts.Series
  7058. */
  7059. var GanttSeries = /** @class */ (function (_super) {
  7060. __extends(GanttSeries, _super);
  7061. function GanttSeries() {
  7062. var _this = _super !== null && _super.apply(this,
  7063. arguments) || this;
  7064. /* *
  7065. *
  7066. * Properties
  7067. *
  7068. * */
  7069. _this.data = void 0;
  7070. _this.options = void 0;
  7071. _this.points = void 0;
  7072. return _this;
  7073. /* eslint-enable valid-jsdoc */
  7074. }
  7075. /* *
  7076. *
  7077. * Functions
  7078. *
  7079. * */
  7080. /* eslint-disable valid-jsdoc */
  7081. /**
  7082. * Draws a single point in the series.
  7083. *
  7084. * This override draws the point as a diamond if point.options.milestone
  7085. * is true, and uses the original drawPoint() if it is false or not set.
  7086. *
  7087. * @requires highcharts-gantt
  7088. *
  7089. * @private
  7090. * @function Highcharts.seriesTypes.gantt#drawPoint
  7091. *
  7092. * @param {Highcharts.Point} point
  7093. * An instance of Point in the series
  7094. *
  7095. * @param {"animate"|"attr"} verb
  7096. * 'animate' (animates changes) or 'attr' (sets options)
  7097. */
  7098. GanttSeries.prototype.drawPoint = function (point, verb) {
  7099. var series = this,
  7100. seriesOpts = series.options,
  7101. renderer = series.chart.renderer,
  7102. shapeArgs = point.shapeArgs,
  7103. plotY = point.plotY,
  7104. graphic = point.graphic,
  7105. state = point.selected && 'select',
  7106. cutOff = seriesOpts.stacking && !seriesOpts.borderRadius,
  7107. diamondShape;
  7108. if (point.options.milestone) {
  7109. if (isNumber(plotY) && point.y !== null && point.visible !== false) {
  7110. diamondShape = renderer.symbols.diamond(shapeArgs.x, shapeArgs.y, shapeArgs.width, shapeArgs.height);
  7111. if (graphic) {
  7112. graphic[verb]({
  7113. d: diamondShape
  7114. });
  7115. }
  7116. else {
  7117. point.graphic = graphic = renderer.path(diamondShape)
  7118. .addClass(point.getClassName(), true)
  7119. .add(point.group || series.group);
  7120. }
  7121. // Presentational
  7122. if (!series.chart.styledMode) {
  7123. point.graphic
  7124. .attr(series.pointAttribs(point, state))
  7125. .shadow(seriesOpts.shadow, null, cutOff);
  7126. }
  7127. }
  7128. else if (graphic) {
  7129. point.graphic = graphic.destroy(); // #1269
  7130. }
  7131. }
  7132. else {
  7133. XRangeSeries.prototype.drawPoint.call(series, point, verb);
  7134. }
  7135. };
  7136. /**
  7137. * Handle milestones, as they have no x2.
  7138. * @private
  7139. */
  7140. GanttSeries.prototype.translatePoint = function (point) {
  7141. var series = this,
  7142. shapeArgs,
  7143. size;
  7144. XRangeSeries.prototype.translatePoint.call(series, point);
  7145. if (point.options.milestone) {
  7146. shapeArgs = point.shapeArgs;
  7147. size = shapeArgs.height;
  7148. point.shapeArgs = {
  7149. x: shapeArgs.x - (size / 2),
  7150. y: shapeArgs.y,
  7151. width: size,
  7152. height: size
  7153. };
  7154. }
  7155. };
  7156. /**
  7157. * A `gantt` series. If the [type](#series.gantt.type) option is not specified,
  7158. * it is inherited from [chart.type](#chart.type).
  7159. *
  7160. * @extends plotOptions.xrange
  7161. * @product gantt
  7162. * @requires highcharts-gantt
  7163. * @optionparent plotOptions.gantt
  7164. */
  7165. GanttSeries.defaultOptions = merge(XRangeSeries.defaultOptions, {
  7166. // options - default options merged with parent
  7167. grouping: false,
  7168. dataLabels: {
  7169. enabled: true
  7170. },
  7171. tooltip: {
  7172. headerFormat: '<span style="font-size: 10px">{series.name}</span><br/>',
  7173. pointFormat: null,
  7174. pointFormatter: function () {
  7175. var point = this,
  7176. series = point.series,
  7177. tooltip = series.chart.tooltip,
  7178. xAxis = series.xAxis,
  7179. formats = series.tooltipOptions.dateTimeLabelFormats,
  7180. startOfWeek = xAxis.options.startOfWeek,
  7181. ttOptions = series.tooltipOptions,
  7182. format = ttOptions.xDateFormat,
  7183. start,
  7184. end,
  7185. milestone = point.options.milestone,
  7186. retVal = '<b>' + (point.name || point.yCategory) + '</b>';
  7187. if (ttOptions.pointFormat) {
  7188. return point.tooltipFormatter(ttOptions.pointFormat);
  7189. }
  7190. if (!format) {
  7191. format = splat(tooltip.getDateFormat(xAxis.closestPointRange, point.start, startOfWeek, formats))[0];
  7192. }
  7193. start = series.chart.time.dateFormat(format, point.start);
  7194. end = series.chart.time.dateFormat(format, point.end);
  7195. retVal += '<br/>';
  7196. if (!milestone) {
  7197. retVal += 'Start: ' + start + '<br/>';
  7198. retVal += 'End: ' + end + '<br/>';
  7199. }
  7200. else {
  7201. retVal += start + '<br/>';
  7202. }
  7203. return retVal;
  7204. }
  7205. },
  7206. connectors: {
  7207. type: 'simpleConnect',
  7208. /**
  7209. * @declare Highcharts.ConnectorsAnimationOptionsObject
  7210. */
  7211. animation: {
  7212. reversed: true // Dependencies go from child to parent
  7213. },
  7214. startMarker: {
  7215. enabled: true,
  7216. symbol: 'arrow-filled',
  7217. radius: 4,
  7218. fill: '#fa0',
  7219. align: 'left'
  7220. },
  7221. endMarker: {
  7222. enabled: false,
  7223. align: 'right'
  7224. }
  7225. }
  7226. });
  7227. return GanttSeries;
  7228. }(XRangeSeries));
  7229. extend(GanttSeries.prototype, {
  7230. // Keyboard navigation, don't use nearest vertical mode
  7231. keyboardMoveVertical: false,
  7232. pointArrayMap: ['start', 'end', 'y'],
  7233. pointClass: GanttPoint,
  7234. setData: Series.prototype.setData
  7235. });
  7236. SeriesRegistry.registerSeriesType('gantt', GanttSeries);
  7237. /* *
  7238. *
  7239. * Default Export
  7240. *
  7241. * */
  7242. /* *
  7243. *
  7244. * API Options
  7245. *
  7246. * */
  7247. /**
  7248. * A `gantt` series.
  7249. *
  7250. * @extends series,plotOptions.gantt
  7251. * @excluding boostThreshold, connectors, dashStyle, findNearestPointBy,
  7252. * getExtremesFromAll, marker, negativeColor, pointInterval,
  7253. * pointIntervalUnit, pointPlacement, pointStart
  7254. * @product gantt
  7255. * @requires highcharts-gantt
  7256. * @apioption series.gantt
  7257. */
  7258. /**
  7259. * Data for a Gantt series.
  7260. *
  7261. * @declare Highcharts.GanttPointOptionsObject
  7262. * @type {Array<*>}
  7263. * @extends series.xrange.data
  7264. * @excluding className, connect, dataLabels, events,
  7265. * partialFill, selected, x, x2
  7266. * @product gantt
  7267. * @apioption series.gantt.data
  7268. */
  7269. /**
  7270. * Whether the grid node belonging to this point should start as collapsed. Used
  7271. * in axes of type treegrid.
  7272. *
  7273. * @sample {gantt} gantt/treegrid-axis/collapsed/
  7274. * Start as collapsed
  7275. *
  7276. * @type {boolean}
  7277. * @default false
  7278. * @product gantt
  7279. * @apioption series.gantt.data.collapsed
  7280. */
  7281. /**
  7282. * The start time of a task.
  7283. *
  7284. * @type {number}
  7285. * @product gantt
  7286. * @apioption series.gantt.data.start
  7287. */
  7288. /**
  7289. * The end time of a task.
  7290. *
  7291. * @type {number}
  7292. * @product gantt
  7293. * @apioption series.gantt.data.end
  7294. */
  7295. /**
  7296. * The Y value of a task.
  7297. *
  7298. * @type {number}
  7299. * @product gantt
  7300. * @apioption series.gantt.data.y
  7301. */
  7302. /**
  7303. * The name of a task. If a `treegrid` y-axis is used (default in Gantt charts),
  7304. * this will be picked up automatically, and used to calculate the y-value.
  7305. *
  7306. * @type {string}
  7307. * @product gantt
  7308. * @apioption series.gantt.data.name
  7309. */
  7310. /**
  7311. * Progress indicator, how much of the task completed. If it is a number, the
  7312. * `fill` will be applied automatically.
  7313. *
  7314. * @sample {gantt} gantt/demo/progress-indicator
  7315. * Progress indicator
  7316. *
  7317. * @type {number|*}
  7318. * @extends series.xrange.data.partialFill
  7319. * @product gantt
  7320. * @apioption series.gantt.data.completed
  7321. */
  7322. /**
  7323. * The amount of the progress indicator, ranging from 0 (not started) to 1
  7324. * (finished).
  7325. *
  7326. * @type {number}
  7327. * @default 0
  7328. * @apioption series.gantt.data.completed.amount
  7329. */
  7330. /**
  7331. * The fill of the progress indicator. Defaults to a darkened variety of the
  7332. * main color.
  7333. *
  7334. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  7335. * @apioption series.gantt.data.completed.fill
  7336. */
  7337. /**
  7338. * The ID of the point (task) that this point depends on in Gantt charts.
  7339. * Aliases [connect](series.xrange.data.connect). Can also be an object,
  7340. * specifying further connecting [options](series.gantt.connectors) between the
  7341. * points. Multiple connections can be specified by providing an array.
  7342. *
  7343. * @sample gantt/demo/project-management
  7344. * Dependencies
  7345. * @sample gantt/pathfinder/demo
  7346. * Different connection types
  7347. *
  7348. * @type {string|Array<string|*>|*}
  7349. * @extends series.xrange.data.connect
  7350. * @since 6.2.0
  7351. * @product gantt
  7352. * @apioption series.gantt.data.dependency
  7353. */
  7354. /**
  7355. * Whether this point is a milestone. If so, only the `start` option is handled,
  7356. * while `end` is ignored.
  7357. *
  7358. * @sample gantt/gantt/milestones
  7359. * Milestones
  7360. *
  7361. * @type {boolean}
  7362. * @since 6.2.0
  7363. * @product gantt
  7364. * @apioption series.gantt.data.milestone
  7365. */
  7366. /**
  7367. * The ID of the parent point (task) of this point in Gantt charts.
  7368. *
  7369. * @sample gantt/demo/subtasks
  7370. * Gantt chart with subtasks
  7371. *
  7372. * @type {string}
  7373. * @since 6.2.0
  7374. * @product gantt
  7375. * @apioption series.gantt.data.parent
  7376. */
  7377. /**
  7378. * @excluding afterAnimate
  7379. * @apioption series.gantt.events
  7380. */
  7381. ''; // adds doclets above to the transpiled file
  7382. return GanttSeries;
  7383. });
  7384. _registerModule(_modules, 'Core/Chart/GanttChart.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Chart, H, U) {
  7385. /* *
  7386. *
  7387. * (c) 2016-2021 Highsoft AS
  7388. *
  7389. * Author: Lars A. V. Cabrera
  7390. *
  7391. * License: www.highcharts.com/license
  7392. *
  7393. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7394. *
  7395. * */
  7396. var getOptions = U.getOptions,
  7397. isArray = U.isArray,
  7398. merge = U.merge,
  7399. splat = U.splat;
  7400. /**
  7401. * Factory function for Gantt charts.
  7402. *
  7403. * @example
  7404. * // Render a chart in to div#container
  7405. * var chart = Highcharts.ganttChart('container', {
  7406. * title: {
  7407. * text: 'My chart'
  7408. * },
  7409. * series: [{
  7410. * data: ...
  7411. * }]
  7412. * });
  7413. *
  7414. * @function Highcharts.ganttChart
  7415. *
  7416. * @param {string|Highcharts.HTMLDOMElement} renderTo
  7417. * The DOM element to render to, or its id.
  7418. *
  7419. * @param {Highcharts.Options} options
  7420. * The chart options structure.
  7421. *
  7422. * @param {Highcharts.ChartCallbackFunction} [callback]
  7423. * Function to run when the chart has loaded and and all external images
  7424. * are loaded. Defining a
  7425. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  7426. * handler is equivalent.
  7427. *
  7428. * @return {Highcharts.Chart}
  7429. * Returns the Chart object.
  7430. */
  7431. H.ganttChart = function (renderTo, options, callback) {
  7432. var hasRenderToArg = typeof renderTo === 'string' || renderTo.nodeName,
  7433. seriesOptions = options.series,
  7434. defaultOptions = getOptions(),
  7435. defaultLinkedTo,
  7436. userOptions = options;
  7437. options = arguments[hasRenderToArg ? 1 : 0];
  7438. // If user hasn't defined axes as array, make it into an array and add a
  7439. // second axis by default.
  7440. if (!isArray(options.xAxis)) {
  7441. options.xAxis = [options.xAxis || {}, {}];
  7442. }
  7443. // apply X axis options to both single and multi x axes
  7444. options.xAxis = options.xAxis.map(function (xAxisOptions, i) {
  7445. if (i === 1) { // Second xAxis
  7446. defaultLinkedTo = 0;
  7447. }
  7448. return merge(defaultOptions.xAxis, {
  7449. grid: {
  7450. enabled: true
  7451. },
  7452. opposite: true,
  7453. linkedTo: defaultLinkedTo
  7454. }, xAxisOptions, // user options
  7455. {
  7456. type: 'datetime'
  7457. });
  7458. });
  7459. // apply Y axis options to both single and multi y axes
  7460. options.yAxis = (splat(options.yAxis || {})).map(function (yAxisOptions) {
  7461. return merge(defaultOptions.yAxis, // #3802
  7462. {
  7463. grid: {
  7464. enabled: true
  7465. },
  7466. staticScale: 50,
  7467. reversed: true,
  7468. // Set default type treegrid, but only if 'categories' is
  7469. // undefined
  7470. type: yAxisOptions.categories ? yAxisOptions.type : 'treegrid'
  7471. }, yAxisOptions // user options
  7472. );
  7473. });
  7474. options.series = null;
  7475. options = merge(true, {
  7476. chart: {
  7477. type: 'gantt'
  7478. },
  7479. title: {
  7480. text: null
  7481. },
  7482. legend: {
  7483. enabled: false
  7484. },
  7485. navigator: {
  7486. series: { type: 'gantt' },
  7487. // Bars were clipped, #14060.
  7488. yAxis: {
  7489. type: 'category'
  7490. }
  7491. }
  7492. }, options, // user's options
  7493. // forced options
  7494. {
  7495. isGantt: true
  7496. });
  7497. options.series = userOptions.series = seriesOptions;
  7498. return hasRenderToArg ?
  7499. new Chart(renderTo, options, callback) :
  7500. new Chart(options, options); // @todo does not look correct
  7501. };
  7502. });
  7503. _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  7504. /* *
  7505. *
  7506. * (c) 2010-2021 Torstein Honsi
  7507. *
  7508. * License: www.highcharts.com/license
  7509. *
  7510. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7511. *
  7512. * */
  7513. var addEvent = U.addEvent,
  7514. defined = U.defined,
  7515. pick = U.pick;
  7516. /* eslint-disable no-invalid-this, valid-jsdoc */
  7517. /**
  7518. * Creates scrollbars if enabled.
  7519. *
  7520. * @private
  7521. */
  7522. var ScrollbarAxis = /** @class */ (function () {
  7523. function ScrollbarAxis() {
  7524. }
  7525. /**
  7526. * Attaches to axis events to create scrollbars if enabled.
  7527. *
  7528. * @private
  7529. *
  7530. * @param AxisClass
  7531. * Axis class to extend.
  7532. *
  7533. * @param ScrollbarClass
  7534. * Scrollbar class to use.
  7535. */
  7536. ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) {
  7537. var getExtremes = function (axis) {
  7538. var axisMin = pick(axis.options && axis.options.min, axis.min);
  7539. var axisMax = pick(axis.options && axis.options.max,
  7540. axis.max);
  7541. return {
  7542. axisMin: axisMin,
  7543. axisMax: axisMax,
  7544. scrollMin: defined(axis.dataMin) ?
  7545. Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin,
  7546. scrollMax: defined(axis.dataMax) ?
  7547. Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax
  7548. };
  7549. };
  7550. // Wrap axis initialization and create scrollbar if enabled:
  7551. addEvent(AxisClass, 'afterInit', function () {
  7552. var axis = this;
  7553. if (axis.options &&
  7554. axis.options.scrollbar &&
  7555. axis.options.scrollbar.enabled) {
  7556. // Predefined options:
  7557. axis.options.scrollbar.vertical = !axis.horiz;
  7558. axis.options.startOnTick = axis.options.endOnTick = false;
  7559. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  7560. addEvent(axis.scrollbar, 'changed', function (e) {
  7561. var _a = getExtremes(axis),
  7562. axisMin = _a.axisMin,
  7563. axisMax = _a.axisMax,
  7564. unitedMin = _a.scrollMin,
  7565. unitedMax = _a.scrollMax,
  7566. range = unitedMax - unitedMin,
  7567. to,
  7568. from;
  7569. // #12834, scroll when show/hide series, wrong extremes
  7570. if (!defined(axisMin) || !defined(axisMax)) {
  7571. return;
  7572. }
  7573. if ((axis.horiz && !axis.reversed) ||
  7574. (!axis.horiz && axis.reversed)) {
  7575. to = unitedMin + range * this.to;
  7576. from = unitedMin + range * this.from;
  7577. }
  7578. else {
  7579. // y-values in browser are reversed, but this also
  7580. // applies for reversed horizontal axis:
  7581. to = unitedMin + range * (1 - this.from);
  7582. from = unitedMin + range * (1 - this.to);
  7583. }
  7584. if (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  7585. // Mouseup always should change extremes
  7586. e.DOMType === 'mouseup' ||
  7587. e.DOMType === 'touchend' ||
  7588. // Internal events
  7589. !defined(e.DOMType)) {
  7590. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove' && e.DOMType !== 'touchmove', e);
  7591. }
  7592. else {
  7593. // When live redraw is disabled, don't change extremes
  7594. // Only change the position of the scollbar thumb
  7595. this.setRange(this.from, this.to);
  7596. }
  7597. });
  7598. }
  7599. });
  7600. // Wrap rendering axis, and update scrollbar if one is created:
  7601. addEvent(AxisClass, 'afterRender', function () {
  7602. var axis = this,
  7603. _a = getExtremes(axis),
  7604. scrollMin = _a.scrollMin,
  7605. scrollMax = _a.scrollMax,
  7606. scrollbar = axis.scrollbar,
  7607. offset = axis.axisTitleMargin + (axis.titleOffset || 0),
  7608. scrollbarsOffsets = axis.chart.scrollbarsOffsets,
  7609. axisMargin = axis.options.margin || 0,
  7610. offsetsIndex,
  7611. from,
  7612. to;
  7613. if (scrollbar) {
  7614. if (axis.horiz) {
  7615. // Reserve space for labels/title
  7616. if (!axis.opposite) {
  7617. scrollbarsOffsets[1] += offset;
  7618. }
  7619. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  7620. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  7621. // Next scrollbar should reserve space for margin (if set)
  7622. if (!axis.opposite) {
  7623. scrollbarsOffsets[1] += axisMargin;
  7624. }
  7625. offsetsIndex = 1;
  7626. }
  7627. else {
  7628. // Reserve space for labels/title
  7629. if (axis.opposite) {
  7630. scrollbarsOffsets[0] += offset;
  7631. }
  7632. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  7633. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  7634. // Next scrollbar should reserve space for margin (if set)
  7635. if (axis.opposite) {
  7636. scrollbarsOffsets[0] += axisMargin;
  7637. }
  7638. offsetsIndex = 0;
  7639. }
  7640. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  7641. scrollbar.options.margin;
  7642. if (isNaN(scrollMin) ||
  7643. isNaN(scrollMax) ||
  7644. !defined(axis.min) ||
  7645. !defined(axis.max) ||
  7646. axis.min === axis.max // #10733
  7647. ) {
  7648. // default action: when extremes are the same or there is
  7649. // not extremes on the axis, but scrollbar exists, make it
  7650. // full size
  7651. scrollbar.setRange(0, 1);
  7652. }
  7653. else {
  7654. from =
  7655. (axis.min - scrollMin) / (scrollMax - scrollMin);
  7656. to =
  7657. (axis.max - scrollMin) / (scrollMax - scrollMin);
  7658. if ((axis.horiz && !axis.reversed) ||
  7659. (!axis.horiz && axis.reversed)) {
  7660. scrollbar.setRange(from, to);
  7661. }
  7662. else {
  7663. // inverse vertical axis
  7664. scrollbar.setRange(1 - to, 1 - from);
  7665. }
  7666. }
  7667. }
  7668. });
  7669. // Make space for a scrollbar:
  7670. addEvent(AxisClass, 'afterGetOffset', function () {
  7671. var axis = this,
  7672. index = axis.horiz ? 2 : 1,
  7673. scrollbar = axis.scrollbar;
  7674. if (scrollbar) {
  7675. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  7676. axis.chart.axisOffset[index] +=
  7677. scrollbar.size + scrollbar.options.margin;
  7678. }
  7679. });
  7680. };
  7681. return ScrollbarAxis;
  7682. }());
  7683. return ScrollbarAxis;
  7684. });
  7685. _registerModule(_modules, 'Core/Scrollbar.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Color/Palette.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Core/Utilities.js'], _modules['Core/Options.js']], function (Axis, H, palette, ScrollbarAxis, U, O) {
  7686. /* *
  7687. *
  7688. * (c) 2010-2021 Torstein Honsi
  7689. *
  7690. * License: www.highcharts.com/license
  7691. *
  7692. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7693. *
  7694. * */
  7695. var addEvent = U.addEvent,
  7696. correctFloat = U.correctFloat,
  7697. defined = U.defined,
  7698. destroyObjectProperties = U.destroyObjectProperties,
  7699. fireEvent = U.fireEvent,
  7700. merge = U.merge,
  7701. pick = U.pick,
  7702. removeEvent = U.removeEvent;
  7703. var defaultOptions = O.defaultOptions;
  7704. var isTouchDevice = H.isTouchDevice;
  7705. /**
  7706. * When we have vertical scrollbar, rifles and arrow in buttons should be
  7707. * rotated. The same method is used in Navigator's handles, to rotate them.
  7708. *
  7709. * @function Highcharts.swapXY
  7710. *
  7711. * @param {Highcharts.SVGPathArray} path
  7712. * Path to be rotated.
  7713. *
  7714. * @param {boolean} [vertical]
  7715. * If vertical scrollbar, swap x-y values.
  7716. *
  7717. * @return {Highcharts.SVGPathArray}
  7718. * Rotated path.
  7719. *
  7720. * @requires modules/stock
  7721. */
  7722. var swapXY = H.swapXY = function (path,
  7723. vertical) {
  7724. if (vertical) {
  7725. path.forEach(function (seg) {
  7726. var len = seg.length;
  7727. var temp;
  7728. for (var i = 0; i < len; i += 2) {
  7729. temp = seg[i + 1];
  7730. if (typeof temp === 'number') {
  7731. seg[i + 1] = seg[i + 2];
  7732. seg[i + 2] = temp;
  7733. }
  7734. }
  7735. });
  7736. }
  7737. return path;
  7738. };
  7739. /* eslint-disable no-invalid-this, valid-jsdoc */
  7740. /**
  7741. * A reusable scrollbar, internally used in Highstock's navigator and optionally
  7742. * on individual axes.
  7743. *
  7744. * @private
  7745. * @class
  7746. * @name Highcharts.Scrollbar
  7747. * @param {Highcharts.SVGRenderer} renderer
  7748. * @param {Highcharts.ScrollbarOptions} options
  7749. * @param {Highcharts.Chart} chart
  7750. */
  7751. var Scrollbar = /** @class */ (function () {
  7752. /* *
  7753. *
  7754. * Constructors
  7755. *
  7756. * */
  7757. function Scrollbar(renderer, options, chart) {
  7758. /* *
  7759. *
  7760. * Properties
  7761. *
  7762. * */
  7763. this._events = [];
  7764. this.chartX = 0;
  7765. this.chartY = 0;
  7766. this.from = 0;
  7767. this.group = void 0;
  7768. this.scrollbar = void 0;
  7769. this.scrollbarButtons = [];
  7770. this.scrollbarGroup = void 0;
  7771. this.scrollbarLeft = 0;
  7772. this.scrollbarRifles = void 0;
  7773. this.scrollbarStrokeWidth = 1;
  7774. this.scrollbarTop = 0;
  7775. this.size = 0;
  7776. this.to = 0;
  7777. this.track = void 0;
  7778. this.trackBorderWidth = 1;
  7779. this.userOptions = {};
  7780. this.x = 0;
  7781. this.y = 0;
  7782. this.chart = chart;
  7783. this.options = options;
  7784. this.renderer = chart.renderer;
  7785. this.init(renderer, options, chart);
  7786. }
  7787. /* *
  7788. *
  7789. * Functions
  7790. *
  7791. * */
  7792. /**
  7793. * Set up the mouse and touch events for the Scrollbar
  7794. *
  7795. * @private
  7796. * @function Highcharts.Scrollbar#addEvents
  7797. * @return {void}
  7798. */
  7799. Scrollbar.prototype.addEvents = function () {
  7800. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1],
  7801. buttons = this.scrollbarButtons,
  7802. bar = this.scrollbarGroup.element,
  7803. track = this.track.element,
  7804. mouseDownHandler = this.mouseDownHandler.bind(this),
  7805. mouseMoveHandler = this.mouseMoveHandler.bind(this),
  7806. mouseUpHandler = this.mouseUpHandler.bind(this),
  7807. _events;
  7808. // Mouse events
  7809. _events = [
  7810. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)],
  7811. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)],
  7812. [track, 'click', this.trackClick.bind(this)],
  7813. [bar, 'mousedown', mouseDownHandler],
  7814. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  7815. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  7816. ];
  7817. // Touch events
  7818. if (H.hasTouch) {
  7819. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  7820. }
  7821. // Add them all
  7822. _events.forEach(function (args) {
  7823. addEvent.apply(null, args);
  7824. });
  7825. this._events = _events;
  7826. };
  7827. Scrollbar.prototype.buttonToMaxClick = function (e) {
  7828. var scroller = this;
  7829. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  7830. scroller.updatePosition(scroller.from + range, scroller.to + range);
  7831. fireEvent(scroller, 'changed', {
  7832. from: scroller.from,
  7833. to: scroller.to,
  7834. trigger: 'scrollbar',
  7835. DOMEvent: e
  7836. });
  7837. };
  7838. Scrollbar.prototype.buttonToMinClick = function (e) {
  7839. var scroller = this;
  7840. var range = correctFloat(scroller.to - scroller.from) *
  7841. pick(scroller.options.step, 0.2);
  7842. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  7843. fireEvent(scroller, 'changed', {
  7844. from: scroller.from,
  7845. to: scroller.to,
  7846. trigger: 'scrollbar',
  7847. DOMEvent: e
  7848. });
  7849. };
  7850. /**
  7851. * Get normalized (0-1) cursor position over the scrollbar
  7852. *
  7853. * @private
  7854. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  7855. *
  7856. * @param {*} normalizedEvent
  7857. * normalized event, with chartX and chartY values
  7858. *
  7859. * @return {Highcharts.Dictionary<number>}
  7860. * Local position {chartX, chartY}
  7861. */
  7862. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  7863. var scroller = this,
  7864. options = scroller.options,
  7865. minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  7866. options.minWidth :
  7867. 0; // minWidth distorts translation
  7868. return {
  7869. chartX: (normalizedEvent.chartX - scroller.x -
  7870. scroller.xOffset) /
  7871. (scroller.barWidth - minWidthDifference),
  7872. chartY: (normalizedEvent.chartY - scroller.y -
  7873. scroller.yOffset) /
  7874. (scroller.barWidth - minWidthDifference)
  7875. };
  7876. };
  7877. /**
  7878. * Destroys allocated elements.
  7879. *
  7880. * @private
  7881. * @function Highcharts.Scrollbar#destroy
  7882. * @return {void}
  7883. */
  7884. Scrollbar.prototype.destroy = function () {
  7885. var scroller = this.chart.scroller;
  7886. // Disconnect events added in addEvents
  7887. this.removeEvents();
  7888. // Destroy properties
  7889. [
  7890. 'track',
  7891. 'scrollbarRifles',
  7892. 'scrollbar',
  7893. 'scrollbarGroup',
  7894. 'group'
  7895. ].forEach(function (prop) {
  7896. if (this[prop] && this[prop].destroy) {
  7897. this[prop] = this[prop].destroy();
  7898. }
  7899. }, this);
  7900. // #6421, chart may have more scrollbars
  7901. if (scroller && this === scroller.scrollbar) {
  7902. scroller.scrollbar = null;
  7903. // Destroy elements in collection
  7904. destroyObjectProperties(scroller.scrollbarButtons);
  7905. }
  7906. };
  7907. /**
  7908. * Draw the scrollbar buttons with arrows
  7909. *
  7910. * @private
  7911. * @function Highcharts.Scrollbar#drawScrollbarButton
  7912. * @param {number} index
  7913. * 0 is left, 1 is right
  7914. * @return {void}
  7915. */
  7916. Scrollbar.prototype.drawScrollbarButton = function (index) {
  7917. var scroller = this,
  7918. renderer = scroller.renderer,
  7919. scrollbarButtons = scroller.scrollbarButtons,
  7920. options = scroller.options,
  7921. size = scroller.size,
  7922. group,
  7923. tempElem;
  7924. group = renderer.g().add(scroller.group);
  7925. scrollbarButtons.push(group);
  7926. // Create a rectangle for the scrollbar button
  7927. tempElem = renderer.rect()
  7928. .addClass('highcharts-scrollbar-button')
  7929. .add(group);
  7930. // Presentational attributes
  7931. if (!this.chart.styledMode) {
  7932. tempElem.attr({
  7933. stroke: options.buttonBorderColor,
  7934. 'stroke-width': options.buttonBorderWidth,
  7935. fill: options.buttonBackgroundColor
  7936. });
  7937. }
  7938. // Place the rectangle based on the rendered stroke width
  7939. tempElem.attr(tempElem.crisp({
  7940. x: -0.5,
  7941. y: -0.5,
  7942. width: size + 1,
  7943. height: size + 1,
  7944. r: options.buttonBorderRadius
  7945. }, tempElem.strokeWidth()));
  7946. // Button arrow
  7947. tempElem = renderer
  7948. .path(swapXY([[
  7949. 'M',
  7950. size / 2 + (index ? -1 : 1),
  7951. size / 2 - 3
  7952. ], [
  7953. 'L',
  7954. size / 2 + (index ? -1 : 1),
  7955. size / 2 + 3
  7956. ], [
  7957. 'L',
  7958. size / 2 + (index ? 2 : -2),
  7959. size / 2
  7960. ]], options.vertical))
  7961. .addClass('highcharts-scrollbar-arrow')
  7962. .add(scrollbarButtons[index]);
  7963. if (!this.chart.styledMode) {
  7964. tempElem.attr({
  7965. fill: options.buttonArrowColor
  7966. });
  7967. }
  7968. };
  7969. /**
  7970. * @private
  7971. * @function Highcharts.Scrollbar#init
  7972. * @param {Highcharts.SVGRenderer} renderer
  7973. * @param {Highcharts.ScrollbarOptions} options
  7974. * @param {Highcharts.Chart} chart
  7975. */
  7976. Scrollbar.prototype.init = function (renderer, options, chart) {
  7977. this.scrollbarButtons = [];
  7978. this.renderer = renderer;
  7979. this.userOptions = options;
  7980. this.options = merge(Scrollbar.defaultOptions, options);
  7981. this.chart = chart;
  7982. // backward compatibility
  7983. this.size = pick(this.options.size, this.options.height);
  7984. // Init
  7985. if (options.enabled) {
  7986. this.render();
  7987. this.addEvents();
  7988. }
  7989. };
  7990. Scrollbar.prototype.mouseDownHandler = function (e) {
  7991. var scroller = this;
  7992. var normalizedEvent = scroller.chart.pointer.normalize(e),
  7993. mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  7994. scroller.chartX = mousePosition.chartX;
  7995. scroller.chartY = mousePosition.chartY;
  7996. scroller.initPositions = [scroller.from, scroller.to];
  7997. scroller.grabbedCenter = true;
  7998. };
  7999. /**
  8000. * Event handler for the mouse move event.
  8001. * @private
  8002. */
  8003. Scrollbar.prototype.mouseMoveHandler = function (e) {
  8004. var scroller = this;
  8005. var normalizedEvent = scroller.chart.pointer.normalize(e),
  8006. options = scroller.options,
  8007. direction = options.vertical ? 'chartY' : 'chartX',
  8008. initPositions = scroller.initPositions || [],
  8009. scrollPosition,
  8010. chartPosition,
  8011. change;
  8012. // In iOS, a mousemove event with e.pageX === 0 is fired when
  8013. // holding the finger down in the center of the scrollbar. This
  8014. // should be ignored.
  8015. if (scroller.grabbedCenter &&
  8016. // #4696, scrollbar failed on Android
  8017. (!e.touches || e.touches[0][direction] !== 0)) {
  8018. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  8019. scrollPosition = scroller[direction];
  8020. change = chartPosition - scrollPosition;
  8021. scroller.hasDragged = true;
  8022. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  8023. if (scroller.hasDragged) {
  8024. fireEvent(scroller, 'changed', {
  8025. from: scroller.from,
  8026. to: scroller.to,
  8027. trigger: 'scrollbar',
  8028. DOMType: e.type,
  8029. DOMEvent: e
  8030. });
  8031. }
  8032. }
  8033. };
  8034. /**
  8035. * Event handler for the mouse up event.
  8036. * @private
  8037. */
  8038. Scrollbar.prototype.mouseUpHandler = function (e) {
  8039. var scroller = this;
  8040. if (scroller.hasDragged) {
  8041. fireEvent(scroller, 'changed', {
  8042. from: scroller.from,
  8043. to: scroller.to,
  8044. trigger: 'scrollbar',
  8045. DOMType: e.type,
  8046. DOMEvent: e
  8047. });
  8048. }
  8049. scroller.grabbedCenter =
  8050. scroller.hasDragged =
  8051. scroller.chartX =
  8052. scroller.chartY = null;
  8053. };
  8054. /**
  8055. * Position the scrollbar, method called from a parent with defined
  8056. * dimensions.
  8057. *
  8058. * @private
  8059. * @function Highcharts.Scrollbar#position
  8060. * @param {number} x
  8061. * x-position on the chart
  8062. * @param {number} y
  8063. * y-position on the chart
  8064. * @param {number} width
  8065. * width of the scrollbar
  8066. * @param {number} height
  8067. * height of the scorllbar
  8068. * @return {void}
  8069. */
  8070. Scrollbar.prototype.position = function (x, y, width, height) {
  8071. var scroller = this,
  8072. options = scroller.options,
  8073. vertical = options.vertical,
  8074. xOffset = height,
  8075. yOffset = 0,
  8076. method = scroller.rendered ? 'animate' : 'attr';
  8077. scroller.x = x;
  8078. scroller.y = y + this.trackBorderWidth;
  8079. scroller.width = width; // width with buttons
  8080. scroller.height = height;
  8081. scroller.xOffset = xOffset;
  8082. scroller.yOffset = yOffset;
  8083. // If Scrollbar is a vertical type, swap options:
  8084. if (vertical) {
  8085. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  8086. scroller.xOffset = xOffset = 0;
  8087. scroller.barWidth = height - width * 2; // width without buttons
  8088. scroller.x = x = x + scroller.options.margin;
  8089. }
  8090. else {
  8091. scroller.height = scroller.xOffset = height = xOffset =
  8092. scroller.size;
  8093. scroller.barWidth = width - height * 2; // width without buttons
  8094. scroller.y = scroller.y + scroller.options.margin;
  8095. }
  8096. // Set general position for a group:
  8097. scroller.group[method]({
  8098. translateX: x,
  8099. translateY: scroller.y
  8100. });
  8101. // Resize background/track:
  8102. scroller.track[method]({
  8103. width: width,
  8104. height: height
  8105. });
  8106. // Move right/bottom button ot it's place:
  8107. scroller.scrollbarButtons[1][method]({
  8108. translateX: vertical ? 0 : width - xOffset,
  8109. translateY: vertical ? height - yOffset : 0
  8110. });
  8111. };
  8112. /**
  8113. * Removes the event handlers attached previously with addEvents.
  8114. *
  8115. * @private
  8116. * @function Highcharts.Scrollbar#removeEvents
  8117. * @return {void}
  8118. */
  8119. Scrollbar.prototype.removeEvents = function () {
  8120. this._events.forEach(function (args) {
  8121. removeEvent.apply(null, args);
  8122. });
  8123. this._events.length = 0;
  8124. };
  8125. /**
  8126. * Render scrollbar with all required items.
  8127. *
  8128. * @private
  8129. * @function Highcharts.Scrollbar#render
  8130. */
  8131. Scrollbar.prototype.render = function () {
  8132. var scroller = this,
  8133. renderer = scroller.renderer,
  8134. options = scroller.options,
  8135. size = scroller.size,
  8136. styledMode = this.chart.styledMode,
  8137. group;
  8138. // Draw the scrollbar group
  8139. scroller.group = group = renderer.g('scrollbar').attr({
  8140. zIndex: options.zIndex,
  8141. translateY: -99999
  8142. }).add();
  8143. // Draw the scrollbar track:
  8144. scroller.track = renderer.rect()
  8145. .addClass('highcharts-scrollbar-track')
  8146. .attr({
  8147. x: 0,
  8148. r: options.trackBorderRadius || 0,
  8149. height: size,
  8150. width: size
  8151. }).add(group);
  8152. if (!styledMode) {
  8153. scroller.track.attr({
  8154. fill: options.trackBackgroundColor,
  8155. stroke: options.trackBorderColor,
  8156. 'stroke-width': options.trackBorderWidth
  8157. });
  8158. }
  8159. this.trackBorderWidth = scroller.track.strokeWidth();
  8160. scroller.track.attr({
  8161. y: -this.trackBorderWidth % 2 / 2
  8162. });
  8163. // Draw the scrollbar itself
  8164. scroller.scrollbarGroup = renderer.g().add(group);
  8165. scroller.scrollbar = renderer.rect()
  8166. .addClass('highcharts-scrollbar-thumb')
  8167. .attr({
  8168. height: size,
  8169. width: size,
  8170. r: options.barBorderRadius || 0
  8171. }).add(scroller.scrollbarGroup);
  8172. scroller.scrollbarRifles = renderer
  8173. .path(swapXY([
  8174. ['M', -3, size / 4],
  8175. ['L', -3, 2 * size / 3],
  8176. ['M', 0, size / 4],
  8177. ['L', 0, 2 * size / 3],
  8178. ['M', 3, size / 4],
  8179. ['L', 3, 2 * size / 3]
  8180. ], options.vertical))
  8181. .addClass('highcharts-scrollbar-rifles')
  8182. .add(scroller.scrollbarGroup);
  8183. if (!styledMode) {
  8184. scroller.scrollbar.attr({
  8185. fill: options.barBackgroundColor,
  8186. stroke: options.barBorderColor,
  8187. 'stroke-width': options.barBorderWidth
  8188. });
  8189. scroller.scrollbarRifles.attr({
  8190. stroke: options.rifleColor,
  8191. 'stroke-width': 1
  8192. });
  8193. }
  8194. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  8195. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  8196. // Draw the buttons:
  8197. scroller.drawScrollbarButton(0);
  8198. scroller.drawScrollbarButton(1);
  8199. };
  8200. /**
  8201. * Set scrollbar size, with a given scale.
  8202. *
  8203. * @private
  8204. * @function Highcharts.Scrollbar#setRange
  8205. * @param {number} from
  8206. * scale (0-1) where bar should start
  8207. * @param {number} to
  8208. * scale (0-1) where bar should end
  8209. * @return {void}
  8210. */
  8211. Scrollbar.prototype.setRange = function (from, to) {
  8212. var scroller = this,
  8213. options = scroller.options,
  8214. vertical = options.vertical,
  8215. minWidth = options.minWidth,
  8216. fullWidth = scroller.barWidth,
  8217. fromPX,
  8218. toPX,
  8219. newPos,
  8220. newSize,
  8221. newRiflesPos,
  8222. method = (this.rendered &&
  8223. !this.hasDragged &&
  8224. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  8225. if (!defined(fullWidth)) {
  8226. return;
  8227. }
  8228. from = Math.max(from, 0);
  8229. fromPX = Math.ceil(fullWidth * from);
  8230. toPX = fullWidth * Math.min(to, 1);
  8231. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  8232. // We need to recalculate position, if minWidth is used
  8233. if (newSize < minWidth) {
  8234. fromPX = (fullWidth - minWidth + newSize) * from;
  8235. newSize = minWidth;
  8236. }
  8237. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  8238. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  8239. // Store current position:
  8240. scroller.from = from;
  8241. scroller.to = to;
  8242. if (!vertical) {
  8243. scroller.scrollbarGroup[method]({
  8244. translateX: newPos
  8245. });
  8246. scroller.scrollbar[method]({
  8247. width: newSize
  8248. });
  8249. scroller.scrollbarRifles[method]({
  8250. translateX: newRiflesPos
  8251. });
  8252. scroller.scrollbarLeft = newPos;
  8253. scroller.scrollbarTop = 0;
  8254. }
  8255. else {
  8256. scroller.scrollbarGroup[method]({
  8257. translateY: newPos
  8258. });
  8259. scroller.scrollbar[method]({
  8260. height: newSize
  8261. });
  8262. scroller.scrollbarRifles[method]({
  8263. translateY: newRiflesPos
  8264. });
  8265. scroller.scrollbarTop = newPos;
  8266. scroller.scrollbarLeft = 0;
  8267. }
  8268. if (newSize <= 12) {
  8269. scroller.scrollbarRifles.hide();
  8270. }
  8271. else {
  8272. scroller.scrollbarRifles.show(true);
  8273. }
  8274. // Show or hide the scrollbar based on the showFull setting
  8275. if (options.showFull === false) {
  8276. if (from <= 0 && to >= 1) {
  8277. scroller.group.hide();
  8278. }
  8279. else {
  8280. scroller.group.show();
  8281. }
  8282. }
  8283. scroller.rendered = true;
  8284. };
  8285. Scrollbar.prototype.trackClick = function (e) {
  8286. var scroller = this;
  8287. var normalizedEvent = scroller.chart.pointer.normalize(e),
  8288. range = scroller.to - scroller.from,
  8289. top = scroller.y + scroller.scrollbarTop,
  8290. left = scroller.x + scroller.scrollbarLeft;
  8291. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  8292. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  8293. // On the top or on the left side of the track:
  8294. scroller.updatePosition(scroller.from + range, scroller.to + range);
  8295. }
  8296. else {
  8297. // On the bottom or the right side of the track:
  8298. scroller.updatePosition(scroller.from - range, scroller.to - range);
  8299. }
  8300. fireEvent(scroller, 'changed', {
  8301. from: scroller.from,
  8302. to: scroller.to,
  8303. trigger: 'scrollbar',
  8304. DOMEvent: e
  8305. });
  8306. };
  8307. /**
  8308. * Update the scrollbar with new options
  8309. *
  8310. * @private
  8311. * @function Highcharts.Scrollbar#update
  8312. * @param {Highcharts.ScrollbarOptions} options
  8313. * @return {void}
  8314. */
  8315. Scrollbar.prototype.update = function (options) {
  8316. this.destroy();
  8317. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  8318. };
  8319. /**
  8320. * Update position option in the Scrollbar, with normalized 0-1 scale
  8321. *
  8322. * @private
  8323. * @function Highcharts.Scrollbar#updatePosition
  8324. * @param {number} from
  8325. * @param {number} to
  8326. * @return {void}
  8327. */
  8328. Scrollbar.prototype.updatePosition = function (from, to) {
  8329. if (to > 1) {
  8330. from = correctFloat(1 - correctFloat(to - from));
  8331. to = 1;
  8332. }
  8333. if (from < 0) {
  8334. to = correctFloat(to - from);
  8335. from = 0;
  8336. }
  8337. this.from = from;
  8338. this.to = to;
  8339. };
  8340. /* *
  8341. *
  8342. * Static Properties
  8343. *
  8344. * */
  8345. /**
  8346. *
  8347. * The scrollbar is a means of panning over the X axis of a stock chart.
  8348. * Scrollbars can also be applied to other types of axes.
  8349. *
  8350. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  8351. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  8352. * is especially suitable for simpler cartesian charts on mobile.
  8353. *
  8354. * In styled mode, all the presentational options for the
  8355. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  8356. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  8357. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  8358. *
  8359. * @sample stock/yaxis/inverted-bar-scrollbar/
  8360. * A scrollbar on a simple bar chart
  8361. *
  8362. * @product highstock gantt
  8363. * @optionparent scrollbar
  8364. *
  8365. * @private
  8366. */
  8367. Scrollbar.defaultOptions = {
  8368. /**
  8369. * The height of the scrollbar. The height also applies to the width
  8370. * of the scroll arrows so that they are always squares. Defaults to
  8371. * 20 for touch devices and 14 for mouse devices.
  8372. *
  8373. * @sample stock/scrollbar/height/
  8374. * A 30px scrollbar
  8375. *
  8376. * @type {number}
  8377. * @default 20/14
  8378. */
  8379. height: isTouchDevice ? 20 : 14,
  8380. /**
  8381. * The border rounding radius of the bar.
  8382. *
  8383. * @sample stock/scrollbar/style/
  8384. * Scrollbar styling
  8385. */
  8386. barBorderRadius: 0,
  8387. /**
  8388. * The corner radius of the scrollbar buttons.
  8389. *
  8390. * @sample stock/scrollbar/style/
  8391. * Scrollbar styling
  8392. */
  8393. buttonBorderRadius: 0,
  8394. /**
  8395. * Enable or disable the scrollbar.
  8396. *
  8397. * @sample stock/scrollbar/enabled/
  8398. * Disable the scrollbar, only use navigator
  8399. *
  8400. * @type {boolean}
  8401. * @default true
  8402. * @apioption scrollbar.enabled
  8403. */
  8404. /**
  8405. * Whether to redraw the main chart as the scrollbar or the navigator
  8406. * zoomed window is moved. Defaults to `true` for modern browsers and
  8407. * `false` for legacy IE browsers as well as mobile devices.
  8408. *
  8409. * @sample stock/scrollbar/liveredraw
  8410. * Setting live redraw to false
  8411. *
  8412. * @type {boolean}
  8413. * @since 1.3
  8414. */
  8415. liveRedraw: void 0,
  8416. /**
  8417. * The margin between the scrollbar and its axis when the scrollbar is
  8418. * applied directly to an axis.
  8419. */
  8420. margin: 10,
  8421. /**
  8422. * The minimum width of the scrollbar.
  8423. *
  8424. * @since 1.2.5
  8425. */
  8426. minWidth: 6,
  8427. /**
  8428. * Whether to show or hide the scrollbar when the scrolled content is
  8429. * zoomed out to it full extent.
  8430. *
  8431. * @type {boolean}
  8432. * @default true
  8433. * @apioption scrollbar.showFull
  8434. */
  8435. step: 0.2,
  8436. /**
  8437. * The z index of the scrollbar group.
  8438. */
  8439. zIndex: 3,
  8440. /**
  8441. * The background color of the scrollbar itself.
  8442. *
  8443. * @sample stock/scrollbar/style/
  8444. * Scrollbar styling
  8445. *
  8446. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8447. */
  8448. barBackgroundColor: palette.neutralColor20,
  8449. /**
  8450. * The width of the bar's border.
  8451. *
  8452. * @sample stock/scrollbar/style/
  8453. * Scrollbar styling
  8454. */
  8455. barBorderWidth: 1,
  8456. /**
  8457. * The color of the scrollbar's border.
  8458. *
  8459. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8460. */
  8461. barBorderColor: palette.neutralColor20,
  8462. /**
  8463. * The color of the small arrow inside the scrollbar buttons.
  8464. *
  8465. * @sample stock/scrollbar/style/
  8466. * Scrollbar styling
  8467. *
  8468. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8469. */
  8470. buttonArrowColor: palette.neutralColor80,
  8471. /**
  8472. * The color of scrollbar buttons.
  8473. *
  8474. * @sample stock/scrollbar/style/
  8475. * Scrollbar styling
  8476. *
  8477. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8478. */
  8479. buttonBackgroundColor: palette.neutralColor10,
  8480. /**
  8481. * The color of the border of the scrollbar buttons.
  8482. *
  8483. * @sample stock/scrollbar/style/
  8484. * Scrollbar styling
  8485. *
  8486. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8487. */
  8488. buttonBorderColor: palette.neutralColor20,
  8489. /**
  8490. * The border width of the scrollbar buttons.
  8491. *
  8492. * @sample stock/scrollbar/style/
  8493. * Scrollbar styling
  8494. */
  8495. buttonBorderWidth: 1,
  8496. /**
  8497. * The color of the small rifles in the middle of the scrollbar.
  8498. *
  8499. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8500. */
  8501. rifleColor: palette.neutralColor80,
  8502. /**
  8503. * The color of the track background.
  8504. *
  8505. * @sample stock/scrollbar/style/
  8506. * Scrollbar styling
  8507. *
  8508. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8509. */
  8510. trackBackgroundColor: palette.neutralColor5,
  8511. /**
  8512. * The color of the border of the scrollbar track.
  8513. *
  8514. * @sample stock/scrollbar/style/
  8515. * Scrollbar styling
  8516. *
  8517. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  8518. */
  8519. trackBorderColor: palette.neutralColor5,
  8520. /**
  8521. * The corner radius of the border of the scrollbar track.
  8522. *
  8523. * @sample stock/scrollbar/style/
  8524. * Scrollbar styling
  8525. *
  8526. * @type {number}
  8527. * @default 0
  8528. * @apioption scrollbar.trackBorderRadius
  8529. */
  8530. /**
  8531. * The width of the border of the scrollbar track.
  8532. *
  8533. * @sample stock/scrollbar/style/
  8534. * Scrollbar styling
  8535. */
  8536. trackBorderWidth: 1
  8537. };
  8538. return Scrollbar;
  8539. }());
  8540. if (!H.Scrollbar) {
  8541. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  8542. H.Scrollbar = Scrollbar;
  8543. ScrollbarAxis.compose(Axis, Scrollbar);
  8544. }
  8545. return H.Scrollbar;
  8546. });
  8547. _registerModule(_modules, 'Extensions/RangeSelector.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/Options.js'], _modules['Core/Color/Palette.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (Axis, Chart, H, O, palette, SVGElement, U) {
  8548. /* *
  8549. *
  8550. * (c) 2010-2021 Torstein Honsi
  8551. *
  8552. * License: www.highcharts.com/license
  8553. *
  8554. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8555. *
  8556. * */
  8557. var defaultOptions = O.defaultOptions;
  8558. var addEvent = U.addEvent,
  8559. createElement = U.createElement,
  8560. css = U.css,
  8561. defined = U.defined,
  8562. destroyObjectProperties = U.destroyObjectProperties,
  8563. discardElement = U.discardElement,
  8564. extend = U.extend,
  8565. find = U.find,
  8566. fireEvent = U.fireEvent,
  8567. isNumber = U.isNumber,
  8568. merge = U.merge,
  8569. objectEach = U.objectEach,
  8570. pad = U.pad,
  8571. pick = U.pick,
  8572. pInt = U.pInt,
  8573. splat = U.splat;
  8574. /**
  8575. * Define the time span for the button
  8576. *
  8577. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  8578. */
  8579. /**
  8580. * Callback function to react on button clicks.
  8581. *
  8582. * @callback Highcharts.RangeSelectorClickCallbackFunction
  8583. *
  8584. * @param {global.Event} e
  8585. * Event arguments.
  8586. *
  8587. * @param {boolean|undefined}
  8588. * Return false to cancel the default button event.
  8589. */
  8590. /**
  8591. * Callback function to parse values entered in the input boxes and return a
  8592. * valid JavaScript time as milliseconds since 1970.
  8593. *
  8594. * @callback Highcharts.RangeSelectorParseCallbackFunction
  8595. *
  8596. * @param {string} value
  8597. * Input value to parse.
  8598. *
  8599. * @return {number}
  8600. * Parsed JavaScript time value.
  8601. */
  8602. /* ************************************************************************** *
  8603. * Start Range Selector code *
  8604. * ************************************************************************** */
  8605. extend(defaultOptions, {
  8606. /**
  8607. * The range selector is a tool for selecting ranges to display within
  8608. * the chart. It provides buttons to select preconfigured ranges in
  8609. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  8610. * boxes where min and max dates can be manually input.
  8611. *
  8612. * @product highstock gantt
  8613. * @optionparent rangeSelector
  8614. */
  8615. rangeSelector: {
  8616. /**
  8617. * Whether to enable all buttons from the start. By default buttons are
  8618. * only enabled if the corresponding time range exists on the X axis,
  8619. * but enabling all buttons allows for dynamically loading different
  8620. * time ranges.
  8621. *
  8622. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  8623. * All buttons enabled
  8624. *
  8625. * @since 2.0.3
  8626. */
  8627. allButtonsEnabled: false,
  8628. /**
  8629. * An array of configuration objects for the buttons.
  8630. *
  8631. * Defaults to:
  8632. * ```js
  8633. * buttons: [{
  8634. * type: 'month',
  8635. * count: 1,
  8636. * text: '1m',
  8637. * title: 'View 1 month'
  8638. * }, {
  8639. * type: 'month',
  8640. * count: 3,
  8641. * text: '3m',
  8642. * title: 'View 3 months'
  8643. * }, {
  8644. * type: 'month',
  8645. * count: 6,
  8646. * text: '6m',
  8647. * title: 'View 6 months'
  8648. * }, {
  8649. * type: 'ytd',
  8650. * text: 'YTD',
  8651. * title: 'View year to date'
  8652. * }, {
  8653. * type: 'year',
  8654. * count: 1,
  8655. * text: '1y',
  8656. * title: 'View 1 year'
  8657. * }, {
  8658. * type: 'all',
  8659. * text: 'All',
  8660. * title: 'View all'
  8661. * }]
  8662. * ```
  8663. *
  8664. * @sample {highstock} stock/rangeselector/datagrouping/
  8665. * Data grouping by buttons
  8666. *
  8667. * @type {Array<*>}
  8668. */
  8669. buttons: void 0,
  8670. /**
  8671. * How many units of the defined type the button should span. If `type`
  8672. * is "month" and `count` is 3, the button spans three months.
  8673. *
  8674. * @type {number}
  8675. * @default 1
  8676. * @apioption rangeSelector.buttons.count
  8677. */
  8678. /**
  8679. * Fires when clicking on the rangeSelector button. One parameter,
  8680. * event, is passed to the function, containing common event
  8681. * information.
  8682. *
  8683. * ```js
  8684. * click: function(e) {
  8685. * console.log(this);
  8686. * }
  8687. * ```
  8688. *
  8689. * Return false to stop default button's click action.
  8690. *
  8691. * @sample {highstock} stock/rangeselector/button-click/
  8692. * Click event on the button
  8693. *
  8694. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  8695. * @apioption rangeSelector.buttons.events.click
  8696. */
  8697. /**
  8698. * Additional range (in milliseconds) added to the end of the calculated
  8699. * time span.
  8700. *
  8701. * @sample {highstock} stock/rangeselector/min-max-offsets/
  8702. * Button offsets
  8703. *
  8704. * @type {number}
  8705. * @default 0
  8706. * @since 6.0.0
  8707. * @apioption rangeSelector.buttons.offsetMax
  8708. */
  8709. /**
  8710. * Additional range (in milliseconds) added to the start of the
  8711. * calculated time span.
  8712. *
  8713. * @sample {highstock} stock/rangeselector/min-max-offsets/
  8714. * Button offsets
  8715. *
  8716. * @type {number}
  8717. * @default 0
  8718. * @since 6.0.0
  8719. * @apioption rangeSelector.buttons.offsetMin
  8720. */
  8721. /**
  8722. * When buttons apply dataGrouping on a series, by default zooming
  8723. * in/out will deselect buttons and unset dataGrouping. Enable this
  8724. * option to keep buttons selected when extremes change.
  8725. *
  8726. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  8727. * Different preserveDataGrouping settings
  8728. *
  8729. * @type {boolean}
  8730. * @default false
  8731. * @since 6.1.2
  8732. * @apioption rangeSelector.buttons.preserveDataGrouping
  8733. */
  8734. /**
  8735. * A custom data grouping object for each button.
  8736. *
  8737. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  8738. *
  8739. * @sample {highstock} stock/rangeselector/datagrouping/
  8740. * Data grouping by range selector buttons
  8741. *
  8742. * @type {*}
  8743. * @extends plotOptions.series.dataGrouping
  8744. * @apioption rangeSelector.buttons.dataGrouping
  8745. */
  8746. /**
  8747. * The text for the button itself.
  8748. *
  8749. * @type {string}
  8750. * @apioption rangeSelector.buttons.text
  8751. */
  8752. /**
  8753. * Explanation for the button, shown as a tooltip on hover, and used by
  8754. * assistive technology.
  8755. *
  8756. * @type {string}
  8757. * @apioption rangeSelector.buttons.title
  8758. */
  8759. /**
  8760. * Defined the time span for the button. Can be one of `millisecond`,
  8761. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  8762. * and `all`.
  8763. *
  8764. * @type {Highcharts.RangeSelectorButtonTypeValue}
  8765. * @apioption rangeSelector.buttons.type
  8766. */
  8767. /**
  8768. * The space in pixels between the buttons in the range selector.
  8769. */
  8770. buttonSpacing: 5,
  8771. /**
  8772. * Whether to collapse the range selector buttons into a dropdown when
  8773. * there is not enough room to show everything in a single row, instead
  8774. * of dividing the range selector into multiple rows.
  8775. * Can be one of the following:
  8776. * - `always`: Always collapse
  8777. * - `responsive`: Only collapse when there is not enough room
  8778. * - `never`: Never collapse
  8779. *
  8780. * @sample {highstock} stock/rangeselector/dropdown/
  8781. * Dropdown option
  8782. *
  8783. * @validvalue ["always", "responsive", "never"]
  8784. * @since 9.0.0
  8785. */
  8786. dropdown: 'responsive',
  8787. /**
  8788. * Enable or disable the range selector. Default to `true` for stock
  8789. * charts, using the `stockChart` factory.
  8790. *
  8791. * @sample {highstock} stock/rangeselector/enabled/
  8792. * Disable the range selector
  8793. *
  8794. * @type {boolean|undefined}
  8795. * @default {highstock} true
  8796. */
  8797. enabled: void 0,
  8798. /**
  8799. * The vertical alignment of the rangeselector box. Allowed properties
  8800. * are `top`, `middle`, `bottom`.
  8801. *
  8802. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  8803. * Middle
  8804. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  8805. * Bottom
  8806. *
  8807. * @type {Highcharts.VerticalAlignValue}
  8808. * @since 6.0.0
  8809. */
  8810. verticalAlign: 'top',
  8811. /**
  8812. * A collection of attributes for the buttons. The object takes SVG
  8813. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  8814. * a collection of CSS properties for the text.
  8815. *
  8816. * The object can also be extended with states, so you can set
  8817. * presentational options for `hover`, `select` or `disabled` button
  8818. * states.
  8819. *
  8820. * CSS styles for the text label.
  8821. *
  8822. * In styled mode, the buttons are styled by the
  8823. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  8824. * different states.
  8825. *
  8826. * @sample {highstock} stock/rangeselector/styling/
  8827. * Styling the buttons and inputs
  8828. *
  8829. * @type {Highcharts.SVGAttributes}
  8830. */
  8831. buttonTheme: {
  8832. /** @ignore */
  8833. width: 28,
  8834. /** @ignore */
  8835. height: 18,
  8836. /** @ignore */
  8837. padding: 2,
  8838. /** @ignore */
  8839. zIndex: 7 // #484, #852
  8840. },
  8841. /**
  8842. * When the rangeselector is floating, the plot area does not reserve
  8843. * space for it. This opens for positioning anywhere on the chart.
  8844. *
  8845. * @sample {highstock} stock/rangeselector/floating/
  8846. * Placing the range selector between the plot area and the
  8847. * navigator
  8848. *
  8849. * @since 6.0.0
  8850. */
  8851. floating: false,
  8852. /**
  8853. * The x offset of the range selector relative to its horizontal
  8854. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8855. *
  8856. * @since 6.0.0
  8857. */
  8858. x: 0,
  8859. /**
  8860. * The y offset of the range selector relative to its horizontal
  8861. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  8862. *
  8863. * @since 6.0.0
  8864. */
  8865. y: 0,
  8866. /**
  8867. * Deprecated. The height of the range selector. Currently it is
  8868. * calculated dynamically.
  8869. *
  8870. * @deprecated
  8871. * @type {number|undefined}
  8872. * @since 2.1.9
  8873. */
  8874. height: void 0,
  8875. /**
  8876. * The border color of the date input boxes.
  8877. *
  8878. * @sample {highstock} stock/rangeselector/styling/
  8879. * Styling the buttons and inputs
  8880. *
  8881. * @type {Highcharts.ColorString}
  8882. * @since 1.3.7
  8883. */
  8884. inputBoxBorderColor: 'none',
  8885. /**
  8886. * The pixel height of the date input boxes.
  8887. *
  8888. * @sample {highstock} stock/rangeselector/styling/
  8889. * Styling the buttons and inputs
  8890. *
  8891. * @since 1.3.7
  8892. */
  8893. inputBoxHeight: 17,
  8894. /**
  8895. * The pixel width of the date input boxes. When `undefined`, the width
  8896. * is fitted to the rendered content.
  8897. *
  8898. * @sample {highstock} stock/rangeselector/styling/
  8899. * Styling the buttons and inputs
  8900. *
  8901. * @type {number|undefined}
  8902. * @since 1.3.7
  8903. */
  8904. inputBoxWidth: void 0,
  8905. /**
  8906. * The date format in the input boxes when not selected for editing.
  8907. * Defaults to `%b %e, %Y`.
  8908. *
  8909. * This is used to determine which type of input to show,
  8910. * `datetime-local`, `date` or `time` and falling back to `text` when
  8911. * the browser does not support the input type or the format contains
  8912. * milliseconds.
  8913. *
  8914. * @sample {highstock} stock/rangeselector/input-type/
  8915. * Input types
  8916. * @sample {highstock} stock/rangeselector/input-format/
  8917. * Milliseconds in the range selector
  8918. *
  8919. */
  8920. inputDateFormat: '%b %e, %Y',
  8921. /**
  8922. * A custom callback function to parse values entered in the input boxes
  8923. * and return a valid JavaScript time as milliseconds since 1970.
  8924. * The first argument passed is a value to parse,
  8925. * second is a boolean indicating use of the UTC time.
  8926. *
  8927. * This will only get called for inputs of type `text`. Since v8.2.3,
  8928. * the input type is dynamically determined based on the granularity
  8929. * of the `inputDateFormat` and the browser support.
  8930. *
  8931. * @sample {highstock} stock/rangeselector/input-format/
  8932. * Milliseconds in the range selector
  8933. *
  8934. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  8935. * @since 1.3.3
  8936. */
  8937. inputDateParser: void 0,
  8938. /**
  8939. * The date format in the input boxes when they are selected for
  8940. * editing. This must be a format that is recognized by JavaScript
  8941. * Date.parse.
  8942. *
  8943. * This will only be used for inputs of type `text`. Since v8.2.3,
  8944. * the input type is dynamically determined based on the granularity
  8945. * of the `inputDateFormat` and the browser support.
  8946. *
  8947. * @sample {highstock} stock/rangeselector/input-format/
  8948. * Milliseconds in the range selector
  8949. *
  8950. */
  8951. inputEditDateFormat: '%Y-%m-%d',
  8952. /**
  8953. * Enable or disable the date input boxes.
  8954. */
  8955. inputEnabled: true,
  8956. /**
  8957. * Positioning for the input boxes. Allowed properties are `align`,
  8958. * `x` and `y`.
  8959. *
  8960. * @since 1.2.4
  8961. */
  8962. inputPosition: {
  8963. /**
  8964. * The alignment of the input box. Allowed properties are `left`,
  8965. * `center`, `right`.
  8966. *
  8967. * @sample {highstock} stock/rangeselector/input-button-position/
  8968. * Alignment
  8969. *
  8970. * @type {Highcharts.AlignValue}
  8971. * @since 6.0.0
  8972. */
  8973. align: 'right',
  8974. /**
  8975. * X offset of the input row.
  8976. */
  8977. x: 0,
  8978. /**
  8979. * Y offset of the input row.
  8980. */
  8981. y: 0
  8982. },
  8983. /**
  8984. * The space in pixels between the labels and the date input boxes in
  8985. * the range selector.
  8986. *
  8987. * @since 9.0.0
  8988. */
  8989. inputSpacing: 5,
  8990. /**
  8991. * The index of the button to appear pre-selected.
  8992. *
  8993. * @type {number}
  8994. */
  8995. selected: void 0,
  8996. /**
  8997. * Positioning for the button row.
  8998. *
  8999. * @since 1.2.4
  9000. */
  9001. buttonPosition: {
  9002. /**
  9003. * The alignment of the input box. Allowed properties are `left`,
  9004. * `center`, `right`.
  9005. *
  9006. * @sample {highstock} stock/rangeselector/input-button-position/
  9007. * Alignment
  9008. *
  9009. * @type {Highcharts.AlignValue}
  9010. * @since 6.0.0
  9011. */
  9012. align: 'left',
  9013. /**
  9014. * X offset of the button row.
  9015. */
  9016. x: 0,
  9017. /**
  9018. * Y offset of the button row.
  9019. */
  9020. y: 0
  9021. },
  9022. /**
  9023. * CSS for the HTML inputs in the range selector.
  9024. *
  9025. * In styled mode, the inputs are styled by the
  9026. * `.highcharts-range-input text` rule in SVG mode, and
  9027. * `input.highcharts-range-selector` when active.
  9028. *
  9029. * @sample {highstock} stock/rangeselector/styling/
  9030. * Styling the buttons and inputs
  9031. *
  9032. * @type {Highcharts.CSSObject}
  9033. * @apioption rangeSelector.inputStyle
  9034. */
  9035. inputStyle: {
  9036. /** @ignore */
  9037. color: palette.highlightColor80,
  9038. /** @ignore */
  9039. cursor: 'pointer'
  9040. },
  9041. /**
  9042. * CSS styles for the labels - the Zoom, From and To texts.
  9043. *
  9044. * In styled mode, the labels are styled by the
  9045. * `.highcharts-range-label` class.
  9046. *
  9047. * @sample {highstock} stock/rangeselector/styling/
  9048. * Styling the buttons and inputs
  9049. *
  9050. * @type {Highcharts.CSSObject}
  9051. */
  9052. labelStyle: {
  9053. /** @ignore */
  9054. color: palette.neutralColor60
  9055. }
  9056. }
  9057. });
  9058. extend(defaultOptions.lang,
  9059. /**
  9060. * Language object. The language object is global and it can't be set
  9061. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  9062. * set it before any chart is initialized.
  9063. *
  9064. * ```js
  9065. * Highcharts.setOptions({
  9066. * lang: {
  9067. * months: [
  9068. * 'Janvier', 'Février', 'Mars', 'Avril',
  9069. * 'Mai', 'Juin', 'Juillet', 'Août',
  9070. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  9071. * ],
  9072. * weekdays: [
  9073. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  9074. * 'Jeudi', 'Vendredi', 'Samedi'
  9075. * ]
  9076. * }
  9077. * });
  9078. * ```
  9079. *
  9080. * @optionparent lang
  9081. */
  9082. {
  9083. /**
  9084. * The text for the label for the range selector buttons.
  9085. *
  9086. * @product highstock gantt
  9087. */
  9088. rangeSelectorZoom: 'Zoom',
  9089. /**
  9090. * The text for the label for the "from" input box in the range
  9091. * selector. Since v9.0, this string is empty as the label is not
  9092. * rendered by default.
  9093. *
  9094. * @product highstock gantt
  9095. */
  9096. rangeSelectorFrom: '',
  9097. /**
  9098. * The text for the label for the "to" input box in the range selector.
  9099. *
  9100. * @product highstock gantt
  9101. */
  9102. rangeSelectorTo: '→'
  9103. });
  9104. /* eslint-disable no-invalid-this, valid-jsdoc */
  9105. /**
  9106. * The range selector.
  9107. *
  9108. * @private
  9109. * @class
  9110. * @name Highcharts.RangeSelector
  9111. * @param {Highcharts.Chart} chart
  9112. */
  9113. var RangeSelector = /** @class */ (function () {
  9114. function RangeSelector(chart) {
  9115. /* *
  9116. *
  9117. * Properties
  9118. *
  9119. * */
  9120. this.buttons = void 0;
  9121. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  9122. this.initialButtonGroupWidth = 0;
  9123. this.options = void 0;
  9124. this.chart = chart;
  9125. // Run RangeSelector
  9126. this.init(chart);
  9127. }
  9128. /**
  9129. * The method to run when one of the buttons in the range selectors is
  9130. * clicked
  9131. *
  9132. * @private
  9133. * @function Highcharts.RangeSelector#clickButton
  9134. * @param {number} i
  9135. * The index of the button
  9136. * @param {boolean} [redraw]
  9137. * @return {void}
  9138. */
  9139. RangeSelector.prototype.clickButton = function (i, redraw) {
  9140. var rangeSelector = this,
  9141. chart = rangeSelector.chart,
  9142. rangeOptions = rangeSelector.buttonOptions[i],
  9143. baseAxis = chart.xAxis[0],
  9144. unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
  9145. dataMin = unionExtremes.dataMin,
  9146. dataMax = unionExtremes.dataMax,
  9147. newMin,
  9148. newMax = baseAxis && Math.round(Math.min(baseAxis.max,
  9149. pick(dataMax,
  9150. baseAxis.max))), // #1568
  9151. type = rangeOptions.type,
  9152. baseXAxisOptions,
  9153. range = rangeOptions._range,
  9154. rangeMin,
  9155. minSetting,
  9156. rangeSetting,
  9157. ctx,
  9158. ytdExtremes,
  9159. dataGrouping = rangeOptions.dataGrouping;
  9160. // chart has no data, base series is removed
  9161. if (dataMin === null || dataMax === null) {
  9162. return;
  9163. }
  9164. // Set the fixed range before range is altered
  9165. chart.fixedRange = range;
  9166. // Apply dataGrouping associated to button
  9167. if (dataGrouping) {
  9168. this.forcedDataGrouping = true;
  9169. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  9170. this.frozenStates = rangeOptions.preserveDataGrouping;
  9171. }
  9172. // Apply range
  9173. if (type === 'month' || type === 'year') {
  9174. if (!baseAxis) {
  9175. // This is set to the user options and picked up later when the
  9176. // axis is instantiated so that we know the min and max.
  9177. range = rangeOptions;
  9178. }
  9179. else {
  9180. ctx = {
  9181. range: rangeOptions,
  9182. max: newMax,
  9183. chart: chart,
  9184. dataMin: dataMin,
  9185. dataMax: dataMax
  9186. };
  9187. newMin = baseAxis.minFromRange.call(ctx);
  9188. if (isNumber(ctx.newMax)) {
  9189. newMax = ctx.newMax;
  9190. }
  9191. }
  9192. // Fixed times like minutes, hours, days
  9193. }
  9194. else if (range) {
  9195. newMin = Math.max(newMax - range, dataMin);
  9196. newMax = Math.min(newMin + range, dataMax);
  9197. }
  9198. else if (type === 'ytd') {
  9199. // On user clicks on the buttons, or a delayed action running from
  9200. // the beforeRender event (below), the baseAxis is defined.
  9201. if (baseAxis) {
  9202. // When "ytd" is the pre-selected button for the initial view,
  9203. // its calculation is delayed and rerun in the beforeRender
  9204. // event (below). When the series are initialized, but before
  9205. // the chart is rendered, we have access to the xData array
  9206. // (#942).
  9207. if (typeof dataMax === 'undefined') {
  9208. dataMin = Number.MAX_VALUE;
  9209. dataMax = Number.MIN_VALUE;
  9210. chart.series.forEach(function (series) {
  9211. // reassign it to the last item
  9212. var xData = series.xData;
  9213. dataMin = Math.min(xData[0], dataMin);
  9214. dataMax = Math.max(xData[xData.length - 1], dataMax);
  9215. });
  9216. redraw = false;
  9217. }
  9218. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  9219. newMin = rangeMin = ytdExtremes.min;
  9220. newMax = ytdExtremes.max;
  9221. // "ytd" is pre-selected. We don't yet have access to processed
  9222. // point and extremes data (things like pointStart and pointInterval
  9223. // are missing), so we delay the process (#942)
  9224. }
  9225. else {
  9226. rangeSelector.deferredYTDClick = i;
  9227. return;
  9228. }
  9229. }
  9230. else if (type === 'all' && baseAxis) {
  9231. newMin = dataMin;
  9232. newMax = dataMax;
  9233. }
  9234. if (defined(newMin)) {
  9235. newMin += rangeOptions._offsetMin;
  9236. }
  9237. if (defined(newMax)) {
  9238. newMax += rangeOptions._offsetMax;
  9239. }
  9240. rangeSelector.setSelected(i);
  9241. if (this.dropdown) {
  9242. this.dropdown.selectedIndex = i + 1;
  9243. }
  9244. // Update the chart
  9245. if (!baseAxis) {
  9246. // Axis not yet instanciated. Temporarily set min and range
  9247. // options and remove them on chart load (#4317).
  9248. baseXAxisOptions = splat(chart.options.xAxis)[0];
  9249. rangeSetting = baseXAxisOptions.range;
  9250. baseXAxisOptions.range = range;
  9251. minSetting = baseXAxisOptions.min;
  9252. baseXAxisOptions.min = rangeMin;
  9253. addEvent(chart, 'load', function resetMinAndRange() {
  9254. baseXAxisOptions.range = rangeSetting;
  9255. baseXAxisOptions.min = minSetting;
  9256. });
  9257. }
  9258. else {
  9259. // Existing axis object. Set extremes after render time.
  9260. baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation
  9261. {
  9262. trigger: 'rangeSelectorButton',
  9263. rangeSelectorButton: rangeOptions
  9264. });
  9265. }
  9266. fireEvent(this, 'afterBtnClick');
  9267. };
  9268. /**
  9269. * Set the selected option. This method only sets the internal flag, it
  9270. * doesn't update the buttons or the actual zoomed range.
  9271. *
  9272. * @private
  9273. * @function Highcharts.RangeSelector#setSelected
  9274. * @param {number} [selected]
  9275. * @return {void}
  9276. */
  9277. RangeSelector.prototype.setSelected = function (selected) {
  9278. this.selected = this.options.selected = selected;
  9279. };
  9280. /**
  9281. * Initialize the range selector
  9282. *
  9283. * @private
  9284. * @function Highcharts.RangeSelector#init
  9285. * @param {Highcharts.Chart} chart
  9286. * @return {void}
  9287. */
  9288. RangeSelector.prototype.init = function (chart) {
  9289. var rangeSelector = this,
  9290. options = chart.options.rangeSelector,
  9291. buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(),
  9292. selectedOption = options.selected,
  9293. blurInputs = function () {
  9294. var minInput = rangeSelector.minInput,
  9295. maxInput = rangeSelector.maxInput;
  9296. // #3274 in some case blur is not defined
  9297. if (minInput && minInput.blur) {
  9298. fireEvent(minInput, 'blur');
  9299. }
  9300. if (maxInput && maxInput.blur) {
  9301. fireEvent(maxInput, 'blur');
  9302. }
  9303. };
  9304. rangeSelector.chart = chart;
  9305. rangeSelector.options = options;
  9306. rangeSelector.buttons = [];
  9307. rangeSelector.buttonOptions = buttonOptions;
  9308. this.eventsToUnbind = [];
  9309. this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));
  9310. this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));
  9311. // Extend the buttonOptions with actual range
  9312. buttonOptions.forEach(rangeSelector.computeButtonRange);
  9313. // zoomed range based on a pre-selected button index
  9314. if (typeof selectedOption !== 'undefined' &&
  9315. buttonOptions[selectedOption]) {
  9316. this.clickButton(selectedOption, false);
  9317. }
  9318. this.eventsToUnbind.push(addEvent(chart, 'load', function () {
  9319. // If a data grouping is applied to the current button, release it
  9320. // when extremes change
  9321. if (chart.xAxis && chart.xAxis[0]) {
  9322. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  9323. if (this.max - this.min !==
  9324. chart.fixedRange &&
  9325. e.trigger !== 'rangeSelectorButton' &&
  9326. e.trigger !== 'updatedData' &&
  9327. rangeSelector.forcedDataGrouping &&
  9328. !rangeSelector.frozenStates) {
  9329. this.setDataGrouping(false, false);
  9330. }
  9331. });
  9332. }
  9333. }));
  9334. };
  9335. /**
  9336. * Dynamically update the range selector buttons after a new range has been
  9337. * set
  9338. *
  9339. * @private
  9340. * @function Highcharts.RangeSelector#updateButtonStates
  9341. * @return {void}
  9342. */
  9343. RangeSelector.prototype.updateButtonStates = function () {
  9344. var rangeSelector = this,
  9345. chart = this.chart,
  9346. dropdown = this.dropdown,
  9347. baseAxis = chart.xAxis[0],
  9348. actualRange = Math.round(baseAxis.max - baseAxis.min),
  9349. hasNoData = !baseAxis.hasVisibleSeries,
  9350. day = 24 * 36e5, // A single day in milliseconds
  9351. unionExtremes = (chart.scroller &&
  9352. chart.scroller.getUnionExtremes()) || baseAxis,
  9353. dataMin = unionExtremes.dataMin,
  9354. dataMax = unionExtremes.dataMax,
  9355. ytdExtremes = rangeSelector.getYTDExtremes(dataMax,
  9356. dataMin,
  9357. chart.time.useUTC),
  9358. ytdMin = ytdExtremes.min,
  9359. ytdMax = ytdExtremes.max,
  9360. selected = rangeSelector.selected,
  9361. selectedExists = isNumber(selected),
  9362. allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
  9363. buttons = rangeSelector.buttons;
  9364. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  9365. var range = rangeOptions._range,
  9366. type = rangeOptions.type,
  9367. count = rangeOptions.count || 1,
  9368. button = buttons[i],
  9369. state = 0,
  9370. disable,
  9371. select,
  9372. offsetRange = rangeOptions._offsetMax -
  9373. rangeOptions._offsetMin,
  9374. isSelected = i === selected,
  9375. // Disable buttons where the range exceeds what is allowed in
  9376. // the current view
  9377. isTooGreatRange = range >
  9378. dataMax - dataMin,
  9379. // Disable buttons where the range is smaller than the minimum
  9380. // range
  9381. isTooSmallRange = range < baseAxis.minRange,
  9382. // Do not select the YTD button if not explicitly told so
  9383. isYTDButNotSelected = false,
  9384. // Disable the All button if we're already showing all
  9385. isAllButAlreadyShowingAll = false,
  9386. isSameRange = range === actualRange;
  9387. // Months and years have a variable range so we check the extremes
  9388. if ((type === 'month' || type === 'year') &&
  9389. (actualRange + 36e5 >=
  9390. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  9391. (actualRange - 36e5 <=
  9392. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  9393. isSameRange = true;
  9394. }
  9395. else if (type === 'ytd') {
  9396. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  9397. isYTDButNotSelected = !isSelected;
  9398. }
  9399. else if (type === 'all') {
  9400. isSameRange = (baseAxis.max - baseAxis.min >=
  9401. dataMax - dataMin);
  9402. isAllButAlreadyShowingAll = (!isSelected &&
  9403. selectedExists &&
  9404. isSameRange);
  9405. }
  9406. // The new zoom area happens to match the range for a button - mark
  9407. // it selected. This happens when scrolling across an ordinal gap.
  9408. // It can be seen in the intraday demos when selecting 1h and scroll
  9409. // across the night gap.
  9410. disable = (!allButtonsEnabled &&
  9411. (isTooGreatRange ||
  9412. isTooSmallRange ||
  9413. isAllButAlreadyShowingAll ||
  9414. hasNoData));
  9415. select = ((isSelected && isSameRange) ||
  9416. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  9417. (isSelected && rangeSelector.frozenStates));
  9418. if (disable) {
  9419. state = 3;
  9420. }
  9421. else if (select) {
  9422. selectedExists = true; // Only one button can be selected
  9423. state = 2;
  9424. }
  9425. // If state has changed, update the button
  9426. if (button.state !== state) {
  9427. button.setState(state);
  9428. if (dropdown) {
  9429. dropdown.options[i + 1].disabled = disable;
  9430. if (state === 2) {
  9431. dropdown.selectedIndex = i + 1;
  9432. }
  9433. }
  9434. // Reset (#9209)
  9435. if (state === 0 && selected === i) {
  9436. rangeSelector.setSelected();
  9437. }
  9438. }
  9439. });
  9440. };
  9441. /**
  9442. * Compute and cache the range for an individual button
  9443. *
  9444. * @private
  9445. * @function Highcharts.RangeSelector#computeButtonRange
  9446. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  9447. * @return {void}
  9448. */
  9449. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  9450. var type = rangeOptions.type,
  9451. count = rangeOptions.count || 1,
  9452. // these time intervals have a fixed number of milliseconds, as
  9453. // opposed to month, ytd and year
  9454. fixedTimes = {
  9455. millisecond: 1,
  9456. second: 1000,
  9457. minute: 60 * 1000,
  9458. hour: 3600 * 1000,
  9459. day: 24 * 3600 * 1000,
  9460. week: 7 * 24 * 3600 * 1000
  9461. };
  9462. // Store the range on the button object
  9463. if (fixedTimes[type]) {
  9464. rangeOptions._range = fixedTimes[type] * count;
  9465. }
  9466. else if (type === 'month' || type === 'year') {
  9467. rangeOptions._range = {
  9468. month: 30,
  9469. year: 365
  9470. }[type] * 24 * 36e5 * count;
  9471. }
  9472. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  9473. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  9474. rangeOptions._range +=
  9475. rangeOptions._offsetMax - rangeOptions._offsetMin;
  9476. };
  9477. /**
  9478. * Get the unix timestamp of a HTML input for the dates
  9479. *
  9480. * @private
  9481. * @function Highcharts.RangeSelector#getInputValue
  9482. * @param {string} name
  9483. * @return {number}
  9484. */
  9485. RangeSelector.prototype.getInputValue = function (name) {
  9486. var input = name === 'min' ? this.minInput : this.maxInput;
  9487. var options = this.chart.options.rangeSelector;
  9488. var time = this.chart.time;
  9489. if (input) {
  9490. return ((input.type === 'text' && options.inputDateParser) ||
  9491. this.defaultInputDateParser)(input.value, time.useUTC, time);
  9492. }
  9493. return 0;
  9494. };
  9495. /**
  9496. * Set the internal and displayed value of a HTML input for the dates
  9497. *
  9498. * @private
  9499. * @function Highcharts.RangeSelector#setInputValue
  9500. * @param {string} name
  9501. * @param {number} [inputTime]
  9502. * @return {void}
  9503. */
  9504. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  9505. var options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  9506. if (input) {
  9507. var hcTimeAttr = input.getAttribute('data-hc-time');
  9508. var updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
  9509. if (defined(inputTime)) {
  9510. var previousTime = updatedTime;
  9511. if (defined(previousTime)) {
  9512. input.setAttribute('data-hc-time-previous', previousTime);
  9513. }
  9514. input.setAttribute('data-hc-time', inputTime);
  9515. updatedTime = inputTime;
  9516. }
  9517. input.value = time.dateFormat(this.inputTypeFormats[input.type] || options.inputEditDateFormat, updatedTime);
  9518. if (dateBox) {
  9519. dateBox.attr({
  9520. text: time.dateFormat(options.inputDateFormat, updatedTime)
  9521. });
  9522. }
  9523. }
  9524. };
  9525. /**
  9526. * Set the min and max value of a HTML input for the dates
  9527. *
  9528. * @private
  9529. * @function Highcharts.RangeSelector#setInputExtremes
  9530. * @param {string} name
  9531. * @param {number} min
  9532. * @param {number} max
  9533. * @return {void}
  9534. */
  9535. RangeSelector.prototype.setInputExtremes = function (name, min, max) {
  9536. var input = name === 'min' ? this.minInput : this.maxInput;
  9537. if (input) {
  9538. var format = this.inputTypeFormats[input.type];
  9539. var time = this.chart.time;
  9540. if (format) {
  9541. var newMin = time.dateFormat(format,
  9542. min);
  9543. if (input.min !== newMin) {
  9544. input.min = newMin;
  9545. }
  9546. var newMax = time.dateFormat(format,
  9547. max);
  9548. if (input.max !== newMax) {
  9549. input.max = newMax;
  9550. }
  9551. }
  9552. }
  9553. };
  9554. /**
  9555. * @private
  9556. * @function Highcharts.RangeSelector#showInput
  9557. * @param {string} name
  9558. * @return {void}
  9559. */
  9560. RangeSelector.prototype.showInput = function (name) {
  9561. var dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  9562. var input = name === 'min' ? this.minInput : this.maxInput;
  9563. if (input && dateBox && this.inputGroup) {
  9564. var isTextInput = input.type === 'text';
  9565. var _a = this.inputGroup,
  9566. translateX = _a.translateX,
  9567. translateY = _a.translateY;
  9568. css(input, {
  9569. width: isTextInput ? ((dateBox.width - 2) + 'px') : 'auto',
  9570. height: isTextInput ? ((dateBox.height - 2) + 'px') : 'auto',
  9571. border: '2px solid silver'
  9572. });
  9573. if (isTextInput) {
  9574. css(input, {
  9575. left: (translateX + dateBox.x) + 'px',
  9576. top: translateY + 'px'
  9577. });
  9578. // Inputs of types date, time or datetime-local should be centered
  9579. // on top of the dateBox
  9580. }
  9581. else {
  9582. css(input, {
  9583. left: Math.min(Math.round(dateBox.x +
  9584. translateX -
  9585. (input.offsetWidth - dateBox.width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
  9586. top: (translateY - (input.offsetHeight - dateBox.height) / 2) + 'px'
  9587. });
  9588. }
  9589. }
  9590. };
  9591. /**
  9592. * @private
  9593. * @function Highcharts.RangeSelector#hideInput
  9594. * @param {string} name
  9595. * @return {void}
  9596. */
  9597. RangeSelector.prototype.hideInput = function (name) {
  9598. var input = name === 'min' ? this.minInput : this.maxInput;
  9599. if (input) {
  9600. css(input, {
  9601. top: '-9999em',
  9602. border: 0,
  9603. width: '1px',
  9604. height: '1px'
  9605. });
  9606. }
  9607. };
  9608. /**
  9609. * @private
  9610. * @function Highcharts.RangeSelector#defaultInputDateParser
  9611. */
  9612. RangeSelector.prototype.defaultInputDateParser = function (inputDate, useUTC, time) {
  9613. var hasTimezone = function (str) {
  9614. return str.length > 6 &&
  9615. (str.lastIndexOf('-') === str.length - 6 ||
  9616. str.lastIndexOf('+') === str.length - 6);
  9617. };
  9618. var input = inputDate.split('/').join('-').split(' ').join('T');
  9619. if (input.indexOf('T') === -1) {
  9620. input += 'T00:00';
  9621. }
  9622. if (useUTC) {
  9623. input += 'Z';
  9624. }
  9625. else if (H.isSafari && !hasTimezone(input)) {
  9626. var offset = new Date(input).getTimezoneOffset() / 60;
  9627. input += offset <= 0 ? "+" + pad(-offset) + ":00" : "-" + pad(offset) + ":00";
  9628. }
  9629. var date = Date.parse(input);
  9630. // If the value isn't parsed directly to a value by the
  9631. // browser's Date.parse method, like YYYY-MM-DD in IE8, try
  9632. // parsing it a different way
  9633. if (!isNumber(date)) {
  9634. var parts = inputDate.split('-');
  9635. date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));
  9636. }
  9637. if (time && useUTC) {
  9638. date += time.getTimezoneOffset(date);
  9639. }
  9640. return date;
  9641. };
  9642. /**
  9643. * Draw either the 'from' or the 'to' HTML input box of the range selector
  9644. *
  9645. * @private
  9646. * @function Highcharts.RangeSelector#drawInput
  9647. * @param {string} name
  9648. * @return {RangeSelectorInputElements}
  9649. */
  9650. RangeSelector.prototype.drawInput = function (name) {
  9651. var _a = this,
  9652. chart = _a.chart,
  9653. div = _a.div,
  9654. inputGroup = _a.inputGroup;
  9655. var rangeSelector = this,
  9656. chartStyle = chart.renderer.style || {},
  9657. renderer = chart.renderer,
  9658. options = chart.options.rangeSelector,
  9659. lang = defaultOptions.lang,
  9660. isMin = name === 'min';
  9661. /**
  9662. * @private
  9663. */
  9664. function updateExtremes() {
  9665. var value = rangeSelector.getInputValue(name),
  9666. chartAxis = chart.xAxis[0],
  9667. dataAxis = chart.scroller && chart.scroller.xAxis ?
  9668. chart.scroller.xAxis :
  9669. chartAxis,
  9670. dataMin = dataAxis.dataMin,
  9671. dataMax = dataAxis.dataMax;
  9672. var maxInput = rangeSelector.maxInput,
  9673. minInput = rangeSelector.minInput;
  9674. if (value !== Number(input.getAttribute('data-hc-time-previous')) &&
  9675. isNumber(value)) {
  9676. input.setAttribute('data-hc-time-previous', value);
  9677. // Validate the extremes. If it goes beyound the data min or
  9678. // max, use the actual data extreme (#2438).
  9679. if (isMin && maxInput && isNumber(dataMin)) {
  9680. if (value > Number(maxInput.getAttribute('data-hc-time'))) {
  9681. value = void 0;
  9682. }
  9683. else if (value < dataMin) {
  9684. value = dataMin;
  9685. }
  9686. }
  9687. else if (minInput && isNumber(dataMax)) {
  9688. if (value < Number(minInput.getAttribute('data-hc-time'))) {
  9689. value = void 0;
  9690. }
  9691. else if (value > dataMax) {
  9692. value = dataMax;
  9693. }
  9694. }
  9695. // Set the extremes
  9696. if (typeof value !== 'undefined') { // @todo typof undefined
  9697. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  9698. }
  9699. }
  9700. }
  9701. // Create the text label
  9702. var text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'];
  9703. var label = renderer
  9704. .label(text, 0)
  9705. .addClass('highcharts-range-label')
  9706. .attr({
  9707. padding: text ? 2 : 0
  9708. })
  9709. .add(inputGroup);
  9710. // Create an SVG label that shows updated date ranges and and records
  9711. // click events that bring in the HTML input.
  9712. var dateBox = renderer
  9713. .label('', 0)
  9714. .addClass('highcharts-range-input')
  9715. .attr({
  9716. padding: 2,
  9717. width: options.inputBoxWidth,
  9718. height: options.inputBoxHeight,
  9719. 'text-align': 'center'
  9720. })
  9721. .on('click',
  9722. function () {
  9723. // If it is already focused, the onfocus event doesn't fire
  9724. // (#3713)
  9725. rangeSelector.showInput(name);
  9726. rangeSelector[name + 'Input'].focus();
  9727. });
  9728. if (!chart.styledMode) {
  9729. dateBox.attr({
  9730. stroke: options.inputBoxBorderColor,
  9731. 'stroke-width': 1
  9732. });
  9733. }
  9734. dateBox.add(inputGroup);
  9735. // Create the HTML input element. This is rendered as 1x1 pixel then set
  9736. // to the right size when focused.
  9737. var input = createElement('input', {
  9738. name: name,
  9739. className: 'highcharts-range-selector'
  9740. },
  9741. void 0,
  9742. div);
  9743. // #14788: Setting input.type to an unsupported type throws in IE, so
  9744. // we need to use setAttribute instead
  9745. input.setAttribute('type', preferredInputType(options.inputDateFormat || '%b %e, %Y'));
  9746. if (!chart.styledMode) {
  9747. // Styles
  9748. label.css(merge(chartStyle, options.labelStyle));
  9749. dateBox.css(merge({
  9750. color: palette.neutralColor80
  9751. }, chartStyle, options.inputStyle));
  9752. css(input, extend({
  9753. position: 'absolute',
  9754. border: 0,
  9755. boxShadow: '0 0 15px rgba(0,0,0,0.3)',
  9756. width: '1px',
  9757. height: '1px',
  9758. padding: 0,
  9759. textAlign: 'center',
  9760. fontSize: chartStyle.fontSize,
  9761. fontFamily: chartStyle.fontFamily,
  9762. top: '-9999em' // #4798
  9763. }, options.inputStyle));
  9764. }
  9765. // Blow up the input box
  9766. input.onfocus = function () {
  9767. rangeSelector.showInput(name);
  9768. };
  9769. // Hide away the input box
  9770. input.onblur = function () {
  9771. // update extermes only when inputs are active
  9772. if (input === H.doc.activeElement) { // Only when focused
  9773. // Update also when no `change` event is triggered, like when
  9774. // clicking inside the SVG (#4710)
  9775. updateExtremes();
  9776. }
  9777. // #10404 - move hide and blur outside focus
  9778. rangeSelector.hideInput(name);
  9779. rangeSelector.setInputValue(name);
  9780. input.blur(); // #4606
  9781. };
  9782. var keyDown = false;
  9783. // handle changes in the input boxes
  9784. input.onchange = function () {
  9785. updateExtremes();
  9786. // Blur input when clicking date input calendar
  9787. if (!keyDown) {
  9788. rangeSelector.hideInput(name);
  9789. input.blur();
  9790. }
  9791. };
  9792. input.onkeypress = function (event) {
  9793. // IE does not fire onchange on enter
  9794. if (event.keyCode === 13) {
  9795. updateExtremes();
  9796. }
  9797. };
  9798. input.onkeydown = function () {
  9799. keyDown = true;
  9800. };
  9801. input.onkeyup = function () {
  9802. keyDown = false;
  9803. };
  9804. return { dateBox: dateBox, input: input, label: label };
  9805. };
  9806. /**
  9807. * Get the position of the range selector buttons and inputs. This can be
  9808. * overridden from outside for custom positioning.
  9809. *
  9810. * @private
  9811. * @function Highcharts.RangeSelector#getPosition
  9812. *
  9813. * @return {Highcharts.Dictionary<number>}
  9814. */
  9815. RangeSelector.prototype.getPosition = function () {
  9816. var chart = this.chart,
  9817. options = chart.options.rangeSelector,
  9818. top = options.verticalAlign === 'top' ?
  9819. chart.plotTop - chart.axisOffset[0] :
  9820. 0; // set offset only for varticalAlign top
  9821. return {
  9822. buttonTop: top + options.buttonPosition.y,
  9823. inputTop: top + options.inputPosition.y - 10
  9824. };
  9825. };
  9826. /**
  9827. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  9828. * the current timestamp. Will choose dataMin if its value is higher than
  9829. * the timestamp for the start of current year.
  9830. *
  9831. * @private
  9832. * @function Highcharts.RangeSelector#getYTDExtremes
  9833. *
  9834. * @param {number} dataMax
  9835. *
  9836. * @param {number} dataMin
  9837. *
  9838. * @return {*}
  9839. * Returns min and max for the YTD
  9840. */
  9841. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  9842. var time = this.chart.time,
  9843. min,
  9844. now = new time.Date(dataMax),
  9845. year = time.get('FullYear',
  9846. now),
  9847. startOfYear = useUTC ?
  9848. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  9849. +new time.Date(year, 0, 1);
  9850. min = Math.max(dataMin, startOfYear);
  9851. var ts = now.getTime();
  9852. return {
  9853. max: Math.min(dataMax || ts, ts),
  9854. min: min
  9855. };
  9856. };
  9857. /**
  9858. * Render the range selector including the buttons and the inputs. The first
  9859. * time render is called, the elements are created and positioned. On
  9860. * subsequent calls, they are moved and updated.
  9861. *
  9862. * @private
  9863. * @function Highcharts.RangeSelector#render
  9864. * @param {number} [min]
  9865. * X axis minimum
  9866. * @param {number} [max]
  9867. * X axis maximum
  9868. * @return {void}
  9869. */
  9870. RangeSelector.prototype.render = function (min, max) {
  9871. var chart = this.chart,
  9872. renderer = chart.renderer,
  9873. container = chart.container,
  9874. chartOptions = chart.options,
  9875. options = chartOptions.rangeSelector,
  9876. // Place inputs above the container
  9877. inputsZIndex = pick(chartOptions.chart.style &&
  9878. chartOptions.chart.style.zIndex, 0) + 1,
  9879. inputEnabled = options.inputEnabled,
  9880. rendered = this.rendered;
  9881. if (options.enabled === false) {
  9882. return;
  9883. }
  9884. // create the elements
  9885. if (!rendered) {
  9886. this.group = renderer.g('range-selector-group')
  9887. .attr({
  9888. zIndex: 7
  9889. })
  9890. .add();
  9891. this.div = createElement('div', void 0, {
  9892. position: 'relative',
  9893. height: 0,
  9894. zIndex: inputsZIndex
  9895. });
  9896. if (this.buttonOptions.length) {
  9897. this.renderButtons();
  9898. }
  9899. // First create a wrapper outside the container in order to make
  9900. // the inputs work and make export correct
  9901. if (container.parentNode) {
  9902. container.parentNode.insertBefore(this.div, container);
  9903. }
  9904. if (inputEnabled) {
  9905. // Create the group to keep the inputs
  9906. this.inputGroup = renderer.g('input-group').add(this.group);
  9907. var minElems = this.drawInput('min');
  9908. this.minDateBox = minElems.dateBox;
  9909. this.minLabel = minElems.label;
  9910. this.minInput = minElems.input;
  9911. var maxElems = this.drawInput('max');
  9912. this.maxDateBox = maxElems.dateBox;
  9913. this.maxLabel = maxElems.label;
  9914. this.maxInput = maxElems.input;
  9915. }
  9916. }
  9917. if (inputEnabled) {
  9918. // Set or reset the input values
  9919. this.setInputValue('min', min);
  9920. this.setInputValue('max', max);
  9921. var unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
  9922. if (defined(unionExtremes.dataMin) && defined(unionExtremes.dataMax)) {
  9923. var minRange = chart.xAxis[0].minRange || 0;
  9924. this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
  9925. this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
  9926. }
  9927. // Reflow
  9928. if (this.inputGroup) {
  9929. var x_1 = 0;
  9930. [
  9931. this.minLabel,
  9932. this.minDateBox,
  9933. this.maxLabel,
  9934. this.maxDateBox
  9935. ].forEach(function (label) {
  9936. if (label && label.width) {
  9937. label.attr({ x: x_1 });
  9938. x_1 += label.width + options.inputSpacing;
  9939. }
  9940. });
  9941. }
  9942. }
  9943. this.alignElements();
  9944. this.rendered = true;
  9945. };
  9946. /**
  9947. * Render the range buttons. This only runs the first time, later the
  9948. * positioning is laid out in alignElements.
  9949. *
  9950. * @private
  9951. * @function Highcharts.RangeSelector#renderButtons
  9952. * @return {void}
  9953. */
  9954. RangeSelector.prototype.renderButtons = function () {
  9955. var _this = this;
  9956. var _a = this,
  9957. buttons = _a.buttons,
  9958. chart = _a.chart,
  9959. options = _a.options;
  9960. var lang = defaultOptions.lang;
  9961. var renderer = chart.renderer;
  9962. var buttonTheme = merge(options.buttonTheme);
  9963. var states = buttonTheme && buttonTheme.states;
  9964. // Prevent the button from resetting the width when the button state
  9965. // changes since we need more control over the width when collapsing
  9966. // the buttons
  9967. var width = buttonTheme.width || 28;
  9968. delete buttonTheme.width;
  9969. this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
  9970. var dropdown = this.dropdown = createElement('select',
  9971. void 0, {
  9972. position: 'absolute',
  9973. width: '1px',
  9974. height: '1px',
  9975. padding: 0,
  9976. border: 0,
  9977. top: '-9999em',
  9978. cursor: 'pointer',
  9979. opacity: 0.0001
  9980. },
  9981. this.div);
  9982. // Prevent page zoom on iPhone
  9983. addEvent(dropdown, 'touchstart', function () {
  9984. dropdown.style.fontSize = '16px';
  9985. });
  9986. // Forward events from select to button
  9987. [
  9988. [H.isMS ? 'mouseover' : 'mouseenter'],
  9989. [H.isMS ? 'mouseout' : 'mouseleave'],
  9990. ['change', 'click']
  9991. ].forEach(function (_a) {
  9992. var from = _a[0],
  9993. to = _a[1];
  9994. addEvent(dropdown, from, function () {
  9995. var button = buttons[_this.currentButtonIndex()];
  9996. if (button) {
  9997. fireEvent(button.element, to || from);
  9998. }
  9999. });
  10000. });
  10001. this.zoomText = renderer
  10002. .text(lang.rangeSelectorZoom, 0, 15)
  10003. .add(this.buttonGroup);
  10004. if (!this.chart.styledMode) {
  10005. this.zoomText.css(options.labelStyle);
  10006. buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);
  10007. }
  10008. createElement('option', {
  10009. textContent: this.zoomText.textStr,
  10010. disabled: true
  10011. }, void 0, dropdown);
  10012. this.buttonOptions.forEach(function (rangeOptions, i) {
  10013. createElement('option', {
  10014. textContent: rangeOptions.title || rangeOptions.text
  10015. }, void 0, dropdown);
  10016. buttons[i] = renderer
  10017. .button(rangeOptions.text, 0, 0, function (e) {
  10018. // extract events from button object and call
  10019. var buttonEvents = (rangeOptions.events &&
  10020. rangeOptions.events.click),
  10021. callDefaultEvent;
  10022. if (buttonEvents) {
  10023. callDefaultEvent =
  10024. buttonEvents.call(rangeOptions, e);
  10025. }
  10026. if (callDefaultEvent !== false) {
  10027. _this.clickButton(i);
  10028. }
  10029. _this.isActive = true;
  10030. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  10031. .attr({
  10032. 'text-align': 'center',
  10033. width: width
  10034. })
  10035. .add(_this.buttonGroup);
  10036. if (rangeOptions.title) {
  10037. buttons[i].attr('title', rangeOptions.title);
  10038. }
  10039. });
  10040. };
  10041. /**
  10042. * Align the elements horizontally and vertically.
  10043. *
  10044. * @private
  10045. * @function Highcharts.RangeSelector#alignElements
  10046. * @return {void}
  10047. */
  10048. RangeSelector.prototype.alignElements = function () {
  10049. var _this = this;
  10050. var _a = this,
  10051. buttonGroup = _a.buttonGroup,
  10052. buttons = _a.buttons,
  10053. chart = _a.chart,
  10054. group = _a.group,
  10055. inputGroup = _a.inputGroup,
  10056. options = _a.options,
  10057. zoomText = _a.zoomText;
  10058. var chartOptions = chart.options;
  10059. var navButtonOptions = (chartOptions.exporting &&
  10060. chartOptions.exporting.enabled !== false &&
  10061. chartOptions.navigation &&
  10062. chartOptions.navigation.buttonOptions);
  10063. var buttonPosition = options.buttonPosition,
  10064. inputPosition = options.inputPosition,
  10065. verticalAlign = options.verticalAlign;
  10066. // Get the X offset required to avoid overlapping with the exporting
  10067. // button. This is is used both by the buttonGroup and the inputGroup.
  10068. var getXOffsetForExportButton = function (group,
  10069. position) {
  10070. if (navButtonOptions &&
  10071. _this.titleCollision(chart) &&
  10072. verticalAlign === 'top' &&
  10073. position.align === 'right' && ((position.y -
  10074. group.getBBox().height - 12) <
  10075. ((navButtonOptions.y || 0) +
  10076. (navButtonOptions.height || 0) +
  10077. chart.spacing[0]))) {
  10078. return -40;
  10079. }
  10080. return 0;
  10081. };
  10082. var plotLeft = chart.plotLeft;
  10083. if (group && buttonPosition && inputPosition) {
  10084. var translateX = buttonPosition.x - chart.spacing[3];
  10085. if (buttonGroup) {
  10086. this.positionButtons();
  10087. if (!this.initialButtonGroupWidth) {
  10088. var width_1 = 0;
  10089. if (zoomText) {
  10090. width_1 += zoomText.getBBox().width + 5;
  10091. }
  10092. buttons.forEach(function (button, i) {
  10093. width_1 += button.width;
  10094. if (i !== buttons.length - 1) {
  10095. width_1 += options.buttonSpacing;
  10096. }
  10097. });
  10098. this.initialButtonGroupWidth = width_1;
  10099. }
  10100. plotLeft -= chart.spacing[3];
  10101. this.updateButtonStates();
  10102. // Detect collision between button group and exporting
  10103. var xOffsetForExportButton_1 = getXOffsetForExportButton(buttonGroup,
  10104. buttonPosition);
  10105. this.alignButtonGroup(xOffsetForExportButton_1);
  10106. // Skip animation
  10107. group.placed = buttonGroup.placed = chart.hasLoaded;
  10108. }
  10109. var xOffsetForExportButton = 0;
  10110. if (inputGroup) {
  10111. // Detect collision between the input group and exporting button
  10112. xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);
  10113. if (inputPosition.align === 'left') {
  10114. translateX = plotLeft;
  10115. }
  10116. else if (inputPosition.align === 'right') {
  10117. translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
  10118. }
  10119. // Update the alignment to the updated spacing box
  10120. inputGroup.align({
  10121. y: inputPosition.y,
  10122. width: inputGroup.getBBox().width,
  10123. align: inputPosition.align,
  10124. // fix wrong getBBox() value on right align
  10125. x: inputPosition.x + translateX - 2
  10126. }, true, chart.spacingBox);
  10127. // Skip animation
  10128. inputGroup.placed = chart.hasLoaded;
  10129. }
  10130. this.handleCollision(xOffsetForExportButton);
  10131. // Vertical align
  10132. group.align({
  10133. verticalAlign: verticalAlign
  10134. }, true, chart.spacingBox);
  10135. var alignTranslateY = group.alignAttr.translateY;
  10136. // Set position
  10137. var groupHeight = group.getBBox().height + 20; // # 20 padding
  10138. var translateY = 0;
  10139. // Calculate bottom position
  10140. if (verticalAlign === 'bottom') {
  10141. var legendOptions = chart.legend && chart.legend.options;
  10142. var legendHeight = (legendOptions &&
  10143. legendOptions.verticalAlign === 'bottom' &&
  10144. legendOptions.enabled &&
  10145. !legendOptions.floating ?
  10146. (chart.legend.legendHeight +
  10147. pick(legendOptions.margin, 10)) :
  10148. 0);
  10149. groupHeight = groupHeight + legendHeight - 20;
  10150. translateY = (alignTranslateY -
  10151. groupHeight -
  10152. (options.floating ? 0 : options.y) -
  10153. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  10154. 10 // 10 spacing
  10155. );
  10156. }
  10157. if (verticalAlign === 'top') {
  10158. if (options.floating) {
  10159. translateY = 0;
  10160. }
  10161. if (chart.titleOffset && chart.titleOffset[0]) {
  10162. translateY = chart.titleOffset[0];
  10163. }
  10164. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  10165. }
  10166. else if (verticalAlign === 'middle') {
  10167. if (inputPosition.y === buttonPosition.y) {
  10168. translateY = alignTranslateY;
  10169. }
  10170. else if (inputPosition.y || buttonPosition.y) {
  10171. if (inputPosition.y < 0 ||
  10172. buttonPosition.y < 0) {
  10173. translateY -= Math.min(inputPosition.y, buttonPosition.y);
  10174. }
  10175. else {
  10176. translateY = alignTranslateY - groupHeight;
  10177. }
  10178. }
  10179. }
  10180. group.translate(options.x, options.y + Math.floor(translateY));
  10181. // Translate HTML inputs
  10182. var _b = this,
  10183. minInput = _b.minInput,
  10184. maxInput = _b.maxInput,
  10185. dropdown = _b.dropdown;
  10186. if (options.inputEnabled && minInput && maxInput) {
  10187. minInput.style.marginTop = group.translateY + 'px';
  10188. maxInput.style.marginTop = group.translateY + 'px';
  10189. }
  10190. if (dropdown) {
  10191. dropdown.style.marginTop = group.translateY + 'px';
  10192. }
  10193. }
  10194. };
  10195. /**
  10196. * Align the button group horizontally and vertically.
  10197. *
  10198. * @private
  10199. * @function Highcharts.RangeSelector#alignButtonGroup
  10200. * @param {number} xOffsetForExportButton
  10201. * @param {number} [width]
  10202. * @return {void}
  10203. */
  10204. RangeSelector.prototype.alignButtonGroup = function (xOffsetForExportButton, width) {
  10205. var _a = this,
  10206. chart = _a.chart,
  10207. options = _a.options,
  10208. buttonGroup = _a.buttonGroup,
  10209. buttons = _a.buttons;
  10210. var buttonPosition = options.buttonPosition;
  10211. var plotLeft = chart.plotLeft - chart.spacing[3];
  10212. var translateX = buttonPosition.x - chart.spacing[3];
  10213. if (buttonPosition.align === 'right') {
  10214. translateX += xOffsetForExportButton - plotLeft; // #13014
  10215. }
  10216. else if (buttonPosition.align === 'center') {
  10217. translateX -= plotLeft / 2;
  10218. }
  10219. if (buttonGroup) {
  10220. // Align button group
  10221. buttonGroup.align({
  10222. y: buttonPosition.y,
  10223. width: pick(width, this.initialButtonGroupWidth),
  10224. align: buttonPosition.align,
  10225. x: translateX
  10226. }, true, chart.spacingBox);
  10227. }
  10228. };
  10229. /**
  10230. * @private
  10231. * @function Highcharts.RangeSelector#positionButtons
  10232. * @return {void}
  10233. */
  10234. RangeSelector.prototype.positionButtons = function () {
  10235. var _a = this,
  10236. buttons = _a.buttons,
  10237. chart = _a.chart,
  10238. options = _a.options,
  10239. zoomText = _a.zoomText;
  10240. var verb = chart.hasLoaded ? 'animate' : 'attr';
  10241. var buttonPosition = options.buttonPosition;
  10242. var plotLeft = chart.plotLeft;
  10243. var buttonLeft = plotLeft;
  10244. if (zoomText && zoomText.visibility !== 'hidden') {
  10245. // #8769, allow dynamically updating margins
  10246. zoomText[verb]({
  10247. x: pick(plotLeft + buttonPosition.x, plotLeft)
  10248. });
  10249. // Button start position
  10250. buttonLeft += buttonPosition.x +
  10251. zoomText.getBBox().width + 5;
  10252. }
  10253. this.buttonOptions.forEach(function (rangeOptions, i) {
  10254. if (buttons[i].visibility !== 'hidden') {
  10255. buttons[i][verb]({ x: buttonLeft });
  10256. // increase button position for the next button
  10257. buttonLeft += buttons[i].width + options.buttonSpacing;
  10258. }
  10259. else {
  10260. buttons[i][verb]({ x: plotLeft });
  10261. }
  10262. });
  10263. };
  10264. /**
  10265. * Handle collision between the button group and the input group
  10266. *
  10267. * @private
  10268. * @function Highcharts.RangeSelector#handleCollision
  10269. *
  10270. * @param {number} xOffsetForExportButton
  10271. * The X offset of the group required to make room for the
  10272. * exporting button
  10273. * @return {void}
  10274. */
  10275. RangeSelector.prototype.handleCollision = function (xOffsetForExportButton) {
  10276. var _this = this;
  10277. var _a = this,
  10278. chart = _a.chart,
  10279. buttonGroup = _a.buttonGroup,
  10280. inputGroup = _a.inputGroup;
  10281. var _b = this.options,
  10282. buttonPosition = _b.buttonPosition,
  10283. dropdown = _b.dropdown,
  10284. inputPosition = _b.inputPosition;
  10285. var maxButtonWidth = function () {
  10286. var buttonWidth = 0;
  10287. _this.buttons.forEach(function (button) {
  10288. var bBox = button.getBBox();
  10289. if (bBox.width > buttonWidth) {
  10290. buttonWidth = bBox.width;
  10291. }
  10292. });
  10293. return buttonWidth;
  10294. };
  10295. var groupsOverlap = function (buttonGroupWidth) {
  10296. if (inputGroup && buttonGroup) {
  10297. var inputGroupX = (inputGroup.alignAttr.translateX +
  10298. inputGroup.alignOptions.x -
  10299. xOffsetForExportButton +
  10300. // getBBox for detecing left margin
  10301. inputGroup.getBBox().x +
  10302. // 2px padding to not overlap input and label
  10303. 2);
  10304. var inputGroupWidth = inputGroup.alignOptions.width;
  10305. var buttonGroupX = buttonGroup.alignAttr.translateX +
  10306. buttonGroup.getBBox().x;
  10307. return (buttonGroupX + buttonGroupWidth > inputGroupX) &&
  10308. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  10309. (buttonPosition.y <
  10310. (inputPosition.y +
  10311. inputGroup.getBBox().height));
  10312. }
  10313. return false;
  10314. };
  10315. var moveInputsDown = function () {
  10316. if (inputGroup && buttonGroup) {
  10317. inputGroup.attr({
  10318. translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
  10319. 0 :
  10320. -xOffsetForExportButton),
  10321. translateY: inputGroup.alignAttr.translateY +
  10322. buttonGroup.getBBox().height + 10
  10323. });
  10324. }
  10325. };
  10326. if (buttonGroup) {
  10327. if (dropdown === 'always') {
  10328. this.collapseButtons(xOffsetForExportButton);
  10329. if (groupsOverlap(maxButtonWidth())) {
  10330. // Move the inputs down if there is still a collision
  10331. // after collapsing the buttons
  10332. moveInputsDown();
  10333. }
  10334. return;
  10335. }
  10336. if (dropdown === 'never') {
  10337. this.expandButtons();
  10338. }
  10339. }
  10340. // Detect collision
  10341. if (inputGroup && buttonGroup) {
  10342. if ((inputPosition.align === buttonPosition.align) ||
  10343. // 20 is minimal spacing between elements
  10344. groupsOverlap(this.initialButtonGroupWidth + 20)) {
  10345. if (dropdown === 'responsive') {
  10346. this.collapseButtons(xOffsetForExportButton);
  10347. if (groupsOverlap(maxButtonWidth())) {
  10348. moveInputsDown();
  10349. }
  10350. }
  10351. else {
  10352. moveInputsDown();
  10353. }
  10354. }
  10355. else if (dropdown === 'responsive') {
  10356. this.expandButtons();
  10357. }
  10358. }
  10359. else if (buttonGroup && dropdown === 'responsive') {
  10360. if (this.initialButtonGroupWidth > chart.plotWidth) {
  10361. this.collapseButtons(xOffsetForExportButton);
  10362. }
  10363. else {
  10364. this.expandButtons();
  10365. }
  10366. }
  10367. };
  10368. /**
  10369. * Collapse the buttons and put the select element on top.
  10370. *
  10371. * @private
  10372. * @function Highcharts.RangeSelector#collapseButtons
  10373. * @param {number} xOffsetForExportButton
  10374. * @return {void}
  10375. */
  10376. RangeSelector.prototype.collapseButtons = function (xOffsetForExportButton) {
  10377. var _a;
  10378. var _b = this,
  10379. buttons = _b.buttons,
  10380. buttonOptions = _b.buttonOptions,
  10381. dropdown = _b.dropdown,
  10382. options = _b.options,
  10383. zoomText = _b.zoomText;
  10384. var getAttribs = function (text) { return ({
  10385. text: text ? text + " \u25BE" : '▾',
  10386. width: 'auto',
  10387. paddingLeft: 8,
  10388. paddingRight: 8
  10389. }); };
  10390. if (zoomText) {
  10391. zoomText.hide();
  10392. }
  10393. var hasActiveButton = false;
  10394. buttonOptions.forEach(function (rangeOptions, i) {
  10395. var button = buttons[i];
  10396. if (button.state !== 2) {
  10397. button.hide();
  10398. }
  10399. else {
  10400. button.show();
  10401. button.attr(getAttribs(rangeOptions.text));
  10402. hasActiveButton = true;
  10403. }
  10404. });
  10405. if (!hasActiveButton) {
  10406. if (dropdown) {
  10407. dropdown.selectedIndex = 0;
  10408. }
  10409. buttons[0].show();
  10410. buttons[0].attr(getAttribs((_a = this.zoomText) === null || _a === void 0 ? void 0 : _a.textStr));
  10411. }
  10412. var align = options.buttonPosition.align;
  10413. this.positionButtons();
  10414. if (align === 'right' || align === 'center') {
  10415. this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);
  10416. }
  10417. this.showDropdown();
  10418. };
  10419. /**
  10420. * Show all the buttons and hide the select element.
  10421. *
  10422. * @private
  10423. * @function Highcharts.RangeSelector#expandButtons
  10424. * @return {void}
  10425. */
  10426. RangeSelector.prototype.expandButtons = function () {
  10427. var _a = this,
  10428. buttons = _a.buttons,
  10429. buttonOptions = _a.buttonOptions,
  10430. options = _a.options,
  10431. zoomText = _a.zoomText;
  10432. this.hideDropdown();
  10433. if (zoomText) {
  10434. zoomText.show();
  10435. }
  10436. buttonOptions.forEach(function (rangeOptions, i) {
  10437. var button = buttons[i];
  10438. button.show();
  10439. button.attr({
  10440. text: rangeOptions.text,
  10441. width: options.buttonTheme.width || 28,
  10442. paddingLeft: 'unset',
  10443. paddingRight: 'unset'
  10444. });
  10445. if (button.state < 2) {
  10446. button.setState(0);
  10447. }
  10448. });
  10449. this.positionButtons();
  10450. };
  10451. /**
  10452. * Get the index of the visible button when the buttons are collapsed.
  10453. *
  10454. * @private
  10455. * @function Highcharts.RangeSelector#currentButtonIndex
  10456. * @return {number}
  10457. */
  10458. RangeSelector.prototype.currentButtonIndex = function () {
  10459. var dropdown = this.dropdown;
  10460. if (dropdown && dropdown.selectedIndex > 0) {
  10461. return dropdown.selectedIndex - 1;
  10462. }
  10463. return 0;
  10464. };
  10465. /**
  10466. * Position the select element on top of the button.
  10467. *
  10468. * @private
  10469. * @function Highcharts.RangeSelector#showDropdown
  10470. * @return {void}
  10471. */
  10472. RangeSelector.prototype.showDropdown = function () {
  10473. var _a = this,
  10474. buttonGroup = _a.buttonGroup,
  10475. buttons = _a.buttons,
  10476. chart = _a.chart,
  10477. dropdown = _a.dropdown;
  10478. if (buttonGroup && dropdown) {
  10479. var translateX = buttonGroup.translateX,
  10480. translateY = buttonGroup.translateY;
  10481. var bBox = buttons[this.currentButtonIndex()].getBBox();
  10482. css(dropdown, {
  10483. left: (chart.plotLeft + translateX) + 'px',
  10484. top: (translateY + 0.5) + 'px',
  10485. width: bBox.width + 'px',
  10486. height: bBox.height + 'px'
  10487. });
  10488. this.hasVisibleDropdown = true;
  10489. }
  10490. };
  10491. /**
  10492. * @private
  10493. * @function Highcharts.RangeSelector#hideDropdown
  10494. * @return {void}
  10495. */
  10496. RangeSelector.prototype.hideDropdown = function () {
  10497. var dropdown = this.dropdown;
  10498. if (dropdown) {
  10499. css(dropdown, {
  10500. top: '-9999em',
  10501. width: '1px',
  10502. height: '1px'
  10503. });
  10504. this.hasVisibleDropdown = false;
  10505. }
  10506. };
  10507. /**
  10508. * Extracts height of range selector
  10509. *
  10510. * @private
  10511. * @function Highcharts.RangeSelector#getHeight
  10512. * @return {number}
  10513. * Returns rangeSelector height
  10514. */
  10515. RangeSelector.prototype.getHeight = function () {
  10516. var rangeSelector = this,
  10517. options = rangeSelector.options,
  10518. rangeSelectorGroup = rangeSelector.group,
  10519. inputPosition = options.inputPosition,
  10520. buttonPosition = options.buttonPosition,
  10521. yPosition = options.y,
  10522. buttonPositionY = buttonPosition.y,
  10523. inputPositionY = inputPosition.y,
  10524. rangeSelectorHeight = 0,
  10525. minPosition;
  10526. if (options.height) {
  10527. return options.height;
  10528. }
  10529. // Align the elements before we read the height in case we're switching
  10530. // between wrapped and non-wrapped layout
  10531. this.alignElements();
  10532. rangeSelectorHeight = rangeSelectorGroup ?
  10533. // 13px to keep back compatibility
  10534. (rangeSelectorGroup.getBBox(true).height) + 13 +
  10535. yPosition :
  10536. 0;
  10537. minPosition = Math.min(inputPositionY, buttonPositionY);
  10538. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  10539. (inputPositionY > 0 && buttonPositionY > 0)) {
  10540. rangeSelectorHeight += Math.abs(minPosition);
  10541. }
  10542. return rangeSelectorHeight;
  10543. };
  10544. /**
  10545. * Detect collision with title or subtitle
  10546. *
  10547. * @private
  10548. * @function Highcharts.RangeSelector#titleCollision
  10549. *
  10550. * @param {Highcharts.Chart} chart
  10551. *
  10552. * @return {boolean}
  10553. * Returns collision status
  10554. */
  10555. RangeSelector.prototype.titleCollision = function (chart) {
  10556. return !(chart.options.title.text ||
  10557. chart.options.subtitle.text);
  10558. };
  10559. /**
  10560. * Update the range selector with new options
  10561. *
  10562. * @private
  10563. * @function Highcharts.RangeSelector#update
  10564. * @param {Highcharts.RangeSelectorOptions} options
  10565. * @return {void}
  10566. */
  10567. RangeSelector.prototype.update = function (options) {
  10568. var chart = this.chart;
  10569. merge(true, chart.options.rangeSelector, options);
  10570. this.destroy();
  10571. this.init(chart);
  10572. this.render();
  10573. };
  10574. /**
  10575. * Destroys allocated elements.
  10576. *
  10577. * @private
  10578. * @function Highcharts.RangeSelector#destroy
  10579. */
  10580. RangeSelector.prototype.destroy = function () {
  10581. var rSelector = this,
  10582. minInput = rSelector.minInput,
  10583. maxInput = rSelector.maxInput;
  10584. if (rSelector.eventsToUnbind) {
  10585. rSelector.eventsToUnbind.forEach(function (unbind) { return unbind(); });
  10586. rSelector.eventsToUnbind = void 0;
  10587. }
  10588. // Destroy elements in collections
  10589. destroyObjectProperties(rSelector.buttons);
  10590. // Clear input element events
  10591. if (minInput) {
  10592. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  10593. }
  10594. if (maxInput) {
  10595. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  10596. }
  10597. // Destroy HTML and SVG elements
  10598. objectEach(rSelector, function (val, key) {
  10599. if (val && key !== 'chart') {
  10600. if (val instanceof SVGElement) {
  10601. // SVGElement
  10602. val.destroy();
  10603. }
  10604. else if (val instanceof window.HTMLElement) {
  10605. // HTML element
  10606. discardElement(val);
  10607. }
  10608. }
  10609. if (val !== RangeSelector.prototype[key]) {
  10610. rSelector[key] = null;
  10611. }
  10612. }, this);
  10613. };
  10614. return RangeSelector;
  10615. }());
  10616. /**
  10617. * The default buttons for pre-selecting time frames
  10618. */
  10619. RangeSelector.prototype.defaultButtons = [{
  10620. type: 'month',
  10621. count: 1,
  10622. text: '1m',
  10623. title: 'View 1 month'
  10624. }, {
  10625. type: 'month',
  10626. count: 3,
  10627. text: '3m',
  10628. title: 'View 3 months'
  10629. }, {
  10630. type: 'month',
  10631. count: 6,
  10632. text: '6m',
  10633. title: 'View 6 months'
  10634. }, {
  10635. type: 'ytd',
  10636. text: 'YTD',
  10637. title: 'View year to date'
  10638. }, {
  10639. type: 'year',
  10640. count: 1,
  10641. text: '1y',
  10642. title: 'View 1 year'
  10643. }, {
  10644. type: 'all',
  10645. text: 'All',
  10646. title: 'View all'
  10647. }];
  10648. /**
  10649. * The date formats to use when setting min, max and value on date inputs
  10650. */
  10651. RangeSelector.prototype.inputTypeFormats = {
  10652. 'datetime-local': '%Y-%m-%dT%H:%M:%S',
  10653. 'date': '%Y-%m-%d',
  10654. 'time': '%H:%M:%S'
  10655. };
  10656. /**
  10657. * Get the preferred input type based on a date format string.
  10658. *
  10659. * @private
  10660. * @function preferredInputType
  10661. * @param {string} format
  10662. * @return {string}
  10663. */
  10664. function preferredInputType(format) {
  10665. var ms = format.indexOf('%L') !== -1;
  10666. if (ms) {
  10667. return 'text';
  10668. }
  10669. var date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y'].some(function (char) {
  10670. return format.indexOf('%' + char) !== -1;
  10671. });
  10672. var time = ['H', 'k', 'I', 'l', 'M', 'S'].some(function (char) {
  10673. return format.indexOf('%' + char) !== -1;
  10674. });
  10675. if (date && time) {
  10676. return 'datetime-local';
  10677. }
  10678. if (date) {
  10679. return 'date';
  10680. }
  10681. if (time) {
  10682. return 'time';
  10683. }
  10684. return 'text';
  10685. }
  10686. /**
  10687. * Get the axis min value based on the range option and the current max. For
  10688. * stock charts this is extended via the {@link RangeSelector} so that if the
  10689. * selected range is a multiple of months or years, it is compensated for
  10690. * various month lengths.
  10691. *
  10692. * @private
  10693. * @function Highcharts.Axis#minFromRange
  10694. * @return {number|undefined}
  10695. * The new minimum value.
  10696. */
  10697. Axis.prototype.minFromRange = function () {
  10698. var rangeOptions = this.range,
  10699. type = rangeOptions.type,
  10700. min,
  10701. max = this.max,
  10702. dataMin,
  10703. range,
  10704. time = this.chart.time,
  10705. // Get the true range from a start date
  10706. getTrueRange = function (base,
  10707. count) {
  10708. var timeName = type === 'year' ? 'FullYear' : 'Month';
  10709. var date = new time.Date(base);
  10710. var basePeriod = time.get(timeName,
  10711. date);
  10712. time.set(timeName, date, basePeriod + count);
  10713. if (basePeriod === time.get(timeName, date)) {
  10714. time.set('Date', date, 0); // #6537
  10715. }
  10716. return date.getTime() - base;
  10717. };
  10718. if (isNumber(rangeOptions)) {
  10719. min = max - rangeOptions;
  10720. range = rangeOptions;
  10721. }
  10722. else {
  10723. min = max + getTrueRange(max, -rangeOptions.count);
  10724. // Let the fixedRange reflect initial settings (#5930)
  10725. if (this.chart) {
  10726. this.chart.fixedRange = max - min;
  10727. }
  10728. }
  10729. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  10730. if (!isNumber(min)) {
  10731. min = dataMin;
  10732. }
  10733. if (min <= dataMin) {
  10734. min = dataMin;
  10735. if (typeof range === 'undefined') { // #4501
  10736. range = getTrueRange(min, rangeOptions.count);
  10737. }
  10738. this.newMax = Math.min(min + range, this.dataMax);
  10739. }
  10740. if (!isNumber(max)) {
  10741. min = void 0;
  10742. }
  10743. return min;
  10744. };
  10745. if (!H.RangeSelector) {
  10746. var chartDestroyEvents_1 = [];
  10747. var initRangeSelector_1 = function (chart) {
  10748. var extremes,
  10749. rangeSelector = chart.rangeSelector,
  10750. legend,
  10751. alignTo,
  10752. verticalAlign;
  10753. /**
  10754. * @private
  10755. */
  10756. function render() {
  10757. if (rangeSelector) {
  10758. extremes = chart.xAxis[0].getExtremes();
  10759. legend = chart.legend;
  10760. verticalAlign = rangeSelector === null || rangeSelector === void 0 ? void 0 : rangeSelector.options.verticalAlign;
  10761. if (isNumber(extremes.min)) {
  10762. rangeSelector.render(extremes.min, extremes.max);
  10763. }
  10764. // Re-align the legend so that it's below the rangeselector
  10765. if (legend.display &&
  10766. verticalAlign === 'top' &&
  10767. verticalAlign === legend.options.verticalAlign) {
  10768. // Create a new alignment box for the legend.
  10769. alignTo = merge(chart.spacingBox);
  10770. if (legend.options.layout === 'vertical') {
  10771. alignTo.y = chart.plotTop;
  10772. }
  10773. else {
  10774. alignTo.y += rangeSelector.getHeight();
  10775. }
  10776. legend.group.placed = false; // Don't animate the alignment.
  10777. legend.align(alignTo);
  10778. }
  10779. }
  10780. }
  10781. if (rangeSelector) {
  10782. var events = find(chartDestroyEvents_1,
  10783. function (e) { return e[0] === chart; });
  10784. if (!events) {
  10785. chartDestroyEvents_1.push([chart, [
  10786. // redraw the scroller on setExtremes
  10787. addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  10788. if (rangeSelector) {
  10789. rangeSelector.render(e.min, e.max);
  10790. }
  10791. }),
  10792. // redraw the scroller chart resize
  10793. addEvent(chart, 'redraw', render)
  10794. ]]);
  10795. }
  10796. // do it now
  10797. render();
  10798. }
  10799. };
  10800. // Initialize rangeselector for stock charts
  10801. addEvent(Chart, 'afterGetContainer', function () {
  10802. var _a;
  10803. if ((_a = this.options.rangeSelector) === null || _a === void 0 ? void 0 : _a.enabled) {
  10804. this.rangeSelector = new RangeSelector(this);
  10805. }
  10806. });
  10807. addEvent(Chart, 'beforeRender', function () {
  10808. var chart = this,
  10809. axes = chart.axes,
  10810. rangeSelector = chart.rangeSelector,
  10811. verticalAlign;
  10812. if (rangeSelector) {
  10813. if (isNumber(rangeSelector.deferredYTDClick)) {
  10814. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  10815. delete rangeSelector.deferredYTDClick;
  10816. }
  10817. axes.forEach(function (axis) {
  10818. axis.updateNames();
  10819. axis.setScale();
  10820. });
  10821. chart.getAxisMargins();
  10822. rangeSelector.render();
  10823. verticalAlign = rangeSelector.options.verticalAlign;
  10824. if (!rangeSelector.options.floating) {
  10825. if (verticalAlign === 'bottom') {
  10826. this.extraBottomMargin = true;
  10827. }
  10828. else if (verticalAlign !== 'middle') {
  10829. this.extraTopMargin = true;
  10830. }
  10831. }
  10832. }
  10833. });
  10834. addEvent(Chart, 'update', function (e) {
  10835. var chart = this,
  10836. options = e.options,
  10837. optionsRangeSelector = options.rangeSelector,
  10838. rangeSelector = chart.rangeSelector,
  10839. verticalAlign,
  10840. extraBottomMarginWas = this.extraBottomMargin,
  10841. extraTopMarginWas = this.extraTopMargin;
  10842. if (optionsRangeSelector &&
  10843. optionsRangeSelector.enabled &&
  10844. !defined(rangeSelector) &&
  10845. this.options.rangeSelector) {
  10846. this.options.rangeSelector.enabled = true;
  10847. this.rangeSelector = rangeSelector = new RangeSelector(this);
  10848. }
  10849. this.extraBottomMargin = false;
  10850. this.extraTopMargin = false;
  10851. if (rangeSelector) {
  10852. initRangeSelector_1(this);
  10853. verticalAlign = (optionsRangeSelector &&
  10854. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  10855. if (!rangeSelector.options.floating) {
  10856. if (verticalAlign === 'bottom') {
  10857. this.extraBottomMargin = true;
  10858. }
  10859. else if (verticalAlign !== 'middle') {
  10860. this.extraTopMargin = true;
  10861. }
  10862. }
  10863. if (this.extraBottomMargin !== extraBottomMarginWas ||
  10864. this.extraTopMargin !== extraTopMarginWas) {
  10865. this.isDirtyBox = true;
  10866. }
  10867. }
  10868. });
  10869. addEvent(Chart, 'render', function () {
  10870. var chart = this,
  10871. rangeSelector = chart.rangeSelector,
  10872. verticalAlign;
  10873. if (rangeSelector && !rangeSelector.options.floating) {
  10874. rangeSelector.render();
  10875. verticalAlign = rangeSelector.options.verticalAlign;
  10876. if (verticalAlign === 'bottom') {
  10877. this.extraBottomMargin = true;
  10878. }
  10879. else if (verticalAlign !== 'middle') {
  10880. this.extraTopMargin = true;
  10881. }
  10882. }
  10883. });
  10884. addEvent(Chart, 'getMargins', function () {
  10885. var rangeSelector = this.rangeSelector,
  10886. rangeSelectorHeight;
  10887. if (rangeSelector) {
  10888. rangeSelectorHeight = rangeSelector.getHeight();
  10889. if (this.extraTopMargin) {
  10890. this.plotTop += rangeSelectorHeight;
  10891. }
  10892. if (this.extraBottomMargin) {
  10893. this.marginBottom += rangeSelectorHeight;
  10894. }
  10895. }
  10896. });
  10897. Chart.prototype.callbacks.push(initRangeSelector_1);
  10898. // Remove resize/afterSetExtremes at chart destroy
  10899. addEvent(Chart, 'destroy', function destroyEvents() {
  10900. for (var i = 0; i < chartDestroyEvents_1.length; i++) {
  10901. var events = chartDestroyEvents_1[i];
  10902. if (events[0] === this) {
  10903. events[1].forEach(function (unbind) { return unbind(); });
  10904. chartDestroyEvents_1.splice(i, 1);
  10905. return;
  10906. }
  10907. }
  10908. });
  10909. H.RangeSelector = RangeSelector;
  10910. }
  10911. return H.RangeSelector;
  10912. });
  10913. _registerModule(_modules, 'Core/Axis/NavigatorAxis.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  10914. /* *
  10915. *
  10916. * (c) 2010-2021 Torstein Honsi
  10917. *
  10918. * License: www.highcharts.com/license
  10919. *
  10920. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10921. *
  10922. * */
  10923. var isTouchDevice = H.isTouchDevice;
  10924. var addEvent = U.addEvent,
  10925. correctFloat = U.correctFloat,
  10926. defined = U.defined,
  10927. isNumber = U.isNumber,
  10928. pick = U.pick;
  10929. /* eslint-disable valid-jsdoc */
  10930. /**
  10931. * @private
  10932. * @class
  10933. */
  10934. var NavigatorAxisAdditions = /** @class */ (function () {
  10935. /* *
  10936. *
  10937. * Constructors
  10938. *
  10939. * */
  10940. function NavigatorAxisAdditions(axis) {
  10941. this.axis = axis;
  10942. }
  10943. /* *
  10944. *
  10945. * Functions
  10946. *
  10947. * */
  10948. /**
  10949. * @private
  10950. */
  10951. NavigatorAxisAdditions.prototype.destroy = function () {
  10952. this.axis = void 0;
  10953. };
  10954. /**
  10955. * Add logic to normalize the zoomed range in order to preserve the pressed
  10956. * state of range selector buttons
  10957. *
  10958. * @private
  10959. * @function Highcharts.Axis#toFixedRange
  10960. * @param {number} [pxMin]
  10961. * @param {number} [pxMax]
  10962. * @param {number} [fixedMin]
  10963. * @param {number} [fixedMax]
  10964. * @return {*}
  10965. */
  10966. NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  10967. var navigator = this;
  10968. var axis = navigator.axis;
  10969. var chart = axis.chart;
  10970. var fixedRange = chart && chart.fixedRange,
  10971. halfPointRange = (axis.pointRange || 0) / 2,
  10972. newMin = pick(fixedMin,
  10973. axis.translate(pxMin,
  10974. true, !axis.horiz)),
  10975. newMax = pick(fixedMax,
  10976. axis.translate(pxMax,
  10977. true, !axis.horiz)),
  10978. changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  10979. // Add/remove half point range to/from the extremes (#1172)
  10980. if (!defined(fixedMin)) {
  10981. newMin = correctFloat(newMin + halfPointRange);
  10982. }
  10983. if (!defined(fixedMax)) {
  10984. newMax = correctFloat(newMax - halfPointRange);
  10985. }
  10986. // If the difference between the fixed range and the actual requested
  10987. // range is too great, the user is dragging across an ordinal gap, and
  10988. // we need to release the range selector button.
  10989. if (changeRatio > 0.7 && changeRatio < 1.3) {
  10990. if (fixedMax) {
  10991. newMin = newMax - fixedRange;
  10992. }
  10993. else {
  10994. newMax = newMin + fixedRange;
  10995. }
  10996. }
  10997. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  10998. newMin = newMax = void 0;
  10999. }
  11000. return {
  11001. min: newMin,
  11002. max: newMax
  11003. };
  11004. };
  11005. return NavigatorAxisAdditions;
  11006. }());
  11007. /**
  11008. * @private
  11009. * @class
  11010. */
  11011. var NavigatorAxis = /** @class */ (function () {
  11012. function NavigatorAxis() {
  11013. }
  11014. /* *
  11015. *
  11016. * Static Functions
  11017. *
  11018. * */
  11019. /**
  11020. * @private
  11021. */
  11022. NavigatorAxis.compose = function (AxisClass) {
  11023. AxisClass.keepProps.push('navigatorAxis');
  11024. /* eslint-disable no-invalid-this */
  11025. addEvent(AxisClass, 'init', function () {
  11026. var axis = this;
  11027. if (!axis.navigatorAxis) {
  11028. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  11029. }
  11030. });
  11031. // For Stock charts, override selection zooming with some special
  11032. // features because X axis zooming is already allowed by the Navigator
  11033. // and Range selector.
  11034. addEvent(AxisClass, 'zoom', function (e) {
  11035. var axis = this;
  11036. var chart = axis.chart;
  11037. var chartOptions = chart.options;
  11038. var navigator = chartOptions.navigator;
  11039. var navigatorAxis = axis.navigatorAxis;
  11040. var pinchType = chartOptions.chart.pinchType;
  11041. var rangeSelector = chartOptions.rangeSelector;
  11042. var zoomType = chartOptions.chart.zoomType;
  11043. var previousZoom;
  11044. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  11045. (rangeSelector && rangeSelector.enabled))) {
  11046. // For y only zooming, ignore the X axis completely
  11047. if (zoomType === 'y') {
  11048. e.zoomed = false;
  11049. // For xy zooming, record the state of the zoom before zoom
  11050. // selection, then when the reset button is pressed, revert to
  11051. // this state. This should apply only if the chart is
  11052. // initialized with a range (#6612), otherwise zoom all the way
  11053. // out.
  11054. }
  11055. else if (((!isTouchDevice && zoomType === 'xy') ||
  11056. (isTouchDevice && pinchType === 'xy')) &&
  11057. axis.options.range) {
  11058. previousZoom = navigatorAxis.previousZoom;
  11059. if (defined(e.newMin)) {
  11060. navigatorAxis.previousZoom = [axis.min, axis.max];
  11061. }
  11062. else if (previousZoom) {
  11063. e.newMin = previousZoom[0];
  11064. e.newMax = previousZoom[1];
  11065. navigatorAxis.previousZoom = void 0;
  11066. }
  11067. }
  11068. }
  11069. if (typeof e.zoomed !== 'undefined') {
  11070. e.preventDefault();
  11071. }
  11072. });
  11073. /* eslint-enable no-invalid-this */
  11074. };
  11075. /* *
  11076. *
  11077. * Static Properties
  11078. *
  11079. * */
  11080. /**
  11081. * @private
  11082. */
  11083. NavigatorAxis.AdditionsClass = NavigatorAxisAdditions;
  11084. return NavigatorAxis;
  11085. }());
  11086. return NavigatorAxis;
  11087. });
  11088. _registerModule(_modules, 'Core/Navigator.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxis.js'], _modules['Core/Options.js'], _modules['Core/Color/Palette.js'], _modules['Core/Scrollbar.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Axis, Chart, Color, H, NavigatorAxis, O, palette, Scrollbar, Series, SeriesRegistry, U) {
  11089. /* *
  11090. *
  11091. * (c) 2010-2021 Torstein Honsi
  11092. *
  11093. * License: www.highcharts.com/license
  11094. *
  11095. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11096. *
  11097. * */
  11098. var color = Color.parse;
  11099. var hasTouch = H.hasTouch,
  11100. isTouchDevice = H.isTouchDevice;
  11101. var defaultOptions = O.defaultOptions;
  11102. var seriesTypes = SeriesRegistry.seriesTypes;
  11103. var addEvent = U.addEvent,
  11104. clamp = U.clamp,
  11105. correctFloat = U.correctFloat,
  11106. defined = U.defined,
  11107. destroyObjectProperties = U.destroyObjectProperties,
  11108. erase = U.erase,
  11109. extend = U.extend,
  11110. find = U.find,
  11111. isArray = U.isArray,
  11112. isNumber = U.isNumber,
  11113. merge = U.merge,
  11114. pick = U.pick,
  11115. removeEvent = U.removeEvent,
  11116. splat = U.splat;
  11117. var defaultSeriesType,
  11118. // Finding the min or max of a set of variables where we don't know if they
  11119. // are defined, is a pattern that is repeated several places in Highcharts.
  11120. // Consider making this a global utility method.
  11121. numExt = function (extreme) {
  11122. var args = [];
  11123. for (var _i = 1; _i < arguments.length; _i++) {
  11124. args[_i - 1] = arguments[_i];
  11125. }
  11126. var numbers = [].filter.call(args,
  11127. isNumber);
  11128. if (numbers.length) {
  11129. return Math[extreme].apply(0, numbers);
  11130. }
  11131. };
  11132. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  11133. 'line' :
  11134. 'areaspline';
  11135. extend(defaultOptions, {
  11136. /**
  11137. * Maximum range which can be set using the navigator's handles.
  11138. * Opposite of [xAxis.minRange](#xAxis.minRange).
  11139. *
  11140. * @sample {highstock} stock/navigator/maxrange/
  11141. * Defined max and min range
  11142. *
  11143. * @type {number}
  11144. * @since 6.0.0
  11145. * @product highstock gantt
  11146. * @apioption xAxis.maxRange
  11147. */
  11148. /**
  11149. * The navigator is a small series below the main series, displaying
  11150. * a view of the entire data set. It provides tools to zoom in and
  11151. * out on parts of the data as well as panning across the dataset.
  11152. *
  11153. * @product highstock gantt
  11154. * @optionparent navigator
  11155. */
  11156. navigator: {
  11157. /**
  11158. * Whether the navigator and scrollbar should adapt to updated data
  11159. * in the base X axis. When loading data async, as in the demo below,
  11160. * this should be `false`. Otherwise new data will trigger navigator
  11161. * redraw, which will cause unwanted looping. In the demo below, the
  11162. * data in the navigator is set only once. On navigating, only the main
  11163. * chart content is updated.
  11164. *
  11165. * @sample {highstock} stock/demo/lazy-loading/
  11166. * Set to false with async data loading
  11167. *
  11168. * @type {boolean}
  11169. * @default true
  11170. * @apioption navigator.adaptToUpdatedData
  11171. */
  11172. /**
  11173. * An integer identifying the index to use for the base series, or a
  11174. * string representing the id of the series.
  11175. *
  11176. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  11177. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  11178. *
  11179. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  11180. *
  11181. * @deprecated
  11182. * @type {number|string}
  11183. * @default 0
  11184. * @apioption navigator.baseSeries
  11185. */
  11186. /**
  11187. * Enable or disable the navigator.
  11188. *
  11189. * @sample {highstock} stock/navigator/enabled/
  11190. * Disable the navigator
  11191. *
  11192. * @type {boolean}
  11193. * @default true
  11194. * @apioption navigator.enabled
  11195. */
  11196. /**
  11197. * When the chart is inverted, whether to draw the navigator on the
  11198. * opposite side.
  11199. *
  11200. * @type {boolean}
  11201. * @default false
  11202. * @since 5.0.8
  11203. * @apioption navigator.opposite
  11204. */
  11205. /**
  11206. * The height of the navigator.
  11207. *
  11208. * @sample {highstock} stock/navigator/height/
  11209. * A higher navigator
  11210. */
  11211. height: 40,
  11212. /**
  11213. * The distance from the nearest element, the X axis or X axis labels.
  11214. *
  11215. * @sample {highstock} stock/navigator/margin/
  11216. * A margin of 2 draws the navigator closer to the X axis labels
  11217. */
  11218. margin: 25,
  11219. /**
  11220. * Whether the mask should be inside the range marking the zoomed
  11221. * range, or outside. In Highstock 1.x it was always `false`.
  11222. *
  11223. * @sample {highstock} stock/navigator/maskinside-false/
  11224. * False, mask outside
  11225. *
  11226. * @since 2.0
  11227. */
  11228. maskInside: true,
  11229. /**
  11230. * Options for the handles for dragging the zoomed area.
  11231. *
  11232. * @sample {highstock} stock/navigator/handles/
  11233. * Colored handles
  11234. */
  11235. handles: {
  11236. /**
  11237. * Width for handles.
  11238. *
  11239. * @sample {highstock} stock/navigator/styled-handles/
  11240. * Styled handles
  11241. *
  11242. * @since 6.0.0
  11243. */
  11244. width: 7,
  11245. /**
  11246. * Height for handles.
  11247. *
  11248. * @sample {highstock} stock/navigator/styled-handles/
  11249. * Styled handles
  11250. *
  11251. * @since 6.0.0
  11252. */
  11253. height: 15,
  11254. /**
  11255. * Array to define shapes of handles. 0-index for left, 1-index for
  11256. * right.
  11257. *
  11258. * Additionally, the URL to a graphic can be given on this form:
  11259. * `url(graphic.png)`. Note that for the image to be applied to
  11260. * exported charts, its URL needs to be accessible by the export
  11261. * server.
  11262. *
  11263. * Custom callbacks for symbol path generation can also be added to
  11264. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  11265. * used by its method name, as shown in the demo.
  11266. *
  11267. * @sample {highstock} stock/navigator/styled-handles/
  11268. * Styled handles
  11269. *
  11270. * @type {Array<string>}
  11271. * @default ["navigator-handle", "navigator-handle"]
  11272. * @since 6.0.0
  11273. */
  11274. symbols: ['navigator-handle', 'navigator-handle'],
  11275. /**
  11276. * Allows to enable/disable handles.
  11277. *
  11278. * @since 6.0.0
  11279. */
  11280. enabled: true,
  11281. /**
  11282. * The width for the handle border and the stripes inside.
  11283. *
  11284. * @sample {highstock} stock/navigator/styled-handles/
  11285. * Styled handles
  11286. *
  11287. * @since 6.0.0
  11288. * @apioption navigator.handles.lineWidth
  11289. */
  11290. lineWidth: 1,
  11291. /**
  11292. * The fill for the handle.
  11293. *
  11294. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11295. */
  11296. backgroundColor: palette.neutralColor5,
  11297. /**
  11298. * The stroke for the handle border and the stripes inside.
  11299. *
  11300. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11301. */
  11302. borderColor: palette.neutralColor40
  11303. },
  11304. /**
  11305. * The color of the mask covering the areas of the navigator series
  11306. * that are currently not visible in the main series. The default
  11307. * color is bluish with an opacity of 0.3 to see the series below.
  11308. *
  11309. * @see In styled mode, the mask is styled with the
  11310. * `.highcharts-navigator-mask` and
  11311. * `.highcharts-navigator-mask-inside` classes.
  11312. *
  11313. * @sample {highstock} stock/navigator/maskfill/
  11314. * Blue, semi transparent mask
  11315. *
  11316. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11317. * @default rgba(102,133,194,0.3)
  11318. */
  11319. maskFill: color(palette.highlightColor60).setOpacity(0.3).get(),
  11320. /**
  11321. * The color of the line marking the currently zoomed area in the
  11322. * navigator.
  11323. *
  11324. * @sample {highstock} stock/navigator/outline/
  11325. * 2px blue outline
  11326. *
  11327. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11328. * @default #cccccc
  11329. */
  11330. outlineColor: palette.neutralColor20,
  11331. /**
  11332. * The width of the line marking the currently zoomed area in the
  11333. * navigator.
  11334. *
  11335. * @see In styled mode, the outline stroke width is set with the
  11336. * `.highcharts-navigator-outline` class.
  11337. *
  11338. * @sample {highstock} stock/navigator/outline/
  11339. * 2px blue outline
  11340. *
  11341. * @type {number}
  11342. */
  11343. outlineWidth: 1,
  11344. /**
  11345. * Options for the navigator series. Available options are the same
  11346. * as any series, documented at [plotOptions](#plotOptions.series)
  11347. * and [series](#series).
  11348. *
  11349. * Unless data is explicitly defined on navigator.series, the data
  11350. * is borrowed from the first series in the chart.
  11351. *
  11352. * Default series options for the navigator series are:
  11353. * ```js
  11354. * series: {
  11355. * type: 'areaspline',
  11356. * fillOpacity: 0.05,
  11357. * dataGrouping: {
  11358. * smoothed: true
  11359. * },
  11360. * lineWidth: 1,
  11361. * marker: {
  11362. * enabled: false
  11363. * }
  11364. * }
  11365. * ```
  11366. *
  11367. * @see In styled mode, the navigator series is styled with the
  11368. * `.highcharts-navigator-series` class.
  11369. *
  11370. * @sample {highstock} stock/navigator/series-data/
  11371. * Using a separate data set for the navigator
  11372. * @sample {highstock} stock/navigator/series/
  11373. * A green navigator series
  11374. *
  11375. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  11376. */
  11377. series: {
  11378. /**
  11379. * The type of the navigator series.
  11380. *
  11381. * Heads up:
  11382. * In column-type navigator, zooming is limited to at least one
  11383. * point with its `pointRange`.
  11384. *
  11385. * @sample {highstock} stock/navigator/column/
  11386. * Column type navigator
  11387. *
  11388. * @type {string}
  11389. * @default {highstock} `areaspline` if defined, otherwise `line`
  11390. * @default {gantt} gantt
  11391. */
  11392. type: defaultSeriesType,
  11393. /**
  11394. * The fill opacity of the navigator series.
  11395. */
  11396. fillOpacity: 0.05,
  11397. /**
  11398. * The pixel line width of the navigator series.
  11399. */
  11400. lineWidth: 1,
  11401. /**
  11402. * @ignore-option
  11403. */
  11404. compare: null,
  11405. /**
  11406. * Unless data is explicitly defined, the data is borrowed from the
  11407. * first series in the chart.
  11408. *
  11409. * @type {Array<number|Array<number|string|null>|object|null>}
  11410. * @product highstock
  11411. * @apioption navigator.series.data
  11412. */
  11413. /**
  11414. * Data grouping options for the navigator series.
  11415. *
  11416. * @extends plotOptions.series.dataGrouping
  11417. */
  11418. dataGrouping: {
  11419. approximation: 'average',
  11420. enabled: true,
  11421. groupPixelWidth: 2,
  11422. smoothed: true,
  11423. // Day and week differs from plotOptions.series.dataGrouping
  11424. units: [
  11425. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  11426. ['second', [1, 2, 5, 10, 15, 30]],
  11427. ['minute', [1, 2, 5, 10, 15, 30]],
  11428. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  11429. ['day', [1, 2, 3, 4]],
  11430. ['week', [1, 2, 3]],
  11431. ['month', [1, 3, 6]],
  11432. ['year', null]
  11433. ]
  11434. },
  11435. /**
  11436. * Data label options for the navigator series. Data labels are
  11437. * disabled by default on the navigator series.
  11438. *
  11439. * @extends plotOptions.series.dataLabels
  11440. */
  11441. dataLabels: {
  11442. enabled: false,
  11443. zIndex: 2 // #1839
  11444. },
  11445. id: 'highcharts-navigator-series',
  11446. className: 'highcharts-navigator-series',
  11447. /**
  11448. * Sets the fill color of the navigator series.
  11449. *
  11450. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11451. * @apioption navigator.series.color
  11452. */
  11453. /**
  11454. * Line color for the navigator series. Allows setting the color
  11455. * while disallowing the default candlestick setting.
  11456. *
  11457. * @type {Highcharts.ColorString|null}
  11458. */
  11459. lineColor: null,
  11460. marker: {
  11461. enabled: false
  11462. },
  11463. /**
  11464. * Since Highstock v8, default value is the same as default
  11465. * `pointRange` defined for a specific type (e.g. `null` for
  11466. * column type).
  11467. *
  11468. * In Highstock version < 8, defaults to 0.
  11469. *
  11470. * @extends plotOptions.series.pointRange
  11471. * @type {number|null}
  11472. * @apioption navigator.series.pointRange
  11473. */
  11474. /**
  11475. * The threshold option. Setting it to 0 will make the default
  11476. * navigator area series draw its area from the 0 value and up.
  11477. *
  11478. * @type {number|null}
  11479. */
  11480. threshold: null
  11481. },
  11482. /**
  11483. * Options for the navigator X axis. Default series options for the
  11484. * navigator xAxis are:
  11485. * ```js
  11486. * xAxis: {
  11487. * tickWidth: 0,
  11488. * lineWidth: 0,
  11489. * gridLineWidth: 1,
  11490. * tickPixelInterval: 200,
  11491. * labels: {
  11492. * align: 'left',
  11493. * style: {
  11494. * color: '#888'
  11495. * },
  11496. * x: 3,
  11497. * y: -4
  11498. * }
  11499. * }
  11500. * ```
  11501. *
  11502. * @extends xAxis
  11503. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  11504. * showEmpty, maxRange
  11505. */
  11506. xAxis: {
  11507. /**
  11508. * Additional range on the right side of the xAxis. Works similar to
  11509. * xAxis.maxPadding, but value is set in milliseconds.
  11510. * Can be set for both, main xAxis and navigator's xAxis.
  11511. *
  11512. * @since 6.0.0
  11513. */
  11514. overscroll: 0,
  11515. className: 'highcharts-navigator-xaxis',
  11516. tickLength: 0,
  11517. lineWidth: 0,
  11518. gridLineColor: palette.neutralColor10,
  11519. gridLineWidth: 1,
  11520. tickPixelInterval: 200,
  11521. labels: {
  11522. align: 'left',
  11523. /**
  11524. * @type {Highcharts.CSSObject}
  11525. */
  11526. style: {
  11527. /** @ignore */
  11528. color: palette.neutralColor40
  11529. },
  11530. x: 3,
  11531. y: -4
  11532. },
  11533. crosshair: false
  11534. },
  11535. /**
  11536. * Options for the navigator Y axis. Default series options for the
  11537. * navigator yAxis are:
  11538. * ```js
  11539. * yAxis: {
  11540. * gridLineWidth: 0,
  11541. * startOnTick: false,
  11542. * endOnTick: false,
  11543. * minPadding: 0.1,
  11544. * maxPadding: 0.1,
  11545. * labels: {
  11546. * enabled: false
  11547. * },
  11548. * title: {
  11549. * text: null
  11550. * },
  11551. * tickWidth: 0
  11552. * }
  11553. * ```
  11554. *
  11555. * @extends yAxis
  11556. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  11557. * showEmpty, scrollbar, top, units, maxRange, minLength,
  11558. * maxLength, resize
  11559. */
  11560. yAxis: {
  11561. className: 'highcharts-navigator-yaxis',
  11562. gridLineWidth: 0,
  11563. startOnTick: false,
  11564. endOnTick: false,
  11565. minPadding: 0.1,
  11566. maxPadding: 0.1,
  11567. labels: {
  11568. enabled: false
  11569. },
  11570. crosshair: false,
  11571. title: {
  11572. text: null
  11573. },
  11574. tickLength: 0,
  11575. tickWidth: 0
  11576. }
  11577. }
  11578. });
  11579. /* eslint-disable no-invalid-this, valid-jsdoc */
  11580. /**
  11581. * Draw one of the handles on the side of the zoomed range in the navigator
  11582. *
  11583. * @private
  11584. * @function Highcharts.Renderer#symbols.navigator-handle
  11585. * @param {number} x
  11586. * @param {number} y
  11587. * @param {number} w
  11588. * @param {number} h
  11589. * @param {Highcharts.NavigatorHandlesOptions} options
  11590. * @return {Highcharts.SVGPathArray}
  11591. * Path to be used in a handle
  11592. */
  11593. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  11594. var halfWidth = (options && options.width || 0) / 2,
  11595. markerPosition = Math.round(halfWidth / 3) + 0.5,
  11596. height = options && options.height || 0;
  11597. return [
  11598. ['M', -halfWidth - 1, 0.5],
  11599. ['L', halfWidth, 0.5],
  11600. ['L', halfWidth, height + 0.5],
  11601. ['L', -halfWidth - 1, height + 0.5],
  11602. ['L', -halfWidth - 1, 0.5],
  11603. ['M', -markerPosition, 4],
  11604. ['L', -markerPosition, height - 3],
  11605. ['M', markerPosition - 1, 4],
  11606. ['L', markerPosition - 1, height - 3]
  11607. ];
  11608. };
  11609. /**
  11610. * The Navigator class
  11611. *
  11612. * @private
  11613. * @class
  11614. * @name Highcharts.Navigator
  11615. *
  11616. * @param {Highcharts.Chart} chart
  11617. * Chart object
  11618. */
  11619. var Navigator = /** @class */ (function () {
  11620. function Navigator(chart) {
  11621. this.baseSeries = void 0;
  11622. this.chart = void 0;
  11623. this.handles = void 0;
  11624. this.height = void 0;
  11625. this.left = void 0;
  11626. this.navigatorEnabled = void 0;
  11627. this.navigatorGroup = void 0;
  11628. this.navigatorOptions = void 0;
  11629. this.navigatorSeries = void 0;
  11630. this.navigatorSize = void 0;
  11631. this.opposite = void 0;
  11632. this.outline = void 0;
  11633. this.outlineHeight = void 0;
  11634. this.range = void 0;
  11635. this.rendered = void 0;
  11636. this.shades = void 0;
  11637. this.size = void 0;
  11638. this.top = void 0;
  11639. this.xAxis = void 0;
  11640. this.yAxis = void 0;
  11641. this.zoomedMax = void 0;
  11642. this.zoomedMin = void 0;
  11643. this.init(chart);
  11644. }
  11645. /**
  11646. * Draw one of the handles on the side of the zoomed range in the navigator
  11647. *
  11648. * @private
  11649. * @function Highcharts.Navigator#drawHandle
  11650. *
  11651. * @param {number} x
  11652. * The x center for the handle
  11653. *
  11654. * @param {number} index
  11655. * 0 for left and 1 for right
  11656. *
  11657. * @param {boolean|undefined} inverted
  11658. * flag for chart.inverted
  11659. *
  11660. * @param {string} verb
  11661. * use 'animate' or 'attr'
  11662. */
  11663. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  11664. var navigator = this,
  11665. height = navigator.navigatorOptions.handles.height;
  11666. // Place it
  11667. navigator.handles[index][verb](inverted ? {
  11668. translateX: Math.round(navigator.left + navigator.height / 2),
  11669. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  11670. } : {
  11671. translateX: Math.round(navigator.left + parseInt(x, 10)),
  11672. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  11673. });
  11674. };
  11675. /**
  11676. * Render outline around the zoomed range
  11677. *
  11678. * @private
  11679. * @function Highcharts.Navigator#drawOutline
  11680. *
  11681. * @param {number} zoomedMin
  11682. * in pixels position where zoomed range starts
  11683. *
  11684. * @param {number} zoomedMax
  11685. * in pixels position where zoomed range ends
  11686. *
  11687. * @param {boolean|undefined} inverted
  11688. * flag if chart is inverted
  11689. *
  11690. * @param {string} verb
  11691. * use 'animate' or 'attr'
  11692. */
  11693. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  11694. var navigator = this,
  11695. maskInside = navigator.navigatorOptions.maskInside,
  11696. outlineWidth = navigator.outline.strokeWidth(),
  11697. halfOutline = outlineWidth / 2,
  11698. outlineCorrection = (outlineWidth % 2) / 2, // #5800
  11699. outlineHeight = navigator.outlineHeight,
  11700. scrollbarHeight = navigator.scrollbarHeight || 0,
  11701. navigatorSize = navigator.size,
  11702. left = navigator.left - scrollbarHeight,
  11703. navigatorTop = navigator.top,
  11704. verticalMin,
  11705. path;
  11706. if (inverted) {
  11707. left -= halfOutline;
  11708. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  11709. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  11710. path = [
  11711. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  11712. ['L', left + outlineHeight, verticalMin],
  11713. ['L', left, verticalMin],
  11714. ['L', left, zoomedMax],
  11715. ['L', left + outlineHeight, zoomedMax],
  11716. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  11717. ];
  11718. if (maskInside) {
  11719. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  11720. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  11721. );
  11722. }
  11723. }
  11724. else {
  11725. zoomedMin += left + scrollbarHeight - outlineCorrection;
  11726. zoomedMax += left + scrollbarHeight - outlineCorrection;
  11727. navigatorTop += halfOutline;
  11728. path = [
  11729. ['M', left, navigatorTop],
  11730. ['L', zoomedMin, navigatorTop],
  11731. ['L', zoomedMin, navigatorTop + outlineHeight],
  11732. ['L', zoomedMax, navigatorTop + outlineHeight],
  11733. ['L', zoomedMax, navigatorTop],
  11734. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  11735. ];
  11736. if (maskInside) {
  11737. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  11738. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  11739. );
  11740. }
  11741. }
  11742. navigator.outline[verb]({
  11743. d: path
  11744. });
  11745. };
  11746. /**
  11747. * Render outline around the zoomed range
  11748. *
  11749. * @private
  11750. * @function Highcharts.Navigator#drawMasks
  11751. *
  11752. * @param {number} zoomedMin
  11753. * in pixels position where zoomed range starts
  11754. *
  11755. * @param {number} zoomedMax
  11756. * in pixels position where zoomed range ends
  11757. *
  11758. * @param {boolean|undefined} inverted
  11759. * flag if chart is inverted
  11760. *
  11761. * @param {string} verb
  11762. * use 'animate' or 'attr'
  11763. */
  11764. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  11765. var navigator = this,
  11766. left = navigator.left,
  11767. top = navigator.top,
  11768. navigatorHeight = navigator.height,
  11769. height,
  11770. width,
  11771. x,
  11772. y;
  11773. // Determine rectangle position & size
  11774. // According to (non)inverted position:
  11775. if (inverted) {
  11776. x = [left, left, left];
  11777. y = [top, top + zoomedMin, top + zoomedMax];
  11778. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  11779. height = [
  11780. zoomedMin,
  11781. zoomedMax - zoomedMin,
  11782. navigator.size - zoomedMax
  11783. ];
  11784. }
  11785. else {
  11786. x = [left, left + zoomedMin, left + zoomedMax];
  11787. y = [top, top, top];
  11788. width = [
  11789. zoomedMin,
  11790. zoomedMax - zoomedMin,
  11791. navigator.size - zoomedMax
  11792. ];
  11793. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  11794. }
  11795. navigator.shades.forEach(function (shade, i) {
  11796. shade[verb]({
  11797. x: x[i],
  11798. y: y[i],
  11799. width: width[i],
  11800. height: height[i]
  11801. });
  11802. });
  11803. };
  11804. /**
  11805. * Generate DOM elements for a navigator:
  11806. *
  11807. * - main navigator group
  11808. *
  11809. * - all shades
  11810. *
  11811. * - outline
  11812. *
  11813. * - handles
  11814. *
  11815. * @private
  11816. * @function Highcharts.Navigator#renderElements
  11817. */
  11818. Navigator.prototype.renderElements = function () {
  11819. var navigator = this,
  11820. navigatorOptions = navigator.navigatorOptions,
  11821. maskInside = navigatorOptions.maskInside,
  11822. chart = navigator.chart,
  11823. inverted = chart.inverted,
  11824. renderer = chart.renderer,
  11825. navigatorGroup,
  11826. mouseCursor = {
  11827. cursor: inverted ? 'ns-resize' : 'ew-resize'
  11828. };
  11829. // Create the main navigator group
  11830. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  11831. .attr({
  11832. zIndex: 8,
  11833. visibility: 'hidden'
  11834. })
  11835. .add();
  11836. // Create masks, each mask will get events and fill:
  11837. [
  11838. !maskInside,
  11839. maskInside,
  11840. !maskInside
  11841. ].forEach(function (hasMask, index) {
  11842. navigator.shades[index] = renderer.rect()
  11843. .addClass('highcharts-navigator-mask' +
  11844. (index === 1 ? '-inside' : '-outside'))
  11845. .add(navigatorGroup);
  11846. if (!chart.styledMode) {
  11847. navigator.shades[index]
  11848. .attr({
  11849. fill: hasMask ?
  11850. navigatorOptions.maskFill :
  11851. 'rgba(0,0,0,0)'
  11852. })
  11853. .css((index === 1) && mouseCursor);
  11854. }
  11855. });
  11856. // Create the outline:
  11857. navigator.outline = renderer.path()
  11858. .addClass('highcharts-navigator-outline')
  11859. .add(navigatorGroup);
  11860. if (!chart.styledMode) {
  11861. navigator.outline.attr({
  11862. 'stroke-width': navigatorOptions.outlineWidth,
  11863. stroke: navigatorOptions.outlineColor
  11864. });
  11865. }
  11866. // Create the handlers:
  11867. if (navigatorOptions.handles.enabled) {
  11868. [0, 1].forEach(function (index) {
  11869. navigatorOptions.handles.inverted = chart.inverted;
  11870. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  11871. // zIndex = 6 for right handle, 7 for left.
  11872. // Can't be 10, because of the tooltip in inverted chart #2908
  11873. navigator.handles[index].attr({ zIndex: 7 - index })
  11874. .addClass('highcharts-navigator-handle ' +
  11875. 'highcharts-navigator-handle-' +
  11876. ['left', 'right'][index]).add(navigatorGroup);
  11877. if (!chart.styledMode) {
  11878. var handlesOptions = navigatorOptions.handles;
  11879. navigator.handles[index]
  11880. .attr({
  11881. fill: handlesOptions.backgroundColor,
  11882. stroke: handlesOptions.borderColor,
  11883. 'stroke-width': handlesOptions.lineWidth
  11884. })
  11885. .css(mouseCursor);
  11886. }
  11887. });
  11888. }
  11889. };
  11890. /**
  11891. * Update navigator
  11892. *
  11893. * @private
  11894. * @function Highcharts.Navigator#update
  11895. *
  11896. * @param {Highcharts.NavigatorOptions} options
  11897. * Options to merge in when updating navigator
  11898. */
  11899. Navigator.prototype.update = function (options) {
  11900. // Remove references to old navigator series in base series
  11901. (this.series || []).forEach(function (series) {
  11902. if (series.baseSeries) {
  11903. delete series.baseSeries.navigatorSeries;
  11904. }
  11905. });
  11906. // Destroy and rebuild navigator
  11907. this.destroy();
  11908. var chartOptions = this.chart.options;
  11909. merge(true, chartOptions.navigator, this.options, options);
  11910. this.init(this.chart);
  11911. };
  11912. /**
  11913. * Render the navigator
  11914. *
  11915. * @private
  11916. * @function Highcharts.Navigator#render
  11917. * @param {number} min
  11918. * X axis value minimum
  11919. * @param {number} max
  11920. * X axis value maximum
  11921. * @param {number} [pxMin]
  11922. * Pixel value minimum
  11923. * @param {number} [pxMax]
  11924. * Pixel value maximum
  11925. * @return {void}
  11926. */
  11927. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  11928. var navigator = this,
  11929. chart = navigator.chart,
  11930. navigatorWidth,
  11931. scrollbarLeft,
  11932. scrollbarTop,
  11933. scrollbarHeight = navigator.scrollbarHeight,
  11934. navigatorSize,
  11935. xAxis = navigator.xAxis,
  11936. pointRange = xAxis.pointRange || 0,
  11937. scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis,
  11938. navigatorEnabled = navigator.navigatorEnabled,
  11939. zoomedMin,
  11940. zoomedMax,
  11941. rendered = navigator.rendered,
  11942. inverted = chart.inverted,
  11943. verb,
  11944. newMin,
  11945. newMax,
  11946. currentRange,
  11947. minRange = chart.xAxis[0].minRange,
  11948. maxRange = chart.xAxis[0].options.maxRange;
  11949. // Don't redraw while moving the handles (#4703).
  11950. if (this.hasDragged && !defined(pxMin)) {
  11951. return;
  11952. }
  11953. min = correctFloat(min - pointRange / 2);
  11954. max = correctFloat(max + pointRange / 2);
  11955. // Don't render the navigator until we have data (#486, #4202, #5172).
  11956. if (!isNumber(min) || !isNumber(max)) {
  11957. // However, if navigator was already rendered, we may need to resize
  11958. // it. For example hidden series, but visible navigator (#6022).
  11959. if (rendered) {
  11960. pxMin = 0;
  11961. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  11962. }
  11963. else {
  11964. return;
  11965. }
  11966. }
  11967. navigator.left = pick(xAxis.left,
  11968. // in case of scrollbar only, without navigator
  11969. chart.plotLeft + scrollbarHeight +
  11970. (inverted ? chart.plotWidth : 0));
  11971. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  11972. 2 * scrollbarHeight);
  11973. if (inverted) {
  11974. navigatorWidth = scrollbarHeight;
  11975. }
  11976. else {
  11977. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  11978. }
  11979. // Get the pixel position of the handles
  11980. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  11981. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  11982. // Verify (#1851, #2238)
  11983. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  11984. pxMin = 0;
  11985. pxMax = navigatorWidth;
  11986. }
  11987. // Are we below the minRange? (#2618, #6191)
  11988. newMin = xAxis.toValue(pxMin, true);
  11989. newMax = xAxis.toValue(pxMax, true);
  11990. currentRange = Math.abs(correctFloat(newMax - newMin));
  11991. if (currentRange < minRange) {
  11992. if (this.grabbedLeft) {
  11993. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  11994. }
  11995. else if (this.grabbedRight) {
  11996. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  11997. }
  11998. }
  11999. else if (defined(maxRange) &&
  12000. correctFloat(currentRange - pointRange) > maxRange) {
  12001. if (this.grabbedLeft) {
  12002. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  12003. }
  12004. else if (this.grabbedRight) {
  12005. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  12006. }
  12007. }
  12008. // Handles are allowed to cross, but never exceed the plot area
  12009. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  12010. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  12011. navigator.zoomedMax - navigator.fixedWidth :
  12012. Math.min(pxMin, pxMax), 0, zoomedMax);
  12013. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  12014. zoomedMax = Math.round(navigator.zoomedMax);
  12015. zoomedMin = Math.round(navigator.zoomedMin);
  12016. if (navigatorEnabled) {
  12017. navigator.navigatorGroup.attr({
  12018. visibility: 'visible'
  12019. });
  12020. // Place elements
  12021. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  12022. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  12023. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  12024. if (navigator.navigatorOptions.handles.enabled) {
  12025. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  12026. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  12027. }
  12028. }
  12029. if (navigator.scrollbar) {
  12030. if (inverted) {
  12031. scrollbarTop = navigator.top - scrollbarHeight;
  12032. scrollbarLeft = navigator.left - scrollbarHeight +
  12033. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  12034. // Multiple axes has offsets:
  12035. (scrollbarXAxis.titleOffset || 0) +
  12036. // Self margin from the axis.title
  12037. scrollbarXAxis.axisTitleMargin);
  12038. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  12039. }
  12040. else {
  12041. scrollbarTop = navigator.top + (navigatorEnabled ?
  12042. navigator.height :
  12043. -scrollbarHeight);
  12044. scrollbarLeft = navigator.left - scrollbarHeight;
  12045. }
  12046. // Reposition scrollbar
  12047. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  12048. // Keep scale 0-1
  12049. navigator.scrollbar.setRange(
  12050. // Use real value, not rounded because range can be very small
  12051. // (#1716)
  12052. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  12053. }
  12054. navigator.rendered = true;
  12055. };
  12056. /**
  12057. * Set up the mouse and touch events for the navigator
  12058. *
  12059. * @private
  12060. * @function Highcharts.Navigator#addMouseEvents
  12061. */
  12062. Navigator.prototype.addMouseEvents = function () {
  12063. var navigator = this,
  12064. chart = navigator.chart,
  12065. container = chart.container,
  12066. eventsToUnbind = [],
  12067. mouseMoveHandler,
  12068. mouseUpHandler;
  12069. /**
  12070. * Create mouse events' handlers.
  12071. * Make them as separate functions to enable wrapping them:
  12072. */
  12073. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  12074. navigator.onMouseMove(e);
  12075. };
  12076. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  12077. navigator.onMouseUp(e);
  12078. };
  12079. // Add shades and handles mousedown events
  12080. eventsToUnbind = navigator.getPartsEvents('mousedown');
  12081. // Add mouse move and mouseup events. These are bind to doc/container,
  12082. // because Navigator.grabbedSomething flags are stored in mousedown
  12083. // events
  12084. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  12085. // Touch events
  12086. if (hasTouch) {
  12087. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  12088. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  12089. }
  12090. navigator.eventsToUnbind = eventsToUnbind;
  12091. // Data events
  12092. if (navigator.series && navigator.series[0]) {
  12093. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  12094. chart.navigator.modifyNavigatorAxisExtremes();
  12095. }));
  12096. }
  12097. };
  12098. /**
  12099. * Generate events for handles and masks
  12100. *
  12101. * @private
  12102. * @function Highcharts.Navigator#getPartsEvents
  12103. *
  12104. * @param {string} eventName
  12105. * Event name handler, 'mousedown' or 'touchstart'
  12106. *
  12107. * @return {Array<Function>}
  12108. * An array of functions to remove navigator functions from the
  12109. * events again.
  12110. */
  12111. Navigator.prototype.getPartsEvents = function (eventName) {
  12112. var navigator = this,
  12113. events = [];
  12114. ['shades', 'handles'].forEach(function (name) {
  12115. navigator[name].forEach(function (navigatorItem, index) {
  12116. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  12117. navigator[name + 'Mousedown'](e, index);
  12118. }));
  12119. });
  12120. });
  12121. return events;
  12122. };
  12123. /**
  12124. * Mousedown on a shaded mask, either:
  12125. *
  12126. * - will be stored for future drag&drop
  12127. *
  12128. * - will directly shift to a new range
  12129. *
  12130. * @private
  12131. * @function Highcharts.Navigator#shadesMousedown
  12132. *
  12133. * @param {Highcharts.PointerEventObject} e
  12134. * Mouse event
  12135. *
  12136. * @param {number} index
  12137. * Index of a mask in Navigator.shades array
  12138. */
  12139. Navigator.prototype.shadesMousedown = function (e, index) {
  12140. e = this.chart.pointer.normalize(e);
  12141. var navigator = this,
  12142. chart = navigator.chart,
  12143. xAxis = navigator.xAxis,
  12144. zoomedMin = navigator.zoomedMin,
  12145. navigatorPosition = navigator.left,
  12146. navigatorSize = navigator.size,
  12147. range = navigator.range,
  12148. chartX = e.chartX,
  12149. fixedMax,
  12150. fixedMin,
  12151. ext,
  12152. left;
  12153. // For inverted chart, swap some options:
  12154. if (chart.inverted) {
  12155. chartX = e.chartY;
  12156. navigatorPosition = navigator.top;
  12157. }
  12158. if (index === 1) {
  12159. // Store information for drag&drop
  12160. navigator.grabbedCenter = chartX;
  12161. navigator.fixedWidth = range;
  12162. navigator.dragOffset = chartX - zoomedMin;
  12163. }
  12164. else {
  12165. // Shift the range by clicking on shaded areas
  12166. left = chartX - navigatorPosition - range / 2;
  12167. if (index === 0) {
  12168. left = Math.max(0, left);
  12169. }
  12170. else if (index === 2 && left + range >= navigatorSize) {
  12171. left = navigatorSize - range;
  12172. if (navigator.reversedExtremes) {
  12173. // #7713
  12174. left -= range;
  12175. fixedMin = navigator.getUnionExtremes().dataMin;
  12176. }
  12177. else {
  12178. // #2293, #3543
  12179. fixedMax = navigator.getUnionExtremes().dataMax;
  12180. }
  12181. }
  12182. if (left !== zoomedMin) { // it has actually moved
  12183. navigator.fixedWidth = range; // #1370
  12184. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  12185. if (defined(ext.min)) { // #7411
  12186. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  12187. { trigger: 'navigator' });
  12188. }
  12189. }
  12190. }
  12191. };
  12192. /**
  12193. * Mousedown on a handle mask.
  12194. * Will store necessary information for drag&drop.
  12195. *
  12196. * @private
  12197. * @function Highcharts.Navigator#handlesMousedown
  12198. * @param {Highcharts.PointerEventObject} e
  12199. * Mouse event
  12200. * @param {number} index
  12201. * Index of a handle in Navigator.handles array
  12202. * @return {void}
  12203. */
  12204. Navigator.prototype.handlesMousedown = function (e, index) {
  12205. e = this.chart.pointer.normalize(e);
  12206. var navigator = this,
  12207. chart = navigator.chart,
  12208. baseXAxis = chart.xAxis[0],
  12209. // For reversed axes, min and max are changed,
  12210. // so the other extreme should be stored
  12211. reverse = navigator.reversedExtremes;
  12212. if (index === 0) {
  12213. // Grab the left handle
  12214. navigator.grabbedLeft = true;
  12215. navigator.otherHandlePos = navigator.zoomedMax;
  12216. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  12217. }
  12218. else {
  12219. // Grab the right handle
  12220. navigator.grabbedRight = true;
  12221. navigator.otherHandlePos = navigator.zoomedMin;
  12222. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  12223. }
  12224. chart.fixedRange = null;
  12225. };
  12226. /**
  12227. * Mouse move event based on x/y mouse position.
  12228. *
  12229. * @private
  12230. * @function Highcharts.Navigator#onMouseMove
  12231. *
  12232. * @param {Highcharts.PointerEventObject} e
  12233. * Mouse event
  12234. */
  12235. Navigator.prototype.onMouseMove = function (e) {
  12236. var navigator = this,
  12237. chart = navigator.chart,
  12238. left = navigator.left,
  12239. navigatorSize = navigator.navigatorSize,
  12240. range = navigator.range,
  12241. dragOffset = navigator.dragOffset,
  12242. inverted = chart.inverted,
  12243. chartX;
  12244. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  12245. // the finger down in the center of the scrollbar. This should be
  12246. // ignored.
  12247. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  12248. e = chart.pointer.normalize(e);
  12249. chartX = e.chartX;
  12250. // Swap some options for inverted chart
  12251. if (inverted) {
  12252. left = navigator.top;
  12253. chartX = e.chartY;
  12254. }
  12255. // Drag left handle or top handle
  12256. if (navigator.grabbedLeft) {
  12257. navigator.hasDragged = true;
  12258. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  12259. // Drag right handle or bottom handle
  12260. }
  12261. else if (navigator.grabbedRight) {
  12262. navigator.hasDragged = true;
  12263. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  12264. // Drag scrollbar or open area in navigator
  12265. }
  12266. else if (navigator.grabbedCenter) {
  12267. navigator.hasDragged = true;
  12268. if (chartX < dragOffset) { // outside left
  12269. chartX = dragOffset;
  12270. // outside right
  12271. }
  12272. else if (chartX >
  12273. navigatorSize + dragOffset - range) {
  12274. chartX = navigatorSize + dragOffset - range;
  12275. }
  12276. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  12277. }
  12278. if (navigator.hasDragged &&
  12279. navigator.scrollbar &&
  12280. pick(navigator.scrollbar.options.liveRedraw,
  12281. // By default, don't run live redraw on VML, on touch
  12282. // devices or if the chart is in boost.
  12283. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  12284. e.DOMType = e.type; // DOMType is for IE8
  12285. setTimeout(function () {
  12286. navigator.onMouseUp(e);
  12287. }, 0);
  12288. }
  12289. }
  12290. };
  12291. /**
  12292. * Mouse up event based on x/y mouse position.
  12293. *
  12294. * @private
  12295. * @function Highcharts.Navigator#onMouseUp
  12296. * @param {Highcharts.PointerEventObject} e
  12297. * Mouse event
  12298. * @return {void}
  12299. */
  12300. Navigator.prototype.onMouseUp = function (e) {
  12301. var navigator = this,
  12302. chart = navigator.chart,
  12303. xAxis = navigator.xAxis,
  12304. scrollbar = navigator.scrollbar,
  12305. DOMEvent = e.DOMEvent || e,
  12306. inverted = chart.inverted,
  12307. verb = navigator.rendered && !navigator.hasDragged ?
  12308. 'animate' : 'attr',
  12309. zoomedMax,
  12310. zoomedMin,
  12311. unionExtremes,
  12312. fixedMin,
  12313. fixedMax,
  12314. ext;
  12315. if (
  12316. // MouseUp is called for both, navigator and scrollbar (that order),
  12317. // which causes calling afterSetExtremes twice. Prevent first call
  12318. // by checking if scrollbar is going to set new extremes (#6334)
  12319. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  12320. e.trigger === 'scrollbar') {
  12321. unionExtremes = navigator.getUnionExtremes();
  12322. // When dragging one handle, make sure the other one doesn't change
  12323. if (navigator.zoomedMin === navigator.otherHandlePos) {
  12324. fixedMin = navigator.fixedExtreme;
  12325. }
  12326. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  12327. fixedMax = navigator.fixedExtreme;
  12328. }
  12329. // Snap to right edge (#4076)
  12330. if (navigator.zoomedMax === navigator.size) {
  12331. fixedMax = navigator.reversedExtremes ?
  12332. unionExtremes.dataMin :
  12333. unionExtremes.dataMax;
  12334. }
  12335. // Snap to left edge (#7576)
  12336. if (navigator.zoomedMin === 0) {
  12337. fixedMin = navigator.reversedExtremes ?
  12338. unionExtremes.dataMax :
  12339. unionExtremes.dataMin;
  12340. }
  12341. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  12342. if (defined(ext.min)) {
  12343. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  12344. // Run animation when clicking buttons, scrollbar track etc,
  12345. // but not when dragging handles or scrollbar
  12346. navigator.hasDragged ? false : null, {
  12347. trigger: 'navigator',
  12348. triggerOp: 'navigator-drag',
  12349. DOMEvent: DOMEvent // #1838
  12350. });
  12351. }
  12352. }
  12353. if (e.DOMType !== 'mousemove' &&
  12354. e.DOMType !== 'touchmove') {
  12355. navigator.grabbedLeft = navigator.grabbedRight =
  12356. navigator.grabbedCenter = navigator.fixedWidth =
  12357. navigator.fixedExtreme = navigator.otherHandlePos =
  12358. navigator.hasDragged = navigator.dragOffset = null;
  12359. }
  12360. // Update position of navigator shades, outline and handles (#12573)
  12361. if (navigator.navigatorEnabled &&
  12362. isNumber(navigator.zoomedMin) &&
  12363. isNumber(navigator.zoomedMax)) {
  12364. zoomedMin = Math.round(navigator.zoomedMin);
  12365. zoomedMax = Math.round(navigator.zoomedMax);
  12366. if (navigator.shades) {
  12367. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  12368. }
  12369. if (navigator.outline) {
  12370. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  12371. }
  12372. if (navigator.navigatorOptions.handles.enabled &&
  12373. Object.keys(navigator.handles).length ===
  12374. navigator.handles.length) {
  12375. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  12376. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  12377. }
  12378. }
  12379. };
  12380. /**
  12381. * Removes the event handlers attached previously with addEvents.
  12382. *
  12383. * @private
  12384. * @function Highcharts.Navigator#removeEvents
  12385. * @return {void}
  12386. */
  12387. Navigator.prototype.removeEvents = function () {
  12388. if (this.eventsToUnbind) {
  12389. this.eventsToUnbind.forEach(function (unbind) {
  12390. unbind();
  12391. });
  12392. this.eventsToUnbind = void 0;
  12393. }
  12394. this.removeBaseSeriesEvents();
  12395. };
  12396. /**
  12397. * Remove data events.
  12398. *
  12399. * @private
  12400. * @function Highcharts.Navigator#removeBaseSeriesEvents
  12401. * @return {void}
  12402. */
  12403. Navigator.prototype.removeBaseSeriesEvents = function () {
  12404. var baseSeries = this.baseSeries || [];
  12405. if (this.navigatorEnabled && baseSeries[0]) {
  12406. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  12407. baseSeries.forEach(function (series) {
  12408. removeEvent(series, 'updatedData', this.updatedDataHandler);
  12409. }, this);
  12410. }
  12411. // We only listen for extremes-events on the first baseSeries
  12412. if (baseSeries[0].xAxis) {
  12413. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  12414. }
  12415. }
  12416. };
  12417. /**
  12418. * Initialize the Navigator object
  12419. *
  12420. * @private
  12421. * @function Highcharts.Navigator#init
  12422. *
  12423. * @param {Highcharts.Chart} chart
  12424. */
  12425. Navigator.prototype.init = function (chart) {
  12426. var chartOptions = chart.options,
  12427. navigatorOptions = chartOptions.navigator,
  12428. navigatorEnabled = navigatorOptions.enabled,
  12429. scrollbarOptions = chartOptions.scrollbar,
  12430. scrollbarEnabled = scrollbarOptions.enabled,
  12431. height = navigatorEnabled ? navigatorOptions.height : 0,
  12432. scrollbarHeight = scrollbarEnabled ?
  12433. scrollbarOptions.height :
  12434. 0;
  12435. this.handles = [];
  12436. this.shades = [];
  12437. this.chart = chart;
  12438. this.setBaseSeries();
  12439. this.height = height;
  12440. this.scrollbarHeight = scrollbarHeight;
  12441. this.scrollbarEnabled = scrollbarEnabled;
  12442. this.navigatorEnabled = navigatorEnabled;
  12443. this.navigatorOptions = navigatorOptions;
  12444. this.scrollbarOptions = scrollbarOptions;
  12445. this.outlineHeight = height + scrollbarHeight;
  12446. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  12447. var navigator = this,
  12448. baseSeries = navigator.baseSeries,
  12449. xAxisIndex = chart.xAxis.length,
  12450. yAxisIndex = chart.yAxis.length,
  12451. baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  12452. chart.xAxis[0] || { options: {} };
  12453. chart.isDirtyBox = true;
  12454. if (navigator.navigatorEnabled) {
  12455. // an x axis is required for scrollbar also
  12456. navigator.xAxis = new Axis(chart, merge({
  12457. // inherit base xAxis' break and ordinal options
  12458. breaks: baseXaxis.options.breaks,
  12459. ordinal: baseXaxis.options.ordinal
  12460. }, navigatorOptions.xAxis, {
  12461. id: 'navigator-x-axis',
  12462. yAxis: 'navigator-y-axis',
  12463. isX: true,
  12464. type: 'datetime',
  12465. index: xAxisIndex,
  12466. isInternal: true,
  12467. offset: 0,
  12468. keepOrdinalPadding: true,
  12469. startOnTick: false,
  12470. endOnTick: false,
  12471. minPadding: 0,
  12472. maxPadding: 0,
  12473. zoomEnabled: false
  12474. }, chart.inverted ? {
  12475. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  12476. width: height
  12477. } : {
  12478. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  12479. height: height
  12480. }));
  12481. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  12482. id: 'navigator-y-axis',
  12483. alignTicks: false,
  12484. offset: 0,
  12485. index: yAxisIndex,
  12486. isInternal: true,
  12487. reversed: pick((navigatorOptions.yAxis && navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),
  12488. zoomEnabled: false
  12489. }, chart.inverted ? {
  12490. width: height
  12491. } : {
  12492. height: height
  12493. }));
  12494. // If we have a base series, initialize the navigator series
  12495. if (baseSeries || navigatorOptions.series.data) {
  12496. navigator.updateNavigatorSeries(false);
  12497. // If not, set up an event to listen for added series
  12498. }
  12499. else if (chart.series.length === 0) {
  12500. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  12501. // We've got one, now add it as base
  12502. if (chart.series.length > 0 && !navigator.series) {
  12503. navigator.setBaseSeries();
  12504. navigator.unbindRedraw(); // reset
  12505. }
  12506. });
  12507. }
  12508. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  12509. // Render items, so we can bind events to them:
  12510. navigator.renderElements();
  12511. // Add mouse events
  12512. navigator.addMouseEvents();
  12513. // in case of scrollbar only, fake an x axis to get translation
  12514. }
  12515. else {
  12516. navigator.xAxis = {
  12517. chart: chart,
  12518. navigatorAxis: {
  12519. fake: true
  12520. },
  12521. translate: function (value, reverse) {
  12522. var axis = chart.xAxis[0], ext = axis.getExtremes(), scrollTrackWidth = axis.len - 2 * scrollbarHeight, min = numExt('min', axis.options.min, ext.dataMin), valueRange = numExt('max', axis.options.max, ext.dataMax) - min;
  12523. return reverse ?
  12524. // from pixel to value
  12525. (value * valueRange / scrollTrackWidth) + min :
  12526. // from value to pixel
  12527. scrollTrackWidth * (value - min) / valueRange;
  12528. },
  12529. toPixels: function (value) {
  12530. return this.translate(value);
  12531. },
  12532. toValue: function (value) {
  12533. return this.translate(value, true);
  12534. }
  12535. };
  12536. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  12537. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  12538. }
  12539. // Initialize the scrollbar
  12540. if (chart.options.scrollbar.enabled) {
  12541. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  12542. margin: navigator.navigatorEnabled ? 0 : 10,
  12543. vertical: chart.inverted
  12544. }), chart);
  12545. addEvent(navigator.scrollbar, 'changed', function (e) {
  12546. var range = navigator.size,
  12547. to = range * this.to,
  12548. from = range * this.from;
  12549. navigator.hasDragged = navigator.scrollbar.hasDragged;
  12550. navigator.render(0, 0, from, to);
  12551. if (chart.options.scrollbar.liveRedraw ||
  12552. (e.DOMType !== 'mousemove' &&
  12553. e.DOMType !== 'touchmove')) {
  12554. setTimeout(function () {
  12555. navigator.onMouseUp(e);
  12556. });
  12557. }
  12558. });
  12559. }
  12560. // Add data events
  12561. navigator.addBaseSeriesEvents();
  12562. // Add redraw events
  12563. navigator.addChartEvents();
  12564. };
  12565. /**
  12566. * Get the union data extremes of the chart - the outer data extremes of the
  12567. * base X axis and the navigator axis.
  12568. *
  12569. * @private
  12570. * @function Highcharts.Navigator#getUnionExtremes
  12571. * @param {boolean} [returnFalseOnNoBaseSeries]
  12572. * as the param says.
  12573. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  12574. */
  12575. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  12576. var baseAxis = this.chart.xAxis[0],
  12577. navAxis = this.xAxis,
  12578. navAxisOptions = navAxis.options,
  12579. baseAxisOptions = baseAxis.options,
  12580. ret;
  12581. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  12582. ret = {
  12583. dataMin: pick(// #4053
  12584. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  12585. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  12586. };
  12587. }
  12588. return ret;
  12589. };
  12590. /**
  12591. * Set the base series and update the navigator series from this. With a bit
  12592. * of modification we should be able to make this an API method to be called
  12593. * from the outside
  12594. *
  12595. * @private
  12596. * @function Highcharts.Navigator#setBaseSeries
  12597. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  12598. * Additional series options for a navigator
  12599. * @param {boolean} [redraw]
  12600. * Whether to redraw after update.
  12601. * @return {void}
  12602. */
  12603. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  12604. var chart = this.chart,
  12605. baseSeries = this.baseSeries = [];
  12606. baseSeriesOptions = (baseSeriesOptions ||
  12607. chart.options && chart.options.navigator.baseSeries ||
  12608. (chart.series.length ?
  12609. // Find the first non-navigator series (#8430)
  12610. find(chart.series, function (s) {
  12611. return !s.options.isInternal;
  12612. }).index :
  12613. 0));
  12614. // Iterate through series and add the ones that should be shown in
  12615. // navigator.
  12616. (chart.series || []).forEach(function (series, i) {
  12617. if (
  12618. // Don't include existing nav series
  12619. !series.options.isInternal &&
  12620. (series.options.showInNavigator ||
  12621. (i === baseSeriesOptions ||
  12622. series.options.id === baseSeriesOptions) &&
  12623. series.options.showInNavigator !== false)) {
  12624. baseSeries.push(series);
  12625. }
  12626. });
  12627. // When run after render, this.xAxis already exists
  12628. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  12629. this.updateNavigatorSeries(true, redraw);
  12630. }
  12631. };
  12632. /**
  12633. * Update series in the navigator from baseSeries, adding new if does not
  12634. * exist.
  12635. *
  12636. * @private
  12637. * @function Highcharts.Navigator.updateNavigatorSeries
  12638. * @param {boolean} addEvents
  12639. * @param {boolean} [redraw]
  12640. * @return {void}
  12641. */
  12642. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  12643. var navigator = this,
  12644. chart = navigator.chart,
  12645. baseSeries = navigator.baseSeries,
  12646. baseOptions,
  12647. mergedNavSeriesOptions,
  12648. chartNavigatorSeriesOptions = navigator.navigatorOptions.series,
  12649. baseNavigatorOptions,
  12650. navSeriesMixin = {
  12651. enableMouseTracking: false,
  12652. index: null,
  12653. linkedTo: null,
  12654. group: 'nav',
  12655. padXAxis: false,
  12656. xAxis: 'navigator-x-axis',
  12657. yAxis: 'navigator-y-axis',
  12658. showInLegend: false,
  12659. stacking: void 0,
  12660. isInternal: true,
  12661. states: {
  12662. inactive: {
  12663. opacity: 1
  12664. }
  12665. }
  12666. },
  12667. // Remove navigator series that are no longer in the baseSeries
  12668. navigatorSeries = navigator.series =
  12669. (navigator.series || []).filter(function (navSeries) {
  12670. var base = navSeries.baseSeries;
  12671. if (baseSeries.indexOf(base) < 0) { // Not in array
  12672. // If there is still a base series connected to this
  12673. // series, remove event handler and reference.
  12674. if (base) {
  12675. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  12676. delete base.navigatorSeries;
  12677. }
  12678. // Kill the nav series. It may already have been
  12679. // destroyed (#8715).
  12680. if (navSeries.chart) {
  12681. navSeries.destroy();
  12682. }
  12683. return false;
  12684. }
  12685. return true;
  12686. });
  12687. // Go through each base series and merge the options to create new
  12688. // series
  12689. if (baseSeries && baseSeries.length) {
  12690. baseSeries.forEach(function eachBaseSeries(base) {
  12691. var linkedNavSeries = base.navigatorSeries,
  12692. userNavOptions = extend(
  12693. // Grab color and visibility from base as default
  12694. {
  12695. color: base.color,
  12696. visible: base.visible
  12697. }, !isArray(chartNavigatorSeriesOptions) ?
  12698. chartNavigatorSeriesOptions :
  12699. defaultOptions.navigator.series);
  12700. // Don't update if the series exists in nav and we have disabled
  12701. // adaptToUpdatedData.
  12702. if (linkedNavSeries &&
  12703. navigator.navigatorOptions.adaptToUpdatedData === false) {
  12704. return;
  12705. }
  12706. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  12707. baseOptions = base.options || {};
  12708. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  12709. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  12710. // Once nav series type is resolved, pick correct pointRange
  12711. mergedNavSeriesOptions.pointRange = pick(
  12712. // Stricte set pointRange in options
  12713. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  12714. // Fallback to default values, e.g. `null` for column
  12715. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  12716. // Merge data separately. Do a slice to avoid mutating the
  12717. // navigator options from base series (#4923).
  12718. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  12719. navigator.hasNavigatorData =
  12720. navigator.hasNavigatorData || !!navigatorSeriesData;
  12721. mergedNavSeriesOptions.data =
  12722. navigatorSeriesData ||
  12723. baseOptions.data && baseOptions.data.slice(0);
  12724. // Update or add the series
  12725. if (linkedNavSeries && linkedNavSeries.options) {
  12726. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  12727. }
  12728. else {
  12729. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  12730. base.navigatorSeries.baseSeries = base; // Store ref
  12731. navigatorSeries.push(base.navigatorSeries);
  12732. }
  12733. });
  12734. }
  12735. // If user has defined data (and no base series) or explicitly defined
  12736. // navigator.series as an array, we create these series on top of any
  12737. // base series.
  12738. if (chartNavigatorSeriesOptions.data &&
  12739. !(baseSeries && baseSeries.length) ||
  12740. isArray(chartNavigatorSeriesOptions)) {
  12741. navigator.hasNavigatorData = false;
  12742. // Allow navigator.series to be an array
  12743. chartNavigatorSeriesOptions =
  12744. splat(chartNavigatorSeriesOptions);
  12745. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  12746. navSeriesMixin.name =
  12747. 'Navigator ' + (navigatorSeries.length + 1);
  12748. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  12749. // Since we don't have a base series to pull color from,
  12750. // try to fake it by using color from series with same
  12751. // index. Otherwise pull from the colors array. We need
  12752. // an explicit color as otherwise updates will increment
  12753. // color counter and we'll get a new color for each
  12754. // update of the nav series.
  12755. color: chart.series[i] &&
  12756. !chart.series[i].options.isInternal &&
  12757. chart.series[i].color ||
  12758. chart.options.colors[i] ||
  12759. chart.options.colors[0]
  12760. }, navSeriesMixin, userSeriesOptions);
  12761. mergedNavSeriesOptions.data = userSeriesOptions.data;
  12762. if (mergedNavSeriesOptions.data) {
  12763. navigator.hasNavigatorData = true;
  12764. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  12765. }
  12766. });
  12767. }
  12768. if (addEvents) {
  12769. this.addBaseSeriesEvents();
  12770. }
  12771. };
  12772. /**
  12773. * Add data events.
  12774. * For example when main series is updated we need to recalculate extremes
  12775. *
  12776. * @private
  12777. * @function Highcharts.Navigator#addBaseSeriesEvent
  12778. * @return {void}
  12779. */
  12780. Navigator.prototype.addBaseSeriesEvents = function () {
  12781. var navigator = this,
  12782. baseSeries = navigator.baseSeries || [];
  12783. // Bind modified extremes event to first base's xAxis only.
  12784. // In event of > 1 base-xAxes, the navigator will ignore those.
  12785. // Adding this multiple times to the same axis is no problem, as
  12786. // duplicates should be discarded by the browser.
  12787. if (baseSeries[0] && baseSeries[0].xAxis) {
  12788. addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  12789. }
  12790. baseSeries.forEach(function (base) {
  12791. // Link base series show/hide to navigator series visibility
  12792. addEvent(base, 'show', function () {
  12793. if (this.navigatorSeries) {
  12794. this.navigatorSeries.setVisible(true, false);
  12795. }
  12796. });
  12797. addEvent(base, 'hide', function () {
  12798. if (this.navigatorSeries) {
  12799. this.navigatorSeries.setVisible(false, false);
  12800. }
  12801. });
  12802. // Respond to updated data in the base series, unless explicitily
  12803. // not adapting to data changes.
  12804. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  12805. if (base.xAxis) {
  12806. addEvent(base, 'updatedData', this.updatedDataHandler);
  12807. }
  12808. }
  12809. // Handle series removal
  12810. addEvent(base, 'remove', function () {
  12811. if (this.navigatorSeries) {
  12812. erase(navigator.series, this.navigatorSeries);
  12813. if (defined(this.navigatorSeries.options)) {
  12814. this.navigatorSeries.remove(false);
  12815. }
  12816. delete this.navigatorSeries;
  12817. }
  12818. });
  12819. }, this);
  12820. };
  12821. /**
  12822. * Get minimum from all base series connected to the navigator
  12823. * @private
  12824. * @param {number} currentSeriesMin
  12825. * Minium from the current series
  12826. * @return {number} Minimum from all series
  12827. */
  12828. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  12829. return this.baseSeries.reduce(function (min, series) {
  12830. // (#10193)
  12831. return Math.min(min, series.xData ? series.xData[0] : min);
  12832. }, currentSeriesMin);
  12833. };
  12834. /**
  12835. * Set the navigator x axis extremes to reflect the total. The navigator
  12836. * extremes should always be the extremes of the union of all series in the
  12837. * chart as well as the navigator series.
  12838. *
  12839. * @private
  12840. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  12841. */
  12842. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  12843. var xAxis = this.xAxis,
  12844. unionExtremes;
  12845. if (typeof xAxis.getExtremes !== 'undefined') {
  12846. unionExtremes = this.getUnionExtremes(true);
  12847. if (unionExtremes &&
  12848. (unionExtremes.dataMin !== xAxis.min ||
  12849. unionExtremes.dataMax !== xAxis.max)) {
  12850. xAxis.min = unionExtremes.dataMin;
  12851. xAxis.max = unionExtremes.dataMax;
  12852. }
  12853. }
  12854. };
  12855. /**
  12856. * Hook to modify the base axis extremes with information from the Navigator
  12857. *
  12858. * @private
  12859. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  12860. */
  12861. Navigator.prototype.modifyBaseAxisExtremes = function () {
  12862. var baseXAxis = this,
  12863. navigator = baseXAxis.chart.navigator,
  12864. baseExtremes = baseXAxis.getExtremes(),
  12865. baseMin = baseExtremes.min,
  12866. baseMax = baseExtremes.max,
  12867. baseDataMin = baseExtremes.dataMin,
  12868. baseDataMax = baseExtremes.dataMax,
  12869. range = baseMax - baseMin,
  12870. stickToMin = navigator.stickToMin,
  12871. stickToMax = navigator.stickToMax,
  12872. overscroll = pick(baseXAxis.options.overscroll, 0),
  12873. newMax,
  12874. newMin,
  12875. navigatorSeries = navigator.series && navigator.series[0],
  12876. hasSetExtremes = !!baseXAxis.setExtremes,
  12877. // When the extremes have been set by range selector button, don't
  12878. // stick to min or max. The range selector buttons will handle the
  12879. // extremes. (#5489)
  12880. unmutable = baseXAxis.eventArgs &&
  12881. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  12882. if (!unmutable) {
  12883. // If the zoomed range is already at the min, move it to the right
  12884. // as new data comes in
  12885. if (stickToMin) {
  12886. newMin = baseDataMin;
  12887. newMax = newMin + range;
  12888. }
  12889. // If the zoomed range is already at the max, move it to the right
  12890. // as new data comes in
  12891. if (stickToMax) {
  12892. newMax = baseDataMax + overscroll;
  12893. // If stickToMin is true, the new min value is set above
  12894. if (!stickToMin) {
  12895. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  12896. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  12897. navigatorSeries.xData[0] :
  12898. -Number.MAX_VALUE));
  12899. }
  12900. }
  12901. // Update the extremes
  12902. if (hasSetExtremes && (stickToMin || stickToMax)) {
  12903. if (isNumber(newMin)) {
  12904. baseXAxis.min = baseXAxis.userMin = newMin;
  12905. baseXAxis.max = baseXAxis.userMax = newMax;
  12906. }
  12907. }
  12908. }
  12909. // Reset
  12910. navigator.stickToMin =
  12911. navigator.stickToMax = null;
  12912. };
  12913. /**
  12914. * Handler for updated data on the base series. When data is modified, the
  12915. * navigator series must reflect it. This is called from the Chart.redraw
  12916. * function before axis and series extremes are computed.
  12917. *
  12918. * @private
  12919. * @function Highcharts.Navigator#updateDataHandler
  12920. */
  12921. Navigator.prototype.updatedDataHandler = function () {
  12922. var navigator = this.chart.navigator,
  12923. baseSeries = this,
  12924. navigatorSeries = this.navigatorSeries,
  12925. xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  12926. // If the scrollbar is scrolled all the way to the right, keep right as
  12927. // new data comes in.
  12928. navigator.stickToMax = navigator.reversedExtremes ?
  12929. Math.round(navigator.zoomedMin) === 0 :
  12930. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  12931. // Detect whether the zoomed area should stick to the minimum or
  12932. // maximum. If the current axis minimum falls outside the new updated
  12933. // dataset, we must adjust.
  12934. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  12935. (baseSeries.xAxis.min <= xDataMin) &&
  12936. (!this.chart.fixedRange || !navigator.stickToMax);
  12937. // Set the navigator series data to the new data of the base series
  12938. if (navigatorSeries && !navigator.hasNavigatorData) {
  12939. navigatorSeries.options.pointStart = baseSeries.xData[0];
  12940. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  12941. }
  12942. };
  12943. /**
  12944. * Add chart events, like redrawing navigator, when chart requires that.
  12945. *
  12946. * @private
  12947. * @function Highcharts.Navigator#addChartEvents
  12948. * @return {void}
  12949. */
  12950. Navigator.prototype.addChartEvents = function () {
  12951. if (!this.eventsToUnbind) {
  12952. this.eventsToUnbind = [];
  12953. }
  12954. this.eventsToUnbind.push(
  12955. // Move the scrollbar after redraw, like after data updata even if
  12956. // axes don't redraw
  12957. addEvent(this.chart, 'redraw', function () {
  12958. var navigator = this.navigator,
  12959. xAxis = navigator && (navigator.baseSeries &&
  12960. navigator.baseSeries[0] &&
  12961. navigator.baseSeries[0].xAxis ||
  12962. this.xAxis[0]); // #5709, #13114
  12963. if (xAxis) {
  12964. navigator.render(xAxis.min,
  12965. xAxis.max);
  12966. }
  12967. }),
  12968. // Make room for the navigator, can be placed around the chart:
  12969. addEvent(this.chart, 'getMargins', function () {
  12970. var chart = this,
  12971. navigator = chart.navigator,
  12972. marginName = navigator.opposite ?
  12973. 'plotTop' : 'marginBottom';
  12974. if (chart.inverted) {
  12975. marginName = navigator.opposite ?
  12976. 'marginRight' : 'plotLeft';
  12977. }
  12978. chart[marginName] =
  12979. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  12980. navigator.outlineHeight :
  12981. 0) + navigator.navigatorOptions.margin;
  12982. }));
  12983. };
  12984. /**
  12985. * Destroys allocated elements.
  12986. *
  12987. * @private
  12988. * @function Highcharts.Navigator#destroy
  12989. */
  12990. Navigator.prototype.destroy = function () {
  12991. // Disconnect events added in addEvents
  12992. this.removeEvents();
  12993. if (this.xAxis) {
  12994. erase(this.chart.xAxis, this.xAxis);
  12995. erase(this.chart.axes, this.xAxis);
  12996. }
  12997. if (this.yAxis) {
  12998. erase(this.chart.yAxis, this.yAxis);
  12999. erase(this.chart.axes, this.yAxis);
  13000. }
  13001. // Destroy series
  13002. (this.series || []).forEach(function (s) {
  13003. if (s.destroy) {
  13004. s.destroy();
  13005. }
  13006. });
  13007. // Destroy properties
  13008. [
  13009. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  13010. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  13011. 'rendered'
  13012. ].forEach(function (prop) {
  13013. if (this[prop] && this[prop].destroy) {
  13014. this[prop].destroy();
  13015. }
  13016. this[prop] = null;
  13017. }, this);
  13018. // Destroy elements in collection
  13019. [this.handles].forEach(function (coll) {
  13020. destroyObjectProperties(coll);
  13021. }, this);
  13022. };
  13023. return Navigator;
  13024. }());
  13025. // End of prototype
  13026. if (!H.Navigator) {
  13027. H.Navigator = Navigator;
  13028. NavigatorAxis.compose(Axis);
  13029. // For Stock charts. For x only zooming, do not to create the zoom button
  13030. // because X axis zooming is already allowed by the Navigator and Range
  13031. // selector. (#9285)
  13032. addEvent(Chart, 'beforeShowResetZoom', function () {
  13033. var chartOptions = this.options,
  13034. navigator = chartOptions.navigator,
  13035. rangeSelector = chartOptions.rangeSelector;
  13036. if (((navigator && navigator.enabled) ||
  13037. (rangeSelector && rangeSelector.enabled)) &&
  13038. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  13039. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  13040. return false;
  13041. }
  13042. });
  13043. // Initialize navigator for stock charts
  13044. addEvent(Chart, 'beforeRender', function () {
  13045. var options = this.options;
  13046. if (options.navigator.enabled ||
  13047. options.scrollbar.enabled) {
  13048. this.scroller = this.navigator = new Navigator(this);
  13049. }
  13050. });
  13051. // For stock charts, extend the Chart.setChartSize method so that we can set
  13052. // the final top position of the navigator once the height of the chart,
  13053. // including the legend, is determined. #367. We can't use Chart.getMargins,
  13054. // because labels offsets are not calculated yet.
  13055. addEvent(Chart, 'afterSetChartSize', function () {
  13056. var legend = this.legend,
  13057. navigator = this.navigator,
  13058. scrollbarHeight,
  13059. legendOptions,
  13060. xAxis,
  13061. yAxis;
  13062. if (navigator) {
  13063. legendOptions = legend && legend.options;
  13064. xAxis = navigator.xAxis;
  13065. yAxis = navigator.yAxis;
  13066. scrollbarHeight = navigator.scrollbarHeight;
  13067. // Compute the top position
  13068. if (this.inverted) {
  13069. navigator.left = navigator.opposite ?
  13070. this.chartWidth - scrollbarHeight -
  13071. navigator.height :
  13072. this.spacing[3] + scrollbarHeight;
  13073. navigator.top = this.plotTop + scrollbarHeight;
  13074. }
  13075. else {
  13076. navigator.left = this.plotLeft + scrollbarHeight;
  13077. navigator.top = navigator.navigatorOptions.top ||
  13078. this.chartHeight -
  13079. navigator.height -
  13080. scrollbarHeight -
  13081. this.spacing[2] -
  13082. (this.rangeSelector && this.extraBottomMargin ?
  13083. this.rangeSelector.getHeight() :
  13084. 0) -
  13085. ((legendOptions &&
  13086. legendOptions.verticalAlign === 'bottom' &&
  13087. legendOptions.layout !== 'proximate' && // #13392
  13088. legendOptions.enabled &&
  13089. !legendOptions.floating) ?
  13090. legend.legendHeight +
  13091. pick(legendOptions.margin, 10) :
  13092. 0) -
  13093. (this.titleOffset ? this.titleOffset[2] : 0);
  13094. }
  13095. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  13096. if (this.inverted) {
  13097. xAxis.options.left = yAxis.options.left = navigator.left;
  13098. }
  13099. else {
  13100. xAxis.options.top = yAxis.options.top = navigator.top;
  13101. }
  13102. xAxis.setAxisSize();
  13103. yAxis.setAxisSize();
  13104. }
  13105. }
  13106. });
  13107. // Merge options, if no scrolling exists yet
  13108. addEvent(Chart, 'update', function (e) {
  13109. var navigatorOptions = (e.options.navigator || {}),
  13110. scrollbarOptions = (e.options.scrollbar || {});
  13111. if (!this.navigator && !this.scroller &&
  13112. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  13113. merge(true, this.options.navigator, navigatorOptions);
  13114. merge(true, this.options.scrollbar, scrollbarOptions);
  13115. delete e.options.navigator;
  13116. delete e.options.scrollbar;
  13117. }
  13118. });
  13119. // Initialize navigator, if no scrolling exists yet
  13120. addEvent(Chart, 'afterUpdate', function (event) {
  13121. if (!this.navigator && !this.scroller &&
  13122. (this.options.navigator.enabled ||
  13123. this.options.scrollbar.enabled)) {
  13124. this.scroller = this.navigator = new Navigator(this);
  13125. if (pick(event.redraw, true)) {
  13126. this.redraw(event.animation); // #7067
  13127. }
  13128. }
  13129. });
  13130. // Handle adding new series
  13131. addEvent(Chart, 'afterAddSeries', function () {
  13132. if (this.navigator) {
  13133. // Recompute which series should be shown in navigator, and add them
  13134. this.navigator.setBaseSeries(null, false);
  13135. }
  13136. });
  13137. // Handle updating series
  13138. addEvent(Series, 'afterUpdate', function () {
  13139. if (this.chart.navigator && !this.options.isInternal) {
  13140. this.chart.navigator.setBaseSeries(null, false);
  13141. }
  13142. });
  13143. Chart.prototype.callbacks.push(function (chart) {
  13144. var extremes,
  13145. navigator = chart.navigator;
  13146. // Initialize the navigator
  13147. if (navigator && chart.xAxis[0]) {
  13148. extremes = chart.xAxis[0].getExtremes();
  13149. navigator.render(extremes.min, extremes.max);
  13150. }
  13151. });
  13152. }
  13153. H.Navigator = Navigator;
  13154. return H.Navigator;
  13155. });
  13156. _registerModule(_modules, 'masters/modules/gantt.src.js', [], function () {
  13157. });
  13158. }));