stock.src.js 420 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043
  1. /**
  2. * @license Highstock JS v8.1.2 (2020-06-16)
  3. *
  4. * Highstock as a plugin for Highcharts
  5. *
  6. * (c) 2010-2019 Torstein Honsi
  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/stock', ['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, 'parts/NavigatorAxis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  32. /* *
  33. *
  34. * (c) 2010-2020 Torstein Honsi
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * */
  41. var isTouchDevice = H.isTouchDevice;
  42. var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, isNumber = U.isNumber, pick = U.pick;
  43. /* eslint-disable valid-jsdoc */
  44. /**
  45. * @private
  46. * @class
  47. */
  48. var NavigatorAxisAdditions = /** @class */ (function () {
  49. /* *
  50. *
  51. * Constructors
  52. *
  53. * */
  54. function NavigatorAxisAdditions(axis) {
  55. this.axis = axis;
  56. }
  57. /* *
  58. *
  59. * Functions
  60. *
  61. * */
  62. /**
  63. * @private
  64. */
  65. NavigatorAxisAdditions.prototype.destroy = function () {
  66. this.axis = void 0;
  67. };
  68. /**
  69. * Add logic to normalize the zoomed range in order to preserve the pressed
  70. * state of range selector buttons
  71. *
  72. * @private
  73. * @function Highcharts.Axis#toFixedRange
  74. * @param {number} [pxMin]
  75. * @param {number} [pxMax]
  76. * @param {number} [fixedMin]
  77. * @param {number} [fixedMax]
  78. * @return {*}
  79. */
  80. NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  81. var navigator = this;
  82. var axis = navigator.axis;
  83. var chart = axis.chart;
  84. var fixedRange = chart && chart.fixedRange, halfPointRange = (axis.pointRange || 0) / 2, newMin = pick(fixedMin, axis.translate(pxMin, true, !axis.horiz)), newMax = pick(fixedMax, axis.translate(pxMax, true, !axis.horiz)), changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  85. // Add/remove half point range to/from the extremes (#1172)
  86. if (!defined(fixedMin)) {
  87. newMin = correctFloat(newMin + halfPointRange);
  88. }
  89. if (!defined(fixedMax)) {
  90. newMax = correctFloat(newMax - halfPointRange);
  91. }
  92. // If the difference between the fixed range and the actual requested
  93. // range is too great, the user is dragging across an ordinal gap, and
  94. // we need to release the range selector button.
  95. if (changeRatio > 0.7 && changeRatio < 1.3) {
  96. if (fixedMax) {
  97. newMin = newMax - fixedRange;
  98. }
  99. else {
  100. newMax = newMin + fixedRange;
  101. }
  102. }
  103. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  104. newMin = newMax = void 0;
  105. }
  106. return {
  107. min: newMin,
  108. max: newMax
  109. };
  110. };
  111. return NavigatorAxisAdditions;
  112. }());
  113. /**
  114. * @private
  115. * @class
  116. */
  117. var NavigatorAxis = /** @class */ (function () {
  118. function NavigatorAxis() {
  119. }
  120. /* *
  121. *
  122. * Static Functions
  123. *
  124. * */
  125. /**
  126. * @private
  127. */
  128. NavigatorAxis.compose = function (AxisClass) {
  129. AxisClass.keepProps.push('navigatorAxis');
  130. /* eslint-disable no-invalid-this */
  131. addEvent(AxisClass, 'init', function () {
  132. var axis = this;
  133. if (!axis.navigatorAxis) {
  134. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  135. }
  136. });
  137. // For Stock charts, override selection zooming with some special
  138. // features because X axis zooming is already allowed by the Navigator
  139. // and Range selector.
  140. addEvent(AxisClass, 'zoom', function (e) {
  141. var axis = this;
  142. var chart = axis.chart;
  143. var chartOptions = chart.options;
  144. var navigator = chartOptions.navigator;
  145. var navigatorAxis = axis.navigatorAxis;
  146. var pinchType = chartOptions.chart.pinchType;
  147. var rangeSelector = chartOptions.rangeSelector;
  148. var zoomType = chartOptions.chart.zoomType;
  149. var previousZoom;
  150. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  151. (rangeSelector && rangeSelector.enabled))) {
  152. // For y only zooming, ignore the X axis completely
  153. if (zoomType === 'y') {
  154. e.zoomed = false;
  155. // For xy zooming, record the state of the zoom before zoom
  156. // selection, then when the reset button is pressed, revert to
  157. // this state. This should apply only if the chart is
  158. // initialized with a range (#6612), otherwise zoom all the way
  159. // out.
  160. }
  161. else if (((!isTouchDevice && zoomType === 'xy') ||
  162. (isTouchDevice && pinchType === 'xy')) &&
  163. axis.options.range) {
  164. previousZoom = navigatorAxis.previousZoom;
  165. if (defined(e.newMin)) {
  166. navigatorAxis.previousZoom = [axis.min, axis.max];
  167. }
  168. else if (previousZoom) {
  169. e.newMin = previousZoom[0];
  170. e.newMax = previousZoom[1];
  171. navigatorAxis.previousZoom = void 0;
  172. }
  173. }
  174. }
  175. if (typeof e.zoomed !== 'undefined') {
  176. e.preventDefault();
  177. }
  178. });
  179. /* eslint-enable no-invalid-this */
  180. };
  181. /* *
  182. *
  183. * Static Properties
  184. *
  185. * */
  186. /**
  187. * @private
  188. */
  189. NavigatorAxis.AdditionsClass = NavigatorAxisAdditions;
  190. return NavigatorAxis;
  191. }());
  192. return NavigatorAxis;
  193. });
  194. _registerModule(_modules, 'parts/ScrollbarAxis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  195. /* *
  196. *
  197. * (c) 2010-2020 Torstein Honsi
  198. *
  199. * License: www.highcharts.com/license
  200. *
  201. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  202. *
  203. * */
  204. var addEvent = U.addEvent, defined = U.defined, pick = U.pick;
  205. /* eslint-disable no-invalid-this, valid-jsdoc */
  206. /**
  207. * Creates scrollbars if enabled.
  208. *
  209. * @private
  210. */
  211. var ScrollbarAxis = /** @class */ (function () {
  212. function ScrollbarAxis() {
  213. }
  214. /**
  215. * Attaches to axis events to create scrollbars if enabled.
  216. *
  217. * @private
  218. *
  219. * @param AxisClass
  220. * Axis class to extend.
  221. *
  222. * @param ScrollbarClass
  223. * Scrollbar class to use.
  224. */
  225. ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) {
  226. // Wrap axis initialization and create scrollbar if enabled:
  227. addEvent(AxisClass, 'afterInit', function () {
  228. var axis = this;
  229. if (axis.options &&
  230. axis.options.scrollbar &&
  231. axis.options.scrollbar.enabled) {
  232. // Predefined options:
  233. axis.options.scrollbar.vertical = !axis.horiz;
  234. axis.options.startOnTick = axis.options.endOnTick = false;
  235. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  236. addEvent(axis.scrollbar, 'changed', function (e) {
  237. var axisMin = pick(axis.options && axis.options.min, axis.min), axisMax = pick(axis.options && axis.options.max, axis.max), unitedMin = defined(axis.dataMin) ?
  238. Math.min(axisMin, axis.min, axis.dataMin) : axisMin, unitedMax = defined(axis.dataMax) ?
  239. Math.max(axisMax, axis.max, axis.dataMax) : axisMax, range = unitedMax - unitedMin, to, from;
  240. // #12834, scroll when show/hide series, wrong extremes
  241. if (!defined(axisMin) || !defined(axisMax)) {
  242. return;
  243. }
  244. if ((axis.horiz && !axis.reversed) ||
  245. (!axis.horiz && axis.reversed)) {
  246. to = unitedMin + range * this.to;
  247. from = unitedMin + range * this.from;
  248. }
  249. else {
  250. // y-values in browser are reversed, but this also
  251. // applies for reversed horizontal axis:
  252. to = unitedMin + range * (1 - this.from);
  253. from = unitedMin + range * (1 - this.to);
  254. }
  255. if (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  256. // Mouseup always should change extremes
  257. e.DOMType === 'mouseup' ||
  258. // Internal events
  259. !defined(e.DOMType)) {
  260. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove', e);
  261. }
  262. else {
  263. // When live redraw is disabled, don't change extremes
  264. // Only change the position of the scollbar thumb
  265. this.setRange(this.from, this.to);
  266. }
  267. });
  268. }
  269. });
  270. // Wrap rendering axis, and update scrollbar if one is created:
  271. addEvent(AxisClass, 'afterRender', function () {
  272. var axis = this, scrollMin = Math.min(pick(axis.options.min, axis.min), axis.min, pick(axis.dataMin, axis.min) // #6930
  273. ), scrollMax = Math.max(pick(axis.options.max, axis.max), axis.max, pick(axis.dataMax, axis.max) // #6930
  274. ), scrollbar = axis.scrollbar, offset = axis.axisTitleMargin + (axis.titleOffset || 0), scrollbarsOffsets = axis.chart.scrollbarsOffsets, axisMargin = axis.options.margin || 0, offsetsIndex, from, to;
  275. if (scrollbar) {
  276. if (axis.horiz) {
  277. // Reserve space for labels/title
  278. if (!axis.opposite) {
  279. scrollbarsOffsets[1] += offset;
  280. }
  281. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  282. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  283. // Next scrollbar should reserve space for margin (if set)
  284. if (!axis.opposite) {
  285. scrollbarsOffsets[1] += axisMargin;
  286. }
  287. offsetsIndex = 1;
  288. }
  289. else {
  290. // Reserve space for labels/title
  291. if (axis.opposite) {
  292. scrollbarsOffsets[0] += offset;
  293. }
  294. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  295. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  296. // Next scrollbar should reserve space for margin (if set)
  297. if (axis.opposite) {
  298. scrollbarsOffsets[0] += axisMargin;
  299. }
  300. offsetsIndex = 0;
  301. }
  302. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  303. scrollbar.options.margin;
  304. if (isNaN(scrollMin) ||
  305. isNaN(scrollMax) ||
  306. !defined(axis.min) ||
  307. !defined(axis.max) ||
  308. axis.min === axis.max // #10733
  309. ) {
  310. // default action: when extremes are the same or there is
  311. // not extremes on the axis, but scrollbar exists, make it
  312. // full size
  313. scrollbar.setRange(0, 1);
  314. }
  315. else {
  316. from =
  317. (axis.min - scrollMin) / (scrollMax - scrollMin);
  318. to =
  319. (axis.max - scrollMin) / (scrollMax - scrollMin);
  320. if ((axis.horiz && !axis.reversed) ||
  321. (!axis.horiz && axis.reversed)) {
  322. scrollbar.setRange(from, to);
  323. }
  324. else {
  325. // inverse vertical axis
  326. scrollbar.setRange(1 - to, 1 - from);
  327. }
  328. }
  329. }
  330. });
  331. // Make space for a scrollbar:
  332. addEvent(AxisClass, 'afterGetOffset', function () {
  333. var axis = this, index = axis.horiz ? 2 : 1, scrollbar = axis.scrollbar;
  334. if (scrollbar) {
  335. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  336. axis.chart.axisOffset[index] +=
  337. scrollbar.size + scrollbar.options.margin;
  338. }
  339. });
  340. };
  341. return ScrollbarAxis;
  342. }());
  343. return ScrollbarAxis;
  344. });
  345. _registerModule(_modules, 'parts/Scrollbar.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/ScrollbarAxis.js'], _modules['parts/Utilities.js'], _modules['parts/Options.js']], function (Axis, H, ScrollbarAxis, U, O) {
  346. /* *
  347. *
  348. * (c) 2010-2020 Torstein Honsi
  349. *
  350. * License: www.highcharts.com/license
  351. *
  352. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  353. *
  354. * */
  355. var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, fireEvent = U.fireEvent, merge = U.merge, pick = U.pick, removeEvent = U.removeEvent;
  356. var defaultOptions = O.defaultOptions;
  357. var hasTouch = H.hasTouch, isTouchDevice = H.isTouchDevice;
  358. /**
  359. * When we have vertical scrollbar, rifles and arrow in buttons should be
  360. * rotated. The same method is used in Navigator's handles, to rotate them.
  361. *
  362. * @function Highcharts.swapXY
  363. *
  364. * @param {Highcharts.SVGPathArray} path
  365. * Path to be rotated.
  366. *
  367. * @param {boolean} [vertical]
  368. * If vertical scrollbar, swap x-y values.
  369. *
  370. * @return {Highcharts.SVGPathArray}
  371. * Rotated path.
  372. *
  373. * @requires modules/stock
  374. */
  375. var swapXY = H.swapXY = function (path, vertical) {
  376. if (vertical) {
  377. path.forEach(function (seg) {
  378. var len = seg.length;
  379. var temp;
  380. for (var i = 0; i < len; i += 2) {
  381. temp = seg[i + 1];
  382. if (typeof temp === 'number') {
  383. seg[i + 1] = seg[i + 2];
  384. seg[i + 2] = temp;
  385. }
  386. }
  387. });
  388. }
  389. return path;
  390. };
  391. /* eslint-disable no-invalid-this, valid-jsdoc */
  392. /**
  393. * A reusable scrollbar, internally used in Highstock's navigator and optionally
  394. * on individual axes.
  395. *
  396. * @private
  397. * @class
  398. * @name Highcharts.Scrollbar
  399. * @param {Highcharts.SVGRenderer} renderer
  400. * @param {Highcharts.ScrollbarOptions} options
  401. * @param {Highcharts.Chart} chart
  402. */
  403. var Scrollbar = /** @class */ (function () {
  404. /* *
  405. *
  406. * Constructors
  407. *
  408. * */
  409. function Scrollbar(renderer, options, chart) {
  410. /* *
  411. *
  412. * Properties
  413. *
  414. * */
  415. this._events = [];
  416. this.chartX = 0;
  417. this.chartY = 0;
  418. this.from = 0;
  419. this.group = void 0;
  420. this.scrollbar = void 0;
  421. this.scrollbarButtons = [];
  422. this.scrollbarGroup = void 0;
  423. this.scrollbarLeft = 0;
  424. this.scrollbarRifles = void 0;
  425. this.scrollbarStrokeWidth = 1;
  426. this.scrollbarTop = 0;
  427. this.size = 0;
  428. this.to = 0;
  429. this.track = void 0;
  430. this.trackBorderWidth = 1;
  431. this.userOptions = {};
  432. this.x = 0;
  433. this.y = 0;
  434. this.chart = chart;
  435. this.options = options;
  436. this.renderer = chart.renderer;
  437. this.init(renderer, options, chart);
  438. }
  439. /* *
  440. *
  441. * Functions
  442. *
  443. * */
  444. /**
  445. * Set up the mouse and touch events for the Scrollbar
  446. *
  447. * @private
  448. * @function Highcharts.Scrollbar#addEvents
  449. * @return {void}
  450. */
  451. Scrollbar.prototype.addEvents = function () {
  452. var buttonsOrder = this.options.inverted ? [1, 0] : [0, 1], buttons = this.scrollbarButtons, bar = this.scrollbarGroup.element, track = this.track.element, mouseDownHandler = this.mouseDownHandler.bind(this), mouseMoveHandler = this.mouseMoveHandler.bind(this), mouseUpHandler = this.mouseUpHandler.bind(this), _events;
  453. // Mouse events
  454. _events = [
  455. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)],
  456. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)],
  457. [track, 'click', this.trackClick.bind(this)],
  458. [bar, 'mousedown', mouseDownHandler],
  459. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  460. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  461. ];
  462. // Touch events
  463. if (hasTouch) {
  464. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  465. }
  466. // Add them all
  467. _events.forEach(function (args) {
  468. addEvent.apply(null, args);
  469. });
  470. this._events = _events;
  471. };
  472. Scrollbar.prototype.buttonToMaxClick = function (e) {
  473. var scroller = this;
  474. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  475. scroller.updatePosition(scroller.from + range, scroller.to + range);
  476. fireEvent(scroller, 'changed', {
  477. from: scroller.from,
  478. to: scroller.to,
  479. trigger: 'scrollbar',
  480. DOMEvent: e
  481. });
  482. };
  483. Scrollbar.prototype.buttonToMinClick = function (e) {
  484. var scroller = this;
  485. var range = correctFloat(scroller.to - scroller.from) *
  486. pick(scroller.options.step, 0.2);
  487. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  488. fireEvent(scroller, 'changed', {
  489. from: scroller.from,
  490. to: scroller.to,
  491. trigger: 'scrollbar',
  492. DOMEvent: e
  493. });
  494. };
  495. /**
  496. * Get normalized (0-1) cursor position over the scrollbar
  497. *
  498. * @private
  499. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  500. *
  501. * @param {*} normalizedEvent
  502. * normalized event, with chartX and chartY values
  503. *
  504. * @return {Highcharts.Dictionary<number>}
  505. * Local position {chartX, chartY}
  506. */
  507. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  508. var scroller = this, options = scroller.options, minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  509. options.minWidth :
  510. 0; // minWidth distorts translation
  511. return {
  512. chartX: (normalizedEvent.chartX - scroller.x -
  513. scroller.xOffset) /
  514. (scroller.barWidth - minWidthDifference),
  515. chartY: (normalizedEvent.chartY - scroller.y -
  516. scroller.yOffset) /
  517. (scroller.barWidth - minWidthDifference)
  518. };
  519. };
  520. /**
  521. * Destroys allocated elements.
  522. *
  523. * @private
  524. * @function Highcharts.Scrollbar#destroy
  525. * @return {void}
  526. */
  527. Scrollbar.prototype.destroy = function () {
  528. var scroller = this.chart.scroller;
  529. // Disconnect events added in addEvents
  530. this.removeEvents();
  531. // Destroy properties
  532. [
  533. 'track',
  534. 'scrollbarRifles',
  535. 'scrollbar',
  536. 'scrollbarGroup',
  537. 'group'
  538. ].forEach(function (prop) {
  539. if (this[prop] && this[prop].destroy) {
  540. this[prop] = this[prop].destroy();
  541. }
  542. }, this);
  543. // #6421, chart may have more scrollbars
  544. if (scroller && this === scroller.scrollbar) {
  545. scroller.scrollbar = null;
  546. // Destroy elements in collection
  547. destroyObjectProperties(scroller.scrollbarButtons);
  548. }
  549. };
  550. /**
  551. * Draw the scrollbar buttons with arrows
  552. *
  553. * @private
  554. * @function Highcharts.Scrollbar#drawScrollbarButton
  555. * @param {number} index
  556. * 0 is left, 1 is right
  557. * @return {void}
  558. */
  559. Scrollbar.prototype.drawScrollbarButton = function (index) {
  560. var scroller = this, renderer = scroller.renderer, scrollbarButtons = scroller.scrollbarButtons, options = scroller.options, size = scroller.size, group, tempElem;
  561. group = renderer.g().add(scroller.group);
  562. scrollbarButtons.push(group);
  563. // Create a rectangle for the scrollbar button
  564. tempElem = renderer.rect()
  565. .addClass('highcharts-scrollbar-button')
  566. .add(group);
  567. // Presentational attributes
  568. if (!this.chart.styledMode) {
  569. tempElem.attr({
  570. stroke: options.buttonBorderColor,
  571. 'stroke-width': options.buttonBorderWidth,
  572. fill: options.buttonBackgroundColor
  573. });
  574. }
  575. // Place the rectangle based on the rendered stroke width
  576. tempElem.attr(tempElem.crisp({
  577. x: -0.5,
  578. y: -0.5,
  579. width: size + 1,
  580. height: size + 1,
  581. r: options.buttonBorderRadius
  582. }, tempElem.strokeWidth()));
  583. // Button arrow
  584. tempElem = renderer
  585. .path(swapXY([[
  586. 'M',
  587. size / 2 + (index ? -1 : 1),
  588. size / 2 - 3
  589. ], [
  590. 'L',
  591. size / 2 + (index ? -1 : 1),
  592. size / 2 + 3
  593. ], [
  594. 'L',
  595. size / 2 + (index ? 2 : -2),
  596. size / 2
  597. ]], options.vertical))
  598. .addClass('highcharts-scrollbar-arrow')
  599. .add(scrollbarButtons[index]);
  600. if (!this.chart.styledMode) {
  601. tempElem.attr({
  602. fill: options.buttonArrowColor
  603. });
  604. }
  605. };
  606. /**
  607. * @private
  608. * @function Highcharts.Scrollbar#init
  609. * @param {Highcharts.SVGRenderer} renderer
  610. * @param {Highcharts.ScrollbarOptions} options
  611. * @param {Highcharts.Chart} chart
  612. */
  613. Scrollbar.prototype.init = function (renderer, options, chart) {
  614. this.scrollbarButtons = [];
  615. this.renderer = renderer;
  616. this.userOptions = options;
  617. this.options = merge(Scrollbar.defaultOptions, options);
  618. this.chart = chart;
  619. // backward compatibility
  620. this.size = pick(this.options.size, this.options.height);
  621. // Init
  622. if (options.enabled) {
  623. this.render();
  624. this.addEvents();
  625. }
  626. };
  627. Scrollbar.prototype.mouseDownHandler = function (e) {
  628. var scroller = this;
  629. var normalizedEvent = scroller.chart.pointer.normalize(e), mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  630. scroller.chartX = mousePosition.chartX;
  631. scroller.chartY = mousePosition.chartY;
  632. scroller.initPositions = [scroller.from, scroller.to];
  633. scroller.grabbedCenter = true;
  634. };
  635. /**
  636. * Event handler for the mouse move event.
  637. * @private
  638. */
  639. Scrollbar.prototype.mouseMoveHandler = function (e) {
  640. var scroller = this;
  641. var normalizedEvent = scroller.chart.pointer.normalize(e), options = scroller.options, direction = options.vertical ? 'chartY' : 'chartX', initPositions = scroller.initPositions || [], scrollPosition, chartPosition, change;
  642. // In iOS, a mousemove event with e.pageX === 0 is fired when
  643. // holding the finger down in the center of the scrollbar. This
  644. // should be ignored.
  645. if (scroller.grabbedCenter &&
  646. // #4696, scrollbar failed on Android
  647. (!e.touches || e.touches[0][direction] !== 0)) {
  648. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  649. scrollPosition = scroller[direction];
  650. change = chartPosition - scrollPosition;
  651. scroller.hasDragged = true;
  652. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  653. if (scroller.hasDragged) {
  654. fireEvent(scroller, 'changed', {
  655. from: scroller.from,
  656. to: scroller.to,
  657. trigger: 'scrollbar',
  658. DOMType: e.type,
  659. DOMEvent: e
  660. });
  661. }
  662. }
  663. };
  664. /**
  665. * Event handler for the mouse up event.
  666. * @private
  667. */
  668. Scrollbar.prototype.mouseUpHandler = function (e) {
  669. var scroller = this;
  670. if (scroller.hasDragged) {
  671. fireEvent(scroller, 'changed', {
  672. from: scroller.from,
  673. to: scroller.to,
  674. trigger: 'scrollbar',
  675. DOMType: e.type,
  676. DOMEvent: e
  677. });
  678. }
  679. scroller.grabbedCenter =
  680. scroller.hasDragged =
  681. scroller.chartX =
  682. scroller.chartY = null;
  683. };
  684. /**
  685. * Position the scrollbar, method called from a parent with defined
  686. * dimensions.
  687. *
  688. * @private
  689. * @function Highcharts.Scrollbar#position
  690. * @param {number} x
  691. * x-position on the chart
  692. * @param {number} y
  693. * y-position on the chart
  694. * @param {number} width
  695. * width of the scrollbar
  696. * @param {number} height
  697. * height of the scorllbar
  698. * @return {void}
  699. */
  700. Scrollbar.prototype.position = function (x, y, width, height) {
  701. var scroller = this, options = scroller.options, vertical = options.vertical, xOffset = height, yOffset = 0, method = scroller.rendered ? 'animate' : 'attr';
  702. scroller.x = x;
  703. scroller.y = y + this.trackBorderWidth;
  704. scroller.width = width; // width with buttons
  705. scroller.height = height;
  706. scroller.xOffset = xOffset;
  707. scroller.yOffset = yOffset;
  708. // If Scrollbar is a vertical type, swap options:
  709. if (vertical) {
  710. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  711. scroller.xOffset = xOffset = 0;
  712. scroller.barWidth = height - width * 2; // width without buttons
  713. scroller.x = x = x + scroller.options.margin;
  714. }
  715. else {
  716. scroller.height = scroller.xOffset = height = xOffset =
  717. scroller.size;
  718. scroller.barWidth = width - height * 2; // width without buttons
  719. scroller.y = scroller.y + scroller.options.margin;
  720. }
  721. // Set general position for a group:
  722. scroller.group[method]({
  723. translateX: x,
  724. translateY: scroller.y
  725. });
  726. // Resize background/track:
  727. scroller.track[method]({
  728. width: width,
  729. height: height
  730. });
  731. // Move right/bottom button ot it's place:
  732. scroller.scrollbarButtons[1][method]({
  733. translateX: vertical ? 0 : width - xOffset,
  734. translateY: vertical ? height - yOffset : 0
  735. });
  736. };
  737. /**
  738. * Removes the event handlers attached previously with addEvents.
  739. *
  740. * @private
  741. * @function Highcharts.Scrollbar#removeEvents
  742. * @return {void}
  743. */
  744. Scrollbar.prototype.removeEvents = function () {
  745. this._events.forEach(function (args) {
  746. removeEvent.apply(null, args);
  747. });
  748. this._events.length = 0;
  749. };
  750. /**
  751. * Render scrollbar with all required items.
  752. *
  753. * @private
  754. * @function Highcharts.Scrollbar#render
  755. */
  756. Scrollbar.prototype.render = function () {
  757. var scroller = this, renderer = scroller.renderer, options = scroller.options, size = scroller.size, styledMode = this.chart.styledMode, group;
  758. // Draw the scrollbar group
  759. scroller.group = group = renderer.g('scrollbar').attr({
  760. zIndex: options.zIndex,
  761. translateY: -99999
  762. }).add();
  763. // Draw the scrollbar track:
  764. scroller.track = renderer.rect()
  765. .addClass('highcharts-scrollbar-track')
  766. .attr({
  767. x: 0,
  768. r: options.trackBorderRadius || 0,
  769. height: size,
  770. width: size
  771. }).add(group);
  772. if (!styledMode) {
  773. scroller.track.attr({
  774. fill: options.trackBackgroundColor,
  775. stroke: options.trackBorderColor,
  776. 'stroke-width': options.trackBorderWidth
  777. });
  778. }
  779. this.trackBorderWidth = scroller.track.strokeWidth();
  780. scroller.track.attr({
  781. y: -this.trackBorderWidth % 2 / 2
  782. });
  783. // Draw the scrollbar itself
  784. scroller.scrollbarGroup = renderer.g().add(group);
  785. scroller.scrollbar = renderer.rect()
  786. .addClass('highcharts-scrollbar-thumb')
  787. .attr({
  788. height: size,
  789. width: size,
  790. r: options.barBorderRadius || 0
  791. }).add(scroller.scrollbarGroup);
  792. scroller.scrollbarRifles = renderer
  793. .path(swapXY([
  794. ['M', -3, size / 4],
  795. ['L', -3, 2 * size / 3],
  796. ['M', 0, size / 4],
  797. ['L', 0, 2 * size / 3],
  798. ['M', 3, size / 4],
  799. ['L', 3, 2 * size / 3]
  800. ], options.vertical))
  801. .addClass('highcharts-scrollbar-rifles')
  802. .add(scroller.scrollbarGroup);
  803. if (!styledMode) {
  804. scroller.scrollbar.attr({
  805. fill: options.barBackgroundColor,
  806. stroke: options.barBorderColor,
  807. 'stroke-width': options.barBorderWidth
  808. });
  809. scroller.scrollbarRifles.attr({
  810. stroke: options.rifleColor,
  811. 'stroke-width': 1
  812. });
  813. }
  814. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  815. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  816. // Draw the buttons:
  817. scroller.drawScrollbarButton(0);
  818. scroller.drawScrollbarButton(1);
  819. };
  820. /**
  821. * Set scrollbar size, with a given scale.
  822. *
  823. * @private
  824. * @function Highcharts.Scrollbar#setRange
  825. * @param {number} from
  826. * scale (0-1) where bar should start
  827. * @param {number} to
  828. * scale (0-1) where bar should end
  829. * @return {void}
  830. */
  831. Scrollbar.prototype.setRange = function (from, to) {
  832. var scroller = this, options = scroller.options, vertical = options.vertical, minWidth = options.minWidth, fullWidth = scroller.barWidth, fromPX, toPX, newPos, newSize, newRiflesPos, method = (this.rendered &&
  833. !this.hasDragged &&
  834. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  835. if (!defined(fullWidth)) {
  836. return;
  837. }
  838. from = Math.max(from, 0);
  839. fromPX = Math.ceil(fullWidth * from);
  840. toPX = fullWidth * Math.min(to, 1);
  841. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  842. // We need to recalculate position, if minWidth is used
  843. if (newSize < minWidth) {
  844. fromPX = (fullWidth - minWidth + newSize) * from;
  845. newSize = minWidth;
  846. }
  847. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  848. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  849. // Store current position:
  850. scroller.from = from;
  851. scroller.to = to;
  852. if (!vertical) {
  853. scroller.scrollbarGroup[method]({
  854. translateX: newPos
  855. });
  856. scroller.scrollbar[method]({
  857. width: newSize
  858. });
  859. scroller.scrollbarRifles[method]({
  860. translateX: newRiflesPos
  861. });
  862. scroller.scrollbarLeft = newPos;
  863. scroller.scrollbarTop = 0;
  864. }
  865. else {
  866. scroller.scrollbarGroup[method]({
  867. translateY: newPos
  868. });
  869. scroller.scrollbar[method]({
  870. height: newSize
  871. });
  872. scroller.scrollbarRifles[method]({
  873. translateY: newRiflesPos
  874. });
  875. scroller.scrollbarTop = newPos;
  876. scroller.scrollbarLeft = 0;
  877. }
  878. if (newSize <= 12) {
  879. scroller.scrollbarRifles.hide();
  880. }
  881. else {
  882. scroller.scrollbarRifles.show(true);
  883. }
  884. // Show or hide the scrollbar based on the showFull setting
  885. if (options.showFull === false) {
  886. if (from <= 0 && to >= 1) {
  887. scroller.group.hide();
  888. }
  889. else {
  890. scroller.group.show();
  891. }
  892. }
  893. scroller.rendered = true;
  894. };
  895. Scrollbar.prototype.trackClick = function (e) {
  896. var scroller = this;
  897. var normalizedEvent = scroller.chart.pointer.normalize(e), range = scroller.to - scroller.from, top = scroller.y + scroller.scrollbarTop, left = scroller.x + scroller.scrollbarLeft;
  898. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  899. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  900. // On the top or on the left side of the track:
  901. scroller.updatePosition(scroller.from + range, scroller.to + range);
  902. }
  903. else {
  904. // On the bottom or the right side of the track:
  905. scroller.updatePosition(scroller.from - range, scroller.to - range);
  906. }
  907. fireEvent(scroller, 'changed', {
  908. from: scroller.from,
  909. to: scroller.to,
  910. trigger: 'scrollbar',
  911. DOMEvent: e
  912. });
  913. };
  914. /**
  915. * Update the scrollbar with new options
  916. *
  917. * @private
  918. * @function Highcharts.Scrollbar#update
  919. * @param {Highcharts.ScrollbarOptions} options
  920. * @return {void}
  921. */
  922. Scrollbar.prototype.update = function (options) {
  923. this.destroy();
  924. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  925. };
  926. /**
  927. * Update position option in the Scrollbar, with normalized 0-1 scale
  928. *
  929. * @private
  930. * @function Highcharts.Scrollbar#updatePosition
  931. * @param {number} from
  932. * @param {number} to
  933. * @return {void}
  934. */
  935. Scrollbar.prototype.updatePosition = function (from, to) {
  936. if (to > 1) {
  937. from = correctFloat(1 - correctFloat(to - from));
  938. to = 1;
  939. }
  940. if (from < 0) {
  941. to = correctFloat(to - from);
  942. from = 0;
  943. }
  944. this.from = from;
  945. this.to = to;
  946. };
  947. /* *
  948. *
  949. * Static Properties
  950. *
  951. * */
  952. /**
  953. *
  954. * The scrollbar is a means of panning over the X axis of a stock chart.
  955. * Scrollbars can also be applied to other types of axes.
  956. *
  957. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  958. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  959. * is especially suitable for simpler cartesian charts on mobile.
  960. *
  961. * In styled mode, all the presentational options for the
  962. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  963. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  964. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  965. *
  966. * @sample stock/yaxis/inverted-bar-scrollbar/
  967. * A scrollbar on a simple bar chart
  968. *
  969. * @product highstock gantt
  970. * @optionparent scrollbar
  971. *
  972. * @private
  973. */
  974. Scrollbar.defaultOptions = {
  975. /**
  976. * The height of the scrollbar. The height also applies to the width
  977. * of the scroll arrows so that they are always squares. Defaults to
  978. * 20 for touch devices and 14 for mouse devices.
  979. *
  980. * @sample stock/scrollbar/height/
  981. * A 30px scrollbar
  982. *
  983. * @type {number}
  984. * @default 20/14
  985. */
  986. height: isTouchDevice ? 20 : 14,
  987. /**
  988. * The border rounding radius of the bar.
  989. *
  990. * @sample stock/scrollbar/style/
  991. * Scrollbar styling
  992. */
  993. barBorderRadius: 0,
  994. /**
  995. * The corner radius of the scrollbar buttons.
  996. *
  997. * @sample stock/scrollbar/style/
  998. * Scrollbar styling
  999. */
  1000. buttonBorderRadius: 0,
  1001. /**
  1002. * Enable or disable the scrollbar.
  1003. *
  1004. * @sample stock/scrollbar/enabled/
  1005. * Disable the scrollbar, only use navigator
  1006. *
  1007. * @type {boolean}
  1008. * @default true
  1009. * @apioption scrollbar.enabled
  1010. */
  1011. /**
  1012. * Whether to redraw the main chart as the scrollbar or the navigator
  1013. * zoomed window is moved. Defaults to `true` for modern browsers and
  1014. * `false` for legacy IE browsers as well as mobile devices.
  1015. *
  1016. * @sample stock/scrollbar/liveredraw
  1017. * Setting live redraw to false
  1018. *
  1019. * @type {boolean}
  1020. * @since 1.3
  1021. */
  1022. liveRedraw: void 0,
  1023. /**
  1024. * The margin between the scrollbar and its axis when the scrollbar is
  1025. * applied directly to an axis.
  1026. */
  1027. margin: 10,
  1028. /**
  1029. * The minimum width of the scrollbar.
  1030. *
  1031. * @since 1.2.5
  1032. */
  1033. minWidth: 6,
  1034. /**
  1035. * Whether to show or hide the scrollbar when the scrolled content is
  1036. * zoomed out to it full extent.
  1037. *
  1038. * @type {boolean}
  1039. * @default true
  1040. * @apioption scrollbar.showFull
  1041. */
  1042. step: 0.2,
  1043. /**
  1044. * The z index of the scrollbar group.
  1045. */
  1046. zIndex: 3,
  1047. /**
  1048. * The background color of the scrollbar itself.
  1049. *
  1050. * @sample stock/scrollbar/style/
  1051. * Scrollbar styling
  1052. *
  1053. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1054. */
  1055. barBackgroundColor: '#cccccc',
  1056. /**
  1057. * The width of the bar's border.
  1058. *
  1059. * @sample stock/scrollbar/style/
  1060. * Scrollbar styling
  1061. */
  1062. barBorderWidth: 1,
  1063. /**
  1064. * The color of the scrollbar's border.
  1065. *
  1066. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1067. */
  1068. barBorderColor: '#cccccc',
  1069. /**
  1070. * The color of the small arrow inside the scrollbar buttons.
  1071. *
  1072. * @sample stock/scrollbar/style/
  1073. * Scrollbar styling
  1074. *
  1075. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1076. */
  1077. buttonArrowColor: '#333333',
  1078. /**
  1079. * The color of scrollbar buttons.
  1080. *
  1081. * @sample stock/scrollbar/style/
  1082. * Scrollbar styling
  1083. *
  1084. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1085. */
  1086. buttonBackgroundColor: '#e6e6e6',
  1087. /**
  1088. * The color of the border of the scrollbar buttons.
  1089. *
  1090. * @sample stock/scrollbar/style/
  1091. * Scrollbar styling
  1092. *
  1093. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1094. */
  1095. buttonBorderColor: '#cccccc',
  1096. /**
  1097. * The border width of the scrollbar buttons.
  1098. *
  1099. * @sample stock/scrollbar/style/
  1100. * Scrollbar styling
  1101. */
  1102. buttonBorderWidth: 1,
  1103. /**
  1104. * The color of the small rifles in the middle of the scrollbar.
  1105. *
  1106. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1107. */
  1108. rifleColor: '#333333',
  1109. /**
  1110. * The color of the track background.
  1111. *
  1112. * @sample stock/scrollbar/style/
  1113. * Scrollbar styling
  1114. *
  1115. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1116. */
  1117. trackBackgroundColor: '#f2f2f2',
  1118. /**
  1119. * The color of the border of the scrollbar track.
  1120. *
  1121. * @sample stock/scrollbar/style/
  1122. * Scrollbar styling
  1123. *
  1124. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1125. */
  1126. trackBorderColor: '#f2f2f2',
  1127. /**
  1128. * The corner radius of the border of the scrollbar track.
  1129. *
  1130. * @sample stock/scrollbar/style/
  1131. * Scrollbar styling
  1132. *
  1133. * @type {number}
  1134. * @default 0
  1135. * @apioption scrollbar.trackBorderRadius
  1136. */
  1137. /**
  1138. * The width of the border of the scrollbar track.
  1139. *
  1140. * @sample stock/scrollbar/style/
  1141. * Scrollbar styling
  1142. */
  1143. trackBorderWidth: 1
  1144. };
  1145. return Scrollbar;
  1146. }());
  1147. if (!H.Scrollbar) {
  1148. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  1149. H.Scrollbar = Scrollbar;
  1150. ScrollbarAxis.compose(Axis, Scrollbar);
  1151. }
  1152. return H.Scrollbar;
  1153. });
  1154. _registerModule(_modules, 'parts/Navigator.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Color.js'], _modules['parts/Globals.js'], _modules['parts/NavigatorAxis.js'], _modules['parts/Options.js'], _modules['parts/Scrollbar.js'], _modules['parts/Utilities.js']], function (Axis, Chart, Color, H, NavigatorAxis, O, Scrollbar, U) {
  1155. /* *
  1156. *
  1157. * (c) 2010-2020 Torstein Honsi
  1158. *
  1159. * License: www.highcharts.com/license
  1160. *
  1161. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1162. *
  1163. * */
  1164. var color = Color.parse;
  1165. var defaultOptions = O.defaultOptions;
  1166. var addEvent = U.addEvent, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, find = U.find, isArray = U.isArray, isNumber = U.isNumber, merge = U.merge, pick = U.pick, removeEvent = U.removeEvent, splat = U.splat;
  1167. var hasTouch = H.hasTouch, isTouchDevice = H.isTouchDevice, Series = H.Series, seriesTypes = H.seriesTypes, defaultSeriesType,
  1168. // Finding the min or max of a set of variables where we don't know if they
  1169. // are defined, is a pattern that is repeated several places in Highcharts.
  1170. // Consider making this a global utility method.
  1171. numExt = function (extreme) {
  1172. var args = [];
  1173. for (var _i = 1; _i < arguments.length; _i++) {
  1174. args[_i - 1] = arguments[_i];
  1175. }
  1176. var numbers = [].filter.call(args, isNumber);
  1177. if (numbers.length) {
  1178. return Math[extreme].apply(0, numbers);
  1179. }
  1180. };
  1181. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  1182. 'line' :
  1183. 'areaspline';
  1184. extend(defaultOptions, {
  1185. /**
  1186. * Maximum range which can be set using the navigator's handles.
  1187. * Opposite of [xAxis.minRange](#xAxis.minRange).
  1188. *
  1189. * @sample {highstock} stock/navigator/maxrange/
  1190. * Defined max and min range
  1191. *
  1192. * @type {number}
  1193. * @since 6.0.0
  1194. * @product highstock gantt
  1195. * @apioption xAxis.maxRange
  1196. */
  1197. /**
  1198. * The navigator is a small series below the main series, displaying
  1199. * a view of the entire data set. It provides tools to zoom in and
  1200. * out on parts of the data as well as panning across the dataset.
  1201. *
  1202. * @product highstock gantt
  1203. * @optionparent navigator
  1204. */
  1205. navigator: {
  1206. /**
  1207. * Whether the navigator and scrollbar should adapt to updated data
  1208. * in the base X axis. When loading data async, as in the demo below,
  1209. * this should be `false`. Otherwise new data will trigger navigator
  1210. * redraw, which will cause unwanted looping. In the demo below, the
  1211. * data in the navigator is set only once. On navigating, only the main
  1212. * chart content is updated.
  1213. *
  1214. * @sample {highstock} stock/demo/lazy-loading/
  1215. * Set to false with async data loading
  1216. *
  1217. * @type {boolean}
  1218. * @default true
  1219. * @apioption navigator.adaptToUpdatedData
  1220. */
  1221. /**
  1222. * An integer identifying the index to use for the base series, or a
  1223. * string representing the id of the series.
  1224. *
  1225. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  1226. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  1227. *
  1228. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  1229. *
  1230. * @deprecated
  1231. * @type {number|string}
  1232. * @default 0
  1233. * @apioption navigator.baseSeries
  1234. */
  1235. /**
  1236. * Enable or disable the navigator.
  1237. *
  1238. * @sample {highstock} stock/navigator/enabled/
  1239. * Disable the navigator
  1240. *
  1241. * @type {boolean}
  1242. * @default true
  1243. * @apioption navigator.enabled
  1244. */
  1245. /**
  1246. * When the chart is inverted, whether to draw the navigator on the
  1247. * opposite side.
  1248. *
  1249. * @type {boolean}
  1250. * @default false
  1251. * @since 5.0.8
  1252. * @apioption navigator.opposite
  1253. */
  1254. /**
  1255. * The height of the navigator.
  1256. *
  1257. * @sample {highstock} stock/navigator/height/
  1258. * A higher navigator
  1259. */
  1260. height: 40,
  1261. /**
  1262. * The distance from the nearest element, the X axis or X axis labels.
  1263. *
  1264. * @sample {highstock} stock/navigator/margin/
  1265. * A margin of 2 draws the navigator closer to the X axis labels
  1266. */
  1267. margin: 25,
  1268. /**
  1269. * Whether the mask should be inside the range marking the zoomed
  1270. * range, or outside. In Highstock 1.x it was always `false`.
  1271. *
  1272. * @sample {highstock} stock/navigator/maskinside-false/
  1273. * False, mask outside
  1274. *
  1275. * @since 2.0
  1276. */
  1277. maskInside: true,
  1278. /**
  1279. * Options for the handles for dragging the zoomed area.
  1280. *
  1281. * @sample {highstock} stock/navigator/handles/
  1282. * Colored handles
  1283. */
  1284. handles: {
  1285. /**
  1286. * Width for handles.
  1287. *
  1288. * @sample {highstock} stock/navigator/styled-handles/
  1289. * Styled handles
  1290. *
  1291. * @since 6.0.0
  1292. */
  1293. width: 7,
  1294. /**
  1295. * Height for handles.
  1296. *
  1297. * @sample {highstock} stock/navigator/styled-handles/
  1298. * Styled handles
  1299. *
  1300. * @since 6.0.0
  1301. */
  1302. height: 15,
  1303. /**
  1304. * Array to define shapes of handles. 0-index for left, 1-index for
  1305. * right.
  1306. *
  1307. * Additionally, the URL to a graphic can be given on this form:
  1308. * `url(graphic.png)`. Note that for the image to be applied to
  1309. * exported charts, its URL needs to be accessible by the export
  1310. * server.
  1311. *
  1312. * Custom callbacks for symbol path generation can also be added to
  1313. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  1314. * used by its method name, as shown in the demo.
  1315. *
  1316. * @sample {highstock} stock/navigator/styled-handles/
  1317. * Styled handles
  1318. *
  1319. * @type {Array<string>}
  1320. * @default ["navigator-handle", "navigator-handle"]
  1321. * @since 6.0.0
  1322. */
  1323. symbols: ['navigator-handle', 'navigator-handle'],
  1324. /**
  1325. * Allows to enable/disable handles.
  1326. *
  1327. * @since 6.0.0
  1328. */
  1329. enabled: true,
  1330. /**
  1331. * The width for the handle border and the stripes inside.
  1332. *
  1333. * @sample {highstock} stock/navigator/styled-handles/
  1334. * Styled handles
  1335. *
  1336. * @since 6.0.0
  1337. * @apioption navigator.handles.lineWidth
  1338. */
  1339. lineWidth: 1,
  1340. /**
  1341. * The fill for the handle.
  1342. *
  1343. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1344. */
  1345. backgroundColor: '#f2f2f2',
  1346. /**
  1347. * The stroke for the handle border and the stripes inside.
  1348. *
  1349. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1350. */
  1351. borderColor: '#999999'
  1352. },
  1353. /**
  1354. * The color of the mask covering the areas of the navigator series
  1355. * that are currently not visible in the main series. The default
  1356. * color is bluish with an opacity of 0.3 to see the series below.
  1357. *
  1358. * @see In styled mode, the mask is styled with the
  1359. * `.highcharts-navigator-mask` and
  1360. * `.highcharts-navigator-mask-inside` classes.
  1361. *
  1362. * @sample {highstock} stock/navigator/maskfill/
  1363. * Blue, semi transparent mask
  1364. *
  1365. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1366. * @default rgba(102,133,194,0.3)
  1367. */
  1368. maskFill: color('#6685c2').setOpacity(0.3).get(),
  1369. /**
  1370. * The color of the line marking the currently zoomed area in the
  1371. * navigator.
  1372. *
  1373. * @sample {highstock} stock/navigator/outline/
  1374. * 2px blue outline
  1375. *
  1376. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1377. * @default #cccccc
  1378. */
  1379. outlineColor: '#cccccc',
  1380. /**
  1381. * The width of the line marking the currently zoomed area in the
  1382. * navigator.
  1383. *
  1384. * @see In styled mode, the outline stroke width is set with the
  1385. * `.highcharts-navigator-outline` class.
  1386. *
  1387. * @sample {highstock} stock/navigator/outline/
  1388. * 2px blue outline
  1389. *
  1390. * @type {number}
  1391. */
  1392. outlineWidth: 1,
  1393. /**
  1394. * Options for the navigator series. Available options are the same
  1395. * as any series, documented at [plotOptions](#plotOptions.series)
  1396. * and [series](#series).
  1397. *
  1398. * Unless data is explicitly defined on navigator.series, the data
  1399. * is borrowed from the first series in the chart.
  1400. *
  1401. * Default series options for the navigator series are:
  1402. * ```js
  1403. * series: {
  1404. * type: 'areaspline',
  1405. * fillOpacity: 0.05,
  1406. * dataGrouping: {
  1407. * smoothed: true
  1408. * },
  1409. * lineWidth: 1,
  1410. * marker: {
  1411. * enabled: false
  1412. * }
  1413. * }
  1414. * ```
  1415. *
  1416. * @see In styled mode, the navigator series is styled with the
  1417. * `.highcharts-navigator-series` class.
  1418. *
  1419. * @sample {highstock} stock/navigator/series-data/
  1420. * Using a separate data set for the navigator
  1421. * @sample {highstock} stock/navigator/series/
  1422. * A green navigator series
  1423. *
  1424. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  1425. */
  1426. series: {
  1427. /**
  1428. * The type of the navigator series. Defaults to `areaspline` if
  1429. * defined, otherwise `line`.
  1430. *
  1431. * Heads up:
  1432. * In column-type navigator, zooming is limited to at least one
  1433. * point with its `pointRange`.
  1434. *
  1435. * @sample {highstock} stock/navigator/column/
  1436. * Column type navigator
  1437. *
  1438. * @type {string}
  1439. * @default areaspline
  1440. */
  1441. type: defaultSeriesType,
  1442. /**
  1443. * The fill opacity of the navigator series.
  1444. */
  1445. fillOpacity: 0.05,
  1446. /**
  1447. * The pixel line width of the navigator series.
  1448. */
  1449. lineWidth: 1,
  1450. /**
  1451. * @ignore-option
  1452. */
  1453. compare: null,
  1454. /**
  1455. * Unless data is explicitly defined, the data is borrowed from the
  1456. * first series in the chart.
  1457. *
  1458. * @type {Array<number|Array<number|string|null>|object|null>}
  1459. * @product highstock
  1460. * @apioption navigator.series.data
  1461. */
  1462. /**
  1463. * Data grouping options for the navigator series.
  1464. *
  1465. * @extends plotOptions.series.dataGrouping
  1466. */
  1467. dataGrouping: {
  1468. approximation: 'average',
  1469. enabled: true,
  1470. groupPixelWidth: 2,
  1471. smoothed: true,
  1472. // Day and week differs from plotOptions.series.dataGrouping
  1473. units: [
  1474. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  1475. ['second', [1, 2, 5, 10, 15, 30]],
  1476. ['minute', [1, 2, 5, 10, 15, 30]],
  1477. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  1478. ['day', [1, 2, 3, 4]],
  1479. ['week', [1, 2, 3]],
  1480. ['month', [1, 3, 6]],
  1481. ['year', null]
  1482. ]
  1483. },
  1484. /**
  1485. * Data label options for the navigator series. Data labels are
  1486. * disabled by default on the navigator series.
  1487. *
  1488. * @extends plotOptions.series.dataLabels
  1489. */
  1490. dataLabels: {
  1491. enabled: false,
  1492. zIndex: 2 // #1839
  1493. },
  1494. id: 'highcharts-navigator-series',
  1495. className: 'highcharts-navigator-series',
  1496. /**
  1497. * Sets the fill color of the navigator series.
  1498. *
  1499. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1500. * @apioption navigator.series.color
  1501. */
  1502. /**
  1503. * Line color for the navigator series. Allows setting the color
  1504. * while disallowing the default candlestick setting.
  1505. *
  1506. * @type {Highcharts.ColorString|null}
  1507. */
  1508. lineColor: null,
  1509. marker: {
  1510. enabled: false
  1511. },
  1512. /**
  1513. * Since Highstock v8, default value is the same as default
  1514. * `pointRange` defined for a specific type (e.g. `null` for
  1515. * column type).
  1516. *
  1517. * In Highstock version < 8, defaults to 0.
  1518. *
  1519. * @extends plotOptions.series.pointRange
  1520. * @type {number|null}
  1521. * @apioption navigator.series.pointRange
  1522. */
  1523. /**
  1524. * The threshold option. Setting it to 0 will make the default
  1525. * navigator area series draw its area from the 0 value and up.
  1526. *
  1527. * @type {number|null}
  1528. */
  1529. threshold: null
  1530. },
  1531. /**
  1532. * Options for the navigator X axis. Default series options for the
  1533. * navigator xAxis are:
  1534. * ```js
  1535. * xAxis: {
  1536. * tickWidth: 0,
  1537. * lineWidth: 0,
  1538. * gridLineWidth: 1,
  1539. * tickPixelInterval: 200,
  1540. * labels: {
  1541. * align: 'left',
  1542. * style: {
  1543. * color: '#888'
  1544. * },
  1545. * x: 3,
  1546. * y: -4
  1547. * }
  1548. * }
  1549. * ```
  1550. *
  1551. * @extends xAxis
  1552. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  1553. * showEmpty, maxRange
  1554. */
  1555. xAxis: {
  1556. /**
  1557. * Additional range on the right side of the xAxis. Works similar to
  1558. * xAxis.maxPadding, but value is set in milliseconds.
  1559. * Can be set for both, main xAxis and navigator's xAxis.
  1560. *
  1561. * @since 6.0.0
  1562. */
  1563. overscroll: 0,
  1564. className: 'highcharts-navigator-xaxis',
  1565. tickLength: 0,
  1566. lineWidth: 0,
  1567. gridLineColor: '#e6e6e6',
  1568. gridLineWidth: 1,
  1569. tickPixelInterval: 200,
  1570. labels: {
  1571. align: 'left',
  1572. /**
  1573. * @type {Highcharts.CSSObject}
  1574. */
  1575. style: {
  1576. /** @ignore */
  1577. color: '#999999'
  1578. },
  1579. x: 3,
  1580. y: -4
  1581. },
  1582. crosshair: false
  1583. },
  1584. /**
  1585. * Options for the navigator Y axis. Default series options for the
  1586. * navigator yAxis are:
  1587. * ```js
  1588. * yAxis: {
  1589. * gridLineWidth: 0,
  1590. * startOnTick: false,
  1591. * endOnTick: false,
  1592. * minPadding: 0.1,
  1593. * maxPadding: 0.1,
  1594. * labels: {
  1595. * enabled: false
  1596. * },
  1597. * title: {
  1598. * text: null
  1599. * },
  1600. * tickWidth: 0
  1601. * }
  1602. * ```
  1603. *
  1604. * @extends yAxis
  1605. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  1606. * showEmpty, scrollbar, top, units, maxRange, minLength,
  1607. * maxLength, resize
  1608. */
  1609. yAxis: {
  1610. className: 'highcharts-navigator-yaxis',
  1611. gridLineWidth: 0,
  1612. startOnTick: false,
  1613. endOnTick: false,
  1614. minPadding: 0.1,
  1615. maxPadding: 0.1,
  1616. labels: {
  1617. enabled: false
  1618. },
  1619. crosshair: false,
  1620. title: {
  1621. text: null
  1622. },
  1623. tickLength: 0,
  1624. tickWidth: 0
  1625. }
  1626. }
  1627. });
  1628. /* eslint-disable no-invalid-this, valid-jsdoc */
  1629. /**
  1630. * Draw one of the handles on the side of the zoomed range in the navigator
  1631. *
  1632. * @private
  1633. * @function Highcharts.Renderer#symbols.navigator-handle
  1634. * @param {number} x
  1635. * @param {number} y
  1636. * @param {number} w
  1637. * @param {number} h
  1638. * @param {Highcharts.NavigatorHandlesOptions} options
  1639. * @return {Highcharts.SVGPathArray}
  1640. * Path to be used in a handle
  1641. */
  1642. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  1643. var halfWidth = (options && options.width || 0) / 2, markerPosition = Math.round(halfWidth / 3) + 0.5, height = options && options.height || 0;
  1644. return [
  1645. ['M', -halfWidth - 1, 0.5],
  1646. ['L', halfWidth, 0.5],
  1647. ['L', halfWidth, height + 0.5],
  1648. ['L', -halfWidth - 1, height + 0.5],
  1649. ['L', -halfWidth - 1, 0.5],
  1650. ['M', -markerPosition, 4],
  1651. ['L', -markerPosition, height - 3],
  1652. ['M', markerPosition - 1, 4],
  1653. ['L', markerPosition - 1, height - 3]
  1654. ];
  1655. };
  1656. /**
  1657. * The Navigator class
  1658. *
  1659. * @private
  1660. * @class
  1661. * @name Highcharts.Navigator
  1662. *
  1663. * @param {Highcharts.Chart} chart
  1664. * Chart object
  1665. */
  1666. var Navigator = /** @class */ (function () {
  1667. function Navigator(chart) {
  1668. this.baseSeries = void 0;
  1669. this.chart = void 0;
  1670. this.handles = void 0;
  1671. this.height = void 0;
  1672. this.left = void 0;
  1673. this.navigatorEnabled = void 0;
  1674. this.navigatorGroup = void 0;
  1675. this.navigatorOptions = void 0;
  1676. this.navigatorSeries = void 0;
  1677. this.navigatorSize = void 0;
  1678. this.opposite = void 0;
  1679. this.outline = void 0;
  1680. this.outlineHeight = void 0;
  1681. this.range = void 0;
  1682. this.rendered = void 0;
  1683. this.shades = void 0;
  1684. this.size = void 0;
  1685. this.top = void 0;
  1686. this.xAxis = void 0;
  1687. this.yAxis = void 0;
  1688. this.zoomedMax = void 0;
  1689. this.zoomedMin = void 0;
  1690. this.init(chart);
  1691. }
  1692. /**
  1693. * Draw one of the handles on the side of the zoomed range in the navigator
  1694. *
  1695. * @private
  1696. * @function Highcharts.Navigator#drawHandle
  1697. *
  1698. * @param {number} x
  1699. * The x center for the handle
  1700. *
  1701. * @param {number} index
  1702. * 0 for left and 1 for right
  1703. *
  1704. * @param {boolean|undefined} inverted
  1705. * flag for chart.inverted
  1706. *
  1707. * @param {string} verb
  1708. * use 'animate' or 'attr'
  1709. */
  1710. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  1711. var navigator = this, height = navigator.navigatorOptions.handles.height;
  1712. // Place it
  1713. navigator.handles[index][verb](inverted ? {
  1714. translateX: Math.round(navigator.left + navigator.height / 2),
  1715. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  1716. } : {
  1717. translateX: Math.round(navigator.left + parseInt(x, 10)),
  1718. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  1719. });
  1720. };
  1721. /**
  1722. * Render outline around the zoomed range
  1723. *
  1724. * @private
  1725. * @function Highcharts.Navigator#drawOutline
  1726. *
  1727. * @param {number} zoomedMin
  1728. * in pixels position where zoomed range starts
  1729. *
  1730. * @param {number} zoomedMax
  1731. * in pixels position where zoomed range ends
  1732. *
  1733. * @param {boolean|undefined} inverted
  1734. * flag if chart is inverted
  1735. *
  1736. * @param {string} verb
  1737. * use 'animate' or 'attr'
  1738. */
  1739. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  1740. var navigator = this, maskInside = navigator.navigatorOptions.maskInside, outlineWidth = navigator.outline.strokeWidth(), halfOutline = outlineWidth / 2, outlineCorrection = (outlineWidth % 2) / 2, // #5800
  1741. outlineHeight = navigator.outlineHeight, scrollbarHeight = navigator.scrollbarHeight || 0, navigatorSize = navigator.size, left = navigator.left - scrollbarHeight, navigatorTop = navigator.top, verticalMin, path;
  1742. if (inverted) {
  1743. left -= halfOutline;
  1744. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  1745. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  1746. path = [
  1747. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  1748. ['L', left + outlineHeight, verticalMin],
  1749. ['L', left, verticalMin],
  1750. ['L', left, zoomedMax],
  1751. ['L', left + outlineHeight, zoomedMax],
  1752. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  1753. ];
  1754. if (maskInside) {
  1755. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  1756. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  1757. );
  1758. }
  1759. }
  1760. else {
  1761. zoomedMin += left + scrollbarHeight - outlineCorrection;
  1762. zoomedMax += left + scrollbarHeight - outlineCorrection;
  1763. navigatorTop += halfOutline;
  1764. path = [
  1765. ['M', left, navigatorTop],
  1766. ['L', zoomedMin, navigatorTop],
  1767. ['L', zoomedMin, navigatorTop + outlineHeight],
  1768. ['L', zoomedMax, navigatorTop + outlineHeight],
  1769. ['L', zoomedMax, navigatorTop],
  1770. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  1771. ];
  1772. if (maskInside) {
  1773. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  1774. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  1775. );
  1776. }
  1777. }
  1778. navigator.outline[verb]({
  1779. d: path
  1780. });
  1781. };
  1782. /**
  1783. * Render outline around the zoomed range
  1784. *
  1785. * @private
  1786. * @function Highcharts.Navigator#drawMasks
  1787. *
  1788. * @param {number} zoomedMin
  1789. * in pixels position where zoomed range starts
  1790. *
  1791. * @param {number} zoomedMax
  1792. * in pixels position where zoomed range ends
  1793. *
  1794. * @param {boolean|undefined} inverted
  1795. * flag if chart is inverted
  1796. *
  1797. * @param {string} verb
  1798. * use 'animate' or 'attr'
  1799. */
  1800. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  1801. var navigator = this, left = navigator.left, top = navigator.top, navigatorHeight = navigator.height, height, width, x, y;
  1802. // Determine rectangle position & size
  1803. // According to (non)inverted position:
  1804. if (inverted) {
  1805. x = [left, left, left];
  1806. y = [top, top + zoomedMin, top + zoomedMax];
  1807. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  1808. height = [
  1809. zoomedMin,
  1810. zoomedMax - zoomedMin,
  1811. navigator.size - zoomedMax
  1812. ];
  1813. }
  1814. else {
  1815. x = [left, left + zoomedMin, left + zoomedMax];
  1816. y = [top, top, top];
  1817. width = [
  1818. zoomedMin,
  1819. zoomedMax - zoomedMin,
  1820. navigator.size - zoomedMax
  1821. ];
  1822. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  1823. }
  1824. navigator.shades.forEach(function (shade, i) {
  1825. shade[verb]({
  1826. x: x[i],
  1827. y: y[i],
  1828. width: width[i],
  1829. height: height[i]
  1830. });
  1831. });
  1832. };
  1833. /**
  1834. * Generate DOM elements for a navigator:
  1835. *
  1836. * - main navigator group
  1837. *
  1838. * - all shades
  1839. *
  1840. * - outline
  1841. *
  1842. * - handles
  1843. *
  1844. * @private
  1845. * @function Highcharts.Navigator#renderElements
  1846. */
  1847. Navigator.prototype.renderElements = function () {
  1848. var navigator = this, navigatorOptions = navigator.navigatorOptions, maskInside = navigatorOptions.maskInside, chart = navigator.chart, inverted = chart.inverted, renderer = chart.renderer, navigatorGroup, mouseCursor = {
  1849. cursor: inverted ? 'ns-resize' : 'ew-resize'
  1850. };
  1851. // Create the main navigator group
  1852. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  1853. .attr({
  1854. zIndex: 8,
  1855. visibility: 'hidden'
  1856. })
  1857. .add();
  1858. // Create masks, each mask will get events and fill:
  1859. [
  1860. !maskInside,
  1861. maskInside,
  1862. !maskInside
  1863. ].forEach(function (hasMask, index) {
  1864. navigator.shades[index] = renderer.rect()
  1865. .addClass('highcharts-navigator-mask' +
  1866. (index === 1 ? '-inside' : '-outside'))
  1867. .add(navigatorGroup);
  1868. if (!chart.styledMode) {
  1869. navigator.shades[index]
  1870. .attr({
  1871. fill: hasMask ?
  1872. navigatorOptions.maskFill :
  1873. 'rgba(0,0,0,0)'
  1874. })
  1875. .css((index === 1) && mouseCursor);
  1876. }
  1877. });
  1878. // Create the outline:
  1879. navigator.outline = renderer.path()
  1880. .addClass('highcharts-navigator-outline')
  1881. .add(navigatorGroup);
  1882. if (!chart.styledMode) {
  1883. navigator.outline.attr({
  1884. 'stroke-width': navigatorOptions.outlineWidth,
  1885. stroke: navigatorOptions.outlineColor
  1886. });
  1887. }
  1888. // Create the handlers:
  1889. if (navigatorOptions.handles.enabled) {
  1890. [0, 1].forEach(function (index) {
  1891. navigatorOptions.handles.inverted = chart.inverted;
  1892. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  1893. // zIndex = 6 for right handle, 7 for left.
  1894. // Can't be 10, because of the tooltip in inverted chart #2908
  1895. navigator.handles[index].attr({ zIndex: 7 - index })
  1896. .addClass('highcharts-navigator-handle ' +
  1897. 'highcharts-navigator-handle-' +
  1898. ['left', 'right'][index]).add(navigatorGroup);
  1899. if (!chart.styledMode) {
  1900. var handlesOptions = navigatorOptions.handles;
  1901. navigator.handles[index]
  1902. .attr({
  1903. fill: handlesOptions.backgroundColor,
  1904. stroke: handlesOptions.borderColor,
  1905. 'stroke-width': handlesOptions.lineWidth
  1906. })
  1907. .css(mouseCursor);
  1908. }
  1909. });
  1910. }
  1911. };
  1912. /**
  1913. * Update navigator
  1914. *
  1915. * @private
  1916. * @function Highcharts.Navigator#update
  1917. *
  1918. * @param {Highcharts.NavigatorOptions} options
  1919. * Options to merge in when updating navigator
  1920. */
  1921. Navigator.prototype.update = function (options) {
  1922. // Remove references to old navigator series in base series
  1923. (this.series || []).forEach(function (series) {
  1924. if (series.baseSeries) {
  1925. delete series.baseSeries.navigatorSeries;
  1926. }
  1927. });
  1928. // Destroy and rebuild navigator
  1929. this.destroy();
  1930. var chartOptions = this.chart.options;
  1931. merge(true, chartOptions.navigator, this.options, options);
  1932. this.init(this.chart);
  1933. };
  1934. /**
  1935. * Render the navigator
  1936. *
  1937. * @private
  1938. * @function Highcharts.Navigator#render
  1939. * @param {number} min
  1940. * X axis value minimum
  1941. * @param {number} max
  1942. * X axis value maximum
  1943. * @param {number} [pxMin]
  1944. * Pixel value minimum
  1945. * @param {number} [pxMax]
  1946. * Pixel value maximum
  1947. * @return {void}
  1948. */
  1949. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  1950. var navigator = this, chart = navigator.chart, navigatorWidth, scrollbarLeft, scrollbarTop, scrollbarHeight = navigator.scrollbarHeight, navigatorSize, xAxis = navigator.xAxis, pointRange = xAxis.pointRange || 0, scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis, navigatorEnabled = navigator.navigatorEnabled, zoomedMin, zoomedMax, rendered = navigator.rendered, inverted = chart.inverted, verb, newMin, newMax, currentRange, minRange = chart.xAxis[0].minRange, maxRange = chart.xAxis[0].options.maxRange;
  1951. // Don't redraw while moving the handles (#4703).
  1952. if (this.hasDragged && !defined(pxMin)) {
  1953. return;
  1954. }
  1955. min = correctFloat(min - pointRange / 2);
  1956. max = correctFloat(max + pointRange / 2);
  1957. // Don't render the navigator until we have data (#486, #4202, #5172).
  1958. if (!isNumber(min) || !isNumber(max)) {
  1959. // However, if navigator was already rendered, we may need to resize
  1960. // it. For example hidden series, but visible navigator (#6022).
  1961. if (rendered) {
  1962. pxMin = 0;
  1963. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  1964. }
  1965. else {
  1966. return;
  1967. }
  1968. }
  1969. navigator.left = pick(xAxis.left,
  1970. // in case of scrollbar only, without navigator
  1971. chart.plotLeft + scrollbarHeight +
  1972. (inverted ? chart.plotWidth : 0));
  1973. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  1974. 2 * scrollbarHeight);
  1975. if (inverted) {
  1976. navigatorWidth = scrollbarHeight;
  1977. }
  1978. else {
  1979. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  1980. }
  1981. // Get the pixel position of the handles
  1982. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  1983. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  1984. // Verify (#1851, #2238)
  1985. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  1986. pxMin = 0;
  1987. pxMax = navigatorWidth;
  1988. }
  1989. // Are we below the minRange? (#2618, #6191)
  1990. newMin = xAxis.toValue(pxMin, true);
  1991. newMax = xAxis.toValue(pxMax, true);
  1992. currentRange = Math.abs(correctFloat(newMax - newMin));
  1993. if (currentRange < minRange) {
  1994. if (this.grabbedLeft) {
  1995. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  1996. }
  1997. else if (this.grabbedRight) {
  1998. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  1999. }
  2000. }
  2001. else if (defined(maxRange) &&
  2002. correctFloat(currentRange - pointRange) > maxRange) {
  2003. if (this.grabbedLeft) {
  2004. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  2005. }
  2006. else if (this.grabbedRight) {
  2007. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  2008. }
  2009. }
  2010. // Handles are allowed to cross, but never exceed the plot area
  2011. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  2012. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  2013. navigator.zoomedMax - navigator.fixedWidth :
  2014. Math.min(pxMin, pxMax), 0, zoomedMax);
  2015. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  2016. zoomedMax = Math.round(navigator.zoomedMax);
  2017. zoomedMin = Math.round(navigator.zoomedMin);
  2018. if (navigatorEnabled) {
  2019. navigator.navigatorGroup.attr({
  2020. visibility: 'visible'
  2021. });
  2022. // Place elements
  2023. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  2024. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2025. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2026. if (navigator.navigatorOptions.handles.enabled) {
  2027. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2028. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2029. }
  2030. }
  2031. if (navigator.scrollbar) {
  2032. if (inverted) {
  2033. scrollbarTop = navigator.top - scrollbarHeight;
  2034. scrollbarLeft = navigator.left - scrollbarHeight +
  2035. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  2036. // Multiple axes has offsets:
  2037. (scrollbarXAxis.titleOffset || 0) +
  2038. // Self margin from the axis.title
  2039. scrollbarXAxis.axisTitleMargin);
  2040. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  2041. }
  2042. else {
  2043. scrollbarTop = navigator.top + (navigatorEnabled ?
  2044. navigator.height :
  2045. -scrollbarHeight);
  2046. scrollbarLeft = navigator.left - scrollbarHeight;
  2047. }
  2048. // Reposition scrollbar
  2049. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  2050. // Keep scale 0-1
  2051. navigator.scrollbar.setRange(
  2052. // Use real value, not rounded because range can be very small
  2053. // (#1716)
  2054. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  2055. }
  2056. navigator.rendered = true;
  2057. };
  2058. /**
  2059. * Set up the mouse and touch events for the navigator
  2060. *
  2061. * @private
  2062. * @function Highcharts.Navigator#addMouseEvents
  2063. */
  2064. Navigator.prototype.addMouseEvents = function () {
  2065. var navigator = this, chart = navigator.chart, container = chart.container, eventsToUnbind = [], mouseMoveHandler, mouseUpHandler;
  2066. /**
  2067. * Create mouse events' handlers.
  2068. * Make them as separate functions to enable wrapping them:
  2069. */
  2070. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  2071. navigator.onMouseMove(e);
  2072. };
  2073. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  2074. navigator.onMouseUp(e);
  2075. };
  2076. // Add shades and handles mousedown events
  2077. eventsToUnbind = navigator.getPartsEvents('mousedown');
  2078. // Add mouse move and mouseup events. These are bind to doc/container,
  2079. // because Navigator.grabbedSomething flags are stored in mousedown
  2080. // events
  2081. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  2082. // Touch events
  2083. if (hasTouch) {
  2084. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  2085. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  2086. }
  2087. navigator.eventsToUnbind = eventsToUnbind;
  2088. // Data events
  2089. if (navigator.series && navigator.series[0]) {
  2090. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  2091. chart.navigator.modifyNavigatorAxisExtremes();
  2092. }));
  2093. }
  2094. };
  2095. /**
  2096. * Generate events for handles and masks
  2097. *
  2098. * @private
  2099. * @function Highcharts.Navigator#getPartsEvents
  2100. *
  2101. * @param {string} eventName
  2102. * Event name handler, 'mousedown' or 'touchstart'
  2103. *
  2104. * @return {Array<Function>}
  2105. * An array of functions to remove navigator functions from the
  2106. * events again.
  2107. */
  2108. Navigator.prototype.getPartsEvents = function (eventName) {
  2109. var navigator = this, events = [];
  2110. ['shades', 'handles'].forEach(function (name) {
  2111. navigator[name].forEach(function (navigatorItem, index) {
  2112. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  2113. navigator[name + 'Mousedown'](e, index);
  2114. }));
  2115. });
  2116. });
  2117. return events;
  2118. };
  2119. /**
  2120. * Mousedown on a shaded mask, either:
  2121. *
  2122. * - will be stored for future drag&drop
  2123. *
  2124. * - will directly shift to a new range
  2125. *
  2126. * @private
  2127. * @function Highcharts.Navigator#shadesMousedown
  2128. *
  2129. * @param {Highcharts.PointerEventObject} e
  2130. * Mouse event
  2131. *
  2132. * @param {number} index
  2133. * Index of a mask in Navigator.shades array
  2134. */
  2135. Navigator.prototype.shadesMousedown = function (e, index) {
  2136. e = this.chart.pointer.normalize(e);
  2137. var navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, zoomedMin = navigator.zoomedMin, navigatorPosition = navigator.left, navigatorSize = navigator.size, range = navigator.range, chartX = e.chartX, fixedMax, fixedMin, ext, left;
  2138. // For inverted chart, swap some options:
  2139. if (chart.inverted) {
  2140. chartX = e.chartY;
  2141. navigatorPosition = navigator.top;
  2142. }
  2143. if (index === 1) {
  2144. // Store information for drag&drop
  2145. navigator.grabbedCenter = chartX;
  2146. navigator.fixedWidth = range;
  2147. navigator.dragOffset = chartX - zoomedMin;
  2148. }
  2149. else {
  2150. // Shift the range by clicking on shaded areas
  2151. left = chartX - navigatorPosition - range / 2;
  2152. if (index === 0) {
  2153. left = Math.max(0, left);
  2154. }
  2155. else if (index === 2 && left + range >= navigatorSize) {
  2156. left = navigatorSize - range;
  2157. if (navigator.reversedExtremes) {
  2158. // #7713
  2159. left -= range;
  2160. fixedMin = navigator.getUnionExtremes().dataMin;
  2161. }
  2162. else {
  2163. // #2293, #3543
  2164. fixedMax = navigator.getUnionExtremes().dataMax;
  2165. }
  2166. }
  2167. if (left !== zoomedMin) { // it has actually moved
  2168. navigator.fixedWidth = range; // #1370
  2169. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  2170. if (defined(ext.min)) { // #7411
  2171. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  2172. { trigger: 'navigator' });
  2173. }
  2174. }
  2175. }
  2176. };
  2177. /**
  2178. * Mousedown on a handle mask.
  2179. * Will store necessary information for drag&drop.
  2180. *
  2181. * @private
  2182. * @function Highcharts.Navigator#handlesMousedown
  2183. * @param {Highcharts.PointerEventObject} e
  2184. * Mouse event
  2185. * @param {number} index
  2186. * Index of a handle in Navigator.handles array
  2187. * @return {void}
  2188. */
  2189. Navigator.prototype.handlesMousedown = function (e, index) {
  2190. e = this.chart.pointer.normalize(e);
  2191. var navigator = this, chart = navigator.chart, baseXAxis = chart.xAxis[0],
  2192. // For reversed axes, min and max are changed,
  2193. // so the other extreme should be stored
  2194. reverse = navigator.reversedExtremes;
  2195. if (index === 0) {
  2196. // Grab the left handle
  2197. navigator.grabbedLeft = true;
  2198. navigator.otherHandlePos = navigator.zoomedMax;
  2199. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  2200. }
  2201. else {
  2202. // Grab the right handle
  2203. navigator.grabbedRight = true;
  2204. navigator.otherHandlePos = navigator.zoomedMin;
  2205. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  2206. }
  2207. chart.fixedRange = null;
  2208. };
  2209. /**
  2210. * Mouse move event based on x/y mouse position.
  2211. *
  2212. * @private
  2213. * @function Highcharts.Navigator#onMouseMove
  2214. *
  2215. * @param {Highcharts.PointerEventObject} e
  2216. * Mouse event
  2217. */
  2218. Navigator.prototype.onMouseMove = function (e) {
  2219. var navigator = this, chart = navigator.chart, left = navigator.left, navigatorSize = navigator.navigatorSize, range = navigator.range, dragOffset = navigator.dragOffset, inverted = chart.inverted, chartX;
  2220. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  2221. // the finger down in the center of the scrollbar. This should be
  2222. // ignored.
  2223. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  2224. e = chart.pointer.normalize(e);
  2225. chartX = e.chartX;
  2226. // Swap some options for inverted chart
  2227. if (inverted) {
  2228. left = navigator.top;
  2229. chartX = e.chartY;
  2230. }
  2231. // Drag left handle or top handle
  2232. if (navigator.grabbedLeft) {
  2233. navigator.hasDragged = true;
  2234. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  2235. // Drag right handle or bottom handle
  2236. }
  2237. else if (navigator.grabbedRight) {
  2238. navigator.hasDragged = true;
  2239. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  2240. // Drag scrollbar or open area in navigator
  2241. }
  2242. else if (navigator.grabbedCenter) {
  2243. navigator.hasDragged = true;
  2244. if (chartX < dragOffset) { // outside left
  2245. chartX = dragOffset;
  2246. // outside right
  2247. }
  2248. else if (chartX >
  2249. navigatorSize + dragOffset - range) {
  2250. chartX = navigatorSize + dragOffset - range;
  2251. }
  2252. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  2253. }
  2254. if (navigator.hasDragged &&
  2255. navigator.scrollbar &&
  2256. pick(navigator.scrollbar.options.liveRedraw,
  2257. // By default, don't run live redraw on VML, on touch
  2258. // devices or if the chart is in boost.
  2259. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  2260. e.DOMType = e.type; // DOMType is for IE8
  2261. setTimeout(function () {
  2262. navigator.onMouseUp(e);
  2263. }, 0);
  2264. }
  2265. }
  2266. };
  2267. /**
  2268. * Mouse up event based on x/y mouse position.
  2269. *
  2270. * @private
  2271. * @function Highcharts.Navigator#onMouseUp
  2272. * @param {Highcharts.PointerEventObject} e
  2273. * Mouse event
  2274. * @return {void}
  2275. */
  2276. Navigator.prototype.onMouseUp = function (e) {
  2277. var navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, scrollbar = navigator.scrollbar, DOMEvent = e.DOMEvent || e, inverted = chart.inverted, verb = navigator.rendered && !navigator.hasDragged ?
  2278. 'animate' : 'attr', zoomedMax = Math.round(navigator.zoomedMax), zoomedMin = Math.round(navigator.zoomedMin), unionExtremes, fixedMin, fixedMax, ext;
  2279. if (
  2280. // MouseUp is called for both, navigator and scrollbar (that order),
  2281. // which causes calling afterSetExtremes twice. Prevent first call
  2282. // by checking if scrollbar is going to set new extremes (#6334)
  2283. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  2284. e.trigger === 'scrollbar') {
  2285. unionExtremes = navigator.getUnionExtremes();
  2286. // When dragging one handle, make sure the other one doesn't change
  2287. if (navigator.zoomedMin === navigator.otherHandlePos) {
  2288. fixedMin = navigator.fixedExtreme;
  2289. }
  2290. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  2291. fixedMax = navigator.fixedExtreme;
  2292. }
  2293. // Snap to right edge (#4076)
  2294. if (navigator.zoomedMax === navigator.size) {
  2295. fixedMax = navigator.reversedExtremes ?
  2296. unionExtremes.dataMin :
  2297. unionExtremes.dataMax;
  2298. }
  2299. // Snap to left edge (#7576)
  2300. if (navigator.zoomedMin === 0) {
  2301. fixedMin = navigator.reversedExtremes ?
  2302. unionExtremes.dataMax :
  2303. unionExtremes.dataMin;
  2304. }
  2305. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  2306. if (defined(ext.min)) {
  2307. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  2308. // Run animation when clicking buttons, scrollbar track etc,
  2309. // but not when dragging handles or scrollbar
  2310. navigator.hasDragged ? false : null, {
  2311. trigger: 'navigator',
  2312. triggerOp: 'navigator-drag',
  2313. DOMEvent: DOMEvent // #1838
  2314. });
  2315. }
  2316. }
  2317. if (e.DOMType !== 'mousemove' &&
  2318. e.DOMType !== 'touchmove') {
  2319. navigator.grabbedLeft = navigator.grabbedRight =
  2320. navigator.grabbedCenter = navigator.fixedWidth =
  2321. navigator.fixedExtreme = navigator.otherHandlePos =
  2322. navigator.hasDragged = navigator.dragOffset = null;
  2323. }
  2324. // Update position of navigator shades, outline and handles (#12573)
  2325. if (navigator.navigatorEnabled) {
  2326. if (navigator.shades) {
  2327. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  2328. }
  2329. if (navigator.outline) {
  2330. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  2331. }
  2332. if (navigator.navigatorOptions.handles.enabled &&
  2333. Object.keys(navigator.handles).length ===
  2334. navigator.handles.length) {
  2335. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  2336. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  2337. }
  2338. }
  2339. };
  2340. /**
  2341. * Removes the event handlers attached previously with addEvents.
  2342. *
  2343. * @private
  2344. * @function Highcharts.Navigator#removeEvents
  2345. * @return {void}
  2346. */
  2347. Navigator.prototype.removeEvents = function () {
  2348. if (this.eventsToUnbind) {
  2349. this.eventsToUnbind.forEach(function (unbind) {
  2350. unbind();
  2351. });
  2352. this.eventsToUnbind = void 0;
  2353. }
  2354. this.removeBaseSeriesEvents();
  2355. };
  2356. /**
  2357. * Remove data events.
  2358. *
  2359. * @private
  2360. * @function Highcharts.Navigator#removeBaseSeriesEvents
  2361. * @return {void}
  2362. */
  2363. Navigator.prototype.removeBaseSeriesEvents = function () {
  2364. var baseSeries = this.baseSeries || [];
  2365. if (this.navigatorEnabled && baseSeries[0]) {
  2366. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  2367. baseSeries.forEach(function (series) {
  2368. removeEvent(series, 'updatedData', this.updatedDataHandler);
  2369. }, this);
  2370. }
  2371. // We only listen for extremes-events on the first baseSeries
  2372. if (baseSeries[0].xAxis) {
  2373. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  2374. }
  2375. }
  2376. };
  2377. /**
  2378. * Initialize the Navigator object
  2379. *
  2380. * @private
  2381. * @function Highcharts.Navigator#init
  2382. *
  2383. * @param {Highcharts.Chart} chart
  2384. */
  2385. Navigator.prototype.init = function (chart) {
  2386. var chartOptions = chart.options, navigatorOptions = chartOptions.navigator, navigatorEnabled = navigatorOptions.enabled, scrollbarOptions = chartOptions.scrollbar, scrollbarEnabled = scrollbarOptions.enabled, height = navigatorEnabled ? navigatorOptions.height : 0, scrollbarHeight = scrollbarEnabled ?
  2387. scrollbarOptions.height :
  2388. 0;
  2389. this.handles = [];
  2390. this.shades = [];
  2391. this.chart = chart;
  2392. this.setBaseSeries();
  2393. this.height = height;
  2394. this.scrollbarHeight = scrollbarHeight;
  2395. this.scrollbarEnabled = scrollbarEnabled;
  2396. this.navigatorEnabled = navigatorEnabled;
  2397. this.navigatorOptions = navigatorOptions;
  2398. this.scrollbarOptions = scrollbarOptions;
  2399. this.outlineHeight = height + scrollbarHeight;
  2400. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  2401. var navigator = this, baseSeries = navigator.baseSeries, xAxisIndex = chart.xAxis.length, yAxisIndex = chart.yAxis.length, baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  2402. chart.xAxis[0] || { options: {} };
  2403. chart.isDirtyBox = true;
  2404. if (navigator.navigatorEnabled) {
  2405. // an x axis is required for scrollbar also
  2406. navigator.xAxis = new Axis(chart, merge({
  2407. // inherit base xAxis' break and ordinal options
  2408. breaks: baseXaxis.options.breaks,
  2409. ordinal: baseXaxis.options.ordinal
  2410. }, navigatorOptions.xAxis, {
  2411. id: 'navigator-x-axis',
  2412. yAxis: 'navigator-y-axis',
  2413. isX: true,
  2414. type: 'datetime',
  2415. index: xAxisIndex,
  2416. isInternal: true,
  2417. offset: 0,
  2418. keepOrdinalPadding: true,
  2419. startOnTick: false,
  2420. endOnTick: false,
  2421. minPadding: 0,
  2422. maxPadding: 0,
  2423. zoomEnabled: false
  2424. }, chart.inverted ? {
  2425. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  2426. width: height
  2427. } : {
  2428. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  2429. height: height
  2430. }));
  2431. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  2432. id: 'navigator-y-axis',
  2433. alignTicks: false,
  2434. offset: 0,
  2435. index: yAxisIndex,
  2436. isInternal: true,
  2437. zoomEnabled: false
  2438. }, chart.inverted ? {
  2439. width: height
  2440. } : {
  2441. height: height
  2442. }));
  2443. // If we have a base series, initialize the navigator series
  2444. if (baseSeries || navigatorOptions.series.data) {
  2445. navigator.updateNavigatorSeries(false);
  2446. // If not, set up an event to listen for added series
  2447. }
  2448. else if (chart.series.length === 0) {
  2449. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  2450. // We've got one, now add it as base
  2451. if (chart.series.length > 0 && !navigator.series) {
  2452. navigator.setBaseSeries();
  2453. navigator.unbindRedraw(); // reset
  2454. }
  2455. });
  2456. }
  2457. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  2458. // Render items, so we can bind events to them:
  2459. navigator.renderElements();
  2460. // Add mouse events
  2461. navigator.addMouseEvents();
  2462. // in case of scrollbar only, fake an x axis to get translation
  2463. }
  2464. else {
  2465. navigator.xAxis = {
  2466. chart: chart,
  2467. navigatorAxis: {
  2468. fake: true
  2469. },
  2470. translate: function (value, reverse) {
  2471. 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;
  2472. return reverse ?
  2473. // from pixel to value
  2474. (value * valueRange / scrollTrackWidth) + min :
  2475. // from value to pixel
  2476. scrollTrackWidth * (value - min) / valueRange;
  2477. },
  2478. toPixels: function (value) {
  2479. return this.translate(value);
  2480. },
  2481. toValue: function (value) {
  2482. return this.translate(value, true);
  2483. }
  2484. };
  2485. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  2486. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  2487. }
  2488. // Initialize the scrollbar
  2489. if (chart.options.scrollbar.enabled) {
  2490. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  2491. margin: navigator.navigatorEnabled ? 0 : 10,
  2492. vertical: chart.inverted
  2493. }), chart);
  2494. addEvent(navigator.scrollbar, 'changed', function (e) {
  2495. var range = navigator.size, to = range * this.to, from = range * this.from;
  2496. navigator.hasDragged = navigator.scrollbar.hasDragged;
  2497. navigator.render(0, 0, from, to);
  2498. if (chart.options.scrollbar.liveRedraw ||
  2499. (e.DOMType !== 'mousemove' &&
  2500. e.DOMType !== 'touchmove')) {
  2501. setTimeout(function () {
  2502. navigator.onMouseUp(e);
  2503. });
  2504. }
  2505. });
  2506. }
  2507. // Add data events
  2508. navigator.addBaseSeriesEvents();
  2509. // Add redraw events
  2510. navigator.addChartEvents();
  2511. };
  2512. /**
  2513. * Get the union data extremes of the chart - the outer data extremes of the
  2514. * base X axis and the navigator axis.
  2515. *
  2516. * @private
  2517. * @function Highcharts.Navigator#getUnionExtremes
  2518. * @param {boolean} [returnFalseOnNoBaseSeries]
  2519. * as the param says.
  2520. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  2521. */
  2522. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  2523. var baseAxis = this.chart.xAxis[0], navAxis = this.xAxis, navAxisOptions = navAxis.options, baseAxisOptions = baseAxis.options, ret;
  2524. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  2525. ret = {
  2526. dataMin: pick(// #4053
  2527. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  2528. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  2529. };
  2530. }
  2531. return ret;
  2532. };
  2533. /**
  2534. * Set the base series and update the navigator series from this. With a bit
  2535. * of modification we should be able to make this an API method to be called
  2536. * from the outside
  2537. *
  2538. * @private
  2539. * @function Highcharts.Navigator#setBaseSeries
  2540. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  2541. * Additional series options for a navigator
  2542. * @param {boolean} [redraw]
  2543. * Whether to redraw after update.
  2544. * @return {void}
  2545. */
  2546. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  2547. var chart = this.chart, baseSeries = this.baseSeries = [];
  2548. baseSeriesOptions = (baseSeriesOptions ||
  2549. chart.options && chart.options.navigator.baseSeries ||
  2550. (chart.series.length ?
  2551. // Find the first non-navigator series (#8430)
  2552. find(chart.series, function (s) {
  2553. return !s.options.isInternal;
  2554. }).index :
  2555. 0));
  2556. // Iterate through series and add the ones that should be shown in
  2557. // navigator.
  2558. (chart.series || []).forEach(function (series, i) {
  2559. if (
  2560. // Don't include existing nav series
  2561. !series.options.isInternal &&
  2562. (series.options.showInNavigator ||
  2563. (i === baseSeriesOptions ||
  2564. series.options.id === baseSeriesOptions) &&
  2565. series.options.showInNavigator !== false)) {
  2566. baseSeries.push(series);
  2567. }
  2568. });
  2569. // When run after render, this.xAxis already exists
  2570. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  2571. this.updateNavigatorSeries(true, redraw);
  2572. }
  2573. };
  2574. /**
  2575. * Update series in the navigator from baseSeries, adding new if does not
  2576. * exist.
  2577. *
  2578. * @private
  2579. * @function Highcharts.Navigator.updateNavigatorSeries
  2580. * @param {boolean} addEvents
  2581. * @param {boolean} [redraw]
  2582. * @return {void}
  2583. */
  2584. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  2585. var navigator = this, chart = navigator.chart, baseSeries = navigator.baseSeries, baseOptions, mergedNavSeriesOptions, chartNavigatorSeriesOptions = navigator.navigatorOptions.series, baseNavigatorOptions, navSeriesMixin = {
  2586. enableMouseTracking: false,
  2587. index: null,
  2588. linkedTo: null,
  2589. group: 'nav',
  2590. padXAxis: false,
  2591. xAxis: 'navigator-x-axis',
  2592. yAxis: 'navigator-y-axis',
  2593. showInLegend: false,
  2594. stacking: void 0,
  2595. isInternal: true,
  2596. states: {
  2597. inactive: {
  2598. opacity: 1
  2599. }
  2600. }
  2601. },
  2602. // Remove navigator series that are no longer in the baseSeries
  2603. navigatorSeries = navigator.series =
  2604. (navigator.series || []).filter(function (navSeries) {
  2605. var base = navSeries.baseSeries;
  2606. if (baseSeries.indexOf(base) < 0) { // Not in array
  2607. // If there is still a base series connected to this
  2608. // series, remove event handler and reference.
  2609. if (base) {
  2610. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  2611. delete base.navigatorSeries;
  2612. }
  2613. // Kill the nav series. It may already have been
  2614. // destroyed (#8715).
  2615. if (navSeries.chart) {
  2616. navSeries.destroy();
  2617. }
  2618. return false;
  2619. }
  2620. return true;
  2621. });
  2622. // Go through each base series and merge the options to create new
  2623. // series
  2624. if (baseSeries && baseSeries.length) {
  2625. baseSeries.forEach(function eachBaseSeries(base) {
  2626. var linkedNavSeries = base.navigatorSeries, userNavOptions = extend(
  2627. // Grab color and visibility from base as default
  2628. {
  2629. color: base.color,
  2630. visible: base.visible
  2631. }, !isArray(chartNavigatorSeriesOptions) ?
  2632. chartNavigatorSeriesOptions :
  2633. defaultOptions.navigator.series);
  2634. // Don't update if the series exists in nav and we have disabled
  2635. // adaptToUpdatedData.
  2636. if (linkedNavSeries &&
  2637. navigator.navigatorOptions.adaptToUpdatedData === false) {
  2638. return;
  2639. }
  2640. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  2641. baseOptions = base.options || {};
  2642. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  2643. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  2644. // Once nav series type is resolved, pick correct pointRange
  2645. mergedNavSeriesOptions.pointRange = pick(
  2646. // Stricte set pointRange in options
  2647. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  2648. // Fallback to default values, e.g. `null` for column
  2649. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  2650. // Merge data separately. Do a slice to avoid mutating the
  2651. // navigator options from base series (#4923).
  2652. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  2653. navigator.hasNavigatorData =
  2654. navigator.hasNavigatorData || !!navigatorSeriesData;
  2655. mergedNavSeriesOptions.data =
  2656. navigatorSeriesData ||
  2657. baseOptions.data && baseOptions.data.slice(0);
  2658. // Update or add the series
  2659. if (linkedNavSeries && linkedNavSeries.options) {
  2660. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  2661. }
  2662. else {
  2663. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  2664. base.navigatorSeries.baseSeries = base; // Store ref
  2665. navigatorSeries.push(base.navigatorSeries);
  2666. }
  2667. });
  2668. }
  2669. // If user has defined data (and no base series) or explicitly defined
  2670. // navigator.series as an array, we create these series on top of any
  2671. // base series.
  2672. if (chartNavigatorSeriesOptions.data &&
  2673. !(baseSeries && baseSeries.length) ||
  2674. isArray(chartNavigatorSeriesOptions)) {
  2675. navigator.hasNavigatorData = false;
  2676. // Allow navigator.series to be an array
  2677. chartNavigatorSeriesOptions =
  2678. splat(chartNavigatorSeriesOptions);
  2679. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  2680. navSeriesMixin.name =
  2681. 'Navigator ' + (navigatorSeries.length + 1);
  2682. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  2683. // Since we don't have a base series to pull color from,
  2684. // try to fake it by using color from series with same
  2685. // index. Otherwise pull from the colors array. We need
  2686. // an explicit color as otherwise updates will increment
  2687. // color counter and we'll get a new color for each
  2688. // update of the nav series.
  2689. color: chart.series[i] &&
  2690. !chart.series[i].options.isInternal &&
  2691. chart.series[i].color ||
  2692. chart.options.colors[i] ||
  2693. chart.options.colors[0]
  2694. }, navSeriesMixin, userSeriesOptions);
  2695. mergedNavSeriesOptions.data = userSeriesOptions.data;
  2696. if (mergedNavSeriesOptions.data) {
  2697. navigator.hasNavigatorData = true;
  2698. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  2699. }
  2700. });
  2701. }
  2702. if (addEvents) {
  2703. this.addBaseSeriesEvents();
  2704. }
  2705. };
  2706. /**
  2707. * Add data events.
  2708. * For example when main series is updated we need to recalculate extremes
  2709. *
  2710. * @private
  2711. * @function Highcharts.Navigator#addBaseSeriesEvent
  2712. * @return {void}
  2713. */
  2714. Navigator.prototype.addBaseSeriesEvents = function () {
  2715. var navigator = this, baseSeries = navigator.baseSeries || [];
  2716. // Bind modified extremes event to first base's xAxis only.
  2717. // In event of > 1 base-xAxes, the navigator will ignore those.
  2718. // Adding this multiple times to the same axis is no problem, as
  2719. // duplicates should be discarded by the browser.
  2720. if (baseSeries[0] && baseSeries[0].xAxis) {
  2721. addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  2722. }
  2723. baseSeries.forEach(function (base) {
  2724. // Link base series show/hide to navigator series visibility
  2725. addEvent(base, 'show', function () {
  2726. if (this.navigatorSeries) {
  2727. this.navigatorSeries.setVisible(true, false);
  2728. }
  2729. });
  2730. addEvent(base, 'hide', function () {
  2731. if (this.navigatorSeries) {
  2732. this.navigatorSeries.setVisible(false, false);
  2733. }
  2734. });
  2735. // Respond to updated data in the base series, unless explicitily
  2736. // not adapting to data changes.
  2737. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  2738. if (base.xAxis) {
  2739. addEvent(base, 'updatedData', this.updatedDataHandler);
  2740. }
  2741. }
  2742. // Handle series removal
  2743. addEvent(base, 'remove', function () {
  2744. if (this.navigatorSeries) {
  2745. erase(navigator.series, this.navigatorSeries);
  2746. if (defined(this.navigatorSeries.options)) {
  2747. this.navigatorSeries.remove(false);
  2748. }
  2749. delete this.navigatorSeries;
  2750. }
  2751. });
  2752. }, this);
  2753. };
  2754. /**
  2755. * Get minimum from all base series connected to the navigator
  2756. * @private
  2757. * @param {number} currentSeriesMin
  2758. * Minium from the current series
  2759. * @return {number} Minimum from all series
  2760. */
  2761. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  2762. return this.baseSeries.reduce(function (min, series) {
  2763. // (#10193)
  2764. return Math.min(min, series.xData ? series.xData[0] : min);
  2765. }, currentSeriesMin);
  2766. };
  2767. /**
  2768. * Set the navigator x axis extremes to reflect the total. The navigator
  2769. * extremes should always be the extremes of the union of all series in the
  2770. * chart as well as the navigator series.
  2771. *
  2772. * @private
  2773. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  2774. */
  2775. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  2776. var xAxis = this.xAxis, unionExtremes;
  2777. if (typeof xAxis.getExtremes !== 'undefined') {
  2778. unionExtremes = this.getUnionExtremes(true);
  2779. if (unionExtremes &&
  2780. (unionExtremes.dataMin !== xAxis.min ||
  2781. unionExtremes.dataMax !== xAxis.max)) {
  2782. xAxis.min = unionExtremes.dataMin;
  2783. xAxis.max = unionExtremes.dataMax;
  2784. }
  2785. }
  2786. };
  2787. /**
  2788. * Hook to modify the base axis extremes with information from the Navigator
  2789. *
  2790. * @private
  2791. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  2792. */
  2793. Navigator.prototype.modifyBaseAxisExtremes = function () {
  2794. var baseXAxis = this, navigator = baseXAxis.chart.navigator, baseExtremes = baseXAxis.getExtremes(), baseMin = baseExtremes.min, baseMax = baseExtremes.max, baseDataMin = baseExtremes.dataMin, baseDataMax = baseExtremes.dataMax, range = baseMax - baseMin, stickToMin = navigator.stickToMin, stickToMax = navigator.stickToMax, overscroll = pick(baseXAxis.options.overscroll, 0), newMax, newMin, navigatorSeries = navigator.series && navigator.series[0], hasSetExtremes = !!baseXAxis.setExtremes,
  2795. // When the extremes have been set by range selector button, don't
  2796. // stick to min or max. The range selector buttons will handle the
  2797. // extremes. (#5489)
  2798. unmutable = baseXAxis.eventArgs &&
  2799. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  2800. if (!unmutable) {
  2801. // If the zoomed range is already at the min, move it to the right
  2802. // as new data comes in
  2803. if (stickToMin) {
  2804. newMin = baseDataMin;
  2805. newMax = newMin + range;
  2806. }
  2807. // If the zoomed range is already at the max, move it to the right
  2808. // as new data comes in
  2809. if (stickToMax) {
  2810. newMax = baseDataMax + overscroll;
  2811. // If stickToMin is true, the new min value is set above
  2812. if (!stickToMin) {
  2813. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  2814. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  2815. navigatorSeries.xData[0] :
  2816. -Number.MAX_VALUE));
  2817. }
  2818. }
  2819. // Update the extremes
  2820. if (hasSetExtremes && (stickToMin || stickToMax)) {
  2821. if (isNumber(newMin)) {
  2822. baseXAxis.min = baseXAxis.userMin = newMin;
  2823. baseXAxis.max = baseXAxis.userMax = newMax;
  2824. }
  2825. }
  2826. }
  2827. // Reset
  2828. navigator.stickToMin =
  2829. navigator.stickToMax = null;
  2830. };
  2831. /**
  2832. * Handler for updated data on the base series. When data is modified, the
  2833. * navigator series must reflect it. This is called from the Chart.redraw
  2834. * function before axis and series extremes are computed.
  2835. *
  2836. * @private
  2837. * @function Highcharts.Navigator#updateDataHandler
  2838. */
  2839. Navigator.prototype.updatedDataHandler = function () {
  2840. var navigator = this.chart.navigator, baseSeries = this, navigatorSeries = this.navigatorSeries, xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  2841. // If the scrollbar is scrolled all the way to the right, keep right as
  2842. // new data comes in.
  2843. navigator.stickToMax = navigator.reversedExtremes ?
  2844. Math.round(navigator.zoomedMin) === 0 :
  2845. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  2846. // Detect whether the zoomed area should stick to the minimum or
  2847. // maximum. If the current axis minimum falls outside the new updated
  2848. // dataset, we must adjust.
  2849. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  2850. (baseSeries.xAxis.min <= xDataMin) &&
  2851. (!this.chart.fixedRange || !navigator.stickToMax);
  2852. // Set the navigator series data to the new data of the base series
  2853. if (navigatorSeries && !navigator.hasNavigatorData) {
  2854. navigatorSeries.options.pointStart = baseSeries.xData[0];
  2855. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  2856. }
  2857. };
  2858. /**
  2859. * Add chart events, like redrawing navigator, when chart requires that.
  2860. *
  2861. * @private
  2862. * @function Highcharts.Navigator#addChartEvents
  2863. * @return {void}
  2864. */
  2865. Navigator.prototype.addChartEvents = function () {
  2866. if (!this.eventsToUnbind) {
  2867. this.eventsToUnbind = [];
  2868. }
  2869. this.eventsToUnbind.push(
  2870. // Move the scrollbar after redraw, like after data updata even if
  2871. // axes don't redraw
  2872. addEvent(this.chart, 'redraw', function () {
  2873. var navigator = this.navigator, xAxis = navigator && (navigator.baseSeries &&
  2874. navigator.baseSeries[0] &&
  2875. navigator.baseSeries[0].xAxis ||
  2876. this.xAxis[0]); // #5709, #13114
  2877. if (xAxis) {
  2878. navigator.render(xAxis.min, xAxis.max);
  2879. }
  2880. }),
  2881. // Make room for the navigator, can be placed around the chart:
  2882. addEvent(this.chart, 'getMargins', function () {
  2883. var chart = this, navigator = chart.navigator, marginName = navigator.opposite ?
  2884. 'plotTop' : 'marginBottom';
  2885. if (chart.inverted) {
  2886. marginName = navigator.opposite ?
  2887. 'marginRight' : 'plotLeft';
  2888. }
  2889. chart[marginName] =
  2890. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  2891. navigator.outlineHeight :
  2892. 0) + navigator.navigatorOptions.margin;
  2893. }));
  2894. };
  2895. /**
  2896. * Destroys allocated elements.
  2897. *
  2898. * @private
  2899. * @function Highcharts.Navigator#destroy
  2900. */
  2901. Navigator.prototype.destroy = function () {
  2902. // Disconnect events added in addEvents
  2903. this.removeEvents();
  2904. if (this.xAxis) {
  2905. erase(this.chart.xAxis, this.xAxis);
  2906. erase(this.chart.axes, this.xAxis);
  2907. }
  2908. if (this.yAxis) {
  2909. erase(this.chart.yAxis, this.yAxis);
  2910. erase(this.chart.axes, this.yAxis);
  2911. }
  2912. // Destroy series
  2913. (this.series || []).forEach(function (s) {
  2914. if (s.destroy) {
  2915. s.destroy();
  2916. }
  2917. });
  2918. // Destroy properties
  2919. [
  2920. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  2921. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  2922. 'rendered'
  2923. ].forEach(function (prop) {
  2924. if (this[prop] && this[prop].destroy) {
  2925. this[prop].destroy();
  2926. }
  2927. this[prop] = null;
  2928. }, this);
  2929. // Destroy elements in collection
  2930. [this.handles].forEach(function (coll) {
  2931. destroyObjectProperties(coll);
  2932. }, this);
  2933. };
  2934. return Navigator;
  2935. }());
  2936. // End of prototype
  2937. if (!H.Navigator) {
  2938. H.Navigator = Navigator;
  2939. NavigatorAxis.compose(Axis);
  2940. // For Stock charts. For x only zooming, do not to create the zoom button
  2941. // because X axis zooming is already allowed by the Navigator and Range
  2942. // selector. (#9285)
  2943. addEvent(Chart, 'beforeShowResetZoom', function () {
  2944. var chartOptions = this.options, navigator = chartOptions.navigator, rangeSelector = chartOptions.rangeSelector;
  2945. if (((navigator && navigator.enabled) ||
  2946. (rangeSelector && rangeSelector.enabled)) &&
  2947. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  2948. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  2949. return false;
  2950. }
  2951. });
  2952. // Initialize navigator for stock charts
  2953. addEvent(Chart, 'beforeRender', function () {
  2954. var options = this.options;
  2955. if (options.navigator.enabled ||
  2956. options.scrollbar.enabled) {
  2957. this.scroller = this.navigator = new Navigator(this);
  2958. }
  2959. });
  2960. // For stock charts, extend the Chart.setChartSize method so that we can set
  2961. // the final top position of the navigator once the height of the chart,
  2962. // including the legend, is determined. #367. We can't use Chart.getMargins,
  2963. // because labels offsets are not calculated yet.
  2964. addEvent(Chart, 'afterSetChartSize', function () {
  2965. var legend = this.legend, navigator = this.navigator, scrollbarHeight, legendOptions, xAxis, yAxis;
  2966. if (navigator) {
  2967. legendOptions = legend && legend.options;
  2968. xAxis = navigator.xAxis;
  2969. yAxis = navigator.yAxis;
  2970. scrollbarHeight = navigator.scrollbarHeight;
  2971. // Compute the top position
  2972. if (this.inverted) {
  2973. navigator.left = navigator.opposite ?
  2974. this.chartWidth - scrollbarHeight -
  2975. navigator.height :
  2976. this.spacing[3] + scrollbarHeight;
  2977. navigator.top = this.plotTop + scrollbarHeight;
  2978. }
  2979. else {
  2980. navigator.left = this.plotLeft + scrollbarHeight;
  2981. navigator.top = navigator.navigatorOptions.top ||
  2982. this.chartHeight -
  2983. navigator.height -
  2984. scrollbarHeight -
  2985. this.spacing[2] -
  2986. (this.rangeSelector && this.extraBottomMargin ?
  2987. this.rangeSelector.getHeight() :
  2988. 0) -
  2989. ((legendOptions &&
  2990. legendOptions.verticalAlign === 'bottom' &&
  2991. legendOptions.layout !== 'proximate' && // #13392
  2992. legendOptions.enabled &&
  2993. !legendOptions.floating) ?
  2994. legend.legendHeight +
  2995. pick(legendOptions.margin, 10) :
  2996. 0) -
  2997. (this.titleOffset ? this.titleOffset[2] : 0);
  2998. }
  2999. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  3000. if (this.inverted) {
  3001. xAxis.options.left = yAxis.options.left = navigator.left;
  3002. }
  3003. else {
  3004. xAxis.options.top = yAxis.options.top = navigator.top;
  3005. }
  3006. xAxis.setAxisSize();
  3007. yAxis.setAxisSize();
  3008. }
  3009. }
  3010. });
  3011. // Merge options, if no scrolling exists yet
  3012. addEvent(Chart, 'update', function (e) {
  3013. var navigatorOptions = (e.options.navigator || {}), scrollbarOptions = (e.options.scrollbar || {});
  3014. if (!this.navigator && !this.scroller &&
  3015. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  3016. merge(true, this.options.navigator, navigatorOptions);
  3017. merge(true, this.options.scrollbar, scrollbarOptions);
  3018. delete e.options.navigator;
  3019. delete e.options.scrollbar;
  3020. }
  3021. });
  3022. // Initialize navigator, if no scrolling exists yet
  3023. addEvent(Chart, 'afterUpdate', function (event) {
  3024. if (!this.navigator && !this.scroller &&
  3025. (this.options.navigator.enabled ||
  3026. this.options.scrollbar.enabled)) {
  3027. this.scroller = this.navigator = new Navigator(this);
  3028. if (pick(event.redraw, true)) {
  3029. this.redraw(event.animation); // #7067
  3030. }
  3031. }
  3032. });
  3033. // Handle adding new series
  3034. addEvent(Chart, 'afterAddSeries', function () {
  3035. if (this.navigator) {
  3036. // Recompute which series should be shown in navigator, and add them
  3037. this.navigator.setBaseSeries(null, false);
  3038. }
  3039. });
  3040. // Handle updating series
  3041. addEvent(Series, 'afterUpdate', function () {
  3042. if (this.chart.navigator && !this.options.isInternal) {
  3043. this.chart.navigator.setBaseSeries(null, false);
  3044. }
  3045. });
  3046. Chart.prototype.callbacks.push(function (chart) {
  3047. var extremes, navigator = chart.navigator;
  3048. // Initialize the navigator
  3049. if (navigator && chart.xAxis[0]) {
  3050. extremes = chart.xAxis[0].getExtremes();
  3051. navigator.render(extremes.min, extremes.max);
  3052. }
  3053. });
  3054. }
  3055. H.Navigator = Navigator;
  3056. return H.Navigator;
  3057. });
  3058. _registerModule(_modules, 'parts/OrdinalAxis.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Axis, H, U) {
  3059. /* *
  3060. *
  3061. * (c) 2010-2020 Torstein Honsi
  3062. *
  3063. * License: www.highcharts.com/license
  3064. *
  3065. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3066. *
  3067. * */
  3068. var addEvent = U.addEvent, css = U.css, defined = U.defined, pick = U.pick, timeUnits = U.timeUnits;
  3069. // Has a dependency on Navigator due to the use of Axis.toFixedRange
  3070. var Chart = H.Chart, Series = H.Series;
  3071. /**
  3072. * Extends the axis with ordinal support.
  3073. * @private
  3074. */
  3075. var OrdinalAxis;
  3076. (function (OrdinalAxis) {
  3077. /* *
  3078. *
  3079. * Classes
  3080. *
  3081. * */
  3082. /**
  3083. * @private
  3084. */
  3085. var Composition = /** @class */ (function () {
  3086. /* *
  3087. *
  3088. * Constructors
  3089. *
  3090. * */
  3091. /**
  3092. * @private
  3093. */
  3094. function Composition(axis) {
  3095. this.index = {};
  3096. this.axis = axis;
  3097. }
  3098. /* *
  3099. *
  3100. * Functions
  3101. *
  3102. * */
  3103. /**
  3104. * Calculate the ordinal positions before tick positions are calculated.
  3105. *
  3106. * @private
  3107. */
  3108. Composition.prototype.beforeSetTickPositions = function () {
  3109. var axis = this.axis, ordinal = axis.ordinal, len, ordinalPositions = [], uniqueOrdinalPositions, useOrdinal = false, dist, extremes = axis.getExtremes(), min = extremes.min, max = extremes.max, minIndex, maxIndex, slope, hasBreaks = axis.isXAxis && !!axis.options.breaks, isOrdinal = axis.options.ordinal, overscrollPointsRange = Number.MAX_VALUE, ignoreHiddenSeries = axis.chart.options.chart.ignoreHiddenSeries, i, hasBoostedSeries;
  3110. // Apply the ordinal logic
  3111. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  3112. axis.series.forEach(function (series, i) {
  3113. uniqueOrdinalPositions = [];
  3114. if ((!ignoreHiddenSeries || series.visible !== false) &&
  3115. (series.takeOrdinalPosition !== false || hasBreaks)) {
  3116. // concatenate the processed X data into the existing
  3117. // positions, or the empty array
  3118. ordinalPositions = ordinalPositions.concat(series.processedXData);
  3119. len = ordinalPositions.length;
  3120. // remove duplicates (#1588)
  3121. ordinalPositions.sort(function (a, b) {
  3122. // without a custom function it is sorted as strings
  3123. return a - b;
  3124. });
  3125. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  3126. // Check for a single-point series:
  3127. series.closestPointRange, overscrollPointsRange));
  3128. if (len) {
  3129. i = 0;
  3130. while (i < len - 1) {
  3131. if (ordinalPositions[i] !== ordinalPositions[i + 1]) {
  3132. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  3133. }
  3134. i++;
  3135. }
  3136. // Check first item:
  3137. if (uniqueOrdinalPositions[0] !== ordinalPositions[0]) {
  3138. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  3139. }
  3140. ordinalPositions = uniqueOrdinalPositions;
  3141. }
  3142. }
  3143. if (series.isSeriesBoosting) {
  3144. hasBoostedSeries = true;
  3145. }
  3146. });
  3147. if (hasBoostedSeries) {
  3148. ordinalPositions.length = 0;
  3149. }
  3150. // cache the length
  3151. len = ordinalPositions.length;
  3152. // Check if we really need the overhead of mapping axis data
  3153. // against the ordinal positions. If the series consist of
  3154. // evenly spaced data any way, we don't need any ordinal logic.
  3155. if (len > 2) { // two points have equal distance by default
  3156. dist = ordinalPositions[1] - ordinalPositions[0];
  3157. i = len - 1;
  3158. while (i-- && !useOrdinal) {
  3159. if (ordinalPositions[i + 1] - ordinalPositions[i] !== dist) {
  3160. useOrdinal = true;
  3161. }
  3162. }
  3163. // When zooming in on a week, prevent axis padding for
  3164. // weekends even though the data within the week is evenly
  3165. // spaced.
  3166. if (!axis.options.keepOrdinalPadding &&
  3167. (ordinalPositions[0] - min > dist ||
  3168. max - ordinalPositions[ordinalPositions.length - 1] >
  3169. dist)) {
  3170. useOrdinal = true;
  3171. }
  3172. }
  3173. else if (axis.options.overscroll) {
  3174. if (len === 2) {
  3175. // Exactly two points, distance for overscroll is fixed:
  3176. overscrollPointsRange =
  3177. ordinalPositions[1] - ordinalPositions[0];
  3178. }
  3179. else if (len === 1) {
  3180. // We have just one point, closest distance is unknown.
  3181. // Assume then it is last point and overscrolled range:
  3182. overscrollPointsRange = axis.options.overscroll;
  3183. ordinalPositions = [
  3184. ordinalPositions[0],
  3185. ordinalPositions[0] + overscrollPointsRange
  3186. ];
  3187. }
  3188. else {
  3189. // In case of zooming in on overscrolled range, stick to
  3190. // the old range:
  3191. overscrollPointsRange = ordinal.overscrollPointsRange;
  3192. }
  3193. }
  3194. // Record the slope and offset to compute the linear values from
  3195. // the array index. Since the ordinal positions may exceed the
  3196. // current range, get the start and end positions within it
  3197. // (#719, #665b)
  3198. if (useOrdinal) {
  3199. if (axis.options.overscroll) {
  3200. ordinal.overscrollPointsRange = overscrollPointsRange;
  3201. ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
  3202. }
  3203. // Register
  3204. ordinal.positions = ordinalPositions;
  3205. // This relies on the ordinalPositions being set. Use
  3206. // Math.max and Math.min to prevent padding on either sides
  3207. // of the data.
  3208. minIndex = axis.ordinal2lin(// #5979
  3209. Math.max(min, ordinalPositions[0]), true);
  3210. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  3211. // Set the slope and offset of the values compared to the
  3212. // indices in the ordinal positions
  3213. ordinal.slope = slope = (max - min) / (maxIndex - minIndex);
  3214. ordinal.offset = min - (minIndex * slope);
  3215. }
  3216. else {
  3217. ordinal.overscrollPointsRange = pick(axis.closestPointRange, ordinal.overscrollPointsRange);
  3218. ordinal.positions = axis.ordinal.slope = ordinal.offset =
  3219. void 0;
  3220. }
  3221. }
  3222. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  3223. ordinal.groupIntervalFactor = null; // reset for next run
  3224. };
  3225. /**
  3226. * Get the ordinal positions for the entire data set. This is necessary
  3227. * in chart panning because we need to find out what points or data
  3228. * groups are available outside the visible range. When a panning
  3229. * operation starts, if an index for the given grouping does not exists,
  3230. * it is created and cached. This index is deleted on updated data, so
  3231. * it will be regenerated the next time a panning operation starts.
  3232. *
  3233. * @private
  3234. */
  3235. Composition.prototype.getExtendedPositions = function () {
  3236. var ordinal = this, axis = ordinal.axis, axisProto = axis.constructor.prototype, chart = axis.chart, grouping = axis.series[0].currentDataGrouping, ordinalIndex = ordinal.index, key = grouping ?
  3237. grouping.count + grouping.unitName :
  3238. 'raw', overscroll = axis.options.overscroll, extremes = axis.getExtremes(), fakeAxis, fakeSeries;
  3239. // If this is the first time, or the ordinal index is deleted by
  3240. // updatedData,
  3241. // create it.
  3242. if (!ordinalIndex) {
  3243. ordinalIndex = ordinal.index = {};
  3244. }
  3245. if (!ordinalIndex[key]) {
  3246. // Create a fake axis object where the extended ordinal
  3247. // positions are emulated
  3248. fakeAxis = {
  3249. series: [],
  3250. chart: chart,
  3251. getExtremes: function () {
  3252. return {
  3253. min: extremes.dataMin,
  3254. max: extremes.dataMax + overscroll
  3255. };
  3256. },
  3257. options: {
  3258. ordinal: true
  3259. },
  3260. ordinal: {},
  3261. ordinal2lin: axisProto.ordinal2lin,
  3262. val2lin: axisProto.val2lin // #2590
  3263. };
  3264. fakeAxis.ordinal.axis = fakeAxis;
  3265. // Add the fake series to hold the full data, then apply
  3266. // processData to it
  3267. axis.series.forEach(function (series) {
  3268. fakeSeries = {
  3269. xAxis: fakeAxis,
  3270. xData: series.xData.slice(),
  3271. chart: chart,
  3272. destroyGroupedData: H.noop,
  3273. getProcessedData: H.Series.prototype.getProcessedData
  3274. };
  3275. fakeSeries.xData = fakeSeries.xData.concat(ordinal.getOverscrollPositions());
  3276. fakeSeries.options = {
  3277. dataGrouping: grouping ? {
  3278. enabled: true,
  3279. forced: true,
  3280. // doesn't matter which, use the fastest
  3281. approximation: 'open',
  3282. units: [[
  3283. grouping.unitName,
  3284. [grouping.count]
  3285. ]]
  3286. } : {
  3287. enabled: false
  3288. }
  3289. };
  3290. series.processData.apply(fakeSeries);
  3291. fakeAxis.series.push(fakeSeries);
  3292. });
  3293. // Run beforeSetTickPositions to compute the ordinalPositions
  3294. axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
  3295. // Cache it
  3296. ordinalIndex[key] = fakeAxis.ordinal.positions;
  3297. }
  3298. return ordinalIndex[key];
  3299. };
  3300. /**
  3301. * Find the factor to estimate how wide the plot area would have been if
  3302. * ordinal gaps were included. This value is used to compute an imagined
  3303. * plot width in order to establish the data grouping interval.
  3304. *
  3305. * A real world case is the intraday-candlestick example. Without this
  3306. * logic, it would show the correct data grouping when viewing a range
  3307. * within each day, but once moving the range to include the gap between
  3308. * two days, the interval would include the cut-away night hours and the
  3309. * data grouping would be wrong. So the below method tries to compensate
  3310. * by identifying the most common point interval, in this case days.
  3311. *
  3312. * An opposite case is presented in issue #718. We have a long array of
  3313. * daily data, then one point is appended one hour after the last point.
  3314. * We expect the data grouping not to change.
  3315. *
  3316. * In the future, if we find cases where this estimation doesn't work
  3317. * optimally, we might need to add a second pass to the data grouping
  3318. * logic, where we do another run with a greater interval if the number
  3319. * of data groups is more than a certain fraction of the desired group
  3320. * count.
  3321. *
  3322. * @private
  3323. */
  3324. Composition.prototype.getGroupIntervalFactor = function (xMin, xMax, series) {
  3325. var ordinal = this, axis = ordinal.axis, i, processedXData = series.processedXData, len = processedXData.length, distances = [], median, groupIntervalFactor = ordinal.groupIntervalFactor;
  3326. // Only do this computation for the first series, let the other
  3327. // inherit it (#2416)
  3328. if (!groupIntervalFactor) {
  3329. // Register all the distances in an array
  3330. for (i = 0; i < len - 1; i++) {
  3331. distances[i] =
  3332. processedXData[i + 1] - processedXData[i];
  3333. }
  3334. // Sort them and find the median
  3335. distances.sort(function (a, b) {
  3336. return a - b;
  3337. });
  3338. median = distances[Math.floor(len / 2)];
  3339. // Compensate for series that don't extend through the entire
  3340. // axis extent. #1675.
  3341. xMin = Math.max(xMin, processedXData[0]);
  3342. xMax = Math.min(xMax, processedXData[len - 1]);
  3343. ordinal.groupIntervalFactor = groupIntervalFactor =
  3344. (len * median) / (xMax - xMin);
  3345. }
  3346. // Return the factor needed for data grouping
  3347. return groupIntervalFactor;
  3348. };
  3349. /**
  3350. * Get ticks for an ordinal axis within a range where points don't
  3351. * exist. It is required when overscroll is enabled. We can't base on
  3352. * points, because we may not have any, so we use approximated
  3353. * pointRange and generate these ticks between Axis.dataMax,
  3354. * Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
  3355. * navigator scrolling.
  3356. *
  3357. * @private
  3358. */
  3359. Composition.prototype.getOverscrollPositions = function () {
  3360. var ordinal = this, axis = ordinal.axis, extraRange = axis.options.overscroll, distance = ordinal.overscrollPointsRange, positions = [], max = axis.dataMax;
  3361. if (defined(distance)) {
  3362. // Max + pointRange because we need to scroll to the last
  3363. positions.push(max);
  3364. while (max <= axis.dataMax + extraRange) {
  3365. max += distance;
  3366. positions.push(max);
  3367. }
  3368. }
  3369. return positions;
  3370. };
  3371. /**
  3372. * Make the tick intervals closer because the ordinal gaps make the
  3373. * ticks spread out or cluster.
  3374. *
  3375. * @private
  3376. */
  3377. Composition.prototype.postProcessTickInterval = function (tickInterval) {
  3378. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/
  3379. // This is a case where this algorithm doesn't work optimally. In
  3380. // this case, the tick labels are spread out per week, but all the
  3381. // gaps reside within weeks. So we have a situation where the labels
  3382. // are courser than the ordinal gaps, and thus the tick interval
  3383. // should not be altered.
  3384. var ordinal = this, axis = ordinal.axis, ordinalSlope = ordinal.slope, ret;
  3385. if (ordinalSlope) {
  3386. if (!axis.options.breaks) {
  3387. ret = tickInterval / (ordinalSlope / axis.closestPointRange);
  3388. }
  3389. else {
  3390. ret = axis.closestPointRange || tickInterval; // #7275
  3391. }
  3392. }
  3393. else {
  3394. ret = tickInterval;
  3395. }
  3396. return ret;
  3397. };
  3398. return Composition;
  3399. }());
  3400. OrdinalAxis.Composition = Composition;
  3401. /* *
  3402. *
  3403. * Functions
  3404. *
  3405. * */
  3406. /**
  3407. * Extends the axis with ordinal support.
  3408. *
  3409. * @private
  3410. *
  3411. * @param AxisClass
  3412. * Axis class to extend.
  3413. *
  3414. * @param ChartClass
  3415. * Chart class to use.
  3416. *
  3417. * @param SeriesClass
  3418. * Series class to use.
  3419. */
  3420. function compose(AxisClass, ChartClass, SeriesClass) {
  3421. AxisClass.keepProps.push('ordinal');
  3422. var axisProto = AxisClass.prototype;
  3423. /**
  3424. * In an ordinal axis, there might be areas with dense consentrations of
  3425. * points, then large gaps between some. Creating equally distributed
  3426. * ticks over this entire range may lead to a huge number of ticks that
  3427. * will later be removed. So instead, break the positions up in
  3428. * segments, find the tick positions for each segment then concatenize
  3429. * them. This method is used from both data grouping logic and X axis
  3430. * tick position logic.
  3431. *
  3432. * @private
  3433. */
  3434. AxisClass.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek, positions, closestDistance, findHigherRanks) {
  3435. if (positions === void 0) { positions = []; }
  3436. if (closestDistance === void 0) { closestDistance = 0; }
  3437. var start = 0, end, segmentPositions, higherRanks = {}, hasCrossedHigherRank, info, posLength, outsideMax, groupPositions = [], lastGroupPosition = -Number.MAX_VALUE, tickPixelIntervalOption = this.options.tickPixelInterval, time = this.chart.time,
  3438. // Record all the start positions of a segment, to use when
  3439. // deciding what's a gap in the data.
  3440. segmentStarts = [];
  3441. // The positions are not always defined, for example for ordinal
  3442. // positions when data has regular interval (#1557, #2090)
  3443. if ((!this.options.ordinal && !this.options.breaks) ||
  3444. !positions ||
  3445. positions.length < 3 ||
  3446. typeof min === 'undefined') {
  3447. return time.getTimeTicks.apply(time, arguments);
  3448. }
  3449. // Analyze the positions array to split it into segments on gaps
  3450. // larger than 5 times the closest distance. The closest distance is
  3451. // already found at this point, so we reuse that instead of
  3452. // computing it again.
  3453. posLength = positions.length;
  3454. for (end = 0; end < posLength; end++) {
  3455. outsideMax = end && positions[end - 1] > max;
  3456. if (positions[end] < min) { // Set the last position before min
  3457. start = end;
  3458. }
  3459. if (end === posLength - 1 ||
  3460. positions[end + 1] - positions[end] > closestDistance * 5 ||
  3461. outsideMax) {
  3462. // For each segment, calculate the tick positions from the
  3463. // getTimeTicks utility function. The interval will be the
  3464. // same regardless of how long the segment is.
  3465. if (positions[end] > lastGroupPosition) { // #1475
  3466. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  3467. // Prevent duplicate groups, for example for multiple
  3468. // segments within one larger time frame (#1475)
  3469. while (segmentPositions.length &&
  3470. segmentPositions[0] <= lastGroupPosition) {
  3471. segmentPositions.shift();
  3472. }
  3473. if (segmentPositions.length) {
  3474. lastGroupPosition =
  3475. segmentPositions[segmentPositions.length - 1];
  3476. }
  3477. segmentStarts.push(groupPositions.length);
  3478. groupPositions = groupPositions.concat(segmentPositions);
  3479. }
  3480. // Set start of next segment
  3481. start = end + 1;
  3482. }
  3483. if (outsideMax) {
  3484. break;
  3485. }
  3486. }
  3487. // Get the grouping info from the last of the segments. The info is
  3488. // the same for all segments.
  3489. info = segmentPositions.info;
  3490. // Optionally identify ticks with higher rank, for example when the
  3491. // ticks have crossed midnight.
  3492. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  3493. end = groupPositions.length - 1;
  3494. // Compare points two by two
  3495. for (start = 1; start < end; start++) {
  3496. if (time.dateFormat('%d', groupPositions[start]) !==
  3497. time.dateFormat('%d', groupPositions[start - 1])) {
  3498. higherRanks[groupPositions[start]] = 'day';
  3499. hasCrossedHigherRank = true;
  3500. }
  3501. }
  3502. // If the complete array has crossed midnight, we want to mark
  3503. // the first positions also as higher rank
  3504. if (hasCrossedHigherRank) {
  3505. higherRanks[groupPositions[0]] = 'day';
  3506. }
  3507. info.higherRanks = higherRanks;
  3508. }
  3509. // Save the info
  3510. info.segmentStarts = segmentStarts;
  3511. groupPositions.info = info;
  3512. // Don't show ticks within a gap in the ordinal axis, where the
  3513. // space between two points is greater than a portion of the tick
  3514. // pixel interval
  3515. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  3516. var length = groupPositions.length, i = length, itemToRemove, translated, translatedArr = [], lastTranslated, medianDistance, distance, distances = [];
  3517. // Find median pixel distance in order to keep a reasonably even
  3518. // distance between ticks (#748)
  3519. while (i--) {
  3520. translated = this.translate(groupPositions[i]);
  3521. if (lastTranslated) {
  3522. distances[i] = lastTranslated - translated;
  3523. }
  3524. translatedArr[i] = lastTranslated = translated;
  3525. }
  3526. distances.sort();
  3527. medianDistance = distances[Math.floor(distances.length / 2)];
  3528. if (medianDistance < tickPixelIntervalOption * 0.6) {
  3529. medianDistance = null;
  3530. }
  3531. // Now loop over again and remove ticks where needed
  3532. i = groupPositions[length - 1] > max ? length - 1 : length; // #817
  3533. lastTranslated = void 0;
  3534. while (i--) {
  3535. translated = translatedArr[i];
  3536. distance = Math.abs(lastTranslated - translated);
  3537. // #4175 - when axis is reversed, the distance, is negative
  3538. // but tickPixelIntervalOption positive, so we need to
  3539. // compare the same values
  3540. // Remove ticks that are closer than 0.6 times the pixel
  3541. // interval from the one to the right, but not if it is
  3542. // close to the median distance (#748).
  3543. if (lastTranslated &&
  3544. distance < tickPixelIntervalOption * 0.8 &&
  3545. (medianDistance === null || distance < medianDistance * 0.8)) {
  3546. // Is this a higher ranked position with a normal
  3547. // position to the right?
  3548. if (higherRanks[groupPositions[i]] &&
  3549. !higherRanks[groupPositions[i + 1]]) {
  3550. // Yes: remove the lower ranked neighbour to the
  3551. // right
  3552. itemToRemove = i + 1;
  3553. lastTranslated = translated; // #709
  3554. }
  3555. else {
  3556. // No: remove this one
  3557. itemToRemove = i;
  3558. }
  3559. groupPositions.splice(itemToRemove, 1);
  3560. }
  3561. else {
  3562. lastTranslated = translated;
  3563. }
  3564. }
  3565. }
  3566. return groupPositions;
  3567. };
  3568. /**
  3569. * Translate from linear (internal) to axis value.
  3570. *
  3571. * @private
  3572. * @function Highcharts.Axis#lin2val
  3573. *
  3574. * @param {number} val
  3575. * The linear abstracted value.
  3576. *
  3577. * @param {boolean} [fromIndex]
  3578. * Translate from an index in the ordinal positions rather than a
  3579. * value.
  3580. *
  3581. * @return {number}
  3582. */
  3583. axisProto.lin2val = function (val, fromIndex) {
  3584. var axis = this, ordinal = axis.ordinal, ordinalPositions = ordinal.positions, ret;
  3585. // the visible range contains only equally spaced values
  3586. if (!ordinalPositions) {
  3587. ret = val;
  3588. }
  3589. else {
  3590. var ordinalSlope = ordinal.slope, ordinalOffset = ordinal.offset, i = ordinalPositions.length - 1, linearEquivalentLeft, linearEquivalentRight, distance;
  3591. // Handle the case where we translate from the index directly,
  3592. // used only when panning an ordinal axis
  3593. if (fromIndex) {
  3594. if (val < 0) { // out of range, in effect panning to the left
  3595. val = ordinalPositions[0];
  3596. }
  3597. else if (val > i) { // out of range, panning to the right
  3598. val = ordinalPositions[i];
  3599. }
  3600. else { // split it up
  3601. i = Math.floor(val);
  3602. distance = val - i; // the decimal
  3603. }
  3604. // Loop down along the ordinal positions. When the linear
  3605. // equivalent of i matches an ordinal position, interpolate
  3606. // between the left and right values.
  3607. }
  3608. else {
  3609. while (i--) {
  3610. linearEquivalentLeft =
  3611. (ordinalSlope * i) + ordinalOffset;
  3612. if (val >= linearEquivalentLeft) {
  3613. linearEquivalentRight =
  3614. (ordinalSlope *
  3615. (i + 1)) +
  3616. ordinalOffset;
  3617. // something between 0 and 1
  3618. distance = (val - linearEquivalentLeft) /
  3619. (linearEquivalentRight - linearEquivalentLeft);
  3620. break;
  3621. }
  3622. }
  3623. }
  3624. // If the index is within the range of the ordinal positions,
  3625. // return the associated or interpolated value. If not, just
  3626. // return the value.
  3627. return (typeof distance !== 'undefined' &&
  3628. typeof ordinalPositions[i] !== 'undefined' ?
  3629. ordinalPositions[i] + (distance ?
  3630. distance *
  3631. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  3632. 0) :
  3633. val);
  3634. }
  3635. return ret;
  3636. };
  3637. /**
  3638. * Translate from a linear axis value to the corresponding ordinal axis
  3639. * position. If there are no gaps in the ordinal axis this will be the
  3640. * same. The translated value is the value that the point would have if
  3641. * the axis were linear, using the same min and max.
  3642. *
  3643. * @private
  3644. * @function Highcharts.Axis#val2lin
  3645. *
  3646. * @param {number} val
  3647. * The axis value.
  3648. *
  3649. * @param {boolean} [toIndex]
  3650. * Whether to return the index in the ordinalPositions or the new value.
  3651. *
  3652. * @return {number}
  3653. */
  3654. axisProto.val2lin = function (val, toIndex) {
  3655. var axis = this, ordinal = axis.ordinal, ordinalPositions = ordinal.positions, ret;
  3656. if (!ordinalPositions) {
  3657. ret = val;
  3658. }
  3659. else {
  3660. var ordinalLength = ordinalPositions.length, i, distance, ordinalIndex;
  3661. // first look for an exact match in the ordinalpositions array
  3662. i = ordinalLength;
  3663. while (i--) {
  3664. if (ordinalPositions[i] === val) {
  3665. ordinalIndex = i;
  3666. break;
  3667. }
  3668. }
  3669. // if that failed, find the intermediate position between the
  3670. // two nearest values
  3671. i = ordinalLength - 1;
  3672. while (i--) {
  3673. if (val > ordinalPositions[i] || i === 0) { // interpolate
  3674. // something between 0 and 1
  3675. distance = (val - ordinalPositions[i]) /
  3676. (ordinalPositions[i + 1] - ordinalPositions[i]);
  3677. ordinalIndex = i + distance;
  3678. break;
  3679. }
  3680. }
  3681. ret = toIndex ?
  3682. ordinalIndex :
  3683. ordinal.slope *
  3684. (ordinalIndex || 0) +
  3685. ordinal.offset;
  3686. }
  3687. return ret;
  3688. };
  3689. // Record this to prevent overwriting by broken-axis module (#5979)
  3690. axisProto.ordinal2lin = axisProto.val2lin;
  3691. /* eslint-disable no-invalid-this */
  3692. addEvent(AxisClass, 'afterInit', function () {
  3693. var axis = this;
  3694. if (!axis.ordinal) {
  3695. axis.ordinal = new OrdinalAxis.Composition(axis);
  3696. }
  3697. });
  3698. addEvent(AxisClass, 'foundExtremes', function () {
  3699. var axis = this;
  3700. if (axis.isXAxis &&
  3701. defined(axis.options.overscroll) &&
  3702. axis.max === axis.dataMax &&
  3703. (
  3704. // Panning is an execption. We don't want to apply
  3705. // overscroll when panning over the dataMax
  3706. !axis.chart.mouseIsDown ||
  3707. axis.isInternal) && (
  3708. // Scrollbar buttons are the other execption:
  3709. !axis.eventArgs ||
  3710. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  3711. axis.max += axis.options.overscroll;
  3712. // Live data and buttons require translation for the min:
  3713. if (!axis.isInternal && defined(axis.userMin)) {
  3714. axis.min += axis.options.overscroll;
  3715. }
  3716. }
  3717. });
  3718. // For ordinal axis, that loads data async, redraw axis after data is
  3719. // loaded. If we don't do that, axis will have the same extremes as
  3720. // previously, but ordinal positions won't be calculated. See #10290
  3721. addEvent(AxisClass, 'afterSetScale', function () {
  3722. var axis = this;
  3723. if (axis.horiz && !axis.isDirty) {
  3724. axis.isDirty = axis.isOrdinal &&
  3725. axis.chart.navigator &&
  3726. !axis.chart.navigator.adaptToUpdatedData;
  3727. }
  3728. });
  3729. addEvent(AxisClass, 'initialAxisTranslation', function () {
  3730. var axis = this;
  3731. if (axis.ordinal) {
  3732. axis.ordinal.beforeSetTickPositions();
  3733. axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
  3734. }
  3735. });
  3736. // Extending the Chart.pan method for ordinal axes
  3737. addEvent(ChartClass, 'pan', function (e) {
  3738. var chart = this, xAxis = chart.xAxis[0], overscroll = xAxis.options.overscroll, chartX = e.originalEvent.chartX, panning = chart.options.chart &&
  3739. chart.options.chart.panning, runBase = false;
  3740. if (panning &&
  3741. panning.type !== 'y' &&
  3742. xAxis.options.ordinal &&
  3743. xAxis.series.length) {
  3744. var mouseDownX = chart.mouseDownX, extremes = xAxis.getExtremes(), dataMax = extremes.dataMax, min = extremes.min, max = extremes.max, trimmedRange, hoverPoints = chart.hoverPoints, closestPointRange = (xAxis.closestPointRange ||
  3745. (xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)), pointPixelWidth = (xAxis.translationSlope *
  3746. (xAxis.ordinal.slope || closestPointRange)),
  3747. // how many ordinal units did we move?
  3748. movedUnits = (mouseDownX - chartX) / pointPixelWidth,
  3749. // get index of all the chart's points
  3750. extendedAxis = { ordinal: { positions: xAxis.ordinal.getExtendedPositions() } }, ordinalPositions, searchAxisLeft, lin2val = xAxis.lin2val, val2lin = xAxis.val2lin, searchAxisRight;
  3751. // we have an ordinal axis, but the data is equally spaced
  3752. if (!extendedAxis.ordinal.positions) {
  3753. runBase = true;
  3754. }
  3755. else if (Math.abs(movedUnits) > 1) {
  3756. // Remove active points for shared tooltip
  3757. if (hoverPoints) {
  3758. hoverPoints.forEach(function (point) {
  3759. point.setState();
  3760. });
  3761. }
  3762. if (movedUnits < 0) {
  3763. searchAxisLeft = extendedAxis;
  3764. searchAxisRight = xAxis.ordinal.positions ? xAxis : extendedAxis;
  3765. }
  3766. else {
  3767. searchAxisLeft = xAxis.ordinal.positions ? xAxis : extendedAxis;
  3768. searchAxisRight = extendedAxis;
  3769. }
  3770. // In grouped data series, the last ordinal position
  3771. // represents the grouped data, which is to the left of the
  3772. // real data max. If we don't compensate for this, we will
  3773. // be allowed to pan grouped data series passed the right of
  3774. // the plot area.
  3775. ordinalPositions = searchAxisRight.ordinal.positions;
  3776. if (dataMax >
  3777. ordinalPositions[ordinalPositions.length - 1]) {
  3778. ordinalPositions.push(dataMax);
  3779. }
  3780. // Get the new min and max values by getting the ordinal
  3781. // index for the current extreme, then add the moved units
  3782. // and translate back to values. This happens on the
  3783. // extended ordinal positions if the new position is out of
  3784. // range, else it happens on the current x axis which is
  3785. // smaller and faster.
  3786. chart.fixedRange = max - min;
  3787. trimmedRange = xAxis.navigatorAxis.toFixedRange(null, null, lin2val.apply(searchAxisLeft, [
  3788. val2lin.apply(searchAxisLeft, [min, true]) + movedUnits,
  3789. true // translate from index
  3790. ]), lin2val.apply(searchAxisRight, [
  3791. val2lin.apply(searchAxisRight, [max, true]) + movedUnits,
  3792. true // translate from index
  3793. ]));
  3794. // Apply it if it is within the available data range
  3795. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  3796. trimmedRange.max <= Math.max(dataMax, max) + overscroll) {
  3797. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  3798. }
  3799. chart.mouseDownX = chartX; // set new reference for next run
  3800. css(chart.container, { cursor: 'move' });
  3801. }
  3802. }
  3803. else {
  3804. runBase = true;
  3805. }
  3806. // revert to the linear chart.pan version
  3807. if (runBase || (panning && /y/.test(panning.type))) {
  3808. if (overscroll) {
  3809. xAxis.max = xAxis.dataMax + overscroll;
  3810. }
  3811. }
  3812. else {
  3813. e.preventDefault();
  3814. }
  3815. });
  3816. addEvent(SeriesClass, 'updatedData', function () {
  3817. var xAxis = this.xAxis;
  3818. // Destroy the extended ordinal index on updated data
  3819. if (xAxis && xAxis.options.ordinal) {
  3820. delete xAxis.ordinal.index;
  3821. }
  3822. });
  3823. /* eslint-enable no-invalid-this */
  3824. }
  3825. OrdinalAxis.compose = compose;
  3826. })(OrdinalAxis || (OrdinalAxis = {}));
  3827. OrdinalAxis.compose(Axis, Chart, Series); // @todo move to StockChart, remove from master
  3828. return OrdinalAxis;
  3829. });
  3830. _registerModule(_modules, 'modules/broken-axis.src.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['parts/Stacking.js']], function (Axis, H, U, StackItem) {
  3831. /* *
  3832. *
  3833. * (c) 2009-2020 Torstein Honsi
  3834. *
  3835. * License: www.highcharts.com/license
  3836. *
  3837. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3838. *
  3839. * */
  3840. var addEvent = U.addEvent, find = U.find, fireEvent = U.fireEvent, isArray = U.isArray, isNumber = U.isNumber, pick = U.pick;
  3841. var Series = H.Series;
  3842. /* eslint-disable valid-jsdoc */
  3843. /**
  3844. * Provides support for broken axes.
  3845. * @private
  3846. * @class
  3847. */
  3848. var BrokenAxisAdditions = /** @class */ (function () {
  3849. /* *
  3850. *
  3851. * Constructors
  3852. *
  3853. * */
  3854. function BrokenAxisAdditions(axis) {
  3855. this.hasBreaks = false;
  3856. this.axis = axis;
  3857. }
  3858. /* *
  3859. *
  3860. * Static Functions
  3861. *
  3862. * */
  3863. /**
  3864. * @private
  3865. */
  3866. BrokenAxisAdditions.isInBreak = function (brk, val) {
  3867. var ret, repeat = brk.repeat || Infinity, from = brk.from, length = brk.to - brk.from, test = (val >= from ?
  3868. (val - from) % repeat :
  3869. repeat - ((from - val) % repeat));
  3870. if (!brk.inclusive) {
  3871. ret = test < length && test !== 0;
  3872. }
  3873. else {
  3874. ret = test <= length;
  3875. }
  3876. return ret;
  3877. };
  3878. /**
  3879. * @private
  3880. */
  3881. BrokenAxisAdditions.lin2Val = function (val) {
  3882. var axis = this;
  3883. var brokenAxis = axis.brokenAxis;
  3884. var breakArray = brokenAxis && brokenAxis.breakArray;
  3885. if (!breakArray) {
  3886. return val;
  3887. }
  3888. var nval = val, brk, i;
  3889. for (i = 0; i < breakArray.length; i++) {
  3890. brk = breakArray[i];
  3891. if (brk.from >= nval) {
  3892. break;
  3893. }
  3894. else if (brk.to < nval) {
  3895. nval += brk.len;
  3896. }
  3897. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  3898. nval += brk.len;
  3899. }
  3900. }
  3901. return nval;
  3902. };
  3903. /**
  3904. * @private
  3905. */
  3906. BrokenAxisAdditions.val2Lin = function (val) {
  3907. var axis = this;
  3908. var brokenAxis = axis.brokenAxis;
  3909. var breakArray = brokenAxis && brokenAxis.breakArray;
  3910. if (!breakArray) {
  3911. return val;
  3912. }
  3913. var nval = val, brk, i;
  3914. for (i = 0; i < breakArray.length; i++) {
  3915. brk = breakArray[i];
  3916. if (brk.to <= val) {
  3917. nval -= brk.len;
  3918. }
  3919. else if (brk.from >= val) {
  3920. break;
  3921. }
  3922. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  3923. nval -= (val - brk.from);
  3924. break;
  3925. }
  3926. }
  3927. return nval;
  3928. };
  3929. /* *
  3930. *
  3931. * Functions
  3932. *
  3933. * */
  3934. /**
  3935. * Returns the first break found where the x is larger then break.from and
  3936. * smaller then break.to.
  3937. *
  3938. * @param {number} x
  3939. * The number which should be within a break.
  3940. *
  3941. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  3942. * The array of breaks to search within.
  3943. *
  3944. * @return {Highcharts.XAxisBreaksOptions|undefined}
  3945. * Returns the first break found that matches, returns false if no break is
  3946. * found.
  3947. */
  3948. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  3949. return find(breaks, function (b) {
  3950. return b.from < x && x < b.to;
  3951. });
  3952. };
  3953. /**
  3954. * @private
  3955. */
  3956. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  3957. var brokenAxis = this;
  3958. var axis = brokenAxis.axis;
  3959. var breaks = axis.options.breaks, i = breaks && breaks.length, inbrk, keep, ret;
  3960. if (i) {
  3961. while (i--) {
  3962. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  3963. inbrk = true;
  3964. if (!keep) {
  3965. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  3966. }
  3967. }
  3968. }
  3969. if (inbrk && testKeep) {
  3970. ret = inbrk && !keep;
  3971. }
  3972. else {
  3973. ret = inbrk;
  3974. }
  3975. }
  3976. return ret;
  3977. };
  3978. /**
  3979. * Dynamically set or unset breaks in an axis. This function in lighter than
  3980. * usin Axis.update, and it also preserves animation.
  3981. *
  3982. * @private
  3983. * @function Highcharts.Axis#setBreaks
  3984. *
  3985. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  3986. * The breaks to add. When `undefined` it removes existing breaks.
  3987. *
  3988. * @param {boolean} [redraw=true]
  3989. * Whether to redraw the chart immediately.
  3990. *
  3991. * @return {void}
  3992. */
  3993. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  3994. var brokenAxis = this;
  3995. var axis = brokenAxis.axis;
  3996. var hasBreaks = (isArray(breaks) && !!breaks.length);
  3997. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  3998. brokenAxis.hasBreaks = hasBreaks;
  3999. axis.options.breaks = axis.userOptions.breaks = breaks;
  4000. axis.forceRedraw = true; // Force recalculation in setScale
  4001. // Recalculate series related to the axis.
  4002. axis.series.forEach(function (series) {
  4003. series.isDirty = true;
  4004. });
  4005. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  4006. // Revert to prototype functions
  4007. delete axis.val2lin;
  4008. delete axis.lin2val;
  4009. }
  4010. if (hasBreaks) {
  4011. axis.userOptions.ordinal = false;
  4012. axis.lin2val = BrokenAxisAdditions.lin2Val;
  4013. axis.val2lin = BrokenAxisAdditions.val2Lin;
  4014. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  4015. // If trying to set extremes inside a break, extend min to
  4016. // after, and max to before the break ( #3857 )
  4017. if (brokenAxis.hasBreaks) {
  4018. var axisBreak, breaks = this.options.breaks;
  4019. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  4020. newMin = axisBreak.to;
  4021. }
  4022. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  4023. newMax = axisBreak.from;
  4024. }
  4025. // If both min and max is within the same break.
  4026. if (newMax < newMin) {
  4027. newMax = newMin;
  4028. }
  4029. }
  4030. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  4031. };
  4032. axis.setAxisTranslation = function (saveOld) {
  4033. Axis.prototype.setAxisTranslation.call(this, saveOld);
  4034. brokenAxis.unitLength = null;
  4035. if (brokenAxis.hasBreaks) {
  4036. var breaks = axis.options.breaks || [],
  4037. // Temporary one:
  4038. breakArrayT = [], breakArray = [], length = 0, inBrk, repeat, min = axis.userMin || axis.min, max = axis.userMax || axis.max, pointRangePadding = pick(axis.pointRangePadding, 0), start, i;
  4039. // Min & max check (#4247)
  4040. breaks.forEach(function (brk) {
  4041. repeat = brk.repeat || Infinity;
  4042. if (BrokenAxisAdditions.isInBreak(brk, min)) {
  4043. min +=
  4044. (brk.to % repeat) -
  4045. (min % repeat);
  4046. }
  4047. if (BrokenAxisAdditions.isInBreak(brk, max)) {
  4048. max -=
  4049. (max % repeat) -
  4050. (brk.from % repeat);
  4051. }
  4052. });
  4053. // Construct an array holding all breaks in the axis
  4054. breaks.forEach(function (brk) {
  4055. start = brk.from;
  4056. repeat = brk.repeat || Infinity;
  4057. while (start - repeat > min) {
  4058. start -= repeat;
  4059. }
  4060. while (start < min) {
  4061. start += repeat;
  4062. }
  4063. for (i = start; i < max; i += repeat) {
  4064. breakArrayT.push({
  4065. value: i,
  4066. move: 'in'
  4067. });
  4068. breakArrayT.push({
  4069. value: i + (brk.to - brk.from),
  4070. move: 'out',
  4071. size: brk.breakSize
  4072. });
  4073. }
  4074. });
  4075. breakArrayT.sort(function (a, b) {
  4076. return ((a.value === b.value) ?
  4077. ((a.move === 'in' ? 0 : 1) -
  4078. (b.move === 'in' ? 0 : 1)) :
  4079. a.value - b.value);
  4080. });
  4081. // Simplify the breaks
  4082. inBrk = 0;
  4083. start = min;
  4084. breakArrayT.forEach(function (brk) {
  4085. inBrk += (brk.move === 'in' ? 1 : -1);
  4086. if (inBrk === 1 && brk.move === 'in') {
  4087. start = brk.value;
  4088. }
  4089. if (inBrk === 0) {
  4090. breakArray.push({
  4091. from: start,
  4092. to: brk.value,
  4093. len: brk.value - start - (brk.size || 0)
  4094. });
  4095. length += brk.value - start - (brk.size || 0);
  4096. }
  4097. });
  4098. /**
  4099. * HC <= 8 backwards compatibility, used by demo samples.
  4100. * @deprecated
  4101. * @private
  4102. * @requires modules/broken-axis
  4103. */
  4104. axis.breakArray = brokenAxis.breakArray = breakArray;
  4105. // Used with staticScale, and below the actual axis length,
  4106. // when breaks are substracted.
  4107. brokenAxis.unitLength = max - min - length + pointRangePadding;
  4108. fireEvent(axis, 'afterBreaks');
  4109. if (axis.staticScale) {
  4110. axis.transA = axis.staticScale;
  4111. }
  4112. else if (brokenAxis.unitLength) {
  4113. axis.transA *=
  4114. (max - axis.min + pointRangePadding) /
  4115. brokenAxis.unitLength;
  4116. }
  4117. if (pointRangePadding) {
  4118. axis.minPixelPadding =
  4119. axis.transA * axis.minPointOffset;
  4120. }
  4121. axis.min = min;
  4122. axis.max = max;
  4123. }
  4124. };
  4125. }
  4126. if (pick(redraw, true)) {
  4127. axis.chart.redraw();
  4128. }
  4129. };
  4130. return BrokenAxisAdditions;
  4131. }());
  4132. /**
  4133. * Axis with support of broken data rows.
  4134. * @private
  4135. * @class
  4136. */
  4137. var BrokenAxis = /** @class */ (function () {
  4138. function BrokenAxis() {
  4139. }
  4140. /**
  4141. * Adds support for broken axes.
  4142. * @private
  4143. */
  4144. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  4145. AxisClass.keepProps.push('brokenAxis');
  4146. var seriesProto = Series.prototype;
  4147. /**
  4148. * @private
  4149. */
  4150. seriesProto.drawBreaks = function (axis, keys) {
  4151. var series = this, points = series.points, breaks, threshold, eventName, y;
  4152. if (axis && // #5950
  4153. axis.brokenAxis &&
  4154. axis.brokenAxis.hasBreaks) {
  4155. var brokenAxis_1 = axis.brokenAxis;
  4156. keys.forEach(function (key) {
  4157. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  4158. threshold = axis.isXAxis ?
  4159. axis.min :
  4160. pick(series.options.threshold, axis.min);
  4161. points.forEach(function (point) {
  4162. y = pick(point['stack' + key.toUpperCase()], point[key]);
  4163. breaks.forEach(function (brk) {
  4164. if (isNumber(threshold) && isNumber(y)) {
  4165. eventName = false;
  4166. if ((threshold < brk.from && y > brk.to) ||
  4167. (threshold > brk.from && y < brk.from)) {
  4168. eventName = 'pointBreak';
  4169. }
  4170. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  4171. (threshold > brk.from && y > brk.to && y < brk.from)) {
  4172. eventName = 'pointInBreak';
  4173. }
  4174. if (eventName) {
  4175. fireEvent(axis, eventName, { point: point, brk: brk });
  4176. }
  4177. }
  4178. });
  4179. });
  4180. });
  4181. }
  4182. };
  4183. /**
  4184. * Extend getGraphPath by identifying gaps in the data so that we can
  4185. * draw a gap in the line or area. This was moved from ordinal axis
  4186. * module to broken axis module as of #5045.
  4187. *
  4188. * @private
  4189. * @function Highcharts.Series#gappedPath
  4190. *
  4191. * @return {Highcharts.SVGPathArray}
  4192. * Gapped path
  4193. */
  4194. seriesProto.gappedPath = function () {
  4195. var currentDataGrouping = this.currentDataGrouping, groupingSize = currentDataGrouping && currentDataGrouping.gapSize, gapSize = this.options.gapSize, points = this.points.slice(), i = points.length - 1, yAxis = this.yAxis, stack;
  4196. /**
  4197. * Defines when to display a gap in the graph, together with the
  4198. * [gapUnit](plotOptions.series.gapUnit) option.
  4199. *
  4200. * In case when `dataGrouping` is enabled, points can be grouped
  4201. * into a larger time span. This can make the grouped points to have
  4202. * a greater distance than the absolute value of `gapSize` property,
  4203. * which will result in disappearing graph completely. To prevent
  4204. * this situation the mentioned distance between grouped points is
  4205. * used instead of previously defined `gapSize`.
  4206. *
  4207. * In practice, this option is most often used to visualize gaps in
  4208. * time series. In a stock chart, intraday data is available for
  4209. * daytime hours, while gaps will appear in nights and weekends.
  4210. *
  4211. * @see [gapUnit](plotOptions.series.gapUnit)
  4212. * @see [xAxis.breaks](#xAxis.breaks)
  4213. *
  4214. * @sample {highstock} stock/plotoptions/series-gapsize/
  4215. * Setting the gap size to 2 introduces gaps for weekends
  4216. * in daily datasets.
  4217. *
  4218. * @type {number}
  4219. * @default 0
  4220. * @product highstock
  4221. * @requires modules/broken-axis
  4222. * @apioption plotOptions.series.gapSize
  4223. */
  4224. /**
  4225. * Together with [gapSize](plotOptions.series.gapSize), this option
  4226. * defines where to draw gaps in the graph.
  4227. *
  4228. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  4229. * means that if the distance between two points is greater than
  4230. * 5 times that of the two closest points, the graph will be broken.
  4231. *
  4232. * When the `gapUnit` is `"value"`, the gap is based on absolute
  4233. * axis values, which on a datetime axis is milliseconds. This also
  4234. * applies to the navigator series that inherits gap options from
  4235. * the base series.
  4236. *
  4237. * @see [gapSize](plotOptions.series.gapSize)
  4238. *
  4239. * @type {string}
  4240. * @default relative
  4241. * @since 5.0.13
  4242. * @product highstock
  4243. * @validvalue ["relative", "value"]
  4244. * @requires modules/broken-axis
  4245. * @apioption plotOptions.series.gapUnit
  4246. */
  4247. if (gapSize && i > 0) { // #5008
  4248. // Gap unit is relative
  4249. if (this.options.gapUnit !== 'value') {
  4250. gapSize *= this.basePointRange;
  4251. }
  4252. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  4253. if (groupingSize &&
  4254. groupingSize > gapSize &&
  4255. // Except when DG is forced (e.g. from other series)
  4256. // and has lower granularity than actual points (#11351)
  4257. groupingSize >= this.basePointRange) {
  4258. gapSize = groupingSize;
  4259. }
  4260. // extension for ordinal breaks
  4261. var current = void 0, next = void 0;
  4262. while (i--) {
  4263. // Reassign next if it is not visible
  4264. if (!(next && next.visible !== false)) {
  4265. next = points[i + 1];
  4266. }
  4267. current = points[i];
  4268. // Skip iteration if one of the points is not visible
  4269. if (next.visible === false || current.visible === false) {
  4270. continue;
  4271. }
  4272. if (next.x - current.x > gapSize) {
  4273. var xRange = (current.x + next.x) / 2;
  4274. points.splice(// insert after this one
  4275. i + 1, 0, {
  4276. isNull: true,
  4277. x: xRange
  4278. });
  4279. // For stacked chart generate empty stack items, #6546
  4280. if (yAxis.stacking && this.options.stacking) {
  4281. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  4282. new StackItem(yAxis, yAxis.options
  4283. .stackLabels, false, xRange, this.stack);
  4284. stack.total = 0;
  4285. }
  4286. }
  4287. // Assign current to next for the upcoming iteration
  4288. next = current;
  4289. }
  4290. }
  4291. // Call base method
  4292. return this.getGraphPath(points);
  4293. };
  4294. /* eslint-disable no-invalid-this */
  4295. addEvent(AxisClass, 'init', function () {
  4296. var axis = this;
  4297. if (!axis.brokenAxis) {
  4298. axis.brokenAxis = new BrokenAxisAdditions(axis);
  4299. }
  4300. });
  4301. addEvent(AxisClass, 'afterInit', function () {
  4302. if (typeof this.brokenAxis !== 'undefined') {
  4303. this.brokenAxis.setBreaks(this.options.breaks, false);
  4304. }
  4305. });
  4306. addEvent(AxisClass, 'afterSetTickPositions', function () {
  4307. var axis = this;
  4308. var brokenAxis = axis.brokenAxis;
  4309. if (brokenAxis &&
  4310. brokenAxis.hasBreaks) {
  4311. var tickPositions = this.tickPositions, info = this.tickPositions.info, newPositions = [], i;
  4312. for (i = 0; i < tickPositions.length; i++) {
  4313. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  4314. newPositions.push(tickPositions[i]);
  4315. }
  4316. }
  4317. this.tickPositions = newPositions;
  4318. this.tickPositions.info = info;
  4319. }
  4320. });
  4321. // Force Axis to be not-ordinal when breaks are defined
  4322. addEvent(AxisClass, 'afterSetOptions', function () {
  4323. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  4324. this.options.ordinal = false;
  4325. }
  4326. });
  4327. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  4328. var _a = this, isDirty = _a.isDirty, connectNulls = _a.options.connectNulls, points = _a.points, xAxis = _a.xAxis, yAxis = _a.yAxis;
  4329. // Set, or reset visibility of the points. Axis.setBreaks marks the
  4330. // series as isDirty
  4331. if (isDirty) {
  4332. var i = points.length;
  4333. while (i--) {
  4334. var point = points[i];
  4335. // Respect nulls inside the break (#4275)
  4336. var nullGap = point.y === null && connectNulls === false;
  4337. var isPointInBreak = (!nullGap && ((xAxis &&
  4338. xAxis.brokenAxis &&
  4339. xAxis.brokenAxis.isInAnyBreak(point.x, true)) || (yAxis &&
  4340. yAxis.brokenAxis &&
  4341. yAxis.brokenAxis.isInAnyBreak(point.y, true))));
  4342. // Set point.visible if in any break.
  4343. // If not in break, reset visible to original value.
  4344. point.visible = isPointInBreak ?
  4345. false :
  4346. point.options.visible !== false;
  4347. }
  4348. }
  4349. });
  4350. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  4351. this.drawBreaks(this.xAxis, ['x']);
  4352. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  4353. });
  4354. };
  4355. return BrokenAxis;
  4356. }());
  4357. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  4358. return BrokenAxis;
  4359. });
  4360. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [], function () {
  4361. });
  4362. _registerModule(_modules, 'parts/DataGrouping.js', [_modules['parts/DateTimeAxis.js'], _modules['parts/Globals.js'], _modules['parts/Options.js'], _modules['parts/Point.js'], _modules['parts/Tooltip.js'], _modules['parts/Utilities.js']], function (DateTimeAxis, H, O, Point, Tooltip, U) {
  4363. /* *
  4364. *
  4365. * (c) 2010-2020 Torstein Honsi
  4366. *
  4367. * License: www.highcharts.com/license
  4368. *
  4369. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4370. *
  4371. * */
  4372. /**
  4373. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  4374. */
  4375. /**
  4376. * @interface Highcharts.DataGroupingInfoObject
  4377. */ /**
  4378. * @name Highcharts.DataGroupingInfoObject#length
  4379. * @type {number}
  4380. */ /**
  4381. * @name Highcharts.DataGroupingInfoObject#options
  4382. * @type {Highcharts.SeriesOptionsType|undefined}
  4383. */ /**
  4384. * @name Highcharts.DataGroupingInfoObject#start
  4385. * @type {number}
  4386. */
  4387. ''; // detach doclets above
  4388. var defaultOptions = O.defaultOptions;
  4389. var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, correctFloat = U.correctFloat, defined = U.defined, error = U.error, extend = U.extend, format = U.format, isNumber = U.isNumber, merge = U.merge, pick = U.pick;
  4390. var Axis = H.Axis, Series = H.Series;
  4391. /* ************************************************************************** *
  4392. * Start data grouping module *
  4393. * ************************************************************************** */
  4394. /* eslint-disable no-invalid-this, valid-jsdoc */
  4395. /**
  4396. * Define the available approximation types. The data grouping
  4397. * approximations takes an array or numbers as the first parameter. In case
  4398. * of ohlc, four arrays are sent in as four parameters. Each array consists
  4399. * only of numbers. In case null values belong to the group, the property
  4400. * .hasNulls will be set to true on the array.
  4401. *
  4402. * @product highstock
  4403. *
  4404. * @private
  4405. * @name Highcharts.approximations
  4406. * @type {Highcharts.Dictionary<Function>}
  4407. */
  4408. var approximations = H.approximations = {
  4409. sum: function (arr) {
  4410. var len = arr.length, ret;
  4411. // 1. it consists of nulls exclusive
  4412. if (!len && arr.hasNulls) {
  4413. ret = null;
  4414. // 2. it has a length and real values
  4415. }
  4416. else if (len) {
  4417. ret = 0;
  4418. while (len--) {
  4419. ret += arr[len];
  4420. }
  4421. }
  4422. // 3. it has zero length, so just return undefined
  4423. // => doNothing()
  4424. return ret;
  4425. },
  4426. average: function (arr) {
  4427. var len = arr.length, ret = approximations.sum(arr);
  4428. // If we have a number, return it divided by the length. If not,
  4429. // return null or undefined based on what the sum method finds.
  4430. if (isNumber(ret) && len) {
  4431. ret = correctFloat(ret / len);
  4432. }
  4433. return ret;
  4434. },
  4435. // The same as average, but for series with multiple values, like area
  4436. // ranges.
  4437. averages: function () {
  4438. var ret = [];
  4439. [].forEach.call(arguments, function (arr) {
  4440. ret.push(approximations.average(arr));
  4441. });
  4442. // Return undefined when first elem. is undefined and let
  4443. // sum method handle null (#7377)
  4444. return typeof ret[0] === 'undefined' ? void 0 : ret;
  4445. },
  4446. open: function (arr) {
  4447. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  4448. },
  4449. high: function (arr) {
  4450. return arr.length ?
  4451. arrayMax(arr) :
  4452. (arr.hasNulls ? null : void 0);
  4453. },
  4454. low: function (arr) {
  4455. return arr.length ?
  4456. arrayMin(arr) :
  4457. (arr.hasNulls ? null : void 0);
  4458. },
  4459. close: function (arr) {
  4460. return arr.length ?
  4461. arr[arr.length - 1] :
  4462. (arr.hasNulls ? null : void 0);
  4463. },
  4464. // ohlc and range are special cases where a multidimensional array is
  4465. // input and an array is output
  4466. ohlc: function (open, high, low, close) {
  4467. open = approximations.open(open);
  4468. high = approximations.high(high);
  4469. low = approximations.low(low);
  4470. close = approximations.close(close);
  4471. if (isNumber(open) ||
  4472. isNumber(high) ||
  4473. isNumber(low) ||
  4474. isNumber(close)) {
  4475. return [open, high, low, close];
  4476. }
  4477. // else, return is undefined
  4478. },
  4479. range: function (low, high) {
  4480. low = approximations.low(low);
  4481. high = approximations.high(high);
  4482. if (isNumber(low) || isNumber(high)) {
  4483. return [low, high];
  4484. }
  4485. if (low === null && high === null) {
  4486. return null;
  4487. }
  4488. // else, return is undefined
  4489. }
  4490. };
  4491. var groupData = function (xData, yData, groupPositions, approximation) {
  4492. var series = this, data = series.data, dataOptions = series.options && series.options.data, groupedXData = [], groupedYData = [], groupMap = [], dataLength = xData.length, pointX, pointY, groupedY,
  4493. // when grouping the fake extended axis for panning,
  4494. // we don't need to consider y
  4495. handleYData = !!yData, values = [], approximationFn, pointArrayMap = series.pointArrayMap, pointArrayMapLength = pointArrayMap && pointArrayMap.length, extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']), pos = 0, start = 0, valuesLen, i, j;
  4496. /**
  4497. * @private
  4498. */
  4499. function getApproximation(approx) {
  4500. if (typeof approx === 'function') {
  4501. return approx;
  4502. }
  4503. if (approximations[approx]) {
  4504. return approximations[approx];
  4505. }
  4506. return approximations[(series.getDGApproximation && series.getDGApproximation()) ||
  4507. 'average'];
  4508. }
  4509. approximationFn = getApproximation(approximation);
  4510. // Calculate values array size from pointArrayMap length
  4511. if (pointArrayMapLength) {
  4512. pointArrayMap.forEach(function () {
  4513. values.push([]);
  4514. });
  4515. }
  4516. else {
  4517. values.push([]);
  4518. }
  4519. valuesLen = pointArrayMapLength || 1;
  4520. // Start with the first point within the X axis range (#2696)
  4521. for (i = 0; i <= dataLength; i++) {
  4522. if (xData[i] >= groupPositions[0]) {
  4523. break;
  4524. }
  4525. }
  4526. for (i; i <= dataLength; i++) {
  4527. // when a new group is entered, summarize and initialize
  4528. // the previous group
  4529. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  4530. xData[i] >= groupPositions[pos + 1]) ||
  4531. i === dataLength) { // get the last group
  4532. // get group x and y
  4533. pointX = groupPositions[pos];
  4534. series.dataGroupInfo = {
  4535. start: series.cropStart + start,
  4536. length: values[0].length
  4537. };
  4538. groupedY = approximationFn.apply(series, values);
  4539. // By default, let options of the first grouped point be passed over
  4540. // to the grouped point. This allows preserving properties like
  4541. // `name` and `color` or custom properties. Implementers can
  4542. // override this from the approximation function, where they can
  4543. // write custom options to `this.dataGroupInfo.options`.
  4544. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  4545. // Convert numbers and arrays into objects
  4546. series.dataGroupInfo.options = merge(series.pointClass.prototype
  4547. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  4548. // Make sure the raw data (x, y, open, high etc) is not copied
  4549. // over and overwriting approximated data.
  4550. extendedPointArrayMap.forEach(function (key) {
  4551. delete series.dataGroupInfo.options[key];
  4552. });
  4553. }
  4554. // push the grouped data
  4555. if (typeof groupedY !== 'undefined') {
  4556. groupedXData.push(pointX);
  4557. groupedYData.push(groupedY);
  4558. groupMap.push(series.dataGroupInfo);
  4559. }
  4560. // reset the aggregate arrays
  4561. start = i;
  4562. for (j = 0; j < valuesLen; j++) {
  4563. values[j].length = 0; // faster than values[j] = []
  4564. values[j].hasNulls = false;
  4565. }
  4566. // Advance on the group positions
  4567. pos += 1;
  4568. // don't loop beyond the last group
  4569. if (i === dataLength) {
  4570. break;
  4571. }
  4572. }
  4573. // break out
  4574. if (i === dataLength) {
  4575. break;
  4576. }
  4577. // for each raw data point, push it to an array that contains all values
  4578. // for this specific group
  4579. if (pointArrayMap) {
  4580. var index = series.cropStart + i, point = (data && data[index]) ||
  4581. series.pointClass.prototype.applyOptions.apply({
  4582. series: series
  4583. }, [dataOptions[index]]), val;
  4584. for (j = 0; j < pointArrayMapLength; j++) {
  4585. val = point[pointArrayMap[j]];
  4586. if (isNumber(val)) {
  4587. values[j].push(val);
  4588. }
  4589. else if (val === null) {
  4590. values[j].hasNulls = true;
  4591. }
  4592. }
  4593. }
  4594. else {
  4595. pointY = handleYData ? yData[i] : null;
  4596. if (isNumber(pointY)) {
  4597. values[0].push(pointY);
  4598. }
  4599. else if (pointY === null) {
  4600. values[0].hasNulls = true;
  4601. }
  4602. }
  4603. }
  4604. return {
  4605. groupedXData: groupedXData,
  4606. groupedYData: groupedYData,
  4607. groupMap: groupMap
  4608. };
  4609. };
  4610. var dataGrouping = {
  4611. approximations: approximations,
  4612. groupData: groupData
  4613. };
  4614. // -----------------------------------------------------------------------------
  4615. // The following code applies to implementation of data grouping on a Series
  4616. var seriesProto = Series.prototype, baseProcessData = seriesProto.processData, baseGeneratePoints = seriesProto.generatePoints,
  4617. /** @ignore */
  4618. commonOptions = {
  4619. // enabled: null, // (true for stock charts, false for basic),
  4620. // forced: undefined,
  4621. groupPixelWidth: 2,
  4622. // the first one is the point or start value, the second is the start
  4623. // value if we're dealing with range, the third one is the end value if
  4624. // dealing with a range
  4625. dateTimeLabelFormats: {
  4626. millisecond: [
  4627. '%A, %b %e, %H:%M:%S.%L',
  4628. '%A, %b %e, %H:%M:%S.%L',
  4629. '-%H:%M:%S.%L'
  4630. ],
  4631. second: [
  4632. '%A, %b %e, %H:%M:%S',
  4633. '%A, %b %e, %H:%M:%S',
  4634. '-%H:%M:%S'
  4635. ],
  4636. minute: [
  4637. '%A, %b %e, %H:%M',
  4638. '%A, %b %e, %H:%M',
  4639. '-%H:%M'
  4640. ],
  4641. hour: [
  4642. '%A, %b %e, %H:%M',
  4643. '%A, %b %e, %H:%M',
  4644. '-%H:%M'
  4645. ],
  4646. day: [
  4647. '%A, %b %e, %Y',
  4648. '%A, %b %e',
  4649. '-%A, %b %e, %Y'
  4650. ],
  4651. week: [
  4652. 'Week from %A, %b %e, %Y',
  4653. '%A, %b %e',
  4654. '-%A, %b %e, %Y'
  4655. ],
  4656. month: [
  4657. '%B %Y',
  4658. '%B',
  4659. '-%B %Y'
  4660. ],
  4661. year: [
  4662. '%Y',
  4663. '%Y',
  4664. '-%Y'
  4665. ]
  4666. }
  4667. // smoothed = false, // enable this for navigator series only
  4668. }, specificOptions = {
  4669. line: {},
  4670. spline: {},
  4671. area: {},
  4672. areaspline: {},
  4673. arearange: {},
  4674. column: {
  4675. groupPixelWidth: 10
  4676. },
  4677. columnrange: {
  4678. groupPixelWidth: 10
  4679. },
  4680. candlestick: {
  4681. groupPixelWidth: 10
  4682. },
  4683. ohlc: {
  4684. groupPixelWidth: 5
  4685. }
  4686. },
  4687. // units are defined in a separate array to allow complete overriding in
  4688. // case of a user option
  4689. defaultDataGroupingUnits = H.defaultDataGroupingUnits = [
  4690. [
  4691. 'millisecond',
  4692. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  4693. ], [
  4694. 'second',
  4695. [1, 2, 5, 10, 15, 30]
  4696. ], [
  4697. 'minute',
  4698. [1, 2, 5, 10, 15, 30]
  4699. ], [
  4700. 'hour',
  4701. [1, 2, 3, 4, 6, 8, 12]
  4702. ], [
  4703. 'day',
  4704. [1]
  4705. ], [
  4706. 'week',
  4707. [1]
  4708. ], [
  4709. 'month',
  4710. [1, 3, 6]
  4711. ], [
  4712. 'year',
  4713. null
  4714. ]
  4715. ];
  4716. // Set default approximations to the prototypes if present. Properties are
  4717. // inherited down. Can be overridden for individual series types.
  4718. seriesProto.getDGApproximation = function () {
  4719. if (this.is('arearange')) {
  4720. return 'range';
  4721. }
  4722. if (this.is('ohlc')) {
  4723. return 'ohlc';
  4724. }
  4725. if (this.is('column')) {
  4726. return 'sum';
  4727. }
  4728. return 'average';
  4729. };
  4730. /**
  4731. * Takes parallel arrays of x and y data and groups the data into intervals
  4732. * defined by groupPositions, a collection of starting x values for each group.
  4733. *
  4734. * @private
  4735. * @function Highcharts.Series#groupData
  4736. *
  4737. * @param {Array<number>} xData
  4738. *
  4739. * @param {Array<number>|Array<Array<number>>} yData
  4740. *
  4741. * @param {boolean} groupPositions
  4742. *
  4743. * @param {string|Function} approximation
  4744. *
  4745. * @return {void}
  4746. */
  4747. seriesProto.groupData = groupData;
  4748. // Extend the basic processData method, that crops the data to the current zoom
  4749. // range, with data grouping logic.
  4750. seriesProto.processData = function () {
  4751. var series = this, chart = series.chart, options = series.options, dataGroupingOptions = options.dataGrouping, groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  4752. pick(dataGroupingOptions.enabled, chart.options.isStock), visible = (series.visible || !chart.options.chart.ignoreHiddenSeries), hasGroupedData, skip, lastDataGrouping = this.currentDataGrouping, currentDataGrouping, croppedData, revertRequireSorting = false;
  4753. // Run base method
  4754. series.forceCrop = groupingEnabled; // #334
  4755. series.groupPixelWidth = null; // #2110
  4756. series.hasProcessed = true; // #2692
  4757. // Data needs to be sorted for dataGrouping
  4758. if (groupingEnabled && !series.requireSorting) {
  4759. series.requireSorting = revertRequireSorting = true;
  4760. }
  4761. // Skip if processData returns false or if grouping is disabled (in that
  4762. // order)
  4763. skip = (baseProcessData.apply(series, arguments) === false ||
  4764. !groupingEnabled);
  4765. // Revert original requireSorting value if changed
  4766. if (revertRequireSorting) {
  4767. series.requireSorting = false;
  4768. }
  4769. if (!skip) {
  4770. series.destroyGroupedData();
  4771. var i, processedXData = dataGroupingOptions.groupAll ?
  4772. series.xData :
  4773. series.processedXData, processedYData = dataGroupingOptions.groupAll ?
  4774. series.yData :
  4775. series.processedYData, plotSizeX = chart.plotSizeX, xAxis = series.xAxis, ordinal = xAxis.options.ordinal, groupPixelWidth = series.groupPixelWidth =
  4776. xAxis.getGroupPixelWidth && xAxis.getGroupPixelWidth();
  4777. // Execute grouping if the amount of points is greater than the limit
  4778. // defined in groupPixelWidth
  4779. if (groupPixelWidth) {
  4780. hasGroupedData = true;
  4781. // Force recreation of point instances in series.translate, #5699
  4782. series.isDirty = true;
  4783. series.points = null; // #6709
  4784. var extremes = xAxis.getExtremes(), xMin = extremes.min, xMax = extremes.max, groupIntervalFactor = (ordinal &&
  4785. xAxis.ordinal &&
  4786. xAxis.ordinal.getGroupIntervalFactor(xMin, xMax, series)) || 1, interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  4787. groupIntervalFactor, groupPositions = xAxis.getTimeTicks(DateTimeAxis.AdditionsClass.prototype.normalizeTimeTickInterval(interval, dataGroupingOptions.units ||
  4788. defaultDataGroupingUnits),
  4789. // Processed data may extend beyond axis (#4907)
  4790. Math.min(xMin, processedXData[0]), Math.max(xMax, processedXData[processedXData.length - 1]), xAxis.options.startOfWeek, processedXData, series.closestPointRange), groupedData = seriesProto.groupData.apply(series, [
  4791. processedXData,
  4792. processedYData,
  4793. groupPositions,
  4794. dataGroupingOptions.approximation
  4795. ]), groupedXData = groupedData.groupedXData, groupedYData = groupedData.groupedYData, gapSize = 0;
  4796. // Prevent the smoothed data to spill out left and right, and make
  4797. // sure data is not shifted to the left
  4798. if (dataGroupingOptions.smoothed && groupedXData.length) {
  4799. i = groupedXData.length - 1;
  4800. groupedXData[i] = Math.min(groupedXData[i], xMax);
  4801. while (i-- && i > 0) {
  4802. groupedXData[i] += interval / 2;
  4803. }
  4804. groupedXData[0] = Math.max(groupedXData[0], xMin);
  4805. }
  4806. // Record what data grouping values were used
  4807. for (i = 1; i < groupPositions.length; i++) {
  4808. // The grouped gapSize needs to be the largest distance between
  4809. // the group to capture varying group sizes like months or DST
  4810. // crossing (#10000). Also check that the gap is not at the
  4811. // start of a segment.
  4812. if (!groupPositions.info.segmentStarts ||
  4813. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  4814. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  4815. }
  4816. }
  4817. currentDataGrouping = groupPositions.info;
  4818. currentDataGrouping.gapSize = gapSize;
  4819. series.closestPointRange = groupPositions.info.totalRange;
  4820. series.groupMap = groupedData.groupMap;
  4821. // Make sure the X axis extends to show the first group (#2533)
  4822. // But only for visible series (#5493, #6393)
  4823. if (defined(groupedXData[0]) &&
  4824. groupedXData[0] < xAxis.min &&
  4825. visible) {
  4826. if ((!defined(xAxis.options.min) &&
  4827. xAxis.min <= xAxis.dataMin) ||
  4828. xAxis.min === xAxis.dataMin) {
  4829. xAxis.min = Math.min(groupedXData[0], xAxis.min);
  4830. }
  4831. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  4832. }
  4833. // We calculated all group positions but we should render
  4834. // only the ones within the visible range
  4835. if (dataGroupingOptions.groupAll) {
  4836. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  4837. );
  4838. groupedXData = croppedData.xData;
  4839. groupedYData = croppedData.yData;
  4840. }
  4841. // Set series props
  4842. series.processedXData = groupedXData;
  4843. series.processedYData = groupedYData;
  4844. }
  4845. else {
  4846. series.groupMap = null;
  4847. }
  4848. series.hasGroupedData = hasGroupedData;
  4849. series.currentDataGrouping = currentDataGrouping;
  4850. series.preventGraphAnimation =
  4851. (lastDataGrouping && lastDataGrouping.totalRange) !==
  4852. (currentDataGrouping && currentDataGrouping.totalRange);
  4853. }
  4854. };
  4855. // Destroy the grouped data points. #622, #740
  4856. seriesProto.destroyGroupedData = function () {
  4857. // Clear previous groups
  4858. if (this.groupedData) {
  4859. this.groupedData.forEach(function (point, i) {
  4860. if (point) {
  4861. this.groupedData[i] = point.destroy ?
  4862. point.destroy() : null;
  4863. }
  4864. }, this);
  4865. // Clears all:
  4866. // - `this.groupedData`
  4867. // - `this.points`
  4868. // - `preserve` object in series.update()
  4869. this.groupedData.length = 0;
  4870. }
  4871. };
  4872. // Override the generatePoints method by adding a reference to grouped data
  4873. seriesProto.generatePoints = function () {
  4874. baseGeneratePoints.apply(this);
  4875. // Record grouped data in order to let it be destroyed the next time
  4876. // processData runs
  4877. this.destroyGroupedData(); // #622
  4878. this.groupedData = this.hasGroupedData ? this.points : null;
  4879. };
  4880. // Override point prototype to throw a warning when trying to update grouped
  4881. // points.
  4882. addEvent(Point, 'update', function () {
  4883. if (this.dataGroup) {
  4884. error(24, false, this.series.chart);
  4885. return false;
  4886. }
  4887. });
  4888. // Extend the original method, make the tooltip's header reflect the grouped
  4889. // range.
  4890. addEvent(Tooltip, 'headerFormatter', function (e) {
  4891. var tooltip = this, chart = this.chart, time = chart.time, labelConfig = e.labelConfig, series = labelConfig.series, options = series.options, tooltipOptions = series.tooltipOptions, dataGroupingOptions = options.dataGrouping, xDateFormat = tooltipOptions.xDateFormat, xDateFormatEnd, xAxis = series.xAxis, currentDataGrouping, dateTimeLabelFormats, labelFormats, formattedKey, formatString = tooltipOptions[(e.isFooter ? 'footer' : 'header') + 'Format'];
  4892. // apply only to grouped series
  4893. if (xAxis &&
  4894. xAxis.options.type === 'datetime' &&
  4895. dataGroupingOptions &&
  4896. isNumber(labelConfig.key)) {
  4897. // set variables
  4898. currentDataGrouping = series.currentDataGrouping;
  4899. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  4900. // Fallback to commonOptions (#9693)
  4901. commonOptions.dateTimeLabelFormats;
  4902. // if we have grouped data, use the grouping information to get the
  4903. // right format
  4904. if (currentDataGrouping) {
  4905. labelFormats =
  4906. dateTimeLabelFormats[currentDataGrouping.unitName];
  4907. if (currentDataGrouping.count === 1) {
  4908. xDateFormat = labelFormats[0];
  4909. }
  4910. else {
  4911. xDateFormat = labelFormats[1];
  4912. xDateFormatEnd = labelFormats[2];
  4913. }
  4914. // if not grouped, and we don't have set the xDateFormat option, get the
  4915. // best fit, so if the least distance between points is one minute, show
  4916. // it, but if the least distance is one day, skip hours and minutes etc.
  4917. }
  4918. else if (!xDateFormat && dateTimeLabelFormats) {
  4919. xDateFormat = tooltip.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  4920. }
  4921. // now format the key
  4922. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  4923. if (xDateFormatEnd) {
  4924. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  4925. }
  4926. // Replace default header style with class name
  4927. if (series.chart.styledMode) {
  4928. formatString = this.styledModeFormat(formatString);
  4929. }
  4930. // return the replaced format
  4931. e.text = format(formatString, {
  4932. point: extend(labelConfig.point, { key: formattedKey }),
  4933. series: series
  4934. }, chart);
  4935. e.preventDefault();
  4936. }
  4937. });
  4938. // Destroy grouped data on series destroy
  4939. addEvent(Series, 'destroy', seriesProto.destroyGroupedData);
  4940. // Handle default options for data grouping. This must be set at runtime because
  4941. // some series types are defined after this.
  4942. addEvent(Series, 'afterSetOptions', function (e) {
  4943. var options = e.options, type = this.type, plotOptions = this.chart.options.plotOptions, defaultOptions = O.defaultOptions.plotOptions[type].dataGrouping,
  4944. // External series, for example technical indicators should also
  4945. // inherit commonOptions which are not available outside this module
  4946. baseOptions = this.useCommonDataGrouping && commonOptions;
  4947. if (specificOptions[type] || baseOptions) { // #1284
  4948. if (!defaultOptions) {
  4949. defaultOptions = merge(commonOptions, specificOptions[type]);
  4950. }
  4951. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  4952. // Set by the StockChart constructor:
  4953. plotOptions[type].dataGrouping, this.userOptions.dataGrouping);
  4954. }
  4955. });
  4956. // When resetting the scale reset the hasProccessed flag to avoid taking
  4957. // previous data grouping of neighbour series into accound when determining
  4958. // group pixel width (#2692).
  4959. addEvent(Axis, 'afterSetScale', function () {
  4960. this.series.forEach(function (series) {
  4961. series.hasProcessed = false;
  4962. });
  4963. });
  4964. // Get the data grouping pixel width based on the greatest defined individual
  4965. // width of the axis' series, and if whether one of the axes need grouping.
  4966. Axis.prototype.getGroupPixelWidth = function () {
  4967. var series = this.series, len = series.length, i, groupPixelWidth = 0, doGrouping = false, dataLength, dgOptions;
  4968. // If multiple series are compared on the same x axis, give them the same
  4969. // group pixel width (#334)
  4970. i = len;
  4971. while (i--) {
  4972. dgOptions = series[i].options.dataGrouping;
  4973. if (dgOptions) {
  4974. groupPixelWidth = Math.max(groupPixelWidth,
  4975. // Fallback to commonOptions (#9693)
  4976. pick(dgOptions.groupPixelWidth, commonOptions.groupPixelWidth));
  4977. }
  4978. }
  4979. // If one of the series needs grouping, apply it to all (#1634)
  4980. i = len;
  4981. while (i--) {
  4982. dgOptions = series[i].options.dataGrouping;
  4983. if (dgOptions && series[i].hasProcessed) { // #2692
  4984. dataLength = (series[i].processedXData || series[i].data).length;
  4985. // Execute grouping if the amount of points is greater than the
  4986. // limit defined in groupPixelWidth
  4987. if (series[i].groupPixelWidth ||
  4988. dataLength >
  4989. (this.chart.plotSizeX / groupPixelWidth) ||
  4990. (dataLength && dgOptions.forced)) {
  4991. doGrouping = true;
  4992. }
  4993. }
  4994. }
  4995. return doGrouping ? groupPixelWidth : 0;
  4996. };
  4997. /**
  4998. * Highstock only. Force data grouping on all the axis' series.
  4999. *
  5000. * @product highstock
  5001. *
  5002. * @function Highcharts.Axis#setDataGrouping
  5003. *
  5004. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  5005. * A `dataGrouping` configuration. Use `false` to disable data grouping
  5006. * dynamically.
  5007. *
  5008. * @param {boolean} [redraw=true]
  5009. * Whether to redraw the chart or wait for a later call to
  5010. * {@link Chart#redraw}.
  5011. *
  5012. * @return {void}
  5013. */
  5014. Axis.prototype.setDataGrouping = function (dataGrouping, redraw) {
  5015. var axis = this;
  5016. var i;
  5017. redraw = pick(redraw, true);
  5018. if (!dataGrouping) {
  5019. dataGrouping = {
  5020. forced: false,
  5021. units: null
  5022. };
  5023. }
  5024. // Axis is instantiated, update all series
  5025. if (this instanceof Axis) {
  5026. i = this.series.length;
  5027. while (i--) {
  5028. this.series[i].update({
  5029. dataGrouping: dataGrouping
  5030. }, false);
  5031. }
  5032. // Axis not yet instanciated, alter series options
  5033. }
  5034. else {
  5035. this.chart.options.series.forEach(function (seriesOptions) {
  5036. seriesOptions.dataGrouping = dataGrouping;
  5037. }, false);
  5038. }
  5039. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  5040. if (axis.ordinal) {
  5041. axis.ordinal.slope = void 0;
  5042. }
  5043. if (redraw) {
  5044. this.chart.redraw();
  5045. }
  5046. };
  5047. H.dataGrouping = dataGrouping;
  5048. /* eslint-enable no-invalid-this, valid-jsdoc */
  5049. /**
  5050. * Data grouping is the concept of sampling the data values into larger
  5051. * blocks in order to ease readability and increase performance of the
  5052. * JavaScript charts. Highstock by default applies data grouping when
  5053. * the points become closer than a certain pixel value, determined by
  5054. * the `groupPixelWidth` option.
  5055. *
  5056. * If data grouping is applied, the grouping information of grouped
  5057. * points can be read from the [Point.dataGroup](
  5058. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  5059. * the data itself are set, for example `name` or `color` or custom properties,
  5060. * the grouping logic doesn't know how to group it. In this case the options of
  5061. * the first point instance are copied over to the group point. This can be
  5062. * altered through a custom `approximation` callback function.
  5063. *
  5064. * @declare Highcharts.DataGroupingOptionsObject
  5065. * @product highstock
  5066. * @requires product:highstock
  5067. * @requires module:modules/datagrouping
  5068. * @apioption plotOptions.series.dataGrouping
  5069. */
  5070. /**
  5071. * The method of approximation inside a group. When for example 30 days
  5072. * are grouped into one month, this determines what value should represent
  5073. * the group. Possible values are "average", "averages", "open", "high",
  5074. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  5075. * is "ohlc" by default, which finds the open, high, low and close values
  5076. * within all the grouped data. For ranges, the approximation is "range",
  5077. * which finds the low and high values. For multi-dimensional data,
  5078. * like ranges and OHLC, "averages" will compute the average for each
  5079. * dimension.
  5080. *
  5081. * Custom aggregate methods can be added by assigning a callback function
  5082. * as the approximation. This function takes a numeric array as the
  5083. * argument and should return a single numeric value or `null`. Note
  5084. * that the numeric array will never contain null values, only true
  5085. * numbers. Instead, if null values are present in the raw data, the
  5086. * numeric array will have an `.hasNulls` property set to `true`. For
  5087. * single-value data sets the data is available in the first argument
  5088. * of the callback function. For OHLC data sets, all the open values
  5089. * are in the first argument, all high values in the second etc.
  5090. *
  5091. * Since v4.2.7, grouping meta data is available in the approximation
  5092. * callback from `this.dataGroupInfo`. It can be used to extract information
  5093. * from the raw data.
  5094. *
  5095. * Defaults to `average` for line-type series, `sum` for columns, `range`
  5096. * for range series and `ohlc` for OHLC and candlestick.
  5097. *
  5098. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  5099. * Approximation callback with custom data
  5100. *
  5101. * @type {Highcharts.DataGroupingApproximationValue|Function}
  5102. * @apioption plotOptions.series.dataGrouping.approximation
  5103. */
  5104. /**
  5105. * Datetime formats for the header of the tooltip in a stock chart.
  5106. * The format can vary within a chart depending on the currently selected
  5107. * time range and the current data grouping.
  5108. *
  5109. * The default formats are:
  5110. * ```js
  5111. * {
  5112. * millisecond: [
  5113. * '%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'
  5114. * ],
  5115. * second: ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'],
  5116. * minute: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5117. * hour: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  5118. * day: ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5119. * week: ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  5120. * month: ['%B %Y', '%B', '-%B %Y'],
  5121. * year: ['%Y', '%Y', '-%Y']
  5122. * }
  5123. * ```
  5124. *
  5125. * For each of these array definitions, the first item is the format
  5126. * used when the active time span is one unit. For instance, if the
  5127. * current data applies to one week, the first item of the week array
  5128. * is used. The second and third items are used when the active time
  5129. * span is more than two units. For instance, if the current data applies
  5130. * to two weeks, the second and third item of the week array are used,
  5131. * and applied to the start and end date of the time span.
  5132. *
  5133. * @type {object}
  5134. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  5135. */
  5136. /**
  5137. * Enable or disable data grouping.
  5138. *
  5139. * @type {boolean}
  5140. * @default true
  5141. * @apioption plotOptions.series.dataGrouping.enabled
  5142. */
  5143. /**
  5144. * When data grouping is forced, it runs no matter how small the intervals
  5145. * are. This can be handy for example when the sum should be calculated
  5146. * for values appearing at random times within each hour.
  5147. *
  5148. * @type {boolean}
  5149. * @default false
  5150. * @apioption plotOptions.series.dataGrouping.forced
  5151. */
  5152. /**
  5153. * The approximate pixel width of each group. If for example a series
  5154. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  5155. * is performed. If however the series contains so many points that
  5156. * the spacing is less than the groupPixelWidth, Highcharts will try
  5157. * to group it into appropriate groups so that each is more or less
  5158. * two pixels wide. If multiple series with different group pixel widths
  5159. * are drawn on the same x axis, all series will take the greatest width.
  5160. * For example, line series have 2px default group width, while column
  5161. * series have 10px. If combined, both the line and the column will
  5162. * have 10px by default.
  5163. *
  5164. * @type {number}
  5165. * @default 2
  5166. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  5167. */
  5168. /**
  5169. * By default only points within the visible range are grouped. Enabling this
  5170. * option will force data grouping to calculate all grouped points for a given
  5171. * dataset. That option prevents for example a column series from calculating
  5172. * a grouped point partially. The effect is similar to
  5173. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  5174. * not affect yAxis extremes.
  5175. *
  5176. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  5177. * Two series with the same data but different groupAll setting
  5178. *
  5179. * @type {boolean}
  5180. * @default false
  5181. * @since 6.1.0
  5182. * @apioption plotOptions.series.dataGrouping.groupAll
  5183. */
  5184. /**
  5185. * Normally, a group is indexed by the start of that group, so for example
  5186. * when 30 daily values are grouped into one month, that month's x value
  5187. * will be the 1st of the month. This apparently shifts the data to
  5188. * the left. When the smoothed option is true, this is compensated for.
  5189. * The data is shifted to the middle of the group, and min and max
  5190. * values are preserved. Internally, this is used in the Navigator series.
  5191. *
  5192. * @type {boolean}
  5193. * @default false
  5194. * @apioption plotOptions.series.dataGrouping.smoothed
  5195. */
  5196. /**
  5197. * An array determining what time intervals the data is allowed to be
  5198. * grouped to. Each array item is an array where the first value is
  5199. * the time unit and the second value another array of allowed multiples.
  5200. *
  5201. * Defaults to:
  5202. * ```js
  5203. * units: [[
  5204. * 'millisecond', // unit name
  5205. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  5206. * ], [
  5207. * 'second',
  5208. * [1, 2, 5, 10, 15, 30]
  5209. * ], [
  5210. * 'minute',
  5211. * [1, 2, 5, 10, 15, 30]
  5212. * ], [
  5213. * 'hour',
  5214. * [1, 2, 3, 4, 6, 8, 12]
  5215. * ], [
  5216. * 'day',
  5217. * [1]
  5218. * ], [
  5219. * 'week',
  5220. * [1]
  5221. * ], [
  5222. * 'month',
  5223. * [1, 3, 6]
  5224. * ], [
  5225. * 'year',
  5226. * null
  5227. * ]]
  5228. * ```
  5229. *
  5230. * @type {Array<Array<string,(Array<number>|null)>>}
  5231. * @apioption plotOptions.series.dataGrouping.units
  5232. */
  5233. /**
  5234. * The approximate pixel width of each group. If for example a series
  5235. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  5236. * is performed. If however the series contains so many points that
  5237. * the spacing is less than the groupPixelWidth, Highcharts will try
  5238. * to group it into appropriate groups so that each is more or less
  5239. * two pixels wide. Defaults to `10`.
  5240. *
  5241. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  5242. * Two series with the same data density but different groupPixelWidth
  5243. *
  5244. * @type {number}
  5245. * @default 10
  5246. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  5247. */
  5248. ''; // required by JSDoc parsing
  5249. return dataGrouping;
  5250. });
  5251. _registerModule(_modules, 'parts/OHLCSeries.js', [_modules['parts/Globals.js'], _modules['parts/Point.js'], _modules['parts/Utilities.js']], function (H, Point, U) {
  5252. /* *
  5253. *
  5254. * (c) 2010-2020 Torstein Honsi
  5255. *
  5256. * License: www.highcharts.com/license
  5257. *
  5258. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5259. *
  5260. * */
  5261. var seriesType = U.seriesType;
  5262. var seriesTypes = H.seriesTypes;
  5263. /**
  5264. * The ohlc series type.
  5265. *
  5266. * @private
  5267. * @class
  5268. * @name Highcharts.seriesTypes.ohlc
  5269. *
  5270. * @augments Highcharts.Series
  5271. */
  5272. seriesType('ohlc', 'column'
  5273. /**
  5274. * An OHLC chart is a style of financial chart used to describe price
  5275. * movements over time. It displays open, high, low and close values per
  5276. * data point.
  5277. *
  5278. * @sample stock/demo/ohlc/
  5279. * OHLC chart
  5280. *
  5281. * @extends plotOptions.column
  5282. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  5283. * stack
  5284. * @product highstock
  5285. * @optionparent plotOptions.ohlc
  5286. */
  5287. , {
  5288. /**
  5289. * The approximate pixel width of each group. If for example a series
  5290. * with 30 points is displayed over a 600 pixel wide plot area, no
  5291. * grouping is performed. If however the series contains so many points
  5292. * that the spacing is less than the groupPixelWidth, Highcharts will
  5293. * try to group it into appropriate groups so that each is more or less
  5294. * two pixels wide. Defaults to `5`.
  5295. *
  5296. * @type {number}
  5297. * @default 5
  5298. * @product highstock
  5299. * @apioption plotOptions.ohlc.dataGrouping.groupPixelWidth
  5300. */
  5301. /**
  5302. * The pixel width of the line/border. Defaults to `1`.
  5303. *
  5304. * @sample {highstock} stock/plotoptions/ohlc-linewidth/
  5305. * A greater line width
  5306. *
  5307. * @type {number}
  5308. * @default 1
  5309. * @product highstock
  5310. *
  5311. * @private
  5312. */
  5313. lineWidth: 1,
  5314. tooltip: {
  5315. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  5316. '<b> {series.name}</b><br/>' +
  5317. 'Open: {point.open}<br/>' +
  5318. 'High: {point.high}<br/>' +
  5319. 'Low: {point.low}<br/>' +
  5320. 'Close: {point.close}<br/>'
  5321. },
  5322. threshold: null,
  5323. states: {
  5324. /**
  5325. * @extends plotOptions.column.states.hover
  5326. * @product highstock
  5327. */
  5328. hover: {
  5329. /**
  5330. * The pixel width of the line representing the OHLC point.
  5331. *
  5332. * @type {number}
  5333. * @default 3
  5334. * @product highstock
  5335. */
  5336. lineWidth: 3
  5337. }
  5338. },
  5339. /**
  5340. * Determines which one of `open`, `high`, `low`, `close` values should
  5341. * be represented as `point.y`, which is later used to set dataLabel
  5342. * position and [compare](#plotOptions.series.compare).
  5343. *
  5344. * @sample {highstock} stock/plotoptions/ohlc-pointvalkey/
  5345. * Possible values
  5346. *
  5347. * @type {string}
  5348. * @default close
  5349. * @validvalue ["open", "high", "low", "close"]
  5350. * @product highstock
  5351. * @apioption plotOptions.ohlc.pointValKey
  5352. */
  5353. /**
  5354. * @default close
  5355. * @apioption plotOptions.ohlc.colorKey
  5356. */
  5357. /**
  5358. * Line color for up points.
  5359. *
  5360. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5361. * @product highstock
  5362. * @apioption plotOptions.ohlc.upColor
  5363. */
  5364. stickyTracking: true
  5365. },
  5366. /**
  5367. * @lends Highcharts.seriesTypes.ohlc
  5368. */
  5369. {
  5370. /* eslint-disable valid-jsdoc */
  5371. directTouch: false,
  5372. pointArrayMap: ['open', 'high', 'low', 'close'],
  5373. toYData: function (point) {
  5374. // return a plain array for speedy calculation
  5375. return [point.open, point.high, point.low, point.close];
  5376. },
  5377. pointValKey: 'close',
  5378. pointAttrToOptions: {
  5379. stroke: 'color',
  5380. 'stroke-width': 'lineWidth'
  5381. },
  5382. /**
  5383. * @private
  5384. * @function Highcarts.seriesTypes.ohlc#init
  5385. * @return {void}
  5386. */
  5387. init: function () {
  5388. seriesTypes.column.prototype.init.apply(this, arguments);
  5389. this.options.stacking = void 0; // #8817
  5390. },
  5391. /**
  5392. * Postprocess mapping between options and SVG attributes
  5393. *
  5394. * @private
  5395. * @function Highcharts.seriesTypes.ohlc#pointAttribs
  5396. * @param {Highcharts.OHLCPoint} point
  5397. * @param {string} state
  5398. * @return {Highcharts.SVGAttributes}
  5399. */
  5400. pointAttribs: function (point, state) {
  5401. var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options;
  5402. delete attribs.fill;
  5403. if (!point.options.color &&
  5404. options.upColor &&
  5405. point.open < point.close) {
  5406. attribs.stroke = options.upColor;
  5407. }
  5408. return attribs;
  5409. },
  5410. /**
  5411. * Translate data points from raw values x and y to plotX and plotY
  5412. *
  5413. * @private
  5414. * @function Highcharts.seriesTypes.ohlc#translate
  5415. * @return {void}
  5416. */
  5417. translate: function () {
  5418. var series = this, yAxis = series.yAxis, hasModifyValue = !!series.modifyValue, translated = [
  5419. 'plotOpen',
  5420. 'plotHigh',
  5421. 'plotLow',
  5422. 'plotClose',
  5423. 'yBottom'
  5424. ]; // translate OHLC for
  5425. seriesTypes.column.prototype.translate.apply(series);
  5426. // Do the translation
  5427. series.points.forEach(function (point) {
  5428. [point.open, point.high, point.low, point.close, point.low]
  5429. .forEach(function (value, i) {
  5430. if (value !== null) {
  5431. if (hasModifyValue) {
  5432. value = series.modifyValue(value);
  5433. }
  5434. point[translated[i]] =
  5435. yAxis.toPixels(value, true);
  5436. }
  5437. });
  5438. // Align the tooltip to the high value to avoid covering the
  5439. // point
  5440. point.tooltipPos[1] =
  5441. point.plotHigh + yAxis.pos - series.chart.plotTop;
  5442. });
  5443. },
  5444. /**
  5445. * Draw the data points
  5446. *
  5447. * @private
  5448. * @function Highcharts.seriesTypes.ohlc#drawPoints
  5449. * @return {void}
  5450. */
  5451. drawPoints: function () {
  5452. var series = this, points = series.points, chart = series.chart,
  5453. /**
  5454. * Extend vertical stem to open and close values.
  5455. */
  5456. extendStem = function (path, halfStrokeWidth, openOrClose) {
  5457. var start = path[0];
  5458. var end = path[1];
  5459. // We don't need to worry about crisp - openOrClose value
  5460. // is already crisped and halfStrokeWidth should remove it.
  5461. if (typeof start[2] === 'number') {
  5462. start[2] = Math.max(openOrClose + halfStrokeWidth, start[2]);
  5463. }
  5464. if (typeof end[2] === 'number') {
  5465. end[2] = Math.min(openOrClose - halfStrokeWidth, end[2]);
  5466. }
  5467. };
  5468. points.forEach(function (point) {
  5469. var plotOpen, plotClose, crispCorr, halfWidth, path, graphic = point.graphic, crispX, isNew = !graphic, strokeWidth;
  5470. if (typeof point.plotY !== 'undefined') {
  5471. // Create and/or update the graphic
  5472. if (!graphic) {
  5473. point.graphic = graphic = chart.renderer.path()
  5474. .add(series.group);
  5475. }
  5476. if (!chart.styledMode) {
  5477. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  5478. }
  5479. // crisp vector coordinates
  5480. strokeWidth = graphic.strokeWidth();
  5481. crispCorr = (strokeWidth % 2) / 2;
  5482. // #2596:
  5483. crispX = Math.round(point.plotX) - crispCorr;
  5484. halfWidth = Math.round(point.shapeArgs.width / 2);
  5485. // the vertical stem
  5486. path = [
  5487. ['M', crispX, Math.round(point.yBottom)],
  5488. ['L', crispX, Math.round(point.plotHigh)]
  5489. ];
  5490. // open
  5491. if (point.open !== null) {
  5492. plotOpen = Math.round(point.plotOpen) + crispCorr;
  5493. path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
  5494. extendStem(path, strokeWidth / 2, plotOpen);
  5495. }
  5496. // close
  5497. if (point.close !== null) {
  5498. plotClose = Math.round(point.plotClose) + crispCorr;
  5499. path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
  5500. extendStem(path, strokeWidth / 2, plotClose);
  5501. }
  5502. graphic[isNew ? 'attr' : 'animate']({ d: path })
  5503. .addClass(point.getClassName(), true);
  5504. }
  5505. });
  5506. },
  5507. animate: null // Disable animation
  5508. /* eslint-enable valid-jsdoc */
  5509. },
  5510. /**
  5511. * @lends Highcharts.seriesTypes.ohlc.prototype.pointClass.prototype
  5512. */
  5513. {
  5514. /* eslint-disable valid-jsdoc */
  5515. /**
  5516. * Extend the parent method by adding up or down to the class name.
  5517. * @private
  5518. * @function Highcharts.seriesTypes.ohlc#getClassName
  5519. * @return {string}
  5520. */
  5521. getClassName: function () {
  5522. return Point.prototype.getClassName.call(this) +
  5523. (this.open < this.close ?
  5524. ' highcharts-point-up' :
  5525. ' highcharts-point-down');
  5526. }
  5527. /* eslint-enable valid-jsdoc */
  5528. });
  5529. /**
  5530. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  5531. * specified, it is inherited from [chart.type](#chart.type).
  5532. *
  5533. * @extends series,plotOptions.ohlc
  5534. * @excluding dataParser, dataURL
  5535. * @product highstock
  5536. * @apioption series.ohlc
  5537. */
  5538. /**
  5539. * An array of data points for the series. For the `ohlc` series type,
  5540. * points can be given in the following ways:
  5541. *
  5542. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  5543. * to `x,open,high,low,close`. If the first value is a string, it is applied
  5544. * as the name of the point, and the `x` value is inferred. The `x` value can
  5545. * also be omitted, in which case the inner arrays should be of length 4\.
  5546. * Then the `x` value is automatically calculated, either starting at 0 and
  5547. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  5548. * series options.
  5549. * ```js
  5550. * data: [
  5551. * [0, 6, 5, 6, 7],
  5552. * [1, 9, 4, 8, 2],
  5553. * [2, 6, 3, 4, 10]
  5554. * ]
  5555. * ```
  5556. *
  5557. * 2. An array of objects with named values. The following snippet shows only a
  5558. * few settings, see the complete options set below. If the total number of
  5559. * data points exceeds the series'
  5560. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  5561. * available.
  5562. * ```js
  5563. * data: [{
  5564. * x: 1,
  5565. * open: 3,
  5566. * high: 4,
  5567. * low: 5,
  5568. * close: 2,
  5569. * name: "Point2",
  5570. * color: "#00FF00"
  5571. * }, {
  5572. * x: 1,
  5573. * open: 4,
  5574. * high: 3,
  5575. * low: 6,
  5576. * close: 7,
  5577. * name: "Point1",
  5578. * color: "#FF00FF"
  5579. * }]
  5580. * ```
  5581. *
  5582. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  5583. * @extends series.arearange.data
  5584. * @excluding y, marker
  5585. * @product highstock
  5586. * @apioption series.ohlc.data
  5587. */
  5588. /**
  5589. * The closing value of each data point.
  5590. *
  5591. * @type {number}
  5592. * @product highstock
  5593. * @apioption series.ohlc.data.close
  5594. */
  5595. /**
  5596. * The opening value of each data point.
  5597. *
  5598. * @type {number}
  5599. * @product highstock
  5600. * @apioption series.ohlc.data.open
  5601. */
  5602. ''; // adds doclets above to transpilat
  5603. });
  5604. _registerModule(_modules, 'parts/CandlestickSeries.js', [_modules['parts/Globals.js'], _modules['parts/Options.js'], _modules['parts/Utilities.js']], function (H, O, U) {
  5605. /* *
  5606. *
  5607. * (c) 2010-2020 Torstein Honsi
  5608. *
  5609. * License: www.highcharts.com/license
  5610. *
  5611. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5612. *
  5613. * */
  5614. var defaultOptions = O.defaultOptions;
  5615. var merge = U.merge, seriesType = U.seriesType;
  5616. var seriesTypes = H.seriesTypes;
  5617. /**
  5618. * A candlestick chart is a style of financial chart used to describe price
  5619. * movements over time.
  5620. *
  5621. * @sample stock/demo/candlestick/
  5622. * Candlestick chart
  5623. *
  5624. * @extends plotOptions.ohlc
  5625. * @excluding borderColor,borderRadius,borderWidth
  5626. * @product highstock
  5627. * @optionparent plotOptions.candlestick
  5628. */
  5629. var candlestickOptions = {
  5630. /**
  5631. * The specific line color for up candle sticks. The default is to inherit
  5632. * the general `lineColor` setting.
  5633. *
  5634. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  5635. * Candlestick line colors
  5636. *
  5637. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5638. * @since 1.3.6
  5639. * @product highstock
  5640. * @apioption plotOptions.candlestick.upLineColor
  5641. */
  5642. /**
  5643. * @type {Highcharts.DataGroupingApproximationValue|Function}
  5644. * @default ohlc
  5645. * @product highstock
  5646. * @apioption plotOptions.candlestick.dataGrouping.approximation
  5647. */
  5648. states: {
  5649. /**
  5650. * @extends plotOptions.column.states.hover
  5651. * @product highstock
  5652. */
  5653. hover: {
  5654. /**
  5655. * The pixel width of the line/border around the candlestick.
  5656. *
  5657. * @product highstock
  5658. */
  5659. lineWidth: 2
  5660. }
  5661. },
  5662. /**
  5663. * @extends plotOptions.ohlc.tooltip
  5664. */
  5665. tooltip: defaultOptions.plotOptions.ohlc.tooltip,
  5666. /**
  5667. * @type {number|null}
  5668. * @product highstock
  5669. */
  5670. threshold: null,
  5671. /**
  5672. * The color of the line/border of the candlestick.
  5673. *
  5674. * In styled mode, the line stroke can be set with the
  5675. * `.highcharts-candlestick-series .highcahrts-point` rule.
  5676. *
  5677. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  5678. *
  5679. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  5680. * Candlestick line colors
  5681. *
  5682. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5683. * @default #000000
  5684. * @product highstock
  5685. */
  5686. lineColor: '#000000',
  5687. /**
  5688. * The pixel width of the candlestick line/border. Defaults to `1`.
  5689. *
  5690. *
  5691. * In styled mode, the line stroke width can be set with the
  5692. * `.highcharts-candlestick-series .highcahrts-point` rule.
  5693. *
  5694. * @product highstock
  5695. */
  5696. lineWidth: 1,
  5697. /**
  5698. * The fill color of the candlestick when values are rising.
  5699. *
  5700. * In styled mode, the up color can be set with the
  5701. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  5702. *
  5703. * @sample {highstock} stock/plotoptions/candlestick-color/
  5704. * Custom colors
  5705. * @sample {highstock} highcharts/css/candlestick/
  5706. * Colors in styled mode
  5707. *
  5708. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5709. * @default #ffffff
  5710. * @product highstock
  5711. */
  5712. upColor: '#ffffff',
  5713. /**
  5714. * @product highstock
  5715. */
  5716. stickyTracking: true
  5717. };
  5718. /**
  5719. * The candlestick series type.
  5720. *
  5721. * @private
  5722. * @class
  5723. * @name Highcharts.seriesTypes.candlestick
  5724. *
  5725. * @augments Highcharts.seriesTypes.ohlc
  5726. */
  5727. seriesType('candlestick', 'ohlc', merge(defaultOptions.plotOptions.column, candlestickOptions),
  5728. /**
  5729. * @lends seriesTypes.candlestick
  5730. */
  5731. {
  5732. /* eslint-disable valid-jsdoc */
  5733. /**
  5734. * Postprocess mapping between options and SVG attributes
  5735. *
  5736. * @private
  5737. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  5738. * @param {Highcharts.Point} point
  5739. * @param {string} [state]
  5740. * @return {Highcharts.SVGAttributes}
  5741. */
  5742. pointAttribs: function (point, state) {
  5743. var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options, isUp = point.open < point.close, stroke = options.lineColor || this.color, stateOptions;
  5744. attribs['stroke-width'] = options.lineWidth;
  5745. attribs.fill = point.options.color ||
  5746. (isUp ? (options.upColor || this.color) : this.color);
  5747. attribs.stroke = point.options.lineColor ||
  5748. (isUp ? (options.upLineColor || stroke) : stroke);
  5749. // Select or hover states
  5750. if (state) {
  5751. stateOptions = options.states[state];
  5752. attribs.fill = stateOptions.color || attribs.fill;
  5753. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  5754. attribs['stroke-width'] =
  5755. stateOptions.lineWidth || attribs['stroke-width'];
  5756. }
  5757. return attribs;
  5758. },
  5759. /**
  5760. * Draw the data points.
  5761. *
  5762. * @private
  5763. * @function Highcharts.seriesTypes.candlestick#drawPoints
  5764. * @return {void}
  5765. */
  5766. drawPoints: function () {
  5767. var series = this, points = series.points, chart = series.chart, reversedYAxis = series.yAxis.reversed;
  5768. points.forEach(function (point) {
  5769. var graphic = point.graphic, plotOpen, plotClose, topBox, bottomBox, hasTopWhisker, hasBottomWhisker, crispCorr, crispX, path, halfWidth, isNew = !graphic;
  5770. if (typeof point.plotY !== 'undefined') {
  5771. if (!graphic) {
  5772. point.graphic = graphic = chart.renderer.path()
  5773. .add(series.group);
  5774. }
  5775. if (!series.chart.styledMode) {
  5776. graphic
  5777. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  5778. .shadow(series.options.shadow);
  5779. }
  5780. // Crisp vector coordinates
  5781. crispCorr = (graphic.strokeWidth() % 2) / 2;
  5782. // #2596:
  5783. crispX = Math.round(point.plotX) - crispCorr;
  5784. plotOpen = point.plotOpen;
  5785. plotClose = point.plotClose;
  5786. topBox = Math.min(plotOpen, plotClose);
  5787. bottomBox = Math.max(plotOpen, plotClose);
  5788. halfWidth = Math.round(point.shapeArgs.width / 2);
  5789. hasTopWhisker = reversedYAxis ?
  5790. bottomBox !== point.yBottom :
  5791. Math.round(topBox) !==
  5792. Math.round(point.plotHigh);
  5793. hasBottomWhisker = reversedYAxis ?
  5794. Math.round(topBox) !==
  5795. Math.round(point.plotHigh) :
  5796. bottomBox !== point.yBottom;
  5797. topBox = Math.round(topBox) + crispCorr;
  5798. bottomBox = Math.round(bottomBox) + crispCorr;
  5799. // Create the path. Due to a bug in Chrome 49, the path is
  5800. // first instanciated with no values, then the values
  5801. // pushed. For unknown reasons, instanciating the path array
  5802. // with all the values would lead to a crash when updating
  5803. // frequently (#5193).
  5804. path = [];
  5805. path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
  5806. ['M', crispX, topBox], [
  5807. 'L',
  5808. // #460, #2094
  5809. crispX,
  5810. hasTopWhisker ?
  5811. Math.round(reversedYAxis ?
  5812. point.yBottom :
  5813. point.plotHigh) :
  5814. topBox
  5815. ], ['M', crispX, bottomBox], [
  5816. 'L',
  5817. // #460, #2094
  5818. crispX,
  5819. hasBottomWhisker ?
  5820. Math.round(reversedYAxis ?
  5821. point.plotHigh :
  5822. point.yBottom) :
  5823. bottomBox
  5824. ]);
  5825. graphic[isNew ? 'attr' : 'animate']({ d: path })
  5826. .addClass(point.getClassName(), true);
  5827. }
  5828. });
  5829. /* eslint-enable valid-jsdoc */
  5830. }
  5831. });
  5832. /**
  5833. * A `candlestick` series. If the [type](#series.candlestick.type)
  5834. * option is not specified, it is inherited from [chart.type](
  5835. * #chart.type).
  5836. *
  5837. * @type {*}
  5838. * @extends series,plotOptions.candlestick
  5839. * @excluding dataParser, dataURL
  5840. * @product highstock
  5841. * @apioption series.candlestick
  5842. */
  5843. /**
  5844. * An array of data points for the series. For the `candlestick` series
  5845. * type, points can be given in the following ways:
  5846. *
  5847. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  5848. * to `x,open,high,low,close`. If the first value is a string, it is applied
  5849. * as the name of the point, and the `x` value is inferred. The `x` value can
  5850. * also be omitted, in which case the inner arrays should be of length 4.
  5851. * Then the `x` value is automatically calculated, either starting at 0 and
  5852. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  5853. * series options.
  5854. * ```js
  5855. * data: [
  5856. * [0, 7, 2, 0, 4],
  5857. * [1, 1, 4, 2, 8],
  5858. * [2, 3, 3, 9, 3]
  5859. * ]
  5860. * ```
  5861. *
  5862. * 2. An array of objects with named values. The following snippet shows only a
  5863. * few settings, see the complete options set below. If the total number of
  5864. * data points exceeds the series'
  5865. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  5866. * available.
  5867. * ```js
  5868. * data: [{
  5869. * x: 1,
  5870. * open: 9,
  5871. * high: 2,
  5872. * low: 4,
  5873. * close: 6,
  5874. * name: "Point2",
  5875. * color: "#00FF00"
  5876. * }, {
  5877. * x: 1,
  5878. * open: 1,
  5879. * high: 4,
  5880. * low: 7,
  5881. * close: 7,
  5882. * name: "Point1",
  5883. * color: "#FF00FF"
  5884. * }]
  5885. * ```
  5886. *
  5887. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  5888. * @extends series.ohlc.data
  5889. * @excluding y
  5890. * @product highstock
  5891. * @apioption series.candlestick.data
  5892. */
  5893. ''; // adds doclets above to transpilat
  5894. });
  5895. _registerModule(_modules, 'mixins/on-series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  5896. /* *
  5897. *
  5898. * (c) 2010-2020 Torstein Honsi
  5899. *
  5900. * License: www.highcharts.com/license
  5901. *
  5902. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5903. *
  5904. * */
  5905. var defined = U.defined, stableSort = U.stableSort;
  5906. var seriesTypes = H.seriesTypes;
  5907. /**
  5908. * @private
  5909. * @mixin onSeriesMixin
  5910. */
  5911. var onSeriesMixin = {
  5912. /* eslint-disable valid-jsdoc */
  5913. /**
  5914. * Override getPlotBox. If the onSeries option is valid, return the plot box
  5915. * of the onSeries, otherwise proceed as usual.
  5916. *
  5917. * @private
  5918. * @function onSeriesMixin.getPlotBox
  5919. * @return {Highcharts.SeriesPlotBoxObject}
  5920. */
  5921. getPlotBox: function () {
  5922. return H.Series.prototype.getPlotBox.call((this.options.onSeries &&
  5923. this.chart.get(this.options.onSeries)) || this);
  5924. },
  5925. /**
  5926. * Extend the translate method by placing the point on the related series
  5927. *
  5928. * @private
  5929. * @function onSeriesMixin.translate
  5930. * @return {void}
  5931. */
  5932. translate: function () {
  5933. seriesTypes.column.prototype.translate.apply(this);
  5934. var series = this, options = series.options, chart = series.chart, points = series.points, cursor = points.length - 1, point, lastPoint, optionsOnSeries = options.onSeries, onSeries = (optionsOnSeries &&
  5935. chart.get(optionsOnSeries)), onKey = options.onKey || 'y', step = onSeries && onSeries.options.step, onData = (onSeries && onSeries.points), i = onData && onData.length, inverted = chart.inverted, xAxis = series.xAxis, yAxis = series.yAxis, xOffset = 0, leftPoint, lastX, rightPoint, currentDataGrouping, distanceRatio;
  5936. // relate to a master series
  5937. if (onSeries && onSeries.visible && i) {
  5938. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  5939. currentDataGrouping = onSeries.currentDataGrouping;
  5940. lastX = (onData[i - 1].x +
  5941. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  5942. // sort the data points
  5943. stableSort(points, function (a, b) {
  5944. return (a.x - b.x);
  5945. });
  5946. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  5947. while (i-- && points[cursor]) {
  5948. leftPoint = onData[i];
  5949. point = points[cursor];
  5950. point.y = leftPoint.y;
  5951. if (leftPoint.x <= point.x &&
  5952. typeof leftPoint[onKey] !== 'undefined') {
  5953. if (point.x <= lastX) { // #803
  5954. point.plotY = leftPoint[onKey];
  5955. // interpolate between points, #666
  5956. if (leftPoint.x < point.x &&
  5957. !step) {
  5958. rightPoint = onData[i + 1];
  5959. if (rightPoint &&
  5960. typeof rightPoint[onKey] !== 'undefined') {
  5961. // the distance ratio, between 0 and 1
  5962. distanceRatio =
  5963. (point.x - leftPoint.x) /
  5964. (rightPoint.x - leftPoint.x);
  5965. point.plotY +=
  5966. distanceRatio *
  5967. // the plotY distance
  5968. (rightPoint[onKey] - leftPoint[onKey]);
  5969. point.y +=
  5970. distanceRatio *
  5971. (rightPoint.y - leftPoint.y);
  5972. }
  5973. }
  5974. }
  5975. cursor--;
  5976. i++; // check again for points in the same x position
  5977. if (cursor < 0) {
  5978. break;
  5979. }
  5980. }
  5981. }
  5982. }
  5983. // Add plotY position and handle stacking
  5984. points.forEach(function (point, i) {
  5985. var stackIndex;
  5986. point.plotX += xOffset; // #2049
  5987. // Undefined plotY means the point is either on axis, outside series
  5988. // range or hidden series. If the series is outside the range of the
  5989. // x axis it should fall through with an undefined plotY, but then
  5990. // we must remove the shapeArgs (#847). For inverted charts, we need
  5991. // to calculate position anyway, because series.invertGroups is not
  5992. // defined
  5993. if (typeof point.plotY === 'undefined' || inverted) {
  5994. if (point.plotX >= 0 &&
  5995. point.plotX <= xAxis.len) {
  5996. // We're inside xAxis range
  5997. if (inverted) {
  5998. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  5999. point.plotX = defined(point.y) ?
  6000. yAxis.translate(point.y, 0, 0, 0, 1) :
  6001. 0;
  6002. }
  6003. else {
  6004. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  6005. xAxis.offset; // For the windbarb demo
  6006. }
  6007. }
  6008. else {
  6009. point.shapeArgs = {}; // 847
  6010. }
  6011. }
  6012. // if multiple flags appear at the same x, order them into a stack
  6013. lastPoint = points[i - 1];
  6014. if (lastPoint && lastPoint.plotX === point.plotX) {
  6015. if (typeof lastPoint.stackIndex === 'undefined') {
  6016. lastPoint.stackIndex = 0;
  6017. }
  6018. stackIndex = lastPoint.stackIndex + 1;
  6019. }
  6020. point.stackIndex = stackIndex; // #3639
  6021. });
  6022. this.onSeries = onSeries;
  6023. }
  6024. /* eslint-enable valid-jsdoc */
  6025. };
  6026. return onSeriesMixin;
  6027. });
  6028. _registerModule(_modules, 'parts/FlagsSeries.js', [_modules['parts/Globals.js'], _modules['parts/SVGElement.js'], _modules['parts/SVGRenderer.js'], _modules['parts/Utilities.js'], _modules['mixins/on-series.js']], function (H, SVGElement, SVGRenderer, U, onSeriesMixin) {
  6029. /* *
  6030. *
  6031. * (c) 2010-2020 Torstein Honsi
  6032. *
  6033. * License: www.highcharts.com/license
  6034. *
  6035. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6036. *
  6037. * */
  6038. var addEvent = U.addEvent, defined = U.defined, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, seriesType = U.seriesType, wrap = U.wrap;
  6039. /**
  6040. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  6041. */
  6042. var noop = H.noop, Renderer = H.Renderer, Series = H.Series, TrackerMixin = H.TrackerMixin, VMLRenderer = H.VMLRenderer, symbols = SVGRenderer.prototype.symbols;
  6043. /**
  6044. * The Flags series.
  6045. *
  6046. * @private
  6047. * @class
  6048. * @name Highcharts.seriesTypes.flags
  6049. *
  6050. * @augments Highcharts.Series
  6051. */
  6052. seriesType('flags', 'column'
  6053. /**
  6054. * Flags are used to mark events in stock charts. They can be added on the
  6055. * timeline, or attached to a specific series.
  6056. *
  6057. * @sample stock/demo/flags-general/
  6058. * Flags on a line series
  6059. *
  6060. * @extends plotOptions.column
  6061. * @excluding animation, borderColor, borderRadius, borderWidth,
  6062. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  6063. * turboThreshold
  6064. * @product highstock
  6065. * @optionparent plotOptions.flags
  6066. */
  6067. , {
  6068. /**
  6069. * In case the flag is placed on a series, on what point key to place
  6070. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  6071. * however, the flag can optionally be placed on the `open`, `high`,
  6072. * `low` or `close` key.
  6073. *
  6074. * @sample {highstock} stock/plotoptions/flags-onkey/
  6075. * Range series, flag on high
  6076. *
  6077. * @type {string}
  6078. * @default y
  6079. * @since 4.2.2
  6080. * @product highstock
  6081. * @validvalue ["y", "open", "high", "low", "close"]
  6082. * @apioption plotOptions.flags.onKey
  6083. */
  6084. /**
  6085. * The id of the series that the flags should be drawn on. If no id
  6086. * is given, the flags are drawn on the x axis.
  6087. *
  6088. * @sample {highstock} stock/plotoptions/flags/
  6089. * Flags on series and on x axis
  6090. *
  6091. * @type {string}
  6092. * @product highstock
  6093. * @apioption plotOptions.flags.onSeries
  6094. */
  6095. pointRange: 0,
  6096. /**
  6097. * Whether the flags are allowed to overlap sideways. If `false`, the
  6098. * flags are moved sideways using an algorithm that seeks to place every
  6099. * flag as close as possible to its original position.
  6100. *
  6101. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  6102. * Allow sideways overlap
  6103. *
  6104. * @since 6.0.4
  6105. */
  6106. allowOverlapX: false,
  6107. /**
  6108. * The shape of the marker. Can be one of "flag", "circlepin",
  6109. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  6110. * Individual shapes can also be set for each point.
  6111. *
  6112. * @sample {highstock} stock/plotoptions/flags/
  6113. * Different shapes
  6114. *
  6115. * @type {Highcharts.FlagsShapeValue}
  6116. * @product highstock
  6117. */
  6118. shape: 'flag',
  6119. /**
  6120. * When multiple flags in the same series fall on the same value, this
  6121. * number determines the vertical offset between them.
  6122. *
  6123. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  6124. * A greater stack distance
  6125. *
  6126. * @product highstock
  6127. */
  6128. stackDistance: 12,
  6129. /**
  6130. * Text alignment for the text inside the flag.
  6131. *
  6132. * @since 5.0.0
  6133. * @product highstock
  6134. * @validvalue ["left", "center", "right"]
  6135. */
  6136. textAlign: 'center',
  6137. /**
  6138. * Specific tooltip options for flag series. Flag series tooltips are
  6139. * different from most other types in that a flag doesn't have a data
  6140. * value, so the tooltip rather displays the `text` option for each
  6141. * point.
  6142. *
  6143. * @extends plotOptions.series.tooltip
  6144. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  6145. * @product highstock
  6146. */
  6147. tooltip: {
  6148. pointFormat: '{point.text}<br/>'
  6149. },
  6150. threshold: null,
  6151. /**
  6152. * The text to display on each flag. This can be defined on series
  6153. * level, or individually for each point. Defaults to `"A"`.
  6154. *
  6155. * @type {string}
  6156. * @default A
  6157. * @product highstock
  6158. * @apioption plotOptions.flags.title
  6159. */
  6160. /**
  6161. * The y position of the top left corner of the flag relative to either
  6162. * the series (if onSeries is defined), or the x axis. Defaults to
  6163. * `-30`.
  6164. *
  6165. * @product highstock
  6166. */
  6167. y: -30,
  6168. /**
  6169. * Whether to use HTML to render the flag texts. Using HTML allows for
  6170. * advanced formatting, images and reliable bi-directional text
  6171. * rendering. Note that exported images won't respect the HTML, and that
  6172. * HTML won't respect Z-index settings.
  6173. *
  6174. * @type {boolean}
  6175. * @default false
  6176. * @since 1.3
  6177. * @product highstock
  6178. * @apioption plotOptions.flags.useHTML
  6179. */
  6180. /**
  6181. * Fixed width of the flag's shape. By default, width is autocalculated
  6182. * according to the flag's title.
  6183. *
  6184. * @sample {highstock} stock/demo/flags-shapes/
  6185. * Flags with fixed width
  6186. *
  6187. * @type {number}
  6188. * @product highstock
  6189. * @apioption plotOptions.flags.width
  6190. */
  6191. /**
  6192. * Fixed height of the flag's shape. By default, height is
  6193. * autocalculated according to the flag's title.
  6194. *
  6195. * @type {number}
  6196. * @product highstock
  6197. * @apioption plotOptions.flags.height
  6198. */
  6199. /**
  6200. * The fill color for the flags.
  6201. *
  6202. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6203. * @product highstock
  6204. */
  6205. fillColor: '#ffffff',
  6206. /**
  6207. * The color of the line/border of the flag.
  6208. *
  6209. * In styled mode, the stroke is set in the
  6210. * `.highcharts-flag-series.highcharts-point` rule.
  6211. *
  6212. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6213. * @default #000000
  6214. * @product highstock
  6215. * @apioption plotOptions.flags.lineColor
  6216. */
  6217. /**
  6218. * The pixel width of the flag's line/border.
  6219. *
  6220. * @product highstock
  6221. */
  6222. lineWidth: 1,
  6223. states: {
  6224. /**
  6225. * @extends plotOptions.column.states.hover
  6226. * @product highstock
  6227. */
  6228. hover: {
  6229. /**
  6230. * The color of the line/border of the flag.
  6231. *
  6232. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6233. * @product highstock
  6234. */
  6235. lineColor: '#000000',
  6236. /**
  6237. * The fill or background color of the flag.
  6238. *
  6239. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6240. * @product highstock
  6241. */
  6242. fillColor: '#ccd6eb'
  6243. }
  6244. },
  6245. /**
  6246. * The text styles of the flag.
  6247. *
  6248. * In styled mode, the styles are set in the
  6249. * `.highcharts-flag-series .highcharts-point` rule.
  6250. *
  6251. * @type {Highcharts.CSSObject}
  6252. * @default {"fontSize": "11px", "fontWeight": "bold"}
  6253. * @product highstock
  6254. */
  6255. style: {
  6256. /** @ignore-option */
  6257. fontSize: '11px',
  6258. /** @ignore-option */
  6259. fontWeight: 'bold'
  6260. }
  6261. },
  6262. /**
  6263. * @lends seriesTypes.flags.prototype
  6264. */
  6265. {
  6266. sorted: false,
  6267. noSharedTooltip: true,
  6268. allowDG: false,
  6269. takeOrdinalPosition: false,
  6270. trackerGroups: ['markerGroup'],
  6271. forceCrop: true,
  6272. /* eslint-disable no-invalid-this, valid-jsdoc */
  6273. /**
  6274. * Inherit the initialization from base Series.
  6275. *
  6276. * @private
  6277. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  6278. */
  6279. init: Series.prototype.init,
  6280. /**
  6281. * Get presentational attributes
  6282. *
  6283. * @private
  6284. * @function Highcharts.seriesTypes.flags#pointAttribs
  6285. *
  6286. * @param {Highcharts.Point} point
  6287. *
  6288. * @param {string} [state]
  6289. *
  6290. * @return {Highcharts.SVGAttributes}
  6291. */
  6292. pointAttribs: function (point, state) {
  6293. var options = this.options, color = (point && point.color) || this.color, lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
  6294. if (state) {
  6295. fill = options.states[state].fillColor;
  6296. lineColor = options.states[state].lineColor;
  6297. lineWidth = options.states[state].lineWidth;
  6298. }
  6299. return {
  6300. fill: fill || color,
  6301. stroke: lineColor || color,
  6302. 'stroke-width': lineWidth || options.lineWidth || 0
  6303. };
  6304. },
  6305. translate: onSeriesMixin.translate,
  6306. getPlotBox: onSeriesMixin.getPlotBox,
  6307. /**
  6308. * Draw the markers.
  6309. *
  6310. * @private
  6311. * @function Highcharts.seriesTypes.flags#drawPoints
  6312. * @return {void}
  6313. */
  6314. drawPoints: function () {
  6315. var series = this, points = series.points, chart = series.chart, renderer = chart.renderer, plotX, plotY, inverted = chart.inverted, options = series.options, optionsY = options.y, shape, i, point, graphic, stackIndex, anchorY, attribs, outsideRight, yAxis = series.yAxis, boxesMap = {}, boxes = [], centered;
  6316. i = points.length;
  6317. while (i--) {
  6318. point = points[i];
  6319. outsideRight =
  6320. (inverted ? point.plotY : point.plotX) >
  6321. series.xAxis.len;
  6322. plotX = point.plotX;
  6323. stackIndex = point.stackIndex;
  6324. shape = point.options.shape || options.shape;
  6325. plotY = point.plotY;
  6326. if (typeof plotY !== 'undefined') {
  6327. plotY = point.plotY + optionsY -
  6328. (typeof stackIndex !== 'undefined' &&
  6329. (stackIndex * options.stackDistance));
  6330. }
  6331. // skip connectors for higher level stacked points
  6332. point.anchorX = stackIndex ? void 0 : point.plotX;
  6333. anchorY = stackIndex ? void 0 : point.plotY;
  6334. centered = shape !== 'flag';
  6335. graphic = point.graphic;
  6336. // Only draw the point if y is defined and the flag is within
  6337. // the visible area
  6338. if (typeof plotY !== 'undefined' &&
  6339. plotX >= 0 &&
  6340. !outsideRight) {
  6341. // Create the flag
  6342. if (!graphic) {
  6343. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML);
  6344. if (!chart.styledMode) {
  6345. graphic
  6346. .attr(series.pointAttribs(point))
  6347. .css(merge(options.style, point.style));
  6348. }
  6349. graphic.attr({
  6350. align: centered ? 'center' : 'left',
  6351. width: options.width,
  6352. height: options.height,
  6353. 'text-align': options.textAlign
  6354. })
  6355. .addClass('highcharts-point')
  6356. .add(series.markerGroup);
  6357. // Add reference to the point for tracker (#6303)
  6358. if (point.graphic.div) {
  6359. point.graphic.div.point = point;
  6360. }
  6361. if (!chart.styledMode) {
  6362. graphic.shadow(options.shadow);
  6363. }
  6364. graphic.isNew = true;
  6365. }
  6366. if (plotX > 0) { // #3119
  6367. plotX -= graphic.strokeWidth() % 2; // #4285
  6368. }
  6369. // Plant the flag
  6370. attribs = {
  6371. y: plotY,
  6372. anchorY: anchorY
  6373. };
  6374. if (options.allowOverlapX) {
  6375. attribs.x = plotX;
  6376. attribs.anchorX = point.anchorX;
  6377. }
  6378. graphic.attr({
  6379. text: point.options.title || options.title || 'A'
  6380. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  6381. // Rig for the distribute function
  6382. if (!options.allowOverlapX) {
  6383. if (!boxesMap[point.plotX]) {
  6384. boxesMap[point.plotX] = {
  6385. align: centered ? 0.5 : 0,
  6386. size: graphic.width,
  6387. target: plotX,
  6388. anchorX: plotX
  6389. };
  6390. }
  6391. else {
  6392. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  6393. }
  6394. }
  6395. // Set the tooltip anchor position
  6396. point.tooltipPos = [
  6397. plotX,
  6398. plotY + yAxis.pos - chart.plotTop
  6399. ]; // #6327
  6400. }
  6401. else if (graphic) {
  6402. point.graphic = graphic.destroy();
  6403. }
  6404. }
  6405. // Handle X-dimension overlapping
  6406. if (!options.allowOverlapX) {
  6407. objectEach(boxesMap, function (box) {
  6408. box.plotX = box.anchorX;
  6409. boxes.push(box);
  6410. });
  6411. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  6412. points.forEach(function (point) {
  6413. var box = point.graphic && boxesMap[point.plotX];
  6414. if (box) {
  6415. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  6416. x: box.pos + box.align * box.size,
  6417. anchorX: point.anchorX
  6418. });
  6419. // Hide flag when its box position is not specified
  6420. // (#8573, #9299)
  6421. if (!defined(box.pos)) {
  6422. point.graphic.attr({
  6423. x: -9999,
  6424. anchorX: -9999
  6425. });
  6426. point.graphic.isNew = true;
  6427. }
  6428. else {
  6429. point.graphic.isNew = false;
  6430. }
  6431. }
  6432. });
  6433. }
  6434. // Can be a mix of SVG and HTML and we need events for both (#6303)
  6435. if (options.useHTML) {
  6436. wrap(series.markerGroup, 'on', function (proceed) {
  6437. return SVGElement.prototype.on.apply(
  6438. // for HTML
  6439. proceed.apply(this, [].slice.call(arguments, 1)),
  6440. // and for SVG
  6441. [].slice.call(arguments, 1));
  6442. });
  6443. }
  6444. },
  6445. /**
  6446. * Extend the column trackers with listeners to expand and contract
  6447. * stacks.
  6448. *
  6449. * @private
  6450. * @function Highcharts.seriesTypes.flags#drawTracker
  6451. * @return {void}
  6452. */
  6453. drawTracker: function () {
  6454. var series = this, points = series.points;
  6455. TrackerMixin.drawTrackerPoint.apply(this);
  6456. /* *
  6457. * Bring each stacked flag up on mouse over, this allows readability
  6458. * of vertically stacked elements as well as tight points on the x
  6459. * axis. #1924.
  6460. */
  6461. points.forEach(function (point) {
  6462. var graphic = point.graphic;
  6463. if (graphic) {
  6464. addEvent(graphic.element, 'mouseover', function () {
  6465. // Raise this point
  6466. if (point.stackIndex > 0 &&
  6467. !point.raised) {
  6468. point._y = graphic.y;
  6469. graphic.attr({
  6470. y: point._y - 8
  6471. });
  6472. point.raised = true;
  6473. }
  6474. // Revert other raised points
  6475. points.forEach(function (otherPoint) {
  6476. if (otherPoint !== point &&
  6477. otherPoint.raised &&
  6478. otherPoint.graphic) {
  6479. otherPoint.graphic.attr({
  6480. y: otherPoint._y
  6481. });
  6482. otherPoint.raised = false;
  6483. }
  6484. });
  6485. });
  6486. }
  6487. });
  6488. },
  6489. /**
  6490. * Disable animation, but keep clipping (#8546).
  6491. *
  6492. * @private
  6493. * @function Highcharts.seriesTypes.flags#animate
  6494. * @param {boolean} [init]
  6495. * @return {void}
  6496. */
  6497. animate: function (init) {
  6498. if (init) {
  6499. this.setClip();
  6500. }
  6501. },
  6502. /**
  6503. * @private
  6504. * @function Highcharts.seriesTypes.flags#setClip
  6505. * @return {void}
  6506. */
  6507. setClip: function () {
  6508. Series.prototype.setClip.apply(this, arguments);
  6509. if (this.options.clip !== false && this.sharedClipKey) {
  6510. this.markerGroup
  6511. .clip(this.chart[this.sharedClipKey]);
  6512. }
  6513. },
  6514. /**
  6515. * @private
  6516. * @function Highcharts.seriesTypes.flags#buildKDTree
  6517. */
  6518. buildKDTree: noop,
  6519. /**
  6520. * Don't invert the flag marker group (#4960).
  6521. *
  6522. * @private
  6523. * @function Highcharts.seriesTypes.flags#invertGroups
  6524. */
  6525. invertGroups: noop
  6526. /* eslint-enable no-invalid-this, valid-jsdoc */
  6527. },
  6528. /**
  6529. * @lends Highcharts.seriesTypes.flag.prototype.pointClass.prototype
  6530. */
  6531. {
  6532. isValid: function () {
  6533. // #9233 - Prevent from treating flags as null points (even if
  6534. // they have no y values defined).
  6535. return isNumber(this.y) || typeof this.y === 'undefined';
  6536. }
  6537. });
  6538. // create the flag icon with anchor
  6539. symbols.flag = function (x, y, w, h, options) {
  6540. var anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
  6541. // To do: unwanted any cast because symbols.circle has wrong type, it
  6542. // actually returns an SVGPathArray
  6543. var path = symbols.circle(anchorX - 1, anchorY - 1, 2, 2);
  6544. path.push(['M', anchorX, anchorY], ['L', x, y + h], ['L', x, y], ['L', x + w, y], ['L', x + w, y + h], ['L', x, y + h], ['Z']);
  6545. return path;
  6546. };
  6547. /**
  6548. * Create the circlepin and squarepin icons with anchor.
  6549. * @private
  6550. * @param {string} shape - circle or square
  6551. * @return {void}
  6552. */
  6553. function createPinSymbol(shape) {
  6554. symbols[shape + 'pin'] = function (x, y, w, h, options) {
  6555. var anchorX = options && options.anchorX, anchorY = options && options.anchorY, path;
  6556. // For single-letter flags, make sure circular flags are not taller
  6557. // than their width
  6558. if (shape === 'circle' && h > w) {
  6559. x -= Math.round((h - w) / 2);
  6560. w = h;
  6561. }
  6562. path = (symbols[shape])(x, y, w, h);
  6563. if (anchorX && anchorY) {
  6564. /**
  6565. * If the label is below the anchor, draw the connecting line from
  6566. * the top edge of the label, otherwise start drawing from the
  6567. * bottom edge
  6568. */
  6569. var labelX = anchorX;
  6570. if (shape === 'circle') {
  6571. labelX = x + w / 2;
  6572. }
  6573. else {
  6574. var startSeg = path[0];
  6575. var endSeg = path[1];
  6576. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  6577. labelX = (startSeg[1] + endSeg[1]) / 2;
  6578. }
  6579. }
  6580. var labelY = (y > anchorY) ? y : y + h;
  6581. path.push([
  6582. 'M',
  6583. labelX,
  6584. labelY
  6585. ], [
  6586. 'L',
  6587. anchorX,
  6588. anchorY
  6589. ]);
  6590. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  6591. }
  6592. return path;
  6593. };
  6594. }
  6595. createPinSymbol('circle');
  6596. createPinSymbol('square');
  6597. /**
  6598. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  6599. * Even VML browsers need this in order to generate shapes in export. Now share
  6600. * them with the VMLRenderer.
  6601. */
  6602. if (Renderer === VMLRenderer) {
  6603. ['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
  6604. VMLRenderer.prototype.symbols[shape] = symbols[shape];
  6605. });
  6606. }
  6607. /**
  6608. * A `flags` series. If the [type](#series.flags.type) option is not
  6609. * specified, it is inherited from [chart.type](#chart.type).
  6610. *
  6611. * @extends series,plotOptions.flags
  6612. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  6613. * connectNulls, dashStyle, dataGrouping, dataParser, dataURL,
  6614. * gapSize, gapUnit, linecap, lineWidth, marker, pointPadding,
  6615. * pointWidth, step, turboThreshold, useOhlcData
  6616. * @product highstock
  6617. * @apioption series.flags
  6618. */
  6619. /**
  6620. * An array of data points for the series. For the `flags` series type,
  6621. * points can be given in the following ways:
  6622. *
  6623. * 1. An array of objects with named values. The following snippet shows only a
  6624. * few settings, see the complete options set below. If the total number of
  6625. * data points exceeds the series'
  6626. * [turboThreshold](#series.flags.turboThreshold), this option is not
  6627. * available.
  6628. * ```js
  6629. * data: [{
  6630. * x: 1,
  6631. * title: "A",
  6632. * text: "First event"
  6633. * }, {
  6634. * x: 1,
  6635. * title: "B",
  6636. * text: "Second event"
  6637. * }]
  6638. * ```
  6639. *
  6640. * @type {Array<*>}
  6641. * @extends series.line.data
  6642. * @excluding dataLabels, marker, name, y
  6643. * @product highstock
  6644. * @apioption series.flags.data
  6645. */
  6646. /**
  6647. * The fill color of an individual flag. By default it inherits from
  6648. * the series color.
  6649. *
  6650. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6651. * @product highstock
  6652. * @apioption series.flags.data.fillColor
  6653. */
  6654. /**
  6655. * The longer text to be shown in the flag's tooltip.
  6656. *
  6657. * @type {string}
  6658. * @product highstock
  6659. * @apioption series.flags.data.text
  6660. */
  6661. /**
  6662. * The short text to be shown on the flag.
  6663. *
  6664. * @type {string}
  6665. * @product highstock
  6666. * @apioption series.flags.data.title
  6667. */
  6668. ''; // adds doclets above to transpiled file
  6669. });
  6670. _registerModule(_modules, 'parts/RangeSelector.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Options.js'], _modules['parts/SVGElement.js'], _modules['parts/Utilities.js']], function (Axis, Chart, H, O, SVGElement, U) {
  6671. /* *
  6672. *
  6673. * (c) 2010-2020 Torstein Honsi
  6674. *
  6675. * License: www.highcharts.com/license
  6676. *
  6677. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6678. *
  6679. * */
  6680. var defaultOptions = O.defaultOptions;
  6681. var addEvent = U.addEvent, createElement = U.createElement, css = U.css, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, discardElement = U.discardElement, extend = U.extend, fireEvent = U.fireEvent, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, splat = U.splat;
  6682. /**
  6683. * Define the time span for the button
  6684. *
  6685. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  6686. */
  6687. /**
  6688. * Callback function to react on button clicks.
  6689. *
  6690. * @callback Highcharts.RangeSelectorClickCallbackFunction
  6691. *
  6692. * @param {global.Event} e
  6693. * Event arguments.
  6694. *
  6695. * @param {boolean|undefined}
  6696. * Return false to cancel the default button event.
  6697. */
  6698. /**
  6699. * Callback function to parse values entered in the input boxes and return a
  6700. * valid JavaScript time as milliseconds since 1970.
  6701. *
  6702. * @callback Highcharts.RangeSelectorParseCallbackFunction
  6703. *
  6704. * @param {string} value
  6705. * Input value to parse.
  6706. *
  6707. * @return {number}
  6708. * Parsed JavaScript time value.
  6709. */
  6710. /* ************************************************************************** *
  6711. * Start Range Selector code *
  6712. * ************************************************************************** */
  6713. extend(defaultOptions, {
  6714. /**
  6715. * The range selector is a tool for selecting ranges to display within
  6716. * the chart. It provides buttons to select preconfigured ranges in
  6717. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  6718. * boxes where min and max dates can be manually input.
  6719. *
  6720. * @product highstock gantt
  6721. * @optionparent rangeSelector
  6722. */
  6723. rangeSelector: {
  6724. /**
  6725. * Whether to enable all buttons from the start. By default buttons are
  6726. * only enabled if the corresponding time range exists on the X axis,
  6727. * but enabling all buttons allows for dynamically loading different
  6728. * time ranges.
  6729. *
  6730. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  6731. * All buttons enabled
  6732. *
  6733. * @type {boolean}
  6734. * @default false
  6735. * @since 2.0.3
  6736. * @apioption rangeSelector.allButtonsEnabled
  6737. */
  6738. /**
  6739. * An array of configuration objects for the buttons.
  6740. *
  6741. * Defaults to:
  6742. * ```js
  6743. * buttons: [{
  6744. * type: 'month',
  6745. * count: 1,
  6746. * text: '1m'
  6747. * }, {
  6748. * type: 'month',
  6749. * count: 3,
  6750. * text: '3m'
  6751. * }, {
  6752. * type: 'month',
  6753. * count: 6,
  6754. * text: '6m'
  6755. * }, {
  6756. * type: 'ytd',
  6757. * text: 'YTD'
  6758. * }, {
  6759. * type: 'year',
  6760. * count: 1,
  6761. * text: '1y'
  6762. * }, {
  6763. * type: 'all',
  6764. * text: 'All'
  6765. * }]
  6766. * ```
  6767. *
  6768. * @sample {highstock} stock/rangeselector/datagrouping/
  6769. * Data grouping by buttons
  6770. *
  6771. * @type {Array<*>}
  6772. * @apioption rangeSelector.buttons
  6773. */
  6774. /**
  6775. * How many units of the defined type the button should span. If `type`
  6776. * is "month" and `count` is 3, the button spans three months.
  6777. *
  6778. * @type {number}
  6779. * @default 1
  6780. * @apioption rangeSelector.buttons.count
  6781. */
  6782. /**
  6783. * Fires when clicking on the rangeSelector button. One parameter,
  6784. * event, is passed to the function, containing common event
  6785. * information.
  6786. *
  6787. * ```js
  6788. * click: function(e) {
  6789. * console.log(this);
  6790. * }
  6791. * ```
  6792. *
  6793. * Return false to stop default button's click action.
  6794. *
  6795. * @sample {highstock} stock/rangeselector/button-click/
  6796. * Click event on the button
  6797. *
  6798. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  6799. * @apioption rangeSelector.buttons.events.click
  6800. */
  6801. /**
  6802. * Additional range (in milliseconds) added to the end of the calculated
  6803. * time span.
  6804. *
  6805. * @sample {highstock} stock/rangeselector/min-max-offsets/
  6806. * Button offsets
  6807. *
  6808. * @type {number}
  6809. * @default 0
  6810. * @since 6.0.0
  6811. * @apioption rangeSelector.buttons.offsetMax
  6812. */
  6813. /**
  6814. * Additional range (in milliseconds) added to the start of the
  6815. * calculated time span.
  6816. *
  6817. * @sample {highstock} stock/rangeselector/min-max-offsets/
  6818. * Button offsets
  6819. *
  6820. * @type {number}
  6821. * @default 0
  6822. * @since 6.0.0
  6823. * @apioption rangeSelector.buttons.offsetMin
  6824. */
  6825. /**
  6826. * When buttons apply dataGrouping on a series, by default zooming
  6827. * in/out will deselect buttons and unset dataGrouping. Enable this
  6828. * option to keep buttons selected when extremes change.
  6829. *
  6830. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  6831. * Different preserveDataGrouping settings
  6832. *
  6833. * @type {boolean}
  6834. * @default false
  6835. * @since 6.1.2
  6836. * @apioption rangeSelector.buttons.preserveDataGrouping
  6837. */
  6838. /**
  6839. * A custom data grouping object for each button.
  6840. *
  6841. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  6842. *
  6843. * @sample {highstock} stock/rangeselector/datagrouping/
  6844. * Data grouping by range selector buttons
  6845. *
  6846. * @type {*}
  6847. * @extends plotOptions.series.dataGrouping
  6848. * @apioption rangeSelector.buttons.dataGrouping
  6849. */
  6850. /**
  6851. * The text for the button itself.
  6852. *
  6853. * @type {string}
  6854. * @apioption rangeSelector.buttons.text
  6855. */
  6856. /**
  6857. * Defined the time span for the button. Can be one of `millisecond`,
  6858. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  6859. * and `all`.
  6860. *
  6861. * @type {Highcharts.RangeSelectorButtonTypeValue}
  6862. * @apioption rangeSelector.buttons.type
  6863. */
  6864. /**
  6865. * The space in pixels between the buttons in the range selector.
  6866. *
  6867. * @type {number}
  6868. * @default 0
  6869. * @apioption rangeSelector.buttonSpacing
  6870. */
  6871. /**
  6872. * Enable or disable the range selector.
  6873. *
  6874. * @sample {highstock} stock/rangeselector/enabled/
  6875. * Disable the range selector
  6876. *
  6877. * @type {boolean}
  6878. * @default true
  6879. * @apioption rangeSelector.enabled
  6880. */
  6881. /**
  6882. * The vertical alignment of the rangeselector box. Allowed properties
  6883. * are `top`, `middle`, `bottom`.
  6884. *
  6885. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  6886. * Middle
  6887. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  6888. * Bottom
  6889. *
  6890. * @type {Highcharts.VerticalAlignValue}
  6891. * @since 6.0.0
  6892. */
  6893. verticalAlign: 'top',
  6894. /**
  6895. * A collection of attributes for the buttons. The object takes SVG
  6896. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  6897. * a collection of CSS properties for the text.
  6898. *
  6899. * The object can also be extended with states, so you can set
  6900. * presentational options for `hover`, `select` or `disabled` button
  6901. * states.
  6902. *
  6903. * CSS styles for the text label.
  6904. *
  6905. * In styled mode, the buttons are styled by the
  6906. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  6907. * different states.
  6908. *
  6909. * @sample {highstock} stock/rangeselector/styling/
  6910. * Styling the buttons and inputs
  6911. *
  6912. * @type {Highcharts.SVGAttributes}
  6913. */
  6914. buttonTheme: {
  6915. /** @ignore */
  6916. width: 28,
  6917. /** @ignore */
  6918. height: 18,
  6919. /** @ignore */
  6920. padding: 2,
  6921. /** @ignore */
  6922. zIndex: 7 // #484, #852
  6923. },
  6924. /**
  6925. * When the rangeselector is floating, the plot area does not reserve
  6926. * space for it. This opens for positioning anywhere on the chart.
  6927. *
  6928. * @sample {highstock} stock/rangeselector/floating/
  6929. * Placing the range selector between the plot area and the
  6930. * navigator
  6931. *
  6932. * @since 6.0.0
  6933. */
  6934. floating: false,
  6935. /**
  6936. * The x offset of the range selector relative to its horizontal
  6937. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  6938. *
  6939. * @since 6.0.0
  6940. */
  6941. x: 0,
  6942. /**
  6943. * The y offset of the range selector relative to its horizontal
  6944. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  6945. *
  6946. * @since 6.0.0
  6947. */
  6948. y: 0,
  6949. /**
  6950. * Deprecated. The height of the range selector. Currently it is
  6951. * calculated dynamically.
  6952. *
  6953. * @deprecated
  6954. * @type {number|undefined}
  6955. * @since 2.1.9
  6956. */
  6957. height: void 0,
  6958. /**
  6959. * The border color of the date input boxes.
  6960. *
  6961. * @sample {highstock} stock/rangeselector/styling/
  6962. * Styling the buttons and inputs
  6963. *
  6964. * @type {Highcharts.ColorString}
  6965. * @default #cccccc
  6966. * @since 1.3.7
  6967. * @apioption rangeSelector.inputBoxBorderColor
  6968. */
  6969. /**
  6970. * The pixel height of the date input boxes.
  6971. *
  6972. * @sample {highstock} stock/rangeselector/styling/
  6973. * Styling the buttons and inputs
  6974. *
  6975. * @type {number}
  6976. * @default 17
  6977. * @since 1.3.7
  6978. * @apioption rangeSelector.inputBoxHeight
  6979. */
  6980. /**
  6981. * CSS for the container DIV holding the input boxes. Deprecated as
  6982. * of 1.2.5\. Use [inputPosition](#rangeSelector.inputPosition) instead.
  6983. *
  6984. * @sample {highstock} stock/rangeselector/styling/
  6985. * Styling the buttons and inputs
  6986. *
  6987. * @deprecated
  6988. * @type {Highcharts.CSSObject}
  6989. * @apioption rangeSelector.inputBoxStyle
  6990. */
  6991. /**
  6992. * The pixel width of the date input boxes.
  6993. *
  6994. * @sample {highstock} stock/rangeselector/styling/
  6995. * Styling the buttons and inputs
  6996. *
  6997. * @type {number}
  6998. * @default 90
  6999. * @since 1.3.7
  7000. * @apioption rangeSelector.inputBoxWidth
  7001. */
  7002. /**
  7003. * The date format in the input boxes when not selected for editing.
  7004. * Defaults to `%b %e, %Y`.
  7005. *
  7006. * @sample {highstock} stock/rangeselector/input-format/
  7007. * Milliseconds in the range selector
  7008. *
  7009. * @type {string}
  7010. * @default %b %e, %Y
  7011. * @apioption rangeSelector.inputDateFormat
  7012. */
  7013. /**
  7014. * A custom callback function to parse values entered in the input boxes
  7015. * and return a valid JavaScript time as milliseconds since 1970.
  7016. *
  7017. * @sample {highstock} stock/rangeselector/input-format/
  7018. * Milliseconds in the range selector
  7019. *
  7020. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  7021. * @since 1.3.3
  7022. * @apioption rangeSelector.inputDateParser
  7023. */
  7024. /**
  7025. * The date format in the input boxes when they are selected for
  7026. * editing. This must be a format that is recognized by JavaScript
  7027. * Date.parse.
  7028. *
  7029. * @sample {highstock} stock/rangeselector/input-format/
  7030. * Milliseconds in the range selector
  7031. *
  7032. * @type {string}
  7033. * @default %Y-%m-%d
  7034. * @apioption rangeSelector.inputEditDateFormat
  7035. */
  7036. /**
  7037. * Enable or disable the date input boxes. Defaults to enabled when
  7038. * there is enough space, disabled if not (typically mobile).
  7039. *
  7040. * @sample {highstock} stock/rangeselector/input-datepicker/
  7041. * Extending the input with a jQuery UI datepicker
  7042. *
  7043. * @type {boolean}
  7044. * @default true
  7045. * @apioption rangeSelector.inputEnabled
  7046. */
  7047. /**
  7048. * Positioning for the input boxes. Allowed properties are `align`,
  7049. * `x` and `y`.
  7050. *
  7051. * @since 1.2.4
  7052. */
  7053. inputPosition: {
  7054. /**
  7055. * The alignment of the input box. Allowed properties are `left`,
  7056. * `center`, `right`.
  7057. *
  7058. * @sample {highstock} stock/rangeselector/input-button-position/
  7059. * Alignment
  7060. *
  7061. * @type {Highcharts.AlignValue}
  7062. * @since 6.0.0
  7063. */
  7064. align: 'right',
  7065. /**
  7066. * X offset of the input row.
  7067. */
  7068. x: 0,
  7069. /**
  7070. * Y offset of the input row.
  7071. */
  7072. y: 0
  7073. },
  7074. /**
  7075. * The index of the button to appear pre-selected.
  7076. *
  7077. * @type {number}
  7078. * @apioption rangeSelector.selected
  7079. */
  7080. /**
  7081. * Positioning for the button row.
  7082. *
  7083. * @since 1.2.4
  7084. */
  7085. buttonPosition: {
  7086. /**
  7087. * The alignment of the input box. Allowed properties are `left`,
  7088. * `center`, `right`.
  7089. *
  7090. * @sample {highstock} stock/rangeselector/input-button-position/
  7091. * Alignment
  7092. *
  7093. * @type {Highcharts.AlignValue}
  7094. * @since 6.0.0
  7095. */
  7096. align: 'left',
  7097. /**
  7098. * X offset of the button row.
  7099. */
  7100. x: 0,
  7101. /**
  7102. * Y offset of the button row.
  7103. */
  7104. y: 0
  7105. },
  7106. /**
  7107. * CSS for the HTML inputs in the range selector.
  7108. *
  7109. * In styled mode, the inputs are styled by the
  7110. * `.highcharts-range-input text` rule in SVG mode, and
  7111. * `input.highcharts-range-selector` when active.
  7112. *
  7113. * @sample {highstock} stock/rangeselector/styling/
  7114. * Styling the buttons and inputs
  7115. *
  7116. * @type {Highcharts.CSSObject}
  7117. * @apioption rangeSelector.inputStyle
  7118. */
  7119. /**
  7120. * CSS styles for the labels - the Zoom, From and To texts.
  7121. *
  7122. * In styled mode, the labels are styled by the
  7123. * `.highcharts-range-label` class.
  7124. *
  7125. * @sample {highstock} stock/rangeselector/styling/
  7126. * Styling the buttons and inputs
  7127. *
  7128. * @type {Highcharts.CSSObject}
  7129. */
  7130. labelStyle: {
  7131. /** @ignore */
  7132. color: '#666666'
  7133. }
  7134. }
  7135. });
  7136. defaultOptions.lang = merge(defaultOptions.lang,
  7137. /**
  7138. * Language object. The language object is global and it can't be set
  7139. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  7140. * set it before any chart is initialized.
  7141. *
  7142. * ```js
  7143. * Highcharts.setOptions({
  7144. * lang: {
  7145. * months: [
  7146. * 'Janvier', 'Février', 'Mars', 'Avril',
  7147. * 'Mai', 'Juin', 'Juillet', 'Août',
  7148. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  7149. * ],
  7150. * weekdays: [
  7151. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  7152. * 'Jeudi', 'Vendredi', 'Samedi'
  7153. * ]
  7154. * }
  7155. * });
  7156. * ```
  7157. *
  7158. * @optionparent lang
  7159. */
  7160. {
  7161. /**
  7162. * The text for the label for the range selector buttons.
  7163. *
  7164. * @product highstock gantt
  7165. */
  7166. rangeSelectorZoom: 'Zoom',
  7167. /**
  7168. * The text for the label for the "from" input box in the range
  7169. * selector.
  7170. *
  7171. * @product highstock gantt
  7172. */
  7173. rangeSelectorFrom: 'From',
  7174. /**
  7175. * The text for the label for the "to" input box in the range selector.
  7176. *
  7177. * @product highstock gantt
  7178. */
  7179. rangeSelectorTo: 'To'
  7180. });
  7181. /* eslint-disable no-invalid-this, valid-jsdoc */
  7182. /**
  7183. * The range selector.
  7184. *
  7185. * @private
  7186. * @class
  7187. * @name Highcharts.RangeSelector
  7188. * @param {Highcharts.Chart} chart
  7189. */
  7190. var RangeSelector = /** @class */ (function () {
  7191. function RangeSelector(chart) {
  7192. /* *
  7193. *
  7194. * Properties
  7195. *
  7196. * */
  7197. this.buttons = void 0;
  7198. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  7199. this.options = void 0;
  7200. this.chart = chart;
  7201. // Run RangeSelector
  7202. this.init(chart);
  7203. }
  7204. /**
  7205. * The method to run when one of the buttons in the range selectors is
  7206. * clicked
  7207. *
  7208. * @private
  7209. * @function Highcharts.RangeSelector#clickButton
  7210. * @param {number} i
  7211. * The index of the button
  7212. * @param {boolean} [redraw]
  7213. * @return {void}
  7214. */
  7215. RangeSelector.prototype.clickButton = function (i, redraw) {
  7216. var rangeSelector = this, chart = rangeSelector.chart, rangeOptions = rangeSelector.buttonOptions[i], baseAxis = chart.xAxis[0], unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {}, dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, newMin, newMax = baseAxis && Math.round(Math.min(baseAxis.max, pick(dataMax, baseAxis.max))), // #1568
  7217. type = rangeOptions.type, baseXAxisOptions, range = rangeOptions._range, rangeMin, minSetting, rangeSetting, ctx, ytdExtremes, dataGrouping = rangeOptions.dataGrouping;
  7218. // chart has no data, base series is removed
  7219. if (dataMin === null || dataMax === null) {
  7220. return;
  7221. }
  7222. // Set the fixed range before range is altered
  7223. chart.fixedRange = range;
  7224. // Apply dataGrouping associated to button
  7225. if (dataGrouping) {
  7226. this.forcedDataGrouping = true;
  7227. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  7228. this.frozenStates = rangeOptions.preserveDataGrouping;
  7229. }
  7230. // Apply range
  7231. if (type === 'month' || type === 'year') {
  7232. if (!baseAxis) {
  7233. // This is set to the user options and picked up later when the
  7234. // axis is instantiated so that we know the min and max.
  7235. range = rangeOptions;
  7236. }
  7237. else {
  7238. ctx = {
  7239. range: rangeOptions,
  7240. max: newMax,
  7241. chart: chart,
  7242. dataMin: dataMin,
  7243. dataMax: dataMax
  7244. };
  7245. newMin = baseAxis.minFromRange.call(ctx);
  7246. if (isNumber(ctx.newMax)) {
  7247. newMax = ctx.newMax;
  7248. }
  7249. }
  7250. // Fixed times like minutes, hours, days
  7251. }
  7252. else if (range) {
  7253. newMin = Math.max(newMax - range, dataMin);
  7254. newMax = Math.min(newMin + range, dataMax);
  7255. }
  7256. else if (type === 'ytd') {
  7257. // On user clicks on the buttons, or a delayed action running from
  7258. // the beforeRender event (below), the baseAxis is defined.
  7259. if (baseAxis) {
  7260. // When "ytd" is the pre-selected button for the initial view,
  7261. // its calculation is delayed and rerun in the beforeRender
  7262. // event (below). When the series are initialized, but before
  7263. // the chart is rendered, we have access to the xData array
  7264. // (#942).
  7265. if (typeof dataMax === 'undefined') {
  7266. dataMin = Number.MAX_VALUE;
  7267. dataMax = Number.MIN_VALUE;
  7268. chart.series.forEach(function (series) {
  7269. // reassign it to the last item
  7270. var xData = series.xData;
  7271. dataMin = Math.min(xData[0], dataMin);
  7272. dataMax = Math.max(xData[xData.length - 1], dataMax);
  7273. });
  7274. redraw = false;
  7275. }
  7276. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  7277. newMin = rangeMin = ytdExtremes.min;
  7278. newMax = ytdExtremes.max;
  7279. // "ytd" is pre-selected. We don't yet have access to processed
  7280. // point and extremes data (things like pointStart and pointInterval
  7281. // are missing), so we delay the process (#942)
  7282. }
  7283. else {
  7284. rangeSelector.deferredYTDClick = i;
  7285. return;
  7286. }
  7287. }
  7288. else if (type === 'all' && baseAxis) {
  7289. newMin = dataMin;
  7290. newMax = dataMax;
  7291. }
  7292. newMin += rangeOptions._offsetMin;
  7293. newMax += rangeOptions._offsetMax;
  7294. rangeSelector.setSelected(i);
  7295. // Update the chart
  7296. if (!baseAxis) {
  7297. // Axis not yet instanciated. Temporarily set min and range
  7298. // options and remove them on chart load (#4317).
  7299. baseXAxisOptions = splat(chart.options.xAxis)[0];
  7300. rangeSetting = baseXAxisOptions.range;
  7301. baseXAxisOptions.range = range;
  7302. minSetting = baseXAxisOptions.min;
  7303. baseXAxisOptions.min = rangeMin;
  7304. addEvent(chart, 'load', function resetMinAndRange() {
  7305. baseXAxisOptions.range = rangeSetting;
  7306. baseXAxisOptions.min = minSetting;
  7307. });
  7308. }
  7309. else {
  7310. // Existing axis object. Set extremes after render time.
  7311. baseAxis.setExtremes(newMin, newMax, pick(redraw, 1), null, // auto animation
  7312. {
  7313. trigger: 'rangeSelectorButton',
  7314. rangeSelectorButton: rangeOptions
  7315. });
  7316. }
  7317. };
  7318. /**
  7319. * Set the selected option. This method only sets the internal flag, it
  7320. * doesn't update the buttons or the actual zoomed range.
  7321. *
  7322. * @private
  7323. * @function Highcharts.RangeSelector#setSelected
  7324. * @param {number} [selected]
  7325. * @return {void}
  7326. */
  7327. RangeSelector.prototype.setSelected = function (selected) {
  7328. this.selected = this.options.selected = selected;
  7329. };
  7330. /**
  7331. * Initialize the range selector
  7332. *
  7333. * @private
  7334. * @function Highcharts.RangeSelector#init
  7335. * @param {Highcharts.Chart} chart
  7336. * @return {void}
  7337. */
  7338. RangeSelector.prototype.init = function (chart) {
  7339. var rangeSelector = this, options = chart.options.rangeSelector, buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(), selectedOption = options.selected, blurInputs = function () {
  7340. var minInput = rangeSelector.minInput, maxInput = rangeSelector.maxInput;
  7341. // #3274 in some case blur is not defined
  7342. if (minInput && minInput.blur) {
  7343. fireEvent(minInput, 'blur');
  7344. }
  7345. if (maxInput && maxInput.blur) {
  7346. fireEvent(maxInput, 'blur');
  7347. }
  7348. };
  7349. rangeSelector.chart = chart;
  7350. rangeSelector.options = options;
  7351. rangeSelector.buttons = [];
  7352. rangeSelector.buttonOptions = buttonOptions;
  7353. this.unMouseDown = addEvent(chart.container, 'mousedown', blurInputs);
  7354. this.unResize = addEvent(chart, 'resize', blurInputs);
  7355. // Extend the buttonOptions with actual range
  7356. buttonOptions.forEach(rangeSelector.computeButtonRange);
  7357. // zoomed range based on a pre-selected button index
  7358. if (typeof selectedOption !== 'undefined' &&
  7359. buttonOptions[selectedOption]) {
  7360. this.clickButton(selectedOption, false);
  7361. }
  7362. addEvent(chart, 'load', function () {
  7363. // If a data grouping is applied to the current button, release it
  7364. // when extremes change
  7365. if (chart.xAxis && chart.xAxis[0]) {
  7366. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  7367. if (this.max - this.min !==
  7368. chart.fixedRange &&
  7369. e.trigger !== 'rangeSelectorButton' &&
  7370. e.trigger !== 'updatedData' &&
  7371. rangeSelector.forcedDataGrouping &&
  7372. !rangeSelector.frozenStates) {
  7373. this.setDataGrouping(false, false);
  7374. }
  7375. });
  7376. }
  7377. });
  7378. };
  7379. /**
  7380. * Dynamically update the range selector buttons after a new range has been
  7381. * set
  7382. *
  7383. * @private
  7384. * @function Highcharts.RangeSelector#updateButtonStates
  7385. * @return {void}
  7386. */
  7387. RangeSelector.prototype.updateButtonStates = function () {
  7388. var rangeSelector = this, chart = this.chart, baseAxis = chart.xAxis[0], actualRange = Math.round(baseAxis.max - baseAxis.min), hasNoData = !baseAxis.hasVisibleSeries, day = 24 * 36e5, // A single day in milliseconds
  7389. unionExtremes = (chart.scroller &&
  7390. chart.scroller.getUnionExtremes()) || baseAxis, dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC), ytdMin = ytdExtremes.min, ytdMax = ytdExtremes.max, selected = rangeSelector.selected, selectedExists = isNumber(selected), allButtonsEnabled = rangeSelector.options.allButtonsEnabled, buttons = rangeSelector.buttons;
  7391. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  7392. var range = rangeOptions._range, type = rangeOptions.type, count = rangeOptions.count || 1, button = buttons[i], state = 0, disable, select, offsetRange = rangeOptions._offsetMax -
  7393. rangeOptions._offsetMin, isSelected = i === selected,
  7394. // Disable buttons where the range exceeds what is allowed in
  7395. // the current view
  7396. isTooGreatRange = range >
  7397. dataMax - dataMin,
  7398. // Disable buttons where the range is smaller than the minimum
  7399. // range
  7400. isTooSmallRange = range < baseAxis.minRange,
  7401. // Do not select the YTD button if not explicitly told so
  7402. isYTDButNotSelected = false,
  7403. // Disable the All button if we're already showing all
  7404. isAllButAlreadyShowingAll = false, isSameRange = range === actualRange;
  7405. // Months and years have a variable range so we check the extremes
  7406. if ((type === 'month' || type === 'year') &&
  7407. (actualRange + 36e5 >=
  7408. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  7409. (actualRange - 36e5 <=
  7410. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  7411. isSameRange = true;
  7412. }
  7413. else if (type === 'ytd') {
  7414. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  7415. isYTDButNotSelected = !isSelected;
  7416. }
  7417. else if (type === 'all') {
  7418. isSameRange = (baseAxis.max - baseAxis.min >=
  7419. dataMax - dataMin);
  7420. isAllButAlreadyShowingAll = (!isSelected &&
  7421. selectedExists &&
  7422. isSameRange);
  7423. }
  7424. // The new zoom area happens to match the range for a button - mark
  7425. // it selected. This happens when scrolling across an ordinal gap.
  7426. // It can be seen in the intraday demos when selecting 1h and scroll
  7427. // across the night gap.
  7428. disable = (!allButtonsEnabled &&
  7429. (isTooGreatRange ||
  7430. isTooSmallRange ||
  7431. isAllButAlreadyShowingAll ||
  7432. hasNoData));
  7433. select = ((isSelected && isSameRange) ||
  7434. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  7435. (isSelected && rangeSelector.frozenStates));
  7436. if (disable) {
  7437. state = 3;
  7438. }
  7439. else if (select) {
  7440. selectedExists = true; // Only one button can be selected
  7441. state = 2;
  7442. }
  7443. // If state has changed, update the button
  7444. if (button.state !== state) {
  7445. button.setState(state);
  7446. // Reset (#9209)
  7447. if (state === 0 && selected === i) {
  7448. rangeSelector.setSelected(null);
  7449. }
  7450. }
  7451. });
  7452. };
  7453. /**
  7454. * Compute and cache the range for an individual button
  7455. *
  7456. * @private
  7457. * @function Highcharts.RangeSelector#computeButtonRange
  7458. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  7459. * @return {void}
  7460. */
  7461. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  7462. var type = rangeOptions.type, count = rangeOptions.count || 1,
  7463. // these time intervals have a fixed number of milliseconds, as
  7464. // opposed to month, ytd and year
  7465. fixedTimes = {
  7466. millisecond: 1,
  7467. second: 1000,
  7468. minute: 60 * 1000,
  7469. hour: 3600 * 1000,
  7470. day: 24 * 3600 * 1000,
  7471. week: 7 * 24 * 3600 * 1000
  7472. };
  7473. // Store the range on the button object
  7474. if (fixedTimes[type]) {
  7475. rangeOptions._range = fixedTimes[type] * count;
  7476. }
  7477. else if (type === 'month' || type === 'year') {
  7478. rangeOptions._range = {
  7479. month: 30,
  7480. year: 365
  7481. }[type] * 24 * 36e5 * count;
  7482. }
  7483. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  7484. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  7485. rangeOptions._range +=
  7486. rangeOptions._offsetMax - rangeOptions._offsetMin;
  7487. };
  7488. /**
  7489. * Set the internal and displayed value of a HTML input for the dates
  7490. *
  7491. * @private
  7492. * @function Highcharts.RangeSelector#setInputValue
  7493. * @param {string} name
  7494. * @param {number} [inputTime]
  7495. * @return {void}
  7496. */
  7497. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  7498. var options = this.chart.options.rangeSelector, time = this.chart.time, input = this[name + 'Input'];
  7499. if (defined(inputTime)) {
  7500. input.previousValue = input.HCTime;
  7501. input.HCTime = inputTime;
  7502. }
  7503. input.value = time.dateFormat(options.inputEditDateFormat || '%Y-%m-%d', input.HCTime);
  7504. this[name + 'DateBox'].attr({
  7505. text: time.dateFormat(options.inputDateFormat || '%b %e, %Y', input.HCTime)
  7506. });
  7507. };
  7508. /**
  7509. * @private
  7510. * @function Highcharts.RangeSelector#showInput
  7511. * @param {string} name
  7512. * @return {void}
  7513. */
  7514. RangeSelector.prototype.showInput = function (name) {
  7515. var inputGroup = this.inputGroup, dateBox = this[name + 'DateBox'];
  7516. css(this[name + 'Input'], {
  7517. left: (inputGroup.translateX + dateBox.x) + 'px',
  7518. top: inputGroup.translateY + 'px',
  7519. width: (dateBox.width - 2) + 'px',
  7520. height: (dateBox.height - 2) + 'px',
  7521. border: '2px solid silver'
  7522. });
  7523. };
  7524. /**
  7525. * @private
  7526. * @function Highcharts.RangeSelector#hideInput
  7527. * @param {string} name
  7528. * @return {void}
  7529. */
  7530. RangeSelector.prototype.hideInput = function (name) {
  7531. css(this[name + 'Input'], {
  7532. border: 0,
  7533. width: '1px',
  7534. height: '1px'
  7535. });
  7536. this.setInputValue(name);
  7537. };
  7538. /**
  7539. * Draw either the 'from' or the 'to' HTML input box of the range selector
  7540. *
  7541. * @private
  7542. * @function Highcharts.RangeSelector#drawInput
  7543. * @param {string} name
  7544. * @return {void}
  7545. */
  7546. RangeSelector.prototype.drawInput = function (name) {
  7547. var rangeSelector = this, chart = rangeSelector.chart, chartStyle = chart.renderer.style || {}, renderer = chart.renderer, options = chart.options.rangeSelector, lang = defaultOptions.lang, div = rangeSelector.div, isMin = name === 'min', input, label, dateBox, inputGroup = this.inputGroup;
  7548. /**
  7549. * @private
  7550. */
  7551. function updateExtremes() {
  7552. var inputValue = input.value, value = (options.inputDateParser || Date.parse)(inputValue), chartAxis = chart.xAxis[0], dataAxis = chart.scroller && chart.scroller.xAxis ?
  7553. chart.scroller.xAxis :
  7554. chartAxis, dataMin = dataAxis.dataMin, dataMax = dataAxis.dataMax;
  7555. if (value !== input.previousValue) {
  7556. input.previousValue = value;
  7557. // If the value isn't parsed directly to a value by the
  7558. // browser's Date.parse method, like YYYY-MM-DD in IE, try
  7559. // parsing it a different way
  7560. if (!isNumber(value)) {
  7561. value = inputValue.split('-');
  7562. value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
  7563. }
  7564. if (isNumber(value)) {
  7565. // Correct for timezone offset (#433)
  7566. if (!chart.time.useUTC) {
  7567. value =
  7568. value + new Date().getTimezoneOffset() * 60 * 1000;
  7569. }
  7570. // Validate the extremes. If it goes beyound the data min or
  7571. // max, use the actual data extreme (#2438).
  7572. if (isMin) {
  7573. if (value > rangeSelector.maxInput.HCTime) {
  7574. value = void 0;
  7575. }
  7576. else if (value < dataMin) {
  7577. value = dataMin;
  7578. }
  7579. }
  7580. else {
  7581. if (value < rangeSelector.minInput.HCTime) {
  7582. value = void 0;
  7583. }
  7584. else if (value > dataMax) {
  7585. value = dataMax;
  7586. }
  7587. }
  7588. // Set the extremes
  7589. if (typeof value !== 'undefined') { // @todo typof undefined
  7590. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  7591. }
  7592. }
  7593. }
  7594. }
  7595. // Create the text label
  7596. this[name + 'Label'] = label = renderer
  7597. .label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset)
  7598. .addClass('highcharts-range-label')
  7599. .attr({
  7600. padding: 2
  7601. })
  7602. .add(inputGroup);
  7603. inputGroup.offset += label.width + 5;
  7604. // Create an SVG label that shows updated date ranges and and records
  7605. // click events that bring in the HTML input.
  7606. this[name + 'DateBox'] = dateBox = renderer
  7607. .label('', inputGroup.offset)
  7608. .addClass('highcharts-range-input')
  7609. .attr({
  7610. padding: 2,
  7611. width: options.inputBoxWidth || 90,
  7612. height: options.inputBoxHeight || 17,
  7613. 'text-align': 'center'
  7614. })
  7615. .on('click', function () {
  7616. // If it is already focused, the onfocus event doesn't fire
  7617. // (#3713)
  7618. rangeSelector.showInput(name);
  7619. rangeSelector[name + 'Input'].focus();
  7620. });
  7621. if (!chart.styledMode) {
  7622. dateBox.attr({
  7623. stroke: options.inputBoxBorderColor || '#cccccc',
  7624. 'stroke-width': 1
  7625. });
  7626. }
  7627. dateBox.add(inputGroup);
  7628. inputGroup.offset += dateBox.width + (isMin ? 10 : 0);
  7629. // Create the HTML input element. This is rendered as 1x1 pixel then set
  7630. // to the right size when focused.
  7631. this[name + 'Input'] = input = createElement('input', {
  7632. name: name,
  7633. className: 'highcharts-range-selector',
  7634. type: 'text'
  7635. }, {
  7636. top: chart.plotTop + 'px' // prevent jump on focus in Firefox
  7637. }, div);
  7638. if (!chart.styledMode) {
  7639. // Styles
  7640. label.css(merge(chartStyle, options.labelStyle));
  7641. dateBox.css(merge({
  7642. color: '#333333'
  7643. }, chartStyle, options.inputStyle));
  7644. css(input, extend({
  7645. position: 'absolute',
  7646. border: 0,
  7647. width: '1px',
  7648. height: '1px',
  7649. padding: 0,
  7650. textAlign: 'center',
  7651. fontSize: chartStyle.fontSize,
  7652. fontFamily: chartStyle.fontFamily,
  7653. top: '-9999em' // #4798
  7654. }, options.inputStyle));
  7655. }
  7656. // Blow up the input box
  7657. input.onfocus = function () {
  7658. rangeSelector.showInput(name);
  7659. };
  7660. // Hide away the input box
  7661. input.onblur = function () {
  7662. // update extermes only when inputs are active
  7663. if (input === H.doc.activeElement) { // Only when focused
  7664. // Update also when no `change` event is triggered, like when
  7665. // clicking inside the SVG (#4710)
  7666. updateExtremes();
  7667. }
  7668. // #10404 - move hide and blur outside focus
  7669. rangeSelector.hideInput(name);
  7670. input.blur(); // #4606
  7671. };
  7672. // handle changes in the input boxes
  7673. input.onchange = updateExtremes;
  7674. input.onkeypress = function (event) {
  7675. // IE does not fire onchange on enter
  7676. if (event.keyCode === 13) {
  7677. updateExtremes();
  7678. }
  7679. };
  7680. };
  7681. /**
  7682. * Get the position of the range selector buttons and inputs. This can be
  7683. * overridden from outside for custom positioning.
  7684. *
  7685. * @private
  7686. * @function Highcharts.RangeSelector#getPosition
  7687. *
  7688. * @return {Highcharts.Dictionary<number>}
  7689. */
  7690. RangeSelector.prototype.getPosition = function () {
  7691. var chart = this.chart, options = chart.options.rangeSelector, top = options.verticalAlign === 'top' ?
  7692. chart.plotTop - chart.axisOffset[0] :
  7693. 0; // set offset only for varticalAlign top
  7694. return {
  7695. buttonTop: top + options.buttonPosition.y,
  7696. inputTop: top + options.inputPosition.y - 10
  7697. };
  7698. };
  7699. /**
  7700. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  7701. * the current timestamp. Will choose dataMin if its value is higher than
  7702. * the timestamp for the start of current year.
  7703. *
  7704. * @private
  7705. * @function Highcharts.RangeSelector#getYTDExtremes
  7706. *
  7707. * @param {number} dataMax
  7708. *
  7709. * @param {number} dataMin
  7710. *
  7711. * @return {*}
  7712. * Returns min and max for the YTD
  7713. */
  7714. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  7715. var time = this.chart.time, min, now = new time.Date(dataMax), year = time.get('FullYear', now), startOfYear = useUTC ?
  7716. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  7717. +new time.Date(year, 0, 1);
  7718. min = Math.max(dataMin || 0, startOfYear);
  7719. now = now.getTime();
  7720. return {
  7721. max: Math.min(dataMax || now, now),
  7722. min: min
  7723. };
  7724. };
  7725. /**
  7726. * Render the range selector including the buttons and the inputs. The first
  7727. * time render is called, the elements are created and positioned. On
  7728. * subsequent calls, they are moved and updated.
  7729. *
  7730. * @private
  7731. * @function Highcharts.RangeSelector#render
  7732. * @param {number} [min]
  7733. * X axis minimum
  7734. * @param {number} [max]
  7735. * X axis maximum
  7736. * @return {void}
  7737. */
  7738. RangeSelector.prototype.render = function (min, max) {
  7739. var rangeSelector = this, chart = rangeSelector.chart, renderer = chart.renderer, container = chart.container, chartOptions = chart.options, navButtonOptions = (chartOptions.exporting &&
  7740. chartOptions.exporting.enabled !== false &&
  7741. chartOptions.navigation &&
  7742. chartOptions.navigation.buttonOptions), lang = defaultOptions.lang, div = rangeSelector.div, options = chartOptions.rangeSelector,
  7743. // Place inputs above the container
  7744. inputsZIndex = pick(chartOptions.chart.style &&
  7745. chartOptions.chart.style.zIndex, 0) + 1, floating = options.floating, buttons = rangeSelector.buttons, inputGroup = rangeSelector.inputGroup, buttonTheme = options.buttonTheme, buttonPosition = options.buttonPosition, inputPosition = options.inputPosition, inputEnabled = options.inputEnabled, states = buttonTheme && buttonTheme.states, plotLeft = chart.plotLeft, buttonLeft, buttonGroup = rangeSelector.buttonGroup, group, groupHeight, rendered = rangeSelector.rendered, verticalAlign = rangeSelector.options.verticalAlign, legend = chart.legend, legendOptions = legend && legend.options, buttonPositionY = buttonPosition.y, inputPositionY = inputPosition.y, animate = chart.hasLoaded, verb = animate ? 'animate' : 'attr', exportingX = 0, alignTranslateY, legendHeight, minPosition, translateY = 0, translateX;
  7746. if (options.enabled === false) {
  7747. return;
  7748. }
  7749. // create the elements
  7750. if (!rendered) {
  7751. rangeSelector.group = group = renderer.g('range-selector-group')
  7752. .attr({
  7753. zIndex: 7
  7754. })
  7755. .add();
  7756. rangeSelector.buttonGroup = buttonGroup =
  7757. renderer.g('range-selector-buttons').add(group);
  7758. rangeSelector.zoomText = renderer
  7759. .text(lang.rangeSelectorZoom, 0, 15)
  7760. .add(buttonGroup);
  7761. if (!chart.styledMode) {
  7762. rangeSelector.zoomText.css(options.labelStyle);
  7763. buttonTheme['stroke-width'] =
  7764. pick(buttonTheme['stroke-width'], 0);
  7765. }
  7766. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  7767. buttons[i] = renderer
  7768. .button(rangeOptions.text, 0, 0, function (e) {
  7769. // extract events from button object and call
  7770. var buttonEvents = (rangeOptions.events &&
  7771. rangeOptions.events.click), callDefaultEvent;
  7772. if (buttonEvents) {
  7773. callDefaultEvent =
  7774. buttonEvents.call(rangeOptions, e);
  7775. }
  7776. if (callDefaultEvent !== false) {
  7777. rangeSelector.clickButton(i);
  7778. }
  7779. rangeSelector.isActive = true;
  7780. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  7781. .attr({
  7782. 'text-align': 'center'
  7783. })
  7784. .add(buttonGroup);
  7785. });
  7786. // first create a wrapper outside the container in order to make
  7787. // the inputs work and make export correct
  7788. if (inputEnabled !== false) {
  7789. rangeSelector.div = div = createElement('div', null, {
  7790. position: 'relative',
  7791. height: 0,
  7792. zIndex: inputsZIndex
  7793. });
  7794. container.parentNode.insertBefore(div, container);
  7795. // Create the group to keep the inputs
  7796. rangeSelector.inputGroup = inputGroup =
  7797. renderer.g('input-group').add(group);
  7798. inputGroup.offset = 0;
  7799. rangeSelector.drawInput('min');
  7800. rangeSelector.drawInput('max');
  7801. }
  7802. }
  7803. // #8769, allow dynamically updating margins
  7804. rangeSelector.zoomText[verb]({
  7805. x: pick(plotLeft + buttonPosition.x, plotLeft)
  7806. });
  7807. // button start position
  7808. buttonLeft = pick(plotLeft + buttonPosition.x, plotLeft) +
  7809. rangeSelector.zoomText.getBBox().width + 5;
  7810. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  7811. buttons[i][verb]({ x: buttonLeft });
  7812. // increase button position for the next button
  7813. buttonLeft += buttons[i].width + pick(options.buttonSpacing, 5);
  7814. });
  7815. plotLeft = chart.plotLeft - chart.spacing[3];
  7816. rangeSelector.updateButtonStates();
  7817. // detect collisiton with exporting
  7818. if (navButtonOptions &&
  7819. this.titleCollision(chart) &&
  7820. verticalAlign === 'top' &&
  7821. buttonPosition.align === 'right' && ((buttonPosition.y +
  7822. buttonGroup.getBBox().height - 12) <
  7823. ((navButtonOptions.y || 0) +
  7824. navButtonOptions.height))) {
  7825. exportingX = -40;
  7826. }
  7827. translateX = buttonPosition.x - chart.spacing[3];
  7828. if (buttonPosition.align === 'right') {
  7829. translateX += exportingX - plotLeft; // (#13014)
  7830. }
  7831. else if (buttonPosition.align === 'center') {
  7832. translateX -= plotLeft / 2;
  7833. }
  7834. // align button group
  7835. buttonGroup.align({
  7836. y: buttonPosition.y,
  7837. width: buttonGroup.getBBox().width,
  7838. align: buttonPosition.align,
  7839. x: translateX
  7840. }, true, chart.spacingBox);
  7841. // skip animation
  7842. rangeSelector.group.placed = animate;
  7843. rangeSelector.buttonGroup.placed = animate;
  7844. if (inputEnabled !== false) {
  7845. var inputGroupX, inputGroupWidth, buttonGroupX, buttonGroupWidth;
  7846. // detect collision with exporting
  7847. if (navButtonOptions &&
  7848. this.titleCollision(chart) &&
  7849. verticalAlign === 'top' &&
  7850. inputPosition.align === 'right' && ((inputPosition.y -
  7851. inputGroup.getBBox().height - 12) <
  7852. ((navButtonOptions.y || 0) +
  7853. navButtonOptions.height +
  7854. chart.spacing[0]))) {
  7855. exportingX = -40;
  7856. }
  7857. else {
  7858. exportingX = 0;
  7859. }
  7860. if (inputPosition.align === 'left') {
  7861. translateX = plotLeft;
  7862. }
  7863. else if (inputPosition.align === 'right') {
  7864. translateX = -Math.max(chart.axisOffset[1], -exportingX);
  7865. }
  7866. // Update the alignment to the updated spacing box
  7867. inputGroup.align({
  7868. y: inputPosition.y,
  7869. width: inputGroup.getBBox().width,
  7870. align: inputPosition.align,
  7871. // fix wrong getBBox() value on right align
  7872. x: inputPosition.x + translateX - 2
  7873. }, true, chart.spacingBox);
  7874. // detect collision
  7875. inputGroupX = (inputGroup.alignAttr.translateX +
  7876. inputGroup.alignOptions.x -
  7877. exportingX +
  7878. // getBBox for detecing left margin
  7879. inputGroup.getBBox().x +
  7880. // 2px padding to not overlap input and label
  7881. 2);
  7882. inputGroupWidth = inputGroup.alignOptions.width;
  7883. buttonGroupX = buttonGroup.alignAttr.translateX +
  7884. buttonGroup.getBBox().x;
  7885. // 20 is minimal spacing between elements
  7886. buttonGroupWidth = buttonGroup.getBBox().width + 20;
  7887. if ((inputPosition.align ===
  7888. buttonPosition.align) || ((buttonGroupX + buttonGroupWidth > inputGroupX) &&
  7889. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  7890. (buttonPositionY <
  7891. (inputPositionY +
  7892. inputGroup.getBBox().height)))) {
  7893. inputGroup.attr({
  7894. translateX: inputGroup.alignAttr.translateX +
  7895. (chart.axisOffset[1] >= -exportingX ? 0 : -exportingX),
  7896. translateY: inputGroup.alignAttr.translateY +
  7897. buttonGroup.getBBox().height + 10
  7898. });
  7899. }
  7900. // Set or reset the input values
  7901. rangeSelector.setInputValue('min', min);
  7902. rangeSelector.setInputValue('max', max);
  7903. // skip animation
  7904. rangeSelector.inputGroup.placed = animate;
  7905. }
  7906. // vertical align
  7907. rangeSelector.group.align({
  7908. verticalAlign: verticalAlign
  7909. }, true, chart.spacingBox);
  7910. // set position
  7911. groupHeight =
  7912. rangeSelector.group.getBBox().height + 20; // # 20 padding
  7913. alignTranslateY =
  7914. rangeSelector.group.alignAttr.translateY;
  7915. // calculate bottom position
  7916. if (verticalAlign === 'bottom') {
  7917. legendHeight = (legendOptions &&
  7918. legendOptions.verticalAlign === 'bottom' &&
  7919. legendOptions.enabled &&
  7920. !legendOptions.floating ?
  7921. legend.legendHeight + pick(legendOptions.margin, 10) :
  7922. 0);
  7923. groupHeight = groupHeight + legendHeight - 20;
  7924. translateY = (alignTranslateY -
  7925. groupHeight -
  7926. (floating ? 0 : options.y) -
  7927. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  7928. 10 // 10 spacing
  7929. );
  7930. }
  7931. if (verticalAlign === 'top') {
  7932. if (floating) {
  7933. translateY = 0;
  7934. }
  7935. if (chart.titleOffset && chart.titleOffset[0]) {
  7936. translateY = chart.titleOffset[0];
  7937. }
  7938. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  7939. }
  7940. else if (verticalAlign === 'middle') {
  7941. if (inputPositionY === buttonPositionY) {
  7942. if (inputPositionY < 0) {
  7943. translateY = alignTranslateY + minPosition;
  7944. }
  7945. else {
  7946. translateY = alignTranslateY;
  7947. }
  7948. }
  7949. else if (inputPositionY || buttonPositionY) {
  7950. if (inputPositionY < 0 ||
  7951. buttonPositionY < 0) {
  7952. translateY -= Math.min(inputPositionY, buttonPositionY);
  7953. }
  7954. else {
  7955. translateY =
  7956. alignTranslateY - groupHeight + minPosition;
  7957. }
  7958. }
  7959. }
  7960. rangeSelector.group.translate(options.x, options.y + Math.floor(translateY));
  7961. // translate HTML inputs
  7962. if (inputEnabled !== false) {
  7963. rangeSelector.minInput.style.marginTop =
  7964. rangeSelector.group.translateY + 'px';
  7965. rangeSelector.maxInput.style.marginTop =
  7966. rangeSelector.group.translateY + 'px';
  7967. }
  7968. rangeSelector.rendered = true;
  7969. };
  7970. /**
  7971. * Extracts height of range selector
  7972. *
  7973. * @private
  7974. * @function Highcharts.RangeSelector#getHeight
  7975. * @return {number}
  7976. * Returns rangeSelector height
  7977. */
  7978. RangeSelector.prototype.getHeight = function () {
  7979. var rangeSelector = this, options = rangeSelector.options, rangeSelectorGroup = rangeSelector.group, inputPosition = options.inputPosition, buttonPosition = options.buttonPosition, yPosition = options.y, buttonPositionY = buttonPosition.y, inputPositionY = inputPosition.y, rangeSelectorHeight = 0, minPosition;
  7980. if (options.height) {
  7981. return options.height;
  7982. }
  7983. rangeSelectorHeight = rangeSelectorGroup ?
  7984. // 13px to keep back compatibility
  7985. (rangeSelectorGroup.getBBox(true).height) + 13 +
  7986. yPosition :
  7987. 0;
  7988. minPosition = Math.min(inputPositionY, buttonPositionY);
  7989. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  7990. (inputPositionY > 0 && buttonPositionY > 0)) {
  7991. rangeSelectorHeight += Math.abs(minPosition);
  7992. }
  7993. return rangeSelectorHeight;
  7994. };
  7995. /**
  7996. * Detect collision with title or subtitle
  7997. *
  7998. * @private
  7999. * @function Highcharts.RangeSelector#titleCollision
  8000. *
  8001. * @param {Highcharts.Chart} chart
  8002. *
  8003. * @return {boolean}
  8004. * Returns collision status
  8005. */
  8006. RangeSelector.prototype.titleCollision = function (chart) {
  8007. return !(chart.options.title.text ||
  8008. chart.options.subtitle.text);
  8009. };
  8010. /**
  8011. * Update the range selector with new options
  8012. *
  8013. * @private
  8014. * @function Highcharts.RangeSelector#update
  8015. * @param {Highcharts.RangeSelectorOptions} options
  8016. * @return {void}
  8017. */
  8018. RangeSelector.prototype.update = function (options) {
  8019. var chart = this.chart;
  8020. merge(true, chart.options.rangeSelector, options);
  8021. this.destroy();
  8022. this.init(chart);
  8023. chart.rangeSelector.render();
  8024. };
  8025. /**
  8026. * Destroys allocated elements.
  8027. *
  8028. * @private
  8029. * @function Highcharts.RangeSelector#destroy
  8030. */
  8031. RangeSelector.prototype.destroy = function () {
  8032. var rSelector = this, minInput = rSelector.minInput, maxInput = rSelector.maxInput;
  8033. rSelector.unMouseDown();
  8034. rSelector.unResize();
  8035. // Destroy elements in collections
  8036. destroyObjectProperties(rSelector.buttons);
  8037. // Clear input element events
  8038. if (minInput) {
  8039. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  8040. }
  8041. if (maxInput) {
  8042. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  8043. }
  8044. // Destroy HTML and SVG elements
  8045. objectEach(rSelector, function (val, key) {
  8046. if (val && key !== 'chart') {
  8047. if (val instanceof SVGElement) {
  8048. // SVGElement
  8049. val.destroy();
  8050. }
  8051. else if (val instanceof window.HTMLElement) {
  8052. // HTML element
  8053. discardElement(val);
  8054. }
  8055. }
  8056. if (val !== RangeSelector.prototype[key]) {
  8057. rSelector[key] = null;
  8058. }
  8059. }, this);
  8060. };
  8061. return RangeSelector;
  8062. }());
  8063. /**
  8064. * The default buttons for pre-selecting time frames
  8065. */
  8066. RangeSelector.prototype.defaultButtons = [{
  8067. type: 'month',
  8068. count: 1,
  8069. text: '1m'
  8070. }, {
  8071. type: 'month',
  8072. count: 3,
  8073. text: '3m'
  8074. }, {
  8075. type: 'month',
  8076. count: 6,
  8077. text: '6m'
  8078. }, {
  8079. type: 'ytd',
  8080. text: 'YTD'
  8081. }, {
  8082. type: 'year',
  8083. count: 1,
  8084. text: '1y'
  8085. }, {
  8086. type: 'all',
  8087. text: 'All'
  8088. }];
  8089. /**
  8090. * Get the axis min value based on the range option and the current max. For
  8091. * stock charts this is extended via the {@link RangeSelector} so that if the
  8092. * selected range is a multiple of months or years, it is compensated for
  8093. * various month lengths.
  8094. *
  8095. * @private
  8096. * @function Highcharts.Axis#minFromRange
  8097. * @return {number|undefined}
  8098. * The new minimum value.
  8099. */
  8100. Axis.prototype.minFromRange = function () {
  8101. var rangeOptions = this.range, type = rangeOptions.type, min, max = this.max, dataMin, range, time = this.chart.time,
  8102. // Get the true range from a start date
  8103. getTrueRange = function (base, count) {
  8104. var timeName = type === 'year' ? 'FullYear' : 'Month';
  8105. var date = new time.Date(base);
  8106. var basePeriod = time.get(timeName, date);
  8107. time.set(timeName, date, basePeriod + count);
  8108. if (basePeriod === time.get(timeName, date)) {
  8109. time.set('Date', date, 0); // #6537
  8110. }
  8111. return date.getTime() - base;
  8112. };
  8113. if (isNumber(rangeOptions)) {
  8114. min = max - rangeOptions;
  8115. range = rangeOptions;
  8116. }
  8117. else {
  8118. min = max + getTrueRange(max, -rangeOptions.count);
  8119. // Let the fixedRange reflect initial settings (#5930)
  8120. if (this.chart) {
  8121. this.chart.fixedRange = max - min;
  8122. }
  8123. }
  8124. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  8125. if (!isNumber(min)) {
  8126. min = dataMin;
  8127. }
  8128. if (min <= dataMin) {
  8129. min = dataMin;
  8130. if (typeof range === 'undefined') { // #4501
  8131. range = getTrueRange(min, rangeOptions.count);
  8132. }
  8133. this.newMax = Math.min(min + range, this.dataMax);
  8134. }
  8135. if (!isNumber(max)) {
  8136. min = void 0;
  8137. }
  8138. return min;
  8139. };
  8140. if (!H.RangeSelector) {
  8141. // Initialize rangeselector for stock charts
  8142. addEvent(Chart, 'afterGetContainer', function () {
  8143. if (this.options.rangeSelector.enabled) {
  8144. this.rangeSelector = new RangeSelector(this);
  8145. }
  8146. });
  8147. addEvent(Chart, 'beforeRender', function () {
  8148. var chart = this, axes = chart.axes, rangeSelector = chart.rangeSelector, verticalAlign;
  8149. if (rangeSelector) {
  8150. if (isNumber(rangeSelector.deferredYTDClick)) {
  8151. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  8152. delete rangeSelector.deferredYTDClick;
  8153. }
  8154. axes.forEach(function (axis) {
  8155. axis.updateNames();
  8156. axis.setScale();
  8157. });
  8158. chart.getAxisMargins();
  8159. rangeSelector.render();
  8160. verticalAlign = rangeSelector.options.verticalAlign;
  8161. if (!rangeSelector.options.floating) {
  8162. if (verticalAlign === 'bottom') {
  8163. this.extraBottomMargin = true;
  8164. }
  8165. else if (verticalAlign !== 'middle') {
  8166. this.extraTopMargin = true;
  8167. }
  8168. }
  8169. }
  8170. });
  8171. addEvent(Chart, 'update', function (e) {
  8172. var chart = this, options = e.options, optionsRangeSelector = options.rangeSelector, rangeSelector = chart.rangeSelector, verticalAlign, extraBottomMarginWas = this.extraBottomMargin, extraTopMarginWas = this.extraTopMargin;
  8173. if (optionsRangeSelector &&
  8174. optionsRangeSelector.enabled &&
  8175. !defined(rangeSelector)) {
  8176. this.options.rangeSelector.enabled = true;
  8177. this.rangeSelector = new RangeSelector(this);
  8178. }
  8179. this.extraBottomMargin = false;
  8180. this.extraTopMargin = false;
  8181. if (rangeSelector) {
  8182. rangeSelector.render();
  8183. verticalAlign = (optionsRangeSelector &&
  8184. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  8185. if (!rangeSelector.options.floating) {
  8186. if (verticalAlign === 'bottom') {
  8187. this.extraBottomMargin = true;
  8188. }
  8189. else if (verticalAlign !== 'middle') {
  8190. this.extraTopMargin = true;
  8191. }
  8192. }
  8193. if (this.extraBottomMargin !== extraBottomMarginWas ||
  8194. this.extraTopMargin !== extraTopMarginWas) {
  8195. this.isDirtyBox = true;
  8196. }
  8197. }
  8198. });
  8199. addEvent(Chart, 'render', function () {
  8200. var chart = this, rangeSelector = chart.rangeSelector, verticalAlign;
  8201. if (rangeSelector && !rangeSelector.options.floating) {
  8202. rangeSelector.render();
  8203. verticalAlign = rangeSelector.options.verticalAlign;
  8204. if (verticalAlign === 'bottom') {
  8205. this.extraBottomMargin = true;
  8206. }
  8207. else if (verticalAlign !== 'middle') {
  8208. this.extraTopMargin = true;
  8209. }
  8210. }
  8211. });
  8212. addEvent(Chart, 'getMargins', function () {
  8213. var rangeSelector = this.rangeSelector, rangeSelectorHeight;
  8214. if (rangeSelector) {
  8215. rangeSelectorHeight = rangeSelector.getHeight();
  8216. if (this.extraTopMargin) {
  8217. this.plotTop += rangeSelectorHeight;
  8218. }
  8219. if (this.extraBottomMargin) {
  8220. this.marginBottom += rangeSelectorHeight;
  8221. }
  8222. }
  8223. });
  8224. Chart.prototype.callbacks.push(function (chart) {
  8225. var extremes, rangeSelector = chart.rangeSelector, unbindRender, unbindSetExtremes, legend, alignTo, verticalAlign;
  8226. /**
  8227. * @private
  8228. */
  8229. function renderRangeSelector() {
  8230. extremes = chart.xAxis[0].getExtremes();
  8231. legend = chart.legend;
  8232. verticalAlign = rangeSelector === null || rangeSelector === void 0 ? void 0 : rangeSelector.options.verticalAlign;
  8233. if (isNumber(extremes.min)) {
  8234. rangeSelector.render(extremes.min, extremes.max);
  8235. }
  8236. // Re-align the legend so that it's below the rangeselector
  8237. if (rangeSelector && legend.display &&
  8238. verticalAlign === 'top' &&
  8239. verticalAlign === legend.options.verticalAlign) {
  8240. // Create a new alignment box for the legend.
  8241. alignTo = merge(chart.spacingBox);
  8242. if (legend.options.layout === 'vertical') {
  8243. alignTo.y = chart.plotTop;
  8244. }
  8245. else {
  8246. alignTo.y += rangeSelector.getHeight();
  8247. }
  8248. legend.group.placed = false; // Don't animate the alignment.
  8249. legend.align(alignTo);
  8250. }
  8251. }
  8252. if (rangeSelector) {
  8253. // redraw the scroller on setExtremes
  8254. unbindSetExtremes = addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  8255. rangeSelector.render(e.min, e.max);
  8256. });
  8257. // redraw the scroller chart resize
  8258. unbindRender = addEvent(chart, 'redraw', renderRangeSelector);
  8259. // do it now
  8260. renderRangeSelector();
  8261. }
  8262. // Remove resize/afterSetExtremes at chart destroy
  8263. addEvent(chart, 'destroy', function destroyEvents() {
  8264. if (rangeSelector) {
  8265. unbindRender();
  8266. unbindSetExtremes();
  8267. }
  8268. });
  8269. });
  8270. H.RangeSelector = RangeSelector;
  8271. }
  8272. return H.RangeSelector;
  8273. });
  8274. _registerModule(_modules, 'parts/StockChart.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Point.js'], _modules['parts/SVGRenderer.js'], _modules['parts/Utilities.js']], function (Axis, Chart, H, Point, SVGRenderer, U) {
  8275. /* *
  8276. *
  8277. * (c) 2010-2020 Torstein Honsi
  8278. *
  8279. * License: www.highcharts.com/license
  8280. *
  8281. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8282. *
  8283. * */
  8284. var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, defined = U.defined, extend = U.extend, find = U.find, format = U.format, getOptions = U.getOptions, isNumber = U.isNumber, isString = U.isString, merge = U.merge, pick = U.pick, splat = U.splat;
  8285. // Has a dependency on Navigator due to the use of
  8286. // defaultOptions.navigator
  8287. // Has a dependency on Scrollbar due to the use of
  8288. // defaultOptions.scrollbar
  8289. // Has a dependency on RangeSelector due to the use of
  8290. // defaultOptions.rangeSelector
  8291. var Series = H.Series, seriesProto = Series.prototype, seriesInit = seriesProto.init, seriesProcessData = seriesProto.processData, pointTooltipFormatter = Point.prototype.tooltipFormatter;
  8292. /**
  8293. * Compare the values of the series against the first non-null, non-
  8294. * zero value in the visible range. The y axis will show percentage
  8295. * or absolute change depending on whether `compare` is set to `"percent"`
  8296. * or `"value"`. When this is applied to multiple series, it allows
  8297. * comparing the development of the series against each other. Adds
  8298. * a `change` field to every point object.
  8299. *
  8300. * @see [compareBase](#plotOptions.series.compareBase)
  8301. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  8302. *
  8303. * @sample {highstock} stock/plotoptions/series-compare-percent/
  8304. * Percent
  8305. * @sample {highstock} stock/plotoptions/series-compare-value/
  8306. * Value
  8307. *
  8308. * @type {string}
  8309. * @since 1.0.1
  8310. * @product highstock
  8311. * @apioption plotOptions.series.compare
  8312. */
  8313. /**
  8314. * Defines if comparison should start from the first point within the visible
  8315. * range or should start from the first point **before** the range.
  8316. *
  8317. * In other words, this flag determines if first point within the visible range
  8318. * will have 0% (`compareStart=true`) or should have been already calculated
  8319. * according to the previous point (`compareStart=false`).
  8320. *
  8321. * @sample {highstock} stock/plotoptions/series-comparestart/
  8322. * Calculate compare within visible range
  8323. *
  8324. * @type {boolean}
  8325. * @default false
  8326. * @since 6.0.0
  8327. * @product highstock
  8328. * @apioption plotOptions.series.compareStart
  8329. */
  8330. /**
  8331. * When [compare](#plotOptions.series.compare) is `percent`, this option
  8332. * dictates whether to use 0 or 100 as the base of comparison.
  8333. *
  8334. * @sample {highstock} stock/plotoptions/series-comparebase/
  8335. * Compare base is 100
  8336. *
  8337. * @type {number}
  8338. * @default 0
  8339. * @since 5.0.6
  8340. * @product highstock
  8341. * @validvalue [0, 100]
  8342. * @apioption plotOptions.series.compareBase
  8343. */
  8344. /* eslint-disable no-invalid-this, valid-jsdoc */
  8345. /**
  8346. * Factory function for creating new stock charts. Creates a new
  8347. * {@link Highcharts.Chart|Chart} object with different default options than the
  8348. * basic Chart.
  8349. *
  8350. * @example
  8351. * var chart = Highcharts.stockChart('container', {
  8352. * series: [{
  8353. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  8354. * pointInterval: 24 * 60 * 60 * 1000
  8355. * }]
  8356. * });
  8357. *
  8358. * @function Highcharts.stockChart
  8359. *
  8360. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  8361. * The DOM element to render to, or its id.
  8362. *
  8363. * @param {Highcharts.Options} options
  8364. * The chart options structure as described in the
  8365. * [options reference](https://api.highcharts.com/highstock).
  8366. *
  8367. * @param {Highcharts.ChartCallbackFunction} [callback]
  8368. * A function to execute when the chart object is finished loading and
  8369. * rendering. In most cases the chart is built in one thread, but in
  8370. * Internet Explorer version 8 or less the chart is sometimes
  8371. * initialized before the document is ready, and in these cases the
  8372. * chart object will not be finished synchronously. As a consequence,
  8373. * code that relies on the newly built Chart object should always run in
  8374. * the callback. Defining a
  8375. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  8376. * handler is equivalent.
  8377. *
  8378. * @return {Highcharts.Chart}
  8379. * The chart object.
  8380. */
  8381. H.StockChart = H.stockChart = function (a, b, c) {
  8382. var hasRenderToArg = isString(a) || a.nodeName, options = arguments[hasRenderToArg ? 1 : 0], userOptions = options,
  8383. // to increase performance, don't merge the data
  8384. seriesOptions = options.series, defaultOptions = getOptions(), opposite,
  8385. // Always disable startOnTick:true on the main axis when the navigator
  8386. // is enabled (#1090)
  8387. navigatorEnabled = pick(options.navigator && options.navigator.enabled, defaultOptions.navigator.enabled, true);
  8388. // apply X axis options to both single and multi y axes
  8389. options.xAxis = splat(options.xAxis || {}).map(function (xAxisOptions, i) {
  8390. return merge({
  8391. minPadding: 0,
  8392. maxPadding: 0,
  8393. overscroll: 0,
  8394. ordinal: true,
  8395. title: {
  8396. text: null
  8397. },
  8398. labels: {
  8399. overflow: 'justify'
  8400. },
  8401. showLastLabel: true
  8402. }, defaultOptions.xAxis, // #3802
  8403. defaultOptions.xAxis && defaultOptions.xAxis[i], // #7690
  8404. xAxisOptions, // user options
  8405. {
  8406. type: 'datetime',
  8407. categories: null
  8408. }, (navigatorEnabled ? {
  8409. startOnTick: false,
  8410. endOnTick: false
  8411. } : null));
  8412. });
  8413. // apply Y axis options to both single and multi y axes
  8414. options.yAxis = splat(options.yAxis || {}).map(function (yAxisOptions, i) {
  8415. opposite = pick(yAxisOptions.opposite, true);
  8416. return merge({
  8417. labels: {
  8418. y: -2
  8419. },
  8420. opposite: opposite,
  8421. /**
  8422. * @default {highcharts} true
  8423. * @default {highstock} false
  8424. * @apioption yAxis.showLastLabel
  8425. *
  8426. * @private
  8427. */
  8428. showLastLabel: !!(
  8429. // #6104, show last label by default for category axes
  8430. yAxisOptions.categories ||
  8431. yAxisOptions.type === 'category'),
  8432. title: {
  8433. text: null
  8434. }
  8435. }, defaultOptions.yAxis, // #3802
  8436. defaultOptions.yAxis && defaultOptions.yAxis[i], // #7690
  8437. yAxisOptions // user options
  8438. );
  8439. });
  8440. options.series = null;
  8441. options = merge({
  8442. chart: {
  8443. panning: {
  8444. enabled: true,
  8445. type: 'x'
  8446. },
  8447. pinchType: 'x'
  8448. },
  8449. navigator: {
  8450. enabled: navigatorEnabled
  8451. },
  8452. scrollbar: {
  8453. // #4988 - check if setOptions was called
  8454. enabled: pick(defaultOptions.scrollbar.enabled, true)
  8455. },
  8456. rangeSelector: {
  8457. // #4988 - check if setOptions was called
  8458. enabled: pick(defaultOptions.rangeSelector.enabled, true)
  8459. },
  8460. title: {
  8461. text: null
  8462. },
  8463. tooltip: {
  8464. split: pick(defaultOptions.tooltip.split, true),
  8465. crosshairs: true
  8466. },
  8467. legend: {
  8468. enabled: false
  8469. }
  8470. }, options, // user's options
  8471. {
  8472. isStock: true // internal flag
  8473. });
  8474. options.series = userOptions.series = seriesOptions;
  8475. return hasRenderToArg ?
  8476. new Chart(a, options, c) :
  8477. new Chart(options, b);
  8478. };
  8479. // Handle som Stock-specific series defaults, override the plotOptions before
  8480. // series options are handled.
  8481. addEvent(Series, 'setOptions', function (e) {
  8482. var overrides;
  8483. if (this.chart.options.isStock) {
  8484. if (this.is('column') || this.is('columnrange')) {
  8485. overrides = {
  8486. borderWidth: 0,
  8487. shadow: false
  8488. };
  8489. }
  8490. else if (!this.is('scatter') && !this.is('sma')) {
  8491. overrides = {
  8492. marker: {
  8493. enabled: false,
  8494. radius: 2
  8495. }
  8496. };
  8497. }
  8498. if (overrides) {
  8499. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  8500. }
  8501. }
  8502. });
  8503. // Override the automatic label alignment so that the first Y axis' labels
  8504. // are drawn on top of the grid line, and subsequent axes are drawn outside
  8505. addEvent(Axis, 'autoLabelAlign', function (e) {
  8506. var chart = this.chart, options = this.options, panes = chart._labelPanes = chart._labelPanes || {}, key, labelOptions = this.options.labels;
  8507. if (this.chart.options.isStock && this.coll === 'yAxis') {
  8508. key = options.top + ',' + options.height;
  8509. // do it only for the first Y axis of each pane
  8510. if (!panes[key] && labelOptions.enabled) {
  8511. if (labelOptions.x === 15) { // default
  8512. labelOptions.x = 0;
  8513. }
  8514. if (typeof labelOptions.align === 'undefined') {
  8515. labelOptions.align = 'right';
  8516. }
  8517. panes[key] = this;
  8518. e.align = 'right';
  8519. e.preventDefault();
  8520. }
  8521. }
  8522. });
  8523. // Clear axis from label panes (#6071)
  8524. addEvent(Axis, 'destroy', function () {
  8525. var chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  8526. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  8527. delete chart._labelPanes[key];
  8528. }
  8529. });
  8530. // Override getPlotLinePath to allow for multipane charts
  8531. addEvent(Axis, 'getPlotLinePath', function (e) {
  8532. var axis = this, series = (this.isLinked && !this.series ?
  8533. this.linkedParent.series :
  8534. this.series), chart = axis.chart, renderer = chart.renderer, axisLeft = axis.left, axisTop = axis.top, x1, y1, x2, y2, result = [], axes = [], // #3416 need a default array
  8535. axes2, uniqueAxes, translatedValue = e.translatedValue, value = e.value, force = e.force, transVal;
  8536. /**
  8537. * Return the other axis based on either the axis option or on related
  8538. * series.
  8539. * @private
  8540. */
  8541. function getAxis(coll) {
  8542. var otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis', opt = axis.options[otherColl];
  8543. // Other axis indexed by number
  8544. if (isNumber(opt)) {
  8545. return [chart[otherColl][opt]];
  8546. }
  8547. // Other axis indexed by id (like navigator)
  8548. if (isString(opt)) {
  8549. return [chart.get(opt)];
  8550. }
  8551. // Auto detect based on existing series
  8552. return series.map(function (s) {
  8553. return s[otherColl];
  8554. });
  8555. }
  8556. if ( // For stock chart, by default render paths across the panes
  8557. // except the case when `acrossPanes` is disabled by user (#6644)
  8558. (chart.options.isStock && e.acrossPanes !== false) &&
  8559. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  8560. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  8561. e.preventDefault();
  8562. // Get the related axes based on series
  8563. axes = getAxis(axis.coll);
  8564. // Get the related axes based options.*Axis setting #2810
  8565. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  8566. axes2.forEach(function (A) {
  8567. if (defined(A.options.id) ?
  8568. A.options.id.indexOf('navigator') === -1 :
  8569. true) {
  8570. var a = (A.isXAxis ? 'yAxis' : 'xAxis'), rax = (defined(A.options[a]) ?
  8571. chart[a][A.options[a]] :
  8572. chart[a][0]);
  8573. if (axis === rax) {
  8574. axes.push(A);
  8575. }
  8576. }
  8577. });
  8578. // Remove duplicates in the axes array. If there are no axes in the axes
  8579. // array, we are adding an axis without data, so we need to populate
  8580. // this with grid lines (#2796).
  8581. uniqueAxes = axes.length ?
  8582. [] :
  8583. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  8584. axes.forEach(function (axis2) {
  8585. if (uniqueAxes.indexOf(axis2) === -1 &&
  8586. // Do not draw on axis which overlap completely. #5424
  8587. !find(uniqueAxes, function (unique) {
  8588. return unique.pos === axis2.pos && unique.len === axis2.len;
  8589. })) {
  8590. uniqueAxes.push(axis2);
  8591. }
  8592. });
  8593. transVal = pick(translatedValue, axis.translate(value, null, null, e.old));
  8594. if (isNumber(transVal)) {
  8595. if (axis.horiz) {
  8596. uniqueAxes.forEach(function (axis2) {
  8597. var skip;
  8598. y1 = axis2.pos;
  8599. y2 = y1 + axis2.len;
  8600. x1 = x2 = Math.round(transVal + axis.transB);
  8601. // outside plot area
  8602. if (force !== 'pass' &&
  8603. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  8604. if (force) {
  8605. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  8606. }
  8607. else {
  8608. skip = true;
  8609. }
  8610. }
  8611. if (!skip) {
  8612. result.push(['M', x1, y1], ['L', x2, y2]);
  8613. }
  8614. });
  8615. }
  8616. else {
  8617. uniqueAxes.forEach(function (axis2) {
  8618. var skip;
  8619. x1 = axis2.pos;
  8620. x2 = x1 + axis2.len;
  8621. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  8622. // outside plot area
  8623. if (force !== 'pass' &&
  8624. (y1 < axisTop || y1 > axisTop + axis.height)) {
  8625. if (force) {
  8626. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  8627. }
  8628. else {
  8629. skip = true;
  8630. }
  8631. }
  8632. if (!skip) {
  8633. result.push(['M', x1, y1], ['L', x2, y2]);
  8634. }
  8635. });
  8636. }
  8637. }
  8638. e.path = result.length > 0 ?
  8639. renderer.crispPolyLine(result, e.lineWidth || 1) :
  8640. // #3557 getPlotLinePath in regular Highcharts also returns null
  8641. null;
  8642. }
  8643. });
  8644. /**
  8645. * Function to crisp a line with multiple segments
  8646. *
  8647. * @private
  8648. * @function Highcharts.SVGRenderer#crispPolyLine
  8649. * @param {Highcharts.SVGPathArray} points
  8650. * @param {number} width
  8651. * @return {Highcharts.SVGPathArray}
  8652. */
  8653. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  8654. // points format: [['M', 0, 0], ['L', 100, 0]]
  8655. // normalize to a crisp line
  8656. for (var i = 0; i < points.length; i = i + 2) {
  8657. var start = points[i], end = points[i + 1];
  8658. if (start[1] === end[1]) {
  8659. // Substract due to #1129. Now bottom and left axis gridlines behave
  8660. // the same.
  8661. start[1] = end[1] =
  8662. Math.round(start[1]) - (width % 2 / 2);
  8663. }
  8664. if (start[2] === end[2]) {
  8665. start[2] = end[2] =
  8666. Math.round(start[2]) + (width % 2 / 2);
  8667. }
  8668. }
  8669. return points;
  8670. };
  8671. // Wrapper to hide the label
  8672. addEvent(Axis, 'afterHideCrosshair', function () {
  8673. if (this.crossLabel) {
  8674. this.crossLabel = this.crossLabel.hide();
  8675. }
  8676. });
  8677. // Extend crosshairs to also draw the label
  8678. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  8679. // Check if the label has to be drawn
  8680. if (!defined(this.crosshair.label) ||
  8681. !this.crosshair.label.enabled ||
  8682. !this.cross) {
  8683. return;
  8684. }
  8685. var chart = this.chart, log = this.logarithmic, options = this.options.crosshair.label, // the label's options
  8686. horiz = this.horiz, // axis orientation
  8687. opposite = this.opposite, // axis position
  8688. left = this.left, // left position
  8689. top = this.top, // top position
  8690. crossLabel = this.crossLabel, // the svgElement
  8691. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, value, offset = 0,
  8692. // Use last available event (#5287)
  8693. e = event.e || (this.cross && this.cross.e), point = event.point, min = this.min, max = this.max;
  8694. if (log) {
  8695. min = log.lin2log(min);
  8696. max = log.lin2log(max);
  8697. }
  8698. align = (horiz ? 'center' : opposite ?
  8699. (this.labelAlign === 'right' ? 'right' : 'left') :
  8700. (this.labelAlign === 'left' ? 'left' : 'center'));
  8701. // If the label does not exist yet, create it.
  8702. if (!crossLabel) {
  8703. crossLabel = this.crossLabel = chart.renderer
  8704. .label(null, null, null, options.shape || 'callout')
  8705. .addClass('highcharts-crosshair-label' + (this.series[0] &&
  8706. ' highcharts-color-' + this.series[0].colorIndex))
  8707. .attr({
  8708. align: options.align || align,
  8709. padding: pick(options.padding, 8),
  8710. r: pick(options.borderRadius, 3),
  8711. zIndex: 2
  8712. })
  8713. .add(this.labelGroup);
  8714. // Presentational
  8715. if (!chart.styledMode) {
  8716. crossLabel
  8717. .attr({
  8718. fill: options.backgroundColor ||
  8719. (this.series[0] && this.series[0].color) ||
  8720. '#666666',
  8721. stroke: options.borderColor || '',
  8722. 'stroke-width': options.borderWidth || 0
  8723. })
  8724. .css(extend({
  8725. color: '#ffffff',
  8726. fontWeight: 'normal',
  8727. fontSize: '11px',
  8728. textAlign: 'center'
  8729. }, options.style));
  8730. }
  8731. }
  8732. if (horiz) {
  8733. posx = snap ? point.plotX + left : e.chartX;
  8734. posy = top + (opposite ? 0 : this.height);
  8735. }
  8736. else {
  8737. posx = opposite ? this.width + left : 0;
  8738. posy = snap ? point.plotY + top : e.chartY;
  8739. }
  8740. if (!formatOption && !options.formatter) {
  8741. if (this.dateTime) {
  8742. formatFormat = '%b %d, %Y';
  8743. }
  8744. formatOption =
  8745. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  8746. }
  8747. // Show the label
  8748. value = snap ?
  8749. point[this.isXAxis ? 'x' : 'y'] :
  8750. this.toValue(horiz ? e.chartX : e.chartY);
  8751. crossLabel.attr({
  8752. text: formatOption ?
  8753. format(formatOption, { value: value }, chart) :
  8754. options.formatter.call(this, value),
  8755. x: posx,
  8756. y: posy,
  8757. // Crosshair should be rendered within Axis range (#7219)
  8758. visibility: value < min || value > max ?
  8759. 'hidden' :
  8760. 'visible'
  8761. });
  8762. crossBox = crossLabel.getBBox();
  8763. // now it is placed we can correct its position
  8764. if (isNumber(crossLabel.y)) {
  8765. if (horiz) {
  8766. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  8767. posy = crossLabel.y - crossBox.height;
  8768. }
  8769. }
  8770. else {
  8771. posy = crossLabel.y - (crossBox.height / 2);
  8772. }
  8773. }
  8774. // check the edges
  8775. if (horiz) {
  8776. limit = {
  8777. left: left - crossBox.x,
  8778. right: left + this.width - crossBox.x
  8779. };
  8780. }
  8781. else {
  8782. limit = {
  8783. left: this.labelAlign === 'left' ? left : 0,
  8784. right: this.labelAlign === 'right' ?
  8785. left + this.width :
  8786. chart.chartWidth
  8787. };
  8788. }
  8789. // left edge
  8790. if (crossLabel.translateX < limit.left) {
  8791. offset = limit.left - crossLabel.translateX;
  8792. }
  8793. // right edge
  8794. if (crossLabel.translateX + crossBox.width >= limit.right) {
  8795. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  8796. }
  8797. // show the crosslabel
  8798. crossLabel.attr({
  8799. x: posx + offset,
  8800. y: posy,
  8801. // First set x and y, then anchorX and anchorY, when box is actually
  8802. // calculated, #5702
  8803. anchorX: horiz ?
  8804. posx :
  8805. (this.opposite ? 0 : chart.chartWidth),
  8806. anchorY: horiz ?
  8807. (this.opposite ? chart.chartHeight : 0) :
  8808. posy + crossBox.height / 2
  8809. });
  8810. });
  8811. /* ************************************************************************** *
  8812. * Start value compare logic *
  8813. * ************************************************************************** */
  8814. /**
  8815. * Extend series.init by adding a method to modify the y value used for plotting
  8816. * on the y axis. This method is called both from the axis when finding dataMin
  8817. * and dataMax, and from the series.translate method.
  8818. *
  8819. * @ignore
  8820. * @function Highcharts.Series#init
  8821. */
  8822. seriesProto.init = function () {
  8823. // Call base method
  8824. seriesInit.apply(this, arguments);
  8825. // Set comparison mode
  8826. this.setCompare(this.options.compare);
  8827. };
  8828. /**
  8829. * Highstock only. Set the
  8830. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  8831. * mode of the series after render time. In most cases it is more useful running
  8832. * {@link Axis#setCompare} on the X axis to update all its series.
  8833. *
  8834. * @function Highcharts.Series#setCompare
  8835. *
  8836. * @param {string} [compare]
  8837. * Can be one of `null` (default), `"percent"` or `"value"`.
  8838. */
  8839. seriesProto.setCompare = function (compare) {
  8840. // Set or unset the modifyValue method
  8841. this.modifyValue = (compare === 'value' || compare === 'percent') ?
  8842. function (value, point) {
  8843. var compareValue = this.compareValue;
  8844. if (typeof value !== 'undefined' &&
  8845. typeof compareValue !== 'undefined') { // #2601, #5814
  8846. // Get the modified value
  8847. if (compare === 'value') {
  8848. value -= compareValue;
  8849. // Compare percent
  8850. }
  8851. else {
  8852. value = 100 * (value / compareValue) -
  8853. (this.options.compareBase === 100 ? 0 : 100);
  8854. }
  8855. // record for tooltip etc.
  8856. if (point) {
  8857. point.change = value;
  8858. }
  8859. return value;
  8860. }
  8861. return 0;
  8862. } :
  8863. null;
  8864. // Survive to export, #5485
  8865. this.userOptions.compare = compare;
  8866. // Mark dirty
  8867. if (this.chart.hasRendered) {
  8868. this.isDirty = true;
  8869. }
  8870. };
  8871. /**
  8872. * Extend series.processData by finding the first y value in the plot area,
  8873. * used for comparing the following values
  8874. *
  8875. * @ignore
  8876. * @function Highcharts.Series#processData
  8877. */
  8878. seriesProto.processData = function (force) {
  8879. var series = this, i, keyIndex = -1, processedXData, processedYData, compareStart = series.options.compareStart === true ? 0 : 1, length, compareValue;
  8880. // call base method
  8881. seriesProcessData.apply(this, arguments);
  8882. if (series.xAxis && series.processedYData) { // not pies
  8883. // local variables
  8884. processedXData = series.processedXData;
  8885. processedYData = series.processedYData;
  8886. length = processedYData.length;
  8887. // For series with more than one value (range, OHLC etc), compare
  8888. // against close or the pointValKey (#4922, #3112, #9854)
  8889. if (series.pointArrayMap) {
  8890. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  8891. }
  8892. // find the first value for comparison
  8893. for (i = 0; i < length - compareStart; i++) {
  8894. compareValue = processedYData[i] && keyIndex > -1 ?
  8895. processedYData[i][keyIndex] :
  8896. processedYData[i];
  8897. if (isNumber(compareValue) &&
  8898. processedXData[i + compareStart] >=
  8899. series.xAxis.min &&
  8900. compareValue !== 0) {
  8901. series.compareValue = compareValue;
  8902. break;
  8903. }
  8904. }
  8905. }
  8906. return;
  8907. };
  8908. // Modify series extremes
  8909. addEvent(Series, 'afterGetExtremes', function (e) {
  8910. var dataExtremes = e.dataExtremes;
  8911. if (this.modifyValue && dataExtremes) {
  8912. var extremes = [
  8913. this.modifyValue(dataExtremes.dataMin),
  8914. this.modifyValue(dataExtremes.dataMax)
  8915. ];
  8916. dataExtremes.dataMin = arrayMin(extremes);
  8917. dataExtremes.dataMax = arrayMax(extremes);
  8918. }
  8919. });
  8920. /**
  8921. * Highstock only. Set the compare mode on all series belonging to an Y axis
  8922. * after render time.
  8923. *
  8924. * @see [series.plotOptions.compare](https://api.highcharts.com/highstock/series.plotOptions.compare)
  8925. *
  8926. * @sample stock/members/axis-setcompare/
  8927. * Set compoare
  8928. *
  8929. * @function Highcharts.Axis#setCompare
  8930. *
  8931. * @param {string} [compare]
  8932. * The compare mode. Can be one of `null` (default), `"value"` or
  8933. * `"percent"`.
  8934. *
  8935. * @param {boolean} [redraw=true]
  8936. * Whether to redraw the chart or to wait for a later call to
  8937. * {@link Chart#redraw}.
  8938. */
  8939. Axis.prototype.setCompare = function (compare, redraw) {
  8940. if (!this.isXAxis) {
  8941. this.series.forEach(function (series) {
  8942. series.setCompare(compare);
  8943. });
  8944. if (pick(redraw, true)) {
  8945. this.chart.redraw();
  8946. }
  8947. }
  8948. };
  8949. /**
  8950. * Extend the tooltip formatter by adding support for the point.change variable
  8951. * as well as the changeDecimals option.
  8952. *
  8953. * @ignore
  8954. * @function Highcharts.Point#tooltipFormatter
  8955. *
  8956. * @param {string} pointFormat
  8957. */
  8958. Point.prototype.tooltipFormatter = function (pointFormat) {
  8959. var point = this;
  8960. var numberFormatter = point.series.chart.numberFormatter;
  8961. pointFormat = pointFormat.replace('{point.change}', (point.change > 0 ? '+' : '') + numberFormatter(point.change, pick(point.series.tooltipOptions.changeDecimals, 2)));
  8962. return pointTooltipFormatter.apply(this, [pointFormat]);
  8963. };
  8964. /* ************************************************************************** *
  8965. * End value compare logic *
  8966. * ************************************************************************** */
  8967. // Extend the Series prototype to create a separate series clip box. This is
  8968. // related to using multiple panes, and a future pane logic should incorporate
  8969. // this feature (#2754).
  8970. addEvent(Series, 'render', function () {
  8971. var chart = this.chart, clipHeight;
  8972. // Only do this on not 3d (#2939, #5904) nor polar (#6057) charts, and only
  8973. // if the series type handles clipping in the animate method (#2975).
  8974. if (!(chart.is3d && chart.is3d()) &&
  8975. !chart.polar &&
  8976. this.xAxis &&
  8977. !this.xAxis.isRadial // Gauge, #6192
  8978. ) {
  8979. clipHeight = this.yAxis.len;
  8980. // Include xAxis line width (#8031) but only if the Y axis ends on the
  8981. // edge of the X axis (#11005).
  8982. if (this.xAxis.axisLine) {
  8983. var dist = chart.plotTop + chart.plotHeight -
  8984. this.yAxis.pos - this.yAxis.len, lineHeightCorrection = Math.floor(this.xAxis.axisLine.strokeWidth() / 2);
  8985. if (dist >= 0) {
  8986. clipHeight -= Math.max(lineHeightCorrection - dist, 0);
  8987. }
  8988. }
  8989. // First render, initial clip box
  8990. if (!this.clipBox && this.animate) {
  8991. this.clipBox = merge(chart.clipBox);
  8992. this.clipBox.width = this.xAxis.len;
  8993. this.clipBox.height = clipHeight;
  8994. // On redrawing, resizing etc, update the clip rectangle
  8995. }
  8996. else if (chart[this.sharedClipKey]) {
  8997. // animate in case resize is done during initial animation
  8998. chart[this.sharedClipKey].animate({
  8999. width: this.xAxis.len,
  9000. height: clipHeight
  9001. });
  9002. // also change markers clip animation for consistency
  9003. // (marker clip rects should exist only on chart init)
  9004. if (chart[this.sharedClipKey + 'm']) {
  9005. chart[this.sharedClipKey + 'm'].animate({
  9006. width: this.xAxis.len
  9007. });
  9008. }
  9009. }
  9010. }
  9011. });
  9012. addEvent(Chart, 'update', function (e) {
  9013. var options = e.options;
  9014. // Use case: enabling scrollbar from a disabled state.
  9015. // Scrollbar needs to be initialized from a controller, Navigator in this
  9016. // case (#6615)
  9017. if ('scrollbar' in options && this.navigator) {
  9018. merge(true, this.options.scrollbar, options.scrollbar);
  9019. this.navigator.update({}, false);
  9020. delete options.scrollbar;
  9021. }
  9022. });
  9023. });
  9024. _registerModule(_modules, 'masters/modules/stock.src.js', [], function () {
  9025. });
  9026. }));