svg.node.js 175 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164
  1. /*!
  2. * @svgdotjs/svg.js - A lightweight library for manipulating and animating SVG.
  3. * @version 3.2.0
  4. * https://svgjs.dev/
  5. *
  6. * @copyright Wout Fierens <wout@mick-wout.com>
  7. * @license MIT
  8. *
  9. * BUILT: Mon Jun 12 2023 10:34:51 GMT+0200 (Central European Summer Time)
  10. */;
  11. 'use strict';
  12. Object.defineProperty(exports, '__esModule', { value: true });
  13. const methods$1 = {};
  14. const names = [];
  15. function registerMethods(name, m) {
  16. if (Array.isArray(name)) {
  17. for (const _name of name) {
  18. registerMethods(_name, m);
  19. }
  20. return;
  21. }
  22. if (typeof name === 'object') {
  23. for (const _name in name) {
  24. registerMethods(_name, name[_name]);
  25. }
  26. return;
  27. }
  28. addMethodNames(Object.getOwnPropertyNames(m));
  29. methods$1[name] = Object.assign(methods$1[name] || {}, m);
  30. }
  31. function getMethodsFor(name) {
  32. return methods$1[name] || {};
  33. }
  34. function getMethodNames() {
  35. return [...new Set(names)];
  36. }
  37. function addMethodNames(_names) {
  38. names.push(..._names);
  39. }
  40. // Map function
  41. function map(array, block) {
  42. let i;
  43. const il = array.length;
  44. const result = [];
  45. for (i = 0; i < il; i++) {
  46. result.push(block(array[i]));
  47. }
  48. return result;
  49. } // Filter function
  50. function filter(array, block) {
  51. let i;
  52. const il = array.length;
  53. const result = [];
  54. for (i = 0; i < il; i++) {
  55. if (block(array[i])) {
  56. result.push(array[i]);
  57. }
  58. }
  59. return result;
  60. } // Degrees to radians
  61. function radians(d) {
  62. return d % 360 * Math.PI / 180;
  63. } // Radians to degrees
  64. function degrees(r) {
  65. return r * 180 / Math.PI % 360;
  66. } // Convert dash-separated-string to camelCase
  67. function camelCase(s) {
  68. return s.toLowerCase().replace(/-(.)/g, function (m, g) {
  69. return g.toUpperCase();
  70. });
  71. } // Convert camel cased string to dash separated
  72. function unCamelCase(s) {
  73. return s.replace(/([A-Z])/g, function (m, g) {
  74. return '-' + g.toLowerCase();
  75. });
  76. } // Capitalize first letter of a string
  77. function capitalize(s) {
  78. return s.charAt(0).toUpperCase() + s.slice(1);
  79. } // Calculate proportional width and height values when necessary
  80. function proportionalSize(element, width, height, box) {
  81. if (width == null || height == null) {
  82. box = box || element.bbox();
  83. if (width == null) {
  84. width = box.width / box.height * height;
  85. } else if (height == null) {
  86. height = box.height / box.width * width;
  87. }
  88. }
  89. return {
  90. width: width,
  91. height: height
  92. };
  93. }
  94. /**
  95. * This function adds support for string origins.
  96. * It searches for an origin in o.origin o.ox and o.originX.
  97. * This way, origin: {x: 'center', y: 50} can be passed as well as ox: 'center', oy: 50
  98. **/
  99. function getOrigin(o, element) {
  100. const origin = o.origin; // First check if origin is in ox or originX
  101. let ox = o.ox != null ? o.ox : o.originX != null ? o.originX : 'center';
  102. let oy = o.oy != null ? o.oy : o.originY != null ? o.originY : 'center'; // Then check if origin was used and overwrite in that case
  103. if (origin != null) {
  104. [ox, oy] = Array.isArray(origin) ? origin : typeof origin === 'object' ? [origin.x, origin.y] : [origin, origin];
  105. } // Make sure to only call bbox when actually needed
  106. const condX = typeof ox === 'string';
  107. const condY = typeof oy === 'string';
  108. if (condX || condY) {
  109. const {
  110. height,
  111. width,
  112. x,
  113. y
  114. } = element.bbox(); // And only overwrite if string was passed for this specific axis
  115. if (condX) {
  116. ox = ox.includes('left') ? x : ox.includes('right') ? x + width : x + width / 2;
  117. }
  118. if (condY) {
  119. oy = oy.includes('top') ? y : oy.includes('bottom') ? y + height : y + height / 2;
  120. }
  121. } // Return the origin as it is if it wasn't a string
  122. return [ox, oy];
  123. }
  124. var utils = {
  125. __proto__: null,
  126. map: map,
  127. filter: filter,
  128. radians: radians,
  129. degrees: degrees,
  130. camelCase: camelCase,
  131. unCamelCase: unCamelCase,
  132. capitalize: capitalize,
  133. proportionalSize: proportionalSize,
  134. getOrigin: getOrigin
  135. };
  136. // Default namespaces
  137. const svg = 'http://www.w3.org/2000/svg';
  138. const html = 'http://www.w3.org/1999/xhtml';
  139. const xmlns = 'http://www.w3.org/2000/xmlns/';
  140. const xlink = 'http://www.w3.org/1999/xlink';
  141. const svgjs = 'http://svgjs.dev/svgjs';
  142. var namespaces = {
  143. __proto__: null,
  144. svg: svg,
  145. html: html,
  146. xmlns: xmlns,
  147. xlink: xlink,
  148. svgjs: svgjs
  149. };
  150. const globals = {
  151. window: typeof window === 'undefined' ? null : window,
  152. document: typeof document === 'undefined' ? null : document
  153. };
  154. function registerWindow(win = null, doc = null) {
  155. globals.window = win;
  156. globals.document = doc;
  157. }
  158. const save = {};
  159. function saveWindow() {
  160. save.window = globals.window;
  161. save.document = globals.document;
  162. }
  163. function restoreWindow() {
  164. globals.window = save.window;
  165. globals.document = save.document;
  166. }
  167. function withWindow(win, fn) {
  168. saveWindow();
  169. registerWindow(win, win.document);
  170. fn(win, win.document);
  171. restoreWindow();
  172. }
  173. function getWindow() {
  174. return globals.window;
  175. }
  176. class Base {// constructor (node/*, {extensions = []} */) {
  177. // // this.tags = []
  178. // //
  179. // // for (let extension of extensions) {
  180. // // extension.setup.call(this, node)
  181. // // this.tags.push(extension.name)
  182. // // }
  183. // }
  184. }
  185. const elements = {};
  186. const root = '___SYMBOL___ROOT___'; // Method for element creation
  187. function create(name, ns = svg) {
  188. // create element
  189. return globals.document.createElementNS(ns, name);
  190. }
  191. function makeInstance(element, isHTML = false) {
  192. if (element instanceof Base) return element;
  193. if (typeof element === 'object') {
  194. return adopter(element);
  195. }
  196. if (element == null) {
  197. return new elements[root]();
  198. }
  199. if (typeof element === 'string' && element.charAt(0) !== '<') {
  200. return adopter(globals.document.querySelector(element));
  201. } // Make sure, that HTML elements are created with the correct namespace
  202. const wrapper = isHTML ? globals.document.createElement('div') : create('svg');
  203. wrapper.innerHTML = element; // We can use firstChild here because we know,
  204. // that the first char is < and thus an element
  205. element = adopter(wrapper.firstChild); // make sure, that element doesn't have its wrapper attached
  206. wrapper.removeChild(wrapper.firstChild);
  207. return element;
  208. }
  209. function nodeOrNew(name, node) {
  210. return node && node.ownerDocument && node instanceof node.ownerDocument.defaultView.Node ? node : create(name);
  211. } // Adopt existing svg elements
  212. function adopt(node) {
  213. // check for presence of node
  214. if (!node) return null; // make sure a node isn't already adopted
  215. if (node.instance instanceof Base) return node.instance;
  216. if (node.nodeName === '#document-fragment') {
  217. return new elements.Fragment(node);
  218. } // initialize variables
  219. let className = capitalize(node.nodeName || 'Dom'); // Make sure that gradients are adopted correctly
  220. if (className === 'LinearGradient' || className === 'RadialGradient') {
  221. className = 'Gradient'; // Fallback to Dom if element is not known
  222. } else if (!elements[className]) {
  223. className = 'Dom';
  224. }
  225. return new elements[className](node);
  226. }
  227. let adopter = adopt;
  228. function mockAdopt(mock = adopt) {
  229. adopter = mock;
  230. }
  231. function register(element, name = element.name, asRoot = false) {
  232. elements[name] = element;
  233. if (asRoot) elements[root] = element;
  234. addMethodNames(Object.getOwnPropertyNames(element.prototype));
  235. return element;
  236. }
  237. function getClass(name) {
  238. return elements[name];
  239. } // Element id sequence
  240. let did = 1000; // Get next named element id
  241. function eid(name) {
  242. return 'Svgjs' + capitalize(name) + did++;
  243. } // Deep new id assignment
  244. function assignNewId(node) {
  245. // do the same for SVG child nodes as well
  246. for (let i = node.children.length - 1; i >= 0; i--) {
  247. assignNewId(node.children[i]);
  248. }
  249. if (node.id) {
  250. node.id = eid(node.nodeName);
  251. return node;
  252. }
  253. return node;
  254. } // Method for extending objects
  255. function extend(modules, methods) {
  256. let key, i;
  257. modules = Array.isArray(modules) ? modules : [modules];
  258. for (i = modules.length - 1; i >= 0; i--) {
  259. for (key in methods) {
  260. modules[i].prototype[key] = methods[key];
  261. }
  262. }
  263. }
  264. function wrapWithAttrCheck(fn) {
  265. return function (...args) {
  266. const o = args[args.length - 1];
  267. if (o && o.constructor === Object && !(o instanceof Array)) {
  268. return fn.apply(this, args.slice(0, -1)).attr(o);
  269. } else {
  270. return fn.apply(this, args);
  271. }
  272. };
  273. }
  274. function siblings() {
  275. return this.parent().children();
  276. } // Get the current position siblings
  277. function position() {
  278. return this.parent().index(this);
  279. } // Get the next element (will return null if there is none)
  280. function next() {
  281. return this.siblings()[this.position() + 1];
  282. } // Get the next element (will return null if there is none)
  283. function prev() {
  284. return this.siblings()[this.position() - 1];
  285. } // Send given element one step forward
  286. function forward() {
  287. const i = this.position();
  288. const p = this.parent(); // move node one step forward
  289. p.add(this.remove(), i + 1);
  290. return this;
  291. } // Send given element one step backward
  292. function backward() {
  293. const i = this.position();
  294. const p = this.parent();
  295. p.add(this.remove(), i ? i - 1 : 0);
  296. return this;
  297. } // Send given element all the way to the front
  298. function front() {
  299. const p = this.parent(); // Move node forward
  300. p.add(this.remove());
  301. return this;
  302. } // Send given element all the way to the back
  303. function back() {
  304. const p = this.parent(); // Move node back
  305. p.add(this.remove(), 0);
  306. return this;
  307. } // Inserts a given element before the targeted element
  308. function before(element) {
  309. element = makeInstance(element);
  310. element.remove();
  311. const i = this.position();
  312. this.parent().add(element, i);
  313. return this;
  314. } // Inserts a given element after the targeted element
  315. function after(element) {
  316. element = makeInstance(element);
  317. element.remove();
  318. const i = this.position();
  319. this.parent().add(element, i + 1);
  320. return this;
  321. }
  322. function insertBefore(element) {
  323. element = makeInstance(element);
  324. element.before(this);
  325. return this;
  326. }
  327. function insertAfter(element) {
  328. element = makeInstance(element);
  329. element.after(this);
  330. return this;
  331. }
  332. registerMethods('Dom', {
  333. siblings,
  334. position,
  335. next,
  336. prev,
  337. forward,
  338. backward,
  339. front,
  340. back,
  341. before,
  342. after,
  343. insertBefore,
  344. insertAfter
  345. });
  346. // Parse unit value
  347. const numberAndUnit = /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i; // Parse hex value
  348. const hex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; // Parse rgb value
  349. const rgb = /rgb\((\d+),(\d+),(\d+)\)/; // Parse reference id
  350. const reference = /(#[a-z_][a-z0-9\-_]*)/i; // splits a transformation chain
  351. const transforms = /\)\s*,?\s*/; // Whitespace
  352. const whitespace = /\s/g; // Test hex value
  353. const isHex = /^#[a-f0-9]{3}$|^#[a-f0-9]{6}$/i; // Test rgb value
  354. const isRgb = /^rgb\(/; // Test for blank string
  355. const isBlank = /^(\s+)?$/; // Test for numeric string
  356. const isNumber = /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i; // Test for image url
  357. const isImage = /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i; // split at whitespace and comma
  358. const delimiter = /[\s,]+/; // Test for path letter
  359. const isPathLetter = /[MLHVCSQTAZ]/i;
  360. var regex = {
  361. __proto__: null,
  362. numberAndUnit: numberAndUnit,
  363. hex: hex,
  364. rgb: rgb,
  365. reference: reference,
  366. transforms: transforms,
  367. whitespace: whitespace,
  368. isHex: isHex,
  369. isRgb: isRgb,
  370. isBlank: isBlank,
  371. isNumber: isNumber,
  372. isImage: isImage,
  373. delimiter: delimiter,
  374. isPathLetter: isPathLetter
  375. };
  376. function classes() {
  377. const attr = this.attr('class');
  378. return attr == null ? [] : attr.trim().split(delimiter);
  379. } // Return true if class exists on the node, false otherwise
  380. function hasClass(name) {
  381. return this.classes().indexOf(name) !== -1;
  382. } // Add class to the node
  383. function addClass(name) {
  384. if (!this.hasClass(name)) {
  385. const array = this.classes();
  386. array.push(name);
  387. this.attr('class', array.join(' '));
  388. }
  389. return this;
  390. } // Remove class from the node
  391. function removeClass(name) {
  392. if (this.hasClass(name)) {
  393. this.attr('class', this.classes().filter(function (c) {
  394. return c !== name;
  395. }).join(' '));
  396. }
  397. return this;
  398. } // Toggle the presence of a class on the node
  399. function toggleClass(name) {
  400. return this.hasClass(name) ? this.removeClass(name) : this.addClass(name);
  401. }
  402. registerMethods('Dom', {
  403. classes,
  404. hasClass,
  405. addClass,
  406. removeClass,
  407. toggleClass
  408. });
  409. function css(style, val) {
  410. const ret = {};
  411. if (arguments.length === 0) {
  412. // get full style as object
  413. this.node.style.cssText.split(/\s*;\s*/).filter(function (el) {
  414. return !!el.length;
  415. }).forEach(function (el) {
  416. const t = el.split(/\s*:\s*/);
  417. ret[t[0]] = t[1];
  418. });
  419. return ret;
  420. }
  421. if (arguments.length < 2) {
  422. // get style properties as array
  423. if (Array.isArray(style)) {
  424. for (const name of style) {
  425. const cased = camelCase(name);
  426. ret[name] = this.node.style[cased];
  427. }
  428. return ret;
  429. } // get style for property
  430. if (typeof style === 'string') {
  431. return this.node.style[camelCase(style)];
  432. } // set styles in object
  433. if (typeof style === 'object') {
  434. for (const name in style) {
  435. // set empty string if null/undefined/'' was given
  436. this.node.style[camelCase(name)] = style[name] == null || isBlank.test(style[name]) ? '' : style[name];
  437. }
  438. }
  439. } // set style for property
  440. if (arguments.length === 2) {
  441. this.node.style[camelCase(style)] = val == null || isBlank.test(val) ? '' : val;
  442. }
  443. return this;
  444. } // Show element
  445. function show() {
  446. return this.css('display', '');
  447. } // Hide element
  448. function hide() {
  449. return this.css('display', 'none');
  450. } // Is element visible?
  451. function visible() {
  452. return this.css('display') !== 'none';
  453. }
  454. registerMethods('Dom', {
  455. css,
  456. show,
  457. hide,
  458. visible
  459. });
  460. function data(a, v, r) {
  461. if (a == null) {
  462. // get an object of attributes
  463. return this.data(map(filter(this.node.attributes, el => el.nodeName.indexOf('data-') === 0), el => el.nodeName.slice(5)));
  464. } else if (a instanceof Array) {
  465. const data = {};
  466. for (const key of a) {
  467. data[key] = this.data(key);
  468. }
  469. return data;
  470. } else if (typeof a === 'object') {
  471. for (v in a) {
  472. this.data(v, a[v]);
  473. }
  474. } else if (arguments.length < 2) {
  475. try {
  476. return JSON.parse(this.attr('data-' + a));
  477. } catch (e) {
  478. return this.attr('data-' + a);
  479. }
  480. } else {
  481. this.attr('data-' + a, v === null ? null : r === true || typeof v === 'string' || typeof v === 'number' ? v : JSON.stringify(v));
  482. }
  483. return this;
  484. }
  485. registerMethods('Dom', {
  486. data
  487. });
  488. function remember(k, v) {
  489. // remember every item in an object individually
  490. if (typeof arguments[0] === 'object') {
  491. for (const key in k) {
  492. this.remember(key, k[key]);
  493. }
  494. } else if (arguments.length === 1) {
  495. // retrieve memory
  496. return this.memory()[k];
  497. } else {
  498. // store memory
  499. this.memory()[k] = v;
  500. }
  501. return this;
  502. } // Erase a given memory
  503. function forget() {
  504. if (arguments.length === 0) {
  505. this._memory = {};
  506. } else {
  507. for (let i = arguments.length - 1; i >= 0; i--) {
  508. delete this.memory()[arguments[i]];
  509. }
  510. }
  511. return this;
  512. } // This triggers creation of a new hidden class which is not performant
  513. // However, this function is not rarely used so it will not happen frequently
  514. // Return local memory object
  515. function memory() {
  516. return this._memory = this._memory || {};
  517. }
  518. registerMethods('Dom', {
  519. remember,
  520. forget,
  521. memory
  522. });
  523. function sixDigitHex(hex) {
  524. return hex.length === 4 ? ['#', hex.substring(1, 2), hex.substring(1, 2), hex.substring(2, 3), hex.substring(2, 3), hex.substring(3, 4), hex.substring(3, 4)].join('') : hex;
  525. }
  526. function componentHex(component) {
  527. const integer = Math.round(component);
  528. const bounded = Math.max(0, Math.min(255, integer));
  529. const hex = bounded.toString(16);
  530. return hex.length === 1 ? '0' + hex : hex;
  531. }
  532. function is(object, space) {
  533. for (let i = space.length; i--;) {
  534. if (object[space[i]] == null) {
  535. return false;
  536. }
  537. }
  538. return true;
  539. }
  540. function getParameters(a, b) {
  541. const params = is(a, 'rgb') ? {
  542. _a: a.r,
  543. _b: a.g,
  544. _c: a.b,
  545. _d: 0,
  546. space: 'rgb'
  547. } : is(a, 'xyz') ? {
  548. _a: a.x,
  549. _b: a.y,
  550. _c: a.z,
  551. _d: 0,
  552. space: 'xyz'
  553. } : is(a, 'hsl') ? {
  554. _a: a.h,
  555. _b: a.s,
  556. _c: a.l,
  557. _d: 0,
  558. space: 'hsl'
  559. } : is(a, 'lab') ? {
  560. _a: a.l,
  561. _b: a.a,
  562. _c: a.b,
  563. _d: 0,
  564. space: 'lab'
  565. } : is(a, 'lch') ? {
  566. _a: a.l,
  567. _b: a.c,
  568. _c: a.h,
  569. _d: 0,
  570. space: 'lch'
  571. } : is(a, 'cmyk') ? {
  572. _a: a.c,
  573. _b: a.m,
  574. _c: a.y,
  575. _d: a.k,
  576. space: 'cmyk'
  577. } : {
  578. _a: 0,
  579. _b: 0,
  580. _c: 0,
  581. space: 'rgb'
  582. };
  583. params.space = b || params.space;
  584. return params;
  585. }
  586. function cieSpace(space) {
  587. if (space === 'lab' || space === 'xyz' || space === 'lch') {
  588. return true;
  589. } else {
  590. return false;
  591. }
  592. }
  593. function hueToRgb(p, q, t) {
  594. if (t < 0) t += 1;
  595. if (t > 1) t -= 1;
  596. if (t < 1 / 6) return p + (q - p) * 6 * t;
  597. if (t < 1 / 2) return q;
  598. if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
  599. return p;
  600. }
  601. class Color {
  602. constructor(...inputs) {
  603. this.init(...inputs);
  604. } // Test if given value is a color
  605. static isColor(color) {
  606. return color && (color instanceof Color || this.isRgb(color) || this.test(color));
  607. } // Test if given value is an rgb object
  608. static isRgb(color) {
  609. return color && typeof color.r === 'number' && typeof color.g === 'number' && typeof color.b === 'number';
  610. }
  611. /*
  612. Generating random colors
  613. */
  614. static random(mode = 'vibrant', t, u) {
  615. // Get the math modules
  616. const {
  617. random,
  618. round,
  619. sin,
  620. PI: pi
  621. } = Math; // Run the correct generator
  622. if (mode === 'vibrant') {
  623. const l = (81 - 57) * random() + 57;
  624. const c = (83 - 45) * random() + 45;
  625. const h = 360 * random();
  626. const color = new Color(l, c, h, 'lch');
  627. return color;
  628. } else if (mode === 'sine') {
  629. t = t == null ? random() : t;
  630. const r = round(80 * sin(2 * pi * t / 0.5 + 0.01) + 150);
  631. const g = round(50 * sin(2 * pi * t / 0.5 + 4.6) + 200);
  632. const b = round(100 * sin(2 * pi * t / 0.5 + 2.3) + 150);
  633. const color = new Color(r, g, b);
  634. return color;
  635. } else if (mode === 'pastel') {
  636. const l = (94 - 86) * random() + 86;
  637. const c = (26 - 9) * random() + 9;
  638. const h = 360 * random();
  639. const color = new Color(l, c, h, 'lch');
  640. return color;
  641. } else if (mode === 'dark') {
  642. const l = 10 + 10 * random();
  643. const c = (125 - 75) * random() + 86;
  644. const h = 360 * random();
  645. const color = new Color(l, c, h, 'lch');
  646. return color;
  647. } else if (mode === 'rgb') {
  648. const r = 255 * random();
  649. const g = 255 * random();
  650. const b = 255 * random();
  651. const color = new Color(r, g, b);
  652. return color;
  653. } else if (mode === 'lab') {
  654. const l = 100 * random();
  655. const a = 256 * random() - 128;
  656. const b = 256 * random() - 128;
  657. const color = new Color(l, a, b, 'lab');
  658. return color;
  659. } else if (mode === 'grey') {
  660. const grey = 255 * random();
  661. const color = new Color(grey, grey, grey);
  662. return color;
  663. } else {
  664. throw new Error('Unsupported random color mode');
  665. }
  666. } // Test if given value is a color string
  667. static test(color) {
  668. return typeof color === 'string' && (isHex.test(color) || isRgb.test(color));
  669. }
  670. cmyk() {
  671. // Get the rgb values for the current color
  672. const {
  673. _a,
  674. _b,
  675. _c
  676. } = this.rgb();
  677. const [r, g, b] = [_a, _b, _c].map(v => v / 255); // Get the cmyk values in an unbounded format
  678. const k = Math.min(1 - r, 1 - g, 1 - b);
  679. if (k === 1) {
  680. // Catch the black case
  681. return new Color(0, 0, 0, 1, 'cmyk');
  682. }
  683. const c = (1 - r - k) / (1 - k);
  684. const m = (1 - g - k) / (1 - k);
  685. const y = (1 - b - k) / (1 - k); // Construct the new color
  686. const color = new Color(c, m, y, k, 'cmyk');
  687. return color;
  688. }
  689. hsl() {
  690. // Get the rgb values
  691. const {
  692. _a,
  693. _b,
  694. _c
  695. } = this.rgb();
  696. const [r, g, b] = [_a, _b, _c].map(v => v / 255); // Find the maximum and minimum values to get the lightness
  697. const max = Math.max(r, g, b);
  698. const min = Math.min(r, g, b);
  699. const l = (max + min) / 2; // If the r, g, v values are identical then we are grey
  700. const isGrey = max === min; // Calculate the hue and saturation
  701. const delta = max - min;
  702. const s = isGrey ? 0 : l > 0.5 ? delta / (2 - max - min) : delta / (max + min);
  703. const h = isGrey ? 0 : max === r ? ((g - b) / delta + (g < b ? 6 : 0)) / 6 : max === g ? ((b - r) / delta + 2) / 6 : max === b ? ((r - g) / delta + 4) / 6 : 0; // Construct and return the new color
  704. const color = new Color(360 * h, 100 * s, 100 * l, 'hsl');
  705. return color;
  706. }
  707. init(a = 0, b = 0, c = 0, d = 0, space = 'rgb') {
  708. // This catches the case when a falsy value is passed like ''
  709. a = !a ? 0 : a; // Reset all values in case the init function is rerun with new color space
  710. if (this.space) {
  711. for (const component in this.space) {
  712. delete this[this.space[component]];
  713. }
  714. }
  715. if (typeof a === 'number') {
  716. // Allow for the case that we don't need d...
  717. space = typeof d === 'string' ? d : space;
  718. d = typeof d === 'string' ? 0 : d; // Assign the values straight to the color
  719. Object.assign(this, {
  720. _a: a,
  721. _b: b,
  722. _c: c,
  723. _d: d,
  724. space
  725. }); // If the user gave us an array, make the color from it
  726. } else if (a instanceof Array) {
  727. this.space = b || (typeof a[3] === 'string' ? a[3] : a[4]) || 'rgb';
  728. Object.assign(this, {
  729. _a: a[0],
  730. _b: a[1],
  731. _c: a[2],
  732. _d: a[3] || 0
  733. });
  734. } else if (a instanceof Object) {
  735. // Set the object up and assign its values directly
  736. const values = getParameters(a, b);
  737. Object.assign(this, values);
  738. } else if (typeof a === 'string') {
  739. if (isRgb.test(a)) {
  740. const noWhitespace = a.replace(whitespace, '');
  741. const [_a, _b, _c] = rgb.exec(noWhitespace).slice(1, 4).map(v => parseInt(v));
  742. Object.assign(this, {
  743. _a,
  744. _b,
  745. _c,
  746. _d: 0,
  747. space: 'rgb'
  748. });
  749. } else if (isHex.test(a)) {
  750. const hexParse = v => parseInt(v, 16);
  751. const [, _a, _b, _c] = hex.exec(sixDigitHex(a)).map(hexParse);
  752. Object.assign(this, {
  753. _a,
  754. _b,
  755. _c,
  756. _d: 0,
  757. space: 'rgb'
  758. });
  759. } else throw Error('Unsupported string format, can\'t construct Color');
  760. } // Now add the components as a convenience
  761. const {
  762. _a,
  763. _b,
  764. _c,
  765. _d
  766. } = this;
  767. const components = this.space === 'rgb' ? {
  768. r: _a,
  769. g: _b,
  770. b: _c
  771. } : this.space === 'xyz' ? {
  772. x: _a,
  773. y: _b,
  774. z: _c
  775. } : this.space === 'hsl' ? {
  776. h: _a,
  777. s: _b,
  778. l: _c
  779. } : this.space === 'lab' ? {
  780. l: _a,
  781. a: _b,
  782. b: _c
  783. } : this.space === 'lch' ? {
  784. l: _a,
  785. c: _b,
  786. h: _c
  787. } : this.space === 'cmyk' ? {
  788. c: _a,
  789. m: _b,
  790. y: _c,
  791. k: _d
  792. } : {};
  793. Object.assign(this, components);
  794. }
  795. lab() {
  796. // Get the xyz color
  797. const {
  798. x,
  799. y,
  800. z
  801. } = this.xyz(); // Get the lab components
  802. const l = 116 * y - 16;
  803. const a = 500 * (x - y);
  804. const b = 200 * (y - z); // Construct and return a new color
  805. const color = new Color(l, a, b, 'lab');
  806. return color;
  807. }
  808. lch() {
  809. // Get the lab color directly
  810. const {
  811. l,
  812. a,
  813. b
  814. } = this.lab(); // Get the chromaticity and the hue using polar coordinates
  815. const c = Math.sqrt(a ** 2 + b ** 2);
  816. let h = 180 * Math.atan2(b, a) / Math.PI;
  817. if (h < 0) {
  818. h *= -1;
  819. h = 360 - h;
  820. } // Make a new color and return it
  821. const color = new Color(l, c, h, 'lch');
  822. return color;
  823. }
  824. /*
  825. Conversion Methods
  826. */
  827. rgb() {
  828. if (this.space === 'rgb') {
  829. return this;
  830. } else if (cieSpace(this.space)) {
  831. // Convert to the xyz color space
  832. let {
  833. x,
  834. y,
  835. z
  836. } = this;
  837. if (this.space === 'lab' || this.space === 'lch') {
  838. // Get the values in the lab space
  839. let {
  840. l,
  841. a,
  842. b
  843. } = this;
  844. if (this.space === 'lch') {
  845. const {
  846. c,
  847. h
  848. } = this;
  849. const dToR = Math.PI / 180;
  850. a = c * Math.cos(dToR * h);
  851. b = c * Math.sin(dToR * h);
  852. } // Undo the nonlinear function
  853. const yL = (l + 16) / 116;
  854. const xL = a / 500 + yL;
  855. const zL = yL - b / 200; // Get the xyz values
  856. const ct = 16 / 116;
  857. const mx = 0.008856;
  858. const nm = 7.787;
  859. x = 0.95047 * (xL ** 3 > mx ? xL ** 3 : (xL - ct) / nm);
  860. y = 1.00000 * (yL ** 3 > mx ? yL ** 3 : (yL - ct) / nm);
  861. z = 1.08883 * (zL ** 3 > mx ? zL ** 3 : (zL - ct) / nm);
  862. } // Convert xyz to unbounded rgb values
  863. const rU = x * 3.2406 + y * -1.5372 + z * -0.4986;
  864. const gU = x * -0.9689 + y * 1.8758 + z * 0.0415;
  865. const bU = x * 0.0557 + y * -0.2040 + z * 1.0570; // Convert the values to true rgb values
  866. const pow = Math.pow;
  867. const bd = 0.0031308;
  868. const r = rU > bd ? 1.055 * pow(rU, 1 / 2.4) - 0.055 : 12.92 * rU;
  869. const g = gU > bd ? 1.055 * pow(gU, 1 / 2.4) - 0.055 : 12.92 * gU;
  870. const b = bU > bd ? 1.055 * pow(bU, 1 / 2.4) - 0.055 : 12.92 * bU; // Make and return the color
  871. const color = new Color(255 * r, 255 * g, 255 * b);
  872. return color;
  873. } else if (this.space === 'hsl') {
  874. // https://bgrins.github.io/TinyColor/docs/tinycolor.html
  875. // Get the current hsl values
  876. let {
  877. h,
  878. s,
  879. l
  880. } = this;
  881. h /= 360;
  882. s /= 100;
  883. l /= 100; // If we are grey, then just make the color directly
  884. if (s === 0) {
  885. l *= 255;
  886. const color = new Color(l, l, l);
  887. return color;
  888. } // TODO I have no idea what this does :D If you figure it out, tell me!
  889. const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  890. const p = 2 * l - q; // Get the rgb values
  891. const r = 255 * hueToRgb(p, q, h + 1 / 3);
  892. const g = 255 * hueToRgb(p, q, h);
  893. const b = 255 * hueToRgb(p, q, h - 1 / 3); // Make a new color
  894. const color = new Color(r, g, b);
  895. return color;
  896. } else if (this.space === 'cmyk') {
  897. // https://gist.github.com/felipesabino/5066336
  898. // Get the normalised cmyk values
  899. const {
  900. c,
  901. m,
  902. y,
  903. k
  904. } = this; // Get the rgb values
  905. const r = 255 * (1 - Math.min(1, c * (1 - k) + k));
  906. const g = 255 * (1 - Math.min(1, m * (1 - k) + k));
  907. const b = 255 * (1 - Math.min(1, y * (1 - k) + k)); // Form the color and return it
  908. const color = new Color(r, g, b);
  909. return color;
  910. } else {
  911. return this;
  912. }
  913. }
  914. toArray() {
  915. const {
  916. _a,
  917. _b,
  918. _c,
  919. _d,
  920. space
  921. } = this;
  922. return [_a, _b, _c, _d, space];
  923. }
  924. toHex() {
  925. const [r, g, b] = this._clamped().map(componentHex);
  926. return `#${r}${g}${b}`;
  927. }
  928. toRgb() {
  929. const [rV, gV, bV] = this._clamped();
  930. const string = `rgb(${rV},${gV},${bV})`;
  931. return string;
  932. }
  933. toString() {
  934. return this.toHex();
  935. }
  936. xyz() {
  937. // Normalise the red, green and blue values
  938. const {
  939. _a: r255,
  940. _b: g255,
  941. _c: b255
  942. } = this.rgb();
  943. const [r, g, b] = [r255, g255, b255].map(v => v / 255); // Convert to the lab rgb space
  944. const rL = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
  945. const gL = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
  946. const bL = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92; // Convert to the xyz color space without bounding the values
  947. const xU = (rL * 0.4124 + gL * 0.3576 + bL * 0.1805) / 0.95047;
  948. const yU = (rL * 0.2126 + gL * 0.7152 + bL * 0.0722) / 1.00000;
  949. const zU = (rL * 0.0193 + gL * 0.1192 + bL * 0.9505) / 1.08883; // Get the proper xyz values by applying the bounding
  950. const x = xU > 0.008856 ? Math.pow(xU, 1 / 3) : 7.787 * xU + 16 / 116;
  951. const y = yU > 0.008856 ? Math.pow(yU, 1 / 3) : 7.787 * yU + 16 / 116;
  952. const z = zU > 0.008856 ? Math.pow(zU, 1 / 3) : 7.787 * zU + 16 / 116; // Make and return the color
  953. const color = new Color(x, y, z, 'xyz');
  954. return color;
  955. }
  956. /*
  957. Input and Output methods
  958. */
  959. _clamped() {
  960. const {
  961. _a,
  962. _b,
  963. _c
  964. } = this.rgb();
  965. const {
  966. max,
  967. min,
  968. round
  969. } = Math;
  970. const format = v => max(0, min(round(v), 255));
  971. return [_a, _b, _c].map(format);
  972. }
  973. /*
  974. Constructing colors
  975. */
  976. }
  977. class Point {
  978. // Initialize
  979. constructor(...args) {
  980. this.init(...args);
  981. } // Clone point
  982. clone() {
  983. return new Point(this);
  984. }
  985. init(x, y) {
  986. const base = {
  987. x: 0,
  988. y: 0
  989. }; // ensure source as object
  990. const source = Array.isArray(x) ? {
  991. x: x[0],
  992. y: x[1]
  993. } : typeof x === 'object' ? {
  994. x: x.x,
  995. y: x.y
  996. } : {
  997. x: x,
  998. y: y
  999. }; // merge source
  1000. this.x = source.x == null ? base.x : source.x;
  1001. this.y = source.y == null ? base.y : source.y;
  1002. return this;
  1003. }
  1004. toArray() {
  1005. return [this.x, this.y];
  1006. }
  1007. transform(m) {
  1008. return this.clone().transformO(m);
  1009. } // Transform point with matrix
  1010. transformO(m) {
  1011. if (!Matrix.isMatrixLike(m)) {
  1012. m = new Matrix(m);
  1013. }
  1014. const {
  1015. x,
  1016. y
  1017. } = this; // Perform the matrix multiplication
  1018. this.x = m.a * x + m.c * y + m.e;
  1019. this.y = m.b * x + m.d * y + m.f;
  1020. return this;
  1021. }
  1022. }
  1023. function point(x, y) {
  1024. return new Point(x, y).transformO(this.screenCTM().inverseO());
  1025. }
  1026. function closeEnough(a, b, threshold) {
  1027. return Math.abs(b - a) < (threshold || 1e-6);
  1028. }
  1029. class Matrix {
  1030. constructor(...args) {
  1031. this.init(...args);
  1032. }
  1033. static formatTransforms(o) {
  1034. // Get all of the parameters required to form the matrix
  1035. const flipBoth = o.flip === 'both' || o.flip === true;
  1036. const flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1;
  1037. const flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1;
  1038. const skewX = o.skew && o.skew.length ? o.skew[0] : isFinite(o.skew) ? o.skew : isFinite(o.skewX) ? o.skewX : 0;
  1039. const skewY = o.skew && o.skew.length ? o.skew[1] : isFinite(o.skew) ? o.skew : isFinite(o.skewY) ? o.skewY : 0;
  1040. const scaleX = o.scale && o.scale.length ? o.scale[0] * flipX : isFinite(o.scale) ? o.scale * flipX : isFinite(o.scaleX) ? o.scaleX * flipX : flipX;
  1041. const scaleY = o.scale && o.scale.length ? o.scale[1] * flipY : isFinite(o.scale) ? o.scale * flipY : isFinite(o.scaleY) ? o.scaleY * flipY : flipY;
  1042. const shear = o.shear || 0;
  1043. const theta = o.rotate || o.theta || 0;
  1044. const origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY);
  1045. const ox = origin.x;
  1046. const oy = origin.y; // We need Point to be invalid if nothing was passed because we cannot default to 0 here. That is why NaN
  1047. const position = new Point(o.position || o.px || o.positionX || NaN, o.py || o.positionY || NaN);
  1048. const px = position.x;
  1049. const py = position.y;
  1050. const translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY);
  1051. const tx = translate.x;
  1052. const ty = translate.y;
  1053. const relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY);
  1054. const rx = relative.x;
  1055. const ry = relative.y; // Populate all of the values
  1056. return {
  1057. scaleX,
  1058. scaleY,
  1059. skewX,
  1060. skewY,
  1061. shear,
  1062. theta,
  1063. rx,
  1064. ry,
  1065. tx,
  1066. ty,
  1067. ox,
  1068. oy,
  1069. px,
  1070. py
  1071. };
  1072. }
  1073. static fromArray(a) {
  1074. return {
  1075. a: a[0],
  1076. b: a[1],
  1077. c: a[2],
  1078. d: a[3],
  1079. e: a[4],
  1080. f: a[5]
  1081. };
  1082. }
  1083. static isMatrixLike(o) {
  1084. return o.a != null || o.b != null || o.c != null || o.d != null || o.e != null || o.f != null;
  1085. } // left matrix, right matrix, target matrix which is overwritten
  1086. static matrixMultiply(l, r, o) {
  1087. // Work out the product directly
  1088. const a = l.a * r.a + l.c * r.b;
  1089. const b = l.b * r.a + l.d * r.b;
  1090. const c = l.a * r.c + l.c * r.d;
  1091. const d = l.b * r.c + l.d * r.d;
  1092. const e = l.e + l.a * r.e + l.c * r.f;
  1093. const f = l.f + l.b * r.e + l.d * r.f; // make sure to use local variables because l/r and o could be the same
  1094. o.a = a;
  1095. o.b = b;
  1096. o.c = c;
  1097. o.d = d;
  1098. o.e = e;
  1099. o.f = f;
  1100. return o;
  1101. }
  1102. around(cx, cy, matrix) {
  1103. return this.clone().aroundO(cx, cy, matrix);
  1104. } // Transform around a center point
  1105. aroundO(cx, cy, matrix) {
  1106. const dx = cx || 0;
  1107. const dy = cy || 0;
  1108. return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy);
  1109. } // Clones this matrix
  1110. clone() {
  1111. return new Matrix(this);
  1112. } // Decomposes this matrix into its affine parameters
  1113. decompose(cx = 0, cy = 0) {
  1114. // Get the parameters from the matrix
  1115. const a = this.a;
  1116. const b = this.b;
  1117. const c = this.c;
  1118. const d = this.d;
  1119. const e = this.e;
  1120. const f = this.f; // Figure out if the winding direction is clockwise or counterclockwise
  1121. const determinant = a * d - b * c;
  1122. const ccw = determinant > 0 ? 1 : -1; // Since we only shear in x, we can use the x basis to get the x scale
  1123. // and the rotation of the resulting matrix
  1124. const sx = ccw * Math.sqrt(a * a + b * b);
  1125. const thetaRad = Math.atan2(ccw * b, ccw * a);
  1126. const theta = 180 / Math.PI * thetaRad;
  1127. const ct = Math.cos(thetaRad);
  1128. const st = Math.sin(thetaRad); // We can then solve the y basis vector simultaneously to get the other
  1129. // two affine parameters directly from these parameters
  1130. const lam = (a * c + b * d) / determinant;
  1131. const sy = c * sx / (lam * a - b) || d * sx / (lam * b + a); // Use the translations
  1132. const tx = e - cx + cx * ct * sx + cy * (lam * ct * sx - st * sy);
  1133. const ty = f - cy + cx * st * sx + cy * (lam * st * sx + ct * sy); // Construct the decomposition and return it
  1134. return {
  1135. // Return the affine parameters
  1136. scaleX: sx,
  1137. scaleY: sy,
  1138. shear: lam,
  1139. rotate: theta,
  1140. translateX: tx,
  1141. translateY: ty,
  1142. originX: cx,
  1143. originY: cy,
  1144. // Return the matrix parameters
  1145. a: this.a,
  1146. b: this.b,
  1147. c: this.c,
  1148. d: this.d,
  1149. e: this.e,
  1150. f: this.f
  1151. };
  1152. } // Check if two matrices are equal
  1153. equals(other) {
  1154. if (other === this) return true;
  1155. const comp = new Matrix(other);
  1156. return closeEnough(this.a, comp.a) && closeEnough(this.b, comp.b) && closeEnough(this.c, comp.c) && closeEnough(this.d, comp.d) && closeEnough(this.e, comp.e) && closeEnough(this.f, comp.f);
  1157. } // Flip matrix on x or y, at a given offset
  1158. flip(axis, around) {
  1159. return this.clone().flipO(axis, around);
  1160. }
  1161. flipO(axis, around) {
  1162. return axis === 'x' ? this.scaleO(-1, 1, around, 0) : axis === 'y' ? this.scaleO(1, -1, 0, around) : this.scaleO(-1, -1, axis, around || axis); // Define an x, y flip point
  1163. } // Initialize
  1164. init(source) {
  1165. const base = Matrix.fromArray([1, 0, 0, 1, 0, 0]); // ensure source as object
  1166. source = source instanceof Element ? source.matrixify() : typeof source === 'string' ? Matrix.fromArray(source.split(delimiter).map(parseFloat)) : Array.isArray(source) ? Matrix.fromArray(source) : typeof source === 'object' && Matrix.isMatrixLike(source) ? source : typeof source === 'object' ? new Matrix().transform(source) : arguments.length === 6 ? Matrix.fromArray([].slice.call(arguments)) : base; // Merge the source matrix with the base matrix
  1167. this.a = source.a != null ? source.a : base.a;
  1168. this.b = source.b != null ? source.b : base.b;
  1169. this.c = source.c != null ? source.c : base.c;
  1170. this.d = source.d != null ? source.d : base.d;
  1171. this.e = source.e != null ? source.e : base.e;
  1172. this.f = source.f != null ? source.f : base.f;
  1173. return this;
  1174. }
  1175. inverse() {
  1176. return this.clone().inverseO();
  1177. } // Inverses matrix
  1178. inverseO() {
  1179. // Get the current parameters out of the matrix
  1180. const a = this.a;
  1181. const b = this.b;
  1182. const c = this.c;
  1183. const d = this.d;
  1184. const e = this.e;
  1185. const f = this.f; // Invert the 2x2 matrix in the top left
  1186. const det = a * d - b * c;
  1187. if (!det) throw new Error('Cannot invert ' + this); // Calculate the top 2x2 matrix
  1188. const na = d / det;
  1189. const nb = -b / det;
  1190. const nc = -c / det;
  1191. const nd = a / det; // Apply the inverted matrix to the top right
  1192. const ne = -(na * e + nc * f);
  1193. const nf = -(nb * e + nd * f); // Construct the inverted matrix
  1194. this.a = na;
  1195. this.b = nb;
  1196. this.c = nc;
  1197. this.d = nd;
  1198. this.e = ne;
  1199. this.f = nf;
  1200. return this;
  1201. }
  1202. lmultiply(matrix) {
  1203. return this.clone().lmultiplyO(matrix);
  1204. }
  1205. lmultiplyO(matrix) {
  1206. const r = this;
  1207. const l = matrix instanceof Matrix ? matrix : new Matrix(matrix);
  1208. return Matrix.matrixMultiply(l, r, this);
  1209. } // Left multiplies by the given matrix
  1210. multiply(matrix) {
  1211. return this.clone().multiplyO(matrix);
  1212. }
  1213. multiplyO(matrix) {
  1214. // Get the matrices
  1215. const l = this;
  1216. const r = matrix instanceof Matrix ? matrix : new Matrix(matrix);
  1217. return Matrix.matrixMultiply(l, r, this);
  1218. } // Rotate matrix
  1219. rotate(r, cx, cy) {
  1220. return this.clone().rotateO(r, cx, cy);
  1221. }
  1222. rotateO(r, cx = 0, cy = 0) {
  1223. // Convert degrees to radians
  1224. r = radians(r);
  1225. const cos = Math.cos(r);
  1226. const sin = Math.sin(r);
  1227. const {
  1228. a,
  1229. b,
  1230. c,
  1231. d,
  1232. e,
  1233. f
  1234. } = this;
  1235. this.a = a * cos - b * sin;
  1236. this.b = b * cos + a * sin;
  1237. this.c = c * cos - d * sin;
  1238. this.d = d * cos + c * sin;
  1239. this.e = e * cos - f * sin + cy * sin - cx * cos + cx;
  1240. this.f = f * cos + e * sin - cx * sin - cy * cos + cy;
  1241. return this;
  1242. } // Scale matrix
  1243. scale(x, y, cx, cy) {
  1244. return this.clone().scaleO(...arguments);
  1245. }
  1246. scaleO(x, y = x, cx = 0, cy = 0) {
  1247. // Support uniform scaling
  1248. if (arguments.length === 3) {
  1249. cy = cx;
  1250. cx = y;
  1251. y = x;
  1252. }
  1253. const {
  1254. a,
  1255. b,
  1256. c,
  1257. d,
  1258. e,
  1259. f
  1260. } = this;
  1261. this.a = a * x;
  1262. this.b = b * y;
  1263. this.c = c * x;
  1264. this.d = d * y;
  1265. this.e = e * x - cx * x + cx;
  1266. this.f = f * y - cy * y + cy;
  1267. return this;
  1268. } // Shear matrix
  1269. shear(a, cx, cy) {
  1270. return this.clone().shearO(a, cx, cy);
  1271. }
  1272. shearO(lx, cx = 0, cy = 0) {
  1273. const {
  1274. a,
  1275. b,
  1276. c,
  1277. d,
  1278. e,
  1279. f
  1280. } = this;
  1281. this.a = a + b * lx;
  1282. this.c = c + d * lx;
  1283. this.e = e + f * lx - cy * lx;
  1284. return this;
  1285. } // Skew Matrix
  1286. skew(x, y, cx, cy) {
  1287. return this.clone().skewO(...arguments);
  1288. }
  1289. skewO(x, y = x, cx = 0, cy = 0) {
  1290. // support uniformal skew
  1291. if (arguments.length === 3) {
  1292. cy = cx;
  1293. cx = y;
  1294. y = x;
  1295. } // Convert degrees to radians
  1296. x = radians(x);
  1297. y = radians(y);
  1298. const lx = Math.tan(x);
  1299. const ly = Math.tan(y);
  1300. const {
  1301. a,
  1302. b,
  1303. c,
  1304. d,
  1305. e,
  1306. f
  1307. } = this;
  1308. this.a = a + b * lx;
  1309. this.b = b + a * ly;
  1310. this.c = c + d * lx;
  1311. this.d = d + c * ly;
  1312. this.e = e + f * lx - cy * lx;
  1313. this.f = f + e * ly - cx * ly;
  1314. return this;
  1315. } // SkewX
  1316. skewX(x, cx, cy) {
  1317. return this.skew(x, 0, cx, cy);
  1318. } // SkewY
  1319. skewY(y, cx, cy) {
  1320. return this.skew(0, y, cx, cy);
  1321. }
  1322. toArray() {
  1323. return [this.a, this.b, this.c, this.d, this.e, this.f];
  1324. } // Convert matrix to string
  1325. toString() {
  1326. return 'matrix(' + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ')';
  1327. } // Transform a matrix into another matrix by manipulating the space
  1328. transform(o) {
  1329. // Check if o is a matrix and then left multiply it directly
  1330. if (Matrix.isMatrixLike(o)) {
  1331. const matrix = new Matrix(o);
  1332. return matrix.multiplyO(this);
  1333. } // Get the proposed transformations and the current transformations
  1334. const t = Matrix.formatTransforms(o);
  1335. const current = this;
  1336. const {
  1337. x: ox,
  1338. y: oy
  1339. } = new Point(t.ox, t.oy).transform(current); // Construct the resulting matrix
  1340. const transformer = new Matrix().translateO(t.rx, t.ry).lmultiplyO(current).translateO(-ox, -oy).scaleO(t.scaleX, t.scaleY).skewO(t.skewX, t.skewY).shearO(t.shear).rotateO(t.theta).translateO(ox, oy); // If we want the origin at a particular place, we force it there
  1341. if (isFinite(t.px) || isFinite(t.py)) {
  1342. const origin = new Point(ox, oy).transform(transformer); // TODO: Replace t.px with isFinite(t.px)
  1343. // Doesn't work because t.px is also 0 if it wasn't passed
  1344. const dx = isFinite(t.px) ? t.px - origin.x : 0;
  1345. const dy = isFinite(t.py) ? t.py - origin.y : 0;
  1346. transformer.translateO(dx, dy);
  1347. } // Translate now after positioning
  1348. transformer.translateO(t.tx, t.ty);
  1349. return transformer;
  1350. } // Translate matrix
  1351. translate(x, y) {
  1352. return this.clone().translateO(x, y);
  1353. }
  1354. translateO(x, y) {
  1355. this.e += x || 0;
  1356. this.f += y || 0;
  1357. return this;
  1358. }
  1359. valueOf() {
  1360. return {
  1361. a: this.a,
  1362. b: this.b,
  1363. c: this.c,
  1364. d: this.d,
  1365. e: this.e,
  1366. f: this.f
  1367. };
  1368. }
  1369. }
  1370. function ctm() {
  1371. return new Matrix(this.node.getCTM());
  1372. }
  1373. function screenCTM() {
  1374. /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537
  1375. This is needed because FF does not return the transformation matrix
  1376. for the inner coordinate system when getScreenCTM() is called on nested svgs.
  1377. However all other Browsers do that */
  1378. if (typeof this.isRoot === 'function' && !this.isRoot()) {
  1379. const rect = this.rect(1, 1);
  1380. const m = rect.node.getScreenCTM();
  1381. rect.remove();
  1382. return new Matrix(m);
  1383. }
  1384. return new Matrix(this.node.getScreenCTM());
  1385. }
  1386. register(Matrix, 'Matrix');
  1387. function parser() {
  1388. // Reuse cached element if possible
  1389. if (!parser.nodes) {
  1390. const svg = makeInstance().size(2, 0);
  1391. svg.node.style.cssText = ['opacity: 0', 'position: absolute', 'left: -100%', 'top: -100%', 'overflow: hidden'].join(';');
  1392. svg.attr('focusable', 'false');
  1393. svg.attr('aria-hidden', 'true');
  1394. const path = svg.path().node;
  1395. parser.nodes = {
  1396. svg,
  1397. path
  1398. };
  1399. }
  1400. if (!parser.nodes.svg.node.parentNode) {
  1401. const b = globals.document.body || globals.document.documentElement;
  1402. parser.nodes.svg.addTo(b);
  1403. }
  1404. return parser.nodes;
  1405. }
  1406. function isNulledBox(box) {
  1407. return !box.width && !box.height && !box.x && !box.y;
  1408. }
  1409. function domContains(node) {
  1410. return node === globals.document || (globals.document.documentElement.contains || function (node) {
  1411. // This is IE - it does not support contains() for top-level SVGs
  1412. while (node.parentNode) {
  1413. node = node.parentNode;
  1414. }
  1415. return node === globals.document;
  1416. }).call(globals.document.documentElement, node);
  1417. }
  1418. class Box {
  1419. constructor(...args) {
  1420. this.init(...args);
  1421. }
  1422. addOffset() {
  1423. // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
  1424. this.x += globals.window.pageXOffset;
  1425. this.y += globals.window.pageYOffset;
  1426. return new Box(this);
  1427. }
  1428. init(source) {
  1429. const base = [0, 0, 0, 0];
  1430. source = typeof source === 'string' ? source.split(delimiter).map(parseFloat) : Array.isArray(source) ? source : typeof source === 'object' ? [source.left != null ? source.left : source.x, source.top != null ? source.top : source.y, source.width, source.height] : arguments.length === 4 ? [].slice.call(arguments) : base;
  1431. this.x = source[0] || 0;
  1432. this.y = source[1] || 0;
  1433. this.width = this.w = source[2] || 0;
  1434. this.height = this.h = source[3] || 0; // Add more bounding box properties
  1435. this.x2 = this.x + this.w;
  1436. this.y2 = this.y + this.h;
  1437. this.cx = this.x + this.w / 2;
  1438. this.cy = this.y + this.h / 2;
  1439. return this;
  1440. }
  1441. isNulled() {
  1442. return isNulledBox(this);
  1443. } // Merge rect box with another, return a new instance
  1444. merge(box) {
  1445. const x = Math.min(this.x, box.x);
  1446. const y = Math.min(this.y, box.y);
  1447. const width = Math.max(this.x + this.width, box.x + box.width) - x;
  1448. const height = Math.max(this.y + this.height, box.y + box.height) - y;
  1449. return new Box(x, y, width, height);
  1450. }
  1451. toArray() {
  1452. return [this.x, this.y, this.width, this.height];
  1453. }
  1454. toString() {
  1455. return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height;
  1456. }
  1457. transform(m) {
  1458. if (!(m instanceof Matrix)) {
  1459. m = new Matrix(m);
  1460. }
  1461. let xMin = Infinity;
  1462. let xMax = -Infinity;
  1463. let yMin = Infinity;
  1464. let yMax = -Infinity;
  1465. const pts = [new Point(this.x, this.y), new Point(this.x2, this.y), new Point(this.x, this.y2), new Point(this.x2, this.y2)];
  1466. pts.forEach(function (p) {
  1467. p = p.transform(m);
  1468. xMin = Math.min(xMin, p.x);
  1469. xMax = Math.max(xMax, p.x);
  1470. yMin = Math.min(yMin, p.y);
  1471. yMax = Math.max(yMax, p.y);
  1472. });
  1473. return new Box(xMin, yMin, xMax - xMin, yMax - yMin);
  1474. }
  1475. }
  1476. function getBox(el, getBBoxFn, retry) {
  1477. let box;
  1478. try {
  1479. // Try to get the box with the provided function
  1480. box = getBBoxFn(el.node); // If the box is worthless and not even in the dom, retry
  1481. // by throwing an error here...
  1482. if (isNulledBox(box) && !domContains(el.node)) {
  1483. throw new Error('Element not in the dom');
  1484. }
  1485. } catch (e) {
  1486. // ... and calling the retry handler here
  1487. box = retry(el);
  1488. }
  1489. return box;
  1490. }
  1491. function bbox() {
  1492. // Function to get bbox is getBBox()
  1493. const getBBox = node => node.getBBox(); // Take all measures so that a stupid browser renders the element
  1494. // so we can get the bbox from it when we try again
  1495. const retry = el => {
  1496. try {
  1497. const clone = el.clone().addTo(parser().svg).show();
  1498. const box = clone.node.getBBox();
  1499. clone.remove();
  1500. return box;
  1501. } catch (e) {
  1502. // We give up...
  1503. throw new Error(`Getting bbox of element "${el.node.nodeName}" is not possible: ${e.toString()}`);
  1504. }
  1505. };
  1506. const box = getBox(this, getBBox, retry);
  1507. const bbox = new Box(box);
  1508. return bbox;
  1509. }
  1510. function rbox(el) {
  1511. const getRBox = node => node.getBoundingClientRect();
  1512. const retry = el => {
  1513. // There is no point in trying tricks here because if we insert the element into the dom ourselves
  1514. // it obviously will be at the wrong position
  1515. throw new Error(`Getting rbox of element "${el.node.nodeName}" is not possible`);
  1516. };
  1517. const box = getBox(this, getRBox, retry);
  1518. const rbox = new Box(box); // If an element was passed, we want the bbox in the coordinate system of that element
  1519. if (el) {
  1520. return rbox.transform(el.screenCTM().inverseO());
  1521. } // Else we want it in absolute screen coordinates
  1522. // Therefore we need to add the scrollOffset
  1523. return rbox.addOffset();
  1524. } // Checks whether the given point is inside the bounding box
  1525. function inside(x, y) {
  1526. const box = this.bbox();
  1527. return x > box.x && y > box.y && x < box.x + box.width && y < box.y + box.height;
  1528. }
  1529. registerMethods({
  1530. viewbox: {
  1531. viewbox(x, y, width, height) {
  1532. // act as getter
  1533. if (x == null) return new Box(this.attr('viewBox')); // act as setter
  1534. return this.attr('viewBox', new Box(x, y, width, height));
  1535. },
  1536. zoom(level, point) {
  1537. // Its best to rely on the attributes here and here is why:
  1538. // clientXYZ: Doesn't work on non-root svgs because they dont have a CSSBox (silly!)
  1539. // getBoundingClientRect: Doesn't work because Chrome just ignores width and height of nested svgs completely
  1540. // that means, their clientRect is always as big as the content.
  1541. // Furthermore this size is incorrect if the element is further transformed by its parents
  1542. // computedStyle: Only returns meaningful values if css was used with px. We dont go this route here!
  1543. // getBBox: returns the bounding box of its content - that doesn't help!
  1544. let {
  1545. width,
  1546. height
  1547. } = this.attr(['width', 'height']); // Width and height is a string when a number with a unit is present which we can't use
  1548. // So we try clientXYZ
  1549. if (!width && !height || typeof width === 'string' || typeof height === 'string') {
  1550. width = this.node.clientWidth;
  1551. height = this.node.clientHeight;
  1552. } // Giving up...
  1553. if (!width || !height) {
  1554. throw new Error('Impossible to get absolute width and height. Please provide an absolute width and height attribute on the zooming element');
  1555. }
  1556. const v = this.viewbox();
  1557. const zoomX = width / v.width;
  1558. const zoomY = height / v.height;
  1559. const zoom = Math.min(zoomX, zoomY);
  1560. if (level == null) {
  1561. return zoom;
  1562. }
  1563. let zoomAmount = zoom / level; // Set the zoomAmount to the highest value which is safe to process and recover from
  1564. // The * 100 is a bit of wiggle room for the matrix transformation
  1565. if (zoomAmount === Infinity) zoomAmount = Number.MAX_SAFE_INTEGER / 100;
  1566. point = point || new Point(width / 2 / zoomX + v.x, height / 2 / zoomY + v.y);
  1567. const box = new Box(v).transform(new Matrix({
  1568. scale: zoomAmount,
  1569. origin: point
  1570. }));
  1571. return this.viewbox(box);
  1572. }
  1573. }
  1574. });
  1575. register(Box, 'Box');
  1576. class List extends Array {
  1577. constructor(arr = [], ...args) {
  1578. super(arr, ...args);
  1579. if (typeof arr === 'number') return this;
  1580. this.length = 0;
  1581. this.push(...arr);
  1582. }
  1583. }
  1584. extend([List], {
  1585. each(fnOrMethodName, ...args) {
  1586. if (typeof fnOrMethodName === 'function') {
  1587. return this.map((el, i, arr) => {
  1588. return fnOrMethodName.call(el, el, i, arr);
  1589. });
  1590. } else {
  1591. return this.map(el => {
  1592. return el[fnOrMethodName](...args);
  1593. });
  1594. }
  1595. },
  1596. toArray() {
  1597. return Array.prototype.concat.apply([], this);
  1598. }
  1599. });
  1600. const reserved = ['toArray', 'constructor', 'each'];
  1601. List.extend = function (methods) {
  1602. methods = methods.reduce((obj, name) => {
  1603. // Don't overwrite own methods
  1604. if (reserved.includes(name)) return obj; // Don't add private methods
  1605. if (name[0] === '_') return obj; // Relay every call to each()
  1606. obj[name] = function (...attrs) {
  1607. return this.each(name, ...attrs);
  1608. };
  1609. return obj;
  1610. }, {});
  1611. extend([List], methods);
  1612. };
  1613. function baseFind(query, parent) {
  1614. return new List(map((parent || globals.document).querySelectorAll(query), function (node) {
  1615. return adopt(node);
  1616. }));
  1617. } // Scoped find method
  1618. function find(query) {
  1619. return baseFind(query, this.node);
  1620. }
  1621. function findOne(query) {
  1622. return adopt(this.node.querySelector(query));
  1623. }
  1624. let listenerId = 0;
  1625. const windowEvents = {};
  1626. function getEvents(instance) {
  1627. let n = instance.getEventHolder(); // We dont want to save events in global space
  1628. if (n === globals.window) n = windowEvents;
  1629. if (!n.events) n.events = {};
  1630. return n.events;
  1631. }
  1632. function getEventTarget(instance) {
  1633. return instance.getEventTarget();
  1634. }
  1635. function clearEvents(instance) {
  1636. let n = instance.getEventHolder();
  1637. if (n === globals.window) n = windowEvents;
  1638. if (n.events) n.events = {};
  1639. } // Add event binder in the SVG namespace
  1640. function on(node, events, listener, binding, options) {
  1641. const l = listener.bind(binding || node);
  1642. const instance = makeInstance(node);
  1643. const bag = getEvents(instance);
  1644. const n = getEventTarget(instance); // events can be an array of events or a string of events
  1645. events = Array.isArray(events) ? events : events.split(delimiter); // add id to listener
  1646. if (!listener._svgjsListenerId) {
  1647. listener._svgjsListenerId = ++listenerId;
  1648. }
  1649. events.forEach(function (event) {
  1650. const ev = event.split('.')[0];
  1651. const ns = event.split('.')[1] || '*'; // ensure valid object
  1652. bag[ev] = bag[ev] || {};
  1653. bag[ev][ns] = bag[ev][ns] || {}; // reference listener
  1654. bag[ev][ns][listener._svgjsListenerId] = l; // add listener
  1655. n.addEventListener(ev, l, options || false);
  1656. });
  1657. } // Add event unbinder in the SVG namespace
  1658. function off(node, events, listener, options) {
  1659. const instance = makeInstance(node);
  1660. const bag = getEvents(instance);
  1661. const n = getEventTarget(instance); // listener can be a function or a number
  1662. if (typeof listener === 'function') {
  1663. listener = listener._svgjsListenerId;
  1664. if (!listener) return;
  1665. } // events can be an array of events or a string or undefined
  1666. events = Array.isArray(events) ? events : (events || '').split(delimiter);
  1667. events.forEach(function (event) {
  1668. const ev = event && event.split('.')[0];
  1669. const ns = event && event.split('.')[1];
  1670. let namespace, l;
  1671. if (listener) {
  1672. // remove listener reference
  1673. if (bag[ev] && bag[ev][ns || '*']) {
  1674. // removeListener
  1675. n.removeEventListener(ev, bag[ev][ns || '*'][listener], options || false);
  1676. delete bag[ev][ns || '*'][listener];
  1677. }
  1678. } else if (ev && ns) {
  1679. // remove all listeners for a namespaced event
  1680. if (bag[ev] && bag[ev][ns]) {
  1681. for (l in bag[ev][ns]) {
  1682. off(n, [ev, ns].join('.'), l);
  1683. }
  1684. delete bag[ev][ns];
  1685. }
  1686. } else if (ns) {
  1687. // remove all listeners for a specific namespace
  1688. for (event in bag) {
  1689. for (namespace in bag[event]) {
  1690. if (ns === namespace) {
  1691. off(n, [event, ns].join('.'));
  1692. }
  1693. }
  1694. }
  1695. } else if (ev) {
  1696. // remove all listeners for the event
  1697. if (bag[ev]) {
  1698. for (namespace in bag[ev]) {
  1699. off(n, [ev, namespace].join('.'));
  1700. }
  1701. delete bag[ev];
  1702. }
  1703. } else {
  1704. // remove all listeners on a given node
  1705. for (event in bag) {
  1706. off(n, event);
  1707. }
  1708. clearEvents(instance);
  1709. }
  1710. });
  1711. }
  1712. function dispatch(node, event, data, options) {
  1713. const n = getEventTarget(node); // Dispatch event
  1714. if (event instanceof globals.window.Event) {
  1715. n.dispatchEvent(event);
  1716. } else {
  1717. event = new globals.window.CustomEvent(event, {
  1718. detail: data,
  1719. cancelable: true,
  1720. ...options
  1721. });
  1722. n.dispatchEvent(event);
  1723. }
  1724. return event;
  1725. }
  1726. class EventTarget extends Base {
  1727. addEventListener() {}
  1728. dispatch(event, data, options) {
  1729. return dispatch(this, event, data, options);
  1730. }
  1731. dispatchEvent(event) {
  1732. const bag = this.getEventHolder().events;
  1733. if (!bag) return true;
  1734. const events = bag[event.type];
  1735. for (const i in events) {
  1736. for (const j in events[i]) {
  1737. events[i][j](event);
  1738. }
  1739. }
  1740. return !event.defaultPrevented;
  1741. } // Fire given event
  1742. fire(event, data, options) {
  1743. this.dispatch(event, data, options);
  1744. return this;
  1745. }
  1746. getEventHolder() {
  1747. return this;
  1748. }
  1749. getEventTarget() {
  1750. return this;
  1751. } // Unbind event from listener
  1752. off(event, listener, options) {
  1753. off(this, event, listener, options);
  1754. return this;
  1755. } // Bind given event to listener
  1756. on(event, listener, binding, options) {
  1757. on(this, event, listener, binding, options);
  1758. return this;
  1759. }
  1760. removeEventListener() {}
  1761. }
  1762. register(EventTarget, 'EventTarget');
  1763. function noop() {} // Default animation values
  1764. const timeline = {
  1765. duration: 400,
  1766. ease: '>',
  1767. delay: 0
  1768. }; // Default attribute values
  1769. const attrs = {
  1770. // fill and stroke
  1771. 'fill-opacity': 1,
  1772. 'stroke-opacity': 1,
  1773. 'stroke-width': 0,
  1774. 'stroke-linejoin': 'miter',
  1775. 'stroke-linecap': 'butt',
  1776. fill: '#000000',
  1777. stroke: '#000000',
  1778. opacity: 1,
  1779. // position
  1780. x: 0,
  1781. y: 0,
  1782. cx: 0,
  1783. cy: 0,
  1784. // size
  1785. width: 0,
  1786. height: 0,
  1787. // radius
  1788. r: 0,
  1789. rx: 0,
  1790. ry: 0,
  1791. // gradient
  1792. offset: 0,
  1793. 'stop-opacity': 1,
  1794. 'stop-color': '#000000',
  1795. // text
  1796. 'text-anchor': 'start'
  1797. };
  1798. var defaults = {
  1799. __proto__: null,
  1800. noop: noop,
  1801. timeline: timeline,
  1802. attrs: attrs
  1803. };
  1804. class SVGArray extends Array {
  1805. constructor(...args) {
  1806. super(...args);
  1807. this.init(...args);
  1808. }
  1809. clone() {
  1810. return new this.constructor(this);
  1811. }
  1812. init(arr) {
  1813. // This catches the case, that native map tries to create an array with new Array(1)
  1814. if (typeof arr === 'number') return this;
  1815. this.length = 0;
  1816. this.push(...this.parse(arr));
  1817. return this;
  1818. } // Parse whitespace separated string
  1819. parse(array = []) {
  1820. // If already is an array, no need to parse it
  1821. if (array instanceof Array) return array;
  1822. return array.trim().split(delimiter).map(parseFloat);
  1823. }
  1824. toArray() {
  1825. return Array.prototype.concat.apply([], this);
  1826. }
  1827. toSet() {
  1828. return new Set(this);
  1829. }
  1830. toString() {
  1831. return this.join(' ');
  1832. } // Flattens the array if needed
  1833. valueOf() {
  1834. const ret = [];
  1835. ret.push(...this);
  1836. return ret;
  1837. }
  1838. }
  1839. class SVGNumber {
  1840. // Initialize
  1841. constructor(...args) {
  1842. this.init(...args);
  1843. }
  1844. convert(unit) {
  1845. return new SVGNumber(this.value, unit);
  1846. } // Divide number
  1847. divide(number) {
  1848. number = new SVGNumber(number);
  1849. return new SVGNumber(this / number, this.unit || number.unit);
  1850. }
  1851. init(value, unit) {
  1852. unit = Array.isArray(value) ? value[1] : unit;
  1853. value = Array.isArray(value) ? value[0] : value; // initialize defaults
  1854. this.value = 0;
  1855. this.unit = unit || ''; // parse value
  1856. if (typeof value === 'number') {
  1857. // ensure a valid numeric value
  1858. this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value;
  1859. } else if (typeof value === 'string') {
  1860. unit = value.match(numberAndUnit);
  1861. if (unit) {
  1862. // make value numeric
  1863. this.value = parseFloat(unit[1]); // normalize
  1864. if (unit[5] === '%') {
  1865. this.value /= 100;
  1866. } else if (unit[5] === 's') {
  1867. this.value *= 1000;
  1868. } // store unit
  1869. this.unit = unit[5];
  1870. }
  1871. } else {
  1872. if (value instanceof SVGNumber) {
  1873. this.value = value.valueOf();
  1874. this.unit = value.unit;
  1875. }
  1876. }
  1877. return this;
  1878. } // Subtract number
  1879. minus(number) {
  1880. number = new SVGNumber(number);
  1881. return new SVGNumber(this - number, this.unit || number.unit);
  1882. } // Add number
  1883. plus(number) {
  1884. number = new SVGNumber(number);
  1885. return new SVGNumber(this + number, this.unit || number.unit);
  1886. } // Multiply number
  1887. times(number) {
  1888. number = new SVGNumber(number);
  1889. return new SVGNumber(this * number, this.unit || number.unit);
  1890. }
  1891. toArray() {
  1892. return [this.value, this.unit];
  1893. }
  1894. toJSON() {
  1895. return this.toString();
  1896. }
  1897. toString() {
  1898. return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 : this.unit === 's' ? this.value / 1e3 : this.value) + this.unit;
  1899. }
  1900. valueOf() {
  1901. return this.value;
  1902. }
  1903. }
  1904. const hooks = [];
  1905. function registerAttrHook(fn) {
  1906. hooks.push(fn);
  1907. } // Set svg element attribute
  1908. function attr(attr, val, ns) {
  1909. // act as full getter
  1910. if (attr == null) {
  1911. // get an object of attributes
  1912. attr = {};
  1913. val = this.node.attributes;
  1914. for (const node of val) {
  1915. attr[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue;
  1916. }
  1917. return attr;
  1918. } else if (attr instanceof Array) {
  1919. // loop through array and get all values
  1920. return attr.reduce((last, curr) => {
  1921. last[curr] = this.attr(curr);
  1922. return last;
  1923. }, {});
  1924. } else if (typeof attr === 'object' && attr.constructor === Object) {
  1925. // apply every attribute individually if an object is passed
  1926. for (val in attr) this.attr(val, attr[val]);
  1927. } else if (val === null) {
  1928. // remove value
  1929. this.node.removeAttribute(attr);
  1930. } else if (val == null) {
  1931. // act as a getter if the first and only argument is not an object
  1932. val = this.node.getAttribute(attr);
  1933. return val == null ? attrs[attr] : isNumber.test(val) ? parseFloat(val) : val;
  1934. } else {
  1935. // Loop through hooks and execute them to convert value
  1936. val = hooks.reduce((_val, hook) => {
  1937. return hook(attr, _val, this);
  1938. }, val); // ensure correct numeric values (also accepts NaN and Infinity)
  1939. if (typeof val === 'number') {
  1940. val = new SVGNumber(val);
  1941. } else if (Color.isColor(val)) {
  1942. // ensure full hex color
  1943. val = new Color(val);
  1944. } else if (val.constructor === Array) {
  1945. // Check for plain arrays and parse array values
  1946. val = new SVGArray(val);
  1947. } // if the passed attribute is leading...
  1948. if (attr === 'leading') {
  1949. // ... call the leading method instead
  1950. if (this.leading) {
  1951. this.leading(val);
  1952. }
  1953. } else {
  1954. // set given attribute on node
  1955. typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString()) : this.node.setAttribute(attr, val.toString());
  1956. } // rebuild if required
  1957. if (this.rebuild && (attr === 'font-size' || attr === 'x')) {
  1958. this.rebuild();
  1959. }
  1960. }
  1961. return this;
  1962. }
  1963. class Dom extends EventTarget {
  1964. constructor(node, attrs) {
  1965. super();
  1966. this.node = node;
  1967. this.type = node.nodeName;
  1968. if (attrs && node !== attrs) {
  1969. this.attr(attrs);
  1970. }
  1971. } // Add given element at a position
  1972. add(element, i) {
  1973. element = makeInstance(element); // If non-root svg nodes are added we have to remove their namespaces
  1974. if (element.removeNamespace && this.node instanceof globals.window.SVGElement) {
  1975. element.removeNamespace();
  1976. }
  1977. if (i == null) {
  1978. this.node.appendChild(element.node);
  1979. } else if (element.node !== this.node.childNodes[i]) {
  1980. this.node.insertBefore(element.node, this.node.childNodes[i]);
  1981. }
  1982. return this;
  1983. } // Add element to given container and return self
  1984. addTo(parent, i) {
  1985. return makeInstance(parent).put(this, i);
  1986. } // Returns all child elements
  1987. children() {
  1988. return new List(map(this.node.children, function (node) {
  1989. return adopt(node);
  1990. }));
  1991. } // Remove all elements in this container
  1992. clear() {
  1993. // remove children
  1994. while (this.node.hasChildNodes()) {
  1995. this.node.removeChild(this.node.lastChild);
  1996. }
  1997. return this;
  1998. } // Clone element
  1999. clone(deep = true, assignNewIds = true) {
  2000. // write dom data to the dom so the clone can pickup the data
  2001. this.writeDataToDom(); // clone element
  2002. let nodeClone = this.node.cloneNode(deep);
  2003. if (assignNewIds) {
  2004. // assign new id
  2005. nodeClone = assignNewId(nodeClone);
  2006. }
  2007. return new this.constructor(nodeClone);
  2008. } // Iterates over all children and invokes a given block
  2009. each(block, deep) {
  2010. const children = this.children();
  2011. let i, il;
  2012. for (i = 0, il = children.length; i < il; i++) {
  2013. block.apply(children[i], [i, children]);
  2014. if (deep) {
  2015. children[i].each(block, deep);
  2016. }
  2017. }
  2018. return this;
  2019. }
  2020. element(nodeName, attrs) {
  2021. return this.put(new Dom(create(nodeName), attrs));
  2022. } // Get first child
  2023. first() {
  2024. return adopt(this.node.firstChild);
  2025. } // Get a element at the given index
  2026. get(i) {
  2027. return adopt(this.node.childNodes[i]);
  2028. }
  2029. getEventHolder() {
  2030. return this.node;
  2031. }
  2032. getEventTarget() {
  2033. return this.node;
  2034. } // Checks if the given element is a child
  2035. has(element) {
  2036. return this.index(element) >= 0;
  2037. }
  2038. html(htmlOrFn, outerHTML) {
  2039. return this.xml(htmlOrFn, outerHTML, html);
  2040. } // Get / set id
  2041. id(id) {
  2042. // generate new id if no id set
  2043. if (typeof id === 'undefined' && !this.node.id) {
  2044. this.node.id = eid(this.type);
  2045. } // don't set directly with this.node.id to make `null` work correctly
  2046. return this.attr('id', id);
  2047. } // Gets index of given element
  2048. index(element) {
  2049. return [].slice.call(this.node.childNodes).indexOf(element.node);
  2050. } // Get the last child
  2051. last() {
  2052. return adopt(this.node.lastChild);
  2053. } // matches the element vs a css selector
  2054. matches(selector) {
  2055. const el = this.node;
  2056. const matcher = el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector || null;
  2057. return matcher && matcher.call(el, selector);
  2058. } // Returns the parent element instance
  2059. parent(type) {
  2060. let parent = this; // check for parent
  2061. if (!parent.node.parentNode) return null; // get parent element
  2062. parent = adopt(parent.node.parentNode);
  2063. if (!type) return parent; // loop through ancestors if type is given
  2064. do {
  2065. if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent;
  2066. } while (parent = adopt(parent.node.parentNode));
  2067. return parent;
  2068. } // Basically does the same as `add()` but returns the added element instead
  2069. put(element, i) {
  2070. element = makeInstance(element);
  2071. this.add(element, i);
  2072. return element;
  2073. } // Add element to given container and return container
  2074. putIn(parent, i) {
  2075. return makeInstance(parent).add(this, i);
  2076. } // Remove element
  2077. remove() {
  2078. if (this.parent()) {
  2079. this.parent().removeElement(this);
  2080. }
  2081. return this;
  2082. } // Remove a given child
  2083. removeElement(element) {
  2084. this.node.removeChild(element.node);
  2085. return this;
  2086. } // Replace this with element
  2087. replace(element) {
  2088. element = makeInstance(element);
  2089. if (this.node.parentNode) {
  2090. this.node.parentNode.replaceChild(element.node, this.node);
  2091. }
  2092. return element;
  2093. }
  2094. round(precision = 2, map = null) {
  2095. const factor = 10 ** precision;
  2096. const attrs = this.attr(map);
  2097. for (const i in attrs) {
  2098. if (typeof attrs[i] === 'number') {
  2099. attrs[i] = Math.round(attrs[i] * factor) / factor;
  2100. }
  2101. }
  2102. this.attr(attrs);
  2103. return this;
  2104. } // Import / Export raw svg
  2105. svg(svgOrFn, outerSVG) {
  2106. return this.xml(svgOrFn, outerSVG, svg);
  2107. } // Return id on string conversion
  2108. toString() {
  2109. return this.id();
  2110. }
  2111. words(text) {
  2112. // This is faster than removing all children and adding a new one
  2113. this.node.textContent = text;
  2114. return this;
  2115. }
  2116. wrap(node) {
  2117. const parent = this.parent();
  2118. if (!parent) {
  2119. return this.addTo(node);
  2120. }
  2121. const position = parent.index(this);
  2122. return parent.put(node, position).put(this);
  2123. } // write svgjs data to the dom
  2124. writeDataToDom() {
  2125. // dump variables recursively
  2126. this.each(function () {
  2127. this.writeDataToDom();
  2128. });
  2129. return this;
  2130. } // Import / Export raw svg
  2131. xml(xmlOrFn, outerXML, ns) {
  2132. if (typeof xmlOrFn === 'boolean') {
  2133. ns = outerXML;
  2134. outerXML = xmlOrFn;
  2135. xmlOrFn = null;
  2136. } // act as getter if no svg string is given
  2137. if (xmlOrFn == null || typeof xmlOrFn === 'function') {
  2138. // The default for exports is, that the outerNode is included
  2139. outerXML = outerXML == null ? true : outerXML; // write svgjs data to the dom
  2140. this.writeDataToDom();
  2141. let current = this; // An export modifier was passed
  2142. if (xmlOrFn != null) {
  2143. current = adopt(current.node.cloneNode(true)); // If the user wants outerHTML we need to process this node, too
  2144. if (outerXML) {
  2145. const result = xmlOrFn(current);
  2146. current = result || current; // The user does not want this node? Well, then he gets nothing
  2147. if (result === false) return '';
  2148. } // Deep loop through all children and apply modifier
  2149. current.each(function () {
  2150. const result = xmlOrFn(this);
  2151. const _this = result || this; // If modifier returns false, discard node
  2152. if (result === false) {
  2153. this.remove(); // If modifier returns new node, use it
  2154. } else if (result && this !== _this) {
  2155. this.replace(_this);
  2156. }
  2157. }, true);
  2158. } // Return outer or inner content
  2159. return outerXML ? current.node.outerHTML : current.node.innerHTML;
  2160. } // Act as setter if we got a string
  2161. // The default for import is, that the current node is not replaced
  2162. outerXML = outerXML == null ? false : outerXML; // Create temporary holder
  2163. const well = create('wrapper', ns);
  2164. const fragment = globals.document.createDocumentFragment(); // Dump raw svg
  2165. well.innerHTML = xmlOrFn; // Transplant nodes into the fragment
  2166. for (let len = well.children.length; len--;) {
  2167. fragment.appendChild(well.firstElementChild);
  2168. }
  2169. const parent = this.parent(); // Add the whole fragment at once
  2170. return outerXML ? this.replace(fragment) && parent : this.add(fragment);
  2171. }
  2172. }
  2173. extend(Dom, {
  2174. attr,
  2175. find,
  2176. findOne
  2177. });
  2178. register(Dom, 'Dom');
  2179. class Element extends Dom {
  2180. constructor(node, attrs) {
  2181. super(node, attrs); // initialize data object
  2182. this.dom = {}; // create circular reference
  2183. this.node.instance = this;
  2184. if (node.hasAttribute('svgjs:data')) {
  2185. // pull svgjs data from the dom (getAttributeNS doesn't work in html5)
  2186. this.setData(JSON.parse(node.getAttribute('svgjs:data')) || {});
  2187. }
  2188. } // Move element by its center
  2189. center(x, y) {
  2190. return this.cx(x).cy(y);
  2191. } // Move by center over x-axis
  2192. cx(x) {
  2193. return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2);
  2194. } // Move by center over y-axis
  2195. cy(y) {
  2196. return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2);
  2197. } // Get defs
  2198. defs() {
  2199. const root = this.root();
  2200. return root && root.defs();
  2201. } // Relative move over x and y axes
  2202. dmove(x, y) {
  2203. return this.dx(x).dy(y);
  2204. } // Relative move over x axis
  2205. dx(x = 0) {
  2206. return this.x(new SVGNumber(x).plus(this.x()));
  2207. } // Relative move over y axis
  2208. dy(y = 0) {
  2209. return this.y(new SVGNumber(y).plus(this.y()));
  2210. }
  2211. getEventHolder() {
  2212. return this;
  2213. } // Set height of element
  2214. height(height) {
  2215. return this.attr('height', height);
  2216. } // Move element to given x and y values
  2217. move(x, y) {
  2218. return this.x(x).y(y);
  2219. } // return array of all ancestors of given type up to the root svg
  2220. parents(until = this.root()) {
  2221. const isSelector = typeof until === 'string';
  2222. if (!isSelector) {
  2223. until = makeInstance(until);
  2224. }
  2225. const parents = new List();
  2226. let parent = this;
  2227. while ((parent = parent.parent()) && parent.node !== globals.document && parent.nodeName !== '#document-fragment') {
  2228. parents.push(parent);
  2229. if (!isSelector && parent.node === until.node) {
  2230. break;
  2231. }
  2232. if (isSelector && parent.matches(until)) {
  2233. break;
  2234. }
  2235. if (parent.node === this.root().node) {
  2236. // We worked our way to the root and didn't match `until`
  2237. return null;
  2238. }
  2239. }
  2240. return parents;
  2241. } // Get referenced element form attribute value
  2242. reference(attr) {
  2243. attr = this.attr(attr);
  2244. if (!attr) return null;
  2245. const m = (attr + '').match(reference);
  2246. return m ? makeInstance(m[1]) : null;
  2247. } // Get parent document
  2248. root() {
  2249. const p = this.parent(getClass(root));
  2250. return p && p.root();
  2251. } // set given data to the elements data property
  2252. setData(o) {
  2253. this.dom = o;
  2254. return this;
  2255. } // Set element size to given width and height
  2256. size(width, height) {
  2257. const p = proportionalSize(this, width, height);
  2258. return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height));
  2259. } // Set width of element
  2260. width(width) {
  2261. return this.attr('width', width);
  2262. } // write svgjs data to the dom
  2263. writeDataToDom() {
  2264. // remove previously set data
  2265. this.node.removeAttribute('svgjs:data');
  2266. if (Object.keys(this.dom).length) {
  2267. this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428
  2268. }
  2269. return super.writeDataToDom();
  2270. } // Move over x-axis
  2271. x(x) {
  2272. return this.attr('x', x);
  2273. } // Move over y-axis
  2274. y(y) {
  2275. return this.attr('y', y);
  2276. }
  2277. }
  2278. extend(Element, {
  2279. bbox,
  2280. rbox,
  2281. inside,
  2282. point,
  2283. ctm,
  2284. screenCTM
  2285. });
  2286. register(Element, 'Element');
  2287. const sugar = {
  2288. stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],
  2289. fill: ['color', 'opacity', 'rule'],
  2290. prefix: function (t, a) {
  2291. return a === 'color' ? t : t + '-' + a;
  2292. }
  2293. } // Add sugar for fill and stroke
  2294. ;
  2295. ['fill', 'stroke'].forEach(function (m) {
  2296. const extension = {};
  2297. let i;
  2298. extension[m] = function (o) {
  2299. if (typeof o === 'undefined') {
  2300. return this.attr(m);
  2301. }
  2302. if (typeof o === 'string' || o instanceof Color || Color.isRgb(o) || o instanceof Element) {
  2303. this.attr(m, o);
  2304. } else {
  2305. // set all attributes from sugar.fill and sugar.stroke list
  2306. for (i = sugar[m].length - 1; i >= 0; i--) {
  2307. if (o[sugar[m][i]] != null) {
  2308. this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);
  2309. }
  2310. }
  2311. }
  2312. return this;
  2313. };
  2314. registerMethods(['Element', 'Runner'], extension);
  2315. });
  2316. registerMethods(['Element', 'Runner'], {
  2317. // Let the user set the matrix directly
  2318. matrix: function (mat, b, c, d, e, f) {
  2319. // Act as a getter
  2320. if (mat == null) {
  2321. return new Matrix(this);
  2322. } // Act as a setter, the user can pass a matrix or a set of numbers
  2323. return this.attr('transform', new Matrix(mat, b, c, d, e, f));
  2324. },
  2325. // Map rotation to transform
  2326. rotate: function (angle, cx, cy) {
  2327. return this.transform({
  2328. rotate: angle,
  2329. ox: cx,
  2330. oy: cy
  2331. }, true);
  2332. },
  2333. // Map skew to transform
  2334. skew: function (x, y, cx, cy) {
  2335. return arguments.length === 1 || arguments.length === 3 ? this.transform({
  2336. skew: x,
  2337. ox: y,
  2338. oy: cx
  2339. }, true) : this.transform({
  2340. skew: [x, y],
  2341. ox: cx,
  2342. oy: cy
  2343. }, true);
  2344. },
  2345. shear: function (lam, cx, cy) {
  2346. return this.transform({
  2347. shear: lam,
  2348. ox: cx,
  2349. oy: cy
  2350. }, true);
  2351. },
  2352. // Map scale to transform
  2353. scale: function (x, y, cx, cy) {
  2354. return arguments.length === 1 || arguments.length === 3 ? this.transform({
  2355. scale: x,
  2356. ox: y,
  2357. oy: cx
  2358. }, true) : this.transform({
  2359. scale: [x, y],
  2360. ox: cx,
  2361. oy: cy
  2362. }, true);
  2363. },
  2364. // Map translate to transform
  2365. translate: function (x, y) {
  2366. return this.transform({
  2367. translate: [x, y]
  2368. }, true);
  2369. },
  2370. // Map relative translations to transform
  2371. relative: function (x, y) {
  2372. return this.transform({
  2373. relative: [x, y]
  2374. }, true);
  2375. },
  2376. // Map flip to transform
  2377. flip: function (direction = 'both', origin = 'center') {
  2378. if ('xybothtrue'.indexOf(direction) === -1) {
  2379. origin = direction;
  2380. direction = 'both';
  2381. }
  2382. return this.transform({
  2383. flip: direction,
  2384. origin: origin
  2385. }, true);
  2386. },
  2387. // Opacity
  2388. opacity: function (value) {
  2389. return this.attr('opacity', value);
  2390. }
  2391. });
  2392. registerMethods('radius', {
  2393. // Add x and y radius
  2394. radius: function (x, y = x) {
  2395. const type = (this._element || this).type;
  2396. return type === 'radialGradient' ? this.attr('r', new SVGNumber(x)) : this.rx(x).ry(y);
  2397. }
  2398. });
  2399. registerMethods('Path', {
  2400. // Get path length
  2401. length: function () {
  2402. return this.node.getTotalLength();
  2403. },
  2404. // Get point at length
  2405. pointAt: function (length) {
  2406. return new Point(this.node.getPointAtLength(length));
  2407. }
  2408. });
  2409. registerMethods(['Element', 'Runner'], {
  2410. // Set font
  2411. font: function (a, v) {
  2412. if (typeof a === 'object') {
  2413. for (v in a) this.font(v, a[v]);
  2414. return this;
  2415. }
  2416. return a === 'leading' ? this.leading(v) : a === 'anchor' ? this.attr('text-anchor', v) : a === 'size' || a === 'family' || a === 'weight' || a === 'stretch' || a === 'variant' || a === 'style' ? this.attr('font-' + a, v) : this.attr(a, v);
  2417. }
  2418. }); // Add events to elements
  2419. const methods = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) {
  2420. // add event to Element
  2421. const fn = function (f) {
  2422. if (f === null) {
  2423. this.off(event);
  2424. } else {
  2425. this.on(event, f);
  2426. }
  2427. return this;
  2428. };
  2429. last[event] = fn;
  2430. return last;
  2431. }, {});
  2432. registerMethods('Element', methods);
  2433. function untransform() {
  2434. return this.attr('transform', null);
  2435. } // merge the whole transformation chain into one matrix and returns it
  2436. function matrixify() {
  2437. const matrix = (this.attr('transform') || '' // split transformations
  2438. ).split(transforms).slice(0, -1).map(function (str) {
  2439. // generate key => value pairs
  2440. const kv = str.trim().split('(');
  2441. return [kv[0], kv[1].split(delimiter).map(function (str) {
  2442. return parseFloat(str);
  2443. })];
  2444. }).reverse() // merge every transformation into one matrix
  2445. .reduce(function (matrix, transform) {
  2446. if (transform[0] === 'matrix') {
  2447. return matrix.lmultiply(Matrix.fromArray(transform[1]));
  2448. }
  2449. return matrix[transform[0]].apply(matrix, transform[1]);
  2450. }, new Matrix());
  2451. return matrix;
  2452. } // add an element to another parent without changing the visual representation on the screen
  2453. function toParent(parent, i) {
  2454. if (this === parent) return this;
  2455. const ctm = this.screenCTM();
  2456. const pCtm = parent.screenCTM().inverse();
  2457. this.addTo(parent, i).untransform().transform(pCtm.multiply(ctm));
  2458. return this;
  2459. } // same as above with parent equals root-svg
  2460. function toRoot(i) {
  2461. return this.toParent(this.root(), i);
  2462. } // Add transformations
  2463. function transform(o, relative) {
  2464. // Act as a getter if no object was passed
  2465. if (o == null || typeof o === 'string') {
  2466. const decomposed = new Matrix(this).decompose();
  2467. return o == null ? decomposed : decomposed[o];
  2468. }
  2469. if (!Matrix.isMatrixLike(o)) {
  2470. // Set the origin according to the defined transform
  2471. o = { ...o,
  2472. origin: getOrigin(o, this)
  2473. };
  2474. } // The user can pass a boolean, an Element or an Matrix or nothing
  2475. const cleanRelative = relative === true ? this : relative || false;
  2476. const result = new Matrix(cleanRelative).transform(o);
  2477. return this.attr('transform', result);
  2478. }
  2479. registerMethods('Element', {
  2480. untransform,
  2481. matrixify,
  2482. toParent,
  2483. toRoot,
  2484. transform
  2485. });
  2486. class Container extends Element {
  2487. flatten(parent = this, index) {
  2488. this.each(function () {
  2489. if (this instanceof Container) {
  2490. return this.flatten().ungroup();
  2491. }
  2492. });
  2493. return this;
  2494. }
  2495. ungroup(parent = this.parent(), index = parent.index(this)) {
  2496. // when parent != this, we want append all elements to the end
  2497. index = index === -1 ? parent.children().length : index;
  2498. this.each(function (i, children) {
  2499. // reverse each
  2500. return children[children.length - i - 1].toParent(parent, index);
  2501. });
  2502. return this.remove();
  2503. }
  2504. }
  2505. register(Container, 'Container');
  2506. class Defs extends Container {
  2507. constructor(node, attrs = node) {
  2508. super(nodeOrNew('defs', node), attrs);
  2509. }
  2510. flatten() {
  2511. return this;
  2512. }
  2513. ungroup() {
  2514. return this;
  2515. }
  2516. }
  2517. register(Defs, 'Defs');
  2518. class Shape extends Element {}
  2519. register(Shape, 'Shape');
  2520. function rx(rx) {
  2521. return this.attr('rx', rx);
  2522. } // Radius y value
  2523. function ry(ry) {
  2524. return this.attr('ry', ry);
  2525. } // Move over x-axis
  2526. function x$3(x) {
  2527. return x == null ? this.cx() - this.rx() : this.cx(x + this.rx());
  2528. } // Move over y-axis
  2529. function y$3(y) {
  2530. return y == null ? this.cy() - this.ry() : this.cy(y + this.ry());
  2531. } // Move by center over x-axis
  2532. function cx$1(x) {
  2533. return this.attr('cx', x);
  2534. } // Move by center over y-axis
  2535. function cy$1(y) {
  2536. return this.attr('cy', y);
  2537. } // Set width of element
  2538. function width$2(width) {
  2539. return width == null ? this.rx() * 2 : this.rx(new SVGNumber(width).divide(2));
  2540. } // Set height of element
  2541. function height$2(height) {
  2542. return height == null ? this.ry() * 2 : this.ry(new SVGNumber(height).divide(2));
  2543. }
  2544. var circled = {
  2545. __proto__: null,
  2546. rx: rx,
  2547. ry: ry,
  2548. x: x$3,
  2549. y: y$3,
  2550. cx: cx$1,
  2551. cy: cy$1,
  2552. width: width$2,
  2553. height: height$2
  2554. };
  2555. class Ellipse extends Shape {
  2556. constructor(node, attrs = node) {
  2557. super(nodeOrNew('ellipse', node), attrs);
  2558. }
  2559. size(width, height) {
  2560. const p = proportionalSize(this, width, height);
  2561. return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2));
  2562. }
  2563. }
  2564. extend(Ellipse, circled);
  2565. registerMethods('Container', {
  2566. // Create an ellipse
  2567. ellipse: wrapWithAttrCheck(function (width = 0, height = width) {
  2568. return this.put(new Ellipse()).size(width, height).move(0, 0);
  2569. })
  2570. });
  2571. register(Ellipse, 'Ellipse');
  2572. class Fragment extends Dom {
  2573. constructor(node = globals.document.createDocumentFragment()) {
  2574. super(node);
  2575. } // Import / Export raw xml
  2576. xml(xmlOrFn, outerXML, ns) {
  2577. if (typeof xmlOrFn === 'boolean') {
  2578. ns = outerXML;
  2579. outerXML = xmlOrFn;
  2580. xmlOrFn = null;
  2581. } // because this is a fragment we have to put all elements into a wrapper first
  2582. // before we can get the innerXML from it
  2583. if (xmlOrFn == null || typeof xmlOrFn === 'function') {
  2584. const wrapper = new Dom(create('wrapper', ns));
  2585. wrapper.add(this.node.cloneNode(true));
  2586. return wrapper.xml(false, ns);
  2587. } // Act as setter if we got a string
  2588. return super.xml(xmlOrFn, false, ns);
  2589. }
  2590. }
  2591. register(Fragment, 'Fragment');
  2592. function from(x, y) {
  2593. return (this._element || this).type === 'radialGradient' ? this.attr({
  2594. fx: new SVGNumber(x),
  2595. fy: new SVGNumber(y)
  2596. }) : this.attr({
  2597. x1: new SVGNumber(x),
  2598. y1: new SVGNumber(y)
  2599. });
  2600. }
  2601. function to(x, y) {
  2602. return (this._element || this).type === 'radialGradient' ? this.attr({
  2603. cx: new SVGNumber(x),
  2604. cy: new SVGNumber(y)
  2605. }) : this.attr({
  2606. x2: new SVGNumber(x),
  2607. y2: new SVGNumber(y)
  2608. });
  2609. }
  2610. var gradiented = {
  2611. __proto__: null,
  2612. from: from,
  2613. to: to
  2614. };
  2615. class Gradient extends Container {
  2616. constructor(type, attrs) {
  2617. super(nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type), attrs);
  2618. } // custom attr to handle transform
  2619. attr(a, b, c) {
  2620. if (a === 'transform') a = 'gradientTransform';
  2621. return super.attr(a, b, c);
  2622. }
  2623. bbox() {
  2624. return new Box();
  2625. }
  2626. targets() {
  2627. return baseFind('svg [fill*=' + this.id() + ']');
  2628. } // Alias string conversion to fill
  2629. toString() {
  2630. return this.url();
  2631. } // Update gradient
  2632. update(block) {
  2633. // remove all stops
  2634. this.clear(); // invoke passed block
  2635. if (typeof block === 'function') {
  2636. block.call(this, this);
  2637. }
  2638. return this;
  2639. } // Return the fill id
  2640. url() {
  2641. return 'url(#' + this.id() + ')';
  2642. }
  2643. }
  2644. extend(Gradient, gradiented);
  2645. registerMethods({
  2646. Container: {
  2647. // Create gradient element in defs
  2648. gradient(...args) {
  2649. return this.defs().gradient(...args);
  2650. }
  2651. },
  2652. // define gradient
  2653. Defs: {
  2654. gradient: wrapWithAttrCheck(function (type, block) {
  2655. return this.put(new Gradient(type)).update(block);
  2656. })
  2657. }
  2658. });
  2659. register(Gradient, 'Gradient');
  2660. class Pattern extends Container {
  2661. // Initialize node
  2662. constructor(node, attrs = node) {
  2663. super(nodeOrNew('pattern', node), attrs);
  2664. } // custom attr to handle transform
  2665. attr(a, b, c) {
  2666. if (a === 'transform') a = 'patternTransform';
  2667. return super.attr(a, b, c);
  2668. }
  2669. bbox() {
  2670. return new Box();
  2671. }
  2672. targets() {
  2673. return baseFind('svg [fill*=' + this.id() + ']');
  2674. } // Alias string conversion to fill
  2675. toString() {
  2676. return this.url();
  2677. } // Update pattern by rebuilding
  2678. update(block) {
  2679. // remove content
  2680. this.clear(); // invoke passed block
  2681. if (typeof block === 'function') {
  2682. block.call(this, this);
  2683. }
  2684. return this;
  2685. } // Return the fill id
  2686. url() {
  2687. return 'url(#' + this.id() + ')';
  2688. }
  2689. }
  2690. registerMethods({
  2691. Container: {
  2692. // Create pattern element in defs
  2693. pattern(...args) {
  2694. return this.defs().pattern(...args);
  2695. }
  2696. },
  2697. Defs: {
  2698. pattern: wrapWithAttrCheck(function (width, height, block) {
  2699. return this.put(new Pattern()).update(block).attr({
  2700. x: 0,
  2701. y: 0,
  2702. width: width,
  2703. height: height,
  2704. patternUnits: 'userSpaceOnUse'
  2705. });
  2706. })
  2707. }
  2708. });
  2709. register(Pattern, 'Pattern');
  2710. class Image extends Shape {
  2711. constructor(node, attrs = node) {
  2712. super(nodeOrNew('image', node), attrs);
  2713. } // (re)load image
  2714. load(url, callback) {
  2715. if (!url) return this;
  2716. const img = new globals.window.Image();
  2717. on(img, 'load', function (e) {
  2718. const p = this.parent(Pattern); // ensure image size
  2719. if (this.width() === 0 && this.height() === 0) {
  2720. this.size(img.width, img.height);
  2721. }
  2722. if (p instanceof Pattern) {
  2723. // ensure pattern size if not set
  2724. if (p.width() === 0 && p.height() === 0) {
  2725. p.size(this.width(), this.height());
  2726. }
  2727. }
  2728. if (typeof callback === 'function') {
  2729. callback.call(this, e);
  2730. }
  2731. }, this);
  2732. on(img, 'load error', function () {
  2733. // dont forget to unbind memory leaking events
  2734. off(img);
  2735. });
  2736. return this.attr('href', img.src = url, xlink);
  2737. }
  2738. }
  2739. registerAttrHook(function (attr, val, _this) {
  2740. // convert image fill and stroke to patterns
  2741. if (attr === 'fill' || attr === 'stroke') {
  2742. if (isImage.test(val)) {
  2743. val = _this.root().defs().image(val);
  2744. }
  2745. }
  2746. if (val instanceof Image) {
  2747. val = _this.root().defs().pattern(0, 0, pattern => {
  2748. pattern.add(val);
  2749. });
  2750. }
  2751. return val;
  2752. });
  2753. registerMethods({
  2754. Container: {
  2755. // create image element, load image and set its size
  2756. image: wrapWithAttrCheck(function (source, callback) {
  2757. return this.put(new Image()).size(0, 0).load(source, callback);
  2758. })
  2759. }
  2760. });
  2761. register(Image, 'Image');
  2762. class PointArray extends SVGArray {
  2763. // Get bounding box of points
  2764. bbox() {
  2765. let maxX = -Infinity;
  2766. let maxY = -Infinity;
  2767. let minX = Infinity;
  2768. let minY = Infinity;
  2769. this.forEach(function (el) {
  2770. maxX = Math.max(el[0], maxX);
  2771. maxY = Math.max(el[1], maxY);
  2772. minX = Math.min(el[0], minX);
  2773. minY = Math.min(el[1], minY);
  2774. });
  2775. return new Box(minX, minY, maxX - minX, maxY - minY);
  2776. } // Move point string
  2777. move(x, y) {
  2778. const box = this.bbox(); // get relative offset
  2779. x -= box.x;
  2780. y -= box.y; // move every point
  2781. if (!isNaN(x) && !isNaN(y)) {
  2782. for (let i = this.length - 1; i >= 0; i--) {
  2783. this[i] = [this[i][0] + x, this[i][1] + y];
  2784. }
  2785. }
  2786. return this;
  2787. } // Parse point string and flat array
  2788. parse(array = [0, 0]) {
  2789. const points = []; // if it is an array, we flatten it and therefore clone it to 1 depths
  2790. if (array instanceof Array) {
  2791. array = Array.prototype.concat.apply([], array);
  2792. } else {
  2793. // Else, it is considered as a string
  2794. // parse points
  2795. array = array.trim().split(delimiter).map(parseFloat);
  2796. } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
  2797. // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
  2798. if (array.length % 2 !== 0) array.pop(); // wrap points in two-tuples
  2799. for (let i = 0, len = array.length; i < len; i = i + 2) {
  2800. points.push([array[i], array[i + 1]]);
  2801. }
  2802. return points;
  2803. } // Resize poly string
  2804. size(width, height) {
  2805. let i;
  2806. const box = this.bbox(); // recalculate position of all points according to new size
  2807. for (i = this.length - 1; i >= 0; i--) {
  2808. if (box.width) this[i][0] = (this[i][0] - box.x) * width / box.width + box.x;
  2809. if (box.height) this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
  2810. }
  2811. return this;
  2812. } // Convert array to line object
  2813. toLine() {
  2814. return {
  2815. x1: this[0][0],
  2816. y1: this[0][1],
  2817. x2: this[1][0],
  2818. y2: this[1][1]
  2819. };
  2820. } // Convert array to string
  2821. toString() {
  2822. const array = []; // convert to a poly point string
  2823. for (let i = 0, il = this.length; i < il; i++) {
  2824. array.push(this[i].join(','));
  2825. }
  2826. return array.join(' ');
  2827. }
  2828. transform(m) {
  2829. return this.clone().transformO(m);
  2830. } // transform points with matrix (similar to Point.transform)
  2831. transformO(m) {
  2832. if (!Matrix.isMatrixLike(m)) {
  2833. m = new Matrix(m);
  2834. }
  2835. for (let i = this.length; i--;) {
  2836. // Perform the matrix multiplication
  2837. const [x, y] = this[i];
  2838. this[i][0] = m.a * x + m.c * y + m.e;
  2839. this[i][1] = m.b * x + m.d * y + m.f;
  2840. }
  2841. return this;
  2842. }
  2843. }
  2844. const MorphArray = PointArray; // Move by left top corner over x-axis
  2845. function x$2(x) {
  2846. return x == null ? this.bbox().x : this.move(x, this.bbox().y);
  2847. } // Move by left top corner over y-axis
  2848. function y$2(y) {
  2849. return y == null ? this.bbox().y : this.move(this.bbox().x, y);
  2850. } // Set width of element
  2851. function width$1(width) {
  2852. const b = this.bbox();
  2853. return width == null ? b.width : this.size(width, b.height);
  2854. } // Set height of element
  2855. function height$1(height) {
  2856. const b = this.bbox();
  2857. return height == null ? b.height : this.size(b.width, height);
  2858. }
  2859. var pointed = {
  2860. __proto__: null,
  2861. MorphArray: MorphArray,
  2862. x: x$2,
  2863. y: y$2,
  2864. width: width$1,
  2865. height: height$1
  2866. };
  2867. class Line extends Shape {
  2868. // Initialize node
  2869. constructor(node, attrs = node) {
  2870. super(nodeOrNew('line', node), attrs);
  2871. } // Get array
  2872. array() {
  2873. return new PointArray([[this.attr('x1'), this.attr('y1')], [this.attr('x2'), this.attr('y2')]]);
  2874. } // Move by left top corner
  2875. move(x, y) {
  2876. return this.attr(this.array().move(x, y).toLine());
  2877. } // Overwrite native plot() method
  2878. plot(x1, y1, x2, y2) {
  2879. if (x1 == null) {
  2880. return this.array();
  2881. } else if (typeof y1 !== 'undefined') {
  2882. x1 = {
  2883. x1,
  2884. y1,
  2885. x2,
  2886. y2
  2887. };
  2888. } else {
  2889. x1 = new PointArray(x1).toLine();
  2890. }
  2891. return this.attr(x1);
  2892. } // Set element size to given width and height
  2893. size(width, height) {
  2894. const p = proportionalSize(this, width, height);
  2895. return this.attr(this.array().size(p.width, p.height).toLine());
  2896. }
  2897. }
  2898. extend(Line, pointed);
  2899. registerMethods({
  2900. Container: {
  2901. // Create a line element
  2902. line: wrapWithAttrCheck(function (...args) {
  2903. // make sure plot is called as a setter
  2904. // x1 is not necessarily a number, it can also be an array, a string and a PointArray
  2905. return Line.prototype.plot.apply(this.put(new Line()), args[0] != null ? args : [0, 0, 0, 0]);
  2906. })
  2907. }
  2908. });
  2909. register(Line, 'Line');
  2910. class Marker extends Container {
  2911. // Initialize node
  2912. constructor(node, attrs = node) {
  2913. super(nodeOrNew('marker', node), attrs);
  2914. } // Set height of element
  2915. height(height) {
  2916. return this.attr('markerHeight', height);
  2917. }
  2918. orient(orient) {
  2919. return this.attr('orient', orient);
  2920. } // Set marker refX and refY
  2921. ref(x, y) {
  2922. return this.attr('refX', x).attr('refY', y);
  2923. } // Return the fill id
  2924. toString() {
  2925. return 'url(#' + this.id() + ')';
  2926. } // Update marker
  2927. update(block) {
  2928. // remove all content
  2929. this.clear(); // invoke passed block
  2930. if (typeof block === 'function') {
  2931. block.call(this, this);
  2932. }
  2933. return this;
  2934. } // Set width of element
  2935. width(width) {
  2936. return this.attr('markerWidth', width);
  2937. }
  2938. }
  2939. registerMethods({
  2940. Container: {
  2941. marker(...args) {
  2942. // Create marker element in defs
  2943. return this.defs().marker(...args);
  2944. }
  2945. },
  2946. Defs: {
  2947. // Create marker
  2948. marker: wrapWithAttrCheck(function (width, height, block) {
  2949. // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
  2950. return this.put(new Marker()).size(width, height).ref(width / 2, height / 2).viewbox(0, 0, width, height).attr('orient', 'auto').update(block);
  2951. })
  2952. },
  2953. marker: {
  2954. // Create and attach markers
  2955. marker(marker, width, height, block) {
  2956. let attr = ['marker']; // Build attribute name
  2957. if (marker !== 'all') attr.push(marker);
  2958. attr = attr.join('-'); // Set marker attribute
  2959. marker = arguments[1] instanceof Marker ? arguments[1] : this.defs().marker(width, height, block);
  2960. return this.attr(attr, marker);
  2961. }
  2962. }
  2963. });
  2964. register(Marker, 'Marker');
  2965. /***
  2966. Base Class
  2967. ==========
  2968. The base stepper class that will be
  2969. ***/
  2970. function makeSetterGetter(k, f) {
  2971. return function (v) {
  2972. if (v == null) return this[k];
  2973. this[k] = v;
  2974. if (f) f.call(this);
  2975. return this;
  2976. };
  2977. }
  2978. const easing = {
  2979. '-': function (pos) {
  2980. return pos;
  2981. },
  2982. '<>': function (pos) {
  2983. return -Math.cos(pos * Math.PI) / 2 + 0.5;
  2984. },
  2985. '>': function (pos) {
  2986. return Math.sin(pos * Math.PI / 2);
  2987. },
  2988. '<': function (pos) {
  2989. return -Math.cos(pos * Math.PI / 2) + 1;
  2990. },
  2991. bezier: function (x1, y1, x2, y2) {
  2992. // see https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo
  2993. return function (t) {
  2994. if (t < 0) {
  2995. if (x1 > 0) {
  2996. return y1 / x1 * t;
  2997. } else if (x2 > 0) {
  2998. return y2 / x2 * t;
  2999. } else {
  3000. return 0;
  3001. }
  3002. } else if (t > 1) {
  3003. if (x2 < 1) {
  3004. return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2);
  3005. } else if (x1 < 1) {
  3006. return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1);
  3007. } else {
  3008. return 1;
  3009. }
  3010. } else {
  3011. return 3 * t * (1 - t) ** 2 * y1 + 3 * t ** 2 * (1 - t) * y2 + t ** 3;
  3012. }
  3013. };
  3014. },
  3015. // see https://www.w3.org/TR/css-easing-1/#step-timing-function-algo
  3016. steps: function (steps, stepPosition = 'end') {
  3017. // deal with "jump-" prefix
  3018. stepPosition = stepPosition.split('-').reverse()[0];
  3019. let jumps = steps;
  3020. if (stepPosition === 'none') {
  3021. --jumps;
  3022. } else if (stepPosition === 'both') {
  3023. ++jumps;
  3024. } // The beforeFlag is essentially useless
  3025. return (t, beforeFlag = false) => {
  3026. // Step is called currentStep in referenced url
  3027. let step = Math.floor(t * steps);
  3028. const jumping = t * step % 1 === 0;
  3029. if (stepPosition === 'start' || stepPosition === 'both') {
  3030. ++step;
  3031. }
  3032. if (beforeFlag && jumping) {
  3033. --step;
  3034. }
  3035. if (t >= 0 && step < 0) {
  3036. step = 0;
  3037. }
  3038. if (t <= 1 && step > jumps) {
  3039. step = jumps;
  3040. }
  3041. return step / jumps;
  3042. };
  3043. }
  3044. };
  3045. class Stepper {
  3046. done() {
  3047. return false;
  3048. }
  3049. }
  3050. /***
  3051. Easing Functions
  3052. ================
  3053. ***/
  3054. class Ease extends Stepper {
  3055. constructor(fn = timeline.ease) {
  3056. super();
  3057. this.ease = easing[fn] || fn;
  3058. }
  3059. step(from, to, pos) {
  3060. if (typeof from !== 'number') {
  3061. return pos < 1 ? from : to;
  3062. }
  3063. return from + (to - from) * this.ease(pos);
  3064. }
  3065. }
  3066. /***
  3067. Controller Types
  3068. ================
  3069. ***/
  3070. class Controller extends Stepper {
  3071. constructor(fn) {
  3072. super();
  3073. this.stepper = fn;
  3074. }
  3075. done(c) {
  3076. return c.done;
  3077. }
  3078. step(current, target, dt, c) {
  3079. return this.stepper(current, target, dt, c);
  3080. }
  3081. }
  3082. function recalculate() {
  3083. // Apply the default parameters
  3084. const duration = (this._duration || 500) / 1000;
  3085. const overshoot = this._overshoot || 0; // Calculate the PID natural response
  3086. const eps = 1e-10;
  3087. const pi = Math.PI;
  3088. const os = Math.log(overshoot / 100 + eps);
  3089. const zeta = -os / Math.sqrt(pi * pi + os * os);
  3090. const wn = 3.9 / (zeta * duration); // Calculate the Spring values
  3091. this.d = 2 * zeta * wn;
  3092. this.k = wn * wn;
  3093. }
  3094. class Spring extends Controller {
  3095. constructor(duration = 500, overshoot = 0) {
  3096. super();
  3097. this.duration(duration).overshoot(overshoot);
  3098. }
  3099. step(current, target, dt, c) {
  3100. if (typeof current === 'string') return current;
  3101. c.done = dt === Infinity;
  3102. if (dt === Infinity) return target;
  3103. if (dt === 0) return current;
  3104. if (dt > 100) dt = 16;
  3105. dt /= 1000; // Get the previous velocity
  3106. const velocity = c.velocity || 0; // Apply the control to get the new position and store it
  3107. const acceleration = -this.d * velocity - this.k * (current - target);
  3108. const newPosition = current + velocity * dt + acceleration * dt * dt / 2; // Store the velocity
  3109. c.velocity = velocity + acceleration * dt; // Figure out if we have converged, and if so, pass the value
  3110. c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 0.002;
  3111. return c.done ? target : newPosition;
  3112. }
  3113. }
  3114. extend(Spring, {
  3115. duration: makeSetterGetter('_duration', recalculate),
  3116. overshoot: makeSetterGetter('_overshoot', recalculate)
  3117. });
  3118. class PID extends Controller {
  3119. constructor(p = 0.1, i = 0.01, d = 0, windup = 1000) {
  3120. super();
  3121. this.p(p).i(i).d(d).windup(windup);
  3122. }
  3123. step(current, target, dt, c) {
  3124. if (typeof current === 'string') return current;
  3125. c.done = dt === Infinity;
  3126. if (dt === Infinity) return target;
  3127. if (dt === 0) return current;
  3128. const p = target - current;
  3129. let i = (c.integral || 0) + p * dt;
  3130. const d = (p - (c.error || 0)) / dt;
  3131. const windup = this._windup; // antiwindup
  3132. if (windup !== false) {
  3133. i = Math.max(-windup, Math.min(i, windup));
  3134. }
  3135. c.error = p;
  3136. c.integral = i;
  3137. c.done = Math.abs(p) < 0.001;
  3138. return c.done ? target : current + (this.P * p + this.I * i + this.D * d);
  3139. }
  3140. }
  3141. extend(PID, {
  3142. windup: makeSetterGetter('_windup'),
  3143. p: makeSetterGetter('P'),
  3144. i: makeSetterGetter('I'),
  3145. d: makeSetterGetter('D')
  3146. });
  3147. const segmentParameters = {
  3148. M: 2,
  3149. L: 2,
  3150. H: 1,
  3151. V: 1,
  3152. C: 6,
  3153. S: 4,
  3154. Q: 4,
  3155. T: 2,
  3156. A: 7,
  3157. Z: 0
  3158. };
  3159. const pathHandlers = {
  3160. M: function (c, p, p0) {
  3161. p.x = p0.x = c[0];
  3162. p.y = p0.y = c[1];
  3163. return ['M', p.x, p.y];
  3164. },
  3165. L: function (c, p) {
  3166. p.x = c[0];
  3167. p.y = c[1];
  3168. return ['L', c[0], c[1]];
  3169. },
  3170. H: function (c, p) {
  3171. p.x = c[0];
  3172. return ['H', c[0]];
  3173. },
  3174. V: function (c, p) {
  3175. p.y = c[0];
  3176. return ['V', c[0]];
  3177. },
  3178. C: function (c, p) {
  3179. p.x = c[4];
  3180. p.y = c[5];
  3181. return ['C', c[0], c[1], c[2], c[3], c[4], c[5]];
  3182. },
  3183. S: function (c, p) {
  3184. p.x = c[2];
  3185. p.y = c[3];
  3186. return ['S', c[0], c[1], c[2], c[3]];
  3187. },
  3188. Q: function (c, p) {
  3189. p.x = c[2];
  3190. p.y = c[3];
  3191. return ['Q', c[0], c[1], c[2], c[3]];
  3192. },
  3193. T: function (c, p) {
  3194. p.x = c[0];
  3195. p.y = c[1];
  3196. return ['T', c[0], c[1]];
  3197. },
  3198. Z: function (c, p, p0) {
  3199. p.x = p0.x;
  3200. p.y = p0.y;
  3201. return ['Z'];
  3202. },
  3203. A: function (c, p) {
  3204. p.x = c[5];
  3205. p.y = c[6];
  3206. return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]];
  3207. }
  3208. };
  3209. const mlhvqtcsaz = 'mlhvqtcsaz'.split('');
  3210. for (let i = 0, il = mlhvqtcsaz.length; i < il; ++i) {
  3211. pathHandlers[mlhvqtcsaz[i]] = function (i) {
  3212. return function (c, p, p0) {
  3213. if (i === 'H') c[0] = c[0] + p.x;else if (i === 'V') c[0] = c[0] + p.y;else if (i === 'A') {
  3214. c[5] = c[5] + p.x;
  3215. c[6] = c[6] + p.y;
  3216. } else {
  3217. for (let j = 0, jl = c.length; j < jl; ++j) {
  3218. c[j] = c[j] + (j % 2 ? p.y : p.x);
  3219. }
  3220. }
  3221. return pathHandlers[i](c, p, p0);
  3222. };
  3223. }(mlhvqtcsaz[i].toUpperCase());
  3224. }
  3225. function makeAbsolut(parser) {
  3226. const command = parser.segment[0];
  3227. return pathHandlers[command](parser.segment.slice(1), parser.p, parser.p0);
  3228. }
  3229. function segmentComplete(parser) {
  3230. return parser.segment.length && parser.segment.length - 1 === segmentParameters[parser.segment[0].toUpperCase()];
  3231. }
  3232. function startNewSegment(parser, token) {
  3233. parser.inNumber && finalizeNumber(parser, false);
  3234. const pathLetter = isPathLetter.test(token);
  3235. if (pathLetter) {
  3236. parser.segment = [token];
  3237. } else {
  3238. const lastCommand = parser.lastCommand;
  3239. const small = lastCommand.toLowerCase();
  3240. const isSmall = lastCommand === small;
  3241. parser.segment = [small === 'm' ? isSmall ? 'l' : 'L' : lastCommand];
  3242. }
  3243. parser.inSegment = true;
  3244. parser.lastCommand = parser.segment[0];
  3245. return pathLetter;
  3246. }
  3247. function finalizeNumber(parser, inNumber) {
  3248. if (!parser.inNumber) throw new Error('Parser Error');
  3249. parser.number && parser.segment.push(parseFloat(parser.number));
  3250. parser.inNumber = inNumber;
  3251. parser.number = '';
  3252. parser.pointSeen = false;
  3253. parser.hasExponent = false;
  3254. if (segmentComplete(parser)) {
  3255. finalizeSegment(parser);
  3256. }
  3257. }
  3258. function finalizeSegment(parser) {
  3259. parser.inSegment = false;
  3260. if (parser.absolute) {
  3261. parser.segment = makeAbsolut(parser);
  3262. }
  3263. parser.segments.push(parser.segment);
  3264. }
  3265. function isArcFlag(parser) {
  3266. if (!parser.segment.length) return false;
  3267. const isArc = parser.segment[0].toUpperCase() === 'A';
  3268. const length = parser.segment.length;
  3269. return isArc && (length === 4 || length === 5);
  3270. }
  3271. function isExponential(parser) {
  3272. return parser.lastToken.toUpperCase() === 'E';
  3273. }
  3274. function pathParser(d, toAbsolute = true) {
  3275. let index = 0;
  3276. let token = '';
  3277. const parser = {
  3278. segment: [],
  3279. inNumber: false,
  3280. number: '',
  3281. lastToken: '',
  3282. inSegment: false,
  3283. segments: [],
  3284. pointSeen: false,
  3285. hasExponent: false,
  3286. absolute: toAbsolute,
  3287. p0: new Point(),
  3288. p: new Point()
  3289. };
  3290. while (parser.lastToken = token, token = d.charAt(index++)) {
  3291. if (!parser.inSegment) {
  3292. if (startNewSegment(parser, token)) {
  3293. continue;
  3294. }
  3295. }
  3296. if (token === '.') {
  3297. if (parser.pointSeen || parser.hasExponent) {
  3298. finalizeNumber(parser, false);
  3299. --index;
  3300. continue;
  3301. }
  3302. parser.inNumber = true;
  3303. parser.pointSeen = true;
  3304. parser.number += token;
  3305. continue;
  3306. }
  3307. if (!isNaN(parseInt(token))) {
  3308. if (parser.number === '0' || isArcFlag(parser)) {
  3309. parser.inNumber = true;
  3310. parser.number = token;
  3311. finalizeNumber(parser, true);
  3312. continue;
  3313. }
  3314. parser.inNumber = true;
  3315. parser.number += token;
  3316. continue;
  3317. }
  3318. if (token === ' ' || token === ',') {
  3319. if (parser.inNumber) {
  3320. finalizeNumber(parser, false);
  3321. }
  3322. continue;
  3323. }
  3324. if (token === '-') {
  3325. if (parser.inNumber && !isExponential(parser)) {
  3326. finalizeNumber(parser, false);
  3327. --index;
  3328. continue;
  3329. }
  3330. parser.number += token;
  3331. parser.inNumber = true;
  3332. continue;
  3333. }
  3334. if (token.toUpperCase() === 'E') {
  3335. parser.number += token;
  3336. parser.hasExponent = true;
  3337. continue;
  3338. }
  3339. if (isPathLetter.test(token)) {
  3340. if (parser.inNumber) {
  3341. finalizeNumber(parser, false);
  3342. } else if (!segmentComplete(parser)) {
  3343. throw new Error('parser Error');
  3344. } else {
  3345. finalizeSegment(parser);
  3346. }
  3347. --index;
  3348. }
  3349. }
  3350. if (parser.inNumber) {
  3351. finalizeNumber(parser, false);
  3352. }
  3353. if (parser.inSegment && segmentComplete(parser)) {
  3354. finalizeSegment(parser);
  3355. }
  3356. return parser.segments;
  3357. }
  3358. function arrayToString(a) {
  3359. let s = '';
  3360. for (let i = 0, il = a.length; i < il; i++) {
  3361. s += a[i][0];
  3362. if (a[i][1] != null) {
  3363. s += a[i][1];
  3364. if (a[i][2] != null) {
  3365. s += ' ';
  3366. s += a[i][2];
  3367. if (a[i][3] != null) {
  3368. s += ' ';
  3369. s += a[i][3];
  3370. s += ' ';
  3371. s += a[i][4];
  3372. if (a[i][5] != null) {
  3373. s += ' ';
  3374. s += a[i][5];
  3375. s += ' ';
  3376. s += a[i][6];
  3377. if (a[i][7] != null) {
  3378. s += ' ';
  3379. s += a[i][7];
  3380. }
  3381. }
  3382. }
  3383. }
  3384. }
  3385. }
  3386. return s + ' ';
  3387. }
  3388. class PathArray extends SVGArray {
  3389. // Get bounding box of path
  3390. bbox() {
  3391. parser().path.setAttribute('d', this.toString());
  3392. return new Box(parser.nodes.path.getBBox());
  3393. } // Move path string
  3394. move(x, y) {
  3395. // get bounding box of current situation
  3396. const box = this.bbox(); // get relative offset
  3397. x -= box.x;
  3398. y -= box.y;
  3399. if (!isNaN(x) && !isNaN(y)) {
  3400. // move every point
  3401. for (let l, i = this.length - 1; i >= 0; i--) {
  3402. l = this[i][0];
  3403. if (l === 'M' || l === 'L' || l === 'T') {
  3404. this[i][1] += x;
  3405. this[i][2] += y;
  3406. } else if (l === 'H') {
  3407. this[i][1] += x;
  3408. } else if (l === 'V') {
  3409. this[i][1] += y;
  3410. } else if (l === 'C' || l === 'S' || l === 'Q') {
  3411. this[i][1] += x;
  3412. this[i][2] += y;
  3413. this[i][3] += x;
  3414. this[i][4] += y;
  3415. if (l === 'C') {
  3416. this[i][5] += x;
  3417. this[i][6] += y;
  3418. }
  3419. } else if (l === 'A') {
  3420. this[i][6] += x;
  3421. this[i][7] += y;
  3422. }
  3423. }
  3424. }
  3425. return this;
  3426. } // Absolutize and parse path to array
  3427. parse(d = 'M0 0') {
  3428. if (Array.isArray(d)) {
  3429. d = Array.prototype.concat.apply([], d).toString();
  3430. }
  3431. return pathParser(d);
  3432. } // Resize path string
  3433. size(width, height) {
  3434. // get bounding box of current situation
  3435. const box = this.bbox();
  3436. let i, l; // If the box width or height is 0 then we ignore
  3437. // transformations on the respective axis
  3438. box.width = box.width === 0 ? 1 : box.width;
  3439. box.height = box.height === 0 ? 1 : box.height; // recalculate position of all points according to new size
  3440. for (i = this.length - 1; i >= 0; i--) {
  3441. l = this[i][0];
  3442. if (l === 'M' || l === 'L' || l === 'T') {
  3443. this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
  3444. this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
  3445. } else if (l === 'H') {
  3446. this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
  3447. } else if (l === 'V') {
  3448. this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
  3449. } else if (l === 'C' || l === 'S' || l === 'Q') {
  3450. this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
  3451. this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
  3452. this[i][3] = (this[i][3] - box.x) * width / box.width + box.x;
  3453. this[i][4] = (this[i][4] - box.y) * height / box.height + box.y;
  3454. if (l === 'C') {
  3455. this[i][5] = (this[i][5] - box.x) * width / box.width + box.x;
  3456. this[i][6] = (this[i][6] - box.y) * height / box.height + box.y;
  3457. }
  3458. } else if (l === 'A') {
  3459. // resize radii
  3460. this[i][1] = this[i][1] * width / box.width;
  3461. this[i][2] = this[i][2] * height / box.height; // move position values
  3462. this[i][6] = (this[i][6] - box.x) * width / box.width + box.x;
  3463. this[i][7] = (this[i][7] - box.y) * height / box.height + box.y;
  3464. }
  3465. }
  3466. return this;
  3467. } // Convert array to string
  3468. toString() {
  3469. return arrayToString(this);
  3470. }
  3471. }
  3472. const getClassForType = value => {
  3473. const type = typeof value;
  3474. if (type === 'number') {
  3475. return SVGNumber;
  3476. } else if (type === 'string') {
  3477. if (Color.isColor(value)) {
  3478. return Color;
  3479. } else if (delimiter.test(value)) {
  3480. return isPathLetter.test(value) ? PathArray : SVGArray;
  3481. } else if (numberAndUnit.test(value)) {
  3482. return SVGNumber;
  3483. } else {
  3484. return NonMorphable;
  3485. }
  3486. } else if (morphableTypes.indexOf(value.constructor) > -1) {
  3487. return value.constructor;
  3488. } else if (Array.isArray(value)) {
  3489. return SVGArray;
  3490. } else if (type === 'object') {
  3491. return ObjectBag;
  3492. } else {
  3493. return NonMorphable;
  3494. }
  3495. };
  3496. class Morphable {
  3497. constructor(stepper) {
  3498. this._stepper = stepper || new Ease('-');
  3499. this._from = null;
  3500. this._to = null;
  3501. this._type = null;
  3502. this._context = null;
  3503. this._morphObj = null;
  3504. }
  3505. at(pos) {
  3506. return this._morphObj.morph(this._from, this._to, pos, this._stepper, this._context);
  3507. }
  3508. done() {
  3509. const complete = this._context.map(this._stepper.done).reduce(function (last, curr) {
  3510. return last && curr;
  3511. }, true);
  3512. return complete;
  3513. }
  3514. from(val) {
  3515. if (val == null) {
  3516. return this._from;
  3517. }
  3518. this._from = this._set(val);
  3519. return this;
  3520. }
  3521. stepper(stepper) {
  3522. if (stepper == null) return this._stepper;
  3523. this._stepper = stepper;
  3524. return this;
  3525. }
  3526. to(val) {
  3527. if (val == null) {
  3528. return this._to;
  3529. }
  3530. this._to = this._set(val);
  3531. return this;
  3532. }
  3533. type(type) {
  3534. // getter
  3535. if (type == null) {
  3536. return this._type;
  3537. } // setter
  3538. this._type = type;
  3539. return this;
  3540. }
  3541. _set(value) {
  3542. if (!this._type) {
  3543. this.type(getClassForType(value));
  3544. }
  3545. let result = new this._type(value);
  3546. if (this._type === Color) {
  3547. result = this._to ? result[this._to[4]]() : this._from ? result[this._from[4]]() : result;
  3548. }
  3549. if (this._type === ObjectBag) {
  3550. result = this._to ? result.align(this._to) : this._from ? result.align(this._from) : result;
  3551. }
  3552. result = result.toConsumable();
  3553. this._morphObj = this._morphObj || new this._type();
  3554. this._context = this._context || Array.apply(null, Array(result.length)).map(Object).map(function (o) {
  3555. o.done = true;
  3556. return o;
  3557. });
  3558. return result;
  3559. }
  3560. }
  3561. class NonMorphable {
  3562. constructor(...args) {
  3563. this.init(...args);
  3564. }
  3565. init(val) {
  3566. val = Array.isArray(val) ? val[0] : val;
  3567. this.value = val;
  3568. return this;
  3569. }
  3570. toArray() {
  3571. return [this.value];
  3572. }
  3573. valueOf() {
  3574. return this.value;
  3575. }
  3576. }
  3577. class TransformBag {
  3578. constructor(...args) {
  3579. this.init(...args);
  3580. }
  3581. init(obj) {
  3582. if (Array.isArray(obj)) {
  3583. obj = {
  3584. scaleX: obj[0],
  3585. scaleY: obj[1],
  3586. shear: obj[2],
  3587. rotate: obj[3],
  3588. translateX: obj[4],
  3589. translateY: obj[5],
  3590. originX: obj[6],
  3591. originY: obj[7]
  3592. };
  3593. }
  3594. Object.assign(this, TransformBag.defaults, obj);
  3595. return this;
  3596. }
  3597. toArray() {
  3598. const v = this;
  3599. return [v.scaleX, v.scaleY, v.shear, v.rotate, v.translateX, v.translateY, v.originX, v.originY];
  3600. }
  3601. }
  3602. TransformBag.defaults = {
  3603. scaleX: 1,
  3604. scaleY: 1,
  3605. shear: 0,
  3606. rotate: 0,
  3607. translateX: 0,
  3608. translateY: 0,
  3609. originX: 0,
  3610. originY: 0
  3611. };
  3612. const sortByKey = (a, b) => {
  3613. return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
  3614. };
  3615. class ObjectBag {
  3616. constructor(...args) {
  3617. this.init(...args);
  3618. }
  3619. align(other) {
  3620. const values = this.values;
  3621. for (let i = 0, il = values.length; i < il; ++i) {
  3622. // If the type is the same we only need to check if the color is in the correct format
  3623. if (values[i + 1] === other[i + 1]) {
  3624. if (values[i + 1] === Color && other[i + 7] !== values[i + 7]) {
  3625. const space = other[i + 7];
  3626. const color = new Color(this.values.splice(i + 3, 5))[space]().toArray();
  3627. this.values.splice(i + 3, 0, ...color);
  3628. }
  3629. i += values[i + 2] + 2;
  3630. continue;
  3631. }
  3632. if (!other[i + 1]) {
  3633. return this;
  3634. } // The types differ, so we overwrite the new type with the old one
  3635. // And initialize it with the types default (e.g. black for color or 0 for number)
  3636. const defaultObject = new other[i + 1]().toArray(); // Than we fix the values array
  3637. const toDelete = values[i + 2] + 3;
  3638. values.splice(i, toDelete, other[i], other[i + 1], other[i + 2], ...defaultObject);
  3639. i += values[i + 2] + 2;
  3640. }
  3641. return this;
  3642. }
  3643. init(objOrArr) {
  3644. this.values = [];
  3645. if (Array.isArray(objOrArr)) {
  3646. this.values = objOrArr.slice();
  3647. return;
  3648. }
  3649. objOrArr = objOrArr || {};
  3650. const entries = [];
  3651. for (const i in objOrArr) {
  3652. const Type = getClassForType(objOrArr[i]);
  3653. const val = new Type(objOrArr[i]).toArray();
  3654. entries.push([i, Type, val.length, ...val]);
  3655. }
  3656. entries.sort(sortByKey);
  3657. this.values = entries.reduce((last, curr) => last.concat(curr), []);
  3658. return this;
  3659. }
  3660. toArray() {
  3661. return this.values;
  3662. }
  3663. valueOf() {
  3664. const obj = {};
  3665. const arr = this.values; // for (var i = 0, len = arr.length; i < len; i += 2) {
  3666. while (arr.length) {
  3667. const key = arr.shift();
  3668. const Type = arr.shift();
  3669. const num = arr.shift();
  3670. const values = arr.splice(0, num);
  3671. obj[key] = new Type(values); // .valueOf()
  3672. }
  3673. return obj;
  3674. }
  3675. }
  3676. const morphableTypes = [NonMorphable, TransformBag, ObjectBag];
  3677. function registerMorphableType(type = []) {
  3678. morphableTypes.push(...[].concat(type));
  3679. }
  3680. function makeMorphable() {
  3681. extend(morphableTypes, {
  3682. to(val) {
  3683. return new Morphable().type(this.constructor).from(this.toArray()) // this.valueOf())
  3684. .to(val);
  3685. },
  3686. fromArray(arr) {
  3687. this.init(arr);
  3688. return this;
  3689. },
  3690. toConsumable() {
  3691. return this.toArray();
  3692. },
  3693. morph(from, to, pos, stepper, context) {
  3694. const mapper = function (i, index) {
  3695. return stepper.step(i, to[index], pos, context[index], context);
  3696. };
  3697. return this.fromArray(from.map(mapper));
  3698. }
  3699. });
  3700. }
  3701. class Path extends Shape {
  3702. // Initialize node
  3703. constructor(node, attrs = node) {
  3704. super(nodeOrNew('path', node), attrs);
  3705. } // Get array
  3706. array() {
  3707. return this._array || (this._array = new PathArray(this.attr('d')));
  3708. } // Clear array cache
  3709. clear() {
  3710. delete this._array;
  3711. return this;
  3712. } // Set height of element
  3713. height(height) {
  3714. return height == null ? this.bbox().height : this.size(this.bbox().width, height);
  3715. } // Move by left top corner
  3716. move(x, y) {
  3717. return this.attr('d', this.array().move(x, y));
  3718. } // Plot new path
  3719. plot(d) {
  3720. return d == null ? this.array() : this.clear().attr('d', typeof d === 'string' ? d : this._array = new PathArray(d));
  3721. } // Set element size to given width and height
  3722. size(width, height) {
  3723. const p = proportionalSize(this, width, height);
  3724. return this.attr('d', this.array().size(p.width, p.height));
  3725. } // Set width of element
  3726. width(width) {
  3727. return width == null ? this.bbox().width : this.size(width, this.bbox().height);
  3728. } // Move by left top corner over x-axis
  3729. x(x) {
  3730. return x == null ? this.bbox().x : this.move(x, this.bbox().y);
  3731. } // Move by left top corner over y-axis
  3732. y(y) {
  3733. return y == null ? this.bbox().y : this.move(this.bbox().x, y);
  3734. }
  3735. } // Define morphable array
  3736. Path.prototype.MorphArray = PathArray; // Add parent method
  3737. registerMethods({
  3738. Container: {
  3739. // Create a wrapped path element
  3740. path: wrapWithAttrCheck(function (d) {
  3741. // make sure plot is called as a setter
  3742. return this.put(new Path()).plot(d || new PathArray());
  3743. })
  3744. }
  3745. });
  3746. register(Path, 'Path');
  3747. function array() {
  3748. return this._array || (this._array = new PointArray(this.attr('points')));
  3749. } // Clear array cache
  3750. function clear() {
  3751. delete this._array;
  3752. return this;
  3753. } // Move by left top corner
  3754. function move$2(x, y) {
  3755. return this.attr('points', this.array().move(x, y));
  3756. } // Plot new path
  3757. function plot(p) {
  3758. return p == null ? this.array() : this.clear().attr('points', typeof p === 'string' ? p : this._array = new PointArray(p));
  3759. } // Set element size to given width and height
  3760. function size$1(width, height) {
  3761. const p = proportionalSize(this, width, height);
  3762. return this.attr('points', this.array().size(p.width, p.height));
  3763. }
  3764. var poly = {
  3765. __proto__: null,
  3766. array: array,
  3767. clear: clear,
  3768. move: move$2,
  3769. plot: plot,
  3770. size: size$1
  3771. };
  3772. class Polygon extends Shape {
  3773. // Initialize node
  3774. constructor(node, attrs = node) {
  3775. super(nodeOrNew('polygon', node), attrs);
  3776. }
  3777. }
  3778. registerMethods({
  3779. Container: {
  3780. // Create a wrapped polygon element
  3781. polygon: wrapWithAttrCheck(function (p) {
  3782. // make sure plot is called as a setter
  3783. return this.put(new Polygon()).plot(p || new PointArray());
  3784. })
  3785. }
  3786. });
  3787. extend(Polygon, pointed);
  3788. extend(Polygon, poly);
  3789. register(Polygon, 'Polygon');
  3790. class Polyline extends Shape {
  3791. // Initialize node
  3792. constructor(node, attrs = node) {
  3793. super(nodeOrNew('polyline', node), attrs);
  3794. }
  3795. }
  3796. registerMethods({
  3797. Container: {
  3798. // Create a wrapped polygon element
  3799. polyline: wrapWithAttrCheck(function (p) {
  3800. // make sure plot is called as a setter
  3801. return this.put(new Polyline()).plot(p || new PointArray());
  3802. })
  3803. }
  3804. });
  3805. extend(Polyline, pointed);
  3806. extend(Polyline, poly);
  3807. register(Polyline, 'Polyline');
  3808. class Rect extends Shape {
  3809. // Initialize node
  3810. constructor(node, attrs = node) {
  3811. super(nodeOrNew('rect', node), attrs);
  3812. }
  3813. }
  3814. extend(Rect, {
  3815. rx,
  3816. ry
  3817. });
  3818. registerMethods({
  3819. Container: {
  3820. // Create a rect element
  3821. rect: wrapWithAttrCheck(function (width, height) {
  3822. return this.put(new Rect()).size(width, height);
  3823. })
  3824. }
  3825. });
  3826. register(Rect, 'Rect');
  3827. class Queue {
  3828. constructor() {
  3829. this._first = null;
  3830. this._last = null;
  3831. } // Shows us the first item in the list
  3832. first() {
  3833. return this._first && this._first.value;
  3834. } // Shows us the last item in the list
  3835. last() {
  3836. return this._last && this._last.value;
  3837. }
  3838. push(value) {
  3839. // An item stores an id and the provided value
  3840. const item = typeof value.next !== 'undefined' ? value : {
  3841. value: value,
  3842. next: null,
  3843. prev: null
  3844. }; // Deal with the queue being empty or populated
  3845. if (this._last) {
  3846. item.prev = this._last;
  3847. this._last.next = item;
  3848. this._last = item;
  3849. } else {
  3850. this._last = item;
  3851. this._first = item;
  3852. } // Return the current item
  3853. return item;
  3854. } // Removes the item that was returned from the push
  3855. remove(item) {
  3856. // Relink the previous item
  3857. if (item.prev) item.prev.next = item.next;
  3858. if (item.next) item.next.prev = item.prev;
  3859. if (item === this._last) this._last = item.prev;
  3860. if (item === this._first) this._first = item.next; // Invalidate item
  3861. item.prev = null;
  3862. item.next = null;
  3863. }
  3864. shift() {
  3865. // Check if we have a value
  3866. const remove = this._first;
  3867. if (!remove) return null; // If we do, remove it and relink things
  3868. this._first = remove.next;
  3869. if (this._first) this._first.prev = null;
  3870. this._last = this._first ? this._last : null;
  3871. return remove.value;
  3872. }
  3873. }
  3874. const Animator = {
  3875. nextDraw: null,
  3876. frames: new Queue(),
  3877. timeouts: new Queue(),
  3878. immediates: new Queue(),
  3879. timer: () => globals.window.performance || globals.window.Date,
  3880. transforms: [],
  3881. frame(fn) {
  3882. // Store the node
  3883. const node = Animator.frames.push({
  3884. run: fn
  3885. }); // Request an animation frame if we don't have one
  3886. if (Animator.nextDraw === null) {
  3887. Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
  3888. } // Return the node so we can remove it easily
  3889. return node;
  3890. },
  3891. timeout(fn, delay) {
  3892. delay = delay || 0; // Work out when the event should fire
  3893. const time = Animator.timer().now() + delay; // Add the timeout to the end of the queue
  3894. const node = Animator.timeouts.push({
  3895. run: fn,
  3896. time: time
  3897. }); // Request another animation frame if we need one
  3898. if (Animator.nextDraw === null) {
  3899. Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
  3900. }
  3901. return node;
  3902. },
  3903. immediate(fn) {
  3904. // Add the immediate fn to the end of the queue
  3905. const node = Animator.immediates.push(fn); // Request another animation frame if we need one
  3906. if (Animator.nextDraw === null) {
  3907. Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
  3908. }
  3909. return node;
  3910. },
  3911. cancelFrame(node) {
  3912. node != null && Animator.frames.remove(node);
  3913. },
  3914. clearTimeout(node) {
  3915. node != null && Animator.timeouts.remove(node);
  3916. },
  3917. cancelImmediate(node) {
  3918. node != null && Animator.immediates.remove(node);
  3919. },
  3920. _draw(now) {
  3921. // Run all the timeouts we can run, if they are not ready yet, add them
  3922. // to the end of the queue immediately! (bad timeouts!!! [sarcasm])
  3923. let nextTimeout = null;
  3924. const lastTimeout = Animator.timeouts.last();
  3925. while (nextTimeout = Animator.timeouts.shift()) {
  3926. // Run the timeout if its time, or push it to the end
  3927. if (now >= nextTimeout.time) {
  3928. nextTimeout.run();
  3929. } else {
  3930. Animator.timeouts.push(nextTimeout);
  3931. } // If we hit the last item, we should stop shifting out more items
  3932. if (nextTimeout === lastTimeout) break;
  3933. } // Run all of the animation frames
  3934. let nextFrame = null;
  3935. const lastFrame = Animator.frames.last();
  3936. while (nextFrame !== lastFrame && (nextFrame = Animator.frames.shift())) {
  3937. nextFrame.run(now);
  3938. }
  3939. let nextImmediate = null;
  3940. while (nextImmediate = Animator.immediates.shift()) {
  3941. nextImmediate();
  3942. } // If we have remaining timeouts or frames, draw until we don't anymore
  3943. Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first() ? globals.window.requestAnimationFrame(Animator._draw) : null;
  3944. }
  3945. };
  3946. const makeSchedule = function (runnerInfo) {
  3947. const start = runnerInfo.start;
  3948. const duration = runnerInfo.runner.duration();
  3949. const end = start + duration;
  3950. return {
  3951. start: start,
  3952. duration: duration,
  3953. end: end,
  3954. runner: runnerInfo.runner
  3955. };
  3956. };
  3957. const defaultSource = function () {
  3958. const w = globals.window;
  3959. return (w.performance || w.Date).now();
  3960. };
  3961. class Timeline extends EventTarget {
  3962. // Construct a new timeline on the given element
  3963. constructor(timeSource = defaultSource) {
  3964. super();
  3965. this._timeSource = timeSource; // Store the timing variables
  3966. this._startTime = 0;
  3967. this._speed = 1.0; // Determines how long a runner is hold in memory. Can be a dt or true/false
  3968. this._persist = 0; // Keep track of the running animations and their starting parameters
  3969. this._nextFrame = null;
  3970. this._paused = true;
  3971. this._runners = [];
  3972. this._runnerIds = [];
  3973. this._lastRunnerId = -1;
  3974. this._time = 0;
  3975. this._lastSourceTime = 0;
  3976. this._lastStepTime = 0; // Make sure that step is always called in class context
  3977. this._step = this._stepFn.bind(this, false);
  3978. this._stepImmediate = this._stepFn.bind(this, true);
  3979. }
  3980. active() {
  3981. return !!this._nextFrame;
  3982. }
  3983. finish() {
  3984. // Go to end and pause
  3985. this.time(this.getEndTimeOfTimeline() + 1);
  3986. return this.pause();
  3987. } // Calculates the end of the timeline
  3988. getEndTime() {
  3989. const lastRunnerInfo = this.getLastRunnerInfo();
  3990. const lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0;
  3991. const lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time;
  3992. return lastStartTime + lastDuration;
  3993. }
  3994. getEndTimeOfTimeline() {
  3995. const endTimes = this._runners.map(i => i.start + i.runner.duration());
  3996. return Math.max(0, ...endTimes);
  3997. }
  3998. getLastRunnerInfo() {
  3999. return this.getRunnerInfoById(this._lastRunnerId);
  4000. }
  4001. getRunnerInfoById(id) {
  4002. return this._runners[this._runnerIds.indexOf(id)] || null;
  4003. }
  4004. pause() {
  4005. this._paused = true;
  4006. return this._continue();
  4007. }
  4008. persist(dtOrForever) {
  4009. if (dtOrForever == null) return this._persist;
  4010. this._persist = dtOrForever;
  4011. return this;
  4012. }
  4013. play() {
  4014. // Now make sure we are not paused and continue the animation
  4015. this._paused = false;
  4016. return this.updateTime()._continue();
  4017. }
  4018. reverse(yes) {
  4019. const currentSpeed = this.speed();
  4020. if (yes == null) return this.speed(-currentSpeed);
  4021. const positive = Math.abs(currentSpeed);
  4022. return this.speed(yes ? -positive : positive);
  4023. } // schedules a runner on the timeline
  4024. schedule(runner, delay, when) {
  4025. if (runner == null) {
  4026. return this._runners.map(makeSchedule);
  4027. } // The start time for the next animation can either be given explicitly,
  4028. // derived from the current timeline time or it can be relative to the
  4029. // last start time to chain animations directly
  4030. let absoluteStartTime = 0;
  4031. const endTime = this.getEndTime();
  4032. delay = delay || 0; // Work out when to start the animation
  4033. if (when == null || when === 'last' || when === 'after') {
  4034. // Take the last time and increment
  4035. absoluteStartTime = endTime;
  4036. } else if (when === 'absolute' || when === 'start') {
  4037. absoluteStartTime = delay;
  4038. delay = 0;
  4039. } else if (when === 'now') {
  4040. absoluteStartTime = this._time;
  4041. } else if (when === 'relative') {
  4042. const runnerInfo = this.getRunnerInfoById(runner.id);
  4043. if (runnerInfo) {
  4044. absoluteStartTime = runnerInfo.start + delay;
  4045. delay = 0;
  4046. }
  4047. } else if (when === 'with-last') {
  4048. const lastRunnerInfo = this.getLastRunnerInfo();
  4049. const lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time;
  4050. absoluteStartTime = lastStartTime;
  4051. } else {
  4052. throw new Error('Invalid value for the "when" parameter');
  4053. } // Manage runner
  4054. runner.unschedule();
  4055. runner.timeline(this);
  4056. const persist = runner.persist();
  4057. const runnerInfo = {
  4058. persist: persist === null ? this._persist : persist,
  4059. start: absoluteStartTime + delay,
  4060. runner
  4061. };
  4062. this._lastRunnerId = runner.id;
  4063. this._runners.push(runnerInfo);
  4064. this._runners.sort((a, b) => a.start - b.start);
  4065. this._runnerIds = this._runners.map(info => info.runner.id);
  4066. this.updateTime()._continue();
  4067. return this;
  4068. }
  4069. seek(dt) {
  4070. return this.time(this._time + dt);
  4071. }
  4072. source(fn) {
  4073. if (fn == null) return this._timeSource;
  4074. this._timeSource = fn;
  4075. return this;
  4076. }
  4077. speed(speed) {
  4078. if (speed == null) return this._speed;
  4079. this._speed = speed;
  4080. return this;
  4081. }
  4082. stop() {
  4083. // Go to start and pause
  4084. this.time(0);
  4085. return this.pause();
  4086. }
  4087. time(time) {
  4088. if (time == null) return this._time;
  4089. this._time = time;
  4090. return this._continue(true);
  4091. } // Remove the runner from this timeline
  4092. unschedule(runner) {
  4093. const index = this._runnerIds.indexOf(runner.id);
  4094. if (index < 0) return this;
  4095. this._runners.splice(index, 1);
  4096. this._runnerIds.splice(index, 1);
  4097. runner.timeline(null);
  4098. return this;
  4099. } // Makes sure, that after pausing the time doesn't jump
  4100. updateTime() {
  4101. if (!this.active()) {
  4102. this._lastSourceTime = this._timeSource();
  4103. }
  4104. return this;
  4105. } // Checks if we are running and continues the animation
  4106. _continue(immediateStep = false) {
  4107. Animator.cancelFrame(this._nextFrame);
  4108. this._nextFrame = null;
  4109. if (immediateStep) return this._stepImmediate();
  4110. if (this._paused) return this;
  4111. this._nextFrame = Animator.frame(this._step);
  4112. return this;
  4113. }
  4114. _stepFn(immediateStep = false) {
  4115. // Get the time delta from the last time and update the time
  4116. const time = this._timeSource();
  4117. let dtSource = time - this._lastSourceTime;
  4118. if (immediateStep) dtSource = 0;
  4119. const dtTime = this._speed * dtSource + (this._time - this._lastStepTime);
  4120. this._lastSourceTime = time; // Only update the time if we use the timeSource.
  4121. // Otherwise use the current time
  4122. if (!immediateStep) {
  4123. // Update the time
  4124. this._time += dtTime;
  4125. this._time = this._time < 0 ? 0 : this._time;
  4126. }
  4127. this._lastStepTime = this._time;
  4128. this.fire('time', this._time); // This is for the case that the timeline was seeked so that the time
  4129. // is now before the startTime of the runner. That is why we need to set
  4130. // the runner to position 0
  4131. // FIXME:
  4132. // However, resetting in insertion order leads to bugs. Considering the case,
  4133. // where 2 runners change the same attribute but in different times,
  4134. // resetting both of them will lead to the case where the later defined
  4135. // runner always wins the reset even if the other runner started earlier
  4136. // and therefore should win the attribute battle
  4137. // this can be solved by resetting them backwards
  4138. for (let k = this._runners.length; k--;) {
  4139. // Get and run the current runner and ignore it if its inactive
  4140. const runnerInfo = this._runners[k];
  4141. const runner = runnerInfo.runner; // Make sure that we give the actual difference
  4142. // between runner start time and now
  4143. const dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet
  4144. // and try to reset it
  4145. if (dtToStart <= 0) {
  4146. runner.reset();
  4147. }
  4148. } // Run all of the runners directly
  4149. let runnersLeft = false;
  4150. for (let i = 0, len = this._runners.length; i < len; i++) {
  4151. // Get and run the current runner and ignore it if its inactive
  4152. const runnerInfo = this._runners[i];
  4153. const runner = runnerInfo.runner;
  4154. let dt = dtTime; // Make sure that we give the actual difference
  4155. // between runner start time and now
  4156. const dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet
  4157. if (dtToStart <= 0) {
  4158. runnersLeft = true;
  4159. continue;
  4160. } else if (dtToStart < dt) {
  4161. // Adjust dt to make sure that animation is on point
  4162. dt = dtToStart;
  4163. }
  4164. if (!runner.active()) continue; // If this runner is still going, signal that we need another animation
  4165. // frame, otherwise, remove the completed runner
  4166. const finished = runner.step(dt).done;
  4167. if (!finished) {
  4168. runnersLeft = true; // continue
  4169. } else if (runnerInfo.persist !== true) {
  4170. // runner is finished. And runner might get removed
  4171. const endTime = runner.duration() - runner.time() + this._time;
  4172. if (endTime + runnerInfo.persist < this._time) {
  4173. // Delete runner and correct index
  4174. runner.unschedule();
  4175. --i;
  4176. --len;
  4177. }
  4178. }
  4179. } // Basically: we continue when there are runners right from us in time
  4180. // when -->, and when runners are left from us when <--
  4181. if (runnersLeft && !(this._speed < 0 && this._time === 0) || this._runnerIds.length && this._speed < 0 && this._time > 0) {
  4182. this._continue();
  4183. } else {
  4184. this.pause();
  4185. this.fire('finished');
  4186. }
  4187. return this;
  4188. }
  4189. }
  4190. registerMethods({
  4191. Element: {
  4192. timeline: function (timeline) {
  4193. if (timeline == null) {
  4194. this._timeline = this._timeline || new Timeline();
  4195. return this._timeline;
  4196. } else {
  4197. this._timeline = timeline;
  4198. return this;
  4199. }
  4200. }
  4201. }
  4202. });
  4203. class Runner extends EventTarget {
  4204. constructor(options) {
  4205. super(); // Store a unique id on the runner, so that we can identify it later
  4206. this.id = Runner.id++; // Ensure a default value
  4207. options = options == null ? timeline.duration : options; // Ensure that we get a controller
  4208. options = typeof options === 'function' ? new Controller(options) : options; // Declare all of the variables
  4209. this._element = null;
  4210. this._timeline = null;
  4211. this.done = false;
  4212. this._queue = []; // Work out the stepper and the duration
  4213. this._duration = typeof options === 'number' && options;
  4214. this._isDeclarative = options instanceof Controller;
  4215. this._stepper = this._isDeclarative ? options : new Ease(); // We copy the current values from the timeline because they can change
  4216. this._history = {}; // Store the state of the runner
  4217. this.enabled = true;
  4218. this._time = 0;
  4219. this._lastTime = 0; // At creation, the runner is in reset state
  4220. this._reseted = true; // Save transforms applied to this runner
  4221. this.transforms = new Matrix();
  4222. this.transformId = 1; // Looping variables
  4223. this._haveReversed = false;
  4224. this._reverse = false;
  4225. this._loopsDone = 0;
  4226. this._swing = false;
  4227. this._wait = 0;
  4228. this._times = 1;
  4229. this._frameId = null; // Stores how long a runner is stored after being done
  4230. this._persist = this._isDeclarative ? true : null;
  4231. }
  4232. static sanitise(duration, delay, when) {
  4233. // Initialise the default parameters
  4234. let times = 1;
  4235. let swing = false;
  4236. let wait = 0;
  4237. duration = duration || timeline.duration;
  4238. delay = delay || timeline.delay;
  4239. when = when || 'last'; // If we have an object, unpack the values
  4240. if (typeof duration === 'object' && !(duration instanceof Stepper)) {
  4241. delay = duration.delay || delay;
  4242. when = duration.when || when;
  4243. swing = duration.swing || swing;
  4244. times = duration.times || times;
  4245. wait = duration.wait || wait;
  4246. duration = duration.duration || timeline.duration;
  4247. }
  4248. return {
  4249. duration: duration,
  4250. delay: delay,
  4251. swing: swing,
  4252. times: times,
  4253. wait: wait,
  4254. when: when
  4255. };
  4256. }
  4257. active(enabled) {
  4258. if (enabled == null) return this.enabled;
  4259. this.enabled = enabled;
  4260. return this;
  4261. }
  4262. /*
  4263. Private Methods
  4264. ===============
  4265. Methods that shouldn't be used externally
  4266. */
  4267. addTransform(transform, index) {
  4268. this.transforms.lmultiplyO(transform);
  4269. return this;
  4270. }
  4271. after(fn) {
  4272. return this.on('finished', fn);
  4273. }
  4274. animate(duration, delay, when) {
  4275. const o = Runner.sanitise(duration, delay, when);
  4276. const runner = new Runner(o.duration);
  4277. if (this._timeline) runner.timeline(this._timeline);
  4278. if (this._element) runner.element(this._element);
  4279. return runner.loop(o).schedule(o.delay, o.when);
  4280. }
  4281. clearTransform() {
  4282. this.transforms = new Matrix();
  4283. return this;
  4284. } // TODO: Keep track of all transformations so that deletion is faster
  4285. clearTransformsFromQueue() {
  4286. if (!this.done || !this._timeline || !this._timeline._runnerIds.includes(this.id)) {
  4287. this._queue = this._queue.filter(item => {
  4288. return !item.isTransform;
  4289. });
  4290. }
  4291. }
  4292. delay(delay) {
  4293. return this.animate(0, delay);
  4294. }
  4295. duration() {
  4296. return this._times * (this._wait + this._duration) - this._wait;
  4297. }
  4298. during(fn) {
  4299. return this.queue(null, fn);
  4300. }
  4301. ease(fn) {
  4302. this._stepper = new Ease(fn);
  4303. return this;
  4304. }
  4305. /*
  4306. Runner Definitions
  4307. ==================
  4308. These methods help us define the runtime behaviour of the Runner or they
  4309. help us make new runners from the current runner
  4310. */
  4311. element(element) {
  4312. if (element == null) return this._element;
  4313. this._element = element;
  4314. element._prepareRunner();
  4315. return this;
  4316. }
  4317. finish() {
  4318. return this.step(Infinity);
  4319. }
  4320. loop(times, swing, wait) {
  4321. // Deal with the user passing in an object
  4322. if (typeof times === 'object') {
  4323. swing = times.swing;
  4324. wait = times.wait;
  4325. times = times.times;
  4326. } // Sanitise the values and store them
  4327. this._times = times || Infinity;
  4328. this._swing = swing || false;
  4329. this._wait = wait || 0; // Allow true to be passed
  4330. if (this._times === true) {
  4331. this._times = Infinity;
  4332. }
  4333. return this;
  4334. }
  4335. loops(p) {
  4336. const loopDuration = this._duration + this._wait;
  4337. if (p == null) {
  4338. const loopsDone = Math.floor(this._time / loopDuration);
  4339. const relativeTime = this._time - loopsDone * loopDuration;
  4340. const position = relativeTime / this._duration;
  4341. return Math.min(loopsDone + position, this._times);
  4342. }
  4343. const whole = Math.floor(p);
  4344. const partial = p % 1;
  4345. const time = loopDuration * whole + this._duration * partial;
  4346. return this.time(time);
  4347. }
  4348. persist(dtOrForever) {
  4349. if (dtOrForever == null) return this._persist;
  4350. this._persist = dtOrForever;
  4351. return this;
  4352. }
  4353. position(p) {
  4354. // Get all of the variables we need
  4355. const x = this._time;
  4356. const d = this._duration;
  4357. const w = this._wait;
  4358. const t = this._times;
  4359. const s = this._swing;
  4360. const r = this._reverse;
  4361. let position;
  4362. if (p == null) {
  4363. /*
  4364. This function converts a time to a position in the range [0, 1]
  4365. The full explanation can be found in this desmos demonstration
  4366. https://www.desmos.com/calculator/u4fbavgche
  4367. The logic is slightly simplified here because we can use booleans
  4368. */
  4369. // Figure out the value without thinking about the start or end time
  4370. const f = function (x) {
  4371. const swinging = s * Math.floor(x % (2 * (w + d)) / (w + d));
  4372. const backwards = swinging && !r || !swinging && r;
  4373. const uncliped = Math.pow(-1, backwards) * (x % (w + d)) / d + backwards;
  4374. const clipped = Math.max(Math.min(uncliped, 1), 0);
  4375. return clipped;
  4376. }; // Figure out the value by incorporating the start time
  4377. const endTime = t * (w + d) - w;
  4378. position = x <= 0 ? Math.round(f(1e-5)) : x < endTime ? f(x) : Math.round(f(endTime - 1e-5));
  4379. return position;
  4380. } // Work out the loops done and add the position to the loops done
  4381. const loopsDone = Math.floor(this.loops());
  4382. const swingForward = s && loopsDone % 2 === 0;
  4383. const forwards = swingForward && !r || r && swingForward;
  4384. position = loopsDone + (forwards ? p : 1 - p);
  4385. return this.loops(position);
  4386. }
  4387. progress(p) {
  4388. if (p == null) {
  4389. return Math.min(1, this._time / this.duration());
  4390. }
  4391. return this.time(p * this.duration());
  4392. }
  4393. /*
  4394. Basic Functionality
  4395. ===================
  4396. These methods allow us to attach basic functions to the runner directly
  4397. */
  4398. queue(initFn, runFn, retargetFn, isTransform) {
  4399. this._queue.push({
  4400. initialiser: initFn || noop,
  4401. runner: runFn || noop,
  4402. retarget: retargetFn,
  4403. isTransform: isTransform,
  4404. initialised: false,
  4405. finished: false
  4406. });
  4407. const timeline = this.timeline();
  4408. timeline && this.timeline()._continue();
  4409. return this;
  4410. }
  4411. reset() {
  4412. if (this._reseted) return this;
  4413. this.time(0);
  4414. this._reseted = true;
  4415. return this;
  4416. }
  4417. reverse(reverse) {
  4418. this._reverse = reverse == null ? !this._reverse : reverse;
  4419. return this;
  4420. }
  4421. schedule(timeline, delay, when) {
  4422. // The user doesn't need to pass a timeline if we already have one
  4423. if (!(timeline instanceof Timeline)) {
  4424. when = delay;
  4425. delay = timeline;
  4426. timeline = this.timeline();
  4427. } // If there is no timeline, yell at the user...
  4428. if (!timeline) {
  4429. throw Error('Runner cannot be scheduled without timeline');
  4430. } // Schedule the runner on the timeline provided
  4431. timeline.schedule(this, delay, when);
  4432. return this;
  4433. }
  4434. step(dt) {
  4435. // If we are inactive, this stepper just gets skipped
  4436. if (!this.enabled) return this; // Update the time and get the new position
  4437. dt = dt == null ? 16 : dt;
  4438. this._time += dt;
  4439. const position = this.position(); // Figure out if we need to run the stepper in this frame
  4440. const running = this._lastPosition !== position && this._time >= 0;
  4441. this._lastPosition = position; // Figure out if we just started
  4442. const duration = this.duration();
  4443. const justStarted = this._lastTime <= 0 && this._time > 0;
  4444. const justFinished = this._lastTime < duration && this._time >= duration;
  4445. this._lastTime = this._time;
  4446. if (justStarted) {
  4447. this.fire('start', this);
  4448. } // Work out if the runner is finished set the done flag here so animations
  4449. // know, that they are running in the last step (this is good for
  4450. // transformations which can be merged)
  4451. const declarative = this._isDeclarative;
  4452. this.done = !declarative && !justFinished && this._time >= duration; // Runner is running. So its not in reset state anymore
  4453. this._reseted = false;
  4454. let converged = false; // Call initialise and the run function
  4455. if (running || declarative) {
  4456. this._initialise(running); // clear the transforms on this runner so they dont get added again and again
  4457. this.transforms = new Matrix();
  4458. converged = this._run(declarative ? dt : position);
  4459. this.fire('step', this);
  4460. } // correct the done flag here
  4461. // declarative animations itself know when they converged
  4462. this.done = this.done || converged && declarative;
  4463. if (justFinished) {
  4464. this.fire('finished', this);
  4465. }
  4466. return this;
  4467. }
  4468. /*
  4469. Runner animation methods
  4470. ========================
  4471. Control how the animation plays
  4472. */
  4473. time(time) {
  4474. if (time == null) {
  4475. return this._time;
  4476. }
  4477. const dt = time - this._time;
  4478. this.step(dt);
  4479. return this;
  4480. }
  4481. timeline(timeline) {
  4482. // check explicitly for undefined so we can set the timeline to null
  4483. if (typeof timeline === 'undefined') return this._timeline;
  4484. this._timeline = timeline;
  4485. return this;
  4486. }
  4487. unschedule() {
  4488. const timeline = this.timeline();
  4489. timeline && timeline.unschedule(this);
  4490. return this;
  4491. } // Run each initialise function in the runner if required
  4492. _initialise(running) {
  4493. // If we aren't running, we shouldn't initialise when not declarative
  4494. if (!running && !this._isDeclarative) return; // Loop through all of the initialisers
  4495. for (let i = 0, len = this._queue.length; i < len; ++i) {
  4496. // Get the current initialiser
  4497. const current = this._queue[i]; // Determine whether we need to initialise
  4498. const needsIt = this._isDeclarative || !current.initialised && running;
  4499. running = !current.finished; // Call the initialiser if we need to
  4500. if (needsIt && running) {
  4501. current.initialiser.call(this);
  4502. current.initialised = true;
  4503. }
  4504. }
  4505. } // Save a morpher to the morpher list so that we can retarget it later
  4506. _rememberMorpher(method, morpher) {
  4507. this._history[method] = {
  4508. morpher: morpher,
  4509. caller: this._queue[this._queue.length - 1]
  4510. }; // We have to resume the timeline in case a controller
  4511. // is already done without being ever run
  4512. // This can happen when e.g. this is done:
  4513. // anim = el.animate(new SVG.Spring)
  4514. // and later
  4515. // anim.move(...)
  4516. if (this._isDeclarative) {
  4517. const timeline = this.timeline();
  4518. timeline && timeline.play();
  4519. }
  4520. } // Try to set the target for a morpher if the morpher exists, otherwise
  4521. // Run each run function for the position or dt given
  4522. _run(positionOrDt) {
  4523. // Run all of the _queue directly
  4524. let allfinished = true;
  4525. for (let i = 0, len = this._queue.length; i < len; ++i) {
  4526. // Get the current function to run
  4527. const current = this._queue[i]; // Run the function if its not finished, we keep track of the finished
  4528. // flag for the sake of declarative _queue
  4529. const converged = current.runner.call(this, positionOrDt);
  4530. current.finished = current.finished || converged === true;
  4531. allfinished = allfinished && current.finished;
  4532. } // We report when all of the constructors are finished
  4533. return allfinished;
  4534. } // do nothing and return false
  4535. _tryRetarget(method, target, extra) {
  4536. if (this._history[method]) {
  4537. // if the last method wasn't even initialised, throw it away
  4538. if (!this._history[method].caller.initialised) {
  4539. const index = this._queue.indexOf(this._history[method].caller);
  4540. this._queue.splice(index, 1);
  4541. return false;
  4542. } // for the case of transformations, we use the special retarget function
  4543. // which has access to the outer scope
  4544. if (this._history[method].caller.retarget) {
  4545. this._history[method].caller.retarget.call(this, target, extra); // for everything else a simple morpher change is sufficient
  4546. } else {
  4547. this._history[method].morpher.to(target);
  4548. }
  4549. this._history[method].caller.finished = false;
  4550. const timeline = this.timeline();
  4551. timeline && timeline.play();
  4552. return true;
  4553. }
  4554. return false;
  4555. }
  4556. }
  4557. Runner.id = 0;
  4558. class FakeRunner {
  4559. constructor(transforms = new Matrix(), id = -1, done = true) {
  4560. this.transforms = transforms;
  4561. this.id = id;
  4562. this.done = done;
  4563. }
  4564. clearTransformsFromQueue() {}
  4565. }
  4566. extend([Runner, FakeRunner], {
  4567. mergeWith(runner) {
  4568. return new FakeRunner(runner.transforms.lmultiply(this.transforms), runner.id);
  4569. }
  4570. }); // FakeRunner.emptyRunner = new FakeRunner()
  4571. const lmultiply = (last, curr) => last.lmultiplyO(curr);
  4572. const getRunnerTransform = runner => runner.transforms;
  4573. function mergeTransforms() {
  4574. // Find the matrix to apply to the element and apply it
  4575. const runners = this._transformationRunners.runners;
  4576. const netTransform = runners.map(getRunnerTransform).reduce(lmultiply, new Matrix());
  4577. this.transform(netTransform);
  4578. this._transformationRunners.merge();
  4579. if (this._transformationRunners.length() === 1) {
  4580. this._frameId = null;
  4581. }
  4582. }
  4583. class RunnerArray {
  4584. constructor() {
  4585. this.runners = [];
  4586. this.ids = [];
  4587. }
  4588. add(runner) {
  4589. if (this.runners.includes(runner)) return;
  4590. const id = runner.id + 1;
  4591. this.runners.push(runner);
  4592. this.ids.push(id);
  4593. return this;
  4594. }
  4595. clearBefore(id) {
  4596. const deleteCnt = this.ids.indexOf(id + 1) || 1;
  4597. this.ids.splice(0, deleteCnt, 0);
  4598. this.runners.splice(0, deleteCnt, new FakeRunner()).forEach(r => r.clearTransformsFromQueue());
  4599. return this;
  4600. }
  4601. edit(id, newRunner) {
  4602. const index = this.ids.indexOf(id + 1);
  4603. this.ids.splice(index, 1, id + 1);
  4604. this.runners.splice(index, 1, newRunner);
  4605. return this;
  4606. }
  4607. getByID(id) {
  4608. return this.runners[this.ids.indexOf(id + 1)];
  4609. }
  4610. length() {
  4611. return this.ids.length;
  4612. }
  4613. merge() {
  4614. let lastRunner = null;
  4615. for (let i = 0; i < this.runners.length; ++i) {
  4616. const runner = this.runners[i];
  4617. const condition = lastRunner && runner.done && lastRunner.done // don't merge runner when persisted on timeline
  4618. && (!runner._timeline || !runner._timeline._runnerIds.includes(runner.id)) && (!lastRunner._timeline || !lastRunner._timeline._runnerIds.includes(lastRunner.id));
  4619. if (condition) {
  4620. // the +1 happens in the function
  4621. this.remove(runner.id);
  4622. const newRunner = runner.mergeWith(lastRunner);
  4623. this.edit(lastRunner.id, newRunner);
  4624. lastRunner = newRunner;
  4625. --i;
  4626. } else {
  4627. lastRunner = runner;
  4628. }
  4629. }
  4630. return this;
  4631. }
  4632. remove(id) {
  4633. const index = this.ids.indexOf(id + 1);
  4634. this.ids.splice(index, 1);
  4635. this.runners.splice(index, 1);
  4636. return this;
  4637. }
  4638. }
  4639. registerMethods({
  4640. Element: {
  4641. animate(duration, delay, when) {
  4642. const o = Runner.sanitise(duration, delay, when);
  4643. const timeline = this.timeline();
  4644. return new Runner(o.duration).loop(o).element(this).timeline(timeline.play()).schedule(o.delay, o.when);
  4645. },
  4646. delay(by, when) {
  4647. return this.animate(0, by, when);
  4648. },
  4649. // this function searches for all runners on the element and deletes the ones
  4650. // which run before the current one. This is because absolute transformations
  4651. // overwrite anything anyway so there is no need to waste time computing
  4652. // other runners
  4653. _clearTransformRunnersBefore(currentRunner) {
  4654. this._transformationRunners.clearBefore(currentRunner.id);
  4655. },
  4656. _currentTransform(current) {
  4657. return this._transformationRunners.runners // we need the equal sign here to make sure, that also transformations
  4658. // on the same runner which execute before the current transformation are
  4659. // taken into account
  4660. .filter(runner => runner.id <= current.id).map(getRunnerTransform).reduce(lmultiply, new Matrix());
  4661. },
  4662. _addRunner(runner) {
  4663. this._transformationRunners.add(runner); // Make sure that the runner merge is executed at the very end of
  4664. // all Animator functions. That is why we use immediate here to execute
  4665. // the merge right after all frames are run
  4666. Animator.cancelImmediate(this._frameId);
  4667. this._frameId = Animator.immediate(mergeTransforms.bind(this));
  4668. },
  4669. _prepareRunner() {
  4670. if (this._frameId == null) {
  4671. this._transformationRunners = new RunnerArray().add(new FakeRunner(new Matrix(this)));
  4672. }
  4673. }
  4674. }
  4675. }); // Will output the elements from array A that are not in the array B
  4676. const difference = (a, b) => a.filter(x => !b.includes(x));
  4677. extend(Runner, {
  4678. attr(a, v) {
  4679. return this.styleAttr('attr', a, v);
  4680. },
  4681. // Add animatable styles
  4682. css(s, v) {
  4683. return this.styleAttr('css', s, v);
  4684. },
  4685. styleAttr(type, nameOrAttrs, val) {
  4686. if (typeof nameOrAttrs === 'string') {
  4687. return this.styleAttr(type, {
  4688. [nameOrAttrs]: val
  4689. });
  4690. }
  4691. let attrs = nameOrAttrs;
  4692. if (this._tryRetarget(type, attrs)) return this;
  4693. let morpher = new Morphable(this._stepper).to(attrs);
  4694. let keys = Object.keys(attrs);
  4695. this.queue(function () {
  4696. morpher = morpher.from(this.element()[type](keys));
  4697. }, function (pos) {
  4698. this.element()[type](morpher.at(pos).valueOf());
  4699. return morpher.done();
  4700. }, function (newToAttrs) {
  4701. // Check if any new keys were added
  4702. const newKeys = Object.keys(newToAttrs);
  4703. const differences = difference(newKeys, keys); // If their are new keys, initialize them and add them to morpher
  4704. if (differences.length) {
  4705. // Get the values
  4706. const addedFromAttrs = this.element()[type](differences); // Get the already initialized values
  4707. const oldFromAttrs = new ObjectBag(morpher.from()).valueOf(); // Merge old and new
  4708. Object.assign(oldFromAttrs, addedFromAttrs);
  4709. morpher.from(oldFromAttrs);
  4710. } // Get the object from the morpher
  4711. const oldToAttrs = new ObjectBag(morpher.to()).valueOf(); // Merge in new attributes
  4712. Object.assign(oldToAttrs, newToAttrs); // Change morpher target
  4713. morpher.to(oldToAttrs); // Make sure that we save the work we did so we don't need it to do again
  4714. keys = newKeys;
  4715. attrs = newToAttrs;
  4716. });
  4717. this._rememberMorpher(type, morpher);
  4718. return this;
  4719. },
  4720. zoom(level, point) {
  4721. if (this._tryRetarget('zoom', level, point)) return this;
  4722. let morpher = new Morphable(this._stepper).to(new SVGNumber(level));
  4723. this.queue(function () {
  4724. morpher = morpher.from(this.element().zoom());
  4725. }, function (pos) {
  4726. this.element().zoom(morpher.at(pos), point);
  4727. return morpher.done();
  4728. }, function (newLevel, newPoint) {
  4729. point = newPoint;
  4730. morpher.to(newLevel);
  4731. });
  4732. this._rememberMorpher('zoom', morpher);
  4733. return this;
  4734. },
  4735. /**
  4736. ** absolute transformations
  4737. **/
  4738. //
  4739. // M v -----|-----(D M v = F v)------|-----> T v
  4740. //
  4741. // 1. define the final state (T) and decompose it (once)
  4742. // t = [tx, ty, the, lam, sy, sx]
  4743. // 2. on every frame: pull the current state of all previous transforms
  4744. // (M - m can change)
  4745. // and then write this as m = [tx0, ty0, the0, lam0, sy0, sx0]
  4746. // 3. Find the interpolated matrix F(pos) = m + pos * (t - m)
  4747. // - Note F(0) = M
  4748. // - Note F(1) = T
  4749. // 4. Now you get the delta matrix as a result: D = F * inv(M)
  4750. transform(transforms, relative, affine) {
  4751. // If we have a declarative function, we should retarget it if possible
  4752. relative = transforms.relative || relative;
  4753. if (this._isDeclarative && !relative && this._tryRetarget('transform', transforms)) {
  4754. return this;
  4755. } // Parse the parameters
  4756. const isMatrix = Matrix.isMatrixLike(transforms);
  4757. affine = transforms.affine != null ? transforms.affine : affine != null ? affine : !isMatrix; // Create a morpher and set its type
  4758. const morpher = new Morphable(this._stepper).type(affine ? TransformBag : Matrix);
  4759. let origin;
  4760. let element;
  4761. let current;
  4762. let currentAngle;
  4763. let startTransform;
  4764. function setup() {
  4765. // make sure element and origin is defined
  4766. element = element || this.element();
  4767. origin = origin || getOrigin(transforms, element);
  4768. startTransform = new Matrix(relative ? undefined : element); // add the runner to the element so it can merge transformations
  4769. element._addRunner(this); // Deactivate all transforms that have run so far if we are absolute
  4770. if (!relative) {
  4771. element._clearTransformRunnersBefore(this);
  4772. }
  4773. }
  4774. function run(pos) {
  4775. // clear all other transforms before this in case something is saved
  4776. // on this runner. We are absolute. We dont need these!
  4777. if (!relative) this.clearTransform();
  4778. const {
  4779. x,
  4780. y
  4781. } = new Point(origin).transform(element._currentTransform(this));
  4782. let target = new Matrix({ ...transforms,
  4783. origin: [x, y]
  4784. });
  4785. let start = this._isDeclarative && current ? current : startTransform;
  4786. if (affine) {
  4787. target = target.decompose(x, y);
  4788. start = start.decompose(x, y); // Get the current and target angle as it was set
  4789. const rTarget = target.rotate;
  4790. const rCurrent = start.rotate; // Figure out the shortest path to rotate directly
  4791. const possibilities = [rTarget - 360, rTarget, rTarget + 360];
  4792. const distances = possibilities.map(a => Math.abs(a - rCurrent));
  4793. const shortest = Math.min(...distances);
  4794. const index = distances.indexOf(shortest);
  4795. target.rotate = possibilities[index];
  4796. }
  4797. if (relative) {
  4798. // we have to be careful here not to overwrite the rotation
  4799. // with the rotate method of Matrix
  4800. if (!isMatrix) {
  4801. target.rotate = transforms.rotate || 0;
  4802. }
  4803. if (this._isDeclarative && currentAngle) {
  4804. start.rotate = currentAngle;
  4805. }
  4806. }
  4807. morpher.from(start);
  4808. morpher.to(target);
  4809. const affineParameters = morpher.at(pos);
  4810. currentAngle = affineParameters.rotate;
  4811. current = new Matrix(affineParameters);
  4812. this.addTransform(current);
  4813. element._addRunner(this);
  4814. return morpher.done();
  4815. }
  4816. function retarget(newTransforms) {
  4817. // only get a new origin if it changed since the last call
  4818. if ((newTransforms.origin || 'center').toString() !== (transforms.origin || 'center').toString()) {
  4819. origin = getOrigin(newTransforms, element);
  4820. } // overwrite the old transformations with the new ones
  4821. transforms = { ...newTransforms,
  4822. origin
  4823. };
  4824. }
  4825. this.queue(setup, run, retarget, true);
  4826. this._isDeclarative && this._rememberMorpher('transform', morpher);
  4827. return this;
  4828. },
  4829. // Animatable x-axis
  4830. x(x, relative) {
  4831. return this._queueNumber('x', x);
  4832. },
  4833. // Animatable y-axis
  4834. y(y) {
  4835. return this._queueNumber('y', y);
  4836. },
  4837. dx(x = 0) {
  4838. return this._queueNumberDelta('x', x);
  4839. },
  4840. dy(y = 0) {
  4841. return this._queueNumberDelta('y', y);
  4842. },
  4843. dmove(x, y) {
  4844. return this.dx(x).dy(y);
  4845. },
  4846. _queueNumberDelta(method, to) {
  4847. to = new SVGNumber(to); // Try to change the target if we have this method already registered
  4848. if (this._tryRetarget(method, to)) return this; // Make a morpher and queue the animation
  4849. const morpher = new Morphable(this._stepper).to(to);
  4850. let from = null;
  4851. this.queue(function () {
  4852. from = this.element()[method]();
  4853. morpher.from(from);
  4854. morpher.to(from + to);
  4855. }, function (pos) {
  4856. this.element()[method](morpher.at(pos));
  4857. return morpher.done();
  4858. }, function (newTo) {
  4859. morpher.to(from + new SVGNumber(newTo));
  4860. }); // Register the morpher so that if it is changed again, we can retarget it
  4861. this._rememberMorpher(method, morpher);
  4862. return this;
  4863. },
  4864. _queueObject(method, to) {
  4865. // Try to change the target if we have this method already registered
  4866. if (this._tryRetarget(method, to)) return this; // Make a morpher and queue the animation
  4867. const morpher = new Morphable(this._stepper).to(to);
  4868. this.queue(function () {
  4869. morpher.from(this.element()[method]());
  4870. }, function (pos) {
  4871. this.element()[method](morpher.at(pos));
  4872. return morpher.done();
  4873. }); // Register the morpher so that if it is changed again, we can retarget it
  4874. this._rememberMorpher(method, morpher);
  4875. return this;
  4876. },
  4877. _queueNumber(method, value) {
  4878. return this._queueObject(method, new SVGNumber(value));
  4879. },
  4880. // Animatable center x-axis
  4881. cx(x) {
  4882. return this._queueNumber('cx', x);
  4883. },
  4884. // Animatable center y-axis
  4885. cy(y) {
  4886. return this._queueNumber('cy', y);
  4887. },
  4888. // Add animatable move
  4889. move(x, y) {
  4890. return this.x(x).y(y);
  4891. },
  4892. // Add animatable center
  4893. center(x, y) {
  4894. return this.cx(x).cy(y);
  4895. },
  4896. // Add animatable size
  4897. size(width, height) {
  4898. // animate bbox based size for all other elements
  4899. let box;
  4900. if (!width || !height) {
  4901. box = this._element.bbox();
  4902. }
  4903. if (!width) {
  4904. width = box.width / box.height * height;
  4905. }
  4906. if (!height) {
  4907. height = box.height / box.width * width;
  4908. }
  4909. return this.width(width).height(height);
  4910. },
  4911. // Add animatable width
  4912. width(width) {
  4913. return this._queueNumber('width', width);
  4914. },
  4915. // Add animatable height
  4916. height(height) {
  4917. return this._queueNumber('height', height);
  4918. },
  4919. // Add animatable plot
  4920. plot(a, b, c, d) {
  4921. // Lines can be plotted with 4 arguments
  4922. if (arguments.length === 4) {
  4923. return this.plot([a, b, c, d]);
  4924. }
  4925. if (this._tryRetarget('plot', a)) return this;
  4926. const morpher = new Morphable(this._stepper).type(this._element.MorphArray).to(a);
  4927. this.queue(function () {
  4928. morpher.from(this._element.array());
  4929. }, function (pos) {
  4930. this._element.plot(morpher.at(pos));
  4931. return morpher.done();
  4932. });
  4933. this._rememberMorpher('plot', morpher);
  4934. return this;
  4935. },
  4936. // Add leading method
  4937. leading(value) {
  4938. return this._queueNumber('leading', value);
  4939. },
  4940. // Add animatable viewbox
  4941. viewbox(x, y, width, height) {
  4942. return this._queueObject('viewbox', new Box(x, y, width, height));
  4943. },
  4944. update(o) {
  4945. if (typeof o !== 'object') {
  4946. return this.update({
  4947. offset: arguments[0],
  4948. color: arguments[1],
  4949. opacity: arguments[2]
  4950. });
  4951. }
  4952. if (o.opacity != null) this.attr('stop-opacity', o.opacity);
  4953. if (o.color != null) this.attr('stop-color', o.color);
  4954. if (o.offset != null) this.attr('offset', o.offset);
  4955. return this;
  4956. }
  4957. });
  4958. extend(Runner, {
  4959. rx,
  4960. ry,
  4961. from,
  4962. to
  4963. });
  4964. register(Runner, 'Runner');
  4965. class Svg extends Container {
  4966. constructor(node, attrs = node) {
  4967. super(nodeOrNew('svg', node), attrs);
  4968. this.namespace();
  4969. } // Creates and returns defs element
  4970. defs() {
  4971. if (!this.isRoot()) return this.root().defs();
  4972. return adopt(this.node.querySelector('defs')) || this.put(new Defs());
  4973. }
  4974. isRoot() {
  4975. return !this.node.parentNode || !(this.node.parentNode instanceof globals.window.SVGElement) && this.node.parentNode.nodeName !== '#document-fragment';
  4976. } // Add namespaces
  4977. namespace() {
  4978. if (!this.isRoot()) return this.root().namespace();
  4979. return this.attr({
  4980. xmlns: svg,
  4981. version: '1.1'
  4982. }).attr('xmlns:xlink', xlink, xmlns).attr('xmlns:svgjs', svgjs, xmlns);
  4983. }
  4984. removeNamespace() {
  4985. return this.attr({
  4986. xmlns: null,
  4987. version: null
  4988. }).attr('xmlns:xlink', null, xmlns).attr('xmlns:svgjs', null, xmlns);
  4989. } // Check if this is a root svg
  4990. // If not, call root() from this element
  4991. root() {
  4992. if (this.isRoot()) return this;
  4993. return super.root();
  4994. }
  4995. }
  4996. registerMethods({
  4997. Container: {
  4998. // Create nested svg document
  4999. nested: wrapWithAttrCheck(function () {
  5000. return this.put(new Svg());
  5001. })
  5002. }
  5003. });
  5004. register(Svg, 'Svg', true);
  5005. class Symbol extends Container {
  5006. // Initialize node
  5007. constructor(node, attrs = node) {
  5008. super(nodeOrNew('symbol', node), attrs);
  5009. }
  5010. }
  5011. registerMethods({
  5012. Container: {
  5013. symbol: wrapWithAttrCheck(function () {
  5014. return this.put(new Symbol());
  5015. })
  5016. }
  5017. });
  5018. register(Symbol, 'Symbol');
  5019. function plain(text) {
  5020. // clear if build mode is disabled
  5021. if (this._build === false) {
  5022. this.clear();
  5023. } // create text node
  5024. this.node.appendChild(globals.document.createTextNode(text));
  5025. return this;
  5026. } // Get length of text element
  5027. function length() {
  5028. return this.node.getComputedTextLength();
  5029. } // Move over x-axis
  5030. // Text is moved by its bounding box
  5031. // text-anchor does NOT matter
  5032. function x$1(x, box = this.bbox()) {
  5033. if (x == null) {
  5034. return box.x;
  5035. }
  5036. return this.attr('x', this.attr('x') + x - box.x);
  5037. } // Move over y-axis
  5038. function y$1(y, box = this.bbox()) {
  5039. if (y == null) {
  5040. return box.y;
  5041. }
  5042. return this.attr('y', this.attr('y') + y - box.y);
  5043. }
  5044. function move$1(x, y, box = this.bbox()) {
  5045. return this.x(x, box).y(y, box);
  5046. } // Move center over x-axis
  5047. function cx(x, box = this.bbox()) {
  5048. if (x == null) {
  5049. return box.cx;
  5050. }
  5051. return this.attr('x', this.attr('x') + x - box.cx);
  5052. } // Move center over y-axis
  5053. function cy(y, box = this.bbox()) {
  5054. if (y == null) {
  5055. return box.cy;
  5056. }
  5057. return this.attr('y', this.attr('y') + y - box.cy);
  5058. }
  5059. function center(x, y, box = this.bbox()) {
  5060. return this.cx(x, box).cy(y, box);
  5061. }
  5062. function ax(x) {
  5063. return this.attr('x', x);
  5064. }
  5065. function ay(y) {
  5066. return this.attr('y', y);
  5067. }
  5068. function amove(x, y) {
  5069. return this.ax(x).ay(y);
  5070. } // Enable / disable build mode
  5071. function build(build) {
  5072. this._build = !!build;
  5073. return this;
  5074. }
  5075. var textable = {
  5076. __proto__: null,
  5077. plain: plain,
  5078. length: length,
  5079. x: x$1,
  5080. y: y$1,
  5081. move: move$1,
  5082. cx: cx,
  5083. cy: cy,
  5084. center: center,
  5085. ax: ax,
  5086. ay: ay,
  5087. amove: amove,
  5088. build: build
  5089. };
  5090. class Text extends Shape {
  5091. // Initialize node
  5092. constructor(node, attrs = node) {
  5093. super(nodeOrNew('text', node), attrs);
  5094. this.dom.leading = new SVGNumber(1.3); // store leading value for rebuilding
  5095. this._rebuild = true; // enable automatic updating of dy values
  5096. this._build = false; // disable build mode for adding multiple lines
  5097. } // Set / get leading
  5098. leading(value) {
  5099. // act as getter
  5100. if (value == null) {
  5101. return this.dom.leading;
  5102. } // act as setter
  5103. this.dom.leading = new SVGNumber(value);
  5104. return this.rebuild();
  5105. } // Rebuild appearance type
  5106. rebuild(rebuild) {
  5107. // store new rebuild flag if given
  5108. if (typeof rebuild === 'boolean') {
  5109. this._rebuild = rebuild;
  5110. } // define position of all lines
  5111. if (this._rebuild) {
  5112. const self = this;
  5113. let blankLineOffset = 0;
  5114. const leading = this.dom.leading;
  5115. this.each(function (i) {
  5116. const fontSize = globals.window.getComputedStyle(this.node).getPropertyValue('font-size');
  5117. const dy = leading * new SVGNumber(fontSize);
  5118. if (this.dom.newLined) {
  5119. this.attr('x', self.attr('x'));
  5120. if (this.text() === '\n') {
  5121. blankLineOffset += dy;
  5122. } else {
  5123. this.attr('dy', i ? dy + blankLineOffset : 0);
  5124. blankLineOffset = 0;
  5125. }
  5126. }
  5127. });
  5128. this.fire('rebuild');
  5129. }
  5130. return this;
  5131. } // overwrite method from parent to set data properly
  5132. setData(o) {
  5133. this.dom = o;
  5134. this.dom.leading = new SVGNumber(o.leading || 1.3);
  5135. return this;
  5136. } // Set the text content
  5137. text(text) {
  5138. // act as getter
  5139. if (text === undefined) {
  5140. const children = this.node.childNodes;
  5141. let firstLine = 0;
  5142. text = '';
  5143. for (let i = 0, len = children.length; i < len; ++i) {
  5144. // skip textPaths - they are no lines
  5145. if (children[i].nodeName === 'textPath') {
  5146. if (i === 0) firstLine = 1;
  5147. continue;
  5148. } // add newline if its not the first child and newLined is set to true
  5149. if (i !== firstLine && children[i].nodeType !== 3 && adopt(children[i]).dom.newLined === true) {
  5150. text += '\n';
  5151. } // add content of this node
  5152. text += children[i].textContent;
  5153. }
  5154. return text;
  5155. } // remove existing content
  5156. this.clear().build(true);
  5157. if (typeof text === 'function') {
  5158. // call block
  5159. text.call(this, this);
  5160. } else {
  5161. // store text and make sure text is not blank
  5162. text = (text + '').split('\n'); // build new lines
  5163. for (let j = 0, jl = text.length; j < jl; j++) {
  5164. this.newLine(text[j]);
  5165. }
  5166. } // disable build mode and rebuild lines
  5167. return this.build(false).rebuild();
  5168. }
  5169. }
  5170. extend(Text, textable);
  5171. registerMethods({
  5172. Container: {
  5173. // Create text element
  5174. text: wrapWithAttrCheck(function (text = '') {
  5175. return this.put(new Text()).text(text);
  5176. }),
  5177. // Create plain text element
  5178. plain: wrapWithAttrCheck(function (text = '') {
  5179. return this.put(new Text()).plain(text);
  5180. })
  5181. }
  5182. });
  5183. register(Text, 'Text');
  5184. class Tspan extends Shape {
  5185. // Initialize node
  5186. constructor(node, attrs = node) {
  5187. super(nodeOrNew('tspan', node), attrs);
  5188. this._build = false; // disable build mode for adding multiple lines
  5189. } // Shortcut dx
  5190. dx(dx) {
  5191. return this.attr('dx', dx);
  5192. } // Shortcut dy
  5193. dy(dy) {
  5194. return this.attr('dy', dy);
  5195. } // Create new line
  5196. newLine() {
  5197. // mark new line
  5198. this.dom.newLined = true; // fetch parent
  5199. const text = this.parent(); // early return in case we are not in a text element
  5200. if (!(text instanceof Text)) {
  5201. return this;
  5202. }
  5203. const i = text.index(this);
  5204. const fontSize = globals.window.getComputedStyle(this.node).getPropertyValue('font-size');
  5205. const dy = text.dom.leading * new SVGNumber(fontSize); // apply new position
  5206. return this.dy(i ? dy : 0).attr('x', text.x());
  5207. } // Set text content
  5208. text(text) {
  5209. if (text == null) return this.node.textContent + (this.dom.newLined ? '\n' : '');
  5210. if (typeof text === 'function') {
  5211. this.clear().build(true);
  5212. text.call(this, this);
  5213. this.build(false);
  5214. } else {
  5215. this.plain(text);
  5216. }
  5217. return this;
  5218. }
  5219. }
  5220. extend(Tspan, textable);
  5221. registerMethods({
  5222. Tspan: {
  5223. tspan: wrapWithAttrCheck(function (text = '') {
  5224. const tspan = new Tspan(); // clear if build mode is disabled
  5225. if (!this._build) {
  5226. this.clear();
  5227. } // add new tspan
  5228. return this.put(tspan).text(text);
  5229. })
  5230. },
  5231. Text: {
  5232. newLine: function (text = '') {
  5233. return this.tspan(text).newLine();
  5234. }
  5235. }
  5236. });
  5237. register(Tspan, 'Tspan');
  5238. class Circle extends Shape {
  5239. constructor(node, attrs = node) {
  5240. super(nodeOrNew('circle', node), attrs);
  5241. }
  5242. radius(r) {
  5243. return this.attr('r', r);
  5244. } // Radius x value
  5245. rx(rx) {
  5246. return this.attr('r', rx);
  5247. } // Alias radius x value
  5248. ry(ry) {
  5249. return this.rx(ry);
  5250. }
  5251. size(size) {
  5252. return this.radius(new SVGNumber(size).divide(2));
  5253. }
  5254. }
  5255. extend(Circle, {
  5256. x: x$3,
  5257. y: y$3,
  5258. cx: cx$1,
  5259. cy: cy$1,
  5260. width: width$2,
  5261. height: height$2
  5262. });
  5263. registerMethods({
  5264. Container: {
  5265. // Create circle element
  5266. circle: wrapWithAttrCheck(function (size = 0) {
  5267. return this.put(new Circle()).size(size).move(0, 0);
  5268. })
  5269. }
  5270. });
  5271. register(Circle, 'Circle');
  5272. class ClipPath extends Container {
  5273. constructor(node, attrs = node) {
  5274. super(nodeOrNew('clipPath', node), attrs);
  5275. } // Unclip all clipped elements and remove itself
  5276. remove() {
  5277. // unclip all targets
  5278. this.targets().forEach(function (el) {
  5279. el.unclip();
  5280. }); // remove clipPath from parent
  5281. return super.remove();
  5282. }
  5283. targets() {
  5284. return baseFind('svg [clip-path*=' + this.id() + ']');
  5285. }
  5286. }
  5287. registerMethods({
  5288. Container: {
  5289. // Create clipping element
  5290. clip: wrapWithAttrCheck(function () {
  5291. return this.defs().put(new ClipPath());
  5292. })
  5293. },
  5294. Element: {
  5295. // Distribute clipPath to svg element
  5296. clipper() {
  5297. return this.reference('clip-path');
  5298. },
  5299. clipWith(element) {
  5300. // use given clip or create a new one
  5301. const clipper = element instanceof ClipPath ? element : this.parent().clip().add(element); // apply mask
  5302. return this.attr('clip-path', 'url(#' + clipper.id() + ')');
  5303. },
  5304. // Unclip element
  5305. unclip() {
  5306. return this.attr('clip-path', null);
  5307. }
  5308. }
  5309. });
  5310. register(ClipPath, 'ClipPath');
  5311. class ForeignObject extends Element {
  5312. constructor(node, attrs = node) {
  5313. super(nodeOrNew('foreignObject', node), attrs);
  5314. }
  5315. }
  5316. registerMethods({
  5317. Container: {
  5318. foreignObject: wrapWithAttrCheck(function (width, height) {
  5319. return this.put(new ForeignObject()).size(width, height);
  5320. })
  5321. }
  5322. });
  5323. register(ForeignObject, 'ForeignObject');
  5324. function dmove(dx, dy) {
  5325. this.children().forEach((child, i) => {
  5326. let bbox; // We have to wrap this for elements that dont have a bbox
  5327. // e.g. title and other descriptive elements
  5328. try {
  5329. // Get the childs bbox
  5330. bbox = child.bbox();
  5331. } catch (e) {
  5332. return;
  5333. } // Get childs matrix
  5334. const m = new Matrix(child); // Translate childs matrix by amount and
  5335. // transform it back into parents space
  5336. const matrix = m.translate(dx, dy).transform(m.inverse()); // Calculate new x and y from old box
  5337. const p = new Point(bbox.x, bbox.y).transform(matrix); // Move element
  5338. child.move(p.x, p.y);
  5339. });
  5340. return this;
  5341. }
  5342. function dx(dx) {
  5343. return this.dmove(dx, 0);
  5344. }
  5345. function dy(dy) {
  5346. return this.dmove(0, dy);
  5347. }
  5348. function height(height, box = this.bbox()) {
  5349. if (height == null) return box.height;
  5350. return this.size(box.width, height, box);
  5351. }
  5352. function move(x = 0, y = 0, box = this.bbox()) {
  5353. const dx = x - box.x;
  5354. const dy = y - box.y;
  5355. return this.dmove(dx, dy);
  5356. }
  5357. function size(width, height, box = this.bbox()) {
  5358. const p = proportionalSize(this, width, height, box);
  5359. const scaleX = p.width / box.width;
  5360. const scaleY = p.height / box.height;
  5361. this.children().forEach((child, i) => {
  5362. const o = new Point(box).transform(new Matrix(child).inverse());
  5363. child.scale(scaleX, scaleY, o.x, o.y);
  5364. });
  5365. return this;
  5366. }
  5367. function width(width, box = this.bbox()) {
  5368. if (width == null) return box.width;
  5369. return this.size(width, box.height, box);
  5370. }
  5371. function x(x, box = this.bbox()) {
  5372. if (x == null) return box.x;
  5373. return this.move(x, box.y, box);
  5374. }
  5375. function y(y, box = this.bbox()) {
  5376. if (y == null) return box.y;
  5377. return this.move(box.x, y, box);
  5378. }
  5379. var containerGeometry = {
  5380. __proto__: null,
  5381. dmove: dmove,
  5382. dx: dx,
  5383. dy: dy,
  5384. height: height,
  5385. move: move,
  5386. size: size,
  5387. width: width,
  5388. x: x,
  5389. y: y
  5390. };
  5391. class G extends Container {
  5392. constructor(node, attrs = node) {
  5393. super(nodeOrNew('g', node), attrs);
  5394. }
  5395. }
  5396. extend(G, containerGeometry);
  5397. registerMethods({
  5398. Container: {
  5399. // Create a group element
  5400. group: wrapWithAttrCheck(function () {
  5401. return this.put(new G());
  5402. })
  5403. }
  5404. });
  5405. register(G, 'G');
  5406. class A extends Container {
  5407. constructor(node, attrs = node) {
  5408. super(nodeOrNew('a', node), attrs);
  5409. } // Link target attribute
  5410. target(target) {
  5411. return this.attr('target', target);
  5412. } // Link url
  5413. to(url) {
  5414. return this.attr('href', url, xlink);
  5415. }
  5416. }
  5417. extend(A, containerGeometry);
  5418. registerMethods({
  5419. Container: {
  5420. // Create a hyperlink element
  5421. link: wrapWithAttrCheck(function (url) {
  5422. return this.put(new A()).to(url);
  5423. })
  5424. },
  5425. Element: {
  5426. unlink() {
  5427. const link = this.linker();
  5428. if (!link) return this;
  5429. const parent = link.parent();
  5430. if (!parent) {
  5431. return this.remove();
  5432. }
  5433. const index = parent.index(link);
  5434. parent.add(this, index);
  5435. link.remove();
  5436. return this;
  5437. },
  5438. linkTo(url) {
  5439. // reuse old link if possible
  5440. let link = this.linker();
  5441. if (!link) {
  5442. link = new A();
  5443. this.wrap(link);
  5444. }
  5445. if (typeof url === 'function') {
  5446. url.call(link, link);
  5447. } else {
  5448. link.to(url);
  5449. }
  5450. return this;
  5451. },
  5452. linker() {
  5453. const link = this.parent();
  5454. if (link && link.node.nodeName.toLowerCase() === 'a') {
  5455. return link;
  5456. }
  5457. return null;
  5458. }
  5459. }
  5460. });
  5461. register(A, 'A');
  5462. class Mask extends Container {
  5463. // Initialize node
  5464. constructor(node, attrs = node) {
  5465. super(nodeOrNew('mask', node), attrs);
  5466. } // Unmask all masked elements and remove itself
  5467. remove() {
  5468. // unmask all targets
  5469. this.targets().forEach(function (el) {
  5470. el.unmask();
  5471. }); // remove mask from parent
  5472. return super.remove();
  5473. }
  5474. targets() {
  5475. return baseFind('svg [mask*=' + this.id() + ']');
  5476. }
  5477. }
  5478. registerMethods({
  5479. Container: {
  5480. mask: wrapWithAttrCheck(function () {
  5481. return this.defs().put(new Mask());
  5482. })
  5483. },
  5484. Element: {
  5485. // Distribute mask to svg element
  5486. masker() {
  5487. return this.reference('mask');
  5488. },
  5489. maskWith(element) {
  5490. // use given mask or create a new one
  5491. const masker = element instanceof Mask ? element : this.parent().mask().add(element); // apply mask
  5492. return this.attr('mask', 'url(#' + masker.id() + ')');
  5493. },
  5494. // Unmask element
  5495. unmask() {
  5496. return this.attr('mask', null);
  5497. }
  5498. }
  5499. });
  5500. register(Mask, 'Mask');
  5501. class Stop extends Element {
  5502. constructor(node, attrs = node) {
  5503. super(nodeOrNew('stop', node), attrs);
  5504. } // add color stops
  5505. update(o) {
  5506. if (typeof o === 'number' || o instanceof SVGNumber) {
  5507. o = {
  5508. offset: arguments[0],
  5509. color: arguments[1],
  5510. opacity: arguments[2]
  5511. };
  5512. } // set attributes
  5513. if (o.opacity != null) this.attr('stop-opacity', o.opacity);
  5514. if (o.color != null) this.attr('stop-color', o.color);
  5515. if (o.offset != null) this.attr('offset', new SVGNumber(o.offset));
  5516. return this;
  5517. }
  5518. }
  5519. registerMethods({
  5520. Gradient: {
  5521. // Add a color stop
  5522. stop: function (offset, color, opacity) {
  5523. return this.put(new Stop()).update(offset, color, opacity);
  5524. }
  5525. }
  5526. });
  5527. register(Stop, 'Stop');
  5528. function cssRule(selector, rule) {
  5529. if (!selector) return '';
  5530. if (!rule) return selector;
  5531. let ret = selector + '{';
  5532. for (const i in rule) {
  5533. ret += unCamelCase(i) + ':' + rule[i] + ';';
  5534. }
  5535. ret += '}';
  5536. return ret;
  5537. }
  5538. class Style extends Element {
  5539. constructor(node, attrs = node) {
  5540. super(nodeOrNew('style', node), attrs);
  5541. }
  5542. addText(w = '') {
  5543. this.node.textContent += w;
  5544. return this;
  5545. }
  5546. font(name, src, params = {}) {
  5547. return this.rule('@font-face', {
  5548. fontFamily: name,
  5549. src: src,
  5550. ...params
  5551. });
  5552. }
  5553. rule(selector, obj) {
  5554. return this.addText(cssRule(selector, obj));
  5555. }
  5556. }
  5557. registerMethods('Dom', {
  5558. style(selector, obj) {
  5559. return this.put(new Style()).rule(selector, obj);
  5560. },
  5561. fontface(name, src, params) {
  5562. return this.put(new Style()).font(name, src, params);
  5563. }
  5564. });
  5565. register(Style, 'Style');
  5566. class TextPath extends Text {
  5567. // Initialize node
  5568. constructor(node, attrs = node) {
  5569. super(nodeOrNew('textPath', node), attrs);
  5570. } // return the array of the path track element
  5571. array() {
  5572. const track = this.track();
  5573. return track ? track.array() : null;
  5574. } // Plot path if any
  5575. plot(d) {
  5576. const track = this.track();
  5577. let pathArray = null;
  5578. if (track) {
  5579. pathArray = track.plot(d);
  5580. }
  5581. return d == null ? pathArray : this;
  5582. } // Get the path element
  5583. track() {
  5584. return this.reference('href');
  5585. }
  5586. }
  5587. registerMethods({
  5588. Container: {
  5589. textPath: wrapWithAttrCheck(function (text, path) {
  5590. // Convert text to instance if needed
  5591. if (!(text instanceof Text)) {
  5592. text = this.text(text);
  5593. }
  5594. return text.path(path);
  5595. })
  5596. },
  5597. Text: {
  5598. // Create path for text to run on
  5599. path: wrapWithAttrCheck(function (track, importNodes = true) {
  5600. const textPath = new TextPath(); // if track is a path, reuse it
  5601. if (!(track instanceof Path)) {
  5602. // create path element
  5603. track = this.defs().path(track);
  5604. } // link textPath to path and add content
  5605. textPath.attr('href', '#' + track, xlink); // Transplant all nodes from text to textPath
  5606. let node;
  5607. if (importNodes) {
  5608. while (node = this.node.firstChild) {
  5609. textPath.node.appendChild(node);
  5610. }
  5611. } // add textPath element as child node and return textPath
  5612. return this.put(textPath);
  5613. }),
  5614. // Get the textPath children
  5615. textPath() {
  5616. return this.findOne('textPath');
  5617. }
  5618. },
  5619. Path: {
  5620. // creates a textPath from this path
  5621. text: wrapWithAttrCheck(function (text) {
  5622. // Convert text to instance if needed
  5623. if (!(text instanceof Text)) {
  5624. text = new Text().addTo(this.parent()).text(text);
  5625. } // Create textPath from text and path and return
  5626. return text.path(this);
  5627. }),
  5628. targets() {
  5629. return baseFind('svg textPath').filter(node => {
  5630. return (node.attr('href') || '').includes(this.id());
  5631. }); // Does not work in IE11. Use when IE support is dropped
  5632. // return baseFind('svg textPath[*|href*=' + this.id() + ']')
  5633. }
  5634. }
  5635. });
  5636. TextPath.prototype.MorphArray = PathArray;
  5637. register(TextPath, 'TextPath');
  5638. class Use extends Shape {
  5639. constructor(node, attrs = node) {
  5640. super(nodeOrNew('use', node), attrs);
  5641. } // Use element as a reference
  5642. use(element, file) {
  5643. // Set lined element
  5644. return this.attr('href', (file || '') + '#' + element, xlink);
  5645. }
  5646. }
  5647. registerMethods({
  5648. Container: {
  5649. // Create a use element
  5650. use: wrapWithAttrCheck(function (element, file) {
  5651. return this.put(new Use()).use(element, file);
  5652. })
  5653. }
  5654. });
  5655. register(Use, 'Use');
  5656. /* Optional Modules */
  5657. const SVG = makeInstance;
  5658. extend([Svg, Symbol, Image, Pattern, Marker], getMethodsFor('viewbox'));
  5659. extend([Line, Polyline, Polygon, Path], getMethodsFor('marker'));
  5660. extend(Text, getMethodsFor('Text'));
  5661. extend(Path, getMethodsFor('Path'));
  5662. extend(Defs, getMethodsFor('Defs'));
  5663. extend([Text, Tspan], getMethodsFor('Tspan'));
  5664. extend([Rect, Ellipse, Gradient, Runner], getMethodsFor('radius'));
  5665. extend(EventTarget, getMethodsFor('EventTarget'));
  5666. extend(Dom, getMethodsFor('Dom'));
  5667. extend(Element, getMethodsFor('Element'));
  5668. extend(Shape, getMethodsFor('Shape'));
  5669. extend([Container, Fragment], getMethodsFor('Container'));
  5670. extend(Gradient, getMethodsFor('Gradient'));
  5671. extend(Runner, getMethodsFor('Runner'));
  5672. List.extend(getMethodNames());
  5673. registerMorphableType([SVGNumber, Color, Box, Matrix, SVGArray, PointArray, PathArray, Point]);
  5674. makeMorphable();
  5675. exports.A = A;
  5676. exports.Animator = Animator;
  5677. exports.Array = SVGArray;
  5678. exports.Box = Box;
  5679. exports.Circle = Circle;
  5680. exports.ClipPath = ClipPath;
  5681. exports.Color = Color;
  5682. exports.Container = Container;
  5683. exports.Controller = Controller;
  5684. exports.Defs = Defs;
  5685. exports.Dom = Dom;
  5686. exports.Ease = Ease;
  5687. exports.Element = Element;
  5688. exports.Ellipse = Ellipse;
  5689. exports.EventTarget = EventTarget;
  5690. exports.ForeignObject = ForeignObject;
  5691. exports.Fragment = Fragment;
  5692. exports.G = G;
  5693. exports.Gradient = Gradient;
  5694. exports.Image = Image;
  5695. exports.Line = Line;
  5696. exports.List = List;
  5697. exports.Marker = Marker;
  5698. exports.Mask = Mask;
  5699. exports.Matrix = Matrix;
  5700. exports.Morphable = Morphable;
  5701. exports.NonMorphable = NonMorphable;
  5702. exports.Number = SVGNumber;
  5703. exports.ObjectBag = ObjectBag;
  5704. exports.PID = PID;
  5705. exports.Path = Path;
  5706. exports.PathArray = PathArray;
  5707. exports.Pattern = Pattern;
  5708. exports.Point = Point;
  5709. exports.PointArray = PointArray;
  5710. exports.Polygon = Polygon;
  5711. exports.Polyline = Polyline;
  5712. exports.Queue = Queue;
  5713. exports.Rect = Rect;
  5714. exports.Runner = Runner;
  5715. exports.SVG = SVG;
  5716. exports.Shape = Shape;
  5717. exports.Spring = Spring;
  5718. exports.Stop = Stop;
  5719. exports.Style = Style;
  5720. exports.Svg = Svg;
  5721. exports.Symbol = Symbol;
  5722. exports.Text = Text;
  5723. exports.TextPath = TextPath;
  5724. exports.Timeline = Timeline;
  5725. exports.TransformBag = TransformBag;
  5726. exports.Tspan = Tspan;
  5727. exports.Use = Use;
  5728. exports.adopt = adopt;
  5729. exports.assignNewId = assignNewId;
  5730. exports.clearEvents = clearEvents;
  5731. exports.create = create;
  5732. exports.defaults = defaults;
  5733. exports.dispatch = dispatch;
  5734. exports.easing = easing;
  5735. exports.eid = eid;
  5736. exports.extend = extend;
  5737. exports.find = baseFind;
  5738. exports.getClass = getClass;
  5739. exports.getEventTarget = getEventTarget;
  5740. exports.getEvents = getEvents;
  5741. exports.getWindow = getWindow;
  5742. exports.makeInstance = makeInstance;
  5743. exports.makeMorphable = makeMorphable;
  5744. exports.mockAdopt = mockAdopt;
  5745. exports.namespaces = namespaces;
  5746. exports.nodeOrNew = nodeOrNew;
  5747. exports.off = off;
  5748. exports.on = on;
  5749. exports.parser = parser;
  5750. exports.regex = regex;
  5751. exports.register = register;
  5752. exports.registerMorphableType = registerMorphableType;
  5753. exports.registerWindow = registerWindow;
  5754. exports.restoreWindow = restoreWindow;
  5755. exports.root = root;
  5756. exports.saveWindow = saveWindow;
  5757. exports.utils = utils;
  5758. exports.windowEvents = windowEvents;
  5759. exports.withWindow = withWindow;
  5760. exports.wrapWithAttrCheck = wrapWithAttrCheck;
  5761. //# sourceMappingURL=svg.node.js.map