annotations-advanced.src.js 320 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612
  1. /**
  2. * @license Highcharts JS v8.0.0 (2019-12-10)
  3. *
  4. * Annotations module
  5. *
  6. * (c) 2009-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/annotations-advanced', ['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, 'annotations/eventEmitterMixin.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  32. var objectEach = U.objectEach,
  33. pick = U.pick;
  34. var fireEvent = H.fireEvent;
  35. /**
  36. * It provides methods for:
  37. * - adding and handling DOM events and a drag event,
  38. * - mapping a mouse move event to the distance between two following events.
  39. * The units of the distance are specific to a transformation,
  40. * e.g. for rotation they are radians, for scaling they are scale factors.
  41. *
  42. * @private
  43. * @mixin
  44. * @memberOf Annotation
  45. */
  46. var eventEmitterMixin = {
  47. /**
  48. * Add emitter events.
  49. */
  50. addEvents: function () {
  51. var emitter = this;
  52. H.addEvent(
  53. emitter.graphic.element,
  54. 'mousedown',
  55. function (e) {
  56. emitter.onMouseDown(e);
  57. }
  58. );
  59. objectEach(emitter.options.events, function (event, type) {
  60. var eventHandler = function (e) {
  61. if (type !== 'click' || !emitter.cancelClick) {
  62. event.call(
  63. emitter,
  64. emitter.chart.pointer.normalize(e),
  65. emitter.target
  66. );
  67. }
  68. };
  69. if (H.inArray(type, emitter.nonDOMEvents || []) === -1) {
  70. emitter.graphic.on(type, eventHandler);
  71. } else {
  72. H.addEvent(emitter, type, eventHandler);
  73. }
  74. });
  75. if (emitter.options.draggable) {
  76. H.addEvent(emitter, 'drag', emitter.onDrag);
  77. if (!emitter.graphic.renderer.styledMode) {
  78. emitter.graphic.css({
  79. cursor: {
  80. x: 'ew-resize',
  81. y: 'ns-resize',
  82. xy: 'move'
  83. }[emitter.options.draggable]
  84. });
  85. }
  86. }
  87. if (!emitter.isUpdating) {
  88. fireEvent(emitter, 'add');
  89. }
  90. },
  91. /**
  92. * Remove emitter document events.
  93. */
  94. removeDocEvents: function () {
  95. if (this.removeDrag) {
  96. this.removeDrag = this.removeDrag();
  97. }
  98. if (this.removeMouseUp) {
  99. this.removeMouseUp = this.removeMouseUp();
  100. }
  101. },
  102. /**
  103. * Mouse down handler.
  104. *
  105. * @param {Object} e event
  106. */
  107. onMouseDown: function (e) {
  108. var emitter = this,
  109. pointer = emitter.chart.pointer,
  110. prevChartX,
  111. prevChartY;
  112. if (e.preventDefault) {
  113. e.preventDefault();
  114. }
  115. // On right click, do nothing:
  116. if (e.button === 2) {
  117. return;
  118. }
  119. e = pointer.normalize(e);
  120. prevChartX = e.chartX;
  121. prevChartY = e.chartY;
  122. emitter.cancelClick = false;
  123. emitter.chart.hasDraggedAnnotation = true;
  124. emitter.removeDrag = H.addEvent(
  125. H.doc,
  126. 'mousemove',
  127. function (e) {
  128. emitter.hasDragged = true;
  129. e = pointer.normalize(e);
  130. e.prevChartX = prevChartX;
  131. e.prevChartY = prevChartY;
  132. fireEvent(emitter, 'drag', e);
  133. prevChartX = e.chartX;
  134. prevChartY = e.chartY;
  135. }
  136. );
  137. emitter.removeMouseUp = H.addEvent(
  138. H.doc,
  139. 'mouseup',
  140. function (e) {
  141. emitter.cancelClick = emitter.hasDragged;
  142. emitter.hasDragged = false;
  143. emitter.chart.hasDraggedAnnotation = false;
  144. // ControlPoints vs Annotation:
  145. fireEvent(pick(emitter.target, emitter), 'afterUpdate');
  146. emitter.onMouseUp(e);
  147. }
  148. );
  149. },
  150. /**
  151. * Mouse up handler.
  152. *
  153. * @param {Object} e event
  154. */
  155. onMouseUp: function () {
  156. var chart = this.chart,
  157. annotation = this.target || this,
  158. annotationsOptions = chart.options.annotations,
  159. index = chart.annotations.indexOf(annotation);
  160. this.removeDocEvents();
  161. annotationsOptions[index] = annotation.options;
  162. },
  163. /**
  164. * Drag and drop event. All basic annotations should share this
  165. * capability as well as the extended ones.
  166. *
  167. * @param {Object} e event
  168. */
  169. onDrag: function (e) {
  170. if (
  171. this.chart.isInsidePlot(
  172. e.chartX - this.chart.plotLeft,
  173. e.chartY - this.chart.plotTop
  174. )
  175. ) {
  176. var translation = this.mouseMoveToTranslation(e);
  177. if (this.options.draggable === 'x') {
  178. translation.y = 0;
  179. }
  180. if (this.options.draggable === 'y') {
  181. translation.x = 0;
  182. }
  183. if (this.points.length) {
  184. this.translate(translation.x, translation.y);
  185. } else {
  186. this.shapes.forEach(function (shape) {
  187. shape.translate(translation.x, translation.y);
  188. });
  189. this.labels.forEach(function (label) {
  190. label.translate(translation.x, translation.y);
  191. });
  192. }
  193. this.redraw(false);
  194. }
  195. },
  196. /**
  197. * Map mouse move event to the radians.
  198. *
  199. * @param {Object} e event
  200. * @param {number} cx center x
  201. * @param {number} cy center y
  202. */
  203. mouseMoveToRadians: function (e, cx, cy) {
  204. var prevDy = e.prevChartY - cy,
  205. prevDx = e.prevChartX - cx,
  206. dy = e.chartY - cy,
  207. dx = e.chartX - cx,
  208. temp;
  209. if (this.chart.inverted) {
  210. temp = prevDx;
  211. prevDx = prevDy;
  212. prevDy = temp;
  213. temp = dx;
  214. dx = dy;
  215. dy = temp;
  216. }
  217. return Math.atan2(dy, dx) - Math.atan2(prevDy, prevDx);
  218. },
  219. /**
  220. * Map mouse move event to the distance between two following events.
  221. *
  222. * @param {Object} e event
  223. */
  224. mouseMoveToTranslation: function (e) {
  225. var dx = e.chartX - e.prevChartX,
  226. dy = e.chartY - e.prevChartY,
  227. temp;
  228. if (this.chart.inverted) {
  229. temp = dy;
  230. dy = dx;
  231. dx = temp;
  232. }
  233. return {
  234. x: dx,
  235. y: dy
  236. };
  237. },
  238. /**
  239. * Map mouse move to the scale factors.
  240. *
  241. * @param {Object} e event
  242. * @param {number} cx center x
  243. * @param {number} cy center y
  244. **/
  245. mouseMoveToScale: function (e, cx, cy) {
  246. var prevDx = e.prevChartX - cx,
  247. prevDy = e.prevChartY - cy,
  248. dx = e.chartX - cx,
  249. dy = e.chartY - cy,
  250. sx = (dx || 1) / (prevDx || 1),
  251. sy = (dy || 1) / (prevDy || 1),
  252. temp;
  253. if (this.chart.inverted) {
  254. temp = sy;
  255. sy = sx;
  256. sx = temp;
  257. }
  258. return {
  259. x: sx,
  260. y: sy
  261. };
  262. },
  263. /**
  264. * Destroy the event emitter.
  265. */
  266. destroy: function () {
  267. this.removeDocEvents();
  268. H.removeEvent(this);
  269. this.hcEvents = null;
  270. }
  271. };
  272. return eventEmitterMixin;
  273. });
  274. _registerModule(_modules, 'annotations/ControlPoint.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['annotations/eventEmitterMixin.js']], function (H, U, eventEmitterMixin) {
  275. var extend = U.extend,
  276. pick = U.pick;
  277. /**
  278. * A control point class which is a connection between controllable
  279. * transform methods and a user actions.
  280. *
  281. * @constructor
  282. * @mixes eventEmitterMixin
  283. * @memberOf Annotation
  284. *
  285. * @param {Highcharts.Chart} chart a chart instance
  286. * @param {Object} target a controllable instance which is a target for
  287. * a control point
  288. * @param {Annotation.ControlPoint.Options} options an options object
  289. * @param {number} [index]
  290. */
  291. function ControlPoint(chart, target, options, index) {
  292. this.chart = chart;
  293. this.target = target;
  294. this.options = options;
  295. this.index = pick(options.index, index);
  296. }
  297. /**
  298. * @typedef {Object} Annotation.ControlPoint.Position
  299. * @property {number} x
  300. * @property {number} y
  301. */
  302. /**
  303. * @callback Annotation.ControlPoint.Positioner
  304. * @param {Object} e event
  305. * @param {Controllable} target
  306. * @return {Annotation.ControlPoint.Position} position
  307. */
  308. /**
  309. * @typedef {Object} Annotation.ControlPoint.Options
  310. * @property {string} symbol
  311. * @property {number} width
  312. * @property {number} height
  313. * @property {Object} style
  314. * @property {boolean} visible
  315. * @property {Annotation.ControlPoint.Positioner} positioner
  316. * @property {Object} events
  317. */
  318. extend(
  319. ControlPoint.prototype,
  320. eventEmitterMixin
  321. );
  322. /**
  323. * List of events for `anntation.options.events` that should not be
  324. * added to `annotation.graphic` but to the `annotation`.
  325. *
  326. * @type {Array<string>}
  327. */
  328. ControlPoint.prototype.nonDOMEvents = ['drag'];
  329. /**
  330. * Set the visibility.
  331. *
  332. * @param {boolean} [visible]
  333. **/
  334. ControlPoint.prototype.setVisibility = function (visible) {
  335. this.graphic.attr('visibility', visible ? 'visible' : 'hidden');
  336. this.options.visible = visible;
  337. };
  338. /**
  339. * Render the control point.
  340. */
  341. ControlPoint.prototype.render = function () {
  342. var chart = this.chart,
  343. options = this.options;
  344. this.graphic = chart.renderer
  345. .symbol(
  346. options.symbol,
  347. 0,
  348. 0,
  349. options.width,
  350. options.height
  351. )
  352. .add(chart.controlPointsGroup)
  353. .css(options.style);
  354. this.setVisibility(options.visible);
  355. this.addEvents();
  356. };
  357. /**
  358. * Redraw the control point.
  359. *
  360. * @param {boolean} [animation]
  361. */
  362. ControlPoint.prototype.redraw = function (animation) {
  363. this.graphic[animation ? 'animate' : 'attr'](
  364. this.options.positioner.call(this, this.target)
  365. );
  366. };
  367. /**
  368. * Destroy the control point.
  369. */
  370. ControlPoint.prototype.destroy = function () {
  371. eventEmitterMixin.destroy.call(this);
  372. if (this.graphic) {
  373. this.graphic = this.graphic.destroy();
  374. }
  375. this.chart = null;
  376. this.target = null;
  377. this.options = null;
  378. };
  379. /**
  380. * Update the control point.
  381. */
  382. ControlPoint.prototype.update = function (userOptions) {
  383. var chart = this.chart,
  384. target = this.target,
  385. index = this.index,
  386. options = H.merge(true, this.options, userOptions);
  387. this.destroy();
  388. this.constructor(chart, target, options, index);
  389. this.render(chart.controlPointsGroup);
  390. this.redraw();
  391. };
  392. return ControlPoint;
  393. });
  394. _registerModule(_modules, 'annotations/MockPoint.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  395. var defined = U.defined,
  396. extend = U.extend;
  397. /**
  398. * A mock point label configuration.
  399. *
  400. * @interface Annotation.MockLabelOptionsObject
  401. *//**
  402. * X value translated to x axis scale
  403. * @name Annotation.MockLabelOptionsObject#x
  404. * @type {number|undefined}
  405. *//**
  406. * Y value translated to y axis scale
  407. * @name Annotation.MockLabelOptionsObject#y
  408. * @type {number|undefined}
  409. *//**
  410. * @name Annotation.MockLabelOptionsObject#point
  411. * @type {Highcharts.Point}
  412. */
  413. /**
  414. * A mock point configuration.
  415. *
  416. * @interface Highcharts.MockPointOptionsObject
  417. *//**
  418. * x value for the point in xAxis scale or pixels
  419. * @name Highcharts.MockPointOptionsObject#x
  420. * @type {number}
  421. *//**
  422. * y value for the point in yAxis scale or pixels
  423. * @name Highcharts.MockPointOptionsObject#y
  424. * @type {number}
  425. *//**
  426. * xAxis index or id
  427. * @name Highcharts.MockPointOptionsObject#xAxis
  428. * @type {Highcharts.Axis|number|string|undefined}
  429. *//**
  430. * yAxis index or id
  431. * @name Highcharts.MockPointOptionsObject#yAxis
  432. * @property {Highcharts.Axis|number|string|undefined}
  433. */
  434. /**
  435. * A point-like object, a mock point or a point uses in series.
  436. *
  437. * @private
  438. * @typedef {Highcharts.Point|Highcharts.MockPoint} Highcharts.PointLike
  439. */
  440. /**
  441. * A trimmed point object which imitates {@link Highchart.Point} class.
  442. * It is created when there is a need of pointing to some chart's position
  443. * using axis values or pixel values
  444. *
  445. * @private
  446. * @class
  447. * @name Highcharts.MockPoint
  448. *
  449. * @param {Highcharts.Chart} chart
  450. * The chart object
  451. *
  452. * @param {Highcharts.MockPointOptionsObject} options
  453. * The options object
  454. */
  455. function MockPoint(chart, target, options) {
  456. /**
  457. * A mock series instance imitating a real series from a real point.
  458. *
  459. * @type {Object}
  460. * @property {boolean} series.visible=true - whether a series is visible
  461. * @property {Chart} series.chart - a chart instance
  462. * @property {function} series.getPlotBox
  463. */
  464. this.series = {
  465. visible: true,
  466. chart: chart,
  467. getPlotBox: H.Series.prototype.getPlotBox
  468. };
  469. /**
  470. * @type {?Controllable}
  471. */
  472. this.target = target || null;
  473. /**
  474. * Options for the mock point.
  475. *
  476. * @type {Highcharts.MockPointOptionsObject}
  477. */
  478. this.options = options;
  479. /**
  480. * If an xAxis is set it represents the point's value in terms of the xAxis.
  481. *
  482. * @name Annotation.MockPoint#x
  483. * @type {?number}
  484. */
  485. /**
  486. * If an yAxis is set it represents the point's value in terms of the yAxis.
  487. *
  488. * @name Annotation.MockPoint#y
  489. * @type {?number}
  490. */
  491. /**
  492. * It represents the point's pixel x coordinate relative to its plot box.
  493. *
  494. * @name Annotation.MockPoint#plotX
  495. * @type {?number}
  496. */
  497. /**
  498. * It represents the point's pixel y position relative to its plot box.
  499. *
  500. * @name Annotation.MockPoint#plotY
  501. * @type {?number}
  502. */
  503. /**
  504. * Whether the point is inside the plot box.
  505. *
  506. * @name Annotation.MockPoint#isInside
  507. * @type {boolean}
  508. */
  509. this.applyOptions(this.getOptions());
  510. }
  511. /**
  512. * Create a mock point from a real Highcharts point.
  513. *
  514. * @param {Point} point
  515. *
  516. * @return {Annotation.MockPoint} a mock point instance.
  517. */
  518. MockPoint.fromPoint = function (point) {
  519. return new MockPoint(point.series.chart, null, {
  520. x: point.x,
  521. y: point.y,
  522. xAxis: point.series.xAxis,
  523. yAxis: point.series.yAxis
  524. });
  525. };
  526. /**
  527. * @typedef Annotation.MockPoint.Position
  528. * @property {number} x
  529. * @property {number} y
  530. */
  531. /**
  532. * Get the pixel position from the point like object.
  533. *
  534. * @param {Annotation.PointLike} point
  535. * @param {boolean} [paneCoordinates]
  536. * whether the pixel position should be relative
  537. *
  538. * @return {Annotation.MockPoint.Position} pixel position
  539. */
  540. MockPoint.pointToPixels = function (point, paneCoordinates) {
  541. var series = point.series,
  542. chart = series.chart,
  543. x = point.plotX,
  544. y = point.plotY,
  545. plotBox;
  546. if (chart.inverted) {
  547. if (point.mock) {
  548. x = point.plotY;
  549. y = point.plotX;
  550. } else {
  551. x = chart.plotWidth - point.plotY;
  552. y = chart.plotHeight - point.plotX;
  553. }
  554. }
  555. if (series && !paneCoordinates) {
  556. plotBox = series.getPlotBox();
  557. x += plotBox.translateX;
  558. y += plotBox.translateY;
  559. }
  560. return {
  561. x: x,
  562. y: y
  563. };
  564. };
  565. /**
  566. * Get fresh mock point options from the point like object.
  567. *
  568. * @param {Annotation.PointLike} point
  569. *
  570. * @return {Annotation.MockPoint.Options} mock point's options
  571. */
  572. MockPoint.pointToOptions = function (point) {
  573. return {
  574. x: point.x,
  575. y: point.y,
  576. xAxis: point.series.xAxis,
  577. yAxis: point.series.yAxis
  578. };
  579. };
  580. extend(MockPoint.prototype, /** @lends Annotation.MockPoint# */ {
  581. /**
  582. * A flag indicating that a point is not the real one.
  583. *
  584. * @type {boolean}
  585. * @default true
  586. */
  587. mock: true,
  588. /**
  589. * Check if the point has dynamic options.
  590. *
  591. * @return {boolean} A positive flag if the point has dynamic options.
  592. */
  593. hasDynamicOptions: function () {
  594. return typeof this.options === 'function';
  595. },
  596. /**
  597. * Get the point's options.
  598. *
  599. * @return {Annotation.MockPoint.Options} the mock point's options.
  600. */
  601. getOptions: function () {
  602. return this.hasDynamicOptions() ?
  603. this.options(this.target) :
  604. this.options;
  605. },
  606. /**
  607. * Apply options for the point.
  608. *
  609. * @param {Annotation.MockPoint.Options} options
  610. */
  611. applyOptions: function (options) {
  612. this.command = options.command;
  613. this.setAxis(options, 'x');
  614. this.setAxis(options, 'y');
  615. this.refresh();
  616. },
  617. /**
  618. * Set x or y axis.
  619. *
  620. * @param {Annotation.MockPoint.Options} options
  621. * @param {string} xOrY 'x' or 'y' string literal
  622. */
  623. setAxis: function (options, xOrY) {
  624. var axisName = xOrY + 'Axis',
  625. axisOptions = options[axisName],
  626. chart = this.series.chart;
  627. this.series[axisName] =
  628. axisOptions instanceof H.Axis ?
  629. axisOptions :
  630. defined(axisOptions) ?
  631. chart[axisName][axisOptions] || chart.get(axisOptions) :
  632. null;
  633. },
  634. /**
  635. * Transform the mock point to an anchor
  636. * (relative position on the chart).
  637. *
  638. * @return {Array<number>} A quadruple of numbers which denotes x, y,
  639. * width and height of the box
  640. **/
  641. toAnchor: function () {
  642. var anchor = [this.plotX, this.plotY, 0, 0];
  643. if (this.series.chart.inverted) {
  644. anchor[0] = this.plotY;
  645. anchor[1] = this.plotX;
  646. }
  647. return anchor;
  648. },
  649. /**
  650. * @typedef {Object} Annotation.MockPoint.LabelConfig
  651. * @property {number|undefined} x x value translated to x axis scale
  652. * @property {number|undefined} y y value translated to y axis scale
  653. * @property {Annotation.MockPoint} point instance of the point
  654. */
  655. /**
  656. * Returns a label config object -
  657. * the same as Highcharts.Point.prototype.getLabelConfig
  658. *
  659. * @return {Annotation.MockPoint.LabelConfig} the point's label config
  660. */
  661. getLabelConfig: function () {
  662. return {
  663. x: this.x,
  664. y: this.y,
  665. point: this
  666. };
  667. },
  668. /**
  669. * Check if the point is inside its pane.
  670. *
  671. * @return {boolean} A flag indicating whether the point is inside the pane.
  672. */
  673. isInsidePane: function () {
  674. var plotX = this.plotX,
  675. plotY = this.plotY,
  676. xAxis = this.series.xAxis,
  677. yAxis = this.series.yAxis,
  678. isInside = true;
  679. if (xAxis) {
  680. isInside = defined(plotX) && plotX >= 0 && plotX <= xAxis.len;
  681. }
  682. if (yAxis) {
  683. isInside =
  684. isInside &&
  685. defined(plotY) &&
  686. plotY >= 0 && plotY <= yAxis.len;
  687. }
  688. return isInside;
  689. },
  690. /**
  691. * Refresh point values and coordinates based on its options.
  692. */
  693. refresh: function () {
  694. var series = this.series,
  695. xAxis = series.xAxis,
  696. yAxis = series.yAxis,
  697. options = this.getOptions();
  698. if (xAxis) {
  699. this.x = options.x;
  700. this.plotX = xAxis.toPixels(options.x, true);
  701. } else {
  702. this.x = null;
  703. this.plotX = options.x;
  704. }
  705. if (yAxis) {
  706. this.y = options.y;
  707. this.plotY = yAxis.toPixels(options.y, true);
  708. } else {
  709. this.y = null;
  710. this.plotY = options.y;
  711. }
  712. this.isInside = this.isInsidePane();
  713. },
  714. /**
  715. * Translate the point.
  716. *
  717. * @param {number} [cx] origin x transformation
  718. * @param {number} [cy] origin y transformation
  719. * @param {number} dx translation for x coordinate
  720. * @param {number} dy translation for y coordinate
  721. **/
  722. translate: function (cx, cy, dx, dy) {
  723. if (!this.hasDynamicOptions()) {
  724. this.plotX += dx;
  725. this.plotY += dy;
  726. this.refreshOptions();
  727. }
  728. },
  729. /**
  730. * Scale the point.
  731. *
  732. * @param {number} cx origin x transformation
  733. * @param {number} cy origin y transformation
  734. * @param {number} sx scale factor x
  735. * @param {number} sy scale factor y
  736. */
  737. scale: function (cx, cy, sx, sy) {
  738. if (!this.hasDynamicOptions()) {
  739. var x = this.plotX * sx,
  740. y = this.plotY * sy,
  741. tx = (1 - sx) * cx,
  742. ty = (1 - sy) * cy;
  743. this.plotX = tx + x;
  744. this.plotY = ty + y;
  745. this.refreshOptions();
  746. }
  747. },
  748. /**
  749. * Rotate the point.
  750. *
  751. * @param {number} cx origin x rotation
  752. * @param {number} cy origin y rotation
  753. * @param {number} radians
  754. */
  755. rotate: function (cx, cy, radians) {
  756. if (!this.hasDynamicOptions()) {
  757. var cos = Math.cos(radians),
  758. sin = Math.sin(radians),
  759. x = this.plotX,
  760. y = this.plotY,
  761. tx,
  762. ty;
  763. x -= cx;
  764. y -= cy;
  765. tx = x * cos - y * sin;
  766. ty = x * sin + y * cos;
  767. this.plotX = tx + cx;
  768. this.plotY = ty + cy;
  769. this.refreshOptions();
  770. }
  771. },
  772. /**
  773. * Refresh point options based on its plot coordinates.
  774. */
  775. refreshOptions: function () {
  776. var series = this.series,
  777. xAxis = series.xAxis,
  778. yAxis = series.yAxis;
  779. this.x = this.options.x = xAxis ?
  780. this.options.x = xAxis.toValue(this.plotX, true) :
  781. this.plotX;
  782. this.y = this.options.y = yAxis ?
  783. yAxis.toValue(this.plotY, true) :
  784. this.plotY;
  785. }
  786. });
  787. return MockPoint;
  788. });
  789. _registerModule(_modules, 'annotations/controllable/controllableMixin.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['annotations/ControlPoint.js'], _modules['annotations/MockPoint.js']], function (H, U, ControlPoint, MockPoint) {
  790. var isObject = U.isObject,
  791. isString = U.isString,
  792. splat = U.splat;
  793. /**
  794. * It provides methods for handling points, control points
  795. * and points transformations.
  796. *
  797. * @private
  798. * @mixin
  799. * @memberOf Annotation
  800. */
  801. var controllableMixin = {
  802. /**
  803. * Init the controllable
  804. *
  805. * @param {Annotation} annotation - an annotation instance
  806. * @param {Object} options - options specific for controllable
  807. * @param {number} index - index of the controllable element
  808. **/
  809. init: function (annotation, options, index) {
  810. this.annotation = annotation;
  811. this.chart = annotation.chart;
  812. this.options = options;
  813. this.points = [];
  814. this.controlPoints = [];
  815. this.index = index;
  816. this.linkPoints();
  817. this.addControlPoints();
  818. },
  819. /**
  820. * Redirect attr usage on the controllable graphic element.
  821. **/
  822. attr: function () {
  823. this.graphic.attr.apply(this.graphic, arguments);
  824. },
  825. /**
  826. * Get the controllable's points options.
  827. *
  828. * @return {Array<PointLikeOptions>} - an array of points' options.
  829. *
  830. */
  831. getPointsOptions: function () {
  832. var options = this.options;
  833. return options.points || (options.point && splat(options.point));
  834. },
  835. /**
  836. * Utility function for mapping item's options
  837. * to element's attribute
  838. *
  839. * @param {Object} options
  840. * @return {Object} mapped options
  841. **/
  842. attrsFromOptions: function (options) {
  843. var map = this.constructor.attrsMap,
  844. attrs = {},
  845. key,
  846. mappedKey,
  847. styledMode = this.chart.styledMode;
  848. for (key in options) {
  849. mappedKey = map[key];
  850. if (
  851. mappedKey &&
  852. (
  853. !styledMode ||
  854. ['fill', 'stroke', 'stroke-width']
  855. .indexOf(mappedKey) === -1
  856. )
  857. ) {
  858. attrs[mappedKey] = options[key];
  859. }
  860. }
  861. return attrs;
  862. },
  863. /**
  864. * @typedef {Object} Annotation.controllableMixin.Position
  865. * @property {number} x
  866. * @property {number} y
  867. */
  868. /**
  869. * An object which denotes an anchor position
  870. *
  871. * @typedef Annotation.controllableMixin.AnchorPosition
  872. * Annotation.controllableMixin.Position
  873. * @property {number} height
  874. * @property {number} width
  875. */
  876. /**
  877. * An object which denots a controllable's anchor positions
  878. * - relative and absolute.
  879. *
  880. * @typedef {Object} Annotation.controllableMixin.Anchor
  881. * @property {Annotation.controllableMixin.AnchorPosition} relativePosition
  882. * @property {Annotation.controllableMixin.AnchorPosition} absolutePosition
  883. */
  884. /**
  885. * Returns object which denotes anchor position - relative and absolute.
  886. *
  887. * @param {Annotation.PointLike} point a point like object
  888. * @return {Annotation.controllableMixin.Anchor} a controllable anchor
  889. */
  890. anchor: function (point) {
  891. var plotBox = point.series.getPlotBox(),
  892. box = point.mock ?
  893. point.toAnchor() :
  894. H.Tooltip.prototype.getAnchor.call({
  895. chart: point.series.chart
  896. }, point),
  897. anchor = {
  898. x: box[0] + (this.options.x || 0),
  899. y: box[1] + (this.options.y || 0),
  900. height: box[2] || 0,
  901. width: box[3] || 0
  902. };
  903. return {
  904. relativePosition: anchor,
  905. absolutePosition: H.merge(anchor, {
  906. x: anchor.x + plotBox.translateX,
  907. y: anchor.y + plotBox.translateY
  908. })
  909. };
  910. },
  911. /**
  912. * Map point's options to a point-like object.
  913. *
  914. * @param {Highcharts.MockPointOptionsObject} pointOptions
  915. * point's options
  916. * @param {Highcharts.PointLike} point
  917. * a point like instance
  918. *
  919. * @return {Highcharts.PointLike|null}
  920. * if the point is found/set returns this point, otherwise null
  921. */
  922. point: function (pointOptions, point) {
  923. if (pointOptions && pointOptions.series) {
  924. return pointOptions;
  925. }
  926. if (!point || point.series === null) {
  927. if (isObject(pointOptions)) {
  928. point = new MockPoint(
  929. this.chart,
  930. this,
  931. pointOptions
  932. );
  933. } else if (isString(pointOptions)) {
  934. point = this.chart.get(pointOptions) || null;
  935. } else if (typeof pointOptions === 'function') {
  936. var pointConfig = pointOptions.call(point, this);
  937. point = pointConfig.series ?
  938. pointConfig :
  939. new MockPoint(
  940. this.chart,
  941. this,
  942. pointOptions
  943. );
  944. }
  945. }
  946. return point;
  947. },
  948. /**
  949. * Find point-like objects based on points options.
  950. *
  951. * @return {Array<Annotation.PointLike>} an array of point-like objects
  952. */
  953. linkPoints: function () {
  954. var pointsOptions = this.getPointsOptions(),
  955. points = this.points,
  956. len = (pointsOptions && pointsOptions.length) || 0,
  957. i,
  958. point;
  959. for (i = 0; i < len; i++) {
  960. point = this.point(pointsOptions[i], points[i]);
  961. if (!point) {
  962. points.length = 0;
  963. return;
  964. }
  965. if (point.mock) {
  966. point.refresh();
  967. }
  968. points[i] = point;
  969. }
  970. return points;
  971. },
  972. /**
  973. * Add control points to a controllable.
  974. */
  975. addControlPoints: function () {
  976. var controlPointsOptions = this.options.controlPoints;
  977. (controlPointsOptions || []).forEach(
  978. function (controlPointOptions, i) {
  979. var options = H.merge(
  980. this.options.controlPointOptions,
  981. controlPointOptions
  982. );
  983. if (!options.index) {
  984. options.index = i;
  985. }
  986. controlPointsOptions[i] = options;
  987. this.controlPoints.push(
  988. new ControlPoint(this.chart, this, options)
  989. );
  990. },
  991. this
  992. );
  993. },
  994. /**
  995. * Check if a controllable should be rendered/redrawn.
  996. *
  997. * @return {boolean} whether a controllable should be drawn.
  998. */
  999. shouldBeDrawn: function () {
  1000. return Boolean(this.points.length);
  1001. },
  1002. /**
  1003. * Render a controllable.
  1004. **/
  1005. render: function () {
  1006. this.controlPoints.forEach(function (controlPoint) {
  1007. controlPoint.render();
  1008. });
  1009. },
  1010. /**
  1011. * Redraw a controllable.
  1012. *
  1013. * @param {boolean} animation
  1014. **/
  1015. redraw: function (animation) {
  1016. this.controlPoints.forEach(function (controlPoint) {
  1017. controlPoint.redraw(animation);
  1018. });
  1019. },
  1020. /**
  1021. * Transform a controllable with a specific transformation.
  1022. *
  1023. * @param {string} transformation a transformation name
  1024. * @param {number} cx origin x transformation
  1025. * @param {number} cy origin y transformation
  1026. * @param {number} p1 param for the transformation
  1027. * @param {number} p2 param for the transformation
  1028. **/
  1029. transform: function (transformation, cx, cy, p1, p2) {
  1030. if (this.chart.inverted) {
  1031. var temp = cx;
  1032. cx = cy;
  1033. cy = temp;
  1034. }
  1035. this.points.forEach(function (point, i) {
  1036. this.transformPoint(transformation, cx, cy, p1, p2, i);
  1037. }, this);
  1038. },
  1039. /**
  1040. * Transform a point with a specific transformation
  1041. * If a transformed point is a real point it is replaced with
  1042. * the mock point.
  1043. *
  1044. * @param {string} transformation a transformation name
  1045. * @param {number} cx origin x transformation
  1046. * @param {number} cy origin y transformation
  1047. * @param {number} p1 param for the transformation
  1048. * @param {number} p2 param for the transformation
  1049. * @param {number} i index of the point
  1050. *
  1051. **/
  1052. transformPoint: function (transformation, cx, cy, p1, p2, i) {
  1053. var point = this.points[i];
  1054. if (!point.mock) {
  1055. point = this.points[i] = MockPoint.fromPoint(point);
  1056. }
  1057. point[transformation](cx, cy, p1, p2);
  1058. },
  1059. /**
  1060. * Translate a controllable.
  1061. *
  1062. * @param {number} dx translation for x coordinate
  1063. * @param {number} dy translation for y coordinate
  1064. **/
  1065. translate: function (dx, dy) {
  1066. this.transform('translate', null, null, dx, dy);
  1067. },
  1068. /**
  1069. * Translate a specific point within a controllable.
  1070. *
  1071. * @param {number} dx translation for x coordinate
  1072. * @param {number} dy translation for y coordinate
  1073. * @param {number} i index of the point
  1074. **/
  1075. translatePoint: function (dx, dy, i) {
  1076. this.transformPoint('translate', null, null, dx, dy, i);
  1077. },
  1078. /**
  1079. * Translate shape within controllable item.
  1080. * Replaces `controllable.translate` method.
  1081. *
  1082. * @param {number} dx translation for x coordinate
  1083. * @param {number} dy translation for y coordinate
  1084. */
  1085. translateShape: function (dx, dy) {
  1086. var chart = this.annotation.chart,
  1087. // Annotation.options
  1088. shapeOptions = this.annotation.userOptions,
  1089. // Chart.options.annotations
  1090. annotationIndex = chart.annotations.indexOf(this.annotation),
  1091. chartOptions = chart.options.annotations[annotationIndex];
  1092. this.translatePoint(dx, dy, 0);
  1093. // Options stored in:
  1094. // - chart (for exporting)
  1095. // - current config (for redraws)
  1096. chartOptions[this.collection][this.index].point = this.options.point;
  1097. shapeOptions[this.collection][this.index].point = this.options.point;
  1098. },
  1099. /**
  1100. * Rotate a controllable.
  1101. *
  1102. * @param {number} cx origin x rotation
  1103. * @param {number} cy origin y rotation
  1104. * @param {number} radians
  1105. **/
  1106. rotate: function (cx, cy, radians) {
  1107. this.transform('rotate', cx, cy, radians);
  1108. },
  1109. /**
  1110. * Scale a controllable.
  1111. *
  1112. * @param {number} cx origin x rotation
  1113. * @param {number} cy origin y rotation
  1114. * @param {number} sx scale factor x
  1115. * @param {number} sy scale factor y
  1116. */
  1117. scale: function (cx, cy, sx, sy) {
  1118. this.transform('scale', cx, cy, sx, sy);
  1119. },
  1120. /**
  1121. * Set control points' visibility.
  1122. *
  1123. * @param {boolean} [visible]
  1124. */
  1125. setControlPointsVisibility: function (visible) {
  1126. this.controlPoints.forEach(function (controlPoint) {
  1127. controlPoint.setVisibility(visible);
  1128. });
  1129. },
  1130. /**
  1131. * Destroy a controllable.
  1132. */
  1133. destroy: function () {
  1134. if (this.graphic) {
  1135. this.graphic = this.graphic.destroy();
  1136. }
  1137. if (this.tracker) {
  1138. this.tracker = this.tracker.destroy();
  1139. }
  1140. this.controlPoints.forEach(function (controlPoint) {
  1141. controlPoint.destroy();
  1142. });
  1143. this.chart = null;
  1144. this.points = null;
  1145. this.controlPoints = null;
  1146. this.options = null;
  1147. if (this.annotation) {
  1148. this.annotation = null;
  1149. }
  1150. },
  1151. /**
  1152. * Update a controllable.
  1153. *
  1154. * @param {Object} newOptions
  1155. */
  1156. update: function (newOptions) {
  1157. var annotation = this.annotation,
  1158. options = H.merge(true, this.options, newOptions),
  1159. parentGroup = this.graphic.parentGroup;
  1160. this.destroy();
  1161. this.constructor(annotation, options);
  1162. this.render(parentGroup);
  1163. this.redraw();
  1164. }
  1165. };
  1166. return controllableMixin;
  1167. });
  1168. _registerModule(_modules, 'annotations/controllable/markerMixin.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  1169. var defined = U.defined,
  1170. objectEach = U.objectEach,
  1171. splat = U.splat;
  1172. /**
  1173. * Options for configuring markers for annotations.
  1174. *
  1175. * An example of the arrow marker:
  1176. * <pre>
  1177. * {
  1178. * arrow: {
  1179. * id: 'arrow',
  1180. * tagName: 'marker',
  1181. * refY: 5,
  1182. * refX: 5,
  1183. * markerWidth: 10,
  1184. * markerHeight: 10,
  1185. * children: [{
  1186. * tagName: 'path',
  1187. * attrs: {
  1188. * d: 'M 0 0 L 10 5 L 0 10 Z',
  1189. * strokeWidth: 0
  1190. * }
  1191. * }]
  1192. * }
  1193. * }
  1194. * </pre>
  1195. * @type {Object}
  1196. * @sample highcharts/annotations/custom-markers/
  1197. * Define a custom marker for annotations
  1198. * @sample highcharts/css/annotations-markers/
  1199. * Define markers in a styled mode
  1200. * @since 6.0.0
  1201. * @apioption defs
  1202. */
  1203. var defaultMarkers = {
  1204. arrow: {
  1205. tagName: 'marker',
  1206. render: false,
  1207. id: 'arrow',
  1208. refY: 5,
  1209. refX: 9,
  1210. markerWidth: 10,
  1211. markerHeight: 10,
  1212. children: [{
  1213. tagName: 'path',
  1214. d: 'M 0 0 L 10 5 L 0 10 Z', // triangle (used as an arrow)
  1215. strokeWidth: 0
  1216. }]
  1217. },
  1218. 'reverse-arrow': {
  1219. tagName: 'marker',
  1220. render: false,
  1221. id: 'reverse-arrow',
  1222. refY: 5,
  1223. refX: 1,
  1224. markerWidth: 10,
  1225. markerHeight: 10,
  1226. children: [{
  1227. tagName: 'path',
  1228. // reverse triangle (used as an arrow)
  1229. d: 'M 0 5 L 10 0 L 10 10 Z',
  1230. strokeWidth: 0
  1231. }]
  1232. }
  1233. };
  1234. H.SVGRenderer.prototype.addMarker = function (id, markerOptions) {
  1235. var options = { id: id };
  1236. var attrs = {
  1237. stroke: markerOptions.color || 'none',
  1238. fill: markerOptions.color || 'rgba(0, 0, 0, 0.75)'
  1239. };
  1240. options.children = markerOptions.children.map(function (child) {
  1241. return H.merge(attrs, child);
  1242. });
  1243. var marker = this.definition(H.merge(true, {
  1244. markerWidth: 20,
  1245. markerHeight: 20,
  1246. refX: 0,
  1247. refY: 0,
  1248. orient: 'auto'
  1249. }, markerOptions, options));
  1250. marker.id = id;
  1251. return marker;
  1252. };
  1253. var createMarkerSetter = function (markerType) {
  1254. return function (value) {
  1255. this.attr(markerType, 'url(#' + value + ')');
  1256. };
  1257. };
  1258. /**
  1259. * @private
  1260. * @mixin
  1261. */
  1262. var markerMixin = {
  1263. markerEndSetter: createMarkerSetter('marker-end'),
  1264. markerStartSetter: createMarkerSetter('marker-start'),
  1265. /*
  1266. * Set markers.
  1267. *
  1268. * @param {Controllable} item
  1269. */
  1270. setItemMarkers: function (item) {
  1271. var itemOptions = item.options,
  1272. chart = item.chart,
  1273. defs = chart.options.defs,
  1274. fill = itemOptions.fill,
  1275. color = defined(fill) && fill !== 'none' ?
  1276. fill :
  1277. itemOptions.stroke,
  1278. setMarker = function (markerType) {
  1279. var markerId = itemOptions[markerType],
  1280. def,
  1281. predefinedMarker,
  1282. key,
  1283. marker;
  1284. if (markerId) {
  1285. for (key in defs) {
  1286. def = defs[key];
  1287. if (
  1288. markerId === def.id && def.tagName === 'marker'
  1289. ) {
  1290. predefinedMarker = def;
  1291. break;
  1292. }
  1293. }
  1294. if (predefinedMarker) {
  1295. marker = item[markerType] = chart.renderer
  1296. .addMarker(
  1297. (itemOptions.id || H.uniqueKey()) + '-' +
  1298. predefinedMarker.id,
  1299. H.merge(predefinedMarker, { color: color })
  1300. );
  1301. item.attr(markerType, marker.attr('id'));
  1302. }
  1303. }
  1304. };
  1305. ['markerStart', 'markerEnd'].forEach(setMarker);
  1306. }
  1307. };
  1308. // In a styled mode definition is implemented
  1309. H.SVGRenderer.prototype.definition = function (def) {
  1310. var ren = this;
  1311. function recurse(config, parent) {
  1312. var ret;
  1313. splat(config).forEach(function (item) {
  1314. var node = ren.createElement(item.tagName),
  1315. attr = {};
  1316. // Set attributes
  1317. objectEach(item, function (val, key) {
  1318. if (
  1319. key !== 'tagName' &&
  1320. key !== 'children' &&
  1321. key !== 'textContent'
  1322. ) {
  1323. attr[key] = val;
  1324. }
  1325. });
  1326. node.attr(attr);
  1327. // Add to the tree
  1328. node.add(parent || ren.defs);
  1329. // Add text content
  1330. if (item.textContent) {
  1331. node.element.appendChild(
  1332. H.doc.createTextNode(item.textContent)
  1333. );
  1334. }
  1335. // Recurse
  1336. recurse(item.children || [], node);
  1337. ret = node;
  1338. });
  1339. // Return last node added (on top level it's the only one)
  1340. return ret;
  1341. }
  1342. return recurse(def);
  1343. };
  1344. H.addEvent(H.Chart, 'afterGetContainer', function () {
  1345. this.options.defs = H.merge(defaultMarkers, this.options.defs || {});
  1346. objectEach(this.options.defs, function (def) {
  1347. if (def.tagName === 'marker' && def.render !== false) {
  1348. this.renderer.addMarker(def.id, def);
  1349. }
  1350. }, this);
  1351. });
  1352. return markerMixin;
  1353. });
  1354. _registerModule(_modules, 'annotations/controllable/ControllablePath.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['annotations/controllable/controllableMixin.js'], _modules['annotations/controllable/markerMixin.js']], function (H, U, controllableMixin, markerMixin) {
  1355. var extend = U.extend;
  1356. // See TRACKER_FILL in highcharts.src.js
  1357. var TRACKER_FILL = 'rgba(192,192,192,' + (H.svg ? 0.0001 : 0.002) + ')';
  1358. /**
  1359. * A controllable path class.
  1360. *
  1361. * @class
  1362. * @mixes Annotation.controllableMixin
  1363. * @mixes Annotation.markerMixin
  1364. * @memberOf Annotation
  1365. *
  1366. * @param {Highcharts.Annotation}
  1367. * @param {Object} options a path's options object
  1368. * @param {number} index of the path
  1369. **/
  1370. function ControllablePath(annotation, options, index) {
  1371. this.init(annotation, options, index);
  1372. this.collection = 'shapes';
  1373. }
  1374. /**
  1375. * @typedef {Object} Annotation.ControllablePath.AttrsMap
  1376. * @property {string} dashStyle=dashstyle
  1377. * @property {string} strokeWidth=stroke-width
  1378. * @property {string} stroke=stroke
  1379. * @property {string} fill=fill
  1380. * @property {string} zIndex=zIndex
  1381. */
  1382. /**
  1383. * A map object which allows to map options attributes to element attributes
  1384. *
  1385. * @type {Annotation.ControllablePath.AttrsMap}
  1386. */
  1387. ControllablePath.attrsMap = {
  1388. dashStyle: 'dashstyle',
  1389. strokeWidth: 'stroke-width',
  1390. stroke: 'stroke',
  1391. fill: 'fill',
  1392. zIndex: 'zIndex'
  1393. };
  1394. H.merge(
  1395. true,
  1396. ControllablePath.prototype,
  1397. controllableMixin, /** @lends Annotation.ControllablePath# */ {
  1398. /**
  1399. * @type 'path'
  1400. */
  1401. type: 'path',
  1402. setMarkers: markerMixin.setItemMarkers,
  1403. /**
  1404. * Map the controllable path to 'd' path attribute
  1405. *
  1406. * @return {Array<(string|number)>} a path's d attribute
  1407. */
  1408. toD: function () {
  1409. var d = this.options.d;
  1410. if (d) {
  1411. return typeof d === 'function' ?
  1412. d.call(this) :
  1413. d;
  1414. }
  1415. var points = this.points,
  1416. len = points.length,
  1417. showPath = len,
  1418. point = points[0],
  1419. position = showPath && this.anchor(point).absolutePosition,
  1420. pointIndex = 0,
  1421. dIndex = 2,
  1422. command;
  1423. d = position && ['M', position.x, position.y];
  1424. while (++pointIndex < len && showPath) {
  1425. point = points[pointIndex];
  1426. command = point.command || 'L';
  1427. position = this.anchor(point).absolutePosition;
  1428. if (command === 'Z') {
  1429. d[++dIndex] = command;
  1430. } else {
  1431. if (command !== points[pointIndex - 1].command) {
  1432. d[++dIndex] = command;
  1433. }
  1434. d[++dIndex] = position.x;
  1435. d[++dIndex] = position.y;
  1436. }
  1437. showPath = point.series.visible;
  1438. }
  1439. return showPath ?
  1440. this.chart.renderer.crispLine(d, this.graphic.strokeWidth()) :
  1441. null;
  1442. },
  1443. shouldBeDrawn: function () {
  1444. return controllableMixin.shouldBeDrawn.call(this) ||
  1445. Boolean(this.options.d);
  1446. },
  1447. render: function (parent) {
  1448. var options = this.options,
  1449. attrs = this.attrsFromOptions(options);
  1450. this.graphic = this.annotation.chart.renderer
  1451. .path(['M', 0, 0])
  1452. .attr(attrs)
  1453. .add(parent);
  1454. if (options.className) {
  1455. this.graphic.addClass(options.className);
  1456. }
  1457. this.tracker = this.annotation.chart.renderer
  1458. .path(['M', 0, 0])
  1459. .addClass('highcharts-tracker-line')
  1460. .attr({
  1461. zIndex: 2
  1462. })
  1463. .add(parent);
  1464. if (!this.annotation.chart.styledMode) {
  1465. this.tracker.attr({
  1466. 'stroke-linejoin': 'round', // #1225
  1467. stroke: TRACKER_FILL,
  1468. fill: TRACKER_FILL,
  1469. 'stroke-width': this.graphic.strokeWidth() +
  1470. options.snap * 2
  1471. });
  1472. }
  1473. controllableMixin.render.call(this);
  1474. extend(this.graphic, {
  1475. markerStartSetter: markerMixin.markerStartSetter,
  1476. markerEndSetter: markerMixin.markerEndSetter
  1477. });
  1478. this.setMarkers(this);
  1479. },
  1480. redraw: function (animation) {
  1481. var d = this.toD(),
  1482. action = animation ? 'animate' : 'attr';
  1483. if (d) {
  1484. this.graphic[action]({ d: d });
  1485. this.tracker[action]({ d: d });
  1486. } else {
  1487. this.graphic.attr({ d: 'M 0 ' + -9e9 });
  1488. this.tracker.attr({ d: 'M 0 ' + -9e9 });
  1489. }
  1490. this.graphic.placed = this.tracker.placed = Boolean(d);
  1491. controllableMixin.redraw.call(this, animation);
  1492. }
  1493. }
  1494. );
  1495. return ControllablePath;
  1496. });
  1497. _registerModule(_modules, 'annotations/controllable/ControllableRect.js', [_modules['parts/Globals.js'], _modules['annotations/controllable/controllableMixin.js'], _modules['annotations/controllable/ControllablePath.js']], function (H, controllableMixin, ControllablePath) {
  1498. /**
  1499. * A controllable rect class.
  1500. *
  1501. * @class
  1502. * @mixes Annotation.controllableMixin
  1503. * @memberOf Annotation
  1504. *
  1505. * @param {Highcharts.Annotation} annotation an annotation instance
  1506. * @param {Object} options a rect's options
  1507. * @param {number} index of the rectangle
  1508. **/
  1509. function ControllableRect(annotation, options, index) {
  1510. this.init(annotation, options, index);
  1511. this.collection = 'shapes';
  1512. }
  1513. /**
  1514. * @typedef {Annotation.ControllablePath.AttrsMap}
  1515. * Annotation.ControllableRect.AttrsMap
  1516. * @property {string} width=width
  1517. * @property {string} height=height
  1518. */
  1519. /**
  1520. * A map object which allows to map options attributes to element attributes
  1521. *
  1522. * @type {Annotation.ControllableRect.AttrsMap}
  1523. */
  1524. ControllableRect.attrsMap = H.merge(ControllablePath.attrsMap, {
  1525. width: 'width',
  1526. height: 'height'
  1527. });
  1528. H.merge(
  1529. true,
  1530. ControllableRect.prototype,
  1531. controllableMixin, /** @lends Annotation.ControllableRect# */ {
  1532. /**
  1533. * @type 'rect'
  1534. */
  1535. type: 'rect',
  1536. translate: controllableMixin.translateShape,
  1537. render: function (parent) {
  1538. var attrs = this.attrsFromOptions(this.options);
  1539. this.graphic = this.annotation.chart.renderer
  1540. .rect(0, -9e9, 0, 0)
  1541. .attr(attrs)
  1542. .add(parent);
  1543. controllableMixin.render.call(this);
  1544. },
  1545. redraw: function (animation) {
  1546. var position = this.anchor(this.points[0]).absolutePosition;
  1547. if (position) {
  1548. this.graphic[animation ? 'animate' : 'attr']({
  1549. x: position.x,
  1550. y: position.y,
  1551. width: this.options.width,
  1552. height: this.options.height
  1553. });
  1554. } else {
  1555. this.attr({
  1556. x: 0,
  1557. y: -9e9
  1558. });
  1559. }
  1560. this.graphic.placed = Boolean(position);
  1561. controllableMixin.redraw.call(this, animation);
  1562. }
  1563. }
  1564. );
  1565. return ControllableRect;
  1566. });
  1567. _registerModule(_modules, 'annotations/controllable/ControllableCircle.js', [_modules['parts/Globals.js'], _modules['annotations/controllable/controllableMixin.js'], _modules['annotations/controllable/ControllablePath.js']], function (H, controllableMixin, ControllablePath) {
  1568. /**
  1569. * A controllable circle class.
  1570. *
  1571. * @constructor
  1572. * @mixes Annotation.controllableMixin
  1573. * @memberOf Annotation
  1574. *
  1575. * @param {Highcharts.Annotation} annotation an annotation instance
  1576. * @param {Object} options a shape's options
  1577. * @param {number} index of the circle
  1578. **/
  1579. function ControllableCircle(annotation, options, index) {
  1580. this.init(annotation, options, index);
  1581. this.collection = 'shapes';
  1582. }
  1583. /**
  1584. * A map object which allows to map options attributes to element attributes.
  1585. */
  1586. ControllableCircle.attrsMap = H.merge(ControllablePath.attrsMap, {
  1587. r: 'r'
  1588. });
  1589. H.merge(
  1590. true,
  1591. ControllableCircle.prototype,
  1592. controllableMixin, /** @lends Annotation.ControllableCircle# */ {
  1593. /**
  1594. * @type 'circle'
  1595. */
  1596. type: 'circle',
  1597. translate: controllableMixin.translateShape,
  1598. render: function (parent) {
  1599. var attrs = this.attrsFromOptions(this.options);
  1600. this.graphic = this.annotation.chart.renderer
  1601. .circle(0, -9e9, 0)
  1602. .attr(attrs)
  1603. .add(parent);
  1604. controllableMixin.render.call(this);
  1605. },
  1606. redraw: function (animation) {
  1607. var position = this.anchor(this.points[0]).absolutePosition;
  1608. if (position) {
  1609. this.graphic[animation ? 'animate' : 'attr']({
  1610. x: position.x,
  1611. y: position.y,
  1612. r: this.options.r
  1613. });
  1614. } else {
  1615. this.graphic.attr({
  1616. x: 0,
  1617. y: -9e9
  1618. });
  1619. }
  1620. this.graphic.placed = Boolean(position);
  1621. controllableMixin.redraw.call(this, animation);
  1622. },
  1623. /**
  1624. * Set the radius.
  1625. *
  1626. * @param {number} r a radius to be set
  1627. */
  1628. setRadius: function (r) {
  1629. this.options.r = r;
  1630. }
  1631. }
  1632. );
  1633. return ControllableCircle;
  1634. });
  1635. _registerModule(_modules, 'annotations/controllable/ControllableLabel.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['annotations/controllable/controllableMixin.js'], _modules['annotations/MockPoint.js']], function (H, U, controllableMixin, MockPoint) {
  1636. var extend = U.extend,
  1637. isNumber = U.isNumber,
  1638. pick = U.pick;
  1639. /**
  1640. * @private
  1641. * @interface Highcharts.AnnotationAnchorObject
  1642. *//**
  1643. * Relative to the plot area position
  1644. * @name Highcharts.AnnotationAnchorObject#relativePosition
  1645. * @type {Highcharts.AnnotationAnchorPositionObject}
  1646. *//**
  1647. * Absolute position
  1648. * @name Highcharts.AnnotationAnchorObject#absolutePosition
  1649. * @type {Highcharts.AnnotationAnchorPositionObject}
  1650. */
  1651. /**
  1652. * An object which denotes an anchor position
  1653. *
  1654. * @private
  1655. * @interface Highcharts.AnnotationAnchorPositionObject
  1656. *//**
  1657. * @name Highcharts.AnnotationAnchorPositionObject#x
  1658. * @property {number}
  1659. *//**
  1660. * @name Highcharts.AnnotationAnchorPositionObject#y
  1661. * @property {number}
  1662. *//**
  1663. * @name Highcharts.AnnotationAnchorPositionObject#height
  1664. * @property {number}
  1665. *//**
  1666. * @name Highcharts.AnnotationAnchorPositionObject#width
  1667. * @property {number}
  1668. */
  1669. /**
  1670. * A controllable label class.
  1671. *
  1672. * @private
  1673. * @class
  1674. * @name Annotation.ControllableLabel
  1675. *
  1676. * @mixes Annotation.controllableMixin
  1677. *
  1678. * @param {Highcharts.Annotation} annotation an annotation instance
  1679. * @param {object} options a label's options
  1680. * @param {number} index of the label
  1681. **/
  1682. function ControllableLabel(annotation, options, index) {
  1683. this.init(annotation, options, index);
  1684. this.collection = 'labels';
  1685. }
  1686. /**
  1687. * Shapes which do not have background - the object is used for proper
  1688. * setting of the contrast color.
  1689. *
  1690. * @type {Array<string>}
  1691. */
  1692. ControllableLabel.shapesWithoutBackground = ['connector'];
  1693. /**
  1694. * Returns new aligned position based alignment options and box to align to.
  1695. * It is almost a one-to-one copy from SVGElement.prototype.align
  1696. * except it does not use and mutate an element
  1697. *
  1698. * @param {Object} alignOptions
  1699. * @param {Object} box
  1700. * @return {Annotation.controllableMixin.Position} aligned position
  1701. */
  1702. ControllableLabel.alignedPosition = function (alignOptions, box) {
  1703. var align = alignOptions.align,
  1704. vAlign = alignOptions.verticalAlign,
  1705. x = (box.x || 0) + (alignOptions.x || 0),
  1706. y = (box.y || 0) + (alignOptions.y || 0),
  1707. alignFactor,
  1708. vAlignFactor;
  1709. if (align === 'right') {
  1710. alignFactor = 1;
  1711. } else if (align === 'center') {
  1712. alignFactor = 2;
  1713. }
  1714. if (alignFactor) {
  1715. x += (box.width - (alignOptions.width || 0)) / alignFactor;
  1716. }
  1717. if (vAlign === 'bottom') {
  1718. vAlignFactor = 1;
  1719. } else if (vAlign === 'middle') {
  1720. vAlignFactor = 2;
  1721. }
  1722. if (vAlignFactor) {
  1723. y += (box.height - (alignOptions.height || 0)) / vAlignFactor;
  1724. }
  1725. return {
  1726. x: Math.round(x),
  1727. y: Math.round(y)
  1728. };
  1729. };
  1730. /**
  1731. * Returns new alignment options for a label if the label is outside the
  1732. * plot area. It is almost a one-to-one copy from
  1733. * Series.prototype.justifyDataLabel except it does not mutate the label and
  1734. * it works with absolute instead of relative position.
  1735. *
  1736. * @param {Object} label
  1737. * @param {Object} alignOptions
  1738. * @param {Object} alignAttr
  1739. * @return {Object} justified options
  1740. **/
  1741. ControllableLabel.justifiedOptions = function (
  1742. chart,
  1743. label,
  1744. alignOptions,
  1745. alignAttr
  1746. ) {
  1747. var align = alignOptions.align,
  1748. verticalAlign = alignOptions.verticalAlign,
  1749. padding = label.box ? 0 : (label.padding || 0),
  1750. bBox = label.getBBox(),
  1751. off,
  1752. options = {
  1753. align: align,
  1754. verticalAlign: verticalAlign,
  1755. x: alignOptions.x,
  1756. y: alignOptions.y,
  1757. width: label.width,
  1758. height: label.height
  1759. },
  1760. x = alignAttr.x - chart.plotLeft,
  1761. y = alignAttr.y - chart.plotTop;
  1762. // Off left
  1763. off = x + padding;
  1764. if (off < 0) {
  1765. if (align === 'right') {
  1766. options.align = 'left';
  1767. } else {
  1768. options.x = -off;
  1769. }
  1770. }
  1771. // Off right
  1772. off = x + bBox.width - padding;
  1773. if (off > chart.plotWidth) {
  1774. if (align === 'left') {
  1775. options.align = 'right';
  1776. } else {
  1777. options.x = chart.plotWidth - off;
  1778. }
  1779. }
  1780. // Off top
  1781. off = y + padding;
  1782. if (off < 0) {
  1783. if (verticalAlign === 'bottom') {
  1784. options.verticalAlign = 'top';
  1785. } else {
  1786. options.y = -off;
  1787. }
  1788. }
  1789. // Off bottom
  1790. off = y + bBox.height - padding;
  1791. if (off > chart.plotHeight) {
  1792. if (verticalAlign === 'top') {
  1793. options.verticalAlign = 'bottom';
  1794. } else {
  1795. options.y = chart.plotHeight - off;
  1796. }
  1797. }
  1798. return options;
  1799. };
  1800. /**
  1801. * @typedef {Object} Annotation.ControllableLabel.AttrsMap
  1802. * @property {string} backgroundColor=fill
  1803. * @property {string} borderColor=stroke
  1804. * @property {string} borderWidth=stroke-width
  1805. * @property {string} zIndex=zIndex
  1806. * @property {string} borderRadius=r
  1807. * @property {string} padding=padding
  1808. */
  1809. /**
  1810. * A map object which allows to map options attributes to element attributes
  1811. *
  1812. * @type {Annotation.ControllableLabel.AttrsMap}
  1813. */
  1814. ControllableLabel.attrsMap = {
  1815. backgroundColor: 'fill',
  1816. borderColor: 'stroke',
  1817. borderWidth: 'stroke-width',
  1818. zIndex: 'zIndex',
  1819. borderRadius: 'r',
  1820. padding: 'padding'
  1821. };
  1822. H.merge(
  1823. true,
  1824. ControllableLabel.prototype,
  1825. controllableMixin, /** @lends Annotation.ControllableLabel# */ {
  1826. /**
  1827. * Translate the point of the label by deltaX and deltaY translations.
  1828. * The point is the label's anchor.
  1829. *
  1830. * @param {number} dx translation for x coordinate
  1831. * @param {number} dy translation for y coordinate
  1832. **/
  1833. translatePoint: function (dx, dy) {
  1834. controllableMixin.translatePoint.call(this, dx, dy, 0);
  1835. },
  1836. /**
  1837. * Translate x and y position relative to the label's anchor.
  1838. *
  1839. * @param {number} dx translation for x coordinate
  1840. * @param {number} dy translation for y coordinate
  1841. **/
  1842. translate: function (dx, dy) {
  1843. var chart = this.annotation.chart,
  1844. // Annotation.options
  1845. labelOptions = this.annotation.userOptions,
  1846. // Chart.options.annotations
  1847. annotationIndex = chart.annotations.indexOf(this.annotation),
  1848. chartAnnotations = chart.options.annotations,
  1849. chartOptions = chartAnnotations[annotationIndex],
  1850. temp;
  1851. if (chart.inverted) {
  1852. temp = dx;
  1853. dx = dy;
  1854. dy = temp;
  1855. }
  1856. // Local options:
  1857. this.options.x += dx;
  1858. this.options.y += dy;
  1859. // Options stored in chart:
  1860. chartOptions[this.collection][this.index].x = this.options.x;
  1861. chartOptions[this.collection][this.index].y = this.options.y;
  1862. labelOptions[this.collection][this.index].x = this.options.x;
  1863. labelOptions[this.collection][this.index].y = this.options.y;
  1864. },
  1865. render: function (parent) {
  1866. var options = this.options,
  1867. attrs = this.attrsFromOptions(options),
  1868. style = options.style;
  1869. this.graphic = this.annotation.chart.renderer
  1870. .label(
  1871. '',
  1872. 0,
  1873. -9999, // #10055
  1874. options.shape,
  1875. null,
  1876. null,
  1877. options.useHTML,
  1878. null,
  1879. 'annotation-label'
  1880. )
  1881. .attr(attrs)
  1882. .add(parent);
  1883. if (!this.annotation.chart.styledMode) {
  1884. if (style.color === 'contrast') {
  1885. style.color = this.annotation.chart.renderer.getContrast(
  1886. ControllableLabel.shapesWithoutBackground.indexOf(
  1887. options.shape
  1888. ) > -1 ? '#FFFFFF' : options.backgroundColor
  1889. );
  1890. }
  1891. this.graphic
  1892. .css(options.style)
  1893. .shadow(options.shadow);
  1894. }
  1895. if (options.className) {
  1896. this.graphic.addClass(options.className);
  1897. }
  1898. this.graphic.labelrank = options.labelrank;
  1899. controllableMixin.render.call(this);
  1900. },
  1901. redraw: function (animation) {
  1902. var options = this.options,
  1903. text = this.text || options.format || options.text,
  1904. label = this.graphic,
  1905. point = this.points[0],
  1906. show = false,
  1907. anchor,
  1908. attrs;
  1909. label.attr({
  1910. text: text ?
  1911. H.format(
  1912. text,
  1913. point.getLabelConfig(),
  1914. this.annotation.chart
  1915. ) :
  1916. options.formatter.call(point, this)
  1917. });
  1918. anchor = this.anchor(point);
  1919. attrs = this.position(anchor);
  1920. show = attrs;
  1921. if (show) {
  1922. label.alignAttr = attrs;
  1923. attrs.anchorX = anchor.absolutePosition.x;
  1924. attrs.anchorY = anchor.absolutePosition.y;
  1925. label[animation ? 'animate' : 'attr'](attrs);
  1926. } else {
  1927. label.attr({
  1928. x: 0,
  1929. y: -9999 // #10055
  1930. });
  1931. }
  1932. label.placed = Boolean(show);
  1933. controllableMixin.redraw.call(this, animation);
  1934. },
  1935. /**
  1936. * All basic shapes don't support alignTo() method except label.
  1937. * For a controllable label, we need to subtract translation from
  1938. * options.
  1939. */
  1940. anchor: function () {
  1941. var anchor = controllableMixin.anchor.apply(this, arguments),
  1942. x = this.options.x || 0,
  1943. y = this.options.y || 0;
  1944. anchor.absolutePosition.x -= x;
  1945. anchor.absolutePosition.y -= y;
  1946. anchor.relativePosition.x -= x;
  1947. anchor.relativePosition.y -= y;
  1948. return anchor;
  1949. },
  1950. /**
  1951. * Returns the label position relative to its anchor.
  1952. *
  1953. * @param {Highcharts.AnnotationAnchorObject} anchor
  1954. *
  1955. * @return {Highcharts.AnnotationAnchorPositionObject|null} position
  1956. */
  1957. position: function (anchor) {
  1958. var item = this.graphic,
  1959. chart = this.annotation.chart,
  1960. point = this.points[0],
  1961. itemOptions = this.options,
  1962. anchorAbsolutePosition = anchor.absolutePosition,
  1963. anchorRelativePosition = anchor.relativePosition,
  1964. itemPosition,
  1965. alignTo,
  1966. itemPosRelativeX,
  1967. itemPosRelativeY,
  1968. showItem =
  1969. point.series.visible &&
  1970. MockPoint.prototype.isInsidePane.call(point);
  1971. if (showItem) {
  1972. if (itemOptions.distance) {
  1973. itemPosition = H.Tooltip.prototype.getPosition.call(
  1974. {
  1975. chart: chart,
  1976. distance: pick(itemOptions.distance, 16)
  1977. },
  1978. item.width,
  1979. item.height,
  1980. {
  1981. plotX: anchorRelativePosition.x,
  1982. plotY: anchorRelativePosition.y,
  1983. negative: point.negative,
  1984. ttBelow: point.ttBelow,
  1985. h: anchorRelativePosition.height ||
  1986. anchorRelativePosition.width
  1987. }
  1988. );
  1989. } else if (itemOptions.positioner) {
  1990. itemPosition = itemOptions.positioner.call(this);
  1991. } else {
  1992. alignTo = {
  1993. x: anchorAbsolutePosition.x,
  1994. y: anchorAbsolutePosition.y,
  1995. width: 0,
  1996. height: 0
  1997. };
  1998. itemPosition = ControllableLabel.alignedPosition(
  1999. extend(itemOptions, {
  2000. width: item.width,
  2001. height: item.height
  2002. }),
  2003. alignTo
  2004. );
  2005. if (this.options.overflow === 'justify') {
  2006. itemPosition = ControllableLabel.alignedPosition(
  2007. ControllableLabel.justifiedOptions(
  2008. chart,
  2009. item,
  2010. itemOptions,
  2011. itemPosition
  2012. ),
  2013. alignTo
  2014. );
  2015. }
  2016. }
  2017. if (itemOptions.crop) {
  2018. itemPosRelativeX = itemPosition.x - chart.plotLeft;
  2019. itemPosRelativeY = itemPosition.y - chart.plotTop;
  2020. showItem =
  2021. chart.isInsidePlot(
  2022. itemPosRelativeX,
  2023. itemPosRelativeY
  2024. ) &&
  2025. chart.isInsidePlot(
  2026. itemPosRelativeX + item.width,
  2027. itemPosRelativeY + item.height
  2028. );
  2029. }
  2030. }
  2031. return showItem ? itemPosition : null;
  2032. }
  2033. }
  2034. );
  2035. /* ********************************************************************** */
  2036. /**
  2037. * General symbol definition for labels with connector
  2038. * @private
  2039. */
  2040. H.SVGRenderer.prototype.symbols.connector = function (x, y, w, h, options) {
  2041. var anchorX = options && options.anchorX,
  2042. anchorY = options && options.anchorY,
  2043. path,
  2044. yOffset,
  2045. lateral = w / 2;
  2046. if (isNumber(anchorX) && isNumber(anchorY)) {
  2047. path = ['M', anchorX, anchorY];
  2048. // Prefer 45 deg connectors
  2049. yOffset = y - anchorY;
  2050. if (yOffset < 0) {
  2051. yOffset = -h - yOffset;
  2052. }
  2053. if (yOffset < w) {
  2054. lateral = anchorX < x + (w / 2) ? yOffset : w - yOffset;
  2055. }
  2056. // Anchor below label
  2057. if (anchorY > y + h) {
  2058. path.push('L', x + lateral, y + h);
  2059. // Anchor above label
  2060. } else if (anchorY < y) {
  2061. path.push('L', x + lateral, y);
  2062. // Anchor left of label
  2063. } else if (anchorX < x) {
  2064. path.push('L', x, y + h / 2);
  2065. // Anchor right of label
  2066. } else if (anchorX > x + w) {
  2067. path.push('L', x + w, y + h / 2);
  2068. }
  2069. }
  2070. return path || [];
  2071. };
  2072. return ControllableLabel;
  2073. });
  2074. _registerModule(_modules, 'annotations/controllable/ControllableImage.js', [_modules['parts/Globals.js'], _modules['annotations/controllable/controllableMixin.js'], _modules['annotations/controllable/ControllableLabel.js']], function (H, controllableMixin, ControllableLabel) {
  2075. /**
  2076. * A controllable image class.
  2077. *
  2078. * @class
  2079. * @mixes Annotation.controllableMixin
  2080. * @memberOf Annotation
  2081. *
  2082. * @param {Highcharts.Annotation} annotation - an annotation instance
  2083. * @param {Object} options a controllable's options
  2084. * @param {number} index of the image
  2085. **/
  2086. function ControllableImage(annotation, options, index) {
  2087. this.init(annotation, options, index);
  2088. this.collection = 'shapes';
  2089. }
  2090. /**
  2091. * @typedef {Object} Annotation.ControllableImage.AttrsMap
  2092. * @property {string} width=width
  2093. * @property {string} height=height
  2094. * @property {string} zIndex=zIndex
  2095. */
  2096. /**
  2097. * A map object which allows to map options attributes to element attributes
  2098. *
  2099. * @type {Annotation.ControllableImage.AttrsMap}
  2100. */
  2101. ControllableImage.attrsMap = {
  2102. width: 'width',
  2103. height: 'height',
  2104. zIndex: 'zIndex'
  2105. };
  2106. H.merge(
  2107. true,
  2108. ControllableImage.prototype,
  2109. controllableMixin, /** @lends Annotation.ControllableImage# */ {
  2110. /**
  2111. * @type 'image'
  2112. */
  2113. type: 'image',
  2114. translate: controllableMixin.translateShape,
  2115. render: function (parent) {
  2116. var attrs = this.attrsFromOptions(this.options),
  2117. options = this.options;
  2118. this.graphic = this.annotation.chart.renderer
  2119. .image(options.src, 0, -9e9, options.width, options.height)
  2120. .attr(attrs)
  2121. .add(parent);
  2122. this.graphic.width = options.width;
  2123. this.graphic.height = options.height;
  2124. controllableMixin.render.call(this);
  2125. },
  2126. redraw: function (animation) {
  2127. var anchor = this.anchor(this.points[0]),
  2128. position = ControllableLabel.prototype.position.call(
  2129. this,
  2130. anchor
  2131. );
  2132. if (position) {
  2133. this.graphic[animation ? 'animate' : 'attr']({
  2134. x: position.x,
  2135. y: position.y
  2136. });
  2137. } else {
  2138. this.graphic.attr({
  2139. x: 0,
  2140. y: -9e9
  2141. });
  2142. }
  2143. this.graphic.placed = Boolean(position);
  2144. controllableMixin.redraw.call(this, animation);
  2145. }
  2146. }
  2147. );
  2148. return ControllableImage;
  2149. });
  2150. _registerModule(_modules, 'annotations/annotations.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['annotations/controllable/controllableMixin.js'], _modules['annotations/controllable/ControllableRect.js'], _modules['annotations/controllable/ControllableCircle.js'], _modules['annotations/controllable/ControllablePath.js'], _modules['annotations/controllable/ControllableImage.js'], _modules['annotations/controllable/ControllableLabel.js'], _modules['annotations/eventEmitterMixin.js'], _modules['annotations/MockPoint.js'], _modules['annotations/ControlPoint.js']], function (H, U, controllableMixin, ControllableRect, ControllableCircle, ControllablePath, ControllableImage, ControllableLabel, eventEmitterMixin, MockPoint, ControlPoint) {
  2151. /* *
  2152. *
  2153. * (c) 2009-2017 Highsoft, Black Label
  2154. *
  2155. * License: www.highcharts.com/license
  2156. *
  2157. * */
  2158. var defined = U.defined,
  2159. destroyObjectProperties = U.destroyObjectProperties,
  2160. erase = U.erase,
  2161. extend = U.extend,
  2162. pick = U.pick,
  2163. splat = U.splat,
  2164. wrap = U.wrap;
  2165. var merge = H.merge,
  2166. addEvent = H.addEvent,
  2167. fireEvent = H.fireEvent,
  2168. find = H.find,
  2169. reduce = H.reduce,
  2170. chartProto = H.Chart.prototype;
  2171. /* *********************************************************************
  2172. *
  2173. * ANNOTATION
  2174. *
  2175. ******************************************************************** */
  2176. /**
  2177. * @typedef {
  2178. * Annotation.ControllableCircle|
  2179. * Annotation.ControllableImage|
  2180. * Annotation.ControllablePath|
  2181. * Annotation.ControllableRect
  2182. * }
  2183. * Annotation.Shape
  2184. */
  2185. /**
  2186. * @typedef {Annotation.ControllableLabel} Annotation.Label
  2187. */
  2188. /**
  2189. * An annotation class which serves as a container for items like labels or
  2190. * shapes. Created items are positioned on the chart either by linking them to
  2191. * existing points or created mock points
  2192. *
  2193. * @class
  2194. * @name Highcharts.Annotation
  2195. *
  2196. * @param {Highcharts.Chart} chart a chart instance
  2197. * @param {Highcharts.AnnotationsOptions} userOptions the options object
  2198. */
  2199. var Annotation = H.Annotation = function (chart, userOptions) {
  2200. var labelsAndShapes;
  2201. /**
  2202. * The chart that the annotation belongs to.
  2203. *
  2204. * @type {Highcharts.Chart}
  2205. */
  2206. this.chart = chart;
  2207. /**
  2208. * The array of points which defines the annotation.
  2209. *
  2210. * @type {Array<Highcharts.Point>}
  2211. */
  2212. this.points = [];
  2213. /**
  2214. * The array of control points.
  2215. *
  2216. * @type {Array<Annotation.ControlPoint>}
  2217. */
  2218. this.controlPoints = [];
  2219. this.coll = 'annotations';
  2220. /**
  2221. * The array of labels which belong to the annotation.
  2222. *
  2223. * @type {Array<Annotation.Label>}
  2224. */
  2225. this.labels = [];
  2226. /**
  2227. * The array of shapes which belong to the annotation.
  2228. *
  2229. * @type {Array<Annotation.Shape>}
  2230. */
  2231. this.shapes = [];
  2232. /**
  2233. * The options for the annotations.
  2234. *
  2235. * @type {Highcharts.AnnotationsOptions}
  2236. */
  2237. this.options = merge(this.defaultOptions, userOptions);
  2238. /**
  2239. * The user options for the annotations.
  2240. *
  2241. * @type {Highcharts.AnnotationsOptions}
  2242. */
  2243. this.userOptions = userOptions;
  2244. // Handle labels and shapes - those are arrays
  2245. // Merging does not work with arrays (stores reference)
  2246. labelsAndShapes = this.getLabelsAndShapesOptions(
  2247. this.options,
  2248. userOptions
  2249. );
  2250. this.options.labels = labelsAndShapes.labels;
  2251. this.options.shapes = labelsAndShapes.shapes;
  2252. /**
  2253. * The callback that reports to the overlapping-labels module which
  2254. * labels it should account for.
  2255. *
  2256. * @name labelCollector
  2257. * @memberOf Annotation#
  2258. * @type {Function}
  2259. */
  2260. /**
  2261. * The group svg element.
  2262. *
  2263. * @name group
  2264. * @memberOf Annotation#
  2265. * @type {Highcharts.SVGElement}
  2266. */
  2267. /**
  2268. * The group svg element of the annotation's shapes.
  2269. *
  2270. * @name shapesGroup
  2271. * @memberOf Annotation#
  2272. * @type {Highcharts.SVGElement}
  2273. */
  2274. /**
  2275. * The group svg element of the annotation's labels.
  2276. *
  2277. * @name labelsGroup
  2278. * @memberOf Annotation#
  2279. * @type {Highcharts.SVGElement}
  2280. */
  2281. this.init(chart, this.options);
  2282. };
  2283. merge(
  2284. true,
  2285. Annotation.prototype,
  2286. controllableMixin,
  2287. eventEmitterMixin,
  2288. /** @lends Annotation# */
  2289. {
  2290. /**
  2291. * List of events for `annotation.options.events` that should not be
  2292. * added to `annotation.graphic` but to the `annotation`.
  2293. *
  2294. * @type {Array<string>}
  2295. */
  2296. nonDOMEvents: ['add', 'afterUpdate', 'drag', 'remove'],
  2297. /**
  2298. * A basic type of an annotation. It allows to add custom labels
  2299. * or shapes. The items can be tied to points, axis coordinates
  2300. * or chart pixel coordinates.
  2301. *
  2302. * @sample highcharts/annotations/basic/
  2303. * Basic annotations
  2304. * @sample highcharts/demo/annotations/
  2305. * Advanced annotations
  2306. * @sample highcharts/css/annotations
  2307. * Styled mode
  2308. * @sample highcharts/annotations-advanced/controllable
  2309. * Controllable items
  2310. * @sample {highstock} stock/annotations/fibonacci-retracements
  2311. * Custom annotation, Fibonacci retracement
  2312. *
  2313. * @type {Array<*>}
  2314. * @since 6.0.0
  2315. * @requires modules/annotations
  2316. * @optionparent annotations
  2317. */
  2318. defaultOptions: {
  2319. /**
  2320. * Sets an ID for an annotation. Can be user later when removing an
  2321. * annotation in [Chart#removeAnnotation(id)](
  2322. * /class-reference/Highcharts.Chart#removeAnnotation) method.
  2323. *
  2324. * @type {string|number}
  2325. * @apioption annotations.id
  2326. */
  2327. /**
  2328. * Whether the annotation is visible.
  2329. *
  2330. * @sample highcharts/annotations/visible/
  2331. * Set annotation visibility
  2332. */
  2333. visible: true,
  2334. /**
  2335. * Allow an annotation to be draggable by a user. Possible
  2336. * values are `"x"`, `"xy"`, `"y"` and `""` (disabled).
  2337. *
  2338. * @sample highcharts/annotations/draggable/
  2339. * Annotations draggable: 'xy'
  2340. *
  2341. * @type {string}
  2342. * @validvalue ["x", "xy", "y", ""]
  2343. */
  2344. draggable: 'xy',
  2345. /**
  2346. * Options for annotation's labels. Each label inherits options
  2347. * from the labelOptions object. An option from the labelOptions
  2348. * can be overwritten by config for a specific label.
  2349. *
  2350. * @requires modules/annotations
  2351. */
  2352. labelOptions: {
  2353. /**
  2354. * The alignment of the annotation's label. If right,
  2355. * the right side of the label should be touching the point.
  2356. *
  2357. * @sample highcharts/annotations/label-position/
  2358. * Set labels position
  2359. *
  2360. * @type {Highcharts.AlignValue}
  2361. */
  2362. align: 'center',
  2363. /**
  2364. * Whether to allow the annotation's labels to overlap.
  2365. * To make the labels less sensitive for overlapping,
  2366. * the can be set to 0.
  2367. *
  2368. * @sample highcharts/annotations/tooltip-like/
  2369. * Hide overlapping labels
  2370. */
  2371. allowOverlap: false,
  2372. /**
  2373. * The background color or gradient for the annotation's label.
  2374. *
  2375. * @sample highcharts/annotations/label-presentation/
  2376. * Set labels graphic options
  2377. *
  2378. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  2379. */
  2380. backgroundColor: 'rgba(0, 0, 0, 0.75)',
  2381. /**
  2382. * The border color for the annotation's label.
  2383. *
  2384. * @sample highcharts/annotations/label-presentation/
  2385. * Set labels graphic options
  2386. *
  2387. * @type {Highcharts.ColorString}
  2388. */
  2389. borderColor: 'black',
  2390. /**
  2391. * The border radius in pixels for the annotaiton's label.
  2392. *
  2393. * @sample highcharts/annotations/label-presentation/
  2394. * Set labels graphic options
  2395. */
  2396. borderRadius: 3,
  2397. /**
  2398. * The border width in pixels for the annotation's label
  2399. *
  2400. * @sample highcharts/annotations/label-presentation/
  2401. * Set labels graphic options
  2402. */
  2403. borderWidth: 1,
  2404. /**
  2405. * A class name for styling by CSS.
  2406. *
  2407. * @sample highcharts/css/annotations
  2408. * Styled mode annotations
  2409. *
  2410. * @since 6.0.5
  2411. */
  2412. className: '',
  2413. /**
  2414. * Whether to hide the annotation's label
  2415. * that is outside the plot area.
  2416. *
  2417. * @sample highcharts/annotations/label-crop-overflow/
  2418. * Crop or justify labels
  2419. */
  2420. crop: false,
  2421. /**
  2422. * The label's pixel distance from the point.
  2423. *
  2424. * @sample highcharts/annotations/label-position/
  2425. * Set labels position
  2426. *
  2427. * @type {number}
  2428. * @apioption annotations.labelOptions.distance
  2429. */
  2430. /**
  2431. * A
  2432. * [format](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  2433. * string for the data label.
  2434. *
  2435. * @see [plotOptions.series.dataLabels.format](plotOptions.series.dataLabels.format.html)
  2436. *
  2437. * @sample highcharts/annotations/label-text/
  2438. * Set labels text
  2439. *
  2440. * @type {string}
  2441. * @apioption annotations.labelOptions.format
  2442. */
  2443. /**
  2444. * Alias for the format option.
  2445. *
  2446. * @see [format](annotations.labelOptions.format.html)
  2447. *
  2448. * @sample highcharts/annotations/label-text/
  2449. * Set labels text
  2450. *
  2451. * @type {string}
  2452. * @apioption annotations.labelOptions.text
  2453. */
  2454. /**
  2455. * Callback JavaScript function to format the annotation's
  2456. * label. Note that if a `format` or `text` are defined, the
  2457. * format or text take precedence and the formatter is ignored.
  2458. * `This` refers to a point object.
  2459. *
  2460. * @sample highcharts/annotations/label-text/
  2461. * Set labels text
  2462. *
  2463. * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
  2464. * @default function () { return defined(this.y) ? this.y : 'Annotation label'; }
  2465. */
  2466. formatter: function () {
  2467. return defined(this.y) ? this.y : 'Annotation label';
  2468. },
  2469. /**
  2470. * How to handle the annotation's label that flow outside the
  2471. * plot area. The justify option aligns the label inside the
  2472. * plot area.
  2473. *
  2474. * @sample highcharts/annotations/label-crop-overflow/
  2475. * Crop or justify labels
  2476. *
  2477. * @validvalue ["allow", "justify"]
  2478. */
  2479. overflow: 'justify',
  2480. /**
  2481. * When either the borderWidth or the backgroundColor is set,
  2482. * this is the padding within the box.
  2483. *
  2484. * @sample highcharts/annotations/label-presentation/
  2485. * Set labels graphic options
  2486. */
  2487. padding: 5,
  2488. /**
  2489. * The shadow of the box. The shadow can be an object
  2490. * configuration containing `color`, `offsetX`, `offsetY`,
  2491. * `opacity` and `width`.
  2492. *
  2493. * @sample highcharts/annotations/label-presentation/
  2494. * Set labels graphic options
  2495. *
  2496. * @type {boolean|Highcharts.ShadowOptionsObject}
  2497. */
  2498. shadow: false,
  2499. /**
  2500. * The name of a symbol to use for the border around the label.
  2501. * Symbols are predefined functions on the Renderer object.
  2502. *
  2503. * @sample highcharts/annotations/shapes/
  2504. * Available shapes for labels
  2505. */
  2506. shape: 'callout',
  2507. /**
  2508. * Styles for the annotation's label.
  2509. *
  2510. * @see [plotOptions.series.dataLabels.style](plotOptions.series.dataLabels.style.html)
  2511. *
  2512. * @sample highcharts/annotations/label-presentation/
  2513. * Set labels graphic options
  2514. *
  2515. * @type {Highcharts.CSSObject}
  2516. */
  2517. style: {
  2518. /** @ignore */
  2519. fontSize: '11px',
  2520. /** @ignore */
  2521. fontWeight: 'normal',
  2522. /** @ignore */
  2523. color: 'contrast'
  2524. },
  2525. /**
  2526. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  2527. * to render the annotation's label.
  2528. */
  2529. useHTML: false,
  2530. /**
  2531. * The vertical alignment of the annotation's label.
  2532. *
  2533. * @sample highcharts/annotations/label-position/
  2534. * Set labels position
  2535. *
  2536. * @type {Highcharts.VerticalAlignValue}
  2537. */
  2538. verticalAlign: 'bottom',
  2539. /**
  2540. * The x position offset of the label relative to the point.
  2541. * Note that if a `distance` is defined, the distance takes
  2542. * precedence over `x` and `y` options.
  2543. *
  2544. * @sample highcharts/annotations/label-position/
  2545. * Set labels position
  2546. */
  2547. x: 0,
  2548. /**
  2549. * The y position offset of the label relative to the point.
  2550. * Note that if a `distance` is defined, the distance takes
  2551. * precedence over `x` and `y` options.
  2552. *
  2553. * @sample highcharts/annotations/label-position/
  2554. * Set labels position
  2555. */
  2556. y: -16
  2557. },
  2558. /**
  2559. * An array of labels for the annotation. For options that apply to
  2560. * multiple labels, they can be added to the
  2561. * [labelOptions](annotations.labelOptions.html).
  2562. *
  2563. * @type {Array<*>}
  2564. * @extends annotations.labelOptions
  2565. * @apioption annotations.labels
  2566. */
  2567. /**
  2568. * This option defines the point to which the label will be
  2569. * connected. It can be either the point which exists in the
  2570. * series - it is referenced by the point's id - or a new point with
  2571. * defined x, y properties and optionally axes.
  2572. *
  2573. * @sample highcharts/annotations/mock-point/
  2574. * Attach annotation to a mock point
  2575. *
  2576. * @type {string|Highcharts.MockPointOptionsObject}
  2577. * @requires modules/annotations
  2578. * @apioption annotations.labels.point
  2579. */
  2580. /**
  2581. * The x position of the point. Units can be either in axis
  2582. * or chart pixel coordinates.
  2583. *
  2584. * @type {number}
  2585. * @apioption annotations.labels.point.x
  2586. */
  2587. /**
  2588. * The y position of the point. Units can be either in axis
  2589. * or chart pixel coordinates.
  2590. *
  2591. * @type {number}
  2592. * @apioption annotations.labels.point.y
  2593. */
  2594. /**
  2595. * This number defines which xAxis the point is connected to. It
  2596. * refers to either the axis id or the index of the axis in the
  2597. * xAxis array. If the option is not configured or the axis is not
  2598. * found the point's x coordinate refers to the chart pixels.
  2599. *
  2600. * @type {number|string}
  2601. * @apioption annotations.labels.point.xAxis
  2602. */
  2603. /**
  2604. * This number defines which yAxis the point is connected to. It
  2605. * refers to either the axis id or the index of the axis in the
  2606. * yAxis array. If the option is not configured or the axis is not
  2607. * found the point's y coordinate refers to the chart pixels.
  2608. *
  2609. * @type {number|string}
  2610. * @apioption annotations.labels.point.yAxis
  2611. */
  2612. /**
  2613. * An array of shapes for the annotation. For options that apply to
  2614. * multiple shapes, then can be added to the
  2615. * [shapeOptions](annotations.shapeOptions.html).
  2616. *
  2617. * @type {Array<*>}
  2618. * @extends annotations.shapeOptions
  2619. * @apioption annotations.shapes
  2620. */
  2621. /**
  2622. * This option defines the point to which the shape will be
  2623. * connected. It can be either the point which exists in the
  2624. * series - it is referenced by the point's id - or a new point with
  2625. * defined x, y properties and optionally axes.
  2626. *
  2627. * @type {string|Highcharts.MockPointOptionsObject}
  2628. * @extends annotations.labels.point
  2629. * @apioption annotations.shapes.point
  2630. */
  2631. /**
  2632. * An array of points for the shape. This option is available for
  2633. * shapes which can use multiple points such as path. A point can be
  2634. * either a point object or a point's id.
  2635. *
  2636. * @see [annotations.shapes.point](annotations.shapes.point.html)
  2637. *
  2638. * @type {Array<string|Highcharts.MockPointOptionsObject>}
  2639. * @extends annotations.labels.point
  2640. * @apioption annotations.shapes.points
  2641. */
  2642. /**
  2643. * Id of the marker which will be drawn at the final vertex of the
  2644. * path. Custom markers can be defined in defs property.
  2645. *
  2646. * @see [defs.markers](defs.markers.html)
  2647. *
  2648. * @sample highcharts/annotations/custom-markers/
  2649. * Define a custom marker for annotations
  2650. *
  2651. * @type {string}
  2652. * @apioption annotations.shapes.markerEnd
  2653. */
  2654. /**
  2655. * Id of the marker which will be drawn at the first vertex of the
  2656. * path. Custom markers can be defined in defs property.
  2657. *
  2658. * @see [defs.markers](defs.markers.html)
  2659. *
  2660. * @sample {highcharts} highcharts/annotations/custom-markers/
  2661. * Define a custom marker for annotations
  2662. *
  2663. * @type {string}
  2664. * @apioption annotations.shapes.markerStart
  2665. */
  2666. /**
  2667. * Options for annotation's shapes. Each shape inherits options from
  2668. * the shapeOptions object. An option from the shapeOptions can be
  2669. * overwritten by config for a specific shape.
  2670. *
  2671. * @requires modules/annotations
  2672. */
  2673. shapeOptions: {
  2674. /**
  2675. * The width of the shape.
  2676. *
  2677. * @sample highcharts/annotations/shape/
  2678. * Basic shape annotation
  2679. *
  2680. * @type {number}
  2681. * @apioption annotations.shapeOptions.width
  2682. **/
  2683. /**
  2684. * The height of the shape.
  2685. *
  2686. * @sample highcharts/annotations/shape/
  2687. * Basic shape annotation
  2688. *
  2689. * @type {number}
  2690. * @apioption annotations.shapeOptions.height
  2691. */
  2692. /**
  2693. * The type of the shape, e.g. circle or rectangle.
  2694. *
  2695. * @sample highcharts/annotations/shape/
  2696. * Basic shape annotation
  2697. *
  2698. * @type {string}
  2699. * @default 'rect'
  2700. * @apioption annotations.shapeOptions.type
  2701. */
  2702. /**
  2703. * The color of the shape's stroke.
  2704. *
  2705. * @sample highcharts/annotations/shape/
  2706. * Basic shape annotation
  2707. *
  2708. * @type {Highcharts.ColorString}
  2709. */
  2710. stroke: 'rgba(0, 0, 0, 0.75)',
  2711. /**
  2712. * The pixel stroke width of the shape.
  2713. *
  2714. * @sample highcharts/annotations/shape/
  2715. * Basic shape annotation
  2716. */
  2717. strokeWidth: 1,
  2718. /**
  2719. * The color of the shape's fill.
  2720. *
  2721. * @sample highcharts/annotations/shape/
  2722. * Basic shape annotation
  2723. *
  2724. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  2725. */
  2726. fill: 'rgba(0, 0, 0, 0.75)',
  2727. /**
  2728. * The radius of the shape.
  2729. *
  2730. * @sample highcharts/annotations/shape/
  2731. * Basic shape annotation
  2732. */
  2733. r: 0,
  2734. /**
  2735. * Defines additional snapping area around an annotation
  2736. * making this annotation to focus. Defined in pixels.
  2737. */
  2738. snap: 2
  2739. },
  2740. /**
  2741. * Options for annotation's control points. Each control point
  2742. * inherits options from controlPointOptions object.
  2743. * Options from the controlPointOptions can be overwritten
  2744. * by options in a specific control point.
  2745. *
  2746. * @type {Annotation.ControlPoint.Options}
  2747. * @requires modules/annotations
  2748. * @apioption annotations.controlPointOptions
  2749. */
  2750. controlPointOptions: {
  2751. /**
  2752. * @function {Annotation.ControlPoint.Positioner}
  2753. * @apioption annotations.controlPointOptions.positioner
  2754. */
  2755. symbol: 'circle',
  2756. width: 10,
  2757. height: 10,
  2758. style: {
  2759. stroke: 'black',
  2760. 'stroke-width': 2,
  2761. fill: 'white'
  2762. },
  2763. visible: false,
  2764. events: {}
  2765. },
  2766. /**
  2767. * Event callback when annotation is added to the chart.
  2768. *
  2769. * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>}
  2770. * @since 7.1.0
  2771. * @apioption annotations.events.add
  2772. */
  2773. /**
  2774. * Event callback when annotation is updated (e.g. drag and
  2775. * droppped or resized by control points).
  2776. *
  2777. * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>}
  2778. * @since 7.1.0
  2779. * @apioption annotations.events.afterUpdate
  2780. */
  2781. /**
  2782. * Event callback when annotation is removed from the chart.
  2783. *
  2784. * @type {Highcharts.EventCallbackFunction<Highcharts.Annotation>}
  2785. * @since 7.1.0
  2786. * @apioption annotations.events.remove
  2787. */
  2788. /**
  2789. * Events available in annotations.
  2790. *
  2791. * @requires modules/annotations
  2792. */
  2793. events: {},
  2794. /**
  2795. * The Z index of the annotation.
  2796. */
  2797. zIndex: 6
  2798. },
  2799. /**
  2800. * Initialize the annotation.
  2801. *
  2802. * @param {Highcharts.Chart}
  2803. * The chart
  2804. * @param {Highcharts.AnnotationsOptions}
  2805. * The user options for the annotation
  2806. */
  2807. init: function () {
  2808. this.linkPoints();
  2809. this.addControlPoints();
  2810. this.addShapes();
  2811. this.addLabels();
  2812. this.addClipPaths();
  2813. this.setLabelCollector();
  2814. },
  2815. getLabelsAndShapesOptions: function (baseOptions, newOptions) {
  2816. var mergedOptions = {};
  2817. ['labels', 'shapes'].forEach(function (name) {
  2818. if (baseOptions[name]) {
  2819. mergedOptions[name] = splat(newOptions[name]).map(
  2820. function (basicOptions, i) {
  2821. return merge(baseOptions[name][i], basicOptions);
  2822. }
  2823. );
  2824. }
  2825. });
  2826. return mergedOptions;
  2827. },
  2828. addShapes: function () {
  2829. (this.options.shapes || []).forEach(function (shapeOptions, i) {
  2830. var shape = this.initShape(shapeOptions, i);
  2831. merge(true, this.options.shapes[i], shape.options);
  2832. }, this);
  2833. },
  2834. addLabels: function () {
  2835. (this.options.labels || []).forEach(function (labelsOptions, i) {
  2836. var labels = this.initLabel(labelsOptions, i);
  2837. merge(true, this.options.labels[i], labels.options);
  2838. }, this);
  2839. },
  2840. addClipPaths: function () {
  2841. this.setClipAxes();
  2842. if (this.clipXAxis && this.clipYAxis) {
  2843. this.clipRect = this.chart.renderer.clipRect(
  2844. this.getClipBox()
  2845. );
  2846. }
  2847. },
  2848. setClipAxes: function () {
  2849. var xAxes = this.chart.xAxis,
  2850. yAxes = this.chart.yAxis,
  2851. linkedAxes = reduce(
  2852. (this.options.labels || [])
  2853. .concat(this.options.shapes || []),
  2854. function (axes, labelOrShape) {
  2855. return [
  2856. xAxes[
  2857. labelOrShape &&
  2858. labelOrShape.point &&
  2859. labelOrShape.point.xAxis
  2860. ] || axes[0],
  2861. yAxes[
  2862. labelOrShape &&
  2863. labelOrShape.point &&
  2864. labelOrShape.point.yAxis
  2865. ] || axes[1]
  2866. ];
  2867. },
  2868. []
  2869. );
  2870. this.clipXAxis = linkedAxes[0];
  2871. this.clipYAxis = linkedAxes[1];
  2872. },
  2873. getClipBox: function () {
  2874. return {
  2875. x: this.clipXAxis.left,
  2876. y: this.clipYAxis.top,
  2877. width: this.clipXAxis.width,
  2878. height: this.clipYAxis.height
  2879. };
  2880. },
  2881. setLabelCollector: function () {
  2882. var annotation = this;
  2883. annotation.labelCollector = function () {
  2884. return annotation.labels.reduce(
  2885. function (labels, label) {
  2886. if (!label.options.allowOverlap) {
  2887. labels.push(label.graphic);
  2888. }
  2889. return labels;
  2890. },
  2891. []
  2892. );
  2893. };
  2894. annotation.chart.labelCollectors.push(
  2895. annotation.labelCollector
  2896. );
  2897. },
  2898. /**
  2899. * Set an annotation options.
  2900. *
  2901. * @param {Highcharts.AnnotationsOptions} - user options for an annotation
  2902. */
  2903. setOptions: function (userOptions) {
  2904. this.options = merge(this.defaultOptions, userOptions);
  2905. },
  2906. redraw: function (animation) {
  2907. this.linkPoints();
  2908. if (!this.graphic) {
  2909. this.render();
  2910. }
  2911. if (this.clipRect) {
  2912. this.clipRect.animate(this.getClipBox());
  2913. }
  2914. this.redrawItems(this.shapes, animation);
  2915. this.redrawItems(this.labels, animation);
  2916. controllableMixin.redraw.call(this, animation);
  2917. },
  2918. /**
  2919. * @param {Array<(Annotation.Label|Annotation.Shape)>} items
  2920. * @param {boolean} [animation]
  2921. */
  2922. redrawItems: function (items, animation) {
  2923. var i = items.length;
  2924. // needs a backward loop
  2925. // labels/shapes array might be modified
  2926. // due to destruction of the item
  2927. while (i--) {
  2928. this.redrawItem(items[i], animation);
  2929. }
  2930. },
  2931. render: function () {
  2932. var renderer = this.chart.renderer;
  2933. this.graphic = renderer
  2934. .g('annotation')
  2935. .attr({
  2936. zIndex: this.options.zIndex,
  2937. visibility: this.options.visible ?
  2938. 'visible' :
  2939. 'hidden'
  2940. })
  2941. .add();
  2942. this.shapesGroup = renderer
  2943. .g('annotation-shapes')
  2944. .add(this.graphic)
  2945. .clip(this.chart.plotBoxClip);
  2946. this.labelsGroup = renderer
  2947. .g('annotation-labels')
  2948. .attr({
  2949. // hideOverlappingLabels requires translation
  2950. translateX: 0,
  2951. translateY: 0
  2952. })
  2953. .add(this.graphic);
  2954. if (this.clipRect) {
  2955. this.graphic.clip(this.clipRect);
  2956. }
  2957. this.addEvents();
  2958. controllableMixin.render.call(this);
  2959. },
  2960. /**
  2961. * Set the annotation's visibility.
  2962. *
  2963. * @param {Boolean} [visible] - Whether to show or hide an annotation.
  2964. * If the param is omitted, the annotation's visibility is toggled.
  2965. */
  2966. setVisibility: function (visibility) {
  2967. var options = this.options,
  2968. visible = pick(visibility, !options.visible);
  2969. this.graphic.attr(
  2970. 'visibility',
  2971. visible ? 'visible' : 'hidden'
  2972. );
  2973. if (!visible) {
  2974. this.setControlPointsVisibility(false);
  2975. }
  2976. options.visible = visible;
  2977. },
  2978. setControlPointsVisibility: function (visible) {
  2979. var setItemControlPointsVisibility = function (item) {
  2980. item.setControlPointsVisibility(visible);
  2981. };
  2982. controllableMixin.setControlPointsVisibility.call(
  2983. this,
  2984. visible
  2985. );
  2986. this.shapes.forEach(setItemControlPointsVisibility);
  2987. this.labels.forEach(setItemControlPointsVisibility);
  2988. },
  2989. /**
  2990. * Destroy the annotation. This function does not touch the chart
  2991. * that the annotation belongs to (all annotations are kept in
  2992. * the chart.annotations array) - it is recommended to use
  2993. * {@link Highcharts.Chart#removeAnnotation} instead.
  2994. */
  2995. destroy: function () {
  2996. var chart = this.chart,
  2997. destroyItem = function (item) {
  2998. item.destroy();
  2999. };
  3000. this.labels.forEach(destroyItem);
  3001. this.shapes.forEach(destroyItem);
  3002. this.clipXAxis = null;
  3003. this.clipYAxis = null;
  3004. erase(chart.labelCollectors, this.labelCollector);
  3005. eventEmitterMixin.destroy.call(this);
  3006. controllableMixin.destroy.call(this);
  3007. destroyObjectProperties(this, chart);
  3008. },
  3009. /**
  3010. * See {@link Highcharts.Chart#removeAnnotation}.
  3011. */
  3012. remove: function () {
  3013. // Let chart.update() remove annoations on demand
  3014. return this.chart.removeAnnotation(this);
  3015. },
  3016. update: function (userOptions) {
  3017. var chart = this.chart,
  3018. labelsAndShapes = this.getLabelsAndShapesOptions(
  3019. this.userOptions,
  3020. userOptions
  3021. ),
  3022. userOptionsIndex = chart.annotations.indexOf(this),
  3023. options = H.merge(true, this.userOptions, userOptions);
  3024. options.labels = labelsAndShapes.labels;
  3025. options.shapes = labelsAndShapes.shapes;
  3026. this.destroy();
  3027. this.constructor(chart, options);
  3028. // Update options in chart options, used in exporting (#9767):
  3029. chart.options.annotations[userOptionsIndex] = options;
  3030. this.isUpdating = true;
  3031. this.redraw();
  3032. this.isUpdating = false;
  3033. fireEvent(this, 'afterUpdate');
  3034. },
  3035. /* *************************************************************
  3036. * ITEM SECTION
  3037. * Contains methods for handling a single item in an annotation
  3038. **************************************************************** */
  3039. /**
  3040. * Initialisation of a single shape
  3041. *
  3042. * @param {Object} shapeOptions - a confg object for a single shape
  3043. */
  3044. initShape: function (shapeOptions, index) {
  3045. var options = merge(
  3046. this.options.shapeOptions,
  3047. {
  3048. controlPointOptions: this.options.controlPointOptions
  3049. },
  3050. shapeOptions
  3051. ),
  3052. shape = new Annotation.shapesMap[options.type](
  3053. this,
  3054. options,
  3055. index
  3056. );
  3057. shape.itemType = 'shape';
  3058. this.shapes.push(shape);
  3059. return shape;
  3060. },
  3061. /**
  3062. * Initialisation of a single label
  3063. *
  3064. * @param {Object} labelOptions
  3065. **/
  3066. initLabel: function (labelOptions, index) {
  3067. var options = merge(
  3068. this.options.labelOptions,
  3069. {
  3070. controlPointOptions: this.options.controlPointOptions
  3071. },
  3072. labelOptions
  3073. ),
  3074. label = new ControllableLabel(
  3075. this,
  3076. options,
  3077. index
  3078. );
  3079. label.itemType = 'label';
  3080. this.labels.push(label);
  3081. return label;
  3082. },
  3083. /**
  3084. * Redraw a single item.
  3085. *
  3086. * @param {Annotation.Label|Annotation.Shape} item
  3087. * @param {boolean} [animation]
  3088. */
  3089. redrawItem: function (item, animation) {
  3090. item.linkPoints();
  3091. if (!item.shouldBeDrawn()) {
  3092. this.destroyItem(item);
  3093. } else {
  3094. if (!item.graphic) {
  3095. this.renderItem(item);
  3096. }
  3097. item.redraw(
  3098. pick(animation, true) && item.graphic.placed
  3099. );
  3100. if (item.points.length) {
  3101. this.adjustVisibility(item);
  3102. }
  3103. }
  3104. },
  3105. /**
  3106. * Hide or show annotaiton attached to points.
  3107. *
  3108. * @param {Annotation.Label|Annotation.Shape} item
  3109. */
  3110. adjustVisibility: function (item) { // #9481
  3111. var hasVisiblePoints = false,
  3112. label = item.graphic;
  3113. item.points.forEach(function (point) {
  3114. if (
  3115. point.series.visible !== false &&
  3116. point.visible !== false
  3117. ) {
  3118. hasVisiblePoints = true;
  3119. }
  3120. });
  3121. if (!hasVisiblePoints) {
  3122. label.hide();
  3123. } else if (label.visibility === 'hidden') {
  3124. label.show();
  3125. }
  3126. },
  3127. /**
  3128. * Destroy a single item.
  3129. *
  3130. * @param {Annotation.Label|Annotation.Shape} item
  3131. */
  3132. destroyItem: function (item) {
  3133. // erase from shapes or labels array
  3134. erase(this[item.itemType + 's'], item);
  3135. item.destroy();
  3136. },
  3137. /**
  3138. * @private
  3139. */
  3140. renderItem: function (item) {
  3141. item.render(
  3142. item.itemType === 'label' ?
  3143. this.labelsGroup :
  3144. this.shapesGroup
  3145. );
  3146. }
  3147. }
  3148. );
  3149. /**
  3150. * An object uses for mapping between a shape type and a constructor.
  3151. * To add a new shape type extend this object with type name as a key
  3152. * and a constructor as its value.
  3153. */
  3154. Annotation.shapesMap = {
  3155. 'rect': ControllableRect,
  3156. 'circle': ControllableCircle,
  3157. 'path': ControllablePath,
  3158. 'image': ControllableImage
  3159. };
  3160. Annotation.types = {};
  3161. Annotation.MockPoint = MockPoint;
  3162. Annotation.ControlPoint = ControlPoint;
  3163. H.extendAnnotation = function (
  3164. Constructor,
  3165. BaseConstructor,
  3166. prototype,
  3167. defaultOptions
  3168. ) {
  3169. BaseConstructor = BaseConstructor || Annotation;
  3170. merge(
  3171. true,
  3172. Constructor.prototype,
  3173. BaseConstructor.prototype,
  3174. prototype
  3175. );
  3176. Constructor.prototype.defaultOptions = merge(
  3177. Constructor.prototype.defaultOptions,
  3178. defaultOptions || {}
  3179. );
  3180. };
  3181. /* *********************************************************************
  3182. *
  3183. * EXTENDING CHART PROTOTYPE
  3184. *
  3185. ******************************************************************** */
  3186. extend(chartProto, /** @lends Highcharts.Chart# */ {
  3187. initAnnotation: function (userOptions) {
  3188. var Constructor =
  3189. Annotation.types[userOptions.type] || Annotation,
  3190. annotation = new Constructor(this, userOptions);
  3191. this.annotations.push(annotation);
  3192. return annotation;
  3193. },
  3194. /**
  3195. * Add an annotation to the chart after render time.
  3196. *
  3197. * @param {Highcharts.AnnotationsOptions} options
  3198. * The annotation options for the new, detailed annotation.
  3199. * @param {boolean} [redraw]
  3200. *
  3201. * @return {Highcharts.Annotation} - The newly generated annotation.
  3202. */
  3203. addAnnotation: function (userOptions, redraw) {
  3204. var annotation = this.initAnnotation(userOptions);
  3205. this.options.annotations.push(annotation.options);
  3206. if (pick(redraw, true)) {
  3207. annotation.redraw();
  3208. }
  3209. return annotation;
  3210. },
  3211. /**
  3212. * Remove an annotation from the chart.
  3213. *
  3214. * @param {String|Number|Annotation} idOrAnnotation - The annotation's id or
  3215. * direct annotation object.
  3216. */
  3217. removeAnnotation: function (idOrAnnotation) {
  3218. var annotations = this.annotations,
  3219. annotation = idOrAnnotation.coll === 'annotations' ?
  3220. idOrAnnotation :
  3221. find(
  3222. annotations,
  3223. function (annotation) {
  3224. return annotation.options.id === idOrAnnotation;
  3225. }
  3226. );
  3227. if (annotation) {
  3228. fireEvent(annotation, 'remove');
  3229. erase(this.options.annotations, annotation.options);
  3230. erase(annotations, annotation);
  3231. annotation.destroy();
  3232. }
  3233. },
  3234. drawAnnotations: function () {
  3235. this.plotBoxClip.attr(this.plotBox);
  3236. this.annotations.forEach(function (annotation) {
  3237. annotation.redraw();
  3238. });
  3239. }
  3240. });
  3241. // Let chart.update() update annotations
  3242. chartProto.collectionsWithUpdate.push('annotations');
  3243. // Let chart.update() create annoations on demand
  3244. chartProto.collectionsWithInit.annotations = [chartProto.addAnnotation];
  3245. chartProto.callbacks.push(function (chart) {
  3246. chart.annotations = [];
  3247. if (!chart.options.annotations) {
  3248. chart.options.annotations = [];
  3249. }
  3250. chart.plotBoxClip = this.renderer.clipRect(this.plotBox);
  3251. chart.controlPointsGroup = chart.renderer
  3252. .g('control-points')
  3253. .attr({ zIndex: 99 })
  3254. .clip(chart.plotBoxClip)
  3255. .add();
  3256. chart.options.annotations.forEach(function (annotationOptions, i) {
  3257. var annotation = chart.initAnnotation(annotationOptions);
  3258. chart.options.annotations[i] = annotation.options;
  3259. });
  3260. chart.drawAnnotations();
  3261. addEvent(chart, 'redraw', chart.drawAnnotations);
  3262. addEvent(chart, 'destroy', function () {
  3263. chart.plotBoxClip.destroy();
  3264. chart.controlPointsGroup.destroy();
  3265. });
  3266. });
  3267. wrap(
  3268. H.Pointer.prototype,
  3269. 'onContainerMouseDown',
  3270. function (proceed) {
  3271. if (!this.chart.hasDraggedAnnotation) {
  3272. proceed.apply(this, Array.prototype.slice.call(arguments, 1));
  3273. }
  3274. }
  3275. );
  3276. });
  3277. _registerModule(_modules, 'annotations/types/CrookedLine.js', [_modules['parts/Globals.js']], function (H) {
  3278. var Annotation = H.Annotation,
  3279. MockPoint = Annotation.MockPoint,
  3280. ControlPoint = Annotation.ControlPoint;
  3281. /**
  3282. * @class
  3283. * @extends Annotation
  3284. * @memberOf Annotation
  3285. */
  3286. function CrookedLine() {
  3287. Annotation.apply(this, arguments);
  3288. }
  3289. H.extendAnnotation(
  3290. CrookedLine,
  3291. null,
  3292. /** @lends Annotation.CrookedLine# */
  3293. {
  3294. /**
  3295. * Overrides default setter to get axes from typeOptions.
  3296. */
  3297. setClipAxes: function () {
  3298. this.clipXAxis = this.chart.xAxis[this.options.typeOptions.xAxis];
  3299. this.clipYAxis = this.chart.yAxis[this.options.typeOptions.yAxis];
  3300. },
  3301. getPointsOptions: function () {
  3302. var typeOptions = this.options.typeOptions;
  3303. return typeOptions.points.map(function (pointOptions) {
  3304. pointOptions.xAxis = typeOptions.xAxis;
  3305. pointOptions.yAxis = typeOptions.yAxis;
  3306. return pointOptions;
  3307. });
  3308. },
  3309. getControlPointsOptions: function () {
  3310. return this.getPointsOptions();
  3311. },
  3312. addControlPoints: function () {
  3313. this.getControlPointsOptions().forEach(
  3314. function (pointOptions, i) {
  3315. var controlPoint = new ControlPoint(
  3316. this.chart,
  3317. this,
  3318. H.merge(
  3319. this.options.controlPointOptions,
  3320. pointOptions.controlPoint
  3321. ),
  3322. i
  3323. );
  3324. this.controlPoints.push(controlPoint);
  3325. pointOptions.controlPoint = controlPoint.options;
  3326. },
  3327. this
  3328. );
  3329. },
  3330. addShapes: function () {
  3331. var typeOptions = this.options.typeOptions,
  3332. shape = this.initShape(
  3333. H.merge(typeOptions.line, {
  3334. type: 'path',
  3335. points: this.points.map(function (point, i) {
  3336. return function (target) {
  3337. return target.annotation.points[i];
  3338. };
  3339. })
  3340. }),
  3341. false
  3342. );
  3343. typeOptions.line = shape.options;
  3344. }
  3345. },
  3346. /**
  3347. * A crooked line annotation.
  3348. *
  3349. * @sample highcharts/annotations-advanced/crooked-line/
  3350. * Crooked line
  3351. *
  3352. * @product highstock
  3353. * @optionparent annotations.crookedLine
  3354. */
  3355. {
  3356. /**
  3357. * @extends annotations.labelOptions
  3358. * @apioption annotations.crookedLine.labelOptions
  3359. */
  3360. /**
  3361. * @extends annotations.shapeOptions
  3362. * @apioption annotations.crookedLine.shapeOptions
  3363. */
  3364. /**
  3365. * Additional options for an annotation with the type.
  3366. */
  3367. typeOptions: {
  3368. /**
  3369. * This number defines which xAxis the point is connected to.
  3370. * It refers to either the axis id or the index of the axis
  3371. * in the xAxis array.
  3372. */
  3373. xAxis: 0,
  3374. /**
  3375. * This number defines which yAxis the point is connected to.
  3376. * It refers to either the axis id or the index of the axis
  3377. * in the xAxis array.
  3378. */
  3379. yAxis: 0,
  3380. /**
  3381. * @type {Array<*>}
  3382. * @apioption annotations.crookedLine.typeOptions.points
  3383. */
  3384. /**
  3385. * The x position of the point.
  3386. *
  3387. * @type {number}
  3388. * @apioption annotations.crookedLine.typeOptions.points.x
  3389. */
  3390. /**
  3391. * The y position of the point.
  3392. *
  3393. * @type {number}
  3394. * @apioption annotations.crookedLine.typeOptions.points.y
  3395. */
  3396. /**
  3397. * @type {number}
  3398. * @excluding positioner, events
  3399. * @apioption annotations.crookedLine.typeOptions.points.controlPoint
  3400. */
  3401. /**
  3402. * Line options.
  3403. *
  3404. * @excluding height, point, points, r, type, width
  3405. */
  3406. line: {
  3407. fill: 'none'
  3408. }
  3409. },
  3410. /**
  3411. * @excluding positioner, events
  3412. */
  3413. controlPointOptions: {
  3414. positioner: function (target) {
  3415. var graphic = this.graphic,
  3416. xy = MockPoint.pointToPixels(target.points[this.index]);
  3417. return {
  3418. x: xy.x - graphic.width / 2,
  3419. y: xy.y - graphic.height / 2
  3420. };
  3421. },
  3422. events: {
  3423. drag: function (e, target) {
  3424. if (
  3425. target.chart.isInsidePlot(
  3426. e.chartX - target.chart.plotLeft,
  3427. e.chartY - target.chart.plotTop
  3428. )
  3429. ) {
  3430. var translation = this.mouseMoveToTranslation(e);
  3431. target.translatePoint(
  3432. translation.x,
  3433. translation.y,
  3434. this.index
  3435. );
  3436. // Update options:
  3437. target.options.typeOptions.points[this.index].x =
  3438. target.points[this.index].x;
  3439. target.options.typeOptions.points[this.index].y =
  3440. target.points[this.index].y;
  3441. target.redraw(false);
  3442. }
  3443. }
  3444. }
  3445. }
  3446. }
  3447. );
  3448. Annotation.types.crookedLine = CrookedLine;
  3449. return CrookedLine;
  3450. });
  3451. _registerModule(_modules, 'annotations/types/ElliottWave.js', [_modules['parts/Globals.js']], function (H) {
  3452. var Annotation = H.Annotation,
  3453. CrookedLine = Annotation.types.crookedLine;
  3454. /**
  3455. * @class
  3456. * @extends Annotation.CrookedLine
  3457. * @memberOf Annotation
  3458. */
  3459. function ElliottWave() {
  3460. CrookedLine.apply(this, arguments);
  3461. }
  3462. H.extendAnnotation(ElliottWave, CrookedLine,
  3463. /** Annotation.CrookedLine# */
  3464. {
  3465. addLabels: function () {
  3466. this.getPointsOptions().forEach(function (point, i) {
  3467. var label = this.initLabel(H.merge(
  3468. point.label, {
  3469. text: this.options.typeOptions.labels[i],
  3470. point: function (target) {
  3471. return target.annotation.points[i];
  3472. }
  3473. }
  3474. ), false);
  3475. point.label = label.options;
  3476. }, this);
  3477. }
  3478. },
  3479. /**
  3480. * An elliott wave annotation.
  3481. *
  3482. * @sample highcharts/annotations-advanced/elliott-wave/
  3483. * Elliott wave
  3484. *
  3485. * @extends annotations.crookedLine
  3486. * @product highstock
  3487. * @optionparent annotations.elliottWave
  3488. */
  3489. {
  3490. typeOptions: {
  3491. /**
  3492. * @extends annotations.crookedLine.labelOptions
  3493. * @apioption annotations.elliottWave.typeOptions.points.label
  3494. */
  3495. /**
  3496. * @ignore-options
  3497. */
  3498. labels: ['(0)', '(A)', '(B)', '(C)', '(D)', '(E)'],
  3499. line: {
  3500. strokeWidth: 1
  3501. }
  3502. },
  3503. labelOptions: {
  3504. align: 'center',
  3505. allowOverlap: true,
  3506. crop: true,
  3507. overflow: 'none',
  3508. type: 'rect',
  3509. backgroundColor: 'none',
  3510. borderWidth: 0,
  3511. y: -5
  3512. }
  3513. });
  3514. Annotation.types.elliottWave = ElliottWave;
  3515. return ElliottWave;
  3516. });
  3517. _registerModule(_modules, 'annotations/types/Tunnel.js', [_modules['parts/Globals.js']], function (H) {
  3518. var Annotation = H.Annotation,
  3519. CrookedLine = Annotation.types.crookedLine,
  3520. ControlPoint = Annotation.ControlPoint,
  3521. MockPoint = Annotation.MockPoint;
  3522. function getSecondCoordinate(p1, p2, x) {
  3523. return (p2.y - p1.y) / (p2.x - p1.x) * (x - p1.x) + p1.y;
  3524. }
  3525. /**
  3526. * @class
  3527. * @extends Annotation.CrookedLine
  3528. * @memberOf Annotation
  3529. **/
  3530. function Tunnel() {
  3531. CrookedLine.apply(this, arguments);
  3532. }
  3533. H.extendAnnotation(
  3534. Tunnel,
  3535. CrookedLine,
  3536. /** @lends Annotation.Tunnel# */
  3537. {
  3538. getPointsOptions: function () {
  3539. var pointsOptions =
  3540. CrookedLine.prototype.getPointsOptions.call(this);
  3541. pointsOptions[2] = this.heightPointOptions(pointsOptions[1]);
  3542. pointsOptions[3] = this.heightPointOptions(pointsOptions[0]);
  3543. return pointsOptions;
  3544. },
  3545. getControlPointsOptions: function () {
  3546. return this.getPointsOptions().slice(0, 2);
  3547. },
  3548. heightPointOptions: function (pointOptions) {
  3549. var heightPointOptions = H.merge(pointOptions);
  3550. heightPointOptions.y += this.options.typeOptions.height;
  3551. return heightPointOptions;
  3552. },
  3553. addControlPoints: function () {
  3554. CrookedLine.prototype.addControlPoints.call(this);
  3555. var options = this.options,
  3556. controlPoint = new ControlPoint(
  3557. this.chart,
  3558. this,
  3559. H.merge(
  3560. options.controlPointOptions,
  3561. options.typeOptions.heightControlPoint
  3562. ),
  3563. 2
  3564. );
  3565. this.controlPoints.push(controlPoint);
  3566. options.typeOptions.heightControlPoint = controlPoint.options;
  3567. },
  3568. addShapes: function () {
  3569. this.addLine();
  3570. this.addBackground();
  3571. },
  3572. addLine: function () {
  3573. var line = this.initShape(
  3574. H.merge(this.options.typeOptions.line, {
  3575. type: 'path',
  3576. points: [
  3577. this.points[0],
  3578. this.points[1],
  3579. function (target) {
  3580. var pointOptions = MockPoint.pointToOptions(
  3581. target.annotation.points[2]
  3582. );
  3583. pointOptions.command = 'M';
  3584. return pointOptions;
  3585. },
  3586. this.points[3]
  3587. ]
  3588. }),
  3589. false
  3590. );
  3591. this.options.typeOptions.line = line.options;
  3592. },
  3593. addBackground: function () {
  3594. var background = this.initShape(H.merge(
  3595. this.options.typeOptions.background,
  3596. {
  3597. type: 'path',
  3598. points: this.points.slice()
  3599. }
  3600. ));
  3601. this.options.typeOptions.background = background.options;
  3602. },
  3603. /**
  3604. * Translate start or end ("left" or "right") side of the tunnel.
  3605. *
  3606. * @param {number} dx - the amount of x translation
  3607. * @param {number} dy - the amount of y translation
  3608. * @param {boolean} [end] - whether to translate start or end side
  3609. */
  3610. translateSide: function (dx, dy, end) {
  3611. var topIndex = Number(end),
  3612. bottomIndex = topIndex === 0 ? 3 : 2;
  3613. this.translatePoint(dx, dy, topIndex);
  3614. this.translatePoint(dx, dy, bottomIndex);
  3615. },
  3616. /**
  3617. * Translate height of the tunnel.
  3618. *
  3619. * @param {number} dh - the amount of height translation
  3620. */
  3621. translateHeight: function (dh) {
  3622. this.translatePoint(0, dh, 2);
  3623. this.translatePoint(0, dh, 3);
  3624. this.options.typeOptions.height =
  3625. this.points[3].y - this.points[0].y;
  3626. }
  3627. },
  3628. /**
  3629. * A tunnel annotation.
  3630. *
  3631. * @extends annotations.crookedLine
  3632. * @sample highcharts/annotations-advanced/tunnel/
  3633. * Tunnel
  3634. * @product highstock
  3635. * @optionparent annotations.tunnel
  3636. */
  3637. {
  3638. typeOptions: {
  3639. xAxis: 0,
  3640. yAxis: 0,
  3641. /**
  3642. * Background options.
  3643. *
  3644. * @type {Object}
  3645. * @excluding height, point, points, r, type, width, markerEnd,
  3646. * markerStart
  3647. */
  3648. background: {
  3649. fill: 'rgba(130, 170, 255, 0.4)',
  3650. strokeWidth: 0
  3651. },
  3652. line: {
  3653. strokeWidth: 1
  3654. },
  3655. /**
  3656. * The height of the annotation in terms of yAxis.
  3657. */
  3658. height: -2,
  3659. /**
  3660. * Options for the control point which controls
  3661. * the annotation's height.
  3662. *
  3663. * @extends annotations.crookedLine.controlPointOptions
  3664. * @excluding positioner, events
  3665. */
  3666. heightControlPoint: {
  3667. positioner: function (target) {
  3668. var startXY = MockPoint.pointToPixels(target.points[2]),
  3669. endXY = MockPoint.pointToPixels(target.points[3]),
  3670. x = (startXY.x + endXY.x) / 2;
  3671. return {
  3672. x: x - this.graphic.width / 2,
  3673. y: getSecondCoordinate(startXY, endXY, x) -
  3674. this.graphic.height / 2
  3675. };
  3676. },
  3677. events: {
  3678. drag: function (e, target) {
  3679. if (
  3680. target.chart.isInsidePlot(
  3681. e.chartX - target.chart.plotLeft,
  3682. e.chartY - target.chart.plotTop
  3683. )
  3684. ) {
  3685. target.translateHeight(
  3686. this.mouseMoveToTranslation(e).y
  3687. );
  3688. target.redraw(false);
  3689. }
  3690. }
  3691. }
  3692. }
  3693. },
  3694. /**
  3695. * @extends annotations.crookedLine.controlPointOptions
  3696. * @excluding positioner, events
  3697. */
  3698. controlPointOptions: {
  3699. events: {
  3700. drag: function (e, target) {
  3701. if (
  3702. target.chart.isInsidePlot(
  3703. e.chartX - target.chart.plotLeft,
  3704. e.chartY - target.chart.plotTop
  3705. )
  3706. ) {
  3707. var translation = this.mouseMoveToTranslation(e);
  3708. target.translateSide(
  3709. translation.x,
  3710. translation.y,
  3711. this.index
  3712. );
  3713. target.redraw(false);
  3714. }
  3715. }
  3716. }
  3717. }
  3718. }
  3719. );
  3720. Annotation.types.tunnel = Tunnel;
  3721. return Tunnel;
  3722. });
  3723. _registerModule(_modules, 'annotations/types/InfinityLine.js', [_modules['parts/Globals.js']], function (H) {
  3724. var Annotation = H.Annotation,
  3725. MockPoint = Annotation.MockPoint,
  3726. CrookedLine = Annotation.types.crookedLine;
  3727. /**
  3728. * @class
  3729. * @extends Annotation.CrookedLine
  3730. * @memberOf Annotation
  3731. */
  3732. function InfinityLine() {
  3733. CrookedLine.apply(this, arguments);
  3734. }
  3735. InfinityLine.findEdgeCoordinate = function (
  3736. firstPoint,
  3737. secondPoint,
  3738. xOrY,
  3739. edgePointFirstCoordinate
  3740. ) {
  3741. var xOrYOpposite = xOrY === 'x' ? 'y' : 'x';
  3742. // solves equation for x or y
  3743. // y - y1 = (y2 - y1) / (x2 - x1) * (x - x1)
  3744. return (
  3745. (secondPoint[xOrY] - firstPoint[xOrY]) *
  3746. (edgePointFirstCoordinate - firstPoint[xOrYOpposite]) /
  3747. (secondPoint[xOrYOpposite] - firstPoint[xOrYOpposite]) +
  3748. firstPoint[xOrY]
  3749. );
  3750. };
  3751. InfinityLine.findEdgePoint = function (firstPoint, secondPoint) {
  3752. var xAxis = firstPoint.series.xAxis,
  3753. yAxis = secondPoint.series.yAxis,
  3754. firstPointPixels = MockPoint.pointToPixels(firstPoint),
  3755. secondPointPixels = MockPoint.pointToPixels(secondPoint),
  3756. deltaX = secondPointPixels.x - firstPointPixels.x,
  3757. deltaY = secondPointPixels.y - firstPointPixels.y,
  3758. xAxisMin = xAxis.left,
  3759. xAxisMax = xAxisMin + xAxis.width,
  3760. yAxisMin = yAxis.top,
  3761. yAxisMax = yAxisMin + yAxis.height,
  3762. xLimit = deltaX < 0 ? xAxisMin : xAxisMax,
  3763. yLimit = deltaY < 0 ? yAxisMin : yAxisMax,
  3764. edgePoint = {
  3765. x: deltaX === 0 ? firstPointPixels.x : xLimit,
  3766. y: deltaY === 0 ? firstPointPixels.y : yLimit
  3767. },
  3768. edgePointX,
  3769. edgePointY,
  3770. swap;
  3771. if (deltaX !== 0 && deltaY !== 0) {
  3772. edgePointY = InfinityLine.findEdgeCoordinate(
  3773. firstPointPixels,
  3774. secondPointPixels,
  3775. 'y',
  3776. xLimit
  3777. );
  3778. edgePointX = InfinityLine.findEdgeCoordinate(
  3779. firstPointPixels,
  3780. secondPointPixels,
  3781. 'x',
  3782. yLimit
  3783. );
  3784. if (edgePointY >= yAxisMin && edgePointY <= yAxisMax) {
  3785. edgePoint.x = xLimit;
  3786. edgePoint.y = edgePointY;
  3787. } else {
  3788. edgePoint.x = edgePointX;
  3789. edgePoint.y = yLimit;
  3790. }
  3791. }
  3792. edgePoint.x -= xAxisMin;
  3793. edgePoint.y -= yAxisMin;
  3794. if (firstPoint.series.chart.inverted) {
  3795. swap = edgePoint.x;
  3796. edgePoint.x = edgePoint.y;
  3797. edgePoint.y = swap;
  3798. }
  3799. return edgePoint;
  3800. };
  3801. var edgePoint = function (startIndex, endIndex) {
  3802. return function (target) {
  3803. var annotation = target.annotation,
  3804. points = annotation.points,
  3805. type = annotation.options.typeOptions.type;
  3806. if (type === 'horizontalLine') {
  3807. // Horizontal line has only one point,
  3808. // make a copy of it:
  3809. points = [
  3810. points[0],
  3811. new MockPoint(
  3812. annotation.chart,
  3813. points[0].target,
  3814. {
  3815. x: points[0].x + 1,
  3816. y: points[0].y,
  3817. xAxis: points[0].options.xAxis,
  3818. yAxis: points[0].options.yAxis
  3819. }
  3820. )
  3821. ];
  3822. } else if (type === 'verticalLine') {
  3823. // The same for verticalLine type:
  3824. points = [
  3825. points[0],
  3826. new MockPoint(
  3827. annotation.chart,
  3828. points[0].target,
  3829. {
  3830. x: points[0].x,
  3831. y: points[0].y + 1,
  3832. xAxis: points[0].options.xAxis,
  3833. yAxis: points[0].options.yAxis
  3834. }
  3835. )
  3836. ];
  3837. }
  3838. return InfinityLine.findEdgePoint(
  3839. points[startIndex],
  3840. points[endIndex]
  3841. );
  3842. };
  3843. };
  3844. InfinityLine.endEdgePoint = edgePoint(0, 1);
  3845. InfinityLine.startEdgePoint = edgePoint(1, 0);
  3846. H.extendAnnotation(
  3847. InfinityLine,
  3848. CrookedLine,
  3849. /** @lends Annotation.InfinityLine# */
  3850. {
  3851. addShapes: function () {
  3852. var typeOptions = this.options.typeOptions,
  3853. points = [
  3854. this.points[0],
  3855. InfinityLine.endEdgePoint
  3856. ];
  3857. if (typeOptions.type.match(/Line/g)) {
  3858. points[0] = InfinityLine.startEdgePoint;
  3859. }
  3860. var line = this.initShape(
  3861. H.merge(typeOptions.line, {
  3862. type: 'path',
  3863. points: points
  3864. }),
  3865. false
  3866. );
  3867. typeOptions.line = line.options;
  3868. }
  3869. }
  3870. );
  3871. /**
  3872. * An infinity line annotation.
  3873. *
  3874. * @sample highcharts/annotations-advanced/infinity-line/
  3875. * Infinity Line
  3876. *
  3877. * @extends annotations.crookedLine
  3878. * @product highstock
  3879. * @apioption annotations.infinityLine
  3880. */
  3881. Annotation.types.infinityLine = InfinityLine;
  3882. return InfinityLine;
  3883. });
  3884. _registerModule(_modules, 'annotations/types/Fibonacci.js', [_modules['parts/Globals.js']], function (H) {
  3885. var Annotation = H.Annotation,
  3886. MockPoint = Annotation.MockPoint,
  3887. Tunnel = Annotation.types.tunnel;
  3888. var createPathDGenerator = function (retracementIndex, isBackground) {
  3889. return function () {
  3890. var annotation = this.annotation,
  3891. leftTop = this.anchor(
  3892. annotation.startRetracements[retracementIndex]
  3893. ).absolutePosition,
  3894. rightTop = this.anchor(
  3895. annotation.endRetracements[retracementIndex]
  3896. ).absolutePosition,
  3897. d = [
  3898. 'M',
  3899. Math.round(leftTop.x),
  3900. Math.round(leftTop.y),
  3901. 'L',
  3902. Math.round(rightTop.x),
  3903. Math.round(rightTop.y)
  3904. ],
  3905. rightBottom,
  3906. leftBottom;
  3907. if (isBackground) {
  3908. rightBottom = this.anchor(
  3909. annotation.endRetracements[retracementIndex - 1]
  3910. ).absolutePosition;
  3911. leftBottom = this.anchor(
  3912. annotation.startRetracements[retracementIndex - 1]
  3913. ).absolutePosition;
  3914. d.push(
  3915. 'L',
  3916. Math.round(rightBottom.x),
  3917. Math.round(rightBottom.y),
  3918. 'L',
  3919. Math.round(leftBottom.x),
  3920. Math.round(leftBottom.y)
  3921. );
  3922. }
  3923. return d;
  3924. };
  3925. };
  3926. /**
  3927. * @class
  3928. * @extends Annotation.Tunnel
  3929. * @memberOf Annotation
  3930. **/
  3931. function Fibonacci() {
  3932. this.startRetracements = [];
  3933. this.endRetracements = [];
  3934. Tunnel.apply(this, arguments);
  3935. }
  3936. Fibonacci.levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1];
  3937. H.extendAnnotation(Fibonacci, Tunnel,
  3938. /** @lends Annotation.Fibonacci# */
  3939. {
  3940. linkPoints: function () {
  3941. Tunnel.prototype.linkPoints.call(this);
  3942. this.linkRetracementsPoints();
  3943. },
  3944. linkRetracementsPoints: function () {
  3945. var points = this.points,
  3946. startDiff = points[0].y - points[3].y,
  3947. endDiff = points[1].y - points[2].y,
  3948. startX = points[0].x,
  3949. endX = points[1].x;
  3950. Fibonacci.levels.forEach(function (level, i) {
  3951. var startRetracement = points[0].y - startDiff * level,
  3952. endRetracement = points[1].y - endDiff * level;
  3953. this.linkRetracementPoint(
  3954. i,
  3955. startX,
  3956. startRetracement,
  3957. this.startRetracements
  3958. );
  3959. this.linkRetracementPoint(
  3960. i,
  3961. endX,
  3962. endRetracement,
  3963. this.endRetracements
  3964. );
  3965. }, this);
  3966. },
  3967. linkRetracementPoint: function (
  3968. pointIndex,
  3969. x,
  3970. y,
  3971. retracements
  3972. ) {
  3973. var point = retracements[pointIndex],
  3974. typeOptions = this.options.typeOptions;
  3975. if (!point) {
  3976. retracements[pointIndex] = new MockPoint(
  3977. this.chart,
  3978. this,
  3979. {
  3980. x: x,
  3981. y: y,
  3982. xAxis: typeOptions.xAxis,
  3983. yAxis: typeOptions.yAxis
  3984. }
  3985. );
  3986. } else {
  3987. point.options.x = x;
  3988. point.options.y = y;
  3989. point.refresh();
  3990. }
  3991. },
  3992. addShapes: function () {
  3993. Fibonacci.levels.forEach(function (level, i) {
  3994. this.initShape({
  3995. type: 'path',
  3996. d: createPathDGenerator(i)
  3997. }, false);
  3998. if (i > 0) {
  3999. this.initShape({
  4000. type: 'path',
  4001. fill: this.options.typeOptions.backgroundColors[i - 1],
  4002. strokeWidth: 0,
  4003. d: createPathDGenerator(i, true)
  4004. });
  4005. }
  4006. }, this);
  4007. },
  4008. addLabels: function () {
  4009. Fibonacci.levels.forEach(function (level, i) {
  4010. var options = this.options.typeOptions,
  4011. label = this.initLabel(
  4012. H.merge(options.labels[i], {
  4013. point: function (target) {
  4014. var point = MockPoint.pointToOptions(
  4015. target.annotation.startRetracements[i]
  4016. );
  4017. return point;
  4018. },
  4019. text: level.toString()
  4020. })
  4021. );
  4022. options.labels[i] = label.options;
  4023. }, this);
  4024. }
  4025. },
  4026. /**
  4027. * A fibonacci annotation.
  4028. *
  4029. * @sample highcharts/annotations-advanced/fibonacci/
  4030. * Fibonacci
  4031. *
  4032. * @extends annotations.crookedLine
  4033. * @product highstock
  4034. * @optionparent annotations.fibonacci
  4035. */
  4036. {
  4037. typeOptions: {
  4038. /**
  4039. * The height of the fibonacci in terms of yAxis.
  4040. */
  4041. height: 2,
  4042. /**
  4043. * An array of background colors:
  4044. * Default to:
  4045. * ```
  4046. * [
  4047. * 'rgba(130, 170, 255, 0.4)',
  4048. * 'rgba(139, 191, 216, 0.4)',
  4049. * 'rgba(150, 216, 192, 0.4)',
  4050. * 'rgba(156, 229, 161, 0.4)',
  4051. * 'rgba(162, 241, 130, 0.4)',
  4052. * 'rgba(169, 255, 101, 0.4)'
  4053. * ]
  4054. * ```
  4055. */
  4056. backgroundColors: [
  4057. 'rgba(130, 170, 255, 0.4)',
  4058. 'rgba(139, 191, 216, 0.4)',
  4059. 'rgba(150, 216, 192, 0.4)',
  4060. 'rgba(156, 229, 161, 0.4)',
  4061. 'rgba(162, 241, 130, 0.4)',
  4062. 'rgba(169, 255, 101, 0.4)'
  4063. ],
  4064. /**
  4065. * The color of line.
  4066. */
  4067. lineColor: 'grey',
  4068. /**
  4069. * An array of colors for the lines.
  4070. */
  4071. lineColors: [],
  4072. /**
  4073. * An array with options for the labels.
  4074. *
  4075. * @type {Array<object>}
  4076. * @extends annotations.crookedLine.labelOptions
  4077. * @apioption annotations.fibonacci.typeOptions.labels
  4078. */
  4079. labels: []
  4080. },
  4081. labelOptions: {
  4082. allowOverlap: true,
  4083. align: 'right',
  4084. backgroundColor: 'none',
  4085. borderWidth: 0,
  4086. crop: false,
  4087. overflow: 'none',
  4088. shape: 'rect',
  4089. style: {
  4090. color: 'grey'
  4091. },
  4092. verticalAlign: 'middle',
  4093. y: 0
  4094. }
  4095. });
  4096. Annotation.types.fibonacci = Fibonacci;
  4097. return Fibonacci;
  4098. });
  4099. _registerModule(_modules, 'annotations/types/Pitchfork.js', [_modules['parts/Globals.js']], function (H) {
  4100. var Annotation = H.Annotation,
  4101. MockPoint = Annotation.MockPoint,
  4102. InfinityLine = Annotation.types.infinityLine;
  4103. /**
  4104. * @class
  4105. * @extends Highcharts.InfinityLine
  4106. * @memberOf Highcharts
  4107. **/
  4108. function Pitchfork() {
  4109. InfinityLine.apply(this, arguments);
  4110. }
  4111. Pitchfork.findEdgePoint = function (
  4112. point,
  4113. firstAnglePoint,
  4114. secondAnglePoint
  4115. ) {
  4116. var angle = Math.atan2(
  4117. secondAnglePoint.plotY - firstAnglePoint.plotY,
  4118. secondAnglePoint.plotX - firstAnglePoint.plotX
  4119. ),
  4120. distance = 1e7;
  4121. return {
  4122. x: point.plotX + distance * Math.cos(angle),
  4123. y: point.plotY + distance * Math.sin(angle)
  4124. };
  4125. };
  4126. Pitchfork.middleLineEdgePoint = function (target) {
  4127. var annotation = target.annotation,
  4128. points = annotation.points;
  4129. return InfinityLine.findEdgePoint(
  4130. points[0],
  4131. new MockPoint(
  4132. annotation.chart,
  4133. target,
  4134. annotation.midPointOptions()
  4135. )
  4136. );
  4137. };
  4138. var outerLineEdgePoint = function (firstPointIndex) {
  4139. return function (target) {
  4140. var annotation = target.annotation,
  4141. points = annotation.points;
  4142. return Pitchfork.findEdgePoint(
  4143. points[firstPointIndex],
  4144. points[0],
  4145. new MockPoint(
  4146. annotation.chart,
  4147. target,
  4148. annotation.midPointOptions()
  4149. )
  4150. );
  4151. };
  4152. };
  4153. Pitchfork.topLineEdgePoint = outerLineEdgePoint(1);
  4154. Pitchfork.bottomLineEdgePoint = outerLineEdgePoint(0);
  4155. H.extendAnnotation(Pitchfork, InfinityLine,
  4156. {
  4157. midPointOptions: function () {
  4158. var points = this.points;
  4159. return {
  4160. x: (points[1].x + points[2].x) / 2,
  4161. y: (points[1].y + points[2].y) / 2,
  4162. xAxis: points[0].series.xAxis,
  4163. yAxis: points[0].series.yAxis
  4164. };
  4165. },
  4166. addShapes: function () {
  4167. this.addLines();
  4168. this.addBackgrounds();
  4169. },
  4170. addLines: function () {
  4171. this.initShape({
  4172. type: 'path',
  4173. points: [
  4174. this.points[0],
  4175. Pitchfork.middleLineEdgePoint
  4176. ]
  4177. }, false);
  4178. this.initShape({
  4179. type: 'path',
  4180. points: [
  4181. this.points[1],
  4182. Pitchfork.topLineEdgePoint
  4183. ]
  4184. }, false);
  4185. this.initShape({
  4186. type: 'path',
  4187. points: [
  4188. this.points[2],
  4189. Pitchfork.bottomLineEdgePoint
  4190. ]
  4191. }, false);
  4192. },
  4193. addBackgrounds: function () {
  4194. var shapes = this.shapes,
  4195. typeOptions = this.options.typeOptions;
  4196. var innerBackground = this.initShape(
  4197. H.merge(typeOptions.innerBackground, {
  4198. type: 'path',
  4199. points: [
  4200. function (target) {
  4201. var annotation = target.annotation,
  4202. points = annotation.points,
  4203. midPointOptions = annotation.midPointOptions();
  4204. return {
  4205. x: (points[1].x + midPointOptions.x) / 2,
  4206. y: (points[1].y + midPointOptions.y) / 2,
  4207. xAxis: midPointOptions.xAxis,
  4208. yAxis: midPointOptions.yAxis
  4209. };
  4210. },
  4211. shapes[1].points[1],
  4212. shapes[2].points[1],
  4213. function (target) {
  4214. var annotation = target.annotation,
  4215. points = annotation.points,
  4216. midPointOptions = annotation.midPointOptions();
  4217. return {
  4218. x: (midPointOptions.x + points[2].x) / 2,
  4219. y: (midPointOptions.y + points[2].y) / 2,
  4220. xAxis: midPointOptions.xAxis,
  4221. yAxis: midPointOptions.yAxis
  4222. };
  4223. }
  4224. ]
  4225. })
  4226. );
  4227. var outerBackground = this.initShape(
  4228. H.merge(typeOptions.outerBackground, {
  4229. type: 'path',
  4230. points: [
  4231. this.points[1],
  4232. shapes[1].points[1],
  4233. shapes[2].points[1],
  4234. this.points[2]
  4235. ]
  4236. })
  4237. );
  4238. typeOptions.innerBackground = innerBackground.options;
  4239. typeOptions.outerBackground = outerBackground.options;
  4240. }
  4241. },
  4242. /**
  4243. * A pitchfork annotation.
  4244. *
  4245. * @sample highcharts/annotations-advanced/pitchfork/
  4246. * Pitchfork
  4247. *
  4248. * @extends annotations.infinityLine
  4249. * @product highstock
  4250. * @optionparent annotations.pitchfork
  4251. */
  4252. {
  4253. typeOptions: {
  4254. /**
  4255. * Inner background options.
  4256. *
  4257. * @extends annotations.crookedLine.shapeOptions
  4258. * @excluding height, r, type, width
  4259. */
  4260. innerBackground: {
  4261. fill: 'rgba(130, 170, 255, 0.4)',
  4262. strokeWidth: 0
  4263. },
  4264. /**
  4265. * Outer background options.
  4266. *
  4267. * @extends annotations.crookedLine.shapeOptions
  4268. * @excluding height, r, type, width
  4269. */
  4270. outerBackground: {
  4271. fill: 'rgba(156, 229, 161, 0.4)',
  4272. strokeWidth: 0
  4273. }
  4274. }
  4275. });
  4276. Annotation.types.pitchfork = Pitchfork;
  4277. return Pitchfork;
  4278. });
  4279. _registerModule(_modules, 'annotations/types/VerticalLine.js', [_modules['parts/Globals.js']], function (H) {
  4280. var Annotation = H.Annotation,
  4281. MockPoint = Annotation.MockPoint;
  4282. /**
  4283. * @class
  4284. * @extends Annotation
  4285. * @memberOf Highcharts
  4286. */
  4287. function VerticalLine() {
  4288. H.Annotation.apply(this, arguments);
  4289. }
  4290. VerticalLine.connectorFirstPoint = function (target) {
  4291. var annotation = target.annotation,
  4292. point = annotation.points[0],
  4293. xy = MockPoint.pointToPixels(point, true),
  4294. y = xy.y,
  4295. offset = annotation.options.typeOptions.label.offset;
  4296. if (annotation.chart.inverted) {
  4297. y = xy.x;
  4298. }
  4299. return {
  4300. x: point.x,
  4301. xAxis: point.series.xAxis,
  4302. y: y + offset
  4303. };
  4304. };
  4305. VerticalLine.connectorSecondPoint = function (target) {
  4306. var annotation = target.annotation,
  4307. typeOptions = annotation.options.typeOptions,
  4308. point = annotation.points[0],
  4309. yOffset = typeOptions.yOffset,
  4310. xy = MockPoint.pointToPixels(point, true),
  4311. y = xy[annotation.chart.inverted ? 'x' : 'y'];
  4312. if (typeOptions.label.offset < 0) {
  4313. yOffset *= -1;
  4314. }
  4315. return {
  4316. x: point.x,
  4317. xAxis: point.series.xAxis,
  4318. y: y + yOffset
  4319. };
  4320. };
  4321. H.extendAnnotation(VerticalLine, null,
  4322. /** @lends Annotation.VerticalLine# */
  4323. {
  4324. getPointsOptions: function () {
  4325. return [this.options.typeOptions.point];
  4326. },
  4327. addShapes: function () {
  4328. var typeOptions = this.options.typeOptions,
  4329. connector = this.initShape(
  4330. H.merge(typeOptions.connector, {
  4331. type: 'path',
  4332. points: [
  4333. VerticalLine.connectorFirstPoint,
  4334. VerticalLine.connectorSecondPoint
  4335. ]
  4336. }),
  4337. false
  4338. );
  4339. typeOptions.connector = connector.options;
  4340. },
  4341. addLabels: function () {
  4342. var typeOptions = this.options.typeOptions,
  4343. labelOptions = typeOptions.label,
  4344. x = 0,
  4345. y = labelOptions.offset,
  4346. verticalAlign = labelOptions.offset < 0 ? 'bottom' : 'top',
  4347. align = 'center';
  4348. if (this.chart.inverted) {
  4349. x = labelOptions.offset;
  4350. y = 0;
  4351. verticalAlign = 'middle';
  4352. align = labelOptions.offset < 0 ? 'right' : 'left';
  4353. }
  4354. var label = this.initLabel(
  4355. H.merge(labelOptions, {
  4356. verticalAlign: verticalAlign,
  4357. align: align,
  4358. x: x,
  4359. y: y
  4360. })
  4361. );
  4362. typeOptions.label = label.options;
  4363. }
  4364. },
  4365. /**
  4366. * A vertical line annotation.
  4367. *
  4368. * @sample highcharts/annotations-advanced/vertical-line/
  4369. * Vertical line
  4370. *
  4371. * @extends annotations.crookedLine
  4372. * @excluding labels, shapes, controlPointOptions
  4373. * @product highstock
  4374. * @optionparent annotations.verticalLine
  4375. */
  4376. {
  4377. typeOptions: {
  4378. /**
  4379. * @ignore
  4380. */
  4381. yOffset: 10,
  4382. /**
  4383. * Label options.
  4384. *
  4385. * @extends annotations.crookedLine.labelOptions
  4386. */
  4387. label: {
  4388. offset: -40,
  4389. point: function (target) {
  4390. return target.annotation.points[0];
  4391. },
  4392. allowOverlap: true,
  4393. backgroundColor: 'none',
  4394. borderWidth: 0,
  4395. crop: true,
  4396. overflow: 'none',
  4397. shape: 'rect',
  4398. text: '{y:.2f}'
  4399. },
  4400. /**
  4401. * Connector options.
  4402. *
  4403. * @extends annotations.crookedLine.shapeOptions
  4404. * @excluding height, r, type, width
  4405. */
  4406. connector: {
  4407. strokeWidth: 1,
  4408. markerEnd: 'arrow'
  4409. }
  4410. }
  4411. });
  4412. Annotation.types.verticalLine = VerticalLine;
  4413. return VerticalLine;
  4414. });
  4415. _registerModule(_modules, 'annotations/types/Measure.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  4416. var extend = U.extend,
  4417. isNumber = U.isNumber;
  4418. var Annotation = H.Annotation,
  4419. ControlPoint = Annotation.ControlPoint,
  4420. merge = H.merge;
  4421. /**
  4422. * @class
  4423. * @extends Annotation
  4424. * @memberOf Annotation
  4425. */
  4426. function Measure() {
  4427. Annotation.apply(this, arguments);
  4428. }
  4429. Annotation.types.measure = Measure;
  4430. H.extendAnnotation(Measure, null,
  4431. /** @lends Annotation.Measure# */
  4432. {
  4433. /**
  4434. * Init annotation object.
  4435. */
  4436. init: function () {
  4437. Annotation.prototype.init.apply(this, arguments);
  4438. this.offsetX = 0;
  4439. this.offsetY = 0;
  4440. this.resizeX = 0;
  4441. this.resizeY = 0;
  4442. this.calculations.init.call(this);
  4443. this.addValues();
  4444. this.addShapes();
  4445. },
  4446. /**
  4447. * Overrides default setter to get axes from typeOptions.
  4448. */
  4449. setClipAxes: function () {
  4450. this.clipXAxis = this.chart.xAxis[this.options.typeOptions.xAxis];
  4451. this.clipYAxis = this.chart.yAxis[this.options.typeOptions.yAxis];
  4452. },
  4453. /**
  4454. * Get measure points configuration objects.
  4455. *
  4456. * @return {Array<Highcharts.MockPointOptions>}
  4457. */
  4458. pointsOptions: function () {
  4459. return this.options.options.points;
  4460. },
  4461. /**
  4462. * Get points configuration objects for shapes.
  4463. *
  4464. * @return {Array<Highcharts.MockPointOptions>}
  4465. */
  4466. shapePointsOptions: function () {
  4467. var options = this.options.typeOptions,
  4468. xAxis = options.xAxis,
  4469. yAxis = options.yAxis;
  4470. return [
  4471. {
  4472. x: this.xAxisMin,
  4473. y: this.yAxisMin,
  4474. xAxis: xAxis,
  4475. yAxis: yAxis
  4476. },
  4477. {
  4478. x: this.xAxisMax,
  4479. y: this.yAxisMin,
  4480. xAxis: xAxis,
  4481. yAxis: yAxis
  4482. },
  4483. {
  4484. x: this.xAxisMax,
  4485. y: this.yAxisMax,
  4486. xAxis: xAxis,
  4487. yAxis: yAxis
  4488. },
  4489. {
  4490. x: this.xAxisMin,
  4491. y: this.yAxisMax,
  4492. xAxis: xAxis,
  4493. yAxis: yAxis
  4494. }
  4495. ];
  4496. },
  4497. addControlPoints: function () {
  4498. var selectType = this.options.typeOptions.selectType,
  4499. controlPoint;
  4500. controlPoint = new ControlPoint(
  4501. this.chart,
  4502. this,
  4503. this.options.controlPointOptions,
  4504. 0
  4505. );
  4506. this.controlPoints.push(controlPoint);
  4507. // add extra controlPoint for horizontal and vertical range
  4508. if (selectType !== 'xy') {
  4509. controlPoint = new ControlPoint(
  4510. this.chart,
  4511. this,
  4512. this.options.controlPointOptions,
  4513. 1
  4514. );
  4515. this.controlPoints.push(controlPoint);
  4516. }
  4517. },
  4518. /**
  4519. * Add label with calculated values (min, max, average, bins).
  4520. *
  4521. * @param {boolean} resize - the flag for resize shape
  4522. */
  4523. addValues: function (resize) {
  4524. var typeOptions = this.options.typeOptions,
  4525. formatter = typeOptions.label.formatter;
  4526. // set xAxisMin, xAxisMax, yAxisMin, yAxisMax
  4527. this.calculations.recalculate.call(this, resize);
  4528. if (!typeOptions.label.enabled) {
  4529. return;
  4530. }
  4531. if (this.labels.length > 0) {
  4532. this.labels[0].text = (formatter && formatter.call(this)) ||
  4533. this.calculations.defaultFormatter.call(this);
  4534. } else {
  4535. this.initLabel(extend({
  4536. shape: 'rect',
  4537. backgroundColor: 'none',
  4538. color: 'black',
  4539. borderWidth: 0,
  4540. dashStyle: 'dash',
  4541. overflow: 'none',
  4542. align: 'left',
  4543. vertical: 'top',
  4544. crop: true,
  4545. point: function (target) {
  4546. var annotation = target.annotation,
  4547. chart = annotation.chart,
  4548. inverted = chart.inverted,
  4549. xAxis = chart.xAxis[typeOptions.xAxis],
  4550. yAxis = chart.yAxis[typeOptions.yAxis],
  4551. top = chart.plotTop,
  4552. left = chart.plotLeft;
  4553. return {
  4554. x: (inverted ? top : 10) +
  4555. xAxis.toPixels(annotation.xAxisMin, !inverted),
  4556. y: (inverted ? -left + 10 : top) +
  4557. yAxis.toPixels(annotation.yAxisMin)
  4558. };
  4559. },
  4560. text: (formatter && formatter.call(this)) ||
  4561. this.calculations.defaultFormatter.call(this)
  4562. }, typeOptions.label));
  4563. }
  4564. },
  4565. /**
  4566. * Crosshair, background (rect)
  4567. */
  4568. addShapes: function () {
  4569. this.addCrosshairs();
  4570. this.addBackground();
  4571. },
  4572. /**
  4573. * Add background shape.
  4574. */
  4575. addBackground: function () {
  4576. var shapePoints = this.shapePointsOptions();
  4577. if (typeof shapePoints[0].x === 'undefined') {
  4578. return;
  4579. }
  4580. this.initShape(extend({
  4581. type: 'path',
  4582. points: this.shapePointsOptions()
  4583. }, this.options.typeOptions.background), false);
  4584. },
  4585. /**
  4586. * Add internal crosshair shapes (on top and bottom)
  4587. */
  4588. addCrosshairs: function () {
  4589. var chart = this.chart,
  4590. options = this.options.typeOptions,
  4591. point = this.options.typeOptions.point,
  4592. xAxis = chart.xAxis[options.xAxis],
  4593. yAxis = chart.yAxis[options.yAxis],
  4594. inverted = chart.inverted,
  4595. xAxisMin = xAxis.toPixels(this.xAxisMin),
  4596. xAxisMax = xAxis.toPixels(this.xAxisMax),
  4597. yAxisMin = yAxis.toPixels(this.yAxisMin),
  4598. yAxisMax = yAxis.toPixels(this.yAxisMax),
  4599. defaultOptions = {
  4600. point: point,
  4601. type: 'path'
  4602. },
  4603. pathH = [],
  4604. pathV = [],
  4605. crosshairOptionsX,
  4606. crosshairOptionsY,
  4607. temp;
  4608. if (inverted) {
  4609. temp = xAxisMin;
  4610. xAxisMin = yAxisMin;
  4611. yAxisMin = temp;
  4612. temp = xAxisMax;
  4613. xAxisMax = yAxisMax;
  4614. yAxisMax = temp;
  4615. }
  4616. // horizontal line
  4617. if (options.crosshairX.enabled) {
  4618. pathH = [
  4619. 'M',
  4620. xAxisMin,
  4621. yAxisMin + ((yAxisMax - yAxisMin) / 2),
  4622. 'L',
  4623. xAxisMax,
  4624. yAxisMin + ((yAxisMax - yAxisMin) / 2)
  4625. ];
  4626. }
  4627. // vertical line
  4628. if (options.crosshairY.enabled) {
  4629. pathV = [
  4630. 'M',
  4631. xAxisMin + ((xAxisMax - xAxisMin) / 2),
  4632. yAxisMin,
  4633. 'L',
  4634. xAxisMin + ((xAxisMax - xAxisMin) / 2),
  4635. yAxisMax
  4636. ];
  4637. }
  4638. // Update existed crosshair
  4639. if (this.shapes.length > 0) {
  4640. this.shapes[0].options.d = pathH;
  4641. this.shapes[1].options.d = pathV;
  4642. } else {
  4643. // Add new crosshairs
  4644. crosshairOptionsX = merge(defaultOptions, options.crosshairX);
  4645. crosshairOptionsY = merge(defaultOptions, options.crosshairY);
  4646. this.initShape(extend({
  4647. d: pathH
  4648. }, crosshairOptionsX), false);
  4649. this.initShape(extend({
  4650. d: pathV
  4651. }, crosshairOptionsY), false);
  4652. }
  4653. },
  4654. onDrag: function (e) {
  4655. var translation = this.mouseMoveToTranslation(e),
  4656. selectType = this.options.typeOptions.selectType,
  4657. x = selectType === 'y' ? 0 : translation.x,
  4658. y = selectType === 'x' ? 0 : translation.y;
  4659. this.translate(x, y);
  4660. this.offsetX += x;
  4661. this.offsetY += y;
  4662. // animation, resize, setStartPoints
  4663. this.redraw(false, false, true);
  4664. },
  4665. /**
  4666. * Translate start or end ("left" or "right") side of the measure.
  4667. * Update start points (startXMin, startXMax, startYMin, startYMax)
  4668. *
  4669. * @param {number} dx - the amount of x translation
  4670. * @param {number} dy - the amount of y translation
  4671. * @param {number} cpIndex - index of control point
  4672. * @param {number} selectType - x / y / xy
  4673. */
  4674. resize: function (dx, dy, cpIndex, selectType) {
  4675. // background shape
  4676. var bckShape = this.shapes[2];
  4677. if (selectType === 'x') {
  4678. if (cpIndex === 0) {
  4679. bckShape.translatePoint(dx, 0, 0);
  4680. bckShape.translatePoint(dx, dy, 3);
  4681. } else {
  4682. bckShape.translatePoint(dx, 0, 1);
  4683. bckShape.translatePoint(dx, dy, 2);
  4684. }
  4685. } else if (selectType === 'y') {
  4686. if (cpIndex === 0) {
  4687. bckShape.translatePoint(0, dy, 0);
  4688. bckShape.translatePoint(0, dy, 1);
  4689. } else {
  4690. bckShape.translatePoint(0, dy, 2);
  4691. bckShape.translatePoint(0, dy, 3);
  4692. }
  4693. } else {
  4694. bckShape.translatePoint(dx, 0, 1);
  4695. bckShape.translatePoint(dx, dy, 2);
  4696. bckShape.translatePoint(0, dy, 3);
  4697. }
  4698. this.calculations.updateStartPoints
  4699. .call(this, false, true, cpIndex, dx, dy);
  4700. this.options.typeOptions.background.height = Math.abs(
  4701. this.startYMax - this.startYMin
  4702. );
  4703. this.options.typeOptions.background.width = Math.abs(
  4704. this.startXMax - this.startXMin
  4705. );
  4706. },
  4707. /**
  4708. * Redraw event which render elements and update start points
  4709. * if needed
  4710. *
  4711. * @param {Boolean} animation
  4712. * @param {Boolean} resize - flag if resized
  4713. * @param {Boolean} setStartPoints - update position of start points
  4714. */
  4715. redraw: function (animation, resize, setStartPoints) {
  4716. this.linkPoints();
  4717. if (!this.graphic) {
  4718. this.render();
  4719. }
  4720. if (setStartPoints) {
  4721. this.calculations.updateStartPoints.call(this, true, false);
  4722. }
  4723. // #11174 - clipBox was not recalculate during resize / redraw
  4724. if (this.clipRect) {
  4725. this.clipRect.animate(this.getClipBox());
  4726. }
  4727. this.addValues(resize);
  4728. this.addCrosshairs();
  4729. this.redrawItems(this.shapes, animation);
  4730. this.redrawItems(this.labels, animation);
  4731. // redraw control point to run positioner
  4732. this.controlPoints.forEach(function (controlPoint) {
  4733. controlPoint.redraw();
  4734. });
  4735. },
  4736. translate: function (dx, dy) {
  4737. this.shapes.forEach(function (item) {
  4738. item.translate(dx, dy);
  4739. });
  4740. this.options.typeOptions.point.x = this.startXMin;
  4741. this.options.typeOptions.point.y = this.startYMin;
  4742. },
  4743. calculations: {
  4744. /**
  4745. * Set starting points
  4746. * @private
  4747. */
  4748. init: function () {
  4749. var options = this.options.typeOptions,
  4750. chart = this.chart,
  4751. getPointPos = this.calculations.getPointPos,
  4752. inverted = chart.inverted,
  4753. xAxis = chart.xAxis[options.xAxis],
  4754. yAxis = chart.yAxis[options.yAxis],
  4755. bck = options.background,
  4756. width = inverted ? bck.height : bck.width,
  4757. height = inverted ? bck.width : bck.height,
  4758. selectType = options.selectType,
  4759. top = inverted ? chart.plotLeft : chart.plotTop,
  4760. left = inverted ? chart.plotTop : chart.plotLeft;
  4761. this.startXMin = options.point.x;
  4762. this.startYMin = options.point.y;
  4763. if (isNumber(width)) {
  4764. this.startXMax = this.startXMin + width;
  4765. } else {
  4766. this.startXMax = getPointPos(
  4767. xAxis,
  4768. this.startXMin,
  4769. parseFloat(width)
  4770. );
  4771. }
  4772. if (isNumber(height)) {
  4773. this.startYMax = this.startYMin - height;
  4774. } else {
  4775. this.startYMax = getPointPos(
  4776. yAxis,
  4777. this.startYMin,
  4778. parseFloat(height)
  4779. );
  4780. }
  4781. // x / y selection type
  4782. if (selectType === 'x') {
  4783. this.startYMin = yAxis.toValue(top);
  4784. this.startYMax = yAxis.toValue(top + yAxis.len);
  4785. } else if (selectType === 'y') {
  4786. this.startXMin = xAxis.toValue(left);
  4787. this.startXMax = xAxis.toValue(left + xAxis.len);
  4788. }
  4789. },
  4790. /**
  4791. * Set current xAxisMin, xAxisMax, yAxisMin, yAxisMax.
  4792. * Calculations of measure values (min, max, average, bins).
  4793. * @private
  4794. * @param {Boolean} resize - flag if shape is resized
  4795. */
  4796. recalculate: function (resize) {
  4797. var calc = this.calculations,
  4798. options = this.options.typeOptions,
  4799. xAxis = this.chart.xAxis[options.xAxis],
  4800. yAxis = this.chart.yAxis[options.yAxis],
  4801. getPointPos = this.calculations.getPointPos,
  4802. offsetX = this.offsetX,
  4803. offsetY = this.offsetY;
  4804. this.xAxisMin = getPointPos(xAxis, this.startXMin, offsetX);
  4805. this.xAxisMax = getPointPos(xAxis, this.startXMax, offsetX);
  4806. this.yAxisMin = getPointPos(yAxis, this.startYMin, offsetY);
  4807. this.yAxisMax = getPointPos(yAxis, this.startYMax, offsetY);
  4808. this.min = calc.min.call(this);
  4809. this.max = calc.max.call(this);
  4810. this.average = calc.average.call(this);
  4811. this.bins = calc.bins.call(this);
  4812. if (resize) {
  4813. this.resize(0, 0);
  4814. }
  4815. },
  4816. /**
  4817. * Set current xAxisMin, xAxisMax, yAxisMin, yAxisMax.
  4818. * Calculations of measure values (min, max, average, bins).
  4819. * @private
  4820. * @param {Object} axis - x or y axis reference
  4821. * @param {Number} value - point's value (x or y)
  4822. * @param {Number} offset - amount of pixels
  4823. */
  4824. getPointPos: function (axis, value, offset) {
  4825. return axis.toValue(
  4826. axis.toPixels(value) + offset
  4827. );
  4828. },
  4829. /**
  4830. * Update position of start points
  4831. * (startXMin, startXMax, startYMin, startYMax)
  4832. * @private
  4833. * @param {Boolean} redraw - flag if shape is redraw
  4834. * @param {Boolean} resize - flag if shape is resized
  4835. * @param {Boolean} cpIndex - index of controlPoint
  4836. */
  4837. updateStartPoints: function (redraw, resize, cpIndex, dx, dy) {
  4838. var options = this.options.typeOptions,
  4839. selectType = options.selectType,
  4840. xAxis = this.chart.xAxis[options.xAxis],
  4841. yAxis = this.chart.yAxis[options.yAxis],
  4842. getPointPos = this.calculations.getPointPos,
  4843. startXMin = this.startXMin,
  4844. startXMax = this.startXMax,
  4845. startYMin = this.startYMin,
  4846. startYMax = this.startYMax,
  4847. offsetX = this.offsetX,
  4848. offsetY = this.offsetY;
  4849. if (resize) {
  4850. if (selectType === 'x') {
  4851. if (cpIndex === 0) {
  4852. this.startXMin = getPointPos(xAxis, startXMin, dx);
  4853. } else {
  4854. this.startXMax = getPointPos(xAxis, startXMax, dx);
  4855. }
  4856. } else if (selectType === 'y') {
  4857. if (cpIndex === 0) {
  4858. this.startYMin = getPointPos(yAxis, startYMin, dy);
  4859. } else {
  4860. this.startYMax = getPointPos(yAxis, startYMax, dy);
  4861. }
  4862. } else {
  4863. this.startXMax = getPointPos(xAxis, startXMax, dx);
  4864. this.startYMax = getPointPos(yAxis, startYMax, dy);
  4865. }
  4866. }
  4867. if (redraw) {
  4868. this.startXMin = getPointPos(xAxis, startXMin, offsetX);
  4869. this.startXMax = getPointPos(xAxis, startXMax, offsetX);
  4870. this.startYMin = getPointPos(yAxis, startYMin, offsetY);
  4871. this.startYMax = getPointPos(yAxis, startYMax, offsetY);
  4872. this.offsetX = 0;
  4873. this.offsetY = 0;
  4874. }
  4875. },
  4876. /**
  4877. * Default formatter of label's content
  4878. * @private
  4879. */
  4880. defaultFormatter: function () {
  4881. return 'Min: ' + this.min +
  4882. '<br>Max: ' + this.max +
  4883. '<br>Average: ' + this.average +
  4884. '<br>Bins: ' + this.bins;
  4885. },
  4886. /**
  4887. * Set values for xAxisMin, xAxisMax, yAxisMin, yAxisMax, also
  4888. * when chart is inverted
  4889. * @private
  4890. */
  4891. getExtremes: function (xAxisMin, xAxisMax, yAxisMin, yAxisMax) {
  4892. return {
  4893. xAxisMin: Math.min(xAxisMax, xAxisMin),
  4894. xAxisMax: Math.max(xAxisMax, xAxisMin),
  4895. yAxisMin: Math.min(yAxisMax, yAxisMin),
  4896. yAxisMax: Math.max(yAxisMax, yAxisMin)
  4897. };
  4898. },
  4899. /**
  4900. * Definitions of calculations (min, max, average, bins)
  4901. * @private
  4902. */
  4903. min: function () {
  4904. var min = Infinity,
  4905. series = this.chart.series,
  4906. ext = this.calculations.getExtremes(
  4907. this.xAxisMin,
  4908. this.xAxisMax,
  4909. this.yAxisMin,
  4910. this.yAxisMax
  4911. ),
  4912. isCalculated = false; // to avoid Infinity in formatter
  4913. series.forEach(function (serie) {
  4914. if (
  4915. serie.visible &&
  4916. serie.options.id !== 'highcharts-navigator-series'
  4917. ) {
  4918. serie.points.forEach(function (point) {
  4919. if (
  4920. !point.isNull &&
  4921. point.y < min &&
  4922. point.x > ext.xAxisMin &&
  4923. point.x <= ext.xAxisMax &&
  4924. point.y > ext.yAxisMin &&
  4925. point.y <= ext.yAxisMax
  4926. ) {
  4927. min = point.y;
  4928. isCalculated = true;
  4929. }
  4930. });
  4931. }
  4932. });
  4933. if (!isCalculated) {
  4934. min = '';
  4935. }
  4936. return min;
  4937. },
  4938. max: function () {
  4939. var max = -Infinity,
  4940. series = this.chart.series,
  4941. ext = this.calculations.getExtremes(
  4942. this.xAxisMin,
  4943. this.xAxisMax,
  4944. this.yAxisMin,
  4945. this.yAxisMax
  4946. ),
  4947. isCalculated = false; // to avoid Infinity in formatter
  4948. series.forEach(function (serie) {
  4949. if (
  4950. serie.visible &&
  4951. serie.options.id !== 'highcharts-navigator-series'
  4952. ) {
  4953. serie.points.forEach(function (point) {
  4954. if (
  4955. !point.isNull &&
  4956. point.y > max &&
  4957. point.x > ext.xAxisMin &&
  4958. point.x <= ext.xAxisMax &&
  4959. point.y > ext.yAxisMin &&
  4960. point.y <= ext.yAxisMax
  4961. ) {
  4962. max = point.y;
  4963. isCalculated = true;
  4964. }
  4965. });
  4966. }
  4967. });
  4968. if (!isCalculated) {
  4969. max = '';
  4970. }
  4971. return max;
  4972. },
  4973. average: function () {
  4974. var average = '';
  4975. if (this.max !== '' && this.min !== '') {
  4976. average = (this.max + this.min) / 2;
  4977. }
  4978. return average;
  4979. },
  4980. bins: function () {
  4981. var bins = 0,
  4982. series = this.chart.series,
  4983. ext = this.calculations.getExtremes(
  4984. this.xAxisMin,
  4985. this.xAxisMax,
  4986. this.yAxisMin,
  4987. this.yAxisMax
  4988. ),
  4989. isCalculated = false; // to avoid Infinity in formatter
  4990. series.forEach(function (serie) {
  4991. if (
  4992. serie.visible &&
  4993. serie.options.id !== 'highcharts-navigator-series'
  4994. ) {
  4995. serie.points.forEach(function (point) {
  4996. if (
  4997. !point.isNull &&
  4998. point.x > ext.xAxisMin &&
  4999. point.x <= ext.xAxisMax &&
  5000. point.y > ext.yAxisMin &&
  5001. point.y <= ext.yAxisMax
  5002. ) {
  5003. bins++;
  5004. isCalculated = true;
  5005. }
  5006. });
  5007. }
  5008. });
  5009. if (!isCalculated) {
  5010. bins = '';
  5011. }
  5012. return bins;
  5013. }
  5014. }
  5015. },
  5016. /**
  5017. * A measure annotation.
  5018. *
  5019. * @extends annotations.crookedLine
  5020. * @excluding labels, labelOptions, shapes, shapeOptions
  5021. * @sample highcharts/annotations-advanced/measure/
  5022. * Measure
  5023. * @product highstock
  5024. * @optionparent annotations.measure
  5025. */
  5026. {
  5027. typeOptions: {
  5028. /**
  5029. * Decides in what dimensions the user can resize by dragging the
  5030. * mouse. Can be one of x, y or xy.
  5031. */
  5032. selectType: 'xy',
  5033. /**
  5034. * This number defines which xAxis the point is connected to.
  5035. * It refers to either the axis id or the index of the axis
  5036. * in the xAxis array.
  5037. */
  5038. xAxis: 0,
  5039. /**
  5040. * This number defines which yAxis the point is connected to.
  5041. * It refers to either the axis id or the index of the axis
  5042. * in the yAxis array.
  5043. */
  5044. yAxis: 0,
  5045. background: {
  5046. /**
  5047. * The color of the rectangle.
  5048. */
  5049. fill: 'rgba(130, 170, 255, 0.4)',
  5050. /**
  5051. * The width of border.
  5052. */
  5053. strokeWidth: 0,
  5054. /**
  5055. * The color of border.
  5056. */
  5057. stroke: void 0
  5058. },
  5059. /**
  5060. * Configure a crosshair that is horizontally placed in middle of
  5061. * rectangle.
  5062. *
  5063. */
  5064. crosshairX: {
  5065. /**
  5066. * Enable or disable the horizontal crosshair.
  5067. *
  5068. */
  5069. enabled: true,
  5070. /**
  5071. * The Z index of the crosshair in annotation.
  5072. */
  5073. zIndex: 6,
  5074. /**
  5075. * The dash or dot style of the crosshair's line. For possible
  5076. * values, see
  5077. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  5078. *
  5079. * @type {Highcharts.DashStyleValue}
  5080. * @default Dash
  5081. */
  5082. dashStyle: 'Dash',
  5083. /**
  5084. * The marker-end defines the arrowhead that will be drawn
  5085. * at the final vertex of the given crosshair's path.
  5086. *
  5087. * @type {string}
  5088. * @default arrow
  5089. */
  5090. markerEnd: 'arrow'
  5091. },
  5092. /**
  5093. * Configure a crosshair that is vertically placed in middle of
  5094. * rectangle.
  5095. */
  5096. crosshairY: {
  5097. /**
  5098. * Enable or disable the vertical crosshair.
  5099. *
  5100. */
  5101. enabled: true,
  5102. /**
  5103. * The Z index of the crosshair in annotation.
  5104. */
  5105. zIndex: 6,
  5106. /**
  5107. * The dash or dot style of the crosshair's line. For possible
  5108. * values, see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  5109. *
  5110. * @type {Highcharts.DashStyleValue}
  5111. * @default Dash
  5112. * @apioption annotations.measure.typeOptions.crosshairY.dashStyle
  5113. *
  5114. */
  5115. dashStyle: 'Dash',
  5116. /**
  5117. * The marker-end defines the arrowhead that will be drawn
  5118. * at the final vertex of the given crosshair's path.
  5119. *
  5120. * @type {string}
  5121. * @default arrow
  5122. * @validvalue ["none", "arrow"]
  5123. *
  5124. */
  5125. markerEnd: 'arrow'
  5126. },
  5127. label: {
  5128. /**
  5129. * Enable or disable the label text (min, max, average,
  5130. * bins values).
  5131. *
  5132. * Defaults to true.
  5133. */
  5134. enabled: true,
  5135. /**
  5136. * CSS styles for the measure label.
  5137. *
  5138. * @type {Highcharts.CSSObject}
  5139. * @default {"color": "#666666", "fontSize": "11px"}
  5140. */
  5141. style: {
  5142. fontSize: '11px',
  5143. color: '#666666'
  5144. },
  5145. /**
  5146. * Formatter function for the label text.
  5147. *
  5148. * Available data are:
  5149. *
  5150. * <table>
  5151. *
  5152. * <tbody>
  5153. *
  5154. * <tr>
  5155. *
  5156. * <td>`this.min`</td>
  5157. *
  5158. * <td>The mininimum value of the points in the selected
  5159. * range.</td>
  5160. *
  5161. * </tr>
  5162. *
  5163. * <tr>
  5164. *
  5165. * <td>`this.max`</td>
  5166. *
  5167. * <td>The maximum value of the points in the selected
  5168. * range.</td>
  5169. *
  5170. * </tr>
  5171. *
  5172. * <tr>
  5173. *
  5174. * <td>`this.average`</td>
  5175. *
  5176. * <td>The average value of the points in the selected
  5177. * range.</td>
  5178. *
  5179. * </tr>
  5180. *
  5181. * <tr>
  5182. *
  5183. * <td>`this.bins`</td>
  5184. *
  5185. * <td>The amount of the points in the selected range.</td>
  5186. *
  5187. * </tr>
  5188. *
  5189. * </table>
  5190. *
  5191. * @type {function}
  5192. *
  5193. */
  5194. formatter: void 0
  5195. }
  5196. },
  5197. controlPointOptions: {
  5198. positioner: function (target) {
  5199. var cpIndex = this.index,
  5200. chart = target.chart,
  5201. options = target.options,
  5202. typeOptions = options.typeOptions,
  5203. selectType = typeOptions.selectType,
  5204. controlPointOptions = options.controlPointOptions,
  5205. inverted = chart.inverted,
  5206. xAxis = chart.xAxis[typeOptions.xAxis],
  5207. yAxis = chart.yAxis[typeOptions.yAxis],
  5208. targetX = target.xAxisMax,
  5209. targetY = target.yAxisMax,
  5210. ext = target.calculations.getExtremes(
  5211. target.xAxisMin,
  5212. target.xAxisMax,
  5213. target.yAxisMin,
  5214. target.yAxisMax
  5215. ),
  5216. x, y;
  5217. if (selectType === 'x') {
  5218. targetY = (ext.yAxisMax - ext.yAxisMin) / 2;
  5219. // first control point
  5220. if (cpIndex === 0) {
  5221. targetX = target.xAxisMin;
  5222. }
  5223. }
  5224. if (selectType === 'y') {
  5225. targetX = ext.xAxisMin +
  5226. ((ext.xAxisMax - ext.xAxisMin) / 2);
  5227. // first control point
  5228. if (cpIndex === 0) {
  5229. targetY = target.yAxisMin;
  5230. }
  5231. }
  5232. if (inverted) {
  5233. x = yAxis.toPixels(targetY);
  5234. y = xAxis.toPixels(targetX);
  5235. } else {
  5236. x = xAxis.toPixels(targetX);
  5237. y = yAxis.toPixels(targetY);
  5238. }
  5239. return {
  5240. x: x - (controlPointOptions.width / 2),
  5241. y: y - (controlPointOptions.height / 2)
  5242. };
  5243. },
  5244. events: {
  5245. drag: function (e, target) {
  5246. var translation = this.mouseMoveToTranslation(e),
  5247. selectType = target.options.typeOptions.selectType,
  5248. index = this.index,
  5249. x = selectType === 'y' ? 0 : translation.x,
  5250. y = selectType === 'x' ? 0 : translation.y;
  5251. target.resize(
  5252. x,
  5253. y,
  5254. index,
  5255. selectType
  5256. );
  5257. target.resizeX += x;
  5258. target.resizeY += y;
  5259. target.redraw(false, true);
  5260. }
  5261. }
  5262. }
  5263. });
  5264. Annotation.types.measure = Measure;
  5265. return Measure;
  5266. });
  5267. _registerModule(_modules, 'mixins/navigation.js', [], function () {
  5268. /**
  5269. *
  5270. * (c) 2010-2018 Paweł Fus
  5271. *
  5272. * License: www.highcharts.com/license
  5273. *
  5274. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5275. *
  5276. * */
  5277. var chartNavigation = {
  5278. /**
  5279. * Initializes `chart.navigation` object which delegates `update()` methods
  5280. * to all other common classes (used in exporting and navigationBindings).
  5281. *
  5282. * @private
  5283. * @param {Highcharts.Chart} chart
  5284. * The chart instance.
  5285. * @return {void}
  5286. */
  5287. initUpdate: function (chart) {
  5288. if (!chart.navigation) {
  5289. chart.navigation = {
  5290. updates: [],
  5291. update: function (options, redraw) {
  5292. this.updates.forEach(function (updateConfig) {
  5293. updateConfig.update.call(updateConfig.context, options, redraw);
  5294. });
  5295. }
  5296. };
  5297. }
  5298. },
  5299. /**
  5300. * Registers an `update()` method in the `chart.navigation` object.
  5301. *
  5302. * @private
  5303. * @param {Highcharts.ChartNavigationUpdateFunction} update
  5304. * The `update()` method that will be called in `chart.update()`.
  5305. * @param {Highcharts.Chart} chart
  5306. * The chart instance. `update()` will use that as a context
  5307. * (`this`).
  5308. * @return {void}
  5309. */
  5310. addUpdate: function (update, chart) {
  5311. if (!chart.navigation) {
  5312. this.initUpdate(chart);
  5313. }
  5314. chart.navigation.updates.push({
  5315. update: update,
  5316. context: chart
  5317. });
  5318. }
  5319. };
  5320. return chartNavigation;
  5321. });
  5322. _registerModule(_modules, 'annotations/navigationBindings.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['mixins/navigation.js']], function (H, U, chartNavigationMixin) {
  5323. /* *
  5324. *
  5325. * (c) 2009-2017 Highsoft, Black Label
  5326. *
  5327. * License: www.highcharts.com/license
  5328. *
  5329. * */
  5330. var attr = U.attr,
  5331. extend = U.extend,
  5332. isArray = U.isArray,
  5333. isNumber = U.isNumber,
  5334. isObject = U.isObject,
  5335. objectEach = U.objectEach,
  5336. pick = U.pick;
  5337. var doc = H.doc,
  5338. win = H.win,
  5339. addEvent = H.addEvent,
  5340. merge = H.merge,
  5341. fireEvent = H.fireEvent,
  5342. PREFIX = 'highcharts-';
  5343. // IE 9-11 polyfill for Element.closest():
  5344. function closestPolyfill(el, s) {
  5345. var ElementProto = win.Element.prototype,
  5346. elementMatches =
  5347. ElementProto.matches ||
  5348. ElementProto.msMatchesSelector ||
  5349. ElementProto.webkitMatchesSelector,
  5350. ret = null;
  5351. if (ElementProto.closest) {
  5352. ret = ElementProto.closest.call(el, s);
  5353. } else {
  5354. do {
  5355. if (elementMatches.call(el, s)) {
  5356. return el;
  5357. }
  5358. el = el.parentElement || el.parentNode;
  5359. } while (el !== null && el.nodeType === 1);
  5360. }
  5361. return ret;
  5362. }
  5363. /**
  5364. * @private
  5365. * @interface bindingsUtils
  5366. */
  5367. var bindingsUtils = {
  5368. /**
  5369. * Update size of background (rect) in some annotations: Measure, Simple
  5370. * Rect.
  5371. *
  5372. * @private
  5373. * @function bindingsUtils.updateRectSize
  5374. *
  5375. * @param {global.Event} event
  5376. * Normalized browser event
  5377. *
  5378. * @param {Highcharts.Annotation} annotation
  5379. * Annotation to be updated
  5380. */
  5381. updateRectSize: function (event, annotation) {
  5382. var chart = annotation.chart,
  5383. options = annotation.options.typeOptions,
  5384. coords = chart.pointer.getCoordinates(event),
  5385. width = coords.xAxis[0].value - options.point.x,
  5386. height = options.point.y - coords.yAxis[0].value;
  5387. annotation.update({
  5388. typeOptions: {
  5389. background: {
  5390. width: chart.inverted ? height : width,
  5391. height: chart.inverted ? width : height
  5392. }
  5393. }
  5394. });
  5395. },
  5396. /**
  5397. * Get field type according to value
  5398. *
  5399. * @private
  5400. * @function bindingsUtils.getFieldType
  5401. *
  5402. * @param {*} value
  5403. * Atomic type (one of: string, number, boolean)
  5404. *
  5405. * @return {string}
  5406. * Field type (one of: text, number, checkbox)
  5407. */
  5408. getFieldType: function (value) {
  5409. return {
  5410. 'string': 'text',
  5411. 'number': 'number',
  5412. 'boolean': 'checkbox'
  5413. }[typeof value];
  5414. }
  5415. };
  5416. H.NavigationBindings = function (chart, options) {
  5417. this.chart = chart;
  5418. this.options = options;
  5419. this.eventsToUnbind = [];
  5420. this.container = doc.getElementsByClassName(
  5421. this.options.bindingsClassName
  5422. );
  5423. };
  5424. // Define which options from annotations should show up in edit box:
  5425. H.NavigationBindings.annotationsEditable = {
  5426. // `typeOptions` are always available
  5427. // Nested and shared options:
  5428. nestedOptions: {
  5429. labelOptions: ['style', 'format', 'backgroundColor'],
  5430. labels: ['style'],
  5431. label: ['style'],
  5432. style: ['fontSize', 'color'],
  5433. background: ['fill', 'strokeWidth', 'stroke'],
  5434. innerBackground: ['fill', 'strokeWidth', 'stroke'],
  5435. outerBackground: ['fill', 'strokeWidth', 'stroke'],
  5436. shapeOptions: ['fill', 'strokeWidth', 'stroke'],
  5437. shapes: ['fill', 'strokeWidth', 'stroke'],
  5438. line: ['strokeWidth', 'stroke'],
  5439. backgroundColors: [true],
  5440. connector: ['fill', 'strokeWidth', 'stroke'],
  5441. crosshairX: ['strokeWidth', 'stroke'],
  5442. crosshairY: ['strokeWidth', 'stroke']
  5443. },
  5444. // Simple shapes:
  5445. circle: ['shapes'],
  5446. verticalLine: [],
  5447. label: ['labelOptions'],
  5448. // Measure
  5449. measure: ['background', 'crosshairY', 'crosshairX'],
  5450. // Others:
  5451. fibonacci: [],
  5452. tunnel: ['background', 'line', 'height'],
  5453. pitchfork: ['innerBackground', 'outerBackground'],
  5454. rect: ['shapes'],
  5455. // Crooked lines, elliots, arrows etc:
  5456. crookedLine: []
  5457. };
  5458. // Define non editable fields per annotation, for example Rectangle inherits
  5459. // options from Measure, but crosshairs are not available
  5460. H.NavigationBindings.annotationsNonEditable = {
  5461. rectangle: ['crosshairX', 'crosshairY', 'label']
  5462. };
  5463. extend(H.NavigationBindings.prototype, {
  5464. // Private properties added by bindings:
  5465. // Active (selected) annotation that is editted through popup/forms
  5466. // activeAnnotation: Annotation
  5467. // Holder for current step, used on mouse move to update bound object
  5468. // mouseMoveEvent: function () {}
  5469. // Next event in `step` array to be called on chart's click
  5470. // nextEvent: function () {}
  5471. // Index in the `step` array of the current event
  5472. // stepIndex: 0
  5473. // Flag to determine if current binding has steps
  5474. // steps: true|false
  5475. // Bindings holder for all events
  5476. // selectedButton: {}
  5477. // Holder for user options, returned from `start` event, and passed on to
  5478. // `step`'s' and `end`.
  5479. // currentUserDetails: {}
  5480. /**
  5481. * Initi all events conencted to NavigationBindings.
  5482. *
  5483. * @private
  5484. * @function Highcharts.NavigationBindings#initEvents
  5485. */
  5486. initEvents: function () {
  5487. var navigation = this,
  5488. chart = navigation.chart,
  5489. bindingsContainer = navigation.container,
  5490. options = navigation.options;
  5491. // Shorthand object for getting events for buttons:
  5492. navigation.boundClassNames = {};
  5493. objectEach(options.bindings, function (value) {
  5494. navigation.boundClassNames[value.className] = value;
  5495. });
  5496. // Handle multiple containers with the same class names:
  5497. [].forEach.call(bindingsContainer, function (subContainer) {
  5498. navigation.eventsToUnbind.push(
  5499. addEvent(
  5500. subContainer,
  5501. 'click',
  5502. function (event) {
  5503. var bindings = navigation.getButtonEvents(
  5504. bindingsContainer,
  5505. event
  5506. );
  5507. if (bindings) {
  5508. navigation.bindingsButtonClick(
  5509. bindings.button,
  5510. bindings.events,
  5511. event
  5512. );
  5513. }
  5514. }
  5515. )
  5516. );
  5517. });
  5518. objectEach(options.events || {}, function (callback, eventName) {
  5519. if (H.isFunction(callback)) {
  5520. navigation.eventsToUnbind.push(
  5521. addEvent(
  5522. navigation,
  5523. eventName,
  5524. callback
  5525. )
  5526. );
  5527. }
  5528. });
  5529. navigation.eventsToUnbind.push(
  5530. addEvent(chart.container, 'click', function (e) {
  5531. if (
  5532. !chart.cancelClick &&
  5533. chart.isInsidePlot(
  5534. e.chartX - chart.plotLeft,
  5535. e.chartY - chart.plotTop
  5536. )
  5537. ) {
  5538. navigation.bindingsChartClick(this, e);
  5539. }
  5540. })
  5541. );
  5542. navigation.eventsToUnbind.push(
  5543. addEvent(chart.container, 'mousemove', function (e) {
  5544. navigation.bindingsContainerMouseMove(this, e);
  5545. })
  5546. );
  5547. },
  5548. /**
  5549. * Common chart.update() delegation, shared between bindings and exporting.
  5550. *
  5551. * @private
  5552. * @function Highcharts.NavigationBindings#initUpdate
  5553. */
  5554. initUpdate: function () {
  5555. var navigation = this;
  5556. chartNavigationMixin.addUpdate(
  5557. function (options) {
  5558. navigation.update(options);
  5559. },
  5560. this.chart
  5561. );
  5562. },
  5563. /**
  5564. * Hook for click on a button, method selcts/unselects buttons,
  5565. * then calls `bindings.init` callback.
  5566. *
  5567. * @private
  5568. * @function Highcharts.NavigationBindings#bindingsButtonClick
  5569. *
  5570. * @param {Highcharts.HTMLDOMElement} [button]
  5571. * Clicked button
  5572. *
  5573. * @param {object} [events]
  5574. * Events passed down from bindings (`init`, `start`, `step`, `end`)
  5575. *
  5576. * @param {global.Event} [clickEvent]
  5577. * Browser's click event
  5578. */
  5579. bindingsButtonClick: function (button, events, clickEvent) {
  5580. var navigation = this,
  5581. chart = navigation.chart;
  5582. if (navigation.selectedButtonElement) {
  5583. fireEvent(
  5584. navigation,
  5585. 'deselectButton',
  5586. { button: navigation.selectedButtonElement }
  5587. );
  5588. if (navigation.nextEvent) {
  5589. // Remove in-progress annotations adders:
  5590. if (
  5591. navigation.currentUserDetails &&
  5592. navigation.currentUserDetails.coll === 'annotations'
  5593. ) {
  5594. chart.removeAnnotation(navigation.currentUserDetails);
  5595. }
  5596. navigation.mouseMoveEvent = navigation.nextEvent = false;
  5597. }
  5598. }
  5599. navigation.selectedButton = events;
  5600. navigation.selectedButtonElement = button;
  5601. fireEvent(navigation, 'selectButton', { button: button });
  5602. // Call "init" event, for example to open modal window
  5603. if (events.init) {
  5604. events.init.call(navigation, button, clickEvent);
  5605. }
  5606. if (events.start || events.steps) {
  5607. chart.renderer.boxWrapper.addClass(PREFIX + 'draw-mode');
  5608. }
  5609. },
  5610. /**
  5611. * Hook for click on a chart, first click on a chart calls `start` event,
  5612. * then on all subsequent clicks iterate over `steps` array.
  5613. * When finished, calls `end` event.
  5614. *
  5615. * @private
  5616. * @function Highcharts.NavigationBindings#bindingsChartClick
  5617. *
  5618. * @param {Highcharts.Chart} chart
  5619. * Chart that click was performed on.
  5620. *
  5621. * @param {global.Event} clickEvent
  5622. * Browser's click event.
  5623. */
  5624. bindingsChartClick: function (chartContainer, clickEvent) {
  5625. var navigation = this,
  5626. chart = navigation.chart,
  5627. selectedButton = navigation.selectedButton,
  5628. svgContainer = chart.renderer.boxWrapper;
  5629. // Click outside popups, should close them and deselect the annotation
  5630. if (
  5631. navigation.activeAnnotation &&
  5632. !clickEvent.activeAnnotation &&
  5633. // Element could be removed in the child action, e.g. button
  5634. clickEvent.target.parentNode &&
  5635. // TO DO: Polyfill for IE11?
  5636. !closestPolyfill(clickEvent.target, '.' + PREFIX + 'popup')
  5637. ) {
  5638. fireEvent(navigation, 'closePopup');
  5639. navigation.deselectAnnotation();
  5640. }
  5641. if (!selectedButton || !selectedButton.start) {
  5642. return;
  5643. }
  5644. if (!navigation.nextEvent) {
  5645. // Call init method:
  5646. navigation.currentUserDetails = selectedButton.start.call(
  5647. navigation,
  5648. clickEvent
  5649. );
  5650. // If steps exists (e.g. Annotations), bind them:
  5651. if (selectedButton.steps) {
  5652. navigation.stepIndex = 0;
  5653. navigation.steps = true;
  5654. navigation.mouseMoveEvent = navigation.nextEvent =
  5655. selectedButton.steps[navigation.stepIndex];
  5656. } else {
  5657. fireEvent(
  5658. navigation,
  5659. 'deselectButton',
  5660. { button: navigation.selectedButtonElement }
  5661. );
  5662. svgContainer.removeClass(PREFIX + 'draw-mode');
  5663. navigation.steps = false;
  5664. navigation.selectedButton = null;
  5665. // First click is also the last one:
  5666. if (selectedButton.end) {
  5667. selectedButton.end.call(
  5668. navigation,
  5669. clickEvent,
  5670. navigation.currentUserDetails
  5671. );
  5672. }
  5673. }
  5674. } else {
  5675. navigation.nextEvent(
  5676. clickEvent,
  5677. navigation.currentUserDetails
  5678. );
  5679. if (navigation.steps) {
  5680. navigation.stepIndex++;
  5681. if (selectedButton.steps[navigation.stepIndex]) {
  5682. // If we have more steps, bind them one by one:
  5683. navigation.mouseMoveEvent = navigation.nextEvent =
  5684. selectedButton.steps[navigation.stepIndex];
  5685. } else {
  5686. fireEvent(
  5687. navigation,
  5688. 'deselectButton',
  5689. { button: navigation.selectedButtonElement }
  5690. );
  5691. svgContainer.removeClass(PREFIX + 'draw-mode');
  5692. // That was the last step, call end():
  5693. if (selectedButton.end) {
  5694. selectedButton.end.call(
  5695. navigation,
  5696. clickEvent,
  5697. navigation.currentUserDetails
  5698. );
  5699. }
  5700. navigation.nextEvent = false;
  5701. navigation.mouseMoveEvent = false;
  5702. navigation.selectedButton = null;
  5703. }
  5704. }
  5705. }
  5706. },
  5707. /**
  5708. * Hook for mouse move on a chart's container. It calls current step.
  5709. *
  5710. * @private
  5711. * @function Highcharts.NavigationBindings#bindingsContainerMouseMove
  5712. *
  5713. * @param {Highcharts.HTMLDOMElement} container
  5714. * Chart's container.
  5715. *
  5716. * @param {global.Event} moveEvent
  5717. * Browser's move event.
  5718. */
  5719. bindingsContainerMouseMove: function (container, moveEvent) {
  5720. if (this.mouseMoveEvent) {
  5721. this.mouseMoveEvent(
  5722. moveEvent,
  5723. this.currentUserDetails
  5724. );
  5725. }
  5726. },
  5727. /**
  5728. * Translate fields (e.g. `params.period` or `marker.styles.color`) to
  5729. * Highcharts options object (e.g. `{ params: { period } }`).
  5730. *
  5731. * @private
  5732. * @function Highcharts.NavigationBindings#fieldsToOptions
  5733. *
  5734. * @param {object} fields
  5735. * Fields from popup form.
  5736. *
  5737. * @param {object} config
  5738. * Default config to be modified.
  5739. *
  5740. * @return {object}
  5741. * Modified config
  5742. */
  5743. fieldsToOptions: function (fields, config) {
  5744. objectEach(fields, function (value, field) {
  5745. var parsedValue = parseFloat(value),
  5746. path = field.split('.'),
  5747. parent = config,
  5748. pathLength = path.length - 1;
  5749. // If it's a number (not "forma" options), parse it:
  5750. if (
  5751. isNumber(parsedValue) &&
  5752. !value.match(/px/g) &&
  5753. !field.match(/format/g)
  5754. ) {
  5755. value = parsedValue;
  5756. }
  5757. // Remove empty strings or values like 0
  5758. if (value !== '' && value !== 'undefined') {
  5759. path.forEach(function (name, index) {
  5760. var nextName = pick(path[index + 1], '');
  5761. if (pathLength === index) {
  5762. // Last index, put value:
  5763. parent[name] = value;
  5764. } else if (!parent[name]) {
  5765. // Create middle property:
  5766. parent[name] = nextName.match(/\d/g) ? [] : {};
  5767. parent = parent[name];
  5768. } else {
  5769. // Jump into next property
  5770. parent = parent[name];
  5771. }
  5772. });
  5773. }
  5774. });
  5775. return config;
  5776. },
  5777. /**
  5778. * Shorthand method to deselect an annotation.
  5779. *
  5780. * @function Highcharts.NavigationBindings#deselectAnnotation
  5781. */
  5782. deselectAnnotation: function () {
  5783. if (this.activeAnnotation) {
  5784. this.activeAnnotation.setControlPointsVisibility(false);
  5785. this.activeAnnotation = false;
  5786. }
  5787. },
  5788. /**
  5789. * Generates API config for popup in the same format as options for
  5790. * Annotation object.
  5791. *
  5792. * @function Highcharts.NavigationBindings#annotationToFields
  5793. *
  5794. * @param {Highcharts.Annotation} annotation
  5795. * Annotations object
  5796. *
  5797. * @return {object}
  5798. * Annotation options to be displayed in popup box
  5799. */
  5800. annotationToFields: function (annotation) {
  5801. var options = annotation.options,
  5802. editables = H.NavigationBindings.annotationsEditable,
  5803. nestedEditables = editables.nestedOptions,
  5804. getFieldType = this.utils.getFieldType,
  5805. type = pick(
  5806. options.type,
  5807. options.shapes && options.shapes[0] &&
  5808. options.shapes[0].type,
  5809. options.labels && options.labels[0] &&
  5810. options.labels[0].itemType,
  5811. 'label'
  5812. ),
  5813. nonEditables = H.NavigationBindings
  5814. .annotationsNonEditable[options.langKey] || [],
  5815. visualOptions = {
  5816. langKey: options.langKey,
  5817. type: type
  5818. };
  5819. /**
  5820. * Nested options traversing. Method goes down to the options and copies
  5821. * allowed options (with values) to new object, which is last parameter:
  5822. * "parent".
  5823. *
  5824. * @private
  5825. * @function Highcharts.NavigationBindings#annotationToFields.traverse
  5826. *
  5827. * @param {*} option
  5828. * Atomic type or object/array
  5829. *
  5830. * @param {string} key
  5831. * Option name, for example "visible" or "x", "y"
  5832. *
  5833. * @param {object} allowed
  5834. * Editables from H.NavigationBindings.annotationsEditable
  5835. *
  5836. * @param {object} parent
  5837. * Where new options will be assigned
  5838. */
  5839. function traverse(option, key, parentEditables, parent) {
  5840. var nextParent;
  5841. if (
  5842. parentEditables &&
  5843. nonEditables.indexOf(key) === -1 &&
  5844. (
  5845. (
  5846. parentEditables.indexOf &&
  5847. parentEditables.indexOf(key)
  5848. ) >= 0 ||
  5849. parentEditables[key] || // nested array
  5850. parentEditables === true // simple array
  5851. )
  5852. ) {
  5853. // Roots:
  5854. if (isArray(option)) {
  5855. parent[key] = [];
  5856. option.forEach(function (arrayOption, i) {
  5857. if (!isObject(arrayOption)) {
  5858. // Simple arrays, e.g. [String, Number, Boolean]
  5859. traverse(
  5860. arrayOption,
  5861. 0,
  5862. nestedEditables[key],
  5863. parent[key]
  5864. );
  5865. } else {
  5866. // Advanced arrays, e.g. [Object, Object]
  5867. parent[key][i] = {};
  5868. objectEach(
  5869. arrayOption,
  5870. function (nestedOption, nestedKey) {
  5871. traverse(
  5872. nestedOption,
  5873. nestedKey,
  5874. nestedEditables[key],
  5875. parent[key][i]
  5876. );
  5877. }
  5878. );
  5879. }
  5880. });
  5881. } else if (isObject(option)) {
  5882. nextParent = {};
  5883. if (isArray(parent)) {
  5884. parent.push(nextParent);
  5885. nextParent[key] = {};
  5886. nextParent = nextParent[key];
  5887. } else {
  5888. parent[key] = nextParent;
  5889. }
  5890. objectEach(option, function (nestedOption, nestedKey) {
  5891. traverse(
  5892. nestedOption,
  5893. nestedKey,
  5894. key === 0 ? parentEditables : nestedEditables[key],
  5895. nextParent
  5896. );
  5897. });
  5898. } else {
  5899. // Leaf:
  5900. if (key === 'format') {
  5901. parent[key] = [
  5902. H.format(
  5903. option,
  5904. annotation.labels[0].points[0]
  5905. ).toString(),
  5906. 'text'
  5907. ];
  5908. } else if (isArray(parent)) {
  5909. parent.push([option, getFieldType(option)]);
  5910. } else {
  5911. parent[key] = [option, getFieldType(option)];
  5912. }
  5913. }
  5914. }
  5915. }
  5916. objectEach(options, function (option, key) {
  5917. if (key === 'typeOptions') {
  5918. visualOptions[key] = {};
  5919. objectEach(options[key], function (typeOption, typeKey) {
  5920. traverse(
  5921. typeOption,
  5922. typeKey,
  5923. nestedEditables,
  5924. visualOptions[key],
  5925. true
  5926. );
  5927. });
  5928. } else {
  5929. traverse(option, key, editables[type], visualOptions);
  5930. }
  5931. });
  5932. return visualOptions;
  5933. },
  5934. /**
  5935. * Get all class names for all parents in the element. Iterates until finds
  5936. * main container.
  5937. *
  5938. * @function Highcharts.NavigationBindings#getClickedClassNames
  5939. *
  5940. * @param {Highcharts.HTMLDOMElement}
  5941. * Container that event is bound to.
  5942. *
  5943. * @param {global.Event} event
  5944. * Browser's event.
  5945. *
  5946. * @return {Array<string>}
  5947. * Array of class names with corresponding elements
  5948. */
  5949. getClickedClassNames: function (container, event) {
  5950. var element = event.target,
  5951. classNames = [],
  5952. elemClassName;
  5953. while (element) {
  5954. elemClassName = attr(element, 'class');
  5955. if (elemClassName) {
  5956. classNames = classNames.concat(
  5957. elemClassName.split(' ').map(
  5958. function (name) { // eslint-disable-line no-loop-func
  5959. return [
  5960. name,
  5961. element
  5962. ];
  5963. }
  5964. )
  5965. );
  5966. }
  5967. element = element.parentNode;
  5968. if (element === container) {
  5969. return classNames;
  5970. }
  5971. }
  5972. return classNames;
  5973. },
  5974. /**
  5975. * Get events bound to a button. It's a custom event delegation to find all
  5976. * events connected to the element.
  5977. *
  5978. * @function Highcharts.NavigationBindings#getButtonEvents
  5979. *
  5980. * @param {Highcharts.HTMLDOMElement}
  5981. * Container that event is bound to.
  5982. *
  5983. * @param {global.Event} event
  5984. * Browser's event.
  5985. *
  5986. * @return {object}
  5987. * Oject with events (init, start, steps, and end)
  5988. */
  5989. getButtonEvents: function (container, event) {
  5990. var navigation = this,
  5991. classNames = this.getClickedClassNames(container, event),
  5992. bindings;
  5993. classNames.forEach(function (className) {
  5994. if (navigation.boundClassNames[className[0]] && !bindings) {
  5995. bindings = {
  5996. events: navigation.boundClassNames[className[0]],
  5997. button: className[1]
  5998. };
  5999. }
  6000. });
  6001. return bindings;
  6002. },
  6003. /**
  6004. * Bindings are just events, so the whole update process is simply
  6005. * removing old events and adding new ones.
  6006. *
  6007. * @private
  6008. * @function Highcharts.NavigationBindings#update
  6009. */
  6010. update: function (options) {
  6011. this.options = merge(true, this.options, options);
  6012. this.removeEvents();
  6013. this.initEvents();
  6014. },
  6015. /**
  6016. * Remove all events created in the navigation.
  6017. *
  6018. * @private
  6019. * @function Highcharts.NavigationBindings#removeEvents
  6020. */
  6021. removeEvents: function () {
  6022. this.eventsToUnbind.forEach(function (unbinder) {
  6023. unbinder();
  6024. });
  6025. },
  6026. destroy: function () {
  6027. this.removeEvents();
  6028. },
  6029. /**
  6030. * General utils for bindings
  6031. *
  6032. * @private
  6033. * @name Highcharts.NavigationBindings#utils
  6034. * @type {bindingsUtils}
  6035. */
  6036. utils: bindingsUtils
  6037. });
  6038. H.Chart.prototype.initNavigationBindings = function () {
  6039. var chart = this,
  6040. options = chart.options;
  6041. if (options && options.navigation && options.navigation.bindings) {
  6042. chart.navigationBindings = new H.NavigationBindings(
  6043. chart,
  6044. options.navigation
  6045. );
  6046. chart.navigationBindings.initEvents();
  6047. chart.navigationBindings.initUpdate();
  6048. }
  6049. };
  6050. addEvent(H.Chart, 'load', function () {
  6051. this.initNavigationBindings();
  6052. });
  6053. addEvent(H.Chart, 'destroy', function () {
  6054. if (this.navigationBindings) {
  6055. this.navigationBindings.destroy();
  6056. }
  6057. });
  6058. addEvent(H.NavigationBindings, 'deselectButton', function () {
  6059. this.selectedButtonElement = null;
  6060. });
  6061. addEvent(H.Annotation, 'remove', function () {
  6062. if (this.chart.navigationBindings) {
  6063. this.chart.navigationBindings.deselectAnnotation();
  6064. }
  6065. });
  6066. // Show edit-annotation form:
  6067. function selectableAnnotation(annotationType) {
  6068. var originalClick = annotationType.prototype.defaultOptions.events &&
  6069. annotationType.prototype.defaultOptions.events.click;
  6070. function selectAndshowPopup(event) {
  6071. var annotation = this,
  6072. navigation = annotation.chart.navigationBindings,
  6073. prevAnnotation = navigation.activeAnnotation;
  6074. if (originalClick) {
  6075. originalClick.click.call(annotation, event);
  6076. }
  6077. if (prevAnnotation !== annotation) {
  6078. // Select current:
  6079. navigation.deselectAnnotation();
  6080. navigation.activeAnnotation = annotation;
  6081. annotation.setControlPointsVisibility(true);
  6082. fireEvent(
  6083. navigation,
  6084. 'showPopup',
  6085. {
  6086. annotation: annotation,
  6087. formType: 'annotation-toolbar',
  6088. options: navigation.annotationToFields(annotation),
  6089. onSubmit: function (data) {
  6090. var config = {},
  6091. typeOptions;
  6092. if (data.actionType === 'remove') {
  6093. navigation.activeAnnotation = false;
  6094. navigation.chart.removeAnnotation(annotation);
  6095. } else {
  6096. navigation.fieldsToOptions(data.fields, config);
  6097. navigation.deselectAnnotation();
  6098. typeOptions = config.typeOptions;
  6099. if (annotation.options.type === 'measure') {
  6100. // Manually disable crooshars according to
  6101. // stroke width of the shape:
  6102. typeOptions.crosshairY.enabled =
  6103. typeOptions.crosshairY.strokeWidth !== 0;
  6104. typeOptions.crosshairX.enabled =
  6105. typeOptions.crosshairX.strokeWidth !== 0;
  6106. }
  6107. annotation.update(config);
  6108. }
  6109. }
  6110. }
  6111. );
  6112. } else {
  6113. // Deselect current:
  6114. navigation.deselectAnnotation();
  6115. fireEvent(navigation, 'closePopup');
  6116. }
  6117. // Let bubble event to chart.click:
  6118. event.activeAnnotation = true;
  6119. }
  6120. H.merge(
  6121. true,
  6122. annotationType.prototype.defaultOptions.events,
  6123. {
  6124. click: selectAndshowPopup
  6125. }
  6126. );
  6127. }
  6128. if (H.Annotation) {
  6129. // Basic shapes:
  6130. selectableAnnotation(H.Annotation);
  6131. // Advanced annotations:
  6132. objectEach(H.Annotation.types, function (annotationType) {
  6133. selectableAnnotation(annotationType);
  6134. });
  6135. }
  6136. H.setOptions({
  6137. /**
  6138. * @optionparent lang
  6139. */
  6140. lang: {
  6141. /**
  6142. * Configure the Popup strings in the chart. Requires the
  6143. * `annotations.js` or `annotations-advanced.src.js` module to be
  6144. * loaded.
  6145. *
  6146. * @since 7.0.0
  6147. * @type {Object}
  6148. * @product highcharts highstock
  6149. */
  6150. navigation: {
  6151. /**
  6152. * Translations for all field names used in popup.
  6153. *
  6154. * @product highcharts highstock
  6155. * @type {Object}
  6156. */
  6157. popup: {
  6158. simpleShapes: 'Simple shapes',
  6159. lines: 'Lines',
  6160. circle: 'Circle',
  6161. rectangle: 'Rectangle',
  6162. label: 'Label',
  6163. shapeOptions: 'Shape options',
  6164. typeOptions: 'Details',
  6165. fill: 'Fill',
  6166. format: 'Text',
  6167. strokeWidth: 'Line width',
  6168. stroke: 'Line color',
  6169. title: 'Title',
  6170. name: 'Name',
  6171. labelOptions: 'Label options',
  6172. labels: 'Labels',
  6173. backgroundColor: 'Background color',
  6174. backgroundColors: 'Background colors',
  6175. borderColor: 'Border color',
  6176. borderRadius: 'Border radius',
  6177. borderWidth: 'Border width',
  6178. style: 'Style',
  6179. padding: 'Padding',
  6180. fontSize: 'Font size',
  6181. color: 'Color',
  6182. height: 'Height',
  6183. shapes: 'Shape options'
  6184. }
  6185. }
  6186. },
  6187. /**
  6188. * @optionparent navigation
  6189. * @product highcharts highstock
  6190. */
  6191. navigation: {
  6192. /**
  6193. * A CSS class name where all bindings will be attached to. Multiple
  6194. * charts on the same page should have separate class names to prevent
  6195. * duplicating events.
  6196. *
  6197. * Default value of versions < 7.0.4 `highcharts-bindings-wrapper`
  6198. *
  6199. * @since 7.0.0
  6200. * @type {string}
  6201. */
  6202. bindingsClassName: 'highcharts-bindings-container',
  6203. /**
  6204. * Bindings definitions for custom HTML buttons. Each binding implements
  6205. * simple event-driven interface:
  6206. *
  6207. * - `className`: classname used to bind event to
  6208. *
  6209. * - `init`: initial event, fired on button click
  6210. *
  6211. * - `start`: fired on first click on a chart
  6212. *
  6213. * - `steps`: array of sequential events fired one after another on each
  6214. * of users clicks
  6215. *
  6216. * - `end`: last event to be called after last step event
  6217. *
  6218. * @type {Highcharts.Dictionary<Highcharts.StockToolsBindingsObject>|*}
  6219. * @sample stock/stocktools/stocktools-thresholds
  6220. * Custom bindings in Highstock
  6221. * @since 7.0.0
  6222. * @product highcharts highstock
  6223. */
  6224. bindings: {
  6225. /**
  6226. * A circle annotation bindings. Includes `start` and one event in
  6227. * `steps` array.
  6228. *
  6229. * @type {Highcharts.StockToolsBindingsObject}
  6230. * @default {"className": "highcharts-circle-annotation", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  6231. */
  6232. circleAnnotation: {
  6233. /** @ignore */
  6234. className: 'highcharts-circle-annotation',
  6235. /** @ignore */
  6236. start: function (e) {
  6237. var coords = this.chart.pointer.getCoordinates(e),
  6238. navigation = this.chart.options.navigation,
  6239. controlPoints = [{
  6240. positioner: function (target) {
  6241. var xy = H.Annotation.MockPoint
  6242. .pointToPixels(
  6243. target.points[0]
  6244. ),
  6245. r = target.options.r;
  6246. return {
  6247. x: xy.x + r * Math.cos(Math.PI / 4) -
  6248. this.graphic.width / 2,
  6249. y: xy.y + r * Math.sin(Math.PI / 4) -
  6250. this.graphic.height / 2
  6251. };
  6252. },
  6253. events: {
  6254. // TRANSFORM RADIUS ACCORDING TO Y
  6255. // TRANSLATION
  6256. drag: function (e, target) {
  6257. var annotation = target.annotation,
  6258. position = this
  6259. .mouseMoveToTranslation(e);
  6260. target.setRadius(
  6261. Math.max(
  6262. target.options.r +
  6263. position.y /
  6264. Math.sin(Math.PI / 4),
  6265. 5
  6266. )
  6267. );
  6268. annotation.options.shapes[0] =
  6269. annotation.userOptions.shapes[0] =
  6270. target.options;
  6271. target.redraw(false);
  6272. }
  6273. }
  6274. }];
  6275. return this.chart.addAnnotation(
  6276. merge(
  6277. {
  6278. langKey: 'circle',
  6279. shapes: [{
  6280. type: 'circle',
  6281. point: {
  6282. xAxis: 0,
  6283. yAxis: 0,
  6284. x: coords.xAxis[0].value,
  6285. y: coords.yAxis[0].value
  6286. },
  6287. r: 5,
  6288. controlPoints: controlPoints
  6289. }]
  6290. },
  6291. navigation
  6292. .annotationsOptions,
  6293. navigation
  6294. .bindings
  6295. .circleAnnotation
  6296. .annotationsOptions
  6297. )
  6298. );
  6299. },
  6300. /** @ignore */
  6301. steps: [
  6302. function (e, annotation) {
  6303. var point = annotation.options.shapes[0].point,
  6304. x = this.chart.xAxis[0].toPixels(point.x),
  6305. y = this.chart.yAxis[0].toPixels(point.y),
  6306. inverted = this.chart.inverted,
  6307. distance = Math.max(
  6308. Math.sqrt(
  6309. Math.pow(
  6310. inverted ? y - e.chartX : x - e.chartX,
  6311. 2
  6312. ) +
  6313. Math.pow(
  6314. inverted ? x - e.chartY : y - e.chartY,
  6315. 2
  6316. )
  6317. ),
  6318. 5
  6319. );
  6320. annotation.update({
  6321. shapes: [{
  6322. r: distance
  6323. }]
  6324. });
  6325. }
  6326. ]
  6327. },
  6328. /**
  6329. * A rectangle annotation bindings. Includes `start` and one event
  6330. * in `steps` array.
  6331. *
  6332. * @type {Highcharts.StockToolsBindingsObject}
  6333. * @default {"className": "highcharts-rectangle-annotation", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  6334. */
  6335. rectangleAnnotation: {
  6336. /** @ignore */
  6337. className: 'highcharts-rectangle-annotation',
  6338. /** @ignore */
  6339. start: function (e) {
  6340. var coords = this.chart.pointer.getCoordinates(e),
  6341. navigation = this.chart.options.navigation,
  6342. x = coords.xAxis[0].value,
  6343. y = coords.yAxis[0].value,
  6344. controlPoints = [{
  6345. positioner: function (annotation) {
  6346. var xy = H.Annotation.MockPoint
  6347. .pointToPixels(
  6348. annotation.shapes[0].points[2]
  6349. );
  6350. return {
  6351. x: xy.x - 4,
  6352. y: xy.y - 4
  6353. };
  6354. },
  6355. events: {
  6356. drag: function (e, target) {
  6357. var coords = this.chart.pointer
  6358. .getCoordinates(e),
  6359. x = coords.xAxis[0].value,
  6360. y = coords.yAxis[0].value,
  6361. shape = target.options.shapes[0],
  6362. points = shape.points;
  6363. // Top right point
  6364. points[1].x = x;
  6365. // Bottom right point (cursor position)
  6366. points[2].x = x;
  6367. points[2].y = y;
  6368. // Bottom left
  6369. points[3].y = y;
  6370. target.options.shapes[0].points = points;
  6371. target.redraw(false);
  6372. }
  6373. }
  6374. }];
  6375. return this.chart.addAnnotation(
  6376. merge(
  6377. {
  6378. langKey: 'rectangle',
  6379. shapes: [{
  6380. type: 'path',
  6381. points: [{
  6382. xAxis: 0,
  6383. yAxis: 0,
  6384. x: x,
  6385. y: y
  6386. }, {
  6387. xAxis: 0,
  6388. yAxis: 0,
  6389. x: x,
  6390. y: y
  6391. }, {
  6392. xAxis: 0,
  6393. yAxis: 0,
  6394. x: x,
  6395. y: y
  6396. }, {
  6397. xAxis: 0,
  6398. yAxis: 0,
  6399. x: x,
  6400. y: y
  6401. }]
  6402. }],
  6403. controlPoints: controlPoints
  6404. },
  6405. navigation
  6406. .annotationsOptions,
  6407. navigation
  6408. .bindings
  6409. .rectangleAnnotation
  6410. .annotationsOptions
  6411. )
  6412. );
  6413. },
  6414. /** @ignore */
  6415. steps: [
  6416. function (e, annotation) {
  6417. var points = annotation.options.shapes[0].points,
  6418. coords = this.chart.pointer.getCoordinates(e),
  6419. x = coords.xAxis[0].value,
  6420. y = coords.yAxis[0].value;
  6421. // Top right point
  6422. points[1].x = x;
  6423. // Bottom right point (cursor position)
  6424. points[2].x = x;
  6425. points[2].y = y;
  6426. // Bottom left
  6427. points[3].y = y;
  6428. annotation.update({
  6429. shapes: [{
  6430. points: points
  6431. }]
  6432. });
  6433. }
  6434. ]
  6435. },
  6436. /**
  6437. * A label annotation bindings. Includes `start` event only.
  6438. *
  6439. * @type {Highcharts.StockToolsBindingsObject}
  6440. * @default {"className": "highcharts-label-annotation", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  6441. */
  6442. labelAnnotation: {
  6443. /** @ignore */
  6444. className: 'highcharts-label-annotation',
  6445. /** @ignore */
  6446. start: function (e) {
  6447. var coords = this.chart.pointer.getCoordinates(e),
  6448. navigation = this.chart.options.navigation,
  6449. controlPoints = [{
  6450. symbol: 'triangle-down',
  6451. positioner: function (target) {
  6452. if (!target.graphic.placed) {
  6453. return {
  6454. x: 0,
  6455. y: -9e7
  6456. };
  6457. }
  6458. var xy = H.Annotation.MockPoint
  6459. .pointToPixels(
  6460. target.points[0]
  6461. );
  6462. return {
  6463. x: xy.x - this.graphic.width / 2,
  6464. y: xy.y - this.graphic.height / 2
  6465. };
  6466. },
  6467. // TRANSLATE POINT/ANCHOR
  6468. events: {
  6469. drag: function (e, target) {
  6470. var xy = this.mouseMoveToTranslation(e);
  6471. target.translatePoint(xy.x, xy.y);
  6472. target.annotation.labels[0].options =
  6473. target.options;
  6474. target.redraw(false);
  6475. }
  6476. }
  6477. }, {
  6478. symbol: 'square',
  6479. positioner: function (target) {
  6480. if (!target.graphic.placed) {
  6481. return {
  6482. x: 0,
  6483. y: -9e7
  6484. };
  6485. }
  6486. return {
  6487. x: target.graphic.alignAttr.x -
  6488. this.graphic.width / 2,
  6489. y: target.graphic.alignAttr.y -
  6490. this.graphic.height / 2
  6491. };
  6492. },
  6493. // TRANSLATE POSITION WITHOUT CHANGING THE
  6494. // ANCHOR
  6495. events: {
  6496. drag: function (e, target) {
  6497. var xy = this.mouseMoveToTranslation(e);
  6498. target.translate(xy.x, xy.y);
  6499. target.annotation.labels[0].options =
  6500. target.options;
  6501. target.redraw(false);
  6502. }
  6503. }
  6504. }];
  6505. return this.chart.addAnnotation(
  6506. merge(
  6507. {
  6508. langKey: 'label',
  6509. labelOptions: {
  6510. format: '{y:.2f}'
  6511. },
  6512. labels: [{
  6513. point: {
  6514. xAxis: 0,
  6515. yAxis: 0,
  6516. x: coords.xAxis[0].value,
  6517. y: coords.yAxis[0].value
  6518. },
  6519. overflow: 'none',
  6520. crop: true,
  6521. controlPoints: controlPoints
  6522. }]
  6523. },
  6524. navigation
  6525. .annotationsOptions,
  6526. navigation
  6527. .bindings
  6528. .labelAnnotation
  6529. .annotationsOptions
  6530. )
  6531. );
  6532. }
  6533. }
  6534. },
  6535. /**
  6536. * Path where Highcharts will look for icons. Change this to use icons
  6537. * from a different server.
  6538. *
  6539. * @type {string}
  6540. * @default https://code.highcharts.com/8.0.0/gfx/stock-icons/
  6541. * @since 7.1.3
  6542. * @apioption navigation.iconsURL
  6543. */
  6544. /**
  6545. * A `showPopup` event. Fired when selecting for example an annotation.
  6546. *
  6547. * @type {Function}
  6548. * @apioption navigation.events.showPopup
  6549. */
  6550. /**
  6551. * A `closePopup` event. Fired when Popup should be hidden, for example
  6552. * when clicking on an annotation again.
  6553. *
  6554. * @type {Function}
  6555. * @apioption navigation.events.closePopup
  6556. */
  6557. /**
  6558. * Event fired on a button click.
  6559. *
  6560. * @type {Function}
  6561. * @sample highcharts/annotations/gui/
  6562. * Change icon in a dropddown on event
  6563. * @sample highcharts/annotations/gui-buttons/
  6564. * Change button class on event
  6565. * @apioption navigation.events.selectButton
  6566. */
  6567. /**
  6568. * Event fired when button state should change, for example after
  6569. * adding an annotation.
  6570. *
  6571. * @type {Function}
  6572. * @sample highcharts/annotations/gui/
  6573. * Change icon in a dropddown on event
  6574. * @sample highcharts/annotations/gui-buttons/
  6575. * Change button class on event
  6576. * @apioption navigation.events.deselectButton
  6577. */
  6578. /**
  6579. * Events to communicate between Stock Tools and custom GUI.
  6580. *
  6581. * @since 7.0.0
  6582. * @product highcharts highstock
  6583. * @optionparent navigation.events
  6584. */
  6585. events: {},
  6586. /**
  6587. * Additional options to be merged into all annotations.
  6588. *
  6589. * @sample stock/stocktools/navigation-annotation-options
  6590. * Set red color of all line annotations
  6591. *
  6592. * @type {Highcharts.AnnotationsOptions}
  6593. * @extends annotations
  6594. * @exclude crookedLine, elliottWave, fibonacci, infinityLine,
  6595. * measure, pitchfork, tunnel, verticalLine
  6596. * @apioption navigation.annotationsOptions
  6597. */
  6598. annotationsOptions: {}
  6599. }
  6600. });
  6601. });
  6602. _registerModule(_modules, 'annotations/popup.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  6603. /* *
  6604. *
  6605. * Popup generator for Stock tools
  6606. *
  6607. * (c) 2009-2017 Sebastian Bochan
  6608. *
  6609. * License: www.highcharts.com/license
  6610. *
  6611. * */
  6612. var defined = U.defined,
  6613. isArray = U.isArray,
  6614. isObject = U.isObject,
  6615. isString = U.isString,
  6616. objectEach = U.objectEach,
  6617. pick = U.pick,
  6618. wrap = U.wrap;
  6619. var addEvent = H.addEvent,
  6620. createElement = H.createElement,
  6621. indexFilter = /\d/g,
  6622. PREFIX = 'highcharts-',
  6623. DIV = 'div',
  6624. INPUT = 'input',
  6625. LABEL = 'label',
  6626. BUTTON = 'button',
  6627. SELECT = 'select',
  6628. OPTION = 'option',
  6629. SPAN = 'span',
  6630. UL = 'ul',
  6631. LI = 'li',
  6632. H3 = 'h3';
  6633. // onContainerMouseDown blocks internal popup events, due to e.preventDefault.
  6634. // Related issue #4606
  6635. wrap(H.Pointer.prototype, 'onContainerMouseDown', function (proceed, e) {
  6636. var popupClass = e.target && e.target.className;
  6637. // elements is not in popup
  6638. if (!(isString(popupClass) &&
  6639. popupClass.indexOf(PREFIX + 'popup-field') >= 0)
  6640. ) {
  6641. proceed.apply(this, Array.prototype.slice.call(arguments, 1));
  6642. }
  6643. });
  6644. H.Popup = function (parentDiv, iconsURL) {
  6645. this.init(parentDiv, iconsURL);
  6646. };
  6647. H.Popup.prototype = {
  6648. /**
  6649. * Initialize the popup. Create base div and add close button.
  6650. * @private
  6651. * @param {HTMLDOMElement} - container where popup should be placed
  6652. * @param {Object} - user options
  6653. * @return {HTMLDOMElement} - return created popup's div
  6654. */
  6655. init: function (parentDiv, iconsURL) {
  6656. // create popup div
  6657. this.container = createElement(DIV, {
  6658. className: PREFIX + 'popup'
  6659. }, null, parentDiv);
  6660. this.lang = this.getLangpack();
  6661. this.iconsURL = iconsURL;
  6662. // add close button
  6663. this.addCloseBtn();
  6664. },
  6665. /**
  6666. * Create HTML element and attach click event (close popup).
  6667. * @private
  6668. */
  6669. addCloseBtn: function () {
  6670. var _self = this,
  6671. closeBtn;
  6672. // create close popup btn
  6673. closeBtn = createElement(DIV, {
  6674. className: PREFIX + 'popup-close'
  6675. }, null, this.container);
  6676. closeBtn.style['background-image'] = 'url(' +
  6677. this.iconsURL + 'close.svg)';
  6678. ['click', 'touchstart'].forEach(function (eventName) {
  6679. addEvent(closeBtn, eventName, function () {
  6680. _self.closePopup();
  6681. });
  6682. });
  6683. },
  6684. /**
  6685. * Create two columns (divs) in HTML.
  6686. * @private
  6687. * @param {HTMLDOMElement} - container of columns
  6688. * @return {Object} - reference to two HTML columns
  6689. */
  6690. addColsContainer: function (container) {
  6691. var rhsCol,
  6692. lhsCol;
  6693. // left column
  6694. lhsCol = createElement(DIV, {
  6695. className: PREFIX + 'popup-lhs-col'
  6696. }, null, container);
  6697. // right column
  6698. rhsCol = createElement(DIV, {
  6699. className: PREFIX + 'popup-rhs-col'
  6700. }, null, container);
  6701. // wrapper content
  6702. createElement(DIV, {
  6703. className: PREFIX + 'popup-rhs-col-wrapper'
  6704. }, null, rhsCol);
  6705. return {
  6706. lhsCol: lhsCol,
  6707. rhsCol: rhsCol
  6708. };
  6709. },
  6710. /**
  6711. * Create input with label.
  6712. * @private
  6713. * @param {String} - chain of fields i.e params.styles.fontSize
  6714. * @param {String} - indicator type
  6715. * @param {HTMLDOMElement} - container where elements should be added
  6716. * @param {String} - dafault value of input i.e period value is 14,
  6717. * extracted from defaultOptions (ADD mode) or series options (EDIT mode)
  6718. */
  6719. addInput: function (option, type, parentDiv, value) {
  6720. var optionParamList = option.split('.'),
  6721. optionName = optionParamList[optionParamList.length - 1],
  6722. lang = this.lang,
  6723. inputName = PREFIX + type + '-' + optionName;
  6724. if (!inputName.match(indexFilter)) {
  6725. // add label
  6726. createElement(
  6727. LABEL, {
  6728. innerHTML: lang[optionName] || optionName,
  6729. htmlFor: inputName
  6730. },
  6731. null,
  6732. parentDiv
  6733. );
  6734. }
  6735. // add input
  6736. createElement(
  6737. INPUT,
  6738. {
  6739. name: inputName,
  6740. value: value[0],
  6741. type: value[1],
  6742. className: PREFIX + 'popup-field'
  6743. },
  6744. null,
  6745. parentDiv
  6746. ).setAttribute(PREFIX + 'data-name', option);
  6747. },
  6748. /**
  6749. * Create button.
  6750. * @private
  6751. * @param {HTMLDOMElement} - container where elements should be added
  6752. * @param {String} - text placed as button label
  6753. * @param {String} - add | edit | remove
  6754. * @param {Function} - on click callback
  6755. * @param {HTMLDOMElement} - container where inputs are generated
  6756. * @return {HTMLDOMElement} - html button
  6757. */
  6758. addButton: function (parentDiv, label, type, callback, fieldsDiv) {
  6759. var _self = this,
  6760. closePopup = this.closePopup,
  6761. getFields = this.getFields,
  6762. button;
  6763. button = createElement(BUTTON, {
  6764. innerHTML: label
  6765. }, null, parentDiv);
  6766. ['click', 'touchstart'].forEach(function (eventName) {
  6767. addEvent(button, eventName, function () {
  6768. closePopup.call(_self);
  6769. return callback(
  6770. getFields(fieldsDiv, type)
  6771. );
  6772. });
  6773. });
  6774. return button;
  6775. },
  6776. /**
  6777. * Get values from all inputs and create JSON.
  6778. * @private
  6779. * @param {HTMLDOMElement} - container where inputs are created
  6780. * @param {String} - add | edit | remove
  6781. * @return {Object} - fields
  6782. */
  6783. getFields: function (parentDiv, type) {
  6784. var inputList = parentDiv.querySelectorAll('input'),
  6785. optionSeries = '#' + PREFIX + 'select-series > option:checked',
  6786. optionVolume = '#' + PREFIX + 'select-volume > option:checked',
  6787. linkedTo = parentDiv.querySelectorAll(optionSeries)[0],
  6788. volumeTo = parentDiv.querySelectorAll(optionVolume)[0],
  6789. seriesId,
  6790. param,
  6791. fieldsOutput;
  6792. fieldsOutput = {
  6793. actionType: type,
  6794. linkedTo: linkedTo && linkedTo.getAttribute('value'),
  6795. fields: { }
  6796. };
  6797. [].forEach.call(inputList, function (input) {
  6798. param = input.getAttribute(PREFIX + 'data-name');
  6799. seriesId = input.getAttribute(PREFIX + 'data-series-id');
  6800. // params
  6801. if (seriesId) {
  6802. fieldsOutput.seriesId = input.value;
  6803. } else if (param) {
  6804. fieldsOutput.fields[param] = input.value;
  6805. } else {
  6806. // type like sma / ema
  6807. fieldsOutput.type = input.value;
  6808. }
  6809. });
  6810. if (volumeTo) {
  6811. fieldsOutput.fields['params.volumeSeriesID'] = volumeTo
  6812. .getAttribute('value');
  6813. }
  6814. return fieldsOutput;
  6815. },
  6816. /**
  6817. * Reset content of the current popup and show.
  6818. * @private
  6819. * @param {Chart} - chart
  6820. * @param {Function} - on click callback
  6821. * @return {Object} - fields
  6822. */
  6823. showPopup: function () {
  6824. var popupDiv = this.container,
  6825. toolbarClass = PREFIX + 'annotation-toolbar',
  6826. popupCloseBtn = popupDiv
  6827. .querySelectorAll('.' + PREFIX + 'popup-close')[0];
  6828. // reset content
  6829. popupDiv.innerHTML = '';
  6830. // reset toolbar styles if exists
  6831. if (popupDiv.className.indexOf(toolbarClass) >= 0) {
  6832. popupDiv.classList.remove(toolbarClass);
  6833. // reset toolbar inline styles
  6834. popupDiv.removeAttribute('style');
  6835. }
  6836. // add close button
  6837. popupDiv.appendChild(popupCloseBtn);
  6838. popupDiv.style.display = 'block';
  6839. },
  6840. /**
  6841. * Hide popup.
  6842. * @private
  6843. */
  6844. closePopup: function () {
  6845. this.popup.container.style.display = 'none';
  6846. },
  6847. /**
  6848. * Create content and show popup.
  6849. * @private
  6850. * @param {String} - type of popup i.e indicators
  6851. * @param {Chart} - chart
  6852. * @param {Object} - options
  6853. * @param {Function} - on click callback
  6854. */
  6855. showForm: function (type, chart, options, callback) {
  6856. this.popup = chart.navigationBindings.popup;
  6857. // show blank popup
  6858. this.showPopup();
  6859. // indicator form
  6860. if (type === 'indicators') {
  6861. this.indicators.addForm.call(this, chart, options, callback);
  6862. }
  6863. // annotation small toolbar
  6864. if (type === 'annotation-toolbar') {
  6865. this.annotations.addToolbar.call(this, chart, options, callback);
  6866. }
  6867. // annotation edit form
  6868. if (type === 'annotation-edit') {
  6869. this.annotations.addForm.call(this, chart, options, callback);
  6870. }
  6871. // flags form - add / edit
  6872. if (type === 'flag') {
  6873. this.annotations.addForm.call(this, chart, options, callback, true);
  6874. }
  6875. },
  6876. /**
  6877. * Return lang definitions for popup.
  6878. * @private
  6879. * @return {Object} - elements translations.
  6880. */
  6881. getLangpack: function () {
  6882. return H.getOptions().lang.navigation.popup;
  6883. },
  6884. annotations: {
  6885. /**
  6886. * Create annotation simple form. It contains two buttons
  6887. * (edit / remove) and text label.
  6888. * @private
  6889. * @param {Chart} - chart
  6890. * @param {Object} - options
  6891. * @param {Function} - on click callback
  6892. */
  6893. addToolbar: function (chart, options, callback) {
  6894. var _self = this,
  6895. lang = this.lang,
  6896. popupDiv = this.popup.container,
  6897. showForm = this.showForm,
  6898. toolbarClass = PREFIX + 'annotation-toolbar',
  6899. button;
  6900. // set small size
  6901. if (popupDiv.className.indexOf(toolbarClass) === -1) {
  6902. popupDiv.className += ' ' + toolbarClass;
  6903. }
  6904. // set position
  6905. popupDiv.style.top = chart.plotTop + 10 + 'px';
  6906. // create label
  6907. createElement(SPAN, {
  6908. innerHTML: pick(
  6909. // Advanced annotations:
  6910. lang[options.langKey] || options.langKey,
  6911. // Basic shapes:
  6912. options.shapes && options.shapes[0].type
  6913. )
  6914. }, null, popupDiv);
  6915. // add buttons
  6916. button = this.addButton(
  6917. popupDiv,
  6918. lang.removeButton || 'remove',
  6919. 'remove',
  6920. callback,
  6921. popupDiv
  6922. );
  6923. button.className += ' ' + PREFIX + 'annotation-remove-button';
  6924. button.style['background-image'] = 'url(' +
  6925. this.iconsURL + 'destroy.svg)';
  6926. button = this.addButton(
  6927. popupDiv,
  6928. lang.editButton || 'edit',
  6929. 'edit',
  6930. function () {
  6931. showForm.call(
  6932. _self,
  6933. 'annotation-edit',
  6934. chart,
  6935. options,
  6936. callback
  6937. );
  6938. },
  6939. popupDiv
  6940. );
  6941. button.className += ' ' + PREFIX + 'annotation-edit-button';
  6942. button.style['background-image'] = 'url(' +
  6943. this.iconsURL + 'edit.svg)';
  6944. },
  6945. /**
  6946. * Create annotation simple form.
  6947. * It contains fields with param names.
  6948. * @private
  6949. * @param {Chart} - chart
  6950. * @param {Object} - options
  6951. * @param {Function} - on click callback
  6952. * @param {Boolean} - if it is a form declared for init annotation
  6953. */
  6954. addForm: function (chart, options, callback, isInit) {
  6955. var popupDiv = this.popup.container,
  6956. lang = this.lang,
  6957. bottomRow,
  6958. lhsCol;
  6959. // create title of annotations
  6960. lhsCol = createElement('h2', {
  6961. innerHTML: lang[options.langKey] || options.langKey,
  6962. className: PREFIX + 'popup-main-title'
  6963. }, null, popupDiv);
  6964. // left column
  6965. lhsCol = createElement(DIV, {
  6966. className: PREFIX + 'popup-lhs-col ' + PREFIX + 'popup-lhs-full'
  6967. }, null, popupDiv);
  6968. bottomRow = createElement(DIV, {
  6969. className: PREFIX + 'popup-bottom-row'
  6970. }, null, popupDiv);
  6971. this.annotations.addFormFields.call(
  6972. this,
  6973. lhsCol,
  6974. chart,
  6975. '',
  6976. options,
  6977. [],
  6978. true
  6979. );
  6980. this.addButton(
  6981. bottomRow,
  6982. isInit ?
  6983. (lang.addButton || 'add') :
  6984. (lang.saveButton || 'save'),
  6985. isInit ? 'add' : 'save',
  6986. callback,
  6987. popupDiv
  6988. );
  6989. },
  6990. /**
  6991. * Create annotation's form fields.
  6992. * @private
  6993. * @param {HTMLDOMElement} - div where inputs are placed
  6994. * @param {Chart} - chart
  6995. * @param {String} - name of parent to create chain of names
  6996. * @param {Object} - options
  6997. * @param {Array} - storage - array where all items are stored
  6998. * @param {Boolean} - isRoot - recursive flag for root
  6999. */
  7000. addFormFields: function (
  7001. parentDiv,
  7002. chart,
  7003. parentNode,
  7004. options,
  7005. storage,
  7006. isRoot
  7007. ) {
  7008. var _self = this,
  7009. addFormFields = this.annotations.addFormFields,
  7010. addInput = this.addInput,
  7011. lang = this.lang,
  7012. parentFullName,
  7013. titleName;
  7014. objectEach(options, function (value, option) {
  7015. // create name like params.styles.fontSize
  7016. parentFullName = parentNode !== '' ?
  7017. parentNode + '.' + option : option;
  7018. if (isObject(value)) {
  7019. if (
  7020. // value is object of options
  7021. !isArray(value) ||
  7022. // array of objects with params. i.e labels in Fibonacci
  7023. (isArray(value) && isObject(value[0]))
  7024. ) {
  7025. titleName = lang[option] || option;
  7026. if (!titleName.match(indexFilter)) {
  7027. storage.push([
  7028. true,
  7029. titleName,
  7030. parentDiv
  7031. ]);
  7032. }
  7033. addFormFields.call(
  7034. _self,
  7035. parentDiv,
  7036. chart,
  7037. parentFullName,
  7038. value,
  7039. storage,
  7040. false
  7041. );
  7042. } else {
  7043. storage.push([
  7044. _self,
  7045. parentFullName,
  7046. 'annotation',
  7047. parentDiv,
  7048. value
  7049. ]);
  7050. }
  7051. }
  7052. });
  7053. if (isRoot) {
  7054. storage = storage.sort(function (a) {
  7055. return a[1].match(/format/g) ? -1 : 1;
  7056. });
  7057. storage.forEach(function (genInput) {
  7058. if (genInput[0] === true) {
  7059. createElement(SPAN, {
  7060. className: PREFIX + 'annotation-title',
  7061. innerHTML: genInput[1]
  7062. }, null, genInput[2]);
  7063. } else {
  7064. addInput.apply(genInput[0], genInput.splice(1));
  7065. }
  7066. });
  7067. }
  7068. }
  7069. },
  7070. indicators: {
  7071. /**
  7072. * Create indicator's form. It contains two tabs (ADD and EDIT) with
  7073. * content.
  7074. * @private
  7075. * @param {Chart} - chart
  7076. * @param {Object} - options
  7077. * @param {Function} - on click callback
  7078. */
  7079. addForm: function (chart, options, callback) {
  7080. var tabsContainers,
  7081. indicators = this.indicators,
  7082. lang = this.lang,
  7083. buttonParentDiv;
  7084. // add tabs
  7085. this.tabs.init.call(this, chart);
  7086. // get all tabs content divs
  7087. tabsContainers = this.popup.container
  7088. .querySelectorAll('.' + PREFIX + 'tab-item-content');
  7089. // ADD tab
  7090. this.addColsContainer(tabsContainers[0]);
  7091. indicators.addIndicatorList.call(
  7092. this,
  7093. chart,
  7094. tabsContainers[0],
  7095. 'add'
  7096. );
  7097. buttonParentDiv = tabsContainers[0]
  7098. .querySelectorAll('.' + PREFIX + 'popup-rhs-col')[0];
  7099. this.addButton(
  7100. buttonParentDiv,
  7101. lang.addButton || 'add',
  7102. 'add',
  7103. callback,
  7104. buttonParentDiv
  7105. );
  7106. // EDIT tab
  7107. this.addColsContainer(tabsContainers[1]);
  7108. indicators.addIndicatorList.call(
  7109. this,
  7110. chart,
  7111. tabsContainers[1],
  7112. 'edit'
  7113. );
  7114. buttonParentDiv = tabsContainers[1]
  7115. .querySelectorAll('.' + PREFIX + 'popup-rhs-col')[0];
  7116. this.addButton(
  7117. buttonParentDiv,
  7118. lang.saveButton || 'save',
  7119. 'edit',
  7120. callback,
  7121. buttonParentDiv
  7122. );
  7123. this.addButton(
  7124. buttonParentDiv,
  7125. lang.removeButton || 'remove',
  7126. 'remove',
  7127. callback,
  7128. buttonParentDiv
  7129. );
  7130. },
  7131. /**
  7132. * Create HTML list of all indicators (ADD mode) or added indicators
  7133. * (EDIT mode).
  7134. * @private
  7135. * @param {Chart} - chart
  7136. * @param {HTMLDOMElement} - container where list is added
  7137. * @param {String} - 'edit' or 'add' mode
  7138. */
  7139. addIndicatorList: function (chart, parentDiv, listType) {
  7140. var _self = this,
  7141. lhsCol = parentDiv
  7142. .querySelectorAll('.' + PREFIX + 'popup-lhs-col')[0],
  7143. rhsCol = parentDiv
  7144. .querySelectorAll('.' + PREFIX + 'popup-rhs-col')[0],
  7145. isEdit = listType === 'edit',
  7146. series = isEdit ? chart.series : // EDIT mode
  7147. chart.options.plotOptions, // ADD mode
  7148. addFormFields = this.indicators.addFormFields,
  7149. rhsColWrapper,
  7150. indicatorList,
  7151. item;
  7152. // create wrapper for list
  7153. indicatorList = createElement(UL, {
  7154. className: PREFIX + 'indicator-list'
  7155. }, null, lhsCol);
  7156. rhsColWrapper = rhsCol
  7157. .querySelectorAll('.' + PREFIX + 'popup-rhs-col-wrapper')[0];
  7158. objectEach(series, function (serie, value) {
  7159. var seriesOptions = serie.options;
  7160. if (
  7161. serie.params ||
  7162. seriesOptions && seriesOptions.params
  7163. ) {
  7164. var indicatorNameType = _self.indicators
  7165. .getNameType(serie, value),
  7166. indicatorType = indicatorNameType.type;
  7167. item = createElement(LI, {
  7168. className: PREFIX + 'indicator-list',
  7169. innerHTML: indicatorNameType.name
  7170. }, null, indicatorList);
  7171. ['click', 'touchstart'].forEach(function (eventName) {
  7172. addEvent(item, eventName, function () {
  7173. addFormFields.call(
  7174. _self,
  7175. chart,
  7176. isEdit ? serie : series[indicatorType],
  7177. indicatorNameType.type,
  7178. rhsColWrapper
  7179. );
  7180. // add hidden input with series.id
  7181. if (isEdit && serie.options) {
  7182. createElement(INPUT, {
  7183. type: 'hidden',
  7184. name: PREFIX + 'id-' + indicatorType,
  7185. value: serie.options.id
  7186. }, null, rhsColWrapper)
  7187. .setAttribute(
  7188. PREFIX + 'data-series-id',
  7189. serie.options.id
  7190. );
  7191. }
  7192. });
  7193. });
  7194. }
  7195. });
  7196. // select first item from the list
  7197. if (indicatorList.childNodes.length > 0) {
  7198. indicatorList.childNodes[0].click();
  7199. }
  7200. },
  7201. /**
  7202. * Extract full name and type of requested indicator.
  7203. * @private
  7204. * @param {Series} - series which name is needed.
  7205. * (EDIT mode - defaultOptions.series, ADD mode - indicator series).
  7206. * @param {String} - indicator type like: sma, ema, etc.
  7207. * @return {Object} - series name and type like: sma, ema, etc.
  7208. */
  7209. getNameType: function (series, type) {
  7210. var options = series.options,
  7211. seriesTypes = H.seriesTypes,
  7212. // add mode
  7213. seriesName = seriesTypes[type] &&
  7214. seriesTypes[type].prototype.nameBase || type.toUpperCase(),
  7215. seriesType = type;
  7216. // edit
  7217. if (options && options.type) {
  7218. seriesType = series.options.type;
  7219. seriesName = series.name;
  7220. }
  7221. return {
  7222. name: seriesName,
  7223. type: seriesType
  7224. };
  7225. },
  7226. /**
  7227. * List all series with unique ID. Its mandatory for indicators to set
  7228. * correct linking.
  7229. * @private
  7230. * @param {String} - indicator type like: sma, ema, etc.
  7231. * @param {String} - type of select i.e series or volume.
  7232. * @param {Chart} - chart
  7233. * @param {HTMLDOMElement} - element where created HTML list is added
  7234. * @param {String} selectedOption
  7235. * optional param for default value in dropdown
  7236. */
  7237. listAllSeries: function (
  7238. type,
  7239. optionName,
  7240. chart,
  7241. parentDiv,
  7242. selectedOption
  7243. ) {
  7244. var selectName = PREFIX + optionName + '-type-' + type,
  7245. lang = this.lang,
  7246. selectBox,
  7247. seriesOptions;
  7248. createElement(
  7249. LABEL, {
  7250. innerHTML: lang[optionName] || optionName,
  7251. htmlFor: selectName
  7252. },
  7253. null,
  7254. parentDiv
  7255. );
  7256. // select type
  7257. selectBox = createElement(
  7258. SELECT,
  7259. {
  7260. name: selectName,
  7261. className: PREFIX + 'popup-field'
  7262. },
  7263. null,
  7264. parentDiv
  7265. );
  7266. selectBox.setAttribute('id', PREFIX + 'select-' + optionName);
  7267. // list all series which have id - mandatory for creating indicator
  7268. chart.series.forEach(function (serie) {
  7269. seriesOptions = serie.options;
  7270. if (
  7271. !seriesOptions.params &&
  7272. seriesOptions.id &&
  7273. seriesOptions.id !== PREFIX + 'navigator-series'
  7274. ) {
  7275. createElement(
  7276. OPTION,
  7277. {
  7278. innerHTML: seriesOptions.name || seriesOptions.id,
  7279. value: seriesOptions.id
  7280. },
  7281. null,
  7282. selectBox
  7283. );
  7284. }
  7285. });
  7286. if (defined(selectedOption)) {
  7287. selectBox.value = selectedOption;
  7288. }
  7289. },
  7290. /**
  7291. * Create typical inputs for chosen indicator. Fields are extracted from
  7292. * defaultOptions (ADD mode) or current indicator (ADD mode). Two extra
  7293. * fields are added:
  7294. * - hidden input - contains indicator type (required for callback)
  7295. * - select - list of series which can be linked with indicator
  7296. * @private
  7297. * @param {Chart} - chart
  7298. * @param {Series} - indicator
  7299. * @param {String} - indicator type like: sma, ema, etc.
  7300. * @param {HTMLDOMElement} - element where created HTML list is added
  7301. */
  7302. addFormFields: function (chart, series, seriesType, rhsColWrapper) {
  7303. var fields = series.params || series.options.params,
  7304. getNameType = this.indicators.getNameType;
  7305. // reset current content
  7306. rhsColWrapper.innerHTML = '';
  7307. // create title (indicator name in the right column)
  7308. createElement(
  7309. H3,
  7310. {
  7311. className: PREFIX + 'indicator-title',
  7312. innerHTML: getNameType(series, seriesType).name
  7313. },
  7314. null,
  7315. rhsColWrapper
  7316. );
  7317. // input type
  7318. createElement(
  7319. INPUT,
  7320. {
  7321. type: 'hidden',
  7322. name: PREFIX + 'type-' + seriesType,
  7323. value: seriesType
  7324. },
  7325. null,
  7326. rhsColWrapper
  7327. );
  7328. // list all series with id
  7329. this.indicators.listAllSeries.call(
  7330. this,
  7331. seriesType,
  7332. 'series',
  7333. chart,
  7334. rhsColWrapper,
  7335. series.linkedParent && fields.volumeSeriesID
  7336. );
  7337. if (fields.volumeSeriesID) {
  7338. this.indicators.listAllSeries.call(
  7339. this,
  7340. seriesType,
  7341. 'volume',
  7342. chart,
  7343. rhsColWrapper,
  7344. series.linkedParent && series.linkedParent.options.id
  7345. );
  7346. }
  7347. // add param fields
  7348. this.indicators.addParamInputs.call(
  7349. this,
  7350. chart,
  7351. 'params',
  7352. fields,
  7353. seriesType,
  7354. rhsColWrapper
  7355. );
  7356. },
  7357. /**
  7358. * Recurent function which lists all fields, from params object and
  7359. * create them as inputs. Each input has unique `data-name` attribute,
  7360. * which keeps chain of fields i.e params.styles.fontSize.
  7361. * @private
  7362. * @param {Chart} - chart
  7363. * @param {String} - name of parent to create chain of names
  7364. * @param {Series} - fields - params which are based for input create
  7365. * @param {String} - indicator type like: sma, ema, etc.
  7366. * @param {HTMLDOMElement} - element where created HTML list is added
  7367. */
  7368. addParamInputs: function (chart, parentNode, fields, type, parentDiv) {
  7369. var _self = this,
  7370. addParamInputs = this.indicators.addParamInputs,
  7371. addInput = this.addInput,
  7372. parentFullName;
  7373. objectEach(fields, function (value, fieldName) {
  7374. // create name like params.styles.fontSize
  7375. parentFullName = parentNode + '.' + fieldName;
  7376. if (isObject(value)) {
  7377. addParamInputs.call(
  7378. _self,
  7379. chart,
  7380. parentFullName,
  7381. value,
  7382. type,
  7383. parentDiv
  7384. );
  7385. } else if (
  7386. // skip volume field which is created by addFormFields
  7387. parentFullName !== 'params.volumeSeriesID'
  7388. ) {
  7389. addInput.call(
  7390. _self,
  7391. parentFullName,
  7392. type,
  7393. parentDiv,
  7394. [value, 'text'] // all inputs are text type
  7395. );
  7396. }
  7397. });
  7398. },
  7399. /**
  7400. * Get amount of indicators added to chart.
  7401. * @private
  7402. * @return {Number} - Amount of indicators
  7403. */
  7404. getAmount: function () {
  7405. var series = this.series,
  7406. counter = 0;
  7407. objectEach(series, function (serie) {
  7408. var seriesOptions = serie.options;
  7409. if (
  7410. serie.params ||
  7411. seriesOptions && seriesOptions.params
  7412. ) {
  7413. counter++;
  7414. }
  7415. });
  7416. return counter;
  7417. }
  7418. },
  7419. tabs: {
  7420. /**
  7421. * Init tabs. Create tab menu items, tabs containers
  7422. * @private
  7423. * @param {Chart} - reference to current chart
  7424. */
  7425. init: function (chart) {
  7426. var tabs = this.tabs,
  7427. indicatorsCount = this.indicators.getAmount.call(chart),
  7428. firstTab; // run by default
  7429. // create menu items
  7430. firstTab = tabs.addMenuItem.call(this, 'add');
  7431. tabs.addMenuItem.call(this, 'edit', indicatorsCount);
  7432. // create tabs containers
  7433. tabs.addContentItem.call(this, 'add');
  7434. tabs.addContentItem.call(this, 'edit');
  7435. tabs.switchTabs.call(this, indicatorsCount);
  7436. // activate first tab
  7437. tabs.selectTab.call(this, firstTab, 0);
  7438. },
  7439. /**
  7440. * Create tab menu item
  7441. * @private
  7442. * @param {String} - `add` or `edit`
  7443. * @param {Number} - Disable tab when 0
  7444. * @return {HTMLDOMElement} - created HTML tab-menu element
  7445. */
  7446. addMenuItem: function (tabName, disableTab) {
  7447. var popupDiv = this.popup.container,
  7448. className = PREFIX + 'tab-item',
  7449. lang = this.lang,
  7450. menuItem;
  7451. if (disableTab === 0) {
  7452. className += ' ' + PREFIX + 'tab-disabled';
  7453. }
  7454. // tab 1
  7455. menuItem = createElement(
  7456. SPAN,
  7457. {
  7458. innerHTML: lang[tabName + 'Button'] || tabName,
  7459. className: className
  7460. },
  7461. null,
  7462. popupDiv
  7463. );
  7464. menuItem.setAttribute(PREFIX + 'data-tab-type', tabName);
  7465. return menuItem;
  7466. },
  7467. /**
  7468. * Create tab content
  7469. * @private
  7470. * @return {HTMLDOMElement} - created HTML tab-content element
  7471. */
  7472. addContentItem: function () {
  7473. var popupDiv = this.popup.container;
  7474. return createElement(
  7475. DIV,
  7476. {
  7477. className: PREFIX + 'tab-item-content'
  7478. },
  7479. null,
  7480. popupDiv
  7481. );
  7482. },
  7483. /**
  7484. * Add click event to each tab
  7485. * @private
  7486. * @param {Number} - Disable tab when 0
  7487. */
  7488. switchTabs: function (disableTab) {
  7489. var _self = this,
  7490. popupDiv = this.popup.container,
  7491. tabs = popupDiv.querySelectorAll('.' + PREFIX + 'tab-item'),
  7492. dataParam;
  7493. tabs.forEach(function (tab, i) {
  7494. dataParam = tab.getAttribute(PREFIX + 'data-tab-type');
  7495. if (dataParam === 'edit' && disableTab === 0) {
  7496. return;
  7497. }
  7498. ['click', 'touchstart'].forEach(function (eventName) {
  7499. addEvent(tab, eventName, function () {
  7500. // reset class on other elements
  7501. _self.tabs.deselectAll.call(_self);
  7502. _self.tabs.selectTab.call(_self, this, i);
  7503. });
  7504. });
  7505. });
  7506. },
  7507. /**
  7508. * Set tab as visible
  7509. * @private
  7510. * @param {HTMLDOMElement} - current tab
  7511. * @param {Number} - Index of tab in menu
  7512. */
  7513. selectTab: function (tab, index) {
  7514. var allTabs = this.popup.container
  7515. .querySelectorAll('.' + PREFIX + 'tab-item-content');
  7516. tab.className += ' ' + PREFIX + 'tab-item-active';
  7517. allTabs[index].className += ' ' + PREFIX + 'tab-item-show';
  7518. },
  7519. /**
  7520. * Set all tabs as invisible.
  7521. * @private
  7522. */
  7523. deselectAll: function () {
  7524. var popupDiv = this.popup.container,
  7525. tabs = popupDiv
  7526. .querySelectorAll('.' + PREFIX + 'tab-item'),
  7527. tabsContent = popupDiv
  7528. .querySelectorAll('.' + PREFIX + 'tab-item-content'),
  7529. i;
  7530. for (i = 0; i < tabs.length; i++) {
  7531. tabs[i].classList.remove(PREFIX + 'tab-item-active');
  7532. tabsContent[i].classList.remove(PREFIX + 'tab-item-show');
  7533. }
  7534. }
  7535. }
  7536. };
  7537. addEvent(H.NavigationBindings, 'showPopup', function (config) {
  7538. if (!this.popup) {
  7539. // Add popup to main container
  7540. this.popup = new H.Popup(
  7541. this.chart.container, (
  7542. this.chart.options.navigation.iconsURL ||
  7543. (
  7544. this.chart.options.stockTools &&
  7545. this.chart.options.stockTools.gui.iconsURL
  7546. ) ||
  7547. 'https://code.highcharts.com/8.0.0/gfx/stock-icons/'
  7548. )
  7549. );
  7550. }
  7551. this.popup.showForm(
  7552. config.formType,
  7553. this.chart,
  7554. config.options,
  7555. config.onSubmit
  7556. );
  7557. });
  7558. addEvent(H.NavigationBindings, 'closePopup', function () {
  7559. if (this.popup) {
  7560. this.popup.closePopup();
  7561. }
  7562. });
  7563. });
  7564. _registerModule(_modules, 'masters/modules/annotations-advanced.src.js', [], function () {
  7565. });
  7566. }));