echarts.js 951 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : factory(global.echarts = {});
  3. })(this, function (exports) {
  4. 'use strict';
  5. /**
  6. * zrender: 生成唯一id
  7. *
  8. * @author errorrik (errorrik@gmail.com)
  9. */
  10. var idStart = 0x0907;
  11. var guid = function () {
  12. return idStart++;
  13. };
  14. /**
  15. * echarts设备环境识别
  16. *
  17. * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
  18. * @author firede[firede@firede.us]
  19. * @desc thanks zepto.
  20. */
  21. var env = {};
  22. if (typeof navigator === 'undefined') {
  23. // In node
  24. env = {
  25. browser: {},
  26. os: {},
  27. node: true,
  28. // Assume canvas is supported
  29. canvasSupported: true,
  30. svgSupported: true
  31. };
  32. } else {
  33. env = detect(navigator.userAgent);
  34. }
  35. var env$1 = env; // Zepto.js
  36. // (c) 2010-2013 Thomas Fuchs
  37. // Zepto.js may be freely distributed under the MIT license.
  38. function detect(ua) {
  39. var os = {};
  40. var browser = {}; // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/);
  41. // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  42. // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  43. // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  44. // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  45. // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/);
  46. // var touchpad = webos && ua.match(/TouchPad/);
  47. // var kindle = ua.match(/Kindle\/([\d.]+)/);
  48. // var silk = ua.match(/Silk\/([\d._]+)/);
  49. // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
  50. // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/);
  51. // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/);
  52. // var playbook = ua.match(/PlayBook/);
  53. // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/);
  54. var firefox = ua.match(/Firefox\/([\d.]+)/); // var safari = webkit && ua.match(/Mobile\//) && !chrome;
  55. // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;
  56. var ie = ua.match(/MSIE\s([\d.]+)/) // IE 11 Trident/7.0; rv:11.0
  57. || ua.match(/Trident\/.+?rv:(([\d.]+))/);
  58. var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+
  59. var weChat = /micromessenger/i.test(ua); // Todo: clean this up with a better OS/browser seperation:
  60. // - discern (more) between multiple browsers on android
  61. // - decide if kindle fire in silk mode is android or not
  62. // - Firefox on Android doesn't specify the Android version
  63. // - possibly devide in os, device and browser hashes
  64. // if (browser.webkit = !!webkit) browser.version = webkit[1];
  65. // if (android) os.android = true, os.version = android[2];
  66. // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
  67. // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
  68. // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  69. // if (webos) os.webos = true, os.version = webos[2];
  70. // if (touchpad) os.touchpad = true;
  71. // if (blackberry) os.blackberry = true, os.version = blackberry[2];
  72. // if (bb10) os.bb10 = true, os.version = bb10[2];
  73. // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
  74. // if (playbook) browser.playbook = true;
  75. // if (kindle) os.kindle = true, os.version = kindle[1];
  76. // if (silk) browser.silk = true, browser.version = silk[1];
  77. // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
  78. // if (chrome) browser.chrome = true, browser.version = chrome[1];
  79. if (firefox) {
  80. browser.firefox = true;
  81. browser.version = firefox[1];
  82. } // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;
  83. // if (webview) browser.webview = true;
  84. if (ie) {
  85. browser.ie = true;
  86. browser.version = ie[1];
  87. }
  88. if (edge) {
  89. browser.edge = true;
  90. browser.version = edge[1];
  91. } // It is difficult to detect WeChat in Win Phone precisely, because ua can
  92. // not be set on win phone. So we do not consider Win Phone.
  93. if (weChat) {
  94. browser.weChat = true;
  95. } // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
  96. // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
  97. // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||
  98. // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
  99. // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
  100. return {
  101. browser: browser,
  102. os: os,
  103. node: false,
  104. // 原生canvas支持,改极端点了
  105. // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)
  106. canvasSupported: !!document.createElement('canvas').getContext,
  107. svgSupported: typeof SVGRect !== 'undefined',
  108. // @see <http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript>
  109. // works on most browsers
  110. // IE10/11 does not support touch event, and MS Edge supports them but not by
  111. // default, so we dont check navigator.maxTouchPoints for them here.
  112. touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,
  113. // <http://caniuse.com/#search=pointer%20event>.
  114. pointerEventsSupported: 'onpointerdown' in window // Firefox supports pointer but not by default, only MS browsers are reliable on pointer
  115. // events currently. So we dont use that on other browsers unless tested sufficiently.
  116. // Although IE 10 supports pointer event, it use old style and is different from the
  117. // standard. So we exclude that. (IE 10 is hardly used on touch device)
  118. && (browser.edge || browser.ie && browser.version >= 11)
  119. };
  120. }
  121. /**
  122. * @module zrender/core/util
  123. */
  124. // 用于处理merge时无法遍历Date等对象的问题
  125. var BUILTIN_OBJECT = {
  126. '[object Function]': 1,
  127. '[object RegExp]': 1,
  128. '[object Date]': 1,
  129. '[object Error]': 1,
  130. '[object CanvasGradient]': 1,
  131. '[object CanvasPattern]': 1,
  132. // For node-canvas
  133. '[object Image]': 1,
  134. '[object Canvas]': 1
  135. };
  136. var TYPED_ARRAY = {
  137. '[object Int8Array]': 1,
  138. '[object Uint8Array]': 1,
  139. '[object Uint8ClampedArray]': 1,
  140. '[object Int16Array]': 1,
  141. '[object Uint16Array]': 1,
  142. '[object Int32Array]': 1,
  143. '[object Uint32Array]': 1,
  144. '[object Float32Array]': 1,
  145. '[object Float64Array]': 1
  146. };
  147. var objToString = Object.prototype.toString;
  148. var arrayProto = Array.prototype;
  149. var nativeForEach = arrayProto.forEach;
  150. var nativeFilter = arrayProto.filter;
  151. var nativeSlice = arrayProto.slice;
  152. var nativeMap = arrayProto.map;
  153. var nativeReduce = arrayProto.reduce; // Avoid assign to an exported variable, for transforming to cjs.
  154. var methods = {};
  155. function $override(name, fn) {
  156. methods[name] = fn;
  157. }
  158. /**
  159. * Those data types can be cloned:
  160. * Plain object, Array, TypedArray, number, string, null, undefined.
  161. * Those data types will be assgined using the orginal data:
  162. * BUILTIN_OBJECT
  163. * Instance of user defined class will be cloned to a plain object, without
  164. * properties in prototype.
  165. * Other data types is not supported (not sure what will happen).
  166. *
  167. * Caution: do not support clone Date, for performance consideration.
  168. * (There might be a large number of date in `series.data`).
  169. * So date should not be modified in and out of echarts.
  170. *
  171. * @param {*} source
  172. * @return {*} new
  173. */
  174. function clone(source) {
  175. if (source == null || typeof source != 'object') {
  176. return source;
  177. }
  178. var result = source;
  179. var typeStr = objToString.call(source);
  180. if (typeStr === '[object Array]') {
  181. result = [];
  182. for (var i = 0, len = source.length; i < len; i++) {
  183. result[i] = clone(source[i]);
  184. }
  185. } else if (TYPED_ARRAY[typeStr]) {
  186. var Ctor = source.constructor;
  187. if (source.constructor.from) {
  188. result = Ctor.from(source);
  189. } else {
  190. result = new Ctor(source.length);
  191. for (var i = 0, len = source.length; i < len; i++) {
  192. result[i] = clone(source[i]);
  193. }
  194. }
  195. } else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
  196. result = {};
  197. for (var key in source) {
  198. if (source.hasOwnProperty(key)) {
  199. result[key] = clone(source[key]);
  200. }
  201. }
  202. }
  203. return result;
  204. }
  205. /**
  206. * @memberOf module:zrender/core/util
  207. * @param {*} target
  208. * @param {*} source
  209. * @param {boolean} [overwrite=false]
  210. */
  211. function merge(target, source, overwrite) {
  212. // We should escapse that source is string
  213. // and enter for ... in ...
  214. if (!isObject(source) || !isObject(target)) {
  215. return overwrite ? clone(source) : target;
  216. }
  217. for (var key in source) {
  218. if (source.hasOwnProperty(key)) {
  219. var targetProp = target[key];
  220. var sourceProp = source[key];
  221. if (isObject(sourceProp) && isObject(targetProp) && !isArray(sourceProp) && !isArray(targetProp) && !isDom(sourceProp) && !isDom(targetProp) && !isBuiltInObject(sourceProp) && !isBuiltInObject(targetProp) && !isPrimitive(sourceProp) && !isPrimitive(targetProp)) {
  222. // 如果需要递归覆盖,就递归调用merge
  223. merge(targetProp, sourceProp, overwrite);
  224. } else if (overwrite || !(key in target)) {
  225. // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
  226. // NOTE,在 target[key] 不存在的时候也是直接覆盖
  227. target[key] = clone(source[key], true);
  228. }
  229. }
  230. }
  231. return target;
  232. }
  233. /**
  234. * @param {Array} targetAndSources The first item is target, and the rests are source.
  235. * @param {boolean} [overwrite=false]
  236. * @return {*} target
  237. */
  238. function mergeAll(targetAndSources, overwrite) {
  239. var result = targetAndSources[0];
  240. for (var i = 1, len = targetAndSources.length; i < len; i++) {
  241. result = merge(result, targetAndSources[i], overwrite);
  242. }
  243. return result;
  244. }
  245. /**
  246. * @param {*} target
  247. * @param {*} source
  248. * @memberOf module:zrender/core/util
  249. */
  250. function extend(target, source) {
  251. for (var key in source) {
  252. if (source.hasOwnProperty(key)) {
  253. target[key] = source[key];
  254. }
  255. }
  256. return target;
  257. }
  258. /**
  259. * @param {*} target
  260. * @param {*} source
  261. * @param {boolean} [overlay=false]
  262. * @memberOf module:zrender/core/util
  263. */
  264. function defaults(target, source, overlay) {
  265. for (var key in source) {
  266. if (source.hasOwnProperty(key) && (overlay ? source[key] != null : target[key] == null)) {
  267. target[key] = source[key];
  268. }
  269. }
  270. return target;
  271. }
  272. var createCanvas = function () {
  273. return methods.createCanvas();
  274. };
  275. methods.createCanvas = function () {
  276. return document.createElement('canvas');
  277. }; // FIXME
  278. var _ctx;
  279. function getContext() {
  280. if (!_ctx) {
  281. // Use util.createCanvas instead of createCanvas
  282. // because createCanvas may be overwritten in different environment
  283. _ctx = createCanvas().getContext('2d');
  284. }
  285. return _ctx;
  286. }
  287. /**
  288. * 查询数组中元素的index
  289. * @memberOf module:zrender/core/util
  290. */
  291. function indexOf(array, value) {
  292. if (array) {
  293. if (array.indexOf) {
  294. return array.indexOf(value);
  295. }
  296. for (var i = 0, len = array.length; i < len; i++) {
  297. if (array[i] === value) {
  298. return i;
  299. }
  300. }
  301. }
  302. return -1;
  303. }
  304. /**
  305. * 构造类继承关系
  306. *
  307. * @memberOf module:zrender/core/util
  308. * @param {Function} clazz 源类
  309. * @param {Function} baseClazz 基类
  310. */
  311. function inherits(clazz, baseClazz) {
  312. var clazzPrototype = clazz.prototype;
  313. function F() {}
  314. F.prototype = baseClazz.prototype;
  315. clazz.prototype = new F();
  316. for (var prop in clazzPrototype) {
  317. clazz.prototype[prop] = clazzPrototype[prop];
  318. }
  319. clazz.prototype.constructor = clazz;
  320. clazz.superClass = baseClazz;
  321. }
  322. /**
  323. * @memberOf module:zrender/core/util
  324. * @param {Object|Function} target
  325. * @param {Object|Function} sorce
  326. * @param {boolean} overlay
  327. */
  328. function mixin(target, source, overlay) {
  329. target = 'prototype' in target ? target.prototype : target;
  330. source = 'prototype' in source ? source.prototype : source;
  331. defaults(target, source, overlay);
  332. }
  333. /**
  334. * Consider typed array.
  335. * @param {Array|TypedArray} data
  336. */
  337. function isArrayLike(data) {
  338. if (!data) {
  339. return;
  340. }
  341. if (typeof data == 'string') {
  342. return false;
  343. }
  344. return typeof data.length == 'number';
  345. }
  346. /**
  347. * 数组或对象遍历
  348. * @memberOf module:zrender/core/util
  349. * @param {Object|Array} obj
  350. * @param {Function} cb
  351. * @param {*} [context]
  352. */
  353. function each$1(obj, cb, context) {
  354. if (!(obj && cb)) {
  355. return;
  356. }
  357. if (obj.forEach && obj.forEach === nativeForEach) {
  358. obj.forEach(cb, context);
  359. } else if (obj.length === +obj.length) {
  360. for (var i = 0, len = obj.length; i < len; i++) {
  361. cb.call(context, obj[i], i, obj);
  362. }
  363. } else {
  364. for (var key in obj) {
  365. if (obj.hasOwnProperty(key)) {
  366. cb.call(context, obj[key], key, obj);
  367. }
  368. }
  369. }
  370. }
  371. /**
  372. * 数组映射
  373. * @memberOf module:zrender/core/util
  374. * @param {Array} obj
  375. * @param {Function} cb
  376. * @param {*} [context]
  377. * @return {Array}
  378. */
  379. function map(obj, cb, context) {
  380. if (!(obj && cb)) {
  381. return;
  382. }
  383. if (obj.map && obj.map === nativeMap) {
  384. return obj.map(cb, context);
  385. } else {
  386. var result = [];
  387. for (var i = 0, len = obj.length; i < len; i++) {
  388. result.push(cb.call(context, obj[i], i, obj));
  389. }
  390. return result;
  391. }
  392. }
  393. /**
  394. * @memberOf module:zrender/core/util
  395. * @param {Array} obj
  396. * @param {Function} cb
  397. * @param {Object} [memo]
  398. * @param {*} [context]
  399. * @return {Array}
  400. */
  401. function reduce(obj, cb, memo, context) {
  402. if (!(obj && cb)) {
  403. return;
  404. }
  405. if (obj.reduce && obj.reduce === nativeReduce) {
  406. return obj.reduce(cb, memo, context);
  407. } else {
  408. for (var i = 0, len = obj.length; i < len; i++) {
  409. memo = cb.call(context, memo, obj[i], i, obj);
  410. }
  411. return memo;
  412. }
  413. }
  414. /**
  415. * 数组过滤
  416. * @memberOf module:zrender/core/util
  417. * @param {Array} obj
  418. * @param {Function} cb
  419. * @param {*} [context]
  420. * @return {Array}
  421. */
  422. function filter(obj, cb, context) {
  423. if (!(obj && cb)) {
  424. return;
  425. }
  426. if (obj.filter && obj.filter === nativeFilter) {
  427. return obj.filter(cb, context);
  428. } else {
  429. var result = [];
  430. for (var i = 0, len = obj.length; i < len; i++) {
  431. if (cb.call(context, obj[i], i, obj)) {
  432. result.push(obj[i]);
  433. }
  434. }
  435. return result;
  436. }
  437. }
  438. /**
  439. * 数组项查找
  440. * @memberOf module:zrender/core/util
  441. * @param {Array} obj
  442. * @param {Function} cb
  443. * @param {*} [context]
  444. * @return {*}
  445. */
  446. function find(obj, cb, context) {
  447. if (!(obj && cb)) {
  448. return;
  449. }
  450. for (var i = 0, len = obj.length; i < len; i++) {
  451. if (cb.call(context, obj[i], i, obj)) {
  452. return obj[i];
  453. }
  454. }
  455. }
  456. /**
  457. * @memberOf module:zrender/core/util
  458. * @param {Function} func
  459. * @param {*} context
  460. * @return {Function}
  461. */
  462. function bind(func, context) {
  463. var args = nativeSlice.call(arguments, 2);
  464. return function () {
  465. return func.apply(context, args.concat(nativeSlice.call(arguments)));
  466. };
  467. }
  468. /**
  469. * @memberOf module:zrender/core/util
  470. * @param {Function} func
  471. * @return {Function}
  472. */
  473. function curry(func) {
  474. var args = nativeSlice.call(arguments, 1);
  475. return function () {
  476. return func.apply(this, args.concat(nativeSlice.call(arguments)));
  477. };
  478. }
  479. /**
  480. * @memberOf module:zrender/core/util
  481. * @param {*} value
  482. * @return {boolean}
  483. */
  484. function isArray(value) {
  485. return objToString.call(value) === '[object Array]';
  486. }
  487. /**
  488. * @memberOf module:zrender/core/util
  489. * @param {*} value
  490. * @return {boolean}
  491. */
  492. function isFunction(value) {
  493. return typeof value === 'function';
  494. }
  495. /**
  496. * @memberOf module:zrender/core/util
  497. * @param {*} value
  498. * @return {boolean}
  499. */
  500. function isString(value) {
  501. return objToString.call(value) === '[object String]';
  502. }
  503. /**
  504. * @memberOf module:zrender/core/util
  505. * @param {*} value
  506. * @return {boolean}
  507. */
  508. function isObject(value) {
  509. // Avoid a V8 JIT bug in Chrome 19-20.
  510. // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
  511. var type = typeof value;
  512. return type === 'function' || !!value && type == 'object';
  513. }
  514. /**
  515. * @memberOf module:zrender/core/util
  516. * @param {*} value
  517. * @return {boolean}
  518. */
  519. function isBuiltInObject(value) {
  520. return !!BUILTIN_OBJECT[objToString.call(value)];
  521. }
  522. /**
  523. * @memberOf module:zrender/core/util
  524. * @param {*} value
  525. * @return {boolean}
  526. */
  527. function isDom(value) {
  528. return typeof value === 'object' && typeof value.nodeType === 'number' && typeof value.ownerDocument === 'object';
  529. }
  530. /**
  531. * Whether is exactly NaN. Notice isNaN('a') returns true.
  532. * @param {*} value
  533. * @return {boolean}
  534. */
  535. function eqNaN(value) {
  536. return value !== value;
  537. }
  538. /**
  539. * If value1 is not null, then return value1, otherwise judget rest of values.
  540. * Low performance.
  541. * @memberOf module:zrender/core/util
  542. * @return {*} Final value
  543. */
  544. function retrieve(values) {
  545. for (var i = 0, len = arguments.length; i < len; i++) {
  546. if (arguments[i] != null) {
  547. return arguments[i];
  548. }
  549. }
  550. }
  551. function retrieve2(value0, value1) {
  552. return value0 != null ? value0 : value1;
  553. }
  554. function retrieve3(value0, value1, value2) {
  555. return value0 != null ? value0 : value1 != null ? value1 : value2;
  556. }
  557. /**
  558. * @memberOf module:zrender/core/util
  559. * @param {Array} arr
  560. * @param {number} startIndex
  561. * @param {number} endIndex
  562. * @return {Array}
  563. */
  564. function slice() {
  565. return Function.call.apply(nativeSlice, arguments);
  566. }
  567. /**
  568. * Normalize css liked array configuration
  569. * e.g.
  570. * 3 => [3, 3, 3, 3]
  571. * [4, 2] => [4, 2, 4, 2]
  572. * [4, 3, 2] => [4, 3, 2, 3]
  573. * @param {number|Array.<number>} val
  574. * @return {Array.<number>}
  575. */
  576. function normalizeCssArray(val) {
  577. if (typeof val === 'number') {
  578. return [val, val, val, val];
  579. }
  580. var len = val.length;
  581. if (len === 2) {
  582. // vertical | horizontal
  583. return [val[0], val[1], val[0], val[1]];
  584. } else if (len === 3) {
  585. // top | horizontal | bottom
  586. return [val[0], val[1], val[2], val[1]];
  587. }
  588. return val;
  589. }
  590. /**
  591. * @memberOf module:zrender/core/util
  592. * @param {boolean} condition
  593. * @param {string} message
  594. */
  595. function assert(condition, message) {
  596. if (!condition) {
  597. throw new Error(message);
  598. }
  599. }
  600. var primitiveKey = '__ec_primitive__';
  601. /**
  602. * Set an object as primitive to be ignored traversing children in clone or merge
  603. */
  604. function setAsPrimitive(obj) {
  605. obj[primitiveKey] = true;
  606. }
  607. function isPrimitive(obj) {
  608. return obj[primitiveKey];
  609. }
  610. /**
  611. * @constructor
  612. * @param {Object} obj Only apply `ownProperty`.
  613. */
  614. function HashMap(obj) {
  615. obj && each$1(obj, function (value, key) {
  616. this.set(key, value);
  617. }, this);
  618. } // Add prefix to avoid conflict with Object.prototype.
  619. var HASH_MAP_PREFIX = '_ec_';
  620. var HASH_MAP_PREFIX_LENGTH = 4;
  621. HashMap.prototype = {
  622. constructor: HashMap,
  623. // Do not provide `has` method to avoid defining what is `has`.
  624. // (We usually treat `null` and `undefined` as the same, different
  625. // from ES6 Map).
  626. get: function (key) {
  627. return this[HASH_MAP_PREFIX + key];
  628. },
  629. set: function (key, value) {
  630. this[HASH_MAP_PREFIX + key] = value; // Comparing with invocation chaining, `return value` is more commonly
  631. // used in this case: `var someVal = map.set('a', genVal());`
  632. return value;
  633. },
  634. // Although util.each can be performed on this hashMap directly, user
  635. // should not use the exposed keys, who are prefixed.
  636. each: function (cb, context) {
  637. context !== void 0 && (cb = bind(cb, context));
  638. for (var prefixedKey in this) {
  639. this.hasOwnProperty(prefixedKey) && cb(this[prefixedKey], prefixedKey.slice(HASH_MAP_PREFIX_LENGTH));
  640. }
  641. },
  642. // Do not use this method if performance sensitive.
  643. removeKey: function (key) {
  644. delete this[HASH_MAP_PREFIX + key];
  645. }
  646. };
  647. function createHashMap(obj) {
  648. return new HashMap(obj);
  649. }
  650. function noop() {}
  651. var zrUtil = (Object.freeze || Object)({
  652. $override: $override,
  653. clone: clone,
  654. merge: merge,
  655. mergeAll: mergeAll,
  656. extend: extend,
  657. defaults: defaults,
  658. createCanvas: createCanvas,
  659. getContext: getContext,
  660. indexOf: indexOf,
  661. inherits: inherits,
  662. mixin: mixin,
  663. isArrayLike: isArrayLike,
  664. each: each$1,
  665. map: map,
  666. reduce: reduce,
  667. filter: filter,
  668. find: find,
  669. bind: bind,
  670. curry: curry,
  671. isArray: isArray,
  672. isFunction: isFunction,
  673. isString: isString,
  674. isObject: isObject,
  675. isBuiltInObject: isBuiltInObject,
  676. isDom: isDom,
  677. eqNaN: eqNaN,
  678. retrieve: retrieve,
  679. retrieve2: retrieve2,
  680. retrieve3: retrieve3,
  681. slice: slice,
  682. normalizeCssArray: normalizeCssArray,
  683. assert: assert,
  684. setAsPrimitive: setAsPrimitive,
  685. isPrimitive: isPrimitive,
  686. createHashMap: createHashMap,
  687. noop: noop
  688. });
  689. var ArrayCtor = typeof Float32Array === 'undefined' ? Array : Float32Array;
  690. /**
  691. * 创建一个向量
  692. * @param {number} [x=0]
  693. * @param {number} [y=0]
  694. * @return {Vector2}
  695. */
  696. function create(x, y) {
  697. var out = new ArrayCtor(2);
  698. if (x == null) {
  699. x = 0;
  700. }
  701. if (y == null) {
  702. y = 0;
  703. }
  704. out[0] = x;
  705. out[1] = y;
  706. return out;
  707. }
  708. /**
  709. * 复制向量数据
  710. * @param {Vector2} out
  711. * @param {Vector2} v
  712. * @return {Vector2}
  713. */
  714. function copy(out, v) {
  715. out[0] = v[0];
  716. out[1] = v[1];
  717. return out;
  718. }
  719. /**
  720. * 克隆一个向量
  721. * @param {Vector2} v
  722. * @return {Vector2}
  723. */
  724. function clone$1(v) {
  725. var out = new ArrayCtor(2);
  726. out[0] = v[0];
  727. out[1] = v[1];
  728. return out;
  729. }
  730. /**
  731. * 设置向量的两个项
  732. * @param {Vector2} out
  733. * @param {number} a
  734. * @param {number} b
  735. * @return {Vector2} 结果
  736. */
  737. function set(out, a, b) {
  738. out[0] = a;
  739. out[1] = b;
  740. return out;
  741. }
  742. /**
  743. * 向量相加
  744. * @param {Vector2} out
  745. * @param {Vector2} v1
  746. * @param {Vector2} v2
  747. */
  748. function add(out, v1, v2) {
  749. out[0] = v1[0] + v2[0];
  750. out[1] = v1[1] + v2[1];
  751. return out;
  752. }
  753. /**
  754. * 向量缩放后相加
  755. * @param {Vector2} out
  756. * @param {Vector2} v1
  757. * @param {Vector2} v2
  758. * @param {number} a
  759. */
  760. function scaleAndAdd(out, v1, v2, a) {
  761. out[0] = v1[0] + v2[0] * a;
  762. out[1] = v1[1] + v2[1] * a;
  763. return out;
  764. }
  765. /**
  766. * 向量相减
  767. * @param {Vector2} out
  768. * @param {Vector2} v1
  769. * @param {Vector2} v2
  770. */
  771. function sub(out, v1, v2) {
  772. out[0] = v1[0] - v2[0];
  773. out[1] = v1[1] - v2[1];
  774. return out;
  775. }
  776. /**
  777. * 向量长度
  778. * @param {Vector2} v
  779. * @return {number}
  780. */
  781. function len(v) {
  782. return Math.sqrt(lenSquare(v));
  783. }
  784. var length = len; // jshint ignore:line
  785. /**
  786. * 向量长度平方
  787. * @param {Vector2} v
  788. * @return {number}
  789. */
  790. function lenSquare(v) {
  791. return v[0] * v[0] + v[1] * v[1];
  792. }
  793. var lengthSquare = lenSquare;
  794. /**
  795. * 向量乘法
  796. * @param {Vector2} out
  797. * @param {Vector2} v1
  798. * @param {Vector2} v2
  799. */
  800. function mul(out, v1, v2) {
  801. out[0] = v1[0] * v2[0];
  802. out[1] = v1[1] * v2[1];
  803. return out;
  804. }
  805. /**
  806. * 向量除法
  807. * @param {Vector2} out
  808. * @param {Vector2} v1
  809. * @param {Vector2} v2
  810. */
  811. function div(out, v1, v2) {
  812. out[0] = v1[0] / v2[0];
  813. out[1] = v1[1] / v2[1];
  814. return out;
  815. }
  816. /**
  817. * 向量点乘
  818. * @param {Vector2} v1
  819. * @param {Vector2} v2
  820. * @return {number}
  821. */
  822. function dot(v1, v2) {
  823. return v1[0] * v2[0] + v1[1] * v2[1];
  824. }
  825. /**
  826. * 向量缩放
  827. * @param {Vector2} out
  828. * @param {Vector2} v
  829. * @param {number} s
  830. */
  831. function scale(out, v, s) {
  832. out[0] = v[0] * s;
  833. out[1] = v[1] * s;
  834. return out;
  835. }
  836. /**
  837. * 向量归一化
  838. * @param {Vector2} out
  839. * @param {Vector2} v
  840. */
  841. function normalize(out, v) {
  842. var d = len(v);
  843. if (d === 0) {
  844. out[0] = 0;
  845. out[1] = 0;
  846. } else {
  847. out[0] = v[0] / d;
  848. out[1] = v[1] / d;
  849. }
  850. return out;
  851. }
  852. /**
  853. * 计算向量间距离
  854. * @param {Vector2} v1
  855. * @param {Vector2} v2
  856. * @return {number}
  857. */
  858. function distance(v1, v2) {
  859. return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]));
  860. }
  861. var dist = distance;
  862. /**
  863. * 向量距离平方
  864. * @param {Vector2} v1
  865. * @param {Vector2} v2
  866. * @return {number}
  867. */
  868. function distanceSquare(v1, v2) {
  869. return (v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]);
  870. }
  871. var distSquare = distanceSquare;
  872. /**
  873. * 求负向量
  874. * @param {Vector2} out
  875. * @param {Vector2} v
  876. */
  877. function negate(out, v) {
  878. out[0] = -v[0];
  879. out[1] = -v[1];
  880. return out;
  881. }
  882. /**
  883. * 插值两个点
  884. * @param {Vector2} out
  885. * @param {Vector2} v1
  886. * @param {Vector2} v2
  887. * @param {number} t
  888. */
  889. function lerp(out, v1, v2, t) {
  890. out[0] = v1[0] + t * (v2[0] - v1[0]);
  891. out[1] = v1[1] + t * (v2[1] - v1[1]);
  892. return out;
  893. }
  894. /**
  895. * 矩阵左乘向量
  896. * @param {Vector2} out
  897. * @param {Vector2} v
  898. * @param {Vector2} m
  899. */
  900. function applyTransform(out, v, m) {
  901. var x = v[0];
  902. var y = v[1];
  903. out[0] = m[0] * x + m[2] * y + m[4];
  904. out[1] = m[1] * x + m[3] * y + m[5];
  905. return out;
  906. }
  907. /**
  908. * 求两个向量最小值
  909. * @param {Vector2} out
  910. * @param {Vector2} v1
  911. * @param {Vector2} v2
  912. */
  913. function min(out, v1, v2) {
  914. out[0] = Math.min(v1[0], v2[0]);
  915. out[1] = Math.min(v1[1], v2[1]);
  916. return out;
  917. }
  918. /**
  919. * 求两个向量最大值
  920. * @param {Vector2} out
  921. * @param {Vector2} v1
  922. * @param {Vector2} v2
  923. */
  924. function max(out, v1, v2) {
  925. out[0] = Math.max(v1[0], v2[0]);
  926. out[1] = Math.max(v1[1], v2[1]);
  927. return out;
  928. }
  929. var vector = (Object.freeze || Object)({
  930. create: create,
  931. copy: copy,
  932. clone: clone$1,
  933. set: set,
  934. add: add,
  935. scaleAndAdd: scaleAndAdd,
  936. sub: sub,
  937. len: len,
  938. length: length,
  939. lenSquare: lenSquare,
  940. lengthSquare: lengthSquare,
  941. mul: mul,
  942. div: div,
  943. dot: dot,
  944. scale: scale,
  945. normalize: normalize,
  946. distance: distance,
  947. dist: dist,
  948. distanceSquare: distanceSquare,
  949. distSquare: distSquare,
  950. negate: negate,
  951. lerp: lerp,
  952. applyTransform: applyTransform,
  953. min: min,
  954. max: max
  955. }); // TODO Draggable for group
  956. // FIXME Draggable on element which has parent rotation or scale
  957. function Draggable() {
  958. this.on('mousedown', this._dragStart, this);
  959. this.on('mousemove', this._drag, this);
  960. this.on('mouseup', this._dragEnd, this);
  961. this.on('globalout', this._dragEnd, this); // this._dropTarget = null;
  962. // this._draggingTarget = null;
  963. // this._x = 0;
  964. // this._y = 0;
  965. }
  966. Draggable.prototype = {
  967. constructor: Draggable,
  968. _dragStart: function (e) {
  969. var draggingTarget = e.target;
  970. if (draggingTarget && draggingTarget.draggable) {
  971. this._draggingTarget = draggingTarget;
  972. draggingTarget.dragging = true;
  973. this._x = e.offsetX;
  974. this._y = e.offsetY;
  975. this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);
  976. }
  977. },
  978. _drag: function (e) {
  979. var draggingTarget = this._draggingTarget;
  980. if (draggingTarget) {
  981. var x = e.offsetX;
  982. var y = e.offsetY;
  983. var dx = x - this._x;
  984. var dy = y - this._y;
  985. this._x = x;
  986. this._y = y;
  987. draggingTarget.drift(dx, dy, e);
  988. this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);
  989. var dropTarget = this.findHover(x, y, draggingTarget).target;
  990. var lastDropTarget = this._dropTarget;
  991. this._dropTarget = dropTarget;
  992. if (draggingTarget !== dropTarget) {
  993. if (lastDropTarget && dropTarget !== lastDropTarget) {
  994. this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);
  995. }
  996. if (dropTarget && dropTarget !== lastDropTarget) {
  997. this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);
  998. }
  999. }
  1000. }
  1001. },
  1002. _dragEnd: function (e) {
  1003. var draggingTarget = this._draggingTarget;
  1004. if (draggingTarget) {
  1005. draggingTarget.dragging = false;
  1006. }
  1007. this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);
  1008. if (this._dropTarget) {
  1009. this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);
  1010. }
  1011. this._draggingTarget = null;
  1012. this._dropTarget = null;
  1013. }
  1014. };
  1015. function param(target, e) {
  1016. return {
  1017. target: target,
  1018. topTarget: e && e.topTarget
  1019. };
  1020. }
  1021. /**
  1022. * 事件扩展
  1023. * @module zrender/mixin/Eventful
  1024. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  1025. * pissang (https://www.github.com/pissang)
  1026. */
  1027. var arrySlice = Array.prototype.slice;
  1028. /**
  1029. * 事件分发器
  1030. * @alias module:zrender/mixin/Eventful
  1031. * @constructor
  1032. */
  1033. var Eventful = function () {
  1034. this._$handlers = {};
  1035. };
  1036. Eventful.prototype = {
  1037. constructor: Eventful,
  1038. /**
  1039. * 单次触发绑定,trigger后销毁
  1040. *
  1041. * @param {string} event 事件名
  1042. * @param {Function} handler 响应函数
  1043. * @param {Object} context
  1044. */
  1045. one: function (event, handler, context) {
  1046. var _h = this._$handlers;
  1047. if (!handler || !event) {
  1048. return this;
  1049. }
  1050. if (!_h[event]) {
  1051. _h[event] = [];
  1052. }
  1053. for (var i = 0; i < _h[event].length; i++) {
  1054. if (_h[event][i].h === handler) {
  1055. return this;
  1056. }
  1057. }
  1058. _h[event].push({
  1059. h: handler,
  1060. one: true,
  1061. ctx: context || this
  1062. });
  1063. return this;
  1064. },
  1065. /**
  1066. * 绑定事件
  1067. * @param {string} event 事件名
  1068. * @param {Function} handler 事件处理函数
  1069. * @param {Object} [context]
  1070. */
  1071. on: function (event, handler, context) {
  1072. var _h = this._$handlers;
  1073. if (!handler || !event) {
  1074. return this;
  1075. }
  1076. if (!_h[event]) {
  1077. _h[event] = [];
  1078. }
  1079. for (var i = 0; i < _h[event].length; i++) {
  1080. if (_h[event][i].h === handler) {
  1081. return this;
  1082. }
  1083. }
  1084. _h[event].push({
  1085. h: handler,
  1086. one: false,
  1087. ctx: context || this
  1088. });
  1089. return this;
  1090. },
  1091. /**
  1092. * 是否绑定了事件
  1093. * @param {string} event
  1094. * @return {boolean}
  1095. */
  1096. isSilent: function (event) {
  1097. var _h = this._$handlers;
  1098. return _h[event] && _h[event].length;
  1099. },
  1100. /**
  1101. * 解绑事件
  1102. * @param {string} event 事件名
  1103. * @param {Function} [handler] 事件处理函数
  1104. */
  1105. off: function (event, handler) {
  1106. var _h = this._$handlers;
  1107. if (!event) {
  1108. this._$handlers = {};
  1109. return this;
  1110. }
  1111. if (handler) {
  1112. if (_h[event]) {
  1113. var newList = [];
  1114. for (var i = 0, l = _h[event].length; i < l; i++) {
  1115. if (_h[event][i]['h'] != handler) {
  1116. newList.push(_h[event][i]);
  1117. }
  1118. }
  1119. _h[event] = newList;
  1120. }
  1121. if (_h[event] && _h[event].length === 0) {
  1122. delete _h[event];
  1123. }
  1124. } else {
  1125. delete _h[event];
  1126. }
  1127. return this;
  1128. },
  1129. /**
  1130. * 事件分发
  1131. *
  1132. * @param {string} type 事件类型
  1133. */
  1134. trigger: function (type) {
  1135. if (this._$handlers[type]) {
  1136. var args = arguments;
  1137. var argLen = args.length;
  1138. if (argLen > 3) {
  1139. args = arrySlice.call(args, 1);
  1140. }
  1141. var _h = this._$handlers[type];
  1142. var len = _h.length;
  1143. for (var i = 0; i < len;) {
  1144. // Optimize advise from backbone
  1145. switch (argLen) {
  1146. case 1:
  1147. _h[i]['h'].call(_h[i]['ctx']);
  1148. break;
  1149. case 2:
  1150. _h[i]['h'].call(_h[i]['ctx'], args[1]);
  1151. break;
  1152. case 3:
  1153. _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]);
  1154. break;
  1155. default:
  1156. // have more than 2 given arguments
  1157. _h[i]['h'].apply(_h[i]['ctx'], args);
  1158. break;
  1159. }
  1160. if (_h[i]['one']) {
  1161. _h.splice(i, 1);
  1162. len--;
  1163. } else {
  1164. i++;
  1165. }
  1166. }
  1167. }
  1168. return this;
  1169. },
  1170. /**
  1171. * 带有context的事件分发, 最后一个参数是事件回调的context
  1172. * @param {string} type 事件类型
  1173. */
  1174. triggerWithContext: function (type) {
  1175. if (this._$handlers[type]) {
  1176. var args = arguments;
  1177. var argLen = args.length;
  1178. if (argLen > 4) {
  1179. args = arrySlice.call(args, 1, args.length - 1);
  1180. }
  1181. var ctx = args[args.length - 1];
  1182. var _h = this._$handlers[type];
  1183. var len = _h.length;
  1184. for (var i = 0; i < len;) {
  1185. // Optimize advise from backbone
  1186. switch (argLen) {
  1187. case 1:
  1188. _h[i]['h'].call(ctx);
  1189. break;
  1190. case 2:
  1191. _h[i]['h'].call(ctx, args[1]);
  1192. break;
  1193. case 3:
  1194. _h[i]['h'].call(ctx, args[1], args[2]);
  1195. break;
  1196. default:
  1197. // have more than 2 given arguments
  1198. _h[i]['h'].apply(ctx, args);
  1199. break;
  1200. }
  1201. if (_h[i]['one']) {
  1202. _h.splice(i, 1);
  1203. len--;
  1204. } else {
  1205. i++;
  1206. }
  1207. }
  1208. }
  1209. return this;
  1210. }
  1211. };
  1212. /**
  1213. * Handler
  1214. * @module zrender/Handler
  1215. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  1216. * errorrik (errorrik@gmail.com)
  1217. * pissang (shenyi.914@gmail.com)
  1218. */
  1219. var SILENT = 'silent';
  1220. function makeEventPacket(eveType, targetInfo, event) {
  1221. return {
  1222. type: eveType,
  1223. event: event,
  1224. // target can only be an element that is not silent.
  1225. target: targetInfo.target,
  1226. // topTarget can be a silent element.
  1227. topTarget: targetInfo.topTarget,
  1228. cancelBubble: false,
  1229. offsetX: event.zrX,
  1230. offsetY: event.zrY,
  1231. gestureEvent: event.gestureEvent,
  1232. pinchX: event.pinchX,
  1233. pinchY: event.pinchY,
  1234. pinchScale: event.pinchScale,
  1235. wheelDelta: event.zrDelta,
  1236. zrByTouch: event.zrByTouch,
  1237. which: event.which
  1238. };
  1239. }
  1240. function EmptyProxy() {}
  1241. EmptyProxy.prototype.dispose = function () {};
  1242. var handlerNames = ['click', 'dblclick', 'mousewheel', 'mouseout', 'mouseup', 'mousedown', 'mousemove', 'contextmenu'];
  1243. /**
  1244. * @alias module:zrender/Handler
  1245. * @constructor
  1246. * @extends module:zrender/mixin/Eventful
  1247. * @param {module:zrender/Storage} storage Storage instance.
  1248. * @param {module:zrender/Painter} painter Painter instance.
  1249. * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.
  1250. * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).
  1251. */
  1252. var Handler = function (storage, painter, proxy, painterRoot) {
  1253. Eventful.call(this);
  1254. this.storage = storage;
  1255. this.painter = painter;
  1256. this.painterRoot = painterRoot;
  1257. proxy = proxy || new EmptyProxy();
  1258. /**
  1259. * Proxy of event. can be Dom, WebGLSurface, etc.
  1260. */
  1261. this.proxy = proxy; // Attach handler
  1262. proxy.handler = this;
  1263. /**
  1264. * {target, topTarget, x, y}
  1265. * @private
  1266. * @type {Object}
  1267. */
  1268. this._hovered = {};
  1269. /**
  1270. * @private
  1271. * @type {Date}
  1272. */
  1273. this._lastTouchMoment;
  1274. /**
  1275. * @private
  1276. * @type {number}
  1277. */
  1278. this._lastX;
  1279. /**
  1280. * @private
  1281. * @type {number}
  1282. */
  1283. this._lastY;
  1284. Draggable.call(this);
  1285. each$1(handlerNames, function (name) {
  1286. proxy.on && proxy.on(name, this[name], this);
  1287. }, this);
  1288. };
  1289. Handler.prototype = {
  1290. constructor: Handler,
  1291. mousemove: function (event) {
  1292. var x = event.zrX;
  1293. var y = event.zrY;
  1294. var lastHovered = this._hovered;
  1295. var lastHoveredTarget = lastHovered.target; // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call
  1296. // (like 'setOption' or 'dispatchAction') in event handlers, we should find
  1297. // lastHovered again here. Otherwise 'mouseout' can not be triggered normally.
  1298. // See #6198.
  1299. if (lastHoveredTarget && !lastHoveredTarget.__zr) {
  1300. lastHovered = this.findHover(lastHovered.x, lastHovered.y);
  1301. lastHoveredTarget = lastHovered.target;
  1302. }
  1303. var hovered = this._hovered = this.findHover(x, y);
  1304. var hoveredTarget = hovered.target;
  1305. var proxy = this.proxy;
  1306. proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default'); // Mouse out on previous hovered element
  1307. if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1308. this.dispatchToElement(lastHovered, 'mouseout', event);
  1309. } // Mouse moving on one element
  1310. this.dispatchToElement(hovered, 'mousemove', event); // Mouse over on a new element
  1311. if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1312. this.dispatchToElement(hovered, 'mouseover', event);
  1313. }
  1314. },
  1315. mouseout: function (event) {
  1316. this.dispatchToElement(this._hovered, 'mouseout', event); // There might be some doms created by upper layer application
  1317. // at the same level of painter.getViewportRoot() (e.g., tooltip
  1318. // dom created by echarts), where 'globalout' event should not
  1319. // be triggered when mouse enters these doms. (But 'mouseout'
  1320. // should be triggered at the original hovered element as usual).
  1321. var element = event.toElement || event.relatedTarget;
  1322. var innerDom;
  1323. do {
  1324. element = element && element.parentNode;
  1325. } while (element && element.nodeType != 9 && !(innerDom = element === this.painterRoot));
  1326. !innerDom && this.trigger('globalout', {
  1327. event: event
  1328. });
  1329. },
  1330. /**
  1331. * Resize
  1332. */
  1333. resize: function (event) {
  1334. this._hovered = {};
  1335. },
  1336. /**
  1337. * Dispatch event
  1338. * @param {string} eventName
  1339. * @param {event=} eventArgs
  1340. */
  1341. dispatch: function (eventName, eventArgs) {
  1342. var handler = this[eventName];
  1343. handler && handler.call(this, eventArgs);
  1344. },
  1345. /**
  1346. * Dispose
  1347. */
  1348. dispose: function () {
  1349. this.proxy.dispose();
  1350. this.storage = this.proxy = this.painter = null;
  1351. },
  1352. /**
  1353. * 设置默认的cursor style
  1354. * @param {string} [cursorStyle='default'] 例如 crosshair
  1355. */
  1356. setCursorStyle: function (cursorStyle) {
  1357. var proxy = this.proxy;
  1358. proxy.setCursor && proxy.setCursor(cursorStyle);
  1359. },
  1360. /**
  1361. * 事件分发代理
  1362. *
  1363. * @private
  1364. * @param {Object} targetInfo {target, topTarget} 目标图形元素
  1365. * @param {string} eventName 事件名称
  1366. * @param {Object} event 事件对象
  1367. */
  1368. dispatchToElement: function (targetInfo, eventName, event) {
  1369. targetInfo = targetInfo || {};
  1370. var el = targetInfo.target;
  1371. if (el && el.silent) {
  1372. return;
  1373. }
  1374. var eventHandler = 'on' + eventName;
  1375. var eventPacket = makeEventPacket(eventName, targetInfo, event);
  1376. while (el) {
  1377. el[eventHandler] && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket));
  1378. el.trigger(eventName, eventPacket);
  1379. el = el.parent;
  1380. if (eventPacket.cancelBubble) {
  1381. break;
  1382. }
  1383. }
  1384. if (!eventPacket.cancelBubble) {
  1385. // 冒泡到顶级 zrender 对象
  1386. this.trigger(eventName, eventPacket); // 分发事件到用户自定义层
  1387. // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在
  1388. this.painter && this.painter.eachOtherLayer(function (layer) {
  1389. if (typeof layer[eventHandler] == 'function') {
  1390. layer[eventHandler].call(layer, eventPacket);
  1391. }
  1392. if (layer.trigger) {
  1393. layer.trigger(eventName, eventPacket);
  1394. }
  1395. });
  1396. }
  1397. },
  1398. /**
  1399. * @private
  1400. * @param {number} x
  1401. * @param {number} y
  1402. * @param {module:zrender/graphic/Displayable} exclude
  1403. * @return {model:zrender/Element}
  1404. * @method
  1405. */
  1406. findHover: function (x, y, exclude) {
  1407. var list = this.storage.getDisplayList();
  1408. var out = {
  1409. x: x,
  1410. y: y
  1411. };
  1412. for (var i = list.length - 1; i >= 0; i--) {
  1413. var hoverCheckResult;
  1414. if (list[i] !== exclude // getDisplayList may include ignored item in VML mode
  1415. && !list[i].ignore && (hoverCheckResult = isHover(list[i], x, y))) {
  1416. !out.topTarget && (out.topTarget = list[i]);
  1417. if (hoverCheckResult !== SILENT) {
  1418. out.target = list[i];
  1419. break;
  1420. }
  1421. }
  1422. }
  1423. return out;
  1424. }
  1425. }; // Common handlers
  1426. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  1427. Handler.prototype[name] = function (event) {
  1428. // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover
  1429. var hovered = this.findHover(event.zrX, event.zrY);
  1430. var hoveredTarget = hovered.target;
  1431. if (name === 'mousedown') {
  1432. this._downEl = hoveredTarget;
  1433. this._downPoint = [event.zrX, event.zrY]; // In case click triggered before mouseup
  1434. this._upEl = hoveredTarget;
  1435. } else if (name === 'mosueup') {
  1436. this._upEl = hoveredTarget;
  1437. } else if (name === 'click') {
  1438. if (this._downEl !== this._upEl // Original click event is triggered on the whole canvas element,
  1439. // including the case that `mousedown` - `mousemove` - `mouseup`,
  1440. // which should be filtered, otherwise it will bring trouble to
  1441. // pan and zoom.
  1442. || !this._downPoint // Arbitrary value
  1443. || dist(this._downPoint, [event.zrX, event.zrY]) > 4) {
  1444. return;
  1445. }
  1446. this._downPoint = null;
  1447. }
  1448. this.dispatchToElement(hovered, name, event);
  1449. };
  1450. });
  1451. function isHover(displayable, x, y) {
  1452. if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {
  1453. var el = displayable;
  1454. var isSilent;
  1455. while (el) {
  1456. // If clipped by ancestor.
  1457. // FIXME: If clipPath has neither stroke nor fill,
  1458. // el.clipPath.contain(x, y) will always return false.
  1459. if (el.clipPath && !el.clipPath.contain(x, y)) {
  1460. return false;
  1461. }
  1462. if (el.silent) {
  1463. isSilent = true;
  1464. }
  1465. el = el.parent;
  1466. }
  1467. return isSilent ? SILENT : true;
  1468. }
  1469. return false;
  1470. }
  1471. mixin(Handler, Eventful);
  1472. mixin(Handler, Draggable);
  1473. /**
  1474. * 3x2矩阵操作类
  1475. * @exports zrender/tool/matrix
  1476. */
  1477. var ArrayCtor$1 = typeof Float32Array === 'undefined' ? Array : Float32Array;
  1478. /**
  1479. * 创建一个单位矩阵
  1480. * @return {Float32Array|Array.<number>}
  1481. */
  1482. function create$1() {
  1483. var out = new ArrayCtor$1(6);
  1484. identity(out);
  1485. return out;
  1486. }
  1487. /**
  1488. * 设置矩阵为单位矩阵
  1489. * @param {Float32Array|Array.<number>} out
  1490. */
  1491. function identity(out) {
  1492. out[0] = 1;
  1493. out[1] = 0;
  1494. out[2] = 0;
  1495. out[3] = 1;
  1496. out[4] = 0;
  1497. out[5] = 0;
  1498. return out;
  1499. }
  1500. /**
  1501. * 复制矩阵
  1502. * @param {Float32Array|Array.<number>} out
  1503. * @param {Float32Array|Array.<number>} m
  1504. */
  1505. function copy$1(out, m) {
  1506. out[0] = m[0];
  1507. out[1] = m[1];
  1508. out[2] = m[2];
  1509. out[3] = m[3];
  1510. out[4] = m[4];
  1511. out[5] = m[5];
  1512. return out;
  1513. }
  1514. /**
  1515. * 矩阵相乘
  1516. * @param {Float32Array|Array.<number>} out
  1517. * @param {Float32Array|Array.<number>} m1
  1518. * @param {Float32Array|Array.<number>} m2
  1519. */
  1520. function mul$1(out, m1, m2) {
  1521. // Consider matrix.mul(m, m2, m);
  1522. // where out is the same as m2.
  1523. // So use temp variable to escape error.
  1524. var out0 = m1[0] * m2[0] + m1[2] * m2[1];
  1525. var out1 = m1[1] * m2[0] + m1[3] * m2[1];
  1526. var out2 = m1[0] * m2[2] + m1[2] * m2[3];
  1527. var out3 = m1[1] * m2[2] + m1[3] * m2[3];
  1528. var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];
  1529. var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];
  1530. out[0] = out0;
  1531. out[1] = out1;
  1532. out[2] = out2;
  1533. out[3] = out3;
  1534. out[4] = out4;
  1535. out[5] = out5;
  1536. return out;
  1537. }
  1538. /**
  1539. * 平移变换
  1540. * @param {Float32Array|Array.<number>} out
  1541. * @param {Float32Array|Array.<number>} a
  1542. * @param {Float32Array|Array.<number>} v
  1543. */
  1544. function translate(out, a, v) {
  1545. out[0] = a[0];
  1546. out[1] = a[1];
  1547. out[2] = a[2];
  1548. out[3] = a[3];
  1549. out[4] = a[4] + v[0];
  1550. out[5] = a[5] + v[1];
  1551. return out;
  1552. }
  1553. /**
  1554. * 旋转变换
  1555. * @param {Float32Array|Array.<number>} out
  1556. * @param {Float32Array|Array.<number>} a
  1557. * @param {number} rad
  1558. */
  1559. function rotate(out, a, rad) {
  1560. var aa = a[0];
  1561. var ac = a[2];
  1562. var atx = a[4];
  1563. var ab = a[1];
  1564. var ad = a[3];
  1565. var aty = a[5];
  1566. var st = Math.sin(rad);
  1567. var ct = Math.cos(rad);
  1568. out[0] = aa * ct + ab * st;
  1569. out[1] = -aa * st + ab * ct;
  1570. out[2] = ac * ct + ad * st;
  1571. out[3] = -ac * st + ct * ad;
  1572. out[4] = ct * atx + st * aty;
  1573. out[5] = ct * aty - st * atx;
  1574. return out;
  1575. }
  1576. /**
  1577. * 缩放变换
  1578. * @param {Float32Array|Array.<number>} out
  1579. * @param {Float32Array|Array.<number>} a
  1580. * @param {Float32Array|Array.<number>} v
  1581. */
  1582. function scale$1(out, a, v) {
  1583. var vx = v[0];
  1584. var vy = v[1];
  1585. out[0] = a[0] * vx;
  1586. out[1] = a[1] * vy;
  1587. out[2] = a[2] * vx;
  1588. out[3] = a[3] * vy;
  1589. out[4] = a[4] * vx;
  1590. out[5] = a[5] * vy;
  1591. return out;
  1592. }
  1593. /**
  1594. * 求逆矩阵
  1595. * @param {Float32Array|Array.<number>} out
  1596. * @param {Float32Array|Array.<number>} a
  1597. */
  1598. function invert(out, a) {
  1599. var aa = a[0];
  1600. var ac = a[2];
  1601. var atx = a[4];
  1602. var ab = a[1];
  1603. var ad = a[3];
  1604. var aty = a[5];
  1605. var det = aa * ad - ab * ac;
  1606. if (!det) {
  1607. return null;
  1608. }
  1609. det = 1.0 / det;
  1610. out[0] = ad * det;
  1611. out[1] = -ab * det;
  1612. out[2] = -ac * det;
  1613. out[3] = aa * det;
  1614. out[4] = (ac * aty - ad * atx) * det;
  1615. out[5] = (ab * atx - aa * aty) * det;
  1616. return out;
  1617. }
  1618. var matrix = (Object.freeze || Object)({
  1619. create: create$1,
  1620. identity: identity,
  1621. copy: copy$1,
  1622. mul: mul$1,
  1623. translate: translate,
  1624. rotate: rotate,
  1625. scale: scale$1,
  1626. invert: invert
  1627. });
  1628. /**
  1629. * 提供变换扩展
  1630. * @module zrender/mixin/Transformable
  1631. * @author pissang (https://www.github.com/pissang)
  1632. */
  1633. var mIdentity = identity;
  1634. var EPSILON = 5e-5;
  1635. function isNotAroundZero(val) {
  1636. return val > EPSILON || val < -EPSILON;
  1637. }
  1638. /**
  1639. * @alias module:zrender/mixin/Transformable
  1640. * @constructor
  1641. */
  1642. var Transformable = function (opts) {
  1643. opts = opts || {}; // If there are no given position, rotation, scale
  1644. if (!opts.position) {
  1645. /**
  1646. * 平移
  1647. * @type {Array.<number>}
  1648. * @default [0, 0]
  1649. */
  1650. this.position = [0, 0];
  1651. }
  1652. if (opts.rotation == null) {
  1653. /**
  1654. * 旋转
  1655. * @type {Array.<number>}
  1656. * @default 0
  1657. */
  1658. this.rotation = 0;
  1659. }
  1660. if (!opts.scale) {
  1661. /**
  1662. * 缩放
  1663. * @type {Array.<number>}
  1664. * @default [1, 1]
  1665. */
  1666. this.scale = [1, 1];
  1667. }
  1668. /**
  1669. * 旋转和缩放的原点
  1670. * @type {Array.<number>}
  1671. * @default null
  1672. */
  1673. this.origin = this.origin || null;
  1674. };
  1675. var transformableProto = Transformable.prototype;
  1676. transformableProto.transform = null;
  1677. /**
  1678. * 判断是否需要有坐标变换
  1679. * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵
  1680. */
  1681. transformableProto.needLocalTransform = function () {
  1682. return isNotAroundZero(this.rotation) || isNotAroundZero(this.position[0]) || isNotAroundZero(this.position[1]) || isNotAroundZero(this.scale[0] - 1) || isNotAroundZero(this.scale[1] - 1);
  1683. };
  1684. transformableProto.updateTransform = function () {
  1685. var parent = this.parent;
  1686. var parentHasTransform = parent && parent.transform;
  1687. var needLocalTransform = this.needLocalTransform();
  1688. var m = this.transform;
  1689. if (!(needLocalTransform || parentHasTransform)) {
  1690. m && mIdentity(m);
  1691. return;
  1692. }
  1693. m = m || create$1();
  1694. if (needLocalTransform) {
  1695. this.getLocalTransform(m);
  1696. } else {
  1697. mIdentity(m);
  1698. } // 应用父节点变换
  1699. if (parentHasTransform) {
  1700. if (needLocalTransform) {
  1701. mul$1(m, parent.transform, m);
  1702. } else {
  1703. copy$1(m, parent.transform);
  1704. }
  1705. } // 保存这个变换矩阵
  1706. this.transform = m;
  1707. this.invTransform = this.invTransform || create$1();
  1708. invert(this.invTransform, m);
  1709. };
  1710. transformableProto.getLocalTransform = function (m) {
  1711. return Transformable.getLocalTransform(this, m);
  1712. };
  1713. /**
  1714. * 将自己的transform应用到context上
  1715. * @param {CanvasRenderingContext2D} ctx
  1716. */
  1717. transformableProto.setTransform = function (ctx) {
  1718. var m = this.transform;
  1719. var dpr = ctx.dpr || 1;
  1720. if (m) {
  1721. ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
  1722. } else {
  1723. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  1724. }
  1725. };
  1726. transformableProto.restoreTransform = function (ctx) {
  1727. var dpr = ctx.dpr || 1;
  1728. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  1729. };
  1730. var tmpTransform = [];
  1731. /**
  1732. * 分解`transform`矩阵到`position`, `rotation`, `scale`
  1733. */
  1734. transformableProto.decomposeTransform = function () {
  1735. if (!this.transform) {
  1736. return;
  1737. }
  1738. var parent = this.parent;
  1739. var m = this.transform;
  1740. if (parent && parent.transform) {
  1741. // Get local transform and decompose them to position, scale, rotation
  1742. mul$1(tmpTransform, parent.invTransform, m);
  1743. m = tmpTransform;
  1744. }
  1745. var sx = m[0] * m[0] + m[1] * m[1];
  1746. var sy = m[2] * m[2] + m[3] * m[3];
  1747. var position = this.position;
  1748. var scale$$1 = this.scale;
  1749. if (isNotAroundZero(sx - 1)) {
  1750. sx = Math.sqrt(sx);
  1751. }
  1752. if (isNotAroundZero(sy - 1)) {
  1753. sy = Math.sqrt(sy);
  1754. }
  1755. if (m[0] < 0) {
  1756. sx = -sx;
  1757. }
  1758. if (m[3] < 0) {
  1759. sy = -sy;
  1760. }
  1761. position[0] = m[4];
  1762. position[1] = m[5];
  1763. scale$$1[0] = sx;
  1764. scale$$1[1] = sy;
  1765. this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
  1766. };
  1767. /**
  1768. * Get global scale
  1769. * @return {Array.<number>}
  1770. */
  1771. transformableProto.getGlobalScale = function () {
  1772. var m = this.transform;
  1773. if (!m) {
  1774. return [1, 1];
  1775. }
  1776. var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
  1777. var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
  1778. if (m[0] < 0) {
  1779. sx = -sx;
  1780. }
  1781. if (m[3] < 0) {
  1782. sy = -sy;
  1783. }
  1784. return [sx, sy];
  1785. };
  1786. /**
  1787. * 变换坐标位置到 shape 的局部坐标空间
  1788. * @method
  1789. * @param {number} x
  1790. * @param {number} y
  1791. * @return {Array.<number>}
  1792. */
  1793. transformableProto.transformCoordToLocal = function (x, y) {
  1794. var v2 = [x, y];
  1795. var invTransform = this.invTransform;
  1796. if (invTransform) {
  1797. applyTransform(v2, v2, invTransform);
  1798. }
  1799. return v2;
  1800. };
  1801. /**
  1802. * 变换局部坐标位置到全局坐标空间
  1803. * @method
  1804. * @param {number} x
  1805. * @param {number} y
  1806. * @return {Array.<number>}
  1807. */
  1808. transformableProto.transformCoordToGlobal = function (x, y) {
  1809. var v2 = [x, y];
  1810. var transform = this.transform;
  1811. if (transform) {
  1812. applyTransform(v2, v2, transform);
  1813. }
  1814. return v2;
  1815. };
  1816. /**
  1817. * @static
  1818. * @param {Object} target
  1819. * @param {Array.<number>} target.origin
  1820. * @param {number} target.rotation
  1821. * @param {Array.<number>} target.position
  1822. * @param {Array.<number>} [m]
  1823. */
  1824. Transformable.getLocalTransform = function (target, m) {
  1825. m = m || [];
  1826. mIdentity(m);
  1827. var origin = target.origin;
  1828. var scale$$1 = target.scale || [1, 1];
  1829. var rotation = target.rotation || 0;
  1830. var position = target.position || [0, 0];
  1831. if (origin) {
  1832. // Translate to origin
  1833. m[4] -= origin[0];
  1834. m[5] -= origin[1];
  1835. }
  1836. scale$1(m, m, scale$$1);
  1837. if (rotation) {
  1838. rotate(m, m, rotation);
  1839. }
  1840. if (origin) {
  1841. // Translate back from origin
  1842. m[4] += origin[0];
  1843. m[5] += origin[1];
  1844. }
  1845. m[4] += position[0];
  1846. m[5] += position[1];
  1847. return m;
  1848. };
  1849. /**
  1850. * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js
  1851. * @see http://sole.github.io/tween.js/examples/03_graphs.html
  1852. * @exports zrender/animation/easing
  1853. */
  1854. var easing = {
  1855. /**
  1856. * @param {number} k
  1857. * @return {number}
  1858. */
  1859. linear: function (k) {
  1860. return k;
  1861. },
  1862. /**
  1863. * @param {number} k
  1864. * @return {number}
  1865. */
  1866. quadraticIn: function (k) {
  1867. return k * k;
  1868. },
  1869. /**
  1870. * @param {number} k
  1871. * @return {number}
  1872. */
  1873. quadraticOut: function (k) {
  1874. return k * (2 - k);
  1875. },
  1876. /**
  1877. * @param {number} k
  1878. * @return {number}
  1879. */
  1880. quadraticInOut: function (k) {
  1881. if ((k *= 2) < 1) {
  1882. return 0.5 * k * k;
  1883. }
  1884. return -0.5 * (--k * (k - 2) - 1);
  1885. },
  1886. // 三次方的缓动(t^3)
  1887. /**
  1888. * @param {number} k
  1889. * @return {number}
  1890. */
  1891. cubicIn: function (k) {
  1892. return k * k * k;
  1893. },
  1894. /**
  1895. * @param {number} k
  1896. * @return {number}
  1897. */
  1898. cubicOut: function (k) {
  1899. return --k * k * k + 1;
  1900. },
  1901. /**
  1902. * @param {number} k
  1903. * @return {number}
  1904. */
  1905. cubicInOut: function (k) {
  1906. if ((k *= 2) < 1) {
  1907. return 0.5 * k * k * k;
  1908. }
  1909. return 0.5 * ((k -= 2) * k * k + 2);
  1910. },
  1911. // 四次方的缓动(t^4)
  1912. /**
  1913. * @param {number} k
  1914. * @return {number}
  1915. */
  1916. quarticIn: function (k) {
  1917. return k * k * k * k;
  1918. },
  1919. /**
  1920. * @param {number} k
  1921. * @return {number}
  1922. */
  1923. quarticOut: function (k) {
  1924. return 1 - --k * k * k * k;
  1925. },
  1926. /**
  1927. * @param {number} k
  1928. * @return {number}
  1929. */
  1930. quarticInOut: function (k) {
  1931. if ((k *= 2) < 1) {
  1932. return 0.5 * k * k * k * k;
  1933. }
  1934. return -0.5 * ((k -= 2) * k * k * k - 2);
  1935. },
  1936. // 五次方的缓动(t^5)
  1937. /**
  1938. * @param {number} k
  1939. * @return {number}
  1940. */
  1941. quinticIn: function (k) {
  1942. return k * k * k * k * k;
  1943. },
  1944. /**
  1945. * @param {number} k
  1946. * @return {number}
  1947. */
  1948. quinticOut: function (k) {
  1949. return --k * k * k * k * k + 1;
  1950. },
  1951. /**
  1952. * @param {number} k
  1953. * @return {number}
  1954. */
  1955. quinticInOut: function (k) {
  1956. if ((k *= 2) < 1) {
  1957. return 0.5 * k * k * k * k * k;
  1958. }
  1959. return 0.5 * ((k -= 2) * k * k * k * k + 2);
  1960. },
  1961. // 正弦曲线的缓动(sin(t))
  1962. /**
  1963. * @param {number} k
  1964. * @return {number}
  1965. */
  1966. sinusoidalIn: function (k) {
  1967. return 1 - Math.cos(k * Math.PI / 2);
  1968. },
  1969. /**
  1970. * @param {number} k
  1971. * @return {number}
  1972. */
  1973. sinusoidalOut: function (k) {
  1974. return Math.sin(k * Math.PI / 2);
  1975. },
  1976. /**
  1977. * @param {number} k
  1978. * @return {number}
  1979. */
  1980. sinusoidalInOut: function (k) {
  1981. return 0.5 * (1 - Math.cos(Math.PI * k));
  1982. },
  1983. // 指数曲线的缓动(2^t)
  1984. /**
  1985. * @param {number} k
  1986. * @return {number}
  1987. */
  1988. exponentialIn: function (k) {
  1989. return k === 0 ? 0 : Math.pow(1024, k - 1);
  1990. },
  1991. /**
  1992. * @param {number} k
  1993. * @return {number}
  1994. */
  1995. exponentialOut: function (k) {
  1996. return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
  1997. },
  1998. /**
  1999. * @param {number} k
  2000. * @return {number}
  2001. */
  2002. exponentialInOut: function (k) {
  2003. if (k === 0) {
  2004. return 0;
  2005. }
  2006. if (k === 1) {
  2007. return 1;
  2008. }
  2009. if ((k *= 2) < 1) {
  2010. return 0.5 * Math.pow(1024, k - 1);
  2011. }
  2012. return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
  2013. },
  2014. // 圆形曲线的缓动(sqrt(1-t^2))
  2015. /**
  2016. * @param {number} k
  2017. * @return {number}
  2018. */
  2019. circularIn: function (k) {
  2020. return 1 - Math.sqrt(1 - k * k);
  2021. },
  2022. /**
  2023. * @param {number} k
  2024. * @return {number}
  2025. */
  2026. circularOut: function (k) {
  2027. return Math.sqrt(1 - --k * k);
  2028. },
  2029. /**
  2030. * @param {number} k
  2031. * @return {number}
  2032. */
  2033. circularInOut: function (k) {
  2034. if ((k *= 2) < 1) {
  2035. return -0.5 * (Math.sqrt(1 - k * k) - 1);
  2036. }
  2037. return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
  2038. },
  2039. // 创建类似于弹簧在停止前来回振荡的动画
  2040. /**
  2041. * @param {number} k
  2042. * @return {number}
  2043. */
  2044. elasticIn: function (k) {
  2045. var s;
  2046. var a = 0.1;
  2047. var p = 0.4;
  2048. if (k === 0) {
  2049. return 0;
  2050. }
  2051. if (k === 1) {
  2052. return 1;
  2053. }
  2054. if (!a || a < 1) {
  2055. a = 1;
  2056. s = p / 4;
  2057. } else {
  2058. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2059. }
  2060. return -(a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
  2061. },
  2062. /**
  2063. * @param {number} k
  2064. * @return {number}
  2065. */
  2066. elasticOut: function (k) {
  2067. var s;
  2068. var a = 0.1;
  2069. var p = 0.4;
  2070. if (k === 0) {
  2071. return 0;
  2072. }
  2073. if (k === 1) {
  2074. return 1;
  2075. }
  2076. if (!a || a < 1) {
  2077. a = 1;
  2078. s = p / 4;
  2079. } else {
  2080. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2081. }
  2082. return a * Math.pow(2, -10 * k) * Math.sin((k - s) * (2 * Math.PI) / p) + 1;
  2083. },
  2084. /**
  2085. * @param {number} k
  2086. * @return {number}
  2087. */
  2088. elasticInOut: function (k) {
  2089. var s;
  2090. var a = 0.1;
  2091. var p = 0.4;
  2092. if (k === 0) {
  2093. return 0;
  2094. }
  2095. if (k === 1) {
  2096. return 1;
  2097. }
  2098. if (!a || a < 1) {
  2099. a = 1;
  2100. s = p / 4;
  2101. } else {
  2102. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2103. }
  2104. if ((k *= 2) < 1) {
  2105. return -0.5 * (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
  2106. }
  2107. return a * Math.pow(2, -10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;
  2108. },
  2109. // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动
  2110. /**
  2111. * @param {number} k
  2112. * @return {number}
  2113. */
  2114. backIn: function (k) {
  2115. var s = 1.70158;
  2116. return k * k * ((s + 1) * k - s);
  2117. },
  2118. /**
  2119. * @param {number} k
  2120. * @return {number}
  2121. */
  2122. backOut: function (k) {
  2123. var s = 1.70158;
  2124. return --k * k * ((s + 1) * k + s) + 1;
  2125. },
  2126. /**
  2127. * @param {number} k
  2128. * @return {number}
  2129. */
  2130. backInOut: function (k) {
  2131. var s = 1.70158 * 1.525;
  2132. if ((k *= 2) < 1) {
  2133. return 0.5 * (k * k * ((s + 1) * k - s));
  2134. }
  2135. return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
  2136. },
  2137. // 创建弹跳效果
  2138. /**
  2139. * @param {number} k
  2140. * @return {number}
  2141. */
  2142. bounceIn: function (k) {
  2143. return 1 - easing.bounceOut(1 - k);
  2144. },
  2145. /**
  2146. * @param {number} k
  2147. * @return {number}
  2148. */
  2149. bounceOut: function (k) {
  2150. if (k < 1 / 2.75) {
  2151. return 7.5625 * k * k;
  2152. } else if (k < 2 / 2.75) {
  2153. return 7.5625 * (k -= 1.5 / 2.75) * k + 0.75;
  2154. } else if (k < 2.5 / 2.75) {
  2155. return 7.5625 * (k -= 2.25 / 2.75) * k + 0.9375;
  2156. } else {
  2157. return 7.5625 * (k -= 2.625 / 2.75) * k + 0.984375;
  2158. }
  2159. },
  2160. /**
  2161. * @param {number} k
  2162. * @return {number}
  2163. */
  2164. bounceInOut: function (k) {
  2165. if (k < 0.5) {
  2166. return easing.bounceIn(k * 2) * 0.5;
  2167. }
  2168. return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
  2169. }
  2170. };
  2171. /**
  2172. * 动画主控制器
  2173. * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件
  2174. * @config life(1000) 动画时长
  2175. * @config delay(0) 动画延迟时间
  2176. * @config loop(true)
  2177. * @config gap(0) 循环的间隔时间
  2178. * @config onframe
  2179. * @config easing(optional)
  2180. * @config ondestroy(optional)
  2181. * @config onrestart(optional)
  2182. *
  2183. * TODO pause
  2184. */
  2185. function Clip(options) {
  2186. this._target = options.target; // 生命周期
  2187. this._life = options.life || 1000; // 延时
  2188. this._delay = options.delay || 0; // 开始时间
  2189. // this._startTime = new Date().getTime() + this._delay;// 单位毫秒
  2190. this._initialized = false; // 是否循环
  2191. this.loop = options.loop == null ? false : options.loop;
  2192. this.gap = options.gap || 0;
  2193. this.easing = options.easing || 'Linear';
  2194. this.onframe = options.onframe;
  2195. this.ondestroy = options.ondestroy;
  2196. this.onrestart = options.onrestart;
  2197. this._pausedTime = 0;
  2198. this._paused = false;
  2199. }
  2200. Clip.prototype = {
  2201. constructor: Clip,
  2202. step: function (globalTime, deltaTime) {
  2203. // Set startTime on first step, or _startTime may has milleseconds different between clips
  2204. // PENDING
  2205. if (!this._initialized) {
  2206. this._startTime = globalTime + this._delay;
  2207. this._initialized = true;
  2208. }
  2209. if (this._paused) {
  2210. this._pausedTime += deltaTime;
  2211. return;
  2212. }
  2213. var percent = (globalTime - this._startTime - this._pausedTime) / this._life; // 还没开始
  2214. if (percent < 0) {
  2215. return;
  2216. }
  2217. percent = Math.min(percent, 1);
  2218. var easing$$1 = this.easing;
  2219. var easingFunc = typeof easing$$1 == 'string' ? easing[easing$$1] : easing$$1;
  2220. var schedule = typeof easingFunc === 'function' ? easingFunc(percent) : percent;
  2221. this.fire('frame', schedule); // 结束
  2222. if (percent == 1) {
  2223. if (this.loop) {
  2224. this.restart(globalTime); // 重新开始周期
  2225. // 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件
  2226. return 'restart';
  2227. } // 动画完成将这个控制器标识为待删除
  2228. // 在Animation.update中进行批量删除
  2229. this._needsRemove = true;
  2230. return 'destroy';
  2231. }
  2232. return null;
  2233. },
  2234. restart: function (globalTime) {
  2235. var remainder = (globalTime - this._startTime - this._pausedTime) % this._life;
  2236. this._startTime = globalTime - remainder + this.gap;
  2237. this._pausedTime = 0;
  2238. this._needsRemove = false;
  2239. },
  2240. fire: function (eventType, arg) {
  2241. eventType = 'on' + eventType;
  2242. if (this[eventType]) {
  2243. this[eventType](this._target, arg);
  2244. }
  2245. },
  2246. pause: function () {
  2247. this._paused = true;
  2248. },
  2249. resume: function () {
  2250. this._paused = false;
  2251. }
  2252. }; // Simple LRU cache use doubly linked list
  2253. // @module zrender/core/LRU
  2254. /**
  2255. * Simple double linked list. Compared with array, it has O(1) remove operation.
  2256. * @constructor
  2257. */
  2258. var LinkedList = function () {
  2259. /**
  2260. * @type {module:zrender/core/LRU~Entry}
  2261. */
  2262. this.head = null;
  2263. /**
  2264. * @type {module:zrender/core/LRU~Entry}
  2265. */
  2266. this.tail = null;
  2267. this._len = 0;
  2268. };
  2269. var linkedListProto = LinkedList.prototype;
  2270. /**
  2271. * Insert a new value at the tail
  2272. * @param {} val
  2273. * @return {module:zrender/core/LRU~Entry}
  2274. */
  2275. linkedListProto.insert = function (val) {
  2276. var entry = new Entry(val);
  2277. this.insertEntry(entry);
  2278. return entry;
  2279. };
  2280. /**
  2281. * Insert an entry at the tail
  2282. * @param {module:zrender/core/LRU~Entry} entry
  2283. */
  2284. linkedListProto.insertEntry = function (entry) {
  2285. if (!this.head) {
  2286. this.head = this.tail = entry;
  2287. } else {
  2288. this.tail.next = entry;
  2289. entry.prev = this.tail;
  2290. entry.next = null;
  2291. this.tail = entry;
  2292. }
  2293. this._len++;
  2294. };
  2295. /**
  2296. * Remove entry.
  2297. * @param {module:zrender/core/LRU~Entry} entry
  2298. */
  2299. linkedListProto.remove = function (entry) {
  2300. var prev = entry.prev;
  2301. var next = entry.next;
  2302. if (prev) {
  2303. prev.next = next;
  2304. } else {
  2305. // Is head
  2306. this.head = next;
  2307. }
  2308. if (next) {
  2309. next.prev = prev;
  2310. } else {
  2311. // Is tail
  2312. this.tail = prev;
  2313. }
  2314. entry.next = entry.prev = null;
  2315. this._len--;
  2316. };
  2317. /**
  2318. * @return {number}
  2319. */
  2320. linkedListProto.len = function () {
  2321. return this._len;
  2322. };
  2323. /**
  2324. * Clear list
  2325. */
  2326. linkedListProto.clear = function () {
  2327. this.head = this.tail = null;
  2328. this._len = 0;
  2329. };
  2330. /**
  2331. * @constructor
  2332. * @param {} val
  2333. */
  2334. var Entry = function (val) {
  2335. /**
  2336. * @type {}
  2337. */
  2338. this.value = val;
  2339. /**
  2340. * @type {module:zrender/core/LRU~Entry}
  2341. */
  2342. this.next;
  2343. /**
  2344. * @type {module:zrender/core/LRU~Entry}
  2345. */
  2346. this.prev;
  2347. };
  2348. /**
  2349. * LRU Cache
  2350. * @constructor
  2351. * @alias module:zrender/core/LRU
  2352. */
  2353. var LRU = function (maxSize) {
  2354. this._list = new LinkedList();
  2355. this._map = {};
  2356. this._maxSize = maxSize || 10;
  2357. this._lastRemovedEntry = null;
  2358. };
  2359. var LRUProto = LRU.prototype;
  2360. /**
  2361. * @param {string} key
  2362. * @param {} value
  2363. * @return {} Removed value
  2364. */
  2365. LRUProto.put = function (key, value) {
  2366. var list = this._list;
  2367. var map = this._map;
  2368. var removed = null;
  2369. if (map[key] == null) {
  2370. var len = list.len(); // Reuse last removed entry
  2371. var entry = this._lastRemovedEntry;
  2372. if (len >= this._maxSize && len > 0) {
  2373. // Remove the least recently used
  2374. var leastUsedEntry = list.head;
  2375. list.remove(leastUsedEntry);
  2376. delete map[leastUsedEntry.key];
  2377. removed = leastUsedEntry.value;
  2378. this._lastRemovedEntry = leastUsedEntry;
  2379. }
  2380. if (entry) {
  2381. entry.value = value;
  2382. } else {
  2383. entry = new Entry(value);
  2384. }
  2385. entry.key = key;
  2386. list.insertEntry(entry);
  2387. map[key] = entry;
  2388. }
  2389. return removed;
  2390. };
  2391. /**
  2392. * @param {string} key
  2393. * @return {}
  2394. */
  2395. LRUProto.get = function (key) {
  2396. var entry = this._map[key];
  2397. var list = this._list;
  2398. if (entry != null) {
  2399. // Put the latest used entry in the tail
  2400. if (entry !== list.tail) {
  2401. list.remove(entry);
  2402. list.insertEntry(entry);
  2403. }
  2404. return entry.value;
  2405. }
  2406. };
  2407. /**
  2408. * Clear the cache
  2409. */
  2410. LRUProto.clear = function () {
  2411. this._list.clear();
  2412. this._map = {};
  2413. };
  2414. var kCSSColorTable = {
  2415. 'transparent': [0, 0, 0, 0],
  2416. 'aliceblue': [240, 248, 255, 1],
  2417. 'antiquewhite': [250, 235, 215, 1],
  2418. 'aqua': [0, 255, 255, 1],
  2419. 'aquamarine': [127, 255, 212, 1],
  2420. 'azure': [240, 255, 255, 1],
  2421. 'beige': [245, 245, 220, 1],
  2422. 'bisque': [255, 228, 196, 1],
  2423. 'black': [0, 0, 0, 1],
  2424. 'blanchedalmond': [255, 235, 205, 1],
  2425. 'blue': [0, 0, 255, 1],
  2426. 'blueviolet': [138, 43, 226, 1],
  2427. 'brown': [165, 42, 42, 1],
  2428. 'burlywood': [222, 184, 135, 1],
  2429. 'cadetblue': [95, 158, 160, 1],
  2430. 'chartreuse': [127, 255, 0, 1],
  2431. 'chocolate': [210, 105, 30, 1],
  2432. 'coral': [255, 127, 80, 1],
  2433. 'cornflowerblue': [100, 149, 237, 1],
  2434. 'cornsilk': [255, 248, 220, 1],
  2435. 'crimson': [220, 20, 60, 1],
  2436. 'cyan': [0, 255, 255, 1],
  2437. 'darkblue': [0, 0, 139, 1],
  2438. 'darkcyan': [0, 139, 139, 1],
  2439. 'darkgoldenrod': [184, 134, 11, 1],
  2440. 'darkgray': [169, 169, 169, 1],
  2441. 'darkgreen': [0, 100, 0, 1],
  2442. 'darkgrey': [169, 169, 169, 1],
  2443. 'darkkhaki': [189, 183, 107, 1],
  2444. 'darkmagenta': [139, 0, 139, 1],
  2445. 'darkolivegreen': [85, 107, 47, 1],
  2446. 'darkorange': [255, 140, 0, 1],
  2447. 'darkorchid': [153, 50, 204, 1],
  2448. 'darkred': [139, 0, 0, 1],
  2449. 'darksalmon': [233, 150, 122, 1],
  2450. 'darkseagreen': [143, 188, 143, 1],
  2451. 'darkslateblue': [72, 61, 139, 1],
  2452. 'darkslategray': [47, 79, 79, 1],
  2453. 'darkslategrey': [47, 79, 79, 1],
  2454. 'darkturquoise': [0, 206, 209, 1],
  2455. 'darkviolet': [148, 0, 211, 1],
  2456. 'deeppink': [255, 20, 147, 1],
  2457. 'deepskyblue': [0, 191, 255, 1],
  2458. 'dimgray': [105, 105, 105, 1],
  2459. 'dimgrey': [105, 105, 105, 1],
  2460. 'dodgerblue': [30, 144, 255, 1],
  2461. 'firebrick': [178, 34, 34, 1],
  2462. 'floralwhite': [255, 250, 240, 1],
  2463. 'forestgreen': [34, 139, 34, 1],
  2464. 'fuchsia': [255, 0, 255, 1],
  2465. 'gainsboro': [220, 220, 220, 1],
  2466. 'ghostwhite': [248, 248, 255, 1],
  2467. 'gold': [255, 215, 0, 1],
  2468. 'goldenrod': [218, 165, 32, 1],
  2469. 'gray': [128, 128, 128, 1],
  2470. 'green': [0, 128, 0, 1],
  2471. 'greenyellow': [173, 255, 47, 1],
  2472. 'grey': [128, 128, 128, 1],
  2473. 'honeydew': [240, 255, 240, 1],
  2474. 'hotpink': [255, 105, 180, 1],
  2475. 'indianred': [205, 92, 92, 1],
  2476. 'indigo': [75, 0, 130, 1],
  2477. 'ivory': [255, 255, 240, 1],
  2478. 'khaki': [240, 230, 140, 1],
  2479. 'lavender': [230, 230, 250, 1],
  2480. 'lavenderblush': [255, 240, 245, 1],
  2481. 'lawngreen': [124, 252, 0, 1],
  2482. 'lemonchiffon': [255, 250, 205, 1],
  2483. 'lightblue': [173, 216, 230, 1],
  2484. 'lightcoral': [240, 128, 128, 1],
  2485. 'lightcyan': [224, 255, 255, 1],
  2486. 'lightgoldenrodyellow': [250, 250, 210, 1],
  2487. 'lightgray': [211, 211, 211, 1],
  2488. 'lightgreen': [144, 238, 144, 1],
  2489. 'lightgrey': [211, 211, 211, 1],
  2490. 'lightpink': [255, 182, 193, 1],
  2491. 'lightsalmon': [255, 160, 122, 1],
  2492. 'lightseagreen': [32, 178, 170, 1],
  2493. 'lightskyblue': [135, 206, 250, 1],
  2494. 'lightslategray': [119, 136, 153, 1],
  2495. 'lightslategrey': [119, 136, 153, 1],
  2496. 'lightsteelblue': [176, 196, 222, 1],
  2497. 'lightyellow': [255, 255, 224, 1],
  2498. 'lime': [0, 255, 0, 1],
  2499. 'limegreen': [50, 205, 50, 1],
  2500. 'linen': [250, 240, 230, 1],
  2501. 'magenta': [255, 0, 255, 1],
  2502. 'maroon': [128, 0, 0, 1],
  2503. 'mediumaquamarine': [102, 205, 170, 1],
  2504. 'mediumblue': [0, 0, 205, 1],
  2505. 'mediumorchid': [186, 85, 211, 1],
  2506. 'mediumpurple': [147, 112, 219, 1],
  2507. 'mediumseagreen': [60, 179, 113, 1],
  2508. 'mediumslateblue': [123, 104, 238, 1],
  2509. 'mediumspringgreen': [0, 250, 154, 1],
  2510. 'mediumturquoise': [72, 209, 204, 1],
  2511. 'mediumvioletred': [199, 21, 133, 1],
  2512. 'midnightblue': [25, 25, 112, 1],
  2513. 'mintcream': [245, 255, 250, 1],
  2514. 'mistyrose': [255, 228, 225, 1],
  2515. 'moccasin': [255, 228, 181, 1],
  2516. 'navajowhite': [255, 222, 173, 1],
  2517. 'navy': [0, 0, 128, 1],
  2518. 'oldlace': [253, 245, 230, 1],
  2519. 'olive': [128, 128, 0, 1],
  2520. 'olivedrab': [107, 142, 35, 1],
  2521. 'orange': [255, 165, 0, 1],
  2522. 'orangered': [255, 69, 0, 1],
  2523. 'orchid': [218, 112, 214, 1],
  2524. 'palegoldenrod': [238, 232, 170, 1],
  2525. 'palegreen': [152, 251, 152, 1],
  2526. 'paleturquoise': [175, 238, 238, 1],
  2527. 'palevioletred': [219, 112, 147, 1],
  2528. 'papayawhip': [255, 239, 213, 1],
  2529. 'peachpuff': [255, 218, 185, 1],
  2530. 'peru': [205, 133, 63, 1],
  2531. 'pink': [255, 192, 203, 1],
  2532. 'plum': [221, 160, 221, 1],
  2533. 'powderblue': [176, 224, 230, 1],
  2534. 'purple': [128, 0, 128, 1],
  2535. 'red': [255, 0, 0, 1],
  2536. 'rosybrown': [188, 143, 143, 1],
  2537. 'royalblue': [65, 105, 225, 1],
  2538. 'saddlebrown': [139, 69, 19, 1],
  2539. 'salmon': [250, 128, 114, 1],
  2540. 'sandybrown': [244, 164, 96, 1],
  2541. 'seagreen': [46, 139, 87, 1],
  2542. 'seashell': [255, 245, 238, 1],
  2543. 'sienna': [160, 82, 45, 1],
  2544. 'silver': [192, 192, 192, 1],
  2545. 'skyblue': [135, 206, 235, 1],
  2546. 'slateblue': [106, 90, 205, 1],
  2547. 'slategray': [112, 128, 144, 1],
  2548. 'slategrey': [112, 128, 144, 1],
  2549. 'snow': [255, 250, 250, 1],
  2550. 'springgreen': [0, 255, 127, 1],
  2551. 'steelblue': [70, 130, 180, 1],
  2552. 'tan': [210, 180, 140, 1],
  2553. 'teal': [0, 128, 128, 1],
  2554. 'thistle': [216, 191, 216, 1],
  2555. 'tomato': [255, 99, 71, 1],
  2556. 'turquoise': [64, 224, 208, 1],
  2557. 'violet': [238, 130, 238, 1],
  2558. 'wheat': [245, 222, 179, 1],
  2559. 'white': [255, 255, 255, 1],
  2560. 'whitesmoke': [245, 245, 245, 1],
  2561. 'yellow': [255, 255, 0, 1],
  2562. 'yellowgreen': [154, 205, 50, 1]
  2563. };
  2564. function clampCssByte(i) {
  2565. // Clamp to integer 0 .. 255.
  2566. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  2567. return i < 0 ? 0 : i > 255 ? 255 : i;
  2568. }
  2569. function clampCssAngle(i) {
  2570. // Clamp to integer 0 .. 360.
  2571. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  2572. return i < 0 ? 0 : i > 360 ? 360 : i;
  2573. }
  2574. function clampCssFloat(f) {
  2575. // Clamp to float 0.0 .. 1.0.
  2576. return f < 0 ? 0 : f > 1 ? 1 : f;
  2577. }
  2578. function parseCssInt(str) {
  2579. // int or percentage.
  2580. if (str.length && str.charAt(str.length - 1) === '%') {
  2581. return clampCssByte(parseFloat(str) / 100 * 255);
  2582. }
  2583. return clampCssByte(parseInt(str, 10));
  2584. }
  2585. function parseCssFloat(str) {
  2586. // float or percentage.
  2587. if (str.length && str.charAt(str.length - 1) === '%') {
  2588. return clampCssFloat(parseFloat(str) / 100);
  2589. }
  2590. return clampCssFloat(parseFloat(str));
  2591. }
  2592. function cssHueToRgb(m1, m2, h) {
  2593. if (h < 0) {
  2594. h += 1;
  2595. } else if (h > 1) {
  2596. h -= 1;
  2597. }
  2598. if (h * 6 < 1) {
  2599. return m1 + (m2 - m1) * h * 6;
  2600. }
  2601. if (h * 2 < 1) {
  2602. return m2;
  2603. }
  2604. if (h * 3 < 2) {
  2605. return m1 + (m2 - m1) * (2 / 3 - h) * 6;
  2606. }
  2607. return m1;
  2608. }
  2609. function lerpNumber(a, b, p) {
  2610. return a + (b - a) * p;
  2611. }
  2612. function setRgba(out, r, g, b, a) {
  2613. out[0] = r;
  2614. out[1] = g;
  2615. out[2] = b;
  2616. out[3] = a;
  2617. return out;
  2618. }
  2619. function copyRgba(out, a) {
  2620. out[0] = a[0];
  2621. out[1] = a[1];
  2622. out[2] = a[2];
  2623. out[3] = a[3];
  2624. return out;
  2625. }
  2626. var colorCache = new LRU(20);
  2627. var lastRemovedArr = null;
  2628. function putToCache(colorStr, rgbaArr) {
  2629. // Reuse removed array
  2630. if (lastRemovedArr) {
  2631. copyRgba(lastRemovedArr, rgbaArr);
  2632. }
  2633. lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || rgbaArr.slice());
  2634. }
  2635. /**
  2636. * @param {string} colorStr
  2637. * @param {Array.<number>} out
  2638. * @return {Array.<number>}
  2639. * @memberOf module:zrender/util/color
  2640. */
  2641. function parse(colorStr, rgbaArr) {
  2642. if (!colorStr) {
  2643. return;
  2644. }
  2645. rgbaArr = rgbaArr || [];
  2646. var cached = colorCache.get(colorStr);
  2647. if (cached) {
  2648. return copyRgba(rgbaArr, cached);
  2649. } // colorStr may be not string
  2650. colorStr = colorStr + ''; // Remove all whitespace, not compliant, but should just be more accepting.
  2651. var str = colorStr.replace(/ /g, '').toLowerCase(); // Color keywords (and transparent) lookup.
  2652. if (str in kCSSColorTable) {
  2653. copyRgba(rgbaArr, kCSSColorTable[str]);
  2654. putToCache(colorStr, rgbaArr);
  2655. return rgbaArr;
  2656. } // #abc and #abc123 syntax.
  2657. if (str.charAt(0) === '#') {
  2658. if (str.length === 4) {
  2659. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  2660. if (!(iv >= 0 && iv <= 0xfff)) {
  2661. setRgba(rgbaArr, 0, 0, 0, 1);
  2662. return; // Covers NaN.
  2663. }
  2664. setRgba(rgbaArr, (iv & 0xf00) >> 4 | (iv & 0xf00) >> 8, iv & 0xf0 | (iv & 0xf0) >> 4, iv & 0xf | (iv & 0xf) << 4, 1);
  2665. putToCache(colorStr, rgbaArr);
  2666. return rgbaArr;
  2667. } else if (str.length === 7) {
  2668. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  2669. if (!(iv >= 0 && iv <= 0xffffff)) {
  2670. setRgba(rgbaArr, 0, 0, 0, 1);
  2671. return; // Covers NaN.
  2672. }
  2673. setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, 1);
  2674. putToCache(colorStr, rgbaArr);
  2675. return rgbaArr;
  2676. }
  2677. return;
  2678. }
  2679. var op = str.indexOf('('),
  2680. ep = str.indexOf(')');
  2681. if (op !== -1 && ep + 1 === str.length) {
  2682. var fname = str.substr(0, op);
  2683. var params = str.substr(op + 1, ep - (op + 1)).split(',');
  2684. var alpha = 1; // To allow case fallthrough.
  2685. switch (fname) {
  2686. case 'rgba':
  2687. if (params.length !== 4) {
  2688. setRgba(rgbaArr, 0, 0, 0, 1);
  2689. return;
  2690. }
  2691. alpha = parseCssFloat(params.pop());
  2692. // jshint ignore:line
  2693. // Fall through.
  2694. case 'rgb':
  2695. if (params.length !== 3) {
  2696. setRgba(rgbaArr, 0, 0, 0, 1);
  2697. return;
  2698. }
  2699. setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), alpha);
  2700. putToCache(colorStr, rgbaArr);
  2701. return rgbaArr;
  2702. case 'hsla':
  2703. if (params.length !== 4) {
  2704. setRgba(rgbaArr, 0, 0, 0, 1);
  2705. return;
  2706. }
  2707. params[3] = parseCssFloat(params[3]);
  2708. hsla2rgba(params, rgbaArr);
  2709. putToCache(colorStr, rgbaArr);
  2710. return rgbaArr;
  2711. case 'hsl':
  2712. if (params.length !== 3) {
  2713. setRgba(rgbaArr, 0, 0, 0, 1);
  2714. return;
  2715. }
  2716. hsla2rgba(params, rgbaArr);
  2717. putToCache(colorStr, rgbaArr);
  2718. return rgbaArr;
  2719. default:
  2720. return;
  2721. }
  2722. }
  2723. setRgba(rgbaArr, 0, 0, 0, 1);
  2724. return;
  2725. }
  2726. /**
  2727. * @param {Array.<number>} hsla
  2728. * @param {Array.<number>} rgba
  2729. * @return {Array.<number>} rgba
  2730. */
  2731. function hsla2rgba(hsla, rgba) {
  2732. var h = (parseFloat(hsla[0]) % 360 + 360) % 360 / 360; // 0 .. 1
  2733. // NOTE(deanm): According to the CSS spec s/l should only be
  2734. // percentages, but we don't bother and let float or percentage.
  2735. var s = parseCssFloat(hsla[1]);
  2736. var l = parseCssFloat(hsla[2]);
  2737. var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
  2738. var m1 = l * 2 - m2;
  2739. rgba = rgba || [];
  2740. setRgba(rgba, clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), clampCssByte(cssHueToRgb(m1, m2, h) * 255), clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 1);
  2741. if (hsla.length === 4) {
  2742. rgba[3] = hsla[3];
  2743. }
  2744. return rgba;
  2745. }
  2746. /**
  2747. * @param {Array.<number>} rgba
  2748. * @return {Array.<number>} hsla
  2749. */
  2750. function rgba2hsla(rgba) {
  2751. if (!rgba) {
  2752. return;
  2753. } // RGB from 0 to 255
  2754. var R = rgba[0] / 255;
  2755. var G = rgba[1] / 255;
  2756. var B = rgba[2] / 255;
  2757. var vMin = Math.min(R, G, B); // Min. value of RGB
  2758. var vMax = Math.max(R, G, B); // Max. value of RGB
  2759. var delta = vMax - vMin; // Delta RGB value
  2760. var L = (vMax + vMin) / 2;
  2761. var H;
  2762. var S; // HSL results from 0 to 1
  2763. if (delta === 0) {
  2764. H = 0;
  2765. S = 0;
  2766. } else {
  2767. if (L < 0.5) {
  2768. S = delta / (vMax + vMin);
  2769. } else {
  2770. S = delta / (2 - vMax - vMin);
  2771. }
  2772. var deltaR = ((vMax - R) / 6 + delta / 2) / delta;
  2773. var deltaG = ((vMax - G) / 6 + delta / 2) / delta;
  2774. var deltaB = ((vMax - B) / 6 + delta / 2) / delta;
  2775. if (R === vMax) {
  2776. H = deltaB - deltaG;
  2777. } else if (G === vMax) {
  2778. H = 1 / 3 + deltaR - deltaB;
  2779. } else if (B === vMax) {
  2780. H = 2 / 3 + deltaG - deltaR;
  2781. }
  2782. if (H < 0) {
  2783. H += 1;
  2784. }
  2785. if (H > 1) {
  2786. H -= 1;
  2787. }
  2788. }
  2789. var hsla = [H * 360, S, L];
  2790. if (rgba[3] != null) {
  2791. hsla.push(rgba[3]);
  2792. }
  2793. return hsla;
  2794. }
  2795. /**
  2796. * @param {string} color
  2797. * @param {number} level
  2798. * @return {string}
  2799. * @memberOf module:zrender/util/color
  2800. */
  2801. function lift(color, level) {
  2802. var colorArr = parse(color);
  2803. if (colorArr) {
  2804. for (var i = 0; i < 3; i++) {
  2805. if (level < 0) {
  2806. colorArr[i] = colorArr[i] * (1 - level) | 0;
  2807. } else {
  2808. colorArr[i] = (255 - colorArr[i]) * level + colorArr[i] | 0;
  2809. }
  2810. }
  2811. return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
  2812. }
  2813. }
  2814. /**
  2815. * @param {string} color
  2816. * @return {string}
  2817. * @memberOf module:zrender/util/color
  2818. */
  2819. function toHex(color) {
  2820. var colorArr = parse(color);
  2821. if (colorArr) {
  2822. return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + +colorArr[2]).toString(16).slice(1);
  2823. }
  2824. }
  2825. /**
  2826. * Map value to color. Faster than lerp methods because color is represented by rgba array.
  2827. * @param {number} normalizedValue A float between 0 and 1.
  2828. * @param {Array.<Array.<number>>} colors List of rgba color array
  2829. * @param {Array.<number>} [out] Mapped gba color array
  2830. * @return {Array.<number>} will be null/undefined if input illegal.
  2831. */
  2832. function fastLerp(normalizedValue, colors, out) {
  2833. if (!(colors && colors.length) || !(normalizedValue >= 0 && normalizedValue <= 1)) {
  2834. return;
  2835. }
  2836. out = out || [];
  2837. var value = normalizedValue * (colors.length - 1);
  2838. var leftIndex = Math.floor(value);
  2839. var rightIndex = Math.ceil(value);
  2840. var leftColor = colors[leftIndex];
  2841. var rightColor = colors[rightIndex];
  2842. var dv = value - leftIndex;
  2843. out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));
  2844. out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));
  2845. out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));
  2846. out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));
  2847. return out;
  2848. }
  2849. /**
  2850. * @deprecated
  2851. */
  2852. var fastMapToColor = fastLerp;
  2853. /**
  2854. * @param {number} normalizedValue A float between 0 and 1.
  2855. * @param {Array.<string>} colors Color list.
  2856. * @param {boolean=} fullOutput Default false.
  2857. * @return {(string|Object)} Result color. If fullOutput,
  2858. * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},
  2859. * @memberOf module:zrender/util/color
  2860. */
  2861. function lerp$1(normalizedValue, colors, fullOutput) {
  2862. if (!(colors && colors.length) || !(normalizedValue >= 0 && normalizedValue <= 1)) {
  2863. return;
  2864. }
  2865. var value = normalizedValue * (colors.length - 1);
  2866. var leftIndex = Math.floor(value);
  2867. var rightIndex = Math.ceil(value);
  2868. var leftColor = parse(colors[leftIndex]);
  2869. var rightColor = parse(colors[rightIndex]);
  2870. var dv = value - leftIndex;
  2871. var color = stringify([clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))], 'rgba');
  2872. return fullOutput ? {
  2873. color: color,
  2874. leftIndex: leftIndex,
  2875. rightIndex: rightIndex,
  2876. value: value
  2877. } : color;
  2878. }
  2879. /**
  2880. * @deprecated
  2881. */
  2882. var mapToColor = lerp$1;
  2883. /**
  2884. * @param {string} color
  2885. * @param {number=} h 0 ~ 360, ignore when null.
  2886. * @param {number=} s 0 ~ 1, ignore when null.
  2887. * @param {number=} l 0 ~ 1, ignore when null.
  2888. * @return {string} Color string in rgba format.
  2889. * @memberOf module:zrender/util/color
  2890. */
  2891. function modifyHSL(color, h, s, l) {
  2892. color = parse(color);
  2893. if (color) {
  2894. color = rgba2hsla(color);
  2895. h != null && (color[0] = clampCssAngle(h));
  2896. s != null && (color[1] = parseCssFloat(s));
  2897. l != null && (color[2] = parseCssFloat(l));
  2898. return stringify(hsla2rgba(color), 'rgba');
  2899. }
  2900. }
  2901. /**
  2902. * @param {string} color
  2903. * @param {number=} alpha 0 ~ 1
  2904. * @return {string} Color string in rgba format.
  2905. * @memberOf module:zrender/util/color
  2906. */
  2907. function modifyAlpha(color, alpha) {
  2908. color = parse(color);
  2909. if (color && alpha != null) {
  2910. color[3] = clampCssFloat(alpha);
  2911. return stringify(color, 'rgba');
  2912. }
  2913. }
  2914. /**
  2915. * @param {Array.<number>} arrColor like [12,33,44,0.4]
  2916. * @param {string} type 'rgba', 'hsva', ...
  2917. * @return {string} Result color. (If input illegal, return undefined).
  2918. */
  2919. function stringify(arrColor, type) {
  2920. if (!arrColor || !arrColor.length) {
  2921. return;
  2922. }
  2923. var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];
  2924. if (type === 'rgba' || type === 'hsva' || type === 'hsla') {
  2925. colorStr += ',' + arrColor[3];
  2926. }
  2927. return type + '(' + colorStr + ')';
  2928. }
  2929. var color = (Object.freeze || Object)({
  2930. parse: parse,
  2931. lift: lift,
  2932. toHex: toHex,
  2933. fastLerp: fastLerp,
  2934. fastMapToColor: fastMapToColor,
  2935. lerp: lerp$1,
  2936. mapToColor: mapToColor,
  2937. modifyHSL: modifyHSL,
  2938. modifyAlpha: modifyAlpha,
  2939. stringify: stringify
  2940. });
  2941. /**
  2942. * @module echarts/animation/Animator
  2943. */
  2944. var arraySlice = Array.prototype.slice;
  2945. function defaultGetter(target, key) {
  2946. return target[key];
  2947. }
  2948. function defaultSetter(target, key, value) {
  2949. target[key] = value;
  2950. }
  2951. /**
  2952. * @param {number} p0
  2953. * @param {number} p1
  2954. * @param {number} percent
  2955. * @return {number}
  2956. */
  2957. function interpolateNumber(p0, p1, percent) {
  2958. return (p1 - p0) * percent + p0;
  2959. }
  2960. /**
  2961. * @param {string} p0
  2962. * @param {string} p1
  2963. * @param {number} percent
  2964. * @return {string}
  2965. */
  2966. function interpolateString(p0, p1, percent) {
  2967. return percent > 0.5 ? p1 : p0;
  2968. }
  2969. /**
  2970. * @param {Array} p0
  2971. * @param {Array} p1
  2972. * @param {number} percent
  2973. * @param {Array} out
  2974. * @param {number} arrDim
  2975. */
  2976. function interpolateArray(p0, p1, percent, out, arrDim) {
  2977. var len = p0.length;
  2978. if (arrDim == 1) {
  2979. for (var i = 0; i < len; i++) {
  2980. out[i] = interpolateNumber(p0[i], p1[i], percent);
  2981. }
  2982. } else {
  2983. var len2 = len && p0[0].length;
  2984. for (var i = 0; i < len; i++) {
  2985. for (var j = 0; j < len2; j++) {
  2986. out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent);
  2987. }
  2988. }
  2989. }
  2990. } // arr0 is source array, arr1 is target array.
  2991. // Do some preprocess to avoid error happened when interpolating from arr0 to arr1
  2992. function fillArr(arr0, arr1, arrDim) {
  2993. var arr0Len = arr0.length;
  2994. var arr1Len = arr1.length;
  2995. if (arr0Len !== arr1Len) {
  2996. // FIXME Not work for TypedArray
  2997. var isPreviousLarger = arr0Len > arr1Len;
  2998. if (isPreviousLarger) {
  2999. // Cut the previous
  3000. arr0.length = arr1Len;
  3001. } else {
  3002. // Fill the previous
  3003. for (var i = arr0Len; i < arr1Len; i++) {
  3004. arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]));
  3005. }
  3006. }
  3007. } // Handling NaN value
  3008. var len2 = arr0[0] && arr0[0].length;
  3009. for (var i = 0; i < arr0.length; i++) {
  3010. if (arrDim === 1) {
  3011. if (isNaN(arr0[i])) {
  3012. arr0[i] = arr1[i];
  3013. }
  3014. } else {
  3015. for (var j = 0; j < len2; j++) {
  3016. if (isNaN(arr0[i][j])) {
  3017. arr0[i][j] = arr1[i][j];
  3018. }
  3019. }
  3020. }
  3021. }
  3022. }
  3023. /**
  3024. * @param {Array} arr0
  3025. * @param {Array} arr1
  3026. * @param {number} arrDim
  3027. * @return {boolean}
  3028. */
  3029. function isArraySame(arr0, arr1, arrDim) {
  3030. if (arr0 === arr1) {
  3031. return true;
  3032. }
  3033. var len = arr0.length;
  3034. if (len !== arr1.length) {
  3035. return false;
  3036. }
  3037. if (arrDim === 1) {
  3038. for (var i = 0; i < len; i++) {
  3039. if (arr0[i] !== arr1[i]) {
  3040. return false;
  3041. }
  3042. }
  3043. } else {
  3044. var len2 = arr0[0].length;
  3045. for (var i = 0; i < len; i++) {
  3046. for (var j = 0; j < len2; j++) {
  3047. if (arr0[i][j] !== arr1[i][j]) {
  3048. return false;
  3049. }
  3050. }
  3051. }
  3052. }
  3053. return true;
  3054. }
  3055. /**
  3056. * Catmull Rom interpolate array
  3057. * @param {Array} p0
  3058. * @param {Array} p1
  3059. * @param {Array} p2
  3060. * @param {Array} p3
  3061. * @param {number} t
  3062. * @param {number} t2
  3063. * @param {number} t3
  3064. * @param {Array} out
  3065. * @param {number} arrDim
  3066. */
  3067. function catmullRomInterpolateArray(p0, p1, p2, p3, t, t2, t3, out, arrDim) {
  3068. var len = p0.length;
  3069. if (arrDim == 1) {
  3070. for (var i = 0; i < len; i++) {
  3071. out[i] = catmullRomInterpolate(p0[i], p1[i], p2[i], p3[i], t, t2, t3);
  3072. }
  3073. } else {
  3074. var len2 = p0[0].length;
  3075. for (var i = 0; i < len; i++) {
  3076. for (var j = 0; j < len2; j++) {
  3077. out[i][j] = catmullRomInterpolate(p0[i][j], p1[i][j], p2[i][j], p3[i][j], t, t2, t3);
  3078. }
  3079. }
  3080. }
  3081. }
  3082. /**
  3083. * Catmull Rom interpolate number
  3084. * @param {number} p0
  3085. * @param {number} p1
  3086. * @param {number} p2
  3087. * @param {number} p3
  3088. * @param {number} t
  3089. * @param {number} t2
  3090. * @param {number} t3
  3091. * @return {number}
  3092. */
  3093. function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {
  3094. var v0 = (p2 - p0) * 0.5;
  3095. var v1 = (p3 - p1) * 0.5;
  3096. return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1;
  3097. }
  3098. function cloneValue(value) {
  3099. if (isArrayLike(value)) {
  3100. var len = value.length;
  3101. if (isArrayLike(value[0])) {
  3102. var ret = [];
  3103. for (var i = 0; i < len; i++) {
  3104. ret.push(arraySlice.call(value[i]));
  3105. }
  3106. return ret;
  3107. }
  3108. return arraySlice.call(value);
  3109. }
  3110. return value;
  3111. }
  3112. function rgba2String(rgba) {
  3113. rgba[0] = Math.floor(rgba[0]);
  3114. rgba[1] = Math.floor(rgba[1]);
  3115. rgba[2] = Math.floor(rgba[2]);
  3116. return 'rgba(' + rgba.join(',') + ')';
  3117. }
  3118. function getArrayDim(keyframes) {
  3119. var lastValue = keyframes[keyframes.length - 1].value;
  3120. return isArrayLike(lastValue && lastValue[0]) ? 2 : 1;
  3121. }
  3122. function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) {
  3123. var getter = animator._getter;
  3124. var setter = animator._setter;
  3125. var useSpline = easing === 'spline';
  3126. var trackLen = keyframes.length;
  3127. if (!trackLen) {
  3128. return;
  3129. } // Guess data type
  3130. var firstVal = keyframes[0].value;
  3131. var isValueArray = isArrayLike(firstVal);
  3132. var isValueColor = false;
  3133. var isValueString = false; // For vertices morphing
  3134. var arrDim = isValueArray ? getArrayDim(keyframes) : 0;
  3135. var trackMaxTime; // Sort keyframe as ascending
  3136. keyframes.sort(function (a, b) {
  3137. return a.time - b.time;
  3138. });
  3139. trackMaxTime = keyframes[trackLen - 1].time; // Percents of each keyframe
  3140. var kfPercents = []; // Value of each keyframe
  3141. var kfValues = [];
  3142. var prevValue = keyframes[0].value;
  3143. var isAllValueEqual = true;
  3144. for (var i = 0; i < trackLen; i++) {
  3145. kfPercents.push(keyframes[i].time / trackMaxTime); // Assume value is a color when it is a string
  3146. var value = keyframes[i].value; // Check if value is equal, deep check if value is array
  3147. if (!(isValueArray && isArraySame(value, prevValue, arrDim) || !isValueArray && value === prevValue)) {
  3148. isAllValueEqual = false;
  3149. }
  3150. prevValue = value; // Try converting a string to a color array
  3151. if (typeof value == 'string') {
  3152. var colorArray = parse(value);
  3153. if (colorArray) {
  3154. value = colorArray;
  3155. isValueColor = true;
  3156. } else {
  3157. isValueString = true;
  3158. }
  3159. }
  3160. kfValues.push(value);
  3161. }
  3162. if (!forceAnimate && isAllValueEqual) {
  3163. return;
  3164. }
  3165. var lastValue = kfValues[trackLen - 1]; // Polyfill array and NaN value
  3166. for (var i = 0; i < trackLen - 1; i++) {
  3167. if (isValueArray) {
  3168. fillArr(kfValues[i], lastValue, arrDim);
  3169. } else {
  3170. if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) {
  3171. kfValues[i] = lastValue;
  3172. }
  3173. }
  3174. }
  3175. isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim); // Cache the key of last frame to speed up when
  3176. // animation playback is sequency
  3177. var lastFrame = 0;
  3178. var lastFramePercent = 0;
  3179. var start;
  3180. var w;
  3181. var p0;
  3182. var p1;
  3183. var p2;
  3184. var p3;
  3185. if (isValueColor) {
  3186. var rgba = [0, 0, 0, 0];
  3187. }
  3188. var onframe = function (target, percent) {
  3189. // Find the range keyframes
  3190. // kf1-----kf2---------current--------kf3
  3191. // find kf2 and kf3 and do interpolation
  3192. var frame; // In the easing function like elasticOut, percent may less than 0
  3193. if (percent < 0) {
  3194. frame = 0;
  3195. } else if (percent < lastFramePercent) {
  3196. // Start from next key
  3197. // PENDING start from lastFrame ?
  3198. start = Math.min(lastFrame + 1, trackLen - 1);
  3199. for (frame = start; frame >= 0; frame--) {
  3200. if (kfPercents[frame] <= percent) {
  3201. break;
  3202. }
  3203. } // PENDING really need to do this ?
  3204. frame = Math.min(frame, trackLen - 2);
  3205. } else {
  3206. for (frame = lastFrame; frame < trackLen; frame++) {
  3207. if (kfPercents[frame] > percent) {
  3208. break;
  3209. }
  3210. }
  3211. frame = Math.min(frame - 1, trackLen - 2);
  3212. }
  3213. lastFrame = frame;
  3214. lastFramePercent = percent;
  3215. var range = kfPercents[frame + 1] - kfPercents[frame];
  3216. if (range === 0) {
  3217. return;
  3218. } else {
  3219. w = (percent - kfPercents[frame]) / range;
  3220. }
  3221. if (useSpline) {
  3222. p1 = kfValues[frame];
  3223. p0 = kfValues[frame === 0 ? frame : frame - 1];
  3224. p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1];
  3225. p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2];
  3226. if (isValueArray) {
  3227. catmullRomInterpolateArray(p0, p1, p2, p3, w, w * w, w * w * w, getter(target, propName), arrDim);
  3228. } else {
  3229. var value;
  3230. if (isValueColor) {
  3231. value = catmullRomInterpolateArray(p0, p1, p2, p3, w, w * w, w * w * w, rgba, 1);
  3232. value = rgba2String(rgba);
  3233. } else if (isValueString) {
  3234. // String is step(0.5)
  3235. return interpolateString(p1, p2, w);
  3236. } else {
  3237. value = catmullRomInterpolate(p0, p1, p2, p3, w, w * w, w * w * w);
  3238. }
  3239. setter(target, propName, value);
  3240. }
  3241. } else {
  3242. if (isValueArray) {
  3243. interpolateArray(kfValues[frame], kfValues[frame + 1], w, getter(target, propName), arrDim);
  3244. } else {
  3245. var value;
  3246. if (isValueColor) {
  3247. interpolateArray(kfValues[frame], kfValues[frame + 1], w, rgba, 1);
  3248. value = rgba2String(rgba);
  3249. } else if (isValueString) {
  3250. // String is step(0.5)
  3251. return interpolateString(kfValues[frame], kfValues[frame + 1], w);
  3252. } else {
  3253. value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w);
  3254. }
  3255. setter(target, propName, value);
  3256. }
  3257. }
  3258. };
  3259. var clip = new Clip({
  3260. target: animator._target,
  3261. life: trackMaxTime,
  3262. loop: animator._loop,
  3263. delay: animator._delay,
  3264. onframe: onframe,
  3265. ondestroy: oneTrackDone
  3266. });
  3267. if (easing && easing !== 'spline') {
  3268. clip.easing = easing;
  3269. }
  3270. return clip;
  3271. }
  3272. /**
  3273. * @alias module:zrender/animation/Animator
  3274. * @constructor
  3275. * @param {Object} target
  3276. * @param {boolean} loop
  3277. * @param {Function} getter
  3278. * @param {Function} setter
  3279. */
  3280. var Animator = function (target, loop, getter, setter) {
  3281. this._tracks = {};
  3282. this._target = target;
  3283. this._loop = loop || false;
  3284. this._getter = getter || defaultGetter;
  3285. this._setter = setter || defaultSetter;
  3286. this._clipCount = 0;
  3287. this._delay = 0;
  3288. this._doneList = [];
  3289. this._onframeList = [];
  3290. this._clipList = [];
  3291. };
  3292. Animator.prototype = {
  3293. /**
  3294. * 设置动画关键帧
  3295. * @param {number} time 关键帧时间,单位是ms
  3296. * @param {Object} props 关键帧的属性值,key-value表示
  3297. * @return {module:zrender/animation/Animator}
  3298. */
  3299. when: function (time
  3300. /* ms */
  3301. , props) {
  3302. var tracks = this._tracks;
  3303. for (var propName in props) {
  3304. if (!props.hasOwnProperty(propName)) {
  3305. continue;
  3306. }
  3307. if (!tracks[propName]) {
  3308. tracks[propName] = []; // Invalid value
  3309. var value = this._getter(this._target, propName);
  3310. if (value == null) {
  3311. // zrLog('Invalid property ' + propName);
  3312. continue;
  3313. } // If time is 0
  3314. // Then props is given initialize value
  3315. // Else
  3316. // Initialize value from current prop value
  3317. if (time !== 0) {
  3318. tracks[propName].push({
  3319. time: 0,
  3320. value: cloneValue(value)
  3321. });
  3322. }
  3323. }
  3324. tracks[propName].push({
  3325. time: time,
  3326. value: props[propName]
  3327. });
  3328. }
  3329. return this;
  3330. },
  3331. /**
  3332. * 添加动画每一帧的回调函数
  3333. * @param {Function} callback
  3334. * @return {module:zrender/animation/Animator}
  3335. */
  3336. during: function (callback) {
  3337. this._onframeList.push(callback);
  3338. return this;
  3339. },
  3340. pause: function () {
  3341. for (var i = 0; i < this._clipList.length; i++) {
  3342. this._clipList[i].pause();
  3343. }
  3344. this._paused = true;
  3345. },
  3346. resume: function () {
  3347. for (var i = 0; i < this._clipList.length; i++) {
  3348. this._clipList[i].resume();
  3349. }
  3350. this._paused = false;
  3351. },
  3352. isPaused: function () {
  3353. return !!this._paused;
  3354. },
  3355. _doneCallback: function () {
  3356. // Clear all tracks
  3357. this._tracks = {}; // Clear all clips
  3358. this._clipList.length = 0;
  3359. var doneList = this._doneList;
  3360. var len = doneList.length;
  3361. for (var i = 0; i < len; i++) {
  3362. doneList[i].call(this);
  3363. }
  3364. },
  3365. /**
  3366. * 开始执行动画
  3367. * @param {string|Function} [easing]
  3368. * 动画缓动函数,详见{@link module:zrender/animation/easing}
  3369. * @param {boolean} forceAnimate
  3370. * @return {module:zrender/animation/Animator}
  3371. */
  3372. start: function (easing, forceAnimate) {
  3373. var self = this;
  3374. var clipCount = 0;
  3375. var oneTrackDone = function () {
  3376. clipCount--;
  3377. if (!clipCount) {
  3378. self._doneCallback();
  3379. }
  3380. };
  3381. var lastClip;
  3382. for (var propName in this._tracks) {
  3383. if (!this._tracks.hasOwnProperty(propName)) {
  3384. continue;
  3385. }
  3386. var clip = createTrackClip(this, easing, oneTrackDone, this._tracks[propName], propName, forceAnimate);
  3387. if (clip) {
  3388. this._clipList.push(clip);
  3389. clipCount++; // If start after added to animation
  3390. if (this.animation) {
  3391. this.animation.addClip(clip);
  3392. }
  3393. lastClip = clip;
  3394. }
  3395. } // Add during callback on the last clip
  3396. if (lastClip) {
  3397. var oldOnFrame = lastClip.onframe;
  3398. lastClip.onframe = function (target, percent) {
  3399. oldOnFrame(target, percent);
  3400. for (var i = 0; i < self._onframeList.length; i++) {
  3401. self._onframeList[i](target, percent);
  3402. }
  3403. };
  3404. } // This optimization will help the case that in the upper application
  3405. // the view may be refreshed frequently, where animation will be
  3406. // called repeatly but nothing changed.
  3407. if (!clipCount) {
  3408. this._doneCallback();
  3409. }
  3410. return this;
  3411. },
  3412. /**
  3413. * 停止动画
  3414. * @param {boolean} forwardToLast If move to last frame before stop
  3415. */
  3416. stop: function (forwardToLast) {
  3417. var clipList = this._clipList;
  3418. var animation = this.animation;
  3419. for (var i = 0; i < clipList.length; i++) {
  3420. var clip = clipList[i];
  3421. if (forwardToLast) {
  3422. // Move to last frame before stop
  3423. clip.onframe(this._target, 1);
  3424. }
  3425. animation && animation.removeClip(clip);
  3426. }
  3427. clipList.length = 0;
  3428. },
  3429. /**
  3430. * 设置动画延迟开始的时间
  3431. * @param {number} time 单位ms
  3432. * @return {module:zrender/animation/Animator}
  3433. */
  3434. delay: function (time) {
  3435. this._delay = time;
  3436. return this;
  3437. },
  3438. /**
  3439. * 添加动画结束的回调
  3440. * @param {Function} cb
  3441. * @return {module:zrender/animation/Animator}
  3442. */
  3443. done: function (cb) {
  3444. if (cb) {
  3445. this._doneList.push(cb);
  3446. }
  3447. return this;
  3448. },
  3449. /**
  3450. * @return {Array.<module:zrender/animation/Clip>}
  3451. */
  3452. getClips: function () {
  3453. return this._clipList;
  3454. }
  3455. };
  3456. var dpr = 1; // If in browser environment
  3457. if (typeof window !== 'undefined') {
  3458. dpr = Math.max(window.devicePixelRatio || 1, 1);
  3459. }
  3460. /**
  3461. * config默认配置项
  3462. * @exports zrender/config
  3463. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  3464. */
  3465. /**
  3466. * debug日志选项:catchBrushException为true下有效
  3467. * 0 : 不生成debug数据,发布用
  3468. * 1 : 异常抛出,调试用
  3469. * 2 : 控制台输出,调试用
  3470. */
  3471. var debugMode = 0; // retina 屏幕优化
  3472. var devicePixelRatio = dpr;
  3473. var log = function () {};
  3474. if (debugMode === 1) {
  3475. log = function () {
  3476. for (var k in arguments) {
  3477. throw new Error(arguments[k]);
  3478. }
  3479. };
  3480. } else if (debugMode > 1) {
  3481. log = function () {
  3482. for (var k in arguments) {
  3483. console.log(arguments[k]);
  3484. }
  3485. };
  3486. }
  3487. var zrLog = log;
  3488. /**
  3489. * @alias modue:zrender/mixin/Animatable
  3490. * @constructor
  3491. */
  3492. var Animatable = function () {
  3493. /**
  3494. * @type {Array.<module:zrender/animation/Animator>}
  3495. * @readOnly
  3496. */
  3497. this.animators = [];
  3498. };
  3499. Animatable.prototype = {
  3500. constructor: Animatable,
  3501. /**
  3502. * 动画
  3503. *
  3504. * @param {string} path The path to fetch value from object, like 'a.b.c'.
  3505. * @param {boolean} [loop] Whether to loop animation.
  3506. * @return {module:zrender/animation/Animator}
  3507. * @example:
  3508. * el.animate('style', false)
  3509. * .when(1000, {x: 10} )
  3510. * .done(function(){ // Animation done })
  3511. * .start()
  3512. */
  3513. animate: function (path, loop) {
  3514. var target;
  3515. var animatingShape = false;
  3516. var el = this;
  3517. var zr = this.__zr;
  3518. if (path) {
  3519. var pathSplitted = path.split('.');
  3520. var prop = el; // If animating shape
  3521. animatingShape = pathSplitted[0] === 'shape';
  3522. for (var i = 0, l = pathSplitted.length; i < l; i++) {
  3523. if (!prop) {
  3524. continue;
  3525. }
  3526. prop = prop[pathSplitted[i]];
  3527. }
  3528. if (prop) {
  3529. target = prop;
  3530. }
  3531. } else {
  3532. target = el;
  3533. }
  3534. if (!target) {
  3535. zrLog('Property "' + path + '" is not existed in element ' + el.id);
  3536. return;
  3537. }
  3538. var animators = el.animators;
  3539. var animator = new Animator(target, loop);
  3540. animator.during(function (target) {
  3541. el.dirty(animatingShape);
  3542. }).done(function () {
  3543. // FIXME Animator will not be removed if use `Animator#stop` to stop animation
  3544. animators.splice(indexOf(animators, animator), 1);
  3545. });
  3546. animators.push(animator); // If animate after added to the zrender
  3547. if (zr) {
  3548. zr.animation.addAnimator(animator);
  3549. }
  3550. return animator;
  3551. },
  3552. /**
  3553. * 停止动画
  3554. * @param {boolean} forwardToLast If move to last frame before stop
  3555. */
  3556. stopAnimation: function (forwardToLast) {
  3557. var animators = this.animators;
  3558. var len = animators.length;
  3559. for (var i = 0; i < len; i++) {
  3560. animators[i].stop(forwardToLast);
  3561. }
  3562. animators.length = 0;
  3563. return this;
  3564. },
  3565. /**
  3566. * Caution: this method will stop previous animation.
  3567. * So do not use this method to one element twice before
  3568. * animation starts, unless you know what you are doing.
  3569. * @param {Object} target
  3570. * @param {number} [time=500] Time in ms
  3571. * @param {string} [easing='linear']
  3572. * @param {number} [delay=0]
  3573. * @param {Function} [callback]
  3574. * @param {Function} [forceAnimate] Prevent stop animation and callback
  3575. * immediently when target values are the same as current values.
  3576. *
  3577. * @example
  3578. * // Animate position
  3579. * el.animateTo({
  3580. * position: [10, 10]
  3581. * }, function () { // done })
  3582. *
  3583. * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing
  3584. * el.animateTo({
  3585. * shape: {
  3586. * width: 500
  3587. * },
  3588. * style: {
  3589. * fill: 'red'
  3590. * }
  3591. * position: [10, 10]
  3592. * }, 100, 100, 'cubicOut', function () { // done })
  3593. */
  3594. // TODO Return animation key
  3595. animateTo: function (target, time, delay, easing, callback, forceAnimate) {
  3596. // animateTo(target, time, easing, callback);
  3597. if (isString(delay)) {
  3598. callback = easing;
  3599. easing = delay;
  3600. delay = 0;
  3601. } // animateTo(target, time, delay, callback);
  3602. else if (isFunction(easing)) {
  3603. callback = easing;
  3604. easing = 'linear';
  3605. delay = 0;
  3606. } // animateTo(target, time, callback);
  3607. else if (isFunction(delay)) {
  3608. callback = delay;
  3609. delay = 0;
  3610. } // animateTo(target, callback)
  3611. else if (isFunction(time)) {
  3612. callback = time;
  3613. time = 500;
  3614. } // animateTo(target)
  3615. else if (!time) {
  3616. time = 500;
  3617. } // Stop all previous animations
  3618. this.stopAnimation();
  3619. this._animateToShallow('', this, target, time, delay); // Animators may be removed immediately after start
  3620. // if there is nothing to animate
  3621. var animators = this.animators.slice();
  3622. var count = animators.length;
  3623. function done() {
  3624. count--;
  3625. if (!count) {
  3626. callback && callback();
  3627. }
  3628. } // No animators. This should be checked before animators[i].start(),
  3629. // because 'done' may be executed immediately if no need to animate.
  3630. if (!count) {
  3631. callback && callback();
  3632. } // Start after all animators created
  3633. // Incase any animator is done immediately when all animation properties are not changed
  3634. for (var i = 0; i < animators.length; i++) {
  3635. animators[i].done(done).start(easing, forceAnimate);
  3636. }
  3637. },
  3638. /**
  3639. * @private
  3640. * @param {string} path=''
  3641. * @param {Object} source=this
  3642. * @param {Object} target
  3643. * @param {number} [time=500]
  3644. * @param {number} [delay=0]
  3645. *
  3646. * @example
  3647. * // Animate position
  3648. * el._animateToShallow({
  3649. * position: [10, 10]
  3650. * })
  3651. *
  3652. * // Animate shape, style and position in 100ms, delayed 100ms
  3653. * el._animateToShallow({
  3654. * shape: {
  3655. * width: 500
  3656. * },
  3657. * style: {
  3658. * fill: 'red'
  3659. * }
  3660. * position: [10, 10]
  3661. * }, 100, 100)
  3662. */
  3663. _animateToShallow: function (path, source, target, time, delay) {
  3664. var objShallow = {};
  3665. var propertyCount = 0;
  3666. for (var name in target) {
  3667. if (!target.hasOwnProperty(name)) {
  3668. continue;
  3669. }
  3670. if (source[name] != null) {
  3671. if (isObject(target[name]) && !isArrayLike(target[name])) {
  3672. this._animateToShallow(path ? path + '.' + name : name, source[name], target[name], time, delay);
  3673. } else {
  3674. objShallow[name] = target[name];
  3675. propertyCount++;
  3676. }
  3677. } else if (target[name] != null) {
  3678. // Attr directly if not has property
  3679. // FIXME, if some property not needed for element ?
  3680. if (!path) {
  3681. this.attr(name, target[name]);
  3682. } else {
  3683. // Shape or style
  3684. var props = {};
  3685. props[path] = {};
  3686. props[path][name] = target[name];
  3687. this.attr(props);
  3688. }
  3689. }
  3690. }
  3691. if (propertyCount > 0) {
  3692. this.animate(path, false).when(time == null ? 500 : time, objShallow).delay(delay || 0);
  3693. }
  3694. return this;
  3695. }
  3696. };
  3697. /**
  3698. * @alias module:zrender/Element
  3699. * @constructor
  3700. * @extends {module:zrender/mixin/Animatable}
  3701. * @extends {module:zrender/mixin/Transformable}
  3702. * @extends {module:zrender/mixin/Eventful}
  3703. */
  3704. var Element = function (opts) {
  3705. // jshint ignore:line
  3706. Transformable.call(this, opts);
  3707. Eventful.call(this, opts);
  3708. Animatable.call(this, opts);
  3709. /**
  3710. * 画布元素ID
  3711. * @type {string}
  3712. */
  3713. this.id = opts.id || guid();
  3714. };
  3715. Element.prototype = {
  3716. /**
  3717. * 元素类型
  3718. * Element type
  3719. * @type {string}
  3720. */
  3721. type: 'element',
  3722. /**
  3723. * 元素名字
  3724. * Element name
  3725. * @type {string}
  3726. */
  3727. name: '',
  3728. /**
  3729. * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值
  3730. * ZRender instance will be assigned when element is associated with zrender
  3731. * @name module:/zrender/Element#__zr
  3732. * @type {module:zrender/ZRender}
  3733. */
  3734. __zr: null,
  3735. /**
  3736. * 图形是否忽略,为true时忽略图形的绘制以及事件触发
  3737. * If ignore drawing and events of the element object
  3738. * @name module:/zrender/Element#ignore
  3739. * @type {boolean}
  3740. * @default false
  3741. */
  3742. ignore: false,
  3743. /**
  3744. * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪
  3745. * 该路径会继承被裁减对象的变换
  3746. * @type {module:zrender/graphic/Path}
  3747. * @see http://www.w3.org/TR/2dcontext/#clipping-region
  3748. * @readOnly
  3749. */
  3750. clipPath: null,
  3751. /**
  3752. * Drift element
  3753. * @param {number} dx dx on the global space
  3754. * @param {number} dy dy on the global space
  3755. */
  3756. drift: function (dx, dy) {
  3757. switch (this.draggable) {
  3758. case 'horizontal':
  3759. dy = 0;
  3760. break;
  3761. case 'vertical':
  3762. dx = 0;
  3763. break;
  3764. }
  3765. var m = this.transform;
  3766. if (!m) {
  3767. m = this.transform = [1, 0, 0, 1, 0, 0];
  3768. }
  3769. m[4] += dx;
  3770. m[5] += dy;
  3771. this.decomposeTransform();
  3772. this.dirty(false);
  3773. },
  3774. /**
  3775. * Hook before update
  3776. */
  3777. beforeUpdate: function () {},
  3778. /**
  3779. * Hook after update
  3780. */
  3781. afterUpdate: function () {},
  3782. /**
  3783. * Update each frame
  3784. */
  3785. update: function () {
  3786. this.updateTransform();
  3787. },
  3788. /**
  3789. * @param {Function} cb
  3790. * @param {} context
  3791. */
  3792. traverse: function (cb, context) {},
  3793. /**
  3794. * @protected
  3795. */
  3796. attrKV: function (key, value) {
  3797. if (key === 'position' || key === 'scale' || key === 'origin') {
  3798. // Copy the array
  3799. if (value) {
  3800. var target = this[key];
  3801. if (!target) {
  3802. target = this[key] = [];
  3803. }
  3804. target[0] = value[0];
  3805. target[1] = value[1];
  3806. }
  3807. } else {
  3808. this[key] = value;
  3809. }
  3810. },
  3811. /**
  3812. * Hide the element
  3813. */
  3814. hide: function () {
  3815. this.ignore = true;
  3816. this.__zr && this.__zr.refresh();
  3817. },
  3818. /**
  3819. * Show the element
  3820. */
  3821. show: function () {
  3822. this.ignore = false;
  3823. this.__zr && this.__zr.refresh();
  3824. },
  3825. /**
  3826. * @param {string|Object} key
  3827. * @param {*} value
  3828. */
  3829. attr: function (key, value) {
  3830. if (typeof key === 'string') {
  3831. this.attrKV(key, value);
  3832. } else if (isObject(key)) {
  3833. for (var name in key) {
  3834. if (key.hasOwnProperty(name)) {
  3835. this.attrKV(name, key[name]);
  3836. }
  3837. }
  3838. }
  3839. this.dirty(false);
  3840. return this;
  3841. },
  3842. /**
  3843. * @param {module:zrender/graphic/Path} clipPath
  3844. */
  3845. setClipPath: function (clipPath) {
  3846. var zr = this.__zr;
  3847. if (zr) {
  3848. clipPath.addSelfToZr(zr);
  3849. } // Remove previous clip path
  3850. if (this.clipPath && this.clipPath !== clipPath) {
  3851. this.removeClipPath();
  3852. }
  3853. this.clipPath = clipPath;
  3854. clipPath.__zr = zr;
  3855. clipPath.__clipTarget = this;
  3856. this.dirty(false);
  3857. },
  3858. /**
  3859. */
  3860. removeClipPath: function () {
  3861. var clipPath = this.clipPath;
  3862. if (clipPath) {
  3863. if (clipPath.__zr) {
  3864. clipPath.removeSelfFromZr(clipPath.__zr);
  3865. }
  3866. clipPath.__zr = null;
  3867. clipPath.__clipTarget = null;
  3868. this.clipPath = null;
  3869. this.dirty(false);
  3870. }
  3871. },
  3872. /**
  3873. * Add self from zrender instance.
  3874. * Not recursively because it will be invoked when element added to storage.
  3875. * @param {module:zrender/ZRender} zr
  3876. */
  3877. addSelfToZr: function (zr) {
  3878. this.__zr = zr; // 添加动画
  3879. var animators = this.animators;
  3880. if (animators) {
  3881. for (var i = 0; i < animators.length; i++) {
  3882. zr.animation.addAnimator(animators[i]);
  3883. }
  3884. }
  3885. if (this.clipPath) {
  3886. this.clipPath.addSelfToZr(zr);
  3887. }
  3888. },
  3889. /**
  3890. * Remove self from zrender instance.
  3891. * Not recursively because it will be invoked when element added to storage.
  3892. * @param {module:zrender/ZRender} zr
  3893. */
  3894. removeSelfFromZr: function (zr) {
  3895. this.__zr = null; // 移除动画
  3896. var animators = this.animators;
  3897. if (animators) {
  3898. for (var i = 0; i < animators.length; i++) {
  3899. zr.animation.removeAnimator(animators[i]);
  3900. }
  3901. }
  3902. if (this.clipPath) {
  3903. this.clipPath.removeSelfFromZr(zr);
  3904. }
  3905. }
  3906. };
  3907. mixin(Element, Animatable);
  3908. mixin(Element, Transformable);
  3909. mixin(Element, Eventful);
  3910. /**
  3911. * @module echarts/core/BoundingRect
  3912. */
  3913. var v2ApplyTransform = applyTransform;
  3914. var mathMin = Math.min;
  3915. var mathMax = Math.max;
  3916. /**
  3917. * @alias module:echarts/core/BoundingRect
  3918. */
  3919. function BoundingRect(x, y, width, height) {
  3920. if (width < 0) {
  3921. x = x + width;
  3922. width = -width;
  3923. }
  3924. if (height < 0) {
  3925. y = y + height;
  3926. height = -height;
  3927. }
  3928. /**
  3929. * @type {number}
  3930. */
  3931. this.x = x;
  3932. /**
  3933. * @type {number}
  3934. */
  3935. this.y = y;
  3936. /**
  3937. * @type {number}
  3938. */
  3939. this.width = width;
  3940. /**
  3941. * @type {number}
  3942. */
  3943. this.height = height;
  3944. }
  3945. BoundingRect.prototype = {
  3946. constructor: BoundingRect,
  3947. /**
  3948. * @param {module:echarts/core/BoundingRect} other
  3949. */
  3950. union: function (other) {
  3951. var x = mathMin(other.x, this.x);
  3952. var y = mathMin(other.y, this.y);
  3953. this.width = mathMax(other.x + other.width, this.x + this.width) - x;
  3954. this.height = mathMax(other.y + other.height, this.y + this.height) - y;
  3955. this.x = x;
  3956. this.y = y;
  3957. },
  3958. /**
  3959. * @param {Array.<number>} m
  3960. * @methods
  3961. */
  3962. applyTransform: function () {
  3963. var lt = [];
  3964. var rb = [];
  3965. var lb = [];
  3966. var rt = [];
  3967. return function (m) {
  3968. // In case usage like this
  3969. // el.getBoundingRect().applyTransform(el.transform)
  3970. // And element has no transform
  3971. if (!m) {
  3972. return;
  3973. }
  3974. lt[0] = lb[0] = this.x;
  3975. lt[1] = rt[1] = this.y;
  3976. rb[0] = rt[0] = this.x + this.width;
  3977. rb[1] = lb[1] = this.y + this.height;
  3978. v2ApplyTransform(lt, lt, m);
  3979. v2ApplyTransform(rb, rb, m);
  3980. v2ApplyTransform(lb, lb, m);
  3981. v2ApplyTransform(rt, rt, m);
  3982. this.x = mathMin(lt[0], rb[0], lb[0], rt[0]);
  3983. this.y = mathMin(lt[1], rb[1], lb[1], rt[1]);
  3984. var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]);
  3985. var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]);
  3986. this.width = maxX - this.x;
  3987. this.height = maxY - this.y;
  3988. };
  3989. }(),
  3990. /**
  3991. * Calculate matrix of transforming from self to target rect
  3992. * @param {module:zrender/core/BoundingRect} b
  3993. * @return {Array.<number>}
  3994. */
  3995. calculateTransform: function (b) {
  3996. var a = this;
  3997. var sx = b.width / a.width;
  3998. var sy = b.height / a.height;
  3999. var m = create$1(); // 矩阵右乘
  4000. translate(m, m, [-a.x, -a.y]);
  4001. scale$1(m, m, [sx, sy]);
  4002. translate(m, m, [b.x, b.y]);
  4003. return m;
  4004. },
  4005. /**
  4006. * @param {(module:echarts/core/BoundingRect|Object)} b
  4007. * @return {boolean}
  4008. */
  4009. intersect: function (b) {
  4010. if (!b) {
  4011. return false;
  4012. }
  4013. if (!(b instanceof BoundingRect)) {
  4014. // Normalize negative width/height.
  4015. b = BoundingRect.create(b);
  4016. }
  4017. var a = this;
  4018. var ax0 = a.x;
  4019. var ax1 = a.x + a.width;
  4020. var ay0 = a.y;
  4021. var ay1 = a.y + a.height;
  4022. var bx0 = b.x;
  4023. var bx1 = b.x + b.width;
  4024. var by0 = b.y;
  4025. var by1 = b.y + b.height;
  4026. return !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
  4027. },
  4028. contain: function (x, y) {
  4029. var rect = this;
  4030. return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height;
  4031. },
  4032. /**
  4033. * @return {module:echarts/core/BoundingRect}
  4034. */
  4035. clone: function () {
  4036. return new BoundingRect(this.x, this.y, this.width, this.height);
  4037. },
  4038. /**
  4039. * Copy from another rect
  4040. */
  4041. copy: function (other) {
  4042. this.x = other.x;
  4043. this.y = other.y;
  4044. this.width = other.width;
  4045. this.height = other.height;
  4046. },
  4047. plain: function () {
  4048. return {
  4049. x: this.x,
  4050. y: this.y,
  4051. width: this.width,
  4052. height: this.height
  4053. };
  4054. }
  4055. };
  4056. /**
  4057. * @param {Object|module:zrender/core/BoundingRect} rect
  4058. * @param {number} rect.x
  4059. * @param {number} rect.y
  4060. * @param {number} rect.width
  4061. * @param {number} rect.height
  4062. * @return {module:zrender/core/BoundingRect}
  4063. */
  4064. BoundingRect.create = function (rect) {
  4065. return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
  4066. };
  4067. /**
  4068. * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
  4069. * @module zrender/graphic/Group
  4070. * @example
  4071. * var Group = require('zrender/container/Group');
  4072. * var Circle = require('zrender/graphic/shape/Circle');
  4073. * var g = new Group();
  4074. * g.position[0] = 100;
  4075. * g.position[1] = 100;
  4076. * g.add(new Circle({
  4077. * style: {
  4078. * x: 100,
  4079. * y: 100,
  4080. * r: 20,
  4081. * }
  4082. * }));
  4083. * zr.add(g);
  4084. */
  4085. /**
  4086. * @alias module:zrender/graphic/Group
  4087. * @constructor
  4088. * @extends module:zrender/mixin/Transformable
  4089. * @extends module:zrender/mixin/Eventful
  4090. */
  4091. var Group = function (opts) {
  4092. opts = opts || {};
  4093. Element.call(this, opts);
  4094. for (var key in opts) {
  4095. if (opts.hasOwnProperty(key)) {
  4096. this[key] = opts[key];
  4097. }
  4098. }
  4099. this._children = [];
  4100. this.__storage = null;
  4101. this.__dirty = true;
  4102. };
  4103. Group.prototype = {
  4104. constructor: Group,
  4105. isGroup: true,
  4106. /**
  4107. * @type {string}
  4108. */
  4109. type: 'group',
  4110. /**
  4111. * 所有子孙元素是否响应鼠标事件
  4112. * @name module:/zrender/container/Group#silent
  4113. * @type {boolean}
  4114. * @default false
  4115. */
  4116. silent: false,
  4117. /**
  4118. * @return {Array.<module:zrender/Element>}
  4119. */
  4120. children: function () {
  4121. return this._children.slice();
  4122. },
  4123. /**
  4124. * 获取指定 index 的儿子节点
  4125. * @param {number} idx
  4126. * @return {module:zrender/Element}
  4127. */
  4128. childAt: function (idx) {
  4129. return this._children[idx];
  4130. },
  4131. /**
  4132. * 获取指定名字的儿子节点
  4133. * @param {string} name
  4134. * @return {module:zrender/Element}
  4135. */
  4136. childOfName: function (name) {
  4137. var children = this._children;
  4138. for (var i = 0; i < children.length; i++) {
  4139. if (children[i].name === name) {
  4140. return children[i];
  4141. }
  4142. }
  4143. },
  4144. /**
  4145. * @return {number}
  4146. */
  4147. childCount: function () {
  4148. return this._children.length;
  4149. },
  4150. /**
  4151. * 添加子节点到最后
  4152. * @param {module:zrender/Element} child
  4153. */
  4154. add: function (child) {
  4155. if (child && child !== this && child.parent !== this) {
  4156. this._children.push(child);
  4157. this._doAdd(child);
  4158. }
  4159. return this;
  4160. },
  4161. /**
  4162. * 添加子节点在 nextSibling 之前
  4163. * @param {module:zrender/Element} child
  4164. * @param {module:zrender/Element} nextSibling
  4165. */
  4166. addBefore: function (child, nextSibling) {
  4167. if (child && child !== this && child.parent !== this && nextSibling && nextSibling.parent === this) {
  4168. var children = this._children;
  4169. var idx = children.indexOf(nextSibling);
  4170. if (idx >= 0) {
  4171. children.splice(idx, 0, child);
  4172. this._doAdd(child);
  4173. }
  4174. }
  4175. return this;
  4176. },
  4177. _doAdd: function (child) {
  4178. if (child.parent) {
  4179. child.parent.remove(child);
  4180. }
  4181. child.parent = this;
  4182. var storage = this.__storage;
  4183. var zr = this.__zr;
  4184. if (storage && storage !== child.__storage) {
  4185. storage.addToStorage(child);
  4186. if (child instanceof Group) {
  4187. child.addChildrenToStorage(storage);
  4188. }
  4189. }
  4190. zr && zr.refresh();
  4191. },
  4192. /**
  4193. * 移除子节点
  4194. * @param {module:zrender/Element} child
  4195. */
  4196. remove: function (child) {
  4197. var zr = this.__zr;
  4198. var storage = this.__storage;
  4199. var children = this._children;
  4200. var idx = indexOf(children, child);
  4201. if (idx < 0) {
  4202. return this;
  4203. }
  4204. children.splice(idx, 1);
  4205. child.parent = null;
  4206. if (storage) {
  4207. storage.delFromStorage(child);
  4208. if (child instanceof Group) {
  4209. child.delChildrenFromStorage(storage);
  4210. }
  4211. }
  4212. zr && zr.refresh();
  4213. return this;
  4214. },
  4215. /**
  4216. * 移除所有子节点
  4217. */
  4218. removeAll: function () {
  4219. var children = this._children;
  4220. var storage = this.__storage;
  4221. var child;
  4222. var i;
  4223. for (i = 0; i < children.length; i++) {
  4224. child = children[i];
  4225. if (storage) {
  4226. storage.delFromStorage(child);
  4227. if (child instanceof Group) {
  4228. child.delChildrenFromStorage(storage);
  4229. }
  4230. }
  4231. child.parent = null;
  4232. }
  4233. children.length = 0;
  4234. return this;
  4235. },
  4236. /**
  4237. * 遍历所有子节点
  4238. * @param {Function} cb
  4239. * @param {} context
  4240. */
  4241. eachChild: function (cb, context) {
  4242. var children = this._children;
  4243. for (var i = 0; i < children.length; i++) {
  4244. var child = children[i];
  4245. cb.call(context, child, i);
  4246. }
  4247. return this;
  4248. },
  4249. /**
  4250. * 深度优先遍历所有子孙节点
  4251. * @param {Function} cb
  4252. * @param {} context
  4253. */
  4254. traverse: function (cb, context) {
  4255. for (var i = 0; i < this._children.length; i++) {
  4256. var child = this._children[i];
  4257. cb.call(context, child);
  4258. if (child.type === 'group') {
  4259. child.traverse(cb, context);
  4260. }
  4261. }
  4262. return this;
  4263. },
  4264. addChildrenToStorage: function (storage) {
  4265. for (var i = 0; i < this._children.length; i++) {
  4266. var child = this._children[i];
  4267. storage.addToStorage(child);
  4268. if (child instanceof Group) {
  4269. child.addChildrenToStorage(storage);
  4270. }
  4271. }
  4272. },
  4273. delChildrenFromStorage: function (storage) {
  4274. for (var i = 0; i < this._children.length; i++) {
  4275. var child = this._children[i];
  4276. storage.delFromStorage(child);
  4277. if (child instanceof Group) {
  4278. child.delChildrenFromStorage(storage);
  4279. }
  4280. }
  4281. },
  4282. dirty: function () {
  4283. this.__dirty = true;
  4284. this.__zr && this.__zr.refresh();
  4285. return this;
  4286. },
  4287. /**
  4288. * @return {module:zrender/core/BoundingRect}
  4289. */
  4290. getBoundingRect: function (includeChildren) {
  4291. // TODO Caching
  4292. var rect = null;
  4293. var tmpRect = new BoundingRect(0, 0, 0, 0);
  4294. var children = includeChildren || this._children;
  4295. var tmpMat = [];
  4296. for (var i = 0; i < children.length; i++) {
  4297. var child = children[i];
  4298. if (child.ignore || child.invisible) {
  4299. continue;
  4300. }
  4301. var childRect = child.getBoundingRect();
  4302. var transform = child.getLocalTransform(tmpMat); // TODO
  4303. // The boundingRect cacluated by transforming original
  4304. // rect may be bigger than the actual bundingRect when rotation
  4305. // is used. (Consider a circle rotated aginst its center, where
  4306. // the actual boundingRect should be the same as that not be
  4307. // rotated.) But we can not find better approach to calculate
  4308. // actual boundingRect yet, considering performance.
  4309. if (transform) {
  4310. tmpRect.copy(childRect);
  4311. tmpRect.applyTransform(transform);
  4312. rect = rect || tmpRect.clone();
  4313. rect.union(tmpRect);
  4314. } else {
  4315. rect = rect || childRect.clone();
  4316. rect.union(childRect);
  4317. }
  4318. }
  4319. return rect || tmpRect;
  4320. }
  4321. };
  4322. inherits(Group, Element); // https://github.com/mziccard/node-timsort
  4323. var DEFAULT_MIN_MERGE = 32;
  4324. var DEFAULT_MIN_GALLOPING = 7;
  4325. function minRunLength(n) {
  4326. var r = 0;
  4327. while (n >= DEFAULT_MIN_MERGE) {
  4328. r |= n & 1;
  4329. n >>= 1;
  4330. }
  4331. return n + r;
  4332. }
  4333. function makeAscendingRun(array, lo, hi, compare) {
  4334. var runHi = lo + 1;
  4335. if (runHi === hi) {
  4336. return 1;
  4337. }
  4338. if (compare(array[runHi++], array[lo]) < 0) {
  4339. while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
  4340. runHi++;
  4341. }
  4342. reverseRun(array, lo, runHi);
  4343. } else {
  4344. while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
  4345. runHi++;
  4346. }
  4347. }
  4348. return runHi - lo;
  4349. }
  4350. function reverseRun(array, lo, hi) {
  4351. hi--;
  4352. while (lo < hi) {
  4353. var t = array[lo];
  4354. array[lo++] = array[hi];
  4355. array[hi--] = t;
  4356. }
  4357. }
  4358. function binaryInsertionSort(array, lo, hi, start, compare) {
  4359. if (start === lo) {
  4360. start++;
  4361. }
  4362. for (; start < hi; start++) {
  4363. var pivot = array[start];
  4364. var left = lo;
  4365. var right = start;
  4366. var mid;
  4367. while (left < right) {
  4368. mid = left + right >>> 1;
  4369. if (compare(pivot, array[mid]) < 0) {
  4370. right = mid;
  4371. } else {
  4372. left = mid + 1;
  4373. }
  4374. }
  4375. var n = start - left;
  4376. switch (n) {
  4377. case 3:
  4378. array[left + 3] = array[left + 2];
  4379. case 2:
  4380. array[left + 2] = array[left + 1];
  4381. case 1:
  4382. array[left + 1] = array[left];
  4383. break;
  4384. default:
  4385. while (n > 0) {
  4386. array[left + n] = array[left + n - 1];
  4387. n--;
  4388. }
  4389. }
  4390. array[left] = pivot;
  4391. }
  4392. }
  4393. function gallopLeft(value, array, start, length, hint, compare) {
  4394. var lastOffset = 0;
  4395. var maxOffset = 0;
  4396. var offset = 1;
  4397. if (compare(value, array[start + hint]) > 0) {
  4398. maxOffset = length - hint;
  4399. while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
  4400. lastOffset = offset;
  4401. offset = (offset << 1) + 1;
  4402. if (offset <= 0) {
  4403. offset = maxOffset;
  4404. }
  4405. }
  4406. if (offset > maxOffset) {
  4407. offset = maxOffset;
  4408. }
  4409. lastOffset += hint;
  4410. offset += hint;
  4411. } else {
  4412. maxOffset = hint + 1;
  4413. while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
  4414. lastOffset = offset;
  4415. offset = (offset << 1) + 1;
  4416. if (offset <= 0) {
  4417. offset = maxOffset;
  4418. }
  4419. }
  4420. if (offset > maxOffset) {
  4421. offset = maxOffset;
  4422. }
  4423. var tmp = lastOffset;
  4424. lastOffset = hint - offset;
  4425. offset = hint - tmp;
  4426. }
  4427. lastOffset++;
  4428. while (lastOffset < offset) {
  4429. var m = lastOffset + (offset - lastOffset >>> 1);
  4430. if (compare(value, array[start + m]) > 0) {
  4431. lastOffset = m + 1;
  4432. } else {
  4433. offset = m;
  4434. }
  4435. }
  4436. return offset;
  4437. }
  4438. function gallopRight(value, array, start, length, hint, compare) {
  4439. var lastOffset = 0;
  4440. var maxOffset = 0;
  4441. var offset = 1;
  4442. if (compare(value, array[start + hint]) < 0) {
  4443. maxOffset = hint + 1;
  4444. while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
  4445. lastOffset = offset;
  4446. offset = (offset << 1) + 1;
  4447. if (offset <= 0) {
  4448. offset = maxOffset;
  4449. }
  4450. }
  4451. if (offset > maxOffset) {
  4452. offset = maxOffset;
  4453. }
  4454. var tmp = lastOffset;
  4455. lastOffset = hint - offset;
  4456. offset = hint - tmp;
  4457. } else {
  4458. maxOffset = length - hint;
  4459. while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
  4460. lastOffset = offset;
  4461. offset = (offset << 1) + 1;
  4462. if (offset <= 0) {
  4463. offset = maxOffset;
  4464. }
  4465. }
  4466. if (offset > maxOffset) {
  4467. offset = maxOffset;
  4468. }
  4469. lastOffset += hint;
  4470. offset += hint;
  4471. }
  4472. lastOffset++;
  4473. while (lastOffset < offset) {
  4474. var m = lastOffset + (offset - lastOffset >>> 1);
  4475. if (compare(value, array[start + m]) < 0) {
  4476. offset = m;
  4477. } else {
  4478. lastOffset = m + 1;
  4479. }
  4480. }
  4481. return offset;
  4482. }
  4483. function TimSort(array, compare) {
  4484. var minGallop = DEFAULT_MIN_GALLOPING;
  4485. var runStart;
  4486. var runLength;
  4487. var stackSize = 0;
  4488. var tmp = [];
  4489. runStart = [];
  4490. runLength = [];
  4491. function pushRun(_runStart, _runLength) {
  4492. runStart[stackSize] = _runStart;
  4493. runLength[stackSize] = _runLength;
  4494. stackSize += 1;
  4495. }
  4496. function mergeRuns() {
  4497. while (stackSize > 1) {
  4498. var n = stackSize - 2;
  4499. if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) {
  4500. if (runLength[n - 1] < runLength[n + 1]) {
  4501. n--;
  4502. }
  4503. } else if (runLength[n] > runLength[n + 1]) {
  4504. break;
  4505. }
  4506. mergeAt(n);
  4507. }
  4508. }
  4509. function forceMergeRuns() {
  4510. while (stackSize > 1) {
  4511. var n = stackSize - 2;
  4512. if (n > 0 && runLength[n - 1] < runLength[n + 1]) {
  4513. n--;
  4514. }
  4515. mergeAt(n);
  4516. }
  4517. }
  4518. function mergeAt(i) {
  4519. var start1 = runStart[i];
  4520. var length1 = runLength[i];
  4521. var start2 = runStart[i + 1];
  4522. var length2 = runLength[i + 1];
  4523. runLength[i] = length1 + length2;
  4524. if (i === stackSize - 3) {
  4525. runStart[i + 1] = runStart[i + 2];
  4526. runLength[i + 1] = runLength[i + 2];
  4527. }
  4528. stackSize--;
  4529. var k = gallopRight(array[start2], array, start1, length1, 0, compare);
  4530. start1 += k;
  4531. length1 -= k;
  4532. if (length1 === 0) {
  4533. return;
  4534. }
  4535. length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
  4536. if (length2 === 0) {
  4537. return;
  4538. }
  4539. if (length1 <= length2) {
  4540. mergeLow(start1, length1, start2, length2);
  4541. } else {
  4542. mergeHigh(start1, length1, start2, length2);
  4543. }
  4544. }
  4545. function mergeLow(start1, length1, start2, length2) {
  4546. var i = 0;
  4547. for (i = 0; i < length1; i++) {
  4548. tmp[i] = array[start1 + i];
  4549. }
  4550. var cursor1 = 0;
  4551. var cursor2 = start2;
  4552. var dest = start1;
  4553. array[dest++] = array[cursor2++];
  4554. if (--length2 === 0) {
  4555. for (i = 0; i < length1; i++) {
  4556. array[dest + i] = tmp[cursor1 + i];
  4557. }
  4558. return;
  4559. }
  4560. if (length1 === 1) {
  4561. for (i = 0; i < length2; i++) {
  4562. array[dest + i] = array[cursor2 + i];
  4563. }
  4564. array[dest + length2] = tmp[cursor1];
  4565. return;
  4566. }
  4567. var _minGallop = minGallop;
  4568. var count1, count2, exit;
  4569. while (1) {
  4570. count1 = 0;
  4571. count2 = 0;
  4572. exit = false;
  4573. do {
  4574. if (compare(array[cursor2], tmp[cursor1]) < 0) {
  4575. array[dest++] = array[cursor2++];
  4576. count2++;
  4577. count1 = 0;
  4578. if (--length2 === 0) {
  4579. exit = true;
  4580. break;
  4581. }
  4582. } else {
  4583. array[dest++] = tmp[cursor1++];
  4584. count1++;
  4585. count2 = 0;
  4586. if (--length1 === 1) {
  4587. exit = true;
  4588. break;
  4589. }
  4590. }
  4591. } while ((count1 | count2) < _minGallop);
  4592. if (exit) {
  4593. break;
  4594. }
  4595. do {
  4596. count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
  4597. if (count1 !== 0) {
  4598. for (i = 0; i < count1; i++) {
  4599. array[dest + i] = tmp[cursor1 + i];
  4600. }
  4601. dest += count1;
  4602. cursor1 += count1;
  4603. length1 -= count1;
  4604. if (length1 <= 1) {
  4605. exit = true;
  4606. break;
  4607. }
  4608. }
  4609. array[dest++] = array[cursor2++];
  4610. if (--length2 === 0) {
  4611. exit = true;
  4612. break;
  4613. }
  4614. count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
  4615. if (count2 !== 0) {
  4616. for (i = 0; i < count2; i++) {
  4617. array[dest + i] = array[cursor2 + i];
  4618. }
  4619. dest += count2;
  4620. cursor2 += count2;
  4621. length2 -= count2;
  4622. if (length2 === 0) {
  4623. exit = true;
  4624. break;
  4625. }
  4626. }
  4627. array[dest++] = tmp[cursor1++];
  4628. if (--length1 === 1) {
  4629. exit = true;
  4630. break;
  4631. }
  4632. _minGallop--;
  4633. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  4634. if (exit) {
  4635. break;
  4636. }
  4637. if (_minGallop < 0) {
  4638. _minGallop = 0;
  4639. }
  4640. _minGallop += 2;
  4641. }
  4642. minGallop = _minGallop;
  4643. minGallop < 1 && (minGallop = 1);
  4644. if (length1 === 1) {
  4645. for (i = 0; i < length2; i++) {
  4646. array[dest + i] = array[cursor2 + i];
  4647. }
  4648. array[dest + length2] = tmp[cursor1];
  4649. } else if (length1 === 0) {
  4650. throw new Error(); // throw new Error('mergeLow preconditions were not respected');
  4651. } else {
  4652. for (i = 0; i < length1; i++) {
  4653. array[dest + i] = tmp[cursor1 + i];
  4654. }
  4655. }
  4656. }
  4657. function mergeHigh(start1, length1, start2, length2) {
  4658. var i = 0;
  4659. for (i = 0; i < length2; i++) {
  4660. tmp[i] = array[start2 + i];
  4661. }
  4662. var cursor1 = start1 + length1 - 1;
  4663. var cursor2 = length2 - 1;
  4664. var dest = start2 + length2 - 1;
  4665. var customCursor = 0;
  4666. var customDest = 0;
  4667. array[dest--] = array[cursor1--];
  4668. if (--length1 === 0) {
  4669. customCursor = dest - (length2 - 1);
  4670. for (i = 0; i < length2; i++) {
  4671. array[customCursor + i] = tmp[i];
  4672. }
  4673. return;
  4674. }
  4675. if (length2 === 1) {
  4676. dest -= length1;
  4677. cursor1 -= length1;
  4678. customDest = dest + 1;
  4679. customCursor = cursor1 + 1;
  4680. for (i = length1 - 1; i >= 0; i--) {
  4681. array[customDest + i] = array[customCursor + i];
  4682. }
  4683. array[dest] = tmp[cursor2];
  4684. return;
  4685. }
  4686. var _minGallop = minGallop;
  4687. while (true) {
  4688. var count1 = 0;
  4689. var count2 = 0;
  4690. var exit = false;
  4691. do {
  4692. if (compare(tmp[cursor2], array[cursor1]) < 0) {
  4693. array[dest--] = array[cursor1--];
  4694. count1++;
  4695. count2 = 0;
  4696. if (--length1 === 0) {
  4697. exit = true;
  4698. break;
  4699. }
  4700. } else {
  4701. array[dest--] = tmp[cursor2--];
  4702. count2++;
  4703. count1 = 0;
  4704. if (--length2 === 1) {
  4705. exit = true;
  4706. break;
  4707. }
  4708. }
  4709. } while ((count1 | count2) < _minGallop);
  4710. if (exit) {
  4711. break;
  4712. }
  4713. do {
  4714. count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
  4715. if (count1 !== 0) {
  4716. dest -= count1;
  4717. cursor1 -= count1;
  4718. length1 -= count1;
  4719. customDest = dest + 1;
  4720. customCursor = cursor1 + 1;
  4721. for (i = count1 - 1; i >= 0; i--) {
  4722. array[customDest + i] = array[customCursor + i];
  4723. }
  4724. if (length1 === 0) {
  4725. exit = true;
  4726. break;
  4727. }
  4728. }
  4729. array[dest--] = tmp[cursor2--];
  4730. if (--length2 === 1) {
  4731. exit = true;
  4732. break;
  4733. }
  4734. count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
  4735. if (count2 !== 0) {
  4736. dest -= count2;
  4737. cursor2 -= count2;
  4738. length2 -= count2;
  4739. customDest = dest + 1;
  4740. customCursor = cursor2 + 1;
  4741. for (i = 0; i < count2; i++) {
  4742. array[customDest + i] = tmp[customCursor + i];
  4743. }
  4744. if (length2 <= 1) {
  4745. exit = true;
  4746. break;
  4747. }
  4748. }
  4749. array[dest--] = array[cursor1--];
  4750. if (--length1 === 0) {
  4751. exit = true;
  4752. break;
  4753. }
  4754. _minGallop--;
  4755. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  4756. if (exit) {
  4757. break;
  4758. }
  4759. if (_minGallop < 0) {
  4760. _minGallop = 0;
  4761. }
  4762. _minGallop += 2;
  4763. }
  4764. minGallop = _minGallop;
  4765. if (minGallop < 1) {
  4766. minGallop = 1;
  4767. }
  4768. if (length2 === 1) {
  4769. dest -= length1;
  4770. cursor1 -= length1;
  4771. customDest = dest + 1;
  4772. customCursor = cursor1 + 1;
  4773. for (i = length1 - 1; i >= 0; i--) {
  4774. array[customDest + i] = array[customCursor + i];
  4775. }
  4776. array[dest] = tmp[cursor2];
  4777. } else if (length2 === 0) {
  4778. throw new Error(); // throw new Error('mergeHigh preconditions were not respected');
  4779. } else {
  4780. customCursor = dest - (length2 - 1);
  4781. for (i = 0; i < length2; i++) {
  4782. array[customCursor + i] = tmp[i];
  4783. }
  4784. }
  4785. }
  4786. this.mergeRuns = mergeRuns;
  4787. this.forceMergeRuns = forceMergeRuns;
  4788. this.pushRun = pushRun;
  4789. }
  4790. function sort(array, compare, lo, hi) {
  4791. if (!lo) {
  4792. lo = 0;
  4793. }
  4794. if (!hi) {
  4795. hi = array.length;
  4796. }
  4797. var remaining = hi - lo;
  4798. if (remaining < 2) {
  4799. return;
  4800. }
  4801. var runLength = 0;
  4802. if (remaining < DEFAULT_MIN_MERGE) {
  4803. runLength = makeAscendingRun(array, lo, hi, compare);
  4804. binaryInsertionSort(array, lo, hi, lo + runLength, compare);
  4805. return;
  4806. }
  4807. var ts = new TimSort(array, compare);
  4808. var minRun = minRunLength(remaining);
  4809. do {
  4810. runLength = makeAscendingRun(array, lo, hi, compare);
  4811. if (runLength < minRun) {
  4812. var force = remaining;
  4813. if (force > minRun) {
  4814. force = minRun;
  4815. }
  4816. binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
  4817. runLength = force;
  4818. }
  4819. ts.pushRun(lo, runLength);
  4820. ts.mergeRuns();
  4821. remaining -= runLength;
  4822. lo += runLength;
  4823. } while (remaining !== 0);
  4824. ts.forceMergeRuns();
  4825. }
  4826. /**
  4827. * Storage内容仓库模块
  4828. * @module zrender/Storage
  4829. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  4830. * @author errorrik (errorrik@gmail.com)
  4831. * @author pissang (https://github.com/pissang/)
  4832. */
  4833. // Use timsort because in most case elements are partially sorted
  4834. // https://jsfiddle.net/pissang/jr4x7mdm/8/
  4835. function shapeCompareFunc(a, b) {
  4836. if (a.zlevel === b.zlevel) {
  4837. if (a.z === b.z) {
  4838. // if (a.z2 === b.z2) {
  4839. // // FIXME Slow has renderidx compare
  4840. // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
  4841. // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
  4842. // return a.__renderidx - b.__renderidx;
  4843. // }
  4844. return a.z2 - b.z2;
  4845. }
  4846. return a.z - b.z;
  4847. }
  4848. return a.zlevel - b.zlevel;
  4849. }
  4850. /**
  4851. * 内容仓库 (M)
  4852. * @alias module:zrender/Storage
  4853. * @constructor
  4854. */
  4855. var Storage = function () {
  4856. // jshint ignore:line
  4857. this._roots = [];
  4858. this._displayList = [];
  4859. this._displayListLen = 0;
  4860. };
  4861. Storage.prototype = {
  4862. constructor: Storage,
  4863. /**
  4864. * @param {Function} cb
  4865. *
  4866. */
  4867. traverse: function (cb, context) {
  4868. for (var i = 0; i < this._roots.length; i++) {
  4869. this._roots[i].traverse(cb, context);
  4870. }
  4871. },
  4872. /**
  4873. * 返回所有图形的绘制队列
  4874. * @param {boolean} [update=false] 是否在返回前更新该数组
  4875. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效
  4876. *
  4877. * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
  4878. * @return {Array.<module:zrender/graphic/Displayable>}
  4879. */
  4880. getDisplayList: function (update, includeIgnore) {
  4881. includeIgnore = includeIgnore || false;
  4882. if (update) {
  4883. this.updateDisplayList(includeIgnore);
  4884. }
  4885. return this._displayList;
  4886. },
  4887. /**
  4888. * 更新图形的绘制队列。
  4889. * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
  4890. * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
  4891. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组
  4892. */
  4893. updateDisplayList: function (includeIgnore) {
  4894. this._displayListLen = 0;
  4895. var roots = this._roots;
  4896. var displayList = this._displayList;
  4897. for (var i = 0, len = roots.length; i < len; i++) {
  4898. this._updateAndAddDisplayable(roots[i], null, includeIgnore);
  4899. }
  4900. displayList.length = this._displayListLen; // for (var i = 0, len = displayList.length; i < len; i++) {
  4901. // displayList[i].__renderidx = i;
  4902. // }
  4903. // displayList.sort(shapeCompareFunc);
  4904. env$1.canvasSupported && sort(displayList, shapeCompareFunc);
  4905. },
  4906. _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
  4907. if (el.ignore && !includeIgnore) {
  4908. return;
  4909. }
  4910. el.beforeUpdate();
  4911. if (el.__dirty) {
  4912. el.update();
  4913. }
  4914. el.afterUpdate();
  4915. var userSetClipPath = el.clipPath;
  4916. if (userSetClipPath) {
  4917. // FIXME 效率影响
  4918. if (clipPaths) {
  4919. clipPaths = clipPaths.slice();
  4920. } else {
  4921. clipPaths = [];
  4922. }
  4923. var currentClipPath = userSetClipPath;
  4924. var parentClipPath = el; // Recursively add clip path
  4925. while (currentClipPath) {
  4926. // clipPath 的变换是基于使用这个 clipPath 的元素
  4927. currentClipPath.parent = parentClipPath;
  4928. currentClipPath.updateTransform();
  4929. clipPaths.push(currentClipPath);
  4930. parentClipPath = currentClipPath;
  4931. currentClipPath = currentClipPath.clipPath;
  4932. }
  4933. }
  4934. if (el.isGroup) {
  4935. var children = el._children;
  4936. for (var i = 0; i < children.length; i++) {
  4937. var child = children[i]; // Force to mark as dirty if group is dirty
  4938. // FIXME __dirtyPath ?
  4939. if (el.__dirty) {
  4940. child.__dirty = true;
  4941. }
  4942. this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
  4943. } // Mark group clean here
  4944. el.__dirty = false;
  4945. } else {
  4946. el.__clipPaths = clipPaths;
  4947. this._displayList[this._displayListLen++] = el;
  4948. }
  4949. },
  4950. /**
  4951. * 添加图形(Shape)或者组(Group)到根节点
  4952. * @param {module:zrender/Element} el
  4953. */
  4954. addRoot: function (el) {
  4955. if (el.__storage === this) {
  4956. return;
  4957. }
  4958. if (el instanceof Group) {
  4959. el.addChildrenToStorage(this);
  4960. }
  4961. this.addToStorage(el);
  4962. this._roots.push(el);
  4963. },
  4964. /**
  4965. * 删除指定的图形(Shape)或者组(Group)
  4966. * @param {string|Array.<string>} [el] 如果为空清空整个Storage
  4967. */
  4968. delRoot: function (el) {
  4969. if (el == null) {
  4970. // 不指定el清空
  4971. for (var i = 0; i < this._roots.length; i++) {
  4972. var root = this._roots[i];
  4973. if (root instanceof Group) {
  4974. root.delChildrenFromStorage(this);
  4975. }
  4976. }
  4977. this._roots = [];
  4978. this._displayList = [];
  4979. this._displayListLen = 0;
  4980. return;
  4981. }
  4982. if (el instanceof Array) {
  4983. for (var i = 0, l = el.length; i < l; i++) {
  4984. this.delRoot(el[i]);
  4985. }
  4986. return;
  4987. }
  4988. var idx = indexOf(this._roots, el);
  4989. if (idx >= 0) {
  4990. this.delFromStorage(el);
  4991. this._roots.splice(idx, 1);
  4992. if (el instanceof Group) {
  4993. el.delChildrenFromStorage(this);
  4994. }
  4995. }
  4996. },
  4997. addToStorage: function (el) {
  4998. el.__storage = this;
  4999. el.dirty(false);
  5000. return this;
  5001. },
  5002. delFromStorage: function (el) {
  5003. if (el) {
  5004. el.__storage = null;
  5005. }
  5006. return this;
  5007. },
  5008. /**
  5009. * 清空并且释放Storage
  5010. */
  5011. dispose: function () {
  5012. this._renderList = this._roots = null;
  5013. },
  5014. displayableSortFunc: shapeCompareFunc
  5015. };
  5016. var STYLE_COMMON_PROPS = [['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'], ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]]; // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
  5017. // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);
  5018. var Style = function (opts, host) {
  5019. this.extendFrom(opts, false);
  5020. this.host = host;
  5021. };
  5022. function createLinearGradient(ctx, obj, rect) {
  5023. var x = obj.x == null ? 0 : obj.x;
  5024. var x2 = obj.x2 == null ? 1 : obj.x2;
  5025. var y = obj.y == null ? 0 : obj.y;
  5026. var y2 = obj.y2 == null ? 0 : obj.y2;
  5027. if (!obj.global) {
  5028. x = x * rect.width + rect.x;
  5029. x2 = x2 * rect.width + rect.x;
  5030. y = y * rect.height + rect.y;
  5031. y2 = y2 * rect.height + rect.y;
  5032. }
  5033. var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
  5034. return canvasGradient;
  5035. }
  5036. function createRadialGradient(ctx, obj, rect) {
  5037. var width = rect.width;
  5038. var height = rect.height;
  5039. var min = Math.min(width, height);
  5040. var x = obj.x == null ? 0.5 : obj.x;
  5041. var y = obj.y == null ? 0.5 : obj.y;
  5042. var r = obj.r == null ? 0.5 : obj.r;
  5043. if (!obj.global) {
  5044. x = x * width + rect.x;
  5045. y = y * height + rect.y;
  5046. r = r * min;
  5047. }
  5048. var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
  5049. return canvasGradient;
  5050. }
  5051. Style.prototype = {
  5052. constructor: Style,
  5053. /**
  5054. * @type {module:zrender/graphic/Displayable}
  5055. */
  5056. host: null,
  5057. /**
  5058. * @type {string}
  5059. */
  5060. fill: '#000',
  5061. /**
  5062. * @type {string}
  5063. */
  5064. stroke: null,
  5065. /**
  5066. * @type {number}
  5067. */
  5068. opacity: 1,
  5069. /**
  5070. * @type {Array.<number>}
  5071. */
  5072. lineDash: null,
  5073. /**
  5074. * @type {number}
  5075. */
  5076. lineDashOffset: 0,
  5077. /**
  5078. * @type {number}
  5079. */
  5080. shadowBlur: 0,
  5081. /**
  5082. * @type {number}
  5083. */
  5084. shadowOffsetX: 0,
  5085. /**
  5086. * @type {number}
  5087. */
  5088. shadowOffsetY: 0,
  5089. /**
  5090. * @type {number}
  5091. */
  5092. lineWidth: 1,
  5093. /**
  5094. * If stroke ignore scale
  5095. * @type {Boolean}
  5096. */
  5097. strokeNoScale: false,
  5098. // Bounding rect text configuration
  5099. // Not affected by element transform
  5100. /**
  5101. * @type {string}
  5102. */
  5103. text: null,
  5104. /**
  5105. * If `fontSize` or `fontFamily` exists, `font` will be reset by
  5106. * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
  5107. * So do not visit it directly in upper application (like echarts),
  5108. * but use `contain/text#makeFont` instead.
  5109. * @type {string}
  5110. */
  5111. font: null,
  5112. /**
  5113. * The same as font. Use font please.
  5114. * @deprecated
  5115. * @type {string}
  5116. */
  5117. textFont: null,
  5118. /**
  5119. * It helps merging respectively, rather than parsing an entire font string.
  5120. * @type {string}
  5121. */
  5122. fontStyle: null,
  5123. /**
  5124. * It helps merging respectively, rather than parsing an entire font string.
  5125. * @type {string}
  5126. */
  5127. fontWeight: null,
  5128. /**
  5129. * It helps merging respectively, rather than parsing an entire font string.
  5130. * Should be 12 but not '12px'.
  5131. * @type {number}
  5132. */
  5133. fontSize: null,
  5134. /**
  5135. * It helps merging respectively, rather than parsing an entire font string.
  5136. * @type {string}
  5137. */
  5138. fontFamily: null,
  5139. /**
  5140. * Reserved for special functinality, like 'hr'.
  5141. * @type {string}
  5142. */
  5143. textTag: null,
  5144. /**
  5145. * @type {string}
  5146. */
  5147. textFill: '#000',
  5148. /**
  5149. * @type {string}
  5150. */
  5151. textStroke: null,
  5152. /**
  5153. * @type {number}
  5154. */
  5155. textWidth: null,
  5156. /**
  5157. * Only for textBackground.
  5158. * @type {number}
  5159. */
  5160. textHeight: null,
  5161. /**
  5162. * textStroke may be set as some color as a default
  5163. * value in upper applicaion, where the default value
  5164. * of textStrokeWidth should be 0 to make sure that
  5165. * user can choose to do not use text stroke.
  5166. * @type {number}
  5167. */
  5168. textStrokeWidth: 0,
  5169. /**
  5170. * @type {number}
  5171. */
  5172. textLineHeight: null,
  5173. /**
  5174. * 'inside', 'left', 'right', 'top', 'bottom'
  5175. * [x, y]
  5176. * Based on x, y of rect.
  5177. * @type {string|Array.<number>}
  5178. * @default 'inside'
  5179. */
  5180. textPosition: 'inside',
  5181. /**
  5182. * If not specified, use the boundingRect of a `displayable`.
  5183. * @type {Object}
  5184. */
  5185. textRect: null,
  5186. /**
  5187. * [x, y]
  5188. * @type {Array.<number>}
  5189. */
  5190. textOffset: null,
  5191. /**
  5192. * @type {string}
  5193. */
  5194. textAlign: null,
  5195. /**
  5196. * @type {string}
  5197. */
  5198. textVerticalAlign: null,
  5199. /**
  5200. * @type {number}
  5201. */
  5202. textDistance: 5,
  5203. /**
  5204. * @type {string}
  5205. */
  5206. textShadowColor: 'transparent',
  5207. /**
  5208. * @type {number}
  5209. */
  5210. textShadowBlur: 0,
  5211. /**
  5212. * @type {number}
  5213. */
  5214. textShadowOffsetX: 0,
  5215. /**
  5216. * @type {number}
  5217. */
  5218. textShadowOffsetY: 0,
  5219. /**
  5220. * @type {string}
  5221. */
  5222. textBoxShadowColor: 'transparent',
  5223. /**
  5224. * @type {number}
  5225. */
  5226. textBoxShadowBlur: 0,
  5227. /**
  5228. * @type {number}
  5229. */
  5230. textBoxShadowOffsetX: 0,
  5231. /**
  5232. * @type {number}
  5233. */
  5234. textBoxShadowOffsetY: 0,
  5235. /**
  5236. * Whether transform text.
  5237. * Only useful in Path and Image element
  5238. * @type {boolean}
  5239. */
  5240. transformText: false,
  5241. /**
  5242. * Text rotate around position of Path or Image
  5243. * Only useful in Path and Image element and transformText is false.
  5244. */
  5245. textRotation: 0,
  5246. /**
  5247. * Text origin of text rotation, like [10, 40].
  5248. * Based on x, y of rect.
  5249. * Useful in label rotation of circular symbol.
  5250. * By default, this origin is textPosition.
  5251. * Can be 'center'.
  5252. * @type {string|Array.<number>}
  5253. */
  5254. textOrigin: null,
  5255. /**
  5256. * @type {string}
  5257. */
  5258. textBackgroundColor: null,
  5259. /**
  5260. * @type {string}
  5261. */
  5262. textBorderColor: null,
  5263. /**
  5264. * @type {number}
  5265. */
  5266. textBorderWidth: 0,
  5267. /**
  5268. * @type {number}
  5269. */
  5270. textBorderRadius: 0,
  5271. /**
  5272. * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
  5273. * @type {number|Array.<number>}
  5274. */
  5275. textPadding: null,
  5276. /**
  5277. * Text styles for rich text.
  5278. * @type {Object}
  5279. */
  5280. rich: null,
  5281. /**
  5282. * {outerWidth, outerHeight, ellipsis, placeholder}
  5283. * @type {Object}
  5284. */
  5285. truncate: null,
  5286. /**
  5287. * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  5288. * @type {string}
  5289. */
  5290. blend: null,
  5291. /**
  5292. * @param {CanvasRenderingContext2D} ctx
  5293. */
  5294. bind: function (ctx, el, prevEl) {
  5295. var style = this;
  5296. var prevStyle = prevEl && prevEl.style;
  5297. var firstDraw = !prevStyle;
  5298. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  5299. var prop = STYLE_COMMON_PROPS[i];
  5300. var styleName = prop[0];
  5301. if (firstDraw || style[styleName] !== prevStyle[styleName]) {
  5302. // FIXME Invalid property value will cause style leak from previous element.
  5303. ctx[styleName] = style[styleName] || prop[1];
  5304. }
  5305. }
  5306. if (firstDraw || style.fill !== prevStyle.fill) {
  5307. ctx.fillStyle = style.fill;
  5308. }
  5309. if (firstDraw || style.stroke !== prevStyle.stroke) {
  5310. ctx.strokeStyle = style.stroke;
  5311. }
  5312. if (firstDraw || style.opacity !== prevStyle.opacity) {
  5313. ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
  5314. }
  5315. if (firstDraw || style.blend !== prevStyle.blend) {
  5316. ctx.globalCompositeOperation = style.blend || 'source-over';
  5317. }
  5318. if (this.hasStroke()) {
  5319. var lineWidth = style.lineWidth;
  5320. ctx.lineWidth = lineWidth / (this.strokeNoScale && el && el.getLineScale ? el.getLineScale() : 1);
  5321. }
  5322. },
  5323. hasFill: function () {
  5324. var fill = this.fill;
  5325. return fill != null && fill !== 'none';
  5326. },
  5327. hasStroke: function () {
  5328. var stroke = this.stroke;
  5329. return stroke != null && stroke !== 'none' && this.lineWidth > 0;
  5330. },
  5331. /**
  5332. * Extend from other style
  5333. * @param {zrender/graphic/Style} otherStyle
  5334. * @param {boolean} overwrite true: overwrirte any way.
  5335. * false: overwrite only when !target.hasOwnProperty
  5336. * others: overwrite when property is not null/undefined.
  5337. */
  5338. extendFrom: function (otherStyle, overwrite) {
  5339. if (otherStyle) {
  5340. for (var name in otherStyle) {
  5341. if (otherStyle.hasOwnProperty(name) && (overwrite === true || (overwrite === false ? !this.hasOwnProperty(name) : otherStyle[name] != null))) {
  5342. this[name] = otherStyle[name];
  5343. }
  5344. }
  5345. }
  5346. },
  5347. /**
  5348. * Batch setting style with a given object
  5349. * @param {Object|string} obj
  5350. * @param {*} [obj]
  5351. */
  5352. set: function (obj, value) {
  5353. if (typeof obj === 'string') {
  5354. this[obj] = value;
  5355. } else {
  5356. this.extendFrom(obj, true);
  5357. }
  5358. },
  5359. /**
  5360. * Clone
  5361. * @return {zrender/graphic/Style} [description]
  5362. */
  5363. clone: function () {
  5364. var newStyle = new this.constructor();
  5365. newStyle.extendFrom(this, true);
  5366. return newStyle;
  5367. },
  5368. getGradient: function (ctx, obj, rect) {
  5369. var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
  5370. var canvasGradient = method(ctx, obj, rect);
  5371. var colorStops = obj.colorStops;
  5372. for (var i = 0; i < colorStops.length; i++) {
  5373. canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color);
  5374. }
  5375. return canvasGradient;
  5376. }
  5377. };
  5378. var styleProto = Style.prototype;
  5379. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  5380. var prop = STYLE_COMMON_PROPS[i];
  5381. if (!(prop[0] in styleProto)) {
  5382. styleProto[prop[0]] = prop[1];
  5383. }
  5384. } // Provide for others
  5385. Style.getGradient = styleProto.getGradient;
  5386. var Pattern = function (image, repeat) {
  5387. // Should do nothing more in this constructor. Because gradient can be
  5388. // declard by `color: {image: ...}`, where this constructor will not be called.
  5389. this.image = image;
  5390. this.repeat = repeat; // Can be cloned
  5391. this.type = 'pattern';
  5392. };
  5393. Pattern.prototype.getCanvasPattern = function (ctx) {
  5394. return ctx.createPattern(this.image, this.repeat || 'repeat');
  5395. };
  5396. /**
  5397. * @module zrender/Layer
  5398. * @author pissang(https://www.github.com/pissang)
  5399. */
  5400. function returnFalse() {
  5401. return false;
  5402. }
  5403. /**
  5404. * 创建dom
  5405. *
  5406. * @inner
  5407. * @param {string} id dom id 待用
  5408. * @param {Painter} painter painter instance
  5409. * @param {number} number
  5410. */
  5411. function createDom(id, painter, dpr) {
  5412. var newDom = createCanvas();
  5413. var width = painter.getWidth();
  5414. var height = painter.getHeight();
  5415. var newDomStyle = newDom.style; // 没append呢,请原谅我这样写,清晰~
  5416. newDomStyle.position = 'absolute';
  5417. newDomStyle.left = 0;
  5418. newDomStyle.top = 0;
  5419. newDomStyle.width = width + 'px';
  5420. newDomStyle.height = height + 'px';
  5421. newDom.width = width * dpr;
  5422. newDom.height = height * dpr; // id不作为索引用,避免可能造成的重名,定义为私有属性
  5423. newDom.setAttribute('data-zr-dom-id', id);
  5424. return newDom;
  5425. }
  5426. /**
  5427. * @alias module:zrender/Layer
  5428. * @constructor
  5429. * @extends module:zrender/mixin/Transformable
  5430. * @param {string} id
  5431. * @param {module:zrender/Painter} painter
  5432. * @param {number} [dpr]
  5433. */
  5434. var Layer = function (id, painter, dpr) {
  5435. var dom;
  5436. dpr = dpr || devicePixelRatio;
  5437. if (typeof id === 'string') {
  5438. dom = createDom(id, painter, dpr);
  5439. } // Not using isDom because in node it will return false
  5440. else if (isObject(id)) {
  5441. dom = id;
  5442. id = dom.id;
  5443. }
  5444. this.id = id;
  5445. this.dom = dom;
  5446. var domStyle = dom.style;
  5447. if (domStyle) {
  5448. // Not in node
  5449. dom.onselectstart = returnFalse; // 避免页面选中的尴尬
  5450. domStyle['-webkit-user-select'] = 'none';
  5451. domStyle['user-select'] = 'none';
  5452. domStyle['-webkit-touch-callout'] = 'none';
  5453. domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';
  5454. domStyle['padding'] = 0;
  5455. domStyle['margin'] = 0;
  5456. domStyle['border-width'] = 0;
  5457. }
  5458. this.domBack = null;
  5459. this.ctxBack = null;
  5460. this.painter = painter;
  5461. this.config = null; // Configs
  5462. /**
  5463. * 每次清空画布的颜色
  5464. * @type {string}
  5465. * @default 0
  5466. */
  5467. this.clearColor = 0;
  5468. /**
  5469. * 是否开启动态模糊
  5470. * @type {boolean}
  5471. * @default false
  5472. */
  5473. this.motionBlur = false;
  5474. /**
  5475. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  5476. * @type {number}
  5477. * @default 0.7
  5478. */
  5479. this.lastFrameAlpha = 0.7;
  5480. /**
  5481. * Layer dpr
  5482. * @type {number}
  5483. */
  5484. this.dpr = dpr;
  5485. };
  5486. Layer.prototype = {
  5487. constructor: Layer,
  5488. elCount: 0,
  5489. __dirty: true,
  5490. initContext: function () {
  5491. this.ctx = this.dom.getContext('2d');
  5492. this.ctx.__currentValues = {};
  5493. this.ctx.dpr = this.dpr;
  5494. },
  5495. createBackBuffer: function () {
  5496. var dpr = this.dpr;
  5497. this.domBack = createDom('back-' + this.id, this.painter, dpr);
  5498. this.ctxBack = this.domBack.getContext('2d');
  5499. this.ctxBack.__currentValues = {};
  5500. if (dpr != 1) {
  5501. this.ctxBack.scale(dpr, dpr);
  5502. }
  5503. },
  5504. /**
  5505. * @param {number} width
  5506. * @param {number} height
  5507. */
  5508. resize: function (width, height) {
  5509. var dpr = this.dpr;
  5510. var dom = this.dom;
  5511. var domStyle = dom.style;
  5512. var domBack = this.domBack;
  5513. domStyle.width = width + 'px';
  5514. domStyle.height = height + 'px';
  5515. dom.width = width * dpr;
  5516. dom.height = height * dpr;
  5517. if (domBack) {
  5518. domBack.width = width * dpr;
  5519. domBack.height = height * dpr;
  5520. if (dpr != 1) {
  5521. this.ctxBack.scale(dpr, dpr);
  5522. }
  5523. }
  5524. },
  5525. /**
  5526. * 清空该层画布
  5527. * @param {boolean} clearAll Clear all with out motion blur
  5528. */
  5529. clear: function (clearAll) {
  5530. var dom = this.dom;
  5531. var ctx = this.ctx;
  5532. var width = dom.width;
  5533. var height = dom.height;
  5534. var clearColor = this.clearColor;
  5535. var haveMotionBLur = this.motionBlur && !clearAll;
  5536. var lastFrameAlpha = this.lastFrameAlpha;
  5537. var dpr = this.dpr;
  5538. if (haveMotionBLur) {
  5539. if (!this.domBack) {
  5540. this.createBackBuffer();
  5541. }
  5542. this.ctxBack.globalCompositeOperation = 'copy';
  5543. this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr);
  5544. }
  5545. ctx.clearRect(0, 0, width, height);
  5546. if (clearColor) {
  5547. var clearColorGradientOrPattern; // Gradient
  5548. if (clearColor.colorStops) {
  5549. // Cache canvas gradient
  5550. clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, {
  5551. x: 0,
  5552. y: 0,
  5553. width: width,
  5554. height: height
  5555. });
  5556. clearColor.__canvasGradient = clearColorGradientOrPattern;
  5557. } // Pattern
  5558. else if (clearColor.image) {
  5559. clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx);
  5560. }
  5561. ctx.save();
  5562. ctx.fillStyle = clearColorGradientOrPattern || clearColor;
  5563. ctx.fillRect(0, 0, width, height);
  5564. ctx.restore();
  5565. }
  5566. if (haveMotionBLur) {
  5567. var domBack = this.domBack;
  5568. ctx.save();
  5569. ctx.globalAlpha = lastFrameAlpha;
  5570. ctx.drawImage(domBack, 0, 0, width, height);
  5571. ctx.restore();
  5572. }
  5573. }
  5574. };
  5575. var requestAnimationFrame = typeof window !== 'undefined' && (window.requestAnimationFrame && window.requestAnimationFrame.bind(window) || // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809
  5576. window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window) || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame) || function (func) {
  5577. setTimeout(func, 16);
  5578. };
  5579. var globalImageCache = new LRU(50);
  5580. /**
  5581. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  5582. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  5583. */
  5584. function findExistImage(newImageOrSrc) {
  5585. if (typeof newImageOrSrc === 'string') {
  5586. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  5587. return cachedImgObj && cachedImgObj.image;
  5588. } else {
  5589. return newImageOrSrc;
  5590. }
  5591. }
  5592. /**
  5593. * Caution: User should cache loaded images, but not just count on LRU.
  5594. * Consider if required images more than LRU size, will dead loop occur?
  5595. *
  5596. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  5597. * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image.
  5598. * @param {module:zrender/Element} [hostEl] For calling `dirty`.
  5599. * @param {Function} [cb] params: (image, cbPayload)
  5600. * @param {Object} [cbPayload] Payload on cb calling.
  5601. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  5602. */
  5603. function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) {
  5604. if (!newImageOrSrc) {
  5605. return image;
  5606. } else if (typeof newImageOrSrc === 'string') {
  5607. // Image should not be loaded repeatly.
  5608. if (image && image.__zrImageSrc === newImageOrSrc || !hostEl) {
  5609. return image;
  5610. } // Only when there is no existent image or existent image src
  5611. // is different, this method is responsible for load.
  5612. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  5613. var pendingWrap = {
  5614. hostEl: hostEl,
  5615. cb: cb,
  5616. cbPayload: cbPayload
  5617. };
  5618. if (cachedImgObj) {
  5619. image = cachedImgObj.image;
  5620. !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);
  5621. } else {
  5622. !image && (image = new Image());
  5623. image.onload = imageOnLoad;
  5624. globalImageCache.put(newImageOrSrc, image.__cachedImgObj = {
  5625. image: image,
  5626. pending: [pendingWrap]
  5627. });
  5628. image.src = image.__zrImageSrc = newImageOrSrc;
  5629. }
  5630. return image;
  5631. } // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas
  5632. else {
  5633. return newImageOrSrc;
  5634. }
  5635. }
  5636. function imageOnLoad() {
  5637. var cachedImgObj = this.__cachedImgObj;
  5638. this.onload = this.__cachedImgObj = null;
  5639. for (var i = 0; i < cachedImgObj.pending.length; i++) {
  5640. var pendingWrap = cachedImgObj.pending[i];
  5641. var cb = pendingWrap.cb;
  5642. cb && cb(this, pendingWrap.cbPayload);
  5643. pendingWrap.hostEl.dirty();
  5644. }
  5645. cachedImgObj.pending.length = 0;
  5646. }
  5647. function isImageReady(image) {
  5648. return image && image.width && image.height;
  5649. }
  5650. var textWidthCache = {};
  5651. var textWidthCacheCounter = 0;
  5652. var TEXT_CACHE_MAX = 5000;
  5653. var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g;
  5654. var DEFAULT_FONT = '12px sans-serif'; // Avoid assign to an exported variable, for transforming to cjs.
  5655. var methods$1 = {};
  5656. function $override$1(name, fn) {
  5657. methods$1[name] = fn;
  5658. }
  5659. /**
  5660. * @public
  5661. * @param {string} text
  5662. * @param {string} font
  5663. * @return {number} width
  5664. */
  5665. function getWidth(text, font) {
  5666. font = font || DEFAULT_FONT;
  5667. var key = text + ':' + font;
  5668. if (textWidthCache[key]) {
  5669. return textWidthCache[key];
  5670. }
  5671. var textLines = (text + '').split('\n');
  5672. var width = 0;
  5673. for (var i = 0, l = textLines.length; i < l; i++) {
  5674. // textContain.measureText may be overrided in SVG or VML
  5675. width = Math.max(measureText(textLines[i], font).width, width);
  5676. }
  5677. if (textWidthCacheCounter > TEXT_CACHE_MAX) {
  5678. textWidthCacheCounter = 0;
  5679. textWidthCache = {};
  5680. }
  5681. textWidthCacheCounter++;
  5682. textWidthCache[key] = width;
  5683. return width;
  5684. }
  5685. /**
  5686. * @public
  5687. * @param {string} text
  5688. * @param {string} font
  5689. * @param {string} [textAlign='left']
  5690. * @param {string} [textVerticalAlign='top']
  5691. * @param {Array.<number>} [textPadding]
  5692. * @param {Object} [rich]
  5693. * @param {Object} [truncate]
  5694. * @return {Object} {x, y, width, height, lineHeight}
  5695. */
  5696. function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
  5697. return rich ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate);
  5698. }
  5699. function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate) {
  5700. var contentBlock = parsePlainText(text, font, textPadding, truncate);
  5701. var outerWidth = getWidth(text, font);
  5702. if (textPadding) {
  5703. outerWidth += textPadding[1] + textPadding[3];
  5704. }
  5705. var outerHeight = contentBlock.outerHeight;
  5706. var x = adjustTextX(0, outerWidth, textAlign);
  5707. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  5708. var rect = new BoundingRect(x, y, outerWidth, outerHeight);
  5709. rect.lineHeight = contentBlock.lineHeight;
  5710. return rect;
  5711. }
  5712. function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
  5713. var contentBlock = parseRichText(text, {
  5714. rich: rich,
  5715. truncate: truncate,
  5716. font: font,
  5717. textAlign: textAlign,
  5718. textPadding: textPadding
  5719. });
  5720. var outerWidth = contentBlock.outerWidth;
  5721. var outerHeight = contentBlock.outerHeight;
  5722. var x = adjustTextX(0, outerWidth, textAlign);
  5723. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  5724. return new BoundingRect(x, y, outerWidth, outerHeight);
  5725. }
  5726. /**
  5727. * @public
  5728. * @param {number} x
  5729. * @param {number} width
  5730. * @param {string} [textAlign='left']
  5731. * @return {number} Adjusted x.
  5732. */
  5733. function adjustTextX(x, width, textAlign) {
  5734. // FIXME Right to left language
  5735. if (textAlign === 'right') {
  5736. x -= width;
  5737. } else if (textAlign === 'center') {
  5738. x -= width / 2;
  5739. }
  5740. return x;
  5741. }
  5742. /**
  5743. * @public
  5744. * @param {number} y
  5745. * @param {number} height
  5746. * @param {string} [textVerticalAlign='top']
  5747. * @return {number} Adjusted y.
  5748. */
  5749. function adjustTextY(y, height, textVerticalAlign) {
  5750. if (textVerticalAlign === 'middle') {
  5751. y -= height / 2;
  5752. } else if (textVerticalAlign === 'bottom') {
  5753. y -= height;
  5754. }
  5755. return y;
  5756. }
  5757. /**
  5758. * @public
  5759. * @param {stirng} textPosition
  5760. * @param {Object} rect {x, y, width, height}
  5761. * @param {number} distance
  5762. * @return {Object} {x, y, textAlign, textVerticalAlign}
  5763. */
  5764. function adjustTextPositionOnRect(textPosition, rect, distance) {
  5765. var x = rect.x;
  5766. var y = rect.y;
  5767. var height = rect.height;
  5768. var width = rect.width;
  5769. var halfHeight = height / 2;
  5770. var textAlign = 'left';
  5771. var textVerticalAlign = 'top';
  5772. switch (textPosition) {
  5773. case 'left':
  5774. x -= distance;
  5775. y += halfHeight;
  5776. textAlign = 'right';
  5777. textVerticalAlign = 'middle';
  5778. break;
  5779. case 'right':
  5780. x += distance + width;
  5781. y += halfHeight;
  5782. textVerticalAlign = 'middle';
  5783. break;
  5784. case 'top':
  5785. x += width / 2;
  5786. y -= distance;
  5787. textAlign = 'center';
  5788. textVerticalAlign = 'bottom';
  5789. break;
  5790. case 'bottom':
  5791. x += width / 2;
  5792. y += height + distance;
  5793. textAlign = 'center';
  5794. break;
  5795. case 'inside':
  5796. x += width / 2;
  5797. y += halfHeight;
  5798. textAlign = 'center';
  5799. textVerticalAlign = 'middle';
  5800. break;
  5801. case 'insideLeft':
  5802. x += distance;
  5803. y += halfHeight;
  5804. textVerticalAlign = 'middle';
  5805. break;
  5806. case 'insideRight':
  5807. x += width - distance;
  5808. y += halfHeight;
  5809. textAlign = 'right';
  5810. textVerticalAlign = 'middle';
  5811. break;
  5812. case 'insideTop':
  5813. x += width / 2;
  5814. y += distance;
  5815. textAlign = 'center';
  5816. break;
  5817. case 'insideBottom':
  5818. x += width / 2;
  5819. y += height - distance;
  5820. textAlign = 'center';
  5821. textVerticalAlign = 'bottom';
  5822. break;
  5823. case 'insideTopLeft':
  5824. x += distance;
  5825. y += distance;
  5826. break;
  5827. case 'insideTopRight':
  5828. x += width - distance;
  5829. y += distance;
  5830. textAlign = 'right';
  5831. break;
  5832. case 'insideBottomLeft':
  5833. x += distance;
  5834. y += height - distance;
  5835. textVerticalAlign = 'bottom';
  5836. break;
  5837. case 'insideBottomRight':
  5838. x += width - distance;
  5839. y += height - distance;
  5840. textAlign = 'right';
  5841. textVerticalAlign = 'bottom';
  5842. break;
  5843. }
  5844. return {
  5845. x: x,
  5846. y: y,
  5847. textAlign: textAlign,
  5848. textVerticalAlign: textVerticalAlign
  5849. };
  5850. }
  5851. /**
  5852. * Show ellipsis if overflow.
  5853. *
  5854. * @public
  5855. * @param {string} text
  5856. * @param {string} containerWidth
  5857. * @param {string} font
  5858. * @param {number} [ellipsis='...']
  5859. * @param {Object} [options]
  5860. * @param {number} [options.maxIterations=3]
  5861. * @param {number} [options.minChar=0] If truncate result are less
  5862. * then minChar, ellipsis will not show, which is
  5863. * better for user hint in some cases.
  5864. * @param {number} [options.placeholder=''] When all truncated, use the placeholder.
  5865. * @return {string}
  5866. */
  5867. function truncateText(text, containerWidth, font, ellipsis, options) {
  5868. if (!containerWidth) {
  5869. return '';
  5870. }
  5871. var textLines = (text + '').split('\n');
  5872. options = prepareTruncateOptions(containerWidth, font, ellipsis, options); // FIXME
  5873. // It is not appropriate that every line has '...' when truncate multiple lines.
  5874. for (var i = 0, len = textLines.length; i < len; i++) {
  5875. textLines[i] = truncateSingleLine(textLines[i], options);
  5876. }
  5877. return textLines.join('\n');
  5878. }
  5879. function prepareTruncateOptions(containerWidth, font, ellipsis, options) {
  5880. options = extend({}, options);
  5881. options.font = font;
  5882. var ellipsis = retrieve2(ellipsis, '...');
  5883. options.maxIterations = retrieve2(options.maxIterations, 2);
  5884. var minChar = options.minChar = retrieve2(options.minChar, 0); // FIXME
  5885. // Other languages?
  5886. options.cnCharWidth = getWidth('国', font); // FIXME
  5887. // Consider proportional font?
  5888. var ascCharWidth = options.ascCharWidth = getWidth('a', font);
  5889. options.placeholder = retrieve2(options.placeholder, ''); // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'.
  5890. // Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'.
  5891. var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap.
  5892. for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {
  5893. contentWidth -= ascCharWidth;
  5894. }
  5895. var ellipsisWidth = getWidth(ellipsis);
  5896. if (ellipsisWidth > contentWidth) {
  5897. ellipsis = '';
  5898. ellipsisWidth = 0;
  5899. }
  5900. contentWidth = containerWidth - ellipsisWidth;
  5901. options.ellipsis = ellipsis;
  5902. options.ellipsisWidth = ellipsisWidth;
  5903. options.contentWidth = contentWidth;
  5904. options.containerWidth = containerWidth;
  5905. return options;
  5906. }
  5907. function truncateSingleLine(textLine, options) {
  5908. var containerWidth = options.containerWidth;
  5909. var font = options.font;
  5910. var contentWidth = options.contentWidth;
  5911. if (!containerWidth) {
  5912. return '';
  5913. }
  5914. var lineWidth = getWidth(textLine, font);
  5915. if (lineWidth <= containerWidth) {
  5916. return textLine;
  5917. }
  5918. for (var j = 0;; j++) {
  5919. if (lineWidth <= contentWidth || j >= options.maxIterations) {
  5920. textLine += options.ellipsis;
  5921. break;
  5922. }
  5923. var subLength = j === 0 ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth) : lineWidth > 0 ? Math.floor(textLine.length * contentWidth / lineWidth) : 0;
  5924. textLine = textLine.substr(0, subLength);
  5925. lineWidth = getWidth(textLine, font);
  5926. }
  5927. if (textLine === '') {
  5928. textLine = options.placeholder;
  5929. }
  5930. return textLine;
  5931. }
  5932. function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {
  5933. var width = 0;
  5934. var i = 0;
  5935. for (var len = text.length; i < len && width < contentWidth; i++) {
  5936. var charCode = text.charCodeAt(i);
  5937. width += 0 <= charCode && charCode <= 127 ? ascCharWidth : cnCharWidth;
  5938. }
  5939. return i;
  5940. }
  5941. /**
  5942. * @public
  5943. * @param {string} font
  5944. * @return {number} line height
  5945. */
  5946. function getLineHeight(font) {
  5947. // FIXME A rough approach.
  5948. return getWidth('国', font);
  5949. }
  5950. /**
  5951. * @public
  5952. * @param {string} text
  5953. * @param {string} font
  5954. * @return {Object} width
  5955. */
  5956. function measureText(text, font) {
  5957. return methods$1.measureText(text, font);
  5958. } // Avoid assign to an exported variable, for transforming to cjs.
  5959. methods$1.measureText = function (text, font) {
  5960. var ctx = getContext();
  5961. ctx.font = font || DEFAULT_FONT;
  5962. return ctx.measureText(text);
  5963. };
  5964. /**
  5965. * @public
  5966. * @param {string} text
  5967. * @param {string} font
  5968. * @param {Object} [truncate]
  5969. * @return {Object} block: {lineHeight, lines, height, outerHeight}
  5970. * Notice: for performance, do not calculate outerWidth util needed.
  5971. */
  5972. function parsePlainText(text, font, padding, truncate) {
  5973. text != null && (text += '');
  5974. var lineHeight = getLineHeight(font);
  5975. var lines = text ? text.split('\n') : [];
  5976. var height = lines.length * lineHeight;
  5977. var outerHeight = height;
  5978. if (padding) {
  5979. outerHeight += padding[0] + padding[2];
  5980. }
  5981. if (text && truncate) {
  5982. var truncOuterHeight = truncate.outerHeight;
  5983. var truncOuterWidth = truncate.outerWidth;
  5984. if (truncOuterHeight != null && outerHeight > truncOuterHeight) {
  5985. text = '';
  5986. lines = [];
  5987. } else if (truncOuterWidth != null) {
  5988. var options = prepareTruncateOptions(truncOuterWidth - (padding ? padding[1] + padding[3] : 0), font, truncate.ellipsis, {
  5989. minChar: truncate.minChar,
  5990. placeholder: truncate.placeholder
  5991. }); // FIXME
  5992. // It is not appropriate that every line has '...' when truncate multiple lines.
  5993. for (var i = 0, len = lines.length; i < len; i++) {
  5994. lines[i] = truncateSingleLine(lines[i], options);
  5995. }
  5996. }
  5997. }
  5998. return {
  5999. lines: lines,
  6000. height: height,
  6001. outerHeight: outerHeight,
  6002. lineHeight: lineHeight
  6003. };
  6004. }
  6005. /**
  6006. * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx'
  6007. * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'.
  6008. *
  6009. * @public
  6010. * @param {string} text
  6011. * @param {Object} style
  6012. * @return {Object} block
  6013. * {
  6014. * width,
  6015. * height,
  6016. * lines: [{
  6017. * lineHeight,
  6018. * width,
  6019. * tokens: [[{
  6020. * styleName,
  6021. * text,
  6022. * width, // include textPadding
  6023. * height, // include textPadding
  6024. * textWidth, // pure text width
  6025. * textHeight, // pure text height
  6026. * lineHeihgt,
  6027. * font,
  6028. * textAlign,
  6029. * textVerticalAlign
  6030. * }], [...], ...]
  6031. * }, ...]
  6032. * }
  6033. * If styleName is undefined, it is plain text.
  6034. */
  6035. function parseRichText(text, style) {
  6036. var contentBlock = {
  6037. lines: [],
  6038. width: 0,
  6039. height: 0
  6040. };
  6041. text != null && (text += '');
  6042. if (!text) {
  6043. return contentBlock;
  6044. }
  6045. var lastIndex = STYLE_REG.lastIndex = 0;
  6046. var result;
  6047. while ((result = STYLE_REG.exec(text)) != null) {
  6048. var matchedIndex = result.index;
  6049. if (matchedIndex > lastIndex) {
  6050. pushTokens(contentBlock, text.substring(lastIndex, matchedIndex));
  6051. }
  6052. pushTokens(contentBlock, result[2], result[1]);
  6053. lastIndex = STYLE_REG.lastIndex;
  6054. }
  6055. if (lastIndex < text.length) {
  6056. pushTokens(contentBlock, text.substring(lastIndex, text.length));
  6057. }
  6058. var lines = contentBlock.lines;
  6059. var contentHeight = 0;
  6060. var contentWidth = 0; // For `textWidth: 100%`
  6061. var pendingList = [];
  6062. var stlPadding = style.textPadding;
  6063. var truncate = style.truncate;
  6064. var truncateWidth = truncate && truncate.outerWidth;
  6065. var truncateHeight = truncate && truncate.outerHeight;
  6066. if (stlPadding) {
  6067. truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]);
  6068. truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]);
  6069. } // Calculate layout info of tokens.
  6070. for (var i = 0; i < lines.length; i++) {
  6071. var line = lines[i];
  6072. var lineHeight = 0;
  6073. var lineWidth = 0;
  6074. for (var j = 0; j < line.tokens.length; j++) {
  6075. var token = line.tokens[j];
  6076. var tokenStyle = token.styleName && style.rich[token.styleName] || {}; // textPadding should not inherit from style.
  6077. var textPadding = token.textPadding = tokenStyle.textPadding; // textFont has been asigned to font by `normalizeStyle`.
  6078. var font = token.font = tokenStyle.font || style.font; // textHeight can be used when textVerticalAlign is specified in token.
  6079. var tokenHeight = token.textHeight = retrieve2( // textHeight should not be inherited, consider it can be specified
  6080. // as box height of the block.
  6081. tokenStyle.textHeight, getLineHeight(font));
  6082. textPadding && (tokenHeight += textPadding[0] + textPadding[2]);
  6083. token.height = tokenHeight;
  6084. token.lineHeight = retrieve3(tokenStyle.textLineHeight, style.textLineHeight, tokenHeight);
  6085. token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign;
  6086. token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle';
  6087. if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) {
  6088. return {
  6089. lines: [],
  6090. width: 0,
  6091. height: 0
  6092. };
  6093. }
  6094. token.textWidth = getWidth(token.text, font);
  6095. var tokenWidth = tokenStyle.textWidth;
  6096. var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto'; // Percent width, can be `100%`, can be used in drawing separate
  6097. // line when box width is needed to be auto.
  6098. if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') {
  6099. token.percentWidth = tokenWidth;
  6100. pendingList.push(token);
  6101. tokenWidth = 0; // Do not truncate in this case, because there is no user case
  6102. // and it is too complicated.
  6103. } else {
  6104. if (tokenWidthNotSpecified) {
  6105. tokenWidth = token.textWidth; // FIXME: If image is not loaded and textWidth is not specified, calling
  6106. // `getBoundingRect()` will not get correct result.
  6107. var textBackgroundColor = tokenStyle.textBackgroundColor;
  6108. var bgImg = textBackgroundColor && textBackgroundColor.image; // Use cases:
  6109. // (1) If image is not loaded, it will be loaded at render phase and call
  6110. // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded
  6111. // image, and then the right size will be calculated here at the next tick.
  6112. // See `graphic/helper/text.js`.
  6113. // (2) If image loaded, and `textBackgroundColor.image` is image src string,
  6114. // use `imageHelper.findExistImage` to find cached image.
  6115. // `imageHelper.findExistImage` will always be called here before
  6116. // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`
  6117. // which ensures that image will not be rendered before correct size calcualted.
  6118. if (bgImg) {
  6119. bgImg = findExistImage(bgImg);
  6120. if (isImageReady(bgImg)) {
  6121. tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height);
  6122. }
  6123. }
  6124. }
  6125. var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0;
  6126. tokenWidth += paddingW;
  6127. var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null;
  6128. if (remianTruncWidth != null && remianTruncWidth < tokenWidth) {
  6129. if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) {
  6130. token.text = '';
  6131. token.textWidth = tokenWidth = 0;
  6132. } else {
  6133. token.text = truncateText(token.text, remianTruncWidth - paddingW, font, truncate.ellipsis, {
  6134. minChar: truncate.minChar
  6135. });
  6136. token.textWidth = getWidth(token.text, font);
  6137. tokenWidth = token.textWidth + paddingW;
  6138. }
  6139. }
  6140. }
  6141. lineWidth += token.width = tokenWidth;
  6142. tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));
  6143. }
  6144. line.width = lineWidth;
  6145. line.lineHeight = lineHeight;
  6146. contentHeight += lineHeight;
  6147. contentWidth = Math.max(contentWidth, lineWidth);
  6148. }
  6149. contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth);
  6150. contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight);
  6151. if (stlPadding) {
  6152. contentBlock.outerWidth += stlPadding[1] + stlPadding[3];
  6153. contentBlock.outerHeight += stlPadding[0] + stlPadding[2];
  6154. }
  6155. for (var i = 0; i < pendingList.length; i++) {
  6156. var token = pendingList[i];
  6157. var percentWidth = token.percentWidth; // Should not base on outerWidth, because token can not be placed out of padding.
  6158. token.width = parseInt(percentWidth, 10) / 100 * contentWidth;
  6159. }
  6160. return contentBlock;
  6161. }
  6162. function pushTokens(block, str, styleName) {
  6163. var isEmptyStr = str === '';
  6164. var strs = str.split('\n');
  6165. var lines = block.lines;
  6166. for (var i = 0; i < strs.length; i++) {
  6167. var text = strs[i];
  6168. var token = {
  6169. styleName: styleName,
  6170. text: text,
  6171. isLineHolder: !text && !isEmptyStr
  6172. }; // The first token should be appended to the last line.
  6173. if (!i) {
  6174. var tokens = (lines[lines.length - 1] || (lines[0] = {
  6175. tokens: []
  6176. })).tokens; // Consider cases:
  6177. // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item
  6178. // (which is a placeholder) should be replaced by new token.
  6179. // (2) A image backage, where token likes {a|}.
  6180. // (3) A redundant '' will affect textAlign in line.
  6181. // (4) tokens with the same tplName should not be merged, because
  6182. // they should be displayed in different box (with border and padding).
  6183. var tokensLen = tokens.length;
  6184. tokensLen === 1 && tokens[0].isLineHolder ? tokens[0] = token : // Consider text is '', only insert when it is the "lineHolder" or
  6185. // "emptyStr". Otherwise a redundant '' will affect textAlign in line.
  6186. (text || !tokensLen || isEmptyStr) && tokens.push(token);
  6187. } // Other tokens always start a new line.
  6188. else {
  6189. // If there is '', insert it as a placeholder.
  6190. lines.push({
  6191. tokens: [token]
  6192. });
  6193. }
  6194. }
  6195. }
  6196. function makeFont(style) {
  6197. // FIXME in node-canvas fontWeight is before fontStyle
  6198. // Use `fontSize` `fontFamily` to check whether font properties are defined.
  6199. return (style.fontSize || style.fontFamily) && [style.fontStyle, style.fontWeight, (style.fontSize || 12) + 'px', // If font properties are defined, `fontFamily` should not be ignored.
  6200. style.fontFamily || 'sans-serif'].join(' ') || style.textFont || style.font;
  6201. }
  6202. function buildPath(ctx, shape) {
  6203. var x = shape.x;
  6204. var y = shape.y;
  6205. var width = shape.width;
  6206. var height = shape.height;
  6207. var r = shape.r;
  6208. var r1;
  6209. var r2;
  6210. var r3;
  6211. var r4; // Convert width and height to positive for better borderRadius
  6212. if (width < 0) {
  6213. x = x + width;
  6214. width = -width;
  6215. }
  6216. if (height < 0) {
  6217. y = y + height;
  6218. height = -height;
  6219. }
  6220. if (typeof r === 'number') {
  6221. r1 = r2 = r3 = r4 = r;
  6222. } else if (r instanceof Array) {
  6223. if (r.length === 1) {
  6224. r1 = r2 = r3 = r4 = r[0];
  6225. } else if (r.length === 2) {
  6226. r1 = r3 = r[0];
  6227. r2 = r4 = r[1];
  6228. } else if (r.length === 3) {
  6229. r1 = r[0];
  6230. r2 = r4 = r[1];
  6231. r3 = r[2];
  6232. } else {
  6233. r1 = r[0];
  6234. r2 = r[1];
  6235. r3 = r[2];
  6236. r4 = r[3];
  6237. }
  6238. } else {
  6239. r1 = r2 = r3 = r4 = 0;
  6240. }
  6241. var total;
  6242. if (r1 + r2 > width) {
  6243. total = r1 + r2;
  6244. r1 *= width / total;
  6245. r2 *= width / total;
  6246. }
  6247. if (r3 + r4 > width) {
  6248. total = r3 + r4;
  6249. r3 *= width / total;
  6250. r4 *= width / total;
  6251. }
  6252. if (r2 + r3 > height) {
  6253. total = r2 + r3;
  6254. r2 *= height / total;
  6255. r3 *= height / total;
  6256. }
  6257. if (r1 + r4 > height) {
  6258. total = r1 + r4;
  6259. r1 *= height / total;
  6260. r4 *= height / total;
  6261. }
  6262. ctx.moveTo(x + r1, y);
  6263. ctx.lineTo(x + width - r2, y);
  6264. r2 !== 0 && ctx.quadraticCurveTo(x + width, y, x + width, y + r2);
  6265. ctx.lineTo(x + width, y + height - r3);
  6266. r3 !== 0 && ctx.quadraticCurveTo(x + width, y + height, x + width - r3, y + height);
  6267. ctx.lineTo(x + r4, y + height);
  6268. r4 !== 0 && ctx.quadraticCurveTo(x, y + height, x, y + height - r4);
  6269. ctx.lineTo(x, y + r1);
  6270. r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y);
  6271. } // TODO: Have not support 'start', 'end' yet.
  6272. var VALID_TEXT_ALIGN = {
  6273. left: 1,
  6274. right: 1,
  6275. center: 1
  6276. };
  6277. var VALID_TEXT_VERTICAL_ALIGN = {
  6278. top: 1,
  6279. bottom: 1,
  6280. middle: 1
  6281. };
  6282. /**
  6283. * @param {module:zrender/graphic/Style} style
  6284. * @return {module:zrender/graphic/Style} The input style.
  6285. */
  6286. function normalizeTextStyle(style) {
  6287. normalizeStyle(style);
  6288. each$1(style.rich, normalizeStyle);
  6289. return style;
  6290. }
  6291. function normalizeStyle(style) {
  6292. if (style) {
  6293. style.font = makeFont(style);
  6294. var textAlign = style.textAlign;
  6295. textAlign === 'middle' && (textAlign = 'center');
  6296. style.textAlign = textAlign == null || VALID_TEXT_ALIGN[textAlign] ? textAlign : 'left'; // Compatible with textBaseline.
  6297. var textVerticalAlign = style.textVerticalAlign || style.textBaseline;
  6298. textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
  6299. style.textVerticalAlign = textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign] ? textVerticalAlign : 'top';
  6300. var textPadding = style.textPadding;
  6301. if (textPadding) {
  6302. style.textPadding = normalizeCssArray(style.textPadding);
  6303. }
  6304. }
  6305. }
  6306. /**
  6307. * @param {CanvasRenderingContext2D} ctx
  6308. * @param {string} text
  6309. * @param {module:zrender/graphic/Style} style
  6310. * @param {Object|boolean} [rect] {x, y, width, height}
  6311. * If set false, rect text is not used.
  6312. */
  6313. function renderText(hostEl, ctx, text, style, rect) {
  6314. style.rich ? renderRichText(hostEl, ctx, text, style, rect) : renderPlainText(hostEl, ctx, text, style, rect);
  6315. }
  6316. function renderPlainText(hostEl, ctx, text, style, rect) {
  6317. var font = setCtx(ctx, 'font', style.font || DEFAULT_FONT);
  6318. var textPadding = style.textPadding;
  6319. var contentBlock = hostEl.__textCotentBlock;
  6320. if (!contentBlock || hostEl.__dirty) {
  6321. contentBlock = hostEl.__textCotentBlock = parsePlainText(text, font, textPadding, style.truncate);
  6322. }
  6323. var outerHeight = contentBlock.outerHeight;
  6324. var textLines = contentBlock.lines;
  6325. var lineHeight = contentBlock.lineHeight;
  6326. var boxPos = getBoxPosition(outerHeight, style, rect);
  6327. var baseX = boxPos.baseX;
  6328. var baseY = boxPos.baseY;
  6329. var textAlign = boxPos.textAlign;
  6330. var textVerticalAlign = boxPos.textVerticalAlign; // Origin of textRotation should be the base point of text drawing.
  6331. applyTextRotation(ctx, style, rect, baseX, baseY);
  6332. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  6333. var textX = baseX;
  6334. var textY = boxY;
  6335. var needDrawBg = needDrawBackground(style);
  6336. if (needDrawBg || textPadding) {
  6337. // Consider performance, do not call getTextWidth util necessary.
  6338. var textWidth = getWidth(text, font);
  6339. var outerWidth = textWidth;
  6340. textPadding && (outerWidth += textPadding[1] + textPadding[3]);
  6341. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  6342. needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
  6343. if (textPadding) {
  6344. textX = getTextXForPadding(baseX, textAlign, textPadding);
  6345. textY += textPadding[0];
  6346. }
  6347. }
  6348. setCtx(ctx, 'textAlign', textAlign || 'left'); // Force baseline to be "middle". Otherwise, if using "top", the
  6349. // text will offset downward a little bit in font "Microsoft YaHei".
  6350. setCtx(ctx, 'textBaseline', 'middle'); // Always set shadowBlur and shadowOffset to avoid leak from displayable.
  6351. setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0);
  6352. setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent');
  6353. setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0);
  6354. setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0); // `textBaseline` is set as 'middle'.
  6355. textY += lineHeight / 2;
  6356. var textStrokeWidth = style.textStrokeWidth;
  6357. var textStroke = getStroke(style.textStroke, textStrokeWidth);
  6358. var textFill = getFill(style.textFill);
  6359. if (textStroke) {
  6360. setCtx(ctx, 'lineWidth', textStrokeWidth);
  6361. setCtx(ctx, 'strokeStyle', textStroke);
  6362. }
  6363. if (textFill) {
  6364. setCtx(ctx, 'fillStyle', textFill);
  6365. }
  6366. for (var i = 0; i < textLines.length; i++) {
  6367. // Fill after stroke so the outline will not cover the main part.
  6368. textStroke && ctx.strokeText(textLines[i], textX, textY);
  6369. textFill && ctx.fillText(textLines[i], textX, textY);
  6370. textY += lineHeight;
  6371. }
  6372. }
  6373. function renderRichText(hostEl, ctx, text, style, rect) {
  6374. var contentBlock = hostEl.__textCotentBlock;
  6375. if (!contentBlock || hostEl.__dirty) {
  6376. contentBlock = hostEl.__textCotentBlock = parseRichText(text, style);
  6377. }
  6378. drawRichText(hostEl, ctx, contentBlock, style, rect);
  6379. }
  6380. function drawRichText(hostEl, ctx, contentBlock, style, rect) {
  6381. var contentWidth = contentBlock.width;
  6382. var outerWidth = contentBlock.outerWidth;
  6383. var outerHeight = contentBlock.outerHeight;
  6384. var textPadding = style.textPadding;
  6385. var boxPos = getBoxPosition(outerHeight, style, rect);
  6386. var baseX = boxPos.baseX;
  6387. var baseY = boxPos.baseY;
  6388. var textAlign = boxPos.textAlign;
  6389. var textVerticalAlign = boxPos.textVerticalAlign; // Origin of textRotation should be the base point of text drawing.
  6390. applyTextRotation(ctx, style, rect, baseX, baseY);
  6391. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  6392. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  6393. var xLeft = boxX;
  6394. var lineTop = boxY;
  6395. if (textPadding) {
  6396. xLeft += textPadding[3];
  6397. lineTop += textPadding[0];
  6398. }
  6399. var xRight = xLeft + contentWidth;
  6400. needDrawBackground(style) && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
  6401. for (var i = 0; i < contentBlock.lines.length; i++) {
  6402. var line = contentBlock.lines[i];
  6403. var tokens = line.tokens;
  6404. var tokenCount = tokens.length;
  6405. var lineHeight = line.lineHeight;
  6406. var usedWidth = line.width;
  6407. var leftIndex = 0;
  6408. var lineXLeft = xLeft;
  6409. var lineXRight = xRight;
  6410. var rightIndex = tokenCount - 1;
  6411. var token;
  6412. while (leftIndex < tokenCount && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')) {
  6413. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');
  6414. usedWidth -= token.width;
  6415. lineXLeft += token.width;
  6416. leftIndex++;
  6417. }
  6418. while (rightIndex >= 0 && (token = tokens[rightIndex], token.textAlign === 'right')) {
  6419. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');
  6420. usedWidth -= token.width;
  6421. lineXRight -= token.width;
  6422. rightIndex--;
  6423. } // The other tokens are placed as textAlign 'center' if there is enough space.
  6424. lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
  6425. while (leftIndex <= rightIndex) {
  6426. token = tokens[leftIndex]; // Consider width specified by user, use 'center' rather than 'left'.
  6427. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
  6428. lineXLeft += token.width;
  6429. leftIndex++;
  6430. }
  6431. lineTop += lineHeight;
  6432. }
  6433. }
  6434. function applyTextRotation(ctx, style, rect, x, y) {
  6435. // textRotation only apply in RectText.
  6436. if (rect && style.textRotation) {
  6437. var origin = style.textOrigin;
  6438. if (origin === 'center') {
  6439. x = rect.width / 2 + rect.x;
  6440. y = rect.height / 2 + rect.y;
  6441. } else if (origin) {
  6442. x = origin[0] + rect.x;
  6443. y = origin[1] + rect.y;
  6444. }
  6445. ctx.translate(x, y); // Positive: anticlockwise
  6446. ctx.rotate(-style.textRotation);
  6447. ctx.translate(-x, -y);
  6448. }
  6449. }
  6450. function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {
  6451. var tokenStyle = style.rich[token.styleName] || {}; // 'ctx.textBaseline' is always set as 'middle', for sake of
  6452. // the bias of "Microsoft YaHei".
  6453. var textVerticalAlign = token.textVerticalAlign;
  6454. var y = lineTop + lineHeight / 2;
  6455. if (textVerticalAlign === 'top') {
  6456. y = lineTop + token.height / 2;
  6457. } else if (textVerticalAlign === 'bottom') {
  6458. y = lineTop + lineHeight - token.height / 2;
  6459. }
  6460. !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(hostEl, ctx, tokenStyle, textAlign === 'right' ? x - token.width : textAlign === 'center' ? x - token.width / 2 : x, y - token.height / 2, token.width, token.height);
  6461. var textPadding = token.textPadding;
  6462. if (textPadding) {
  6463. x = getTextXForPadding(x, textAlign, textPadding);
  6464. y -= token.height / 2 - textPadding[2] - token.textHeight / 2;
  6465. }
  6466. setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));
  6467. setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');
  6468. setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));
  6469. setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));
  6470. setCtx(ctx, 'textAlign', textAlign); // Force baseline to be "middle". Otherwise, if using "top", the
  6471. // text will offset downward a little bit in font "Microsoft YaHei".
  6472. setCtx(ctx, 'textBaseline', 'middle');
  6473. setCtx(ctx, 'font', token.font || DEFAULT_FONT);
  6474. var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);
  6475. var textFill = getFill(tokenStyle.textFill || style.textFill);
  6476. var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth); // Fill after stroke so the outline will not cover the main part.
  6477. if (textStroke) {
  6478. setCtx(ctx, 'lineWidth', textStrokeWidth);
  6479. setCtx(ctx, 'strokeStyle', textStroke);
  6480. ctx.strokeText(token.text, x, y);
  6481. }
  6482. if (textFill) {
  6483. setCtx(ctx, 'fillStyle', textFill);
  6484. ctx.fillText(token.text, x, y);
  6485. }
  6486. }
  6487. function needDrawBackground(style) {
  6488. return style.textBackgroundColor || style.textBorderWidth && style.textBorderColor;
  6489. } // style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius}
  6490. // shape: {x, y, width, height}
  6491. function drawBackground(hostEl, ctx, style, x, y, width, height) {
  6492. var textBackgroundColor = style.textBackgroundColor;
  6493. var textBorderWidth = style.textBorderWidth;
  6494. var textBorderColor = style.textBorderColor;
  6495. var isPlainBg = isString(textBackgroundColor);
  6496. setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);
  6497. setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');
  6498. setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);
  6499. setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);
  6500. if (isPlainBg || textBorderWidth && textBorderColor) {
  6501. ctx.beginPath();
  6502. var textBorderRadius = style.textBorderRadius;
  6503. if (!textBorderRadius) {
  6504. ctx.rect(x, y, width, height);
  6505. } else {
  6506. buildPath(ctx, {
  6507. x: x,
  6508. y: y,
  6509. width: width,
  6510. height: height,
  6511. r: textBorderRadius
  6512. });
  6513. }
  6514. ctx.closePath();
  6515. }
  6516. if (isPlainBg) {
  6517. setCtx(ctx, 'fillStyle', textBackgroundColor);
  6518. ctx.fill();
  6519. } else if (isObject(textBackgroundColor)) {
  6520. var image = textBackgroundColor.image;
  6521. image = createOrUpdateImage(image, null, hostEl, onBgImageLoaded, textBackgroundColor);
  6522. if (image && isImageReady(image)) {
  6523. ctx.drawImage(image, x, y, width, height);
  6524. }
  6525. }
  6526. if (textBorderWidth && textBorderColor) {
  6527. setCtx(ctx, 'lineWidth', textBorderWidth);
  6528. setCtx(ctx, 'strokeStyle', textBorderColor);
  6529. ctx.stroke();
  6530. }
  6531. }
  6532. function onBgImageLoaded(image, textBackgroundColor) {
  6533. // Replace image, so that `contain/text.js#parseRichText`
  6534. // will get correct result in next tick.
  6535. textBackgroundColor.image = image;
  6536. }
  6537. function getBoxPosition(blockHeiht, style, rect) {
  6538. var baseX = style.x || 0;
  6539. var baseY = style.y || 0;
  6540. var textAlign = style.textAlign;
  6541. var textVerticalAlign = style.textVerticalAlign; // Text position represented by coord
  6542. if (rect) {
  6543. var textPosition = style.textPosition;
  6544. if (textPosition instanceof Array) {
  6545. // Percent
  6546. baseX = rect.x + parsePercent(textPosition[0], rect.width);
  6547. baseY = rect.y + parsePercent(textPosition[1], rect.height);
  6548. } else {
  6549. var res = adjustTextPositionOnRect(textPosition, rect, style.textDistance);
  6550. baseX = res.x;
  6551. baseY = res.y; // Default align and baseline when has textPosition
  6552. textAlign = textAlign || res.textAlign;
  6553. textVerticalAlign = textVerticalAlign || res.textVerticalAlign;
  6554. } // textOffset is only support in RectText, otherwise
  6555. // we have to adjust boundingRect for textOffset.
  6556. var textOffset = style.textOffset;
  6557. if (textOffset) {
  6558. baseX += textOffset[0];
  6559. baseY += textOffset[1];
  6560. }
  6561. }
  6562. return {
  6563. baseX: baseX,
  6564. baseY: baseY,
  6565. textAlign: textAlign,
  6566. textVerticalAlign: textVerticalAlign
  6567. };
  6568. }
  6569. function setCtx(ctx, prop, value) {
  6570. // FIXME ??? performance try
  6571. // if (ctx.__currentValues[prop] !== value) {
  6572. // ctx[prop] = ctx.__currentValues[prop] = value;
  6573. ctx[prop] = value; // }
  6574. return ctx[prop];
  6575. }
  6576. /**
  6577. * @param {string} [stroke] If specified, do not check style.textStroke.
  6578. * @param {string} [lineWidth] If specified, do not check style.textStroke.
  6579. * @param {number} style
  6580. */
  6581. function getStroke(stroke, lineWidth) {
  6582. return stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none' ? null // TODO pattern and gradient?
  6583. : stroke.image || stroke.colorStops ? '#000' : stroke;
  6584. }
  6585. function getFill(fill) {
  6586. return fill == null || fill === 'none' ? null // TODO pattern and gradient?
  6587. : fill.image || fill.colorStops ? '#000' : fill;
  6588. }
  6589. function parsePercent(value, maxValue) {
  6590. if (typeof value === 'string') {
  6591. if (value.lastIndexOf('%') >= 0) {
  6592. return parseFloat(value) / 100 * maxValue;
  6593. }
  6594. return parseFloat(value);
  6595. }
  6596. return value;
  6597. }
  6598. function getTextXForPadding(x, textAlign, textPadding) {
  6599. return textAlign === 'right' ? x - textPadding[1] : textAlign === 'center' ? x + textPadding[3] / 2 - textPadding[1] / 2 : x + textPadding[3];
  6600. }
  6601. /**
  6602. * @param {string} text
  6603. * @param {module:zrender/Style} style
  6604. * @return {boolean}
  6605. */
  6606. function needDrawText(text, style) {
  6607. return text != null && (text || style.textBackgroundColor || style.textBorderWidth && style.textBorderColor || style.textPadding);
  6608. }
  6609. /**
  6610. * Mixin for drawing text in a element bounding rect
  6611. * @module zrender/mixin/RectText
  6612. */
  6613. var tmpRect$1 = new BoundingRect();
  6614. var RectText = function () {};
  6615. RectText.prototype = {
  6616. constructor: RectText,
  6617. /**
  6618. * Draw text in a rect with specified position.
  6619. * @param {CanvasRenderingContext2D} ctx
  6620. * @param {Object} rect Displayable rect
  6621. */
  6622. drawRectText: function (ctx, rect) {
  6623. var style = this.style;
  6624. rect = style.textRect || rect; // Optimize, avoid normalize every time.
  6625. this.__dirty && normalizeTextStyle(style, true);
  6626. var text = style.text; // Convert to string
  6627. text != null && (text += '');
  6628. if (!needDrawText(text, style)) {
  6629. return;
  6630. } // FIXME
  6631. ctx.save(); // Transform rect to view space
  6632. var transform = this.transform;
  6633. if (!style.transformText) {
  6634. if (transform) {
  6635. tmpRect$1.copy(rect);
  6636. tmpRect$1.applyTransform(transform);
  6637. rect = tmpRect$1;
  6638. }
  6639. } else {
  6640. this.setTransform(ctx);
  6641. } // transformText and textRotation can not be used at the same time.
  6642. renderText(this, ctx, text, style, rect);
  6643. ctx.restore();
  6644. }
  6645. };
  6646. /**
  6647. * 可绘制的图形基类
  6648. * Base class of all displayable graphic objects
  6649. * @module zrender/graphic/Displayable
  6650. */
  6651. /**
  6652. * @alias module:zrender/graphic/Displayable
  6653. * @extends module:zrender/Element
  6654. * @extends module:zrender/graphic/mixin/RectText
  6655. */
  6656. function Displayable(opts) {
  6657. opts = opts || {};
  6658. Element.call(this, opts); // Extend properties
  6659. for (var name in opts) {
  6660. if (opts.hasOwnProperty(name) && name !== 'style') {
  6661. this[name] = opts[name];
  6662. }
  6663. }
  6664. /**
  6665. * @type {module:zrender/graphic/Style}
  6666. */
  6667. this.style = new Style(opts.style, this);
  6668. this._rect = null; // Shapes for cascade clipping.
  6669. this.__clipPaths = []; // FIXME Stateful must be mixined after style is setted
  6670. // Stateful.call(this, opts);
  6671. }
  6672. Displayable.prototype = {
  6673. constructor: Displayable,
  6674. type: 'displayable',
  6675. /**
  6676. * Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制
  6677. * Dirty flag. From which painter will determine if this displayable object needs brush
  6678. * @name module:zrender/graphic/Displayable#__dirty
  6679. * @type {boolean}
  6680. */
  6681. __dirty: true,
  6682. /**
  6683. * 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件
  6684. * If ignore drawing of the displayable object. Mouse event will still be triggered
  6685. * @name module:/zrender/graphic/Displayable#invisible
  6686. * @type {boolean}
  6687. * @default false
  6688. */
  6689. invisible: false,
  6690. /**
  6691. * @name module:/zrender/graphic/Displayable#z
  6692. * @type {number}
  6693. * @default 0
  6694. */
  6695. z: 0,
  6696. /**
  6697. * @name module:/zrender/graphic/Displayable#z
  6698. * @type {number}
  6699. * @default 0
  6700. */
  6701. z2: 0,
  6702. /**
  6703. * z层level,决定绘画在哪层canvas中
  6704. * @name module:/zrender/graphic/Displayable#zlevel
  6705. * @type {number}
  6706. * @default 0
  6707. */
  6708. zlevel: 0,
  6709. /**
  6710. * 是否可拖拽
  6711. * @name module:/zrender/graphic/Displayable#draggable
  6712. * @type {boolean}
  6713. * @default false
  6714. */
  6715. draggable: false,
  6716. /**
  6717. * 是否正在拖拽
  6718. * @name module:/zrender/graphic/Displayable#draggable
  6719. * @type {boolean}
  6720. * @default false
  6721. */
  6722. dragging: false,
  6723. /**
  6724. * 是否相应鼠标事件
  6725. * @name module:/zrender/graphic/Displayable#silent
  6726. * @type {boolean}
  6727. * @default false
  6728. */
  6729. silent: false,
  6730. /**
  6731. * If enable culling
  6732. * @type {boolean}
  6733. * @default false
  6734. */
  6735. culling: false,
  6736. /**
  6737. * Mouse cursor when hovered
  6738. * @name module:/zrender/graphic/Displayable#cursor
  6739. * @type {string}
  6740. */
  6741. cursor: 'pointer',
  6742. /**
  6743. * If hover area is bounding rect
  6744. * @name module:/zrender/graphic/Displayable#rectHover
  6745. * @type {string}
  6746. */
  6747. rectHover: false,
  6748. /**
  6749. * Render the element progressively when the value >= 0,
  6750. * usefull for large data.
  6751. * @type {number}
  6752. */
  6753. progressive: -1,
  6754. beforeBrush: function (ctx) {},
  6755. afterBrush: function (ctx) {},
  6756. /**
  6757. * 图形绘制方法
  6758. * @param {CanvasRenderingContext2D} ctx
  6759. */
  6760. // Interface
  6761. brush: function (ctx, prevEl) {},
  6762. /**
  6763. * 获取最小包围盒
  6764. * @return {module:zrender/core/BoundingRect}
  6765. */
  6766. // Interface
  6767. getBoundingRect: function () {},
  6768. /**
  6769. * 判断坐标 x, y 是否在图形上
  6770. * If displayable element contain coord x, y
  6771. * @param {number} x
  6772. * @param {number} y
  6773. * @return {boolean}
  6774. */
  6775. contain: function (x, y) {
  6776. return this.rectContain(x, y);
  6777. },
  6778. /**
  6779. * @param {Function} cb
  6780. * @param {} context
  6781. */
  6782. traverse: function (cb, context) {
  6783. cb.call(context, this);
  6784. },
  6785. /**
  6786. * 判断坐标 x, y 是否在图形的包围盒上
  6787. * If bounding rect of element contain coord x, y
  6788. * @param {number} x
  6789. * @param {number} y
  6790. * @return {boolean}
  6791. */
  6792. rectContain: function (x, y) {
  6793. var coord = this.transformCoordToLocal(x, y);
  6794. var rect = this.getBoundingRect();
  6795. return rect.contain(coord[0], coord[1]);
  6796. },
  6797. /**
  6798. * 标记图形元素为脏,并且在下一帧重绘
  6799. * Mark displayable element dirty and refresh next frame
  6800. */
  6801. dirty: function () {
  6802. this.__dirty = true;
  6803. this._rect = null;
  6804. this.__zr && this.__zr.refresh();
  6805. },
  6806. /**
  6807. * 图形是否会触发事件
  6808. * If displayable object binded any event
  6809. * @return {boolean}
  6810. */
  6811. // TODO, 通过 bind 绑定的事件
  6812. // isSilent: function () {
  6813. // return !(
  6814. // this.hoverable || this.draggable
  6815. // || this.onmousemove || this.onmouseover || this.onmouseout
  6816. // || this.onmousedown || this.onmouseup || this.onclick
  6817. // || this.ondragenter || this.ondragover || this.ondragleave
  6818. // || this.ondrop
  6819. // );
  6820. // },
  6821. /**
  6822. * Alias for animate('style')
  6823. * @param {boolean} loop
  6824. */
  6825. animateStyle: function (loop) {
  6826. return this.animate('style', loop);
  6827. },
  6828. attrKV: function (key, value) {
  6829. if (key !== 'style') {
  6830. Element.prototype.attrKV.call(this, key, value);
  6831. } else {
  6832. this.style.set(value);
  6833. }
  6834. },
  6835. /**
  6836. * @param {Object|string} key
  6837. * @param {*} value
  6838. */
  6839. setStyle: function (key, value) {
  6840. this.style.set(key, value);
  6841. this.dirty(false);
  6842. return this;
  6843. },
  6844. /**
  6845. * Use given style object
  6846. * @param {Object} obj
  6847. */
  6848. useStyle: function (obj) {
  6849. this.style = new Style(obj, this);
  6850. this.dirty(false);
  6851. return this;
  6852. }
  6853. };
  6854. inherits(Displayable, Element);
  6855. mixin(Displayable, RectText);
  6856. /**
  6857. * @alias zrender/graphic/Image
  6858. * @extends module:zrender/graphic/Displayable
  6859. * @constructor
  6860. * @param {Object} opts
  6861. */
  6862. function ZImage(opts) {
  6863. Displayable.call(this, opts);
  6864. }
  6865. ZImage.prototype = {
  6866. constructor: ZImage,
  6867. type: 'image',
  6868. brush: function (ctx, prevEl) {
  6869. var style = this.style;
  6870. var src = style.image; // Must bind each time
  6871. style.bind(ctx, this, prevEl);
  6872. var image = this._image = createOrUpdateImage(src, this._image, this, this.onload);
  6873. if (!image || !isImageReady(image)) {
  6874. return;
  6875. } // 图片已经加载完成
  6876. // if (image.nodeName.toUpperCase() == 'IMG') {
  6877. // if (!image.complete) {
  6878. // return;
  6879. // }
  6880. // }
  6881. // Else is canvas
  6882. var x = style.x || 0;
  6883. var y = style.y || 0;
  6884. var width = style.width;
  6885. var height = style.height;
  6886. var aspect = image.width / image.height;
  6887. if (width == null && height != null) {
  6888. // Keep image/height ratio
  6889. width = height * aspect;
  6890. } else if (height == null && width != null) {
  6891. height = width / aspect;
  6892. } else if (width == null && height == null) {
  6893. width = image.width;
  6894. height = image.height;
  6895. } // 设置transform
  6896. this.setTransform(ctx);
  6897. if (style.sWidth && style.sHeight) {
  6898. var sx = style.sx || 0;
  6899. var sy = style.sy || 0;
  6900. ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height);
  6901. } else if (style.sx && style.sy) {
  6902. var sx = style.sx;
  6903. var sy = style.sy;
  6904. var sWidth = width - sx;
  6905. var sHeight = height - sy;
  6906. ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height);
  6907. } else {
  6908. ctx.drawImage(image, x, y, width, height);
  6909. }
  6910. this.restoreTransform(ctx); // Draw rect text
  6911. if (style.text != null) {
  6912. this.drawRectText(ctx, this.getBoundingRect());
  6913. }
  6914. },
  6915. getBoundingRect: function () {
  6916. var style = this.style;
  6917. if (!this._rect) {
  6918. this._rect = new BoundingRect(style.x || 0, style.y || 0, style.width || 0, style.height || 0);
  6919. }
  6920. return this._rect;
  6921. }
  6922. };
  6923. inherits(ZImage, Displayable);
  6924. /**
  6925. * Default canvas painter
  6926. * @module zrender/Painter
  6927. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  6928. * errorrik (errorrik@gmail.com)
  6929. * pissang (https://www.github.com/pissang)
  6930. */
  6931. // PENDIGN
  6932. // Layer exceeds MAX_PROGRESSIVE_LAYER_NUMBER may have some problem when flush directly second time.
  6933. //
  6934. // Maximum progressive layer. When exceeding this number. All elements will be drawed in the last layer.
  6935. var MAX_PROGRESSIVE_LAYER_NUMBER = 5;
  6936. function parseInt10(val) {
  6937. return parseInt(val, 10);
  6938. }
  6939. function isLayerValid(layer) {
  6940. if (!layer) {
  6941. return false;
  6942. }
  6943. if (layer.__builtin__) {
  6944. return true;
  6945. }
  6946. if (typeof layer.resize !== 'function' || typeof layer.refresh !== 'function') {
  6947. return false;
  6948. }
  6949. return true;
  6950. }
  6951. function preProcessLayer(layer) {
  6952. layer.__unusedCount++;
  6953. }
  6954. function postProcessLayer(layer) {
  6955. if (layer.__unusedCount == 1) {
  6956. layer.clear();
  6957. }
  6958. }
  6959. var tmpRect = new BoundingRect(0, 0, 0, 0);
  6960. var viewRect = new BoundingRect(0, 0, 0, 0);
  6961. function isDisplayableCulled(el, width, height) {
  6962. tmpRect.copy(el.getBoundingRect());
  6963. if (el.transform) {
  6964. tmpRect.applyTransform(el.transform);
  6965. }
  6966. viewRect.width = width;
  6967. viewRect.height = height;
  6968. return !tmpRect.intersect(viewRect);
  6969. }
  6970. function isClipPathChanged(clipPaths, prevClipPaths) {
  6971. if (clipPaths == prevClipPaths) {
  6972. // Can both be null or undefined
  6973. return false;
  6974. }
  6975. if (!clipPaths || !prevClipPaths || clipPaths.length !== prevClipPaths.length) {
  6976. return true;
  6977. }
  6978. for (var i = 0; i < clipPaths.length; i++) {
  6979. if (clipPaths[i] !== prevClipPaths[i]) {
  6980. return true;
  6981. }
  6982. }
  6983. }
  6984. function doClip(clipPaths, ctx) {
  6985. for (var i = 0; i < clipPaths.length; i++) {
  6986. var clipPath = clipPaths[i];
  6987. clipPath.setTransform(ctx);
  6988. ctx.beginPath();
  6989. clipPath.buildPath(ctx, clipPath.shape);
  6990. ctx.clip(); // Transform back
  6991. clipPath.restoreTransform(ctx);
  6992. }
  6993. }
  6994. function createRoot(width, height) {
  6995. var domRoot = document.createElement('div'); // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬
  6996. domRoot.style.cssText = ['position:relative', 'overflow:hidden', 'width:' + width + 'px', 'height:' + height + 'px', 'padding:0', 'margin:0', 'border-width:0'].join(';') + ';';
  6997. return domRoot;
  6998. }
  6999. /**
  7000. * @alias module:zrender/Painter
  7001. * @constructor
  7002. * @param {HTMLElement} root 绘图容器
  7003. * @param {module:zrender/Storage} storage
  7004. * @param {Object} opts
  7005. */
  7006. var Painter = function (root, storage, opts) {
  7007. this.type = 'canvas'; // In node environment using node-canvas
  7008. var singleCanvas = !root.nodeName // In node ?
  7009. || root.nodeName.toUpperCase() === 'CANVAS';
  7010. this._opts = opts = extend({}, opts || {});
  7011. /**
  7012. * @type {number}
  7013. */
  7014. this.dpr = opts.devicePixelRatio || devicePixelRatio;
  7015. /**
  7016. * @type {boolean}
  7017. * @private
  7018. */
  7019. this._singleCanvas = singleCanvas;
  7020. /**
  7021. * 绘图容器
  7022. * @type {HTMLElement}
  7023. */
  7024. this.root = root;
  7025. var rootStyle = root.style;
  7026. if (rootStyle) {
  7027. rootStyle['-webkit-tap-highlight-color'] = 'transparent';
  7028. rootStyle['-webkit-user-select'] = rootStyle['user-select'] = rootStyle['-webkit-touch-callout'] = 'none';
  7029. root.innerHTML = '';
  7030. }
  7031. /**
  7032. * @type {module:zrender/Storage}
  7033. */
  7034. this.storage = storage;
  7035. /**
  7036. * @type {Array.<number>}
  7037. * @private
  7038. */
  7039. var zlevelList = this._zlevelList = [];
  7040. /**
  7041. * @type {Object.<string, module:zrender/Layer>}
  7042. * @private
  7043. */
  7044. var layers = this._layers = {};
  7045. /**
  7046. * @type {Object.<string, Object>}
  7047. * @type {private}
  7048. */
  7049. this._layerConfig = {};
  7050. if (!singleCanvas) {
  7051. this._width = this._getSize(0);
  7052. this._height = this._getSize(1);
  7053. var domRoot = this._domRoot = createRoot(this._width, this._height);
  7054. root.appendChild(domRoot);
  7055. } else {
  7056. if (opts.width != null) {
  7057. root.width = opts.width;
  7058. }
  7059. if (opts.height != null) {
  7060. root.height = opts.height;
  7061. } // Use canvas width and height directly
  7062. var width = root.width;
  7063. var height = root.height;
  7064. this._width = width;
  7065. this._height = height; // Create layer if only one given canvas
  7066. // Device pixel ratio is fixed to 1 because given canvas has its specified width and height
  7067. var mainLayer = new Layer(root, this, 1);
  7068. mainLayer.initContext(); // FIXME Use canvas width and height
  7069. // mainLayer.resize(width, height);
  7070. layers[0] = mainLayer;
  7071. zlevelList.push(0);
  7072. this._domRoot = root;
  7073. } // Layers for progressive rendering
  7074. this._progressiveLayers = [];
  7075. /**
  7076. * @type {module:zrender/Layer}
  7077. * @private
  7078. */
  7079. this._hoverlayer;
  7080. this._hoverElements = [];
  7081. };
  7082. Painter.prototype = {
  7083. constructor: Painter,
  7084. getType: function () {
  7085. return 'canvas';
  7086. },
  7087. /**
  7088. * If painter use a single canvas
  7089. * @return {boolean}
  7090. */
  7091. isSingleCanvas: function () {
  7092. return this._singleCanvas;
  7093. },
  7094. /**
  7095. * @return {HTMLDivElement}
  7096. */
  7097. getViewportRoot: function () {
  7098. return this._domRoot;
  7099. },
  7100. getViewportRootOffset: function () {
  7101. var viewportRoot = this.getViewportRoot();
  7102. if (viewportRoot) {
  7103. return {
  7104. offsetLeft: viewportRoot.offsetLeft || 0,
  7105. offsetTop: viewportRoot.offsetTop || 0
  7106. };
  7107. }
  7108. },
  7109. /**
  7110. * 刷新
  7111. * @param {boolean} [paintAll=false] 强制绘制所有displayable
  7112. */
  7113. refresh: function (paintAll) {
  7114. var list = this.storage.getDisplayList(true);
  7115. var zlevelList = this._zlevelList;
  7116. this._paintList(list, paintAll); // Paint custum layers
  7117. for (var i = 0; i < zlevelList.length; i++) {
  7118. var z = zlevelList[i];
  7119. var layer = this._layers[z];
  7120. if (!layer.__builtin__ && layer.refresh) {
  7121. layer.refresh();
  7122. }
  7123. }
  7124. this.refreshHover();
  7125. if (this._progressiveLayers.length) {
  7126. this._startProgessive();
  7127. }
  7128. return this;
  7129. },
  7130. addHover: function (el, hoverStyle) {
  7131. if (el.__hoverMir) {
  7132. return;
  7133. }
  7134. var elMirror = new el.constructor({
  7135. style: el.style,
  7136. shape: el.shape
  7137. });
  7138. elMirror.__from = el;
  7139. el.__hoverMir = elMirror;
  7140. elMirror.setStyle(hoverStyle);
  7141. this._hoverElements.push(elMirror);
  7142. },
  7143. removeHover: function (el) {
  7144. var elMirror = el.__hoverMir;
  7145. var hoverElements = this._hoverElements;
  7146. var idx = indexOf(hoverElements, elMirror);
  7147. if (idx >= 0) {
  7148. hoverElements.splice(idx, 1);
  7149. }
  7150. el.__hoverMir = null;
  7151. },
  7152. clearHover: function (el) {
  7153. var hoverElements = this._hoverElements;
  7154. for (var i = 0; i < hoverElements.length; i++) {
  7155. var from = hoverElements[i].__from;
  7156. if (from) {
  7157. from.__hoverMir = null;
  7158. }
  7159. }
  7160. hoverElements.length = 0;
  7161. },
  7162. refreshHover: function () {
  7163. var hoverElements = this._hoverElements;
  7164. var len = hoverElements.length;
  7165. var hoverLayer = this._hoverlayer;
  7166. hoverLayer && hoverLayer.clear();
  7167. if (!len) {
  7168. return;
  7169. }
  7170. sort(hoverElements, this.storage.displayableSortFunc); // Use a extream large zlevel
  7171. // FIXME?
  7172. if (!hoverLayer) {
  7173. hoverLayer = this._hoverlayer = this.getLayer(1e5);
  7174. }
  7175. var scope = {};
  7176. hoverLayer.ctx.save();
  7177. for (var i = 0; i < len;) {
  7178. var el = hoverElements[i];
  7179. var originalEl = el.__from; // Original el is removed
  7180. // PENDING
  7181. if (!(originalEl && originalEl.__zr)) {
  7182. hoverElements.splice(i, 1);
  7183. originalEl.__hoverMir = null;
  7184. len--;
  7185. continue;
  7186. }
  7187. i++; // Use transform
  7188. // FIXME style and shape ?
  7189. if (!originalEl.invisible) {
  7190. el.transform = originalEl.transform;
  7191. el.invTransform = originalEl.invTransform;
  7192. el.__clipPaths = originalEl.__clipPaths; // el.
  7193. this._doPaintEl(el, hoverLayer, true, scope);
  7194. }
  7195. }
  7196. hoverLayer.ctx.restore();
  7197. },
  7198. _startProgessive: function () {
  7199. var self = this;
  7200. if (!self._furtherProgressive) {
  7201. return;
  7202. } // Use a token to stop progress steps triggered by
  7203. // previous zr.refresh calling.
  7204. var token = self._progressiveToken = +new Date();
  7205. self._progress++;
  7206. requestAnimationFrame(step);
  7207. function step() {
  7208. // In case refreshed or disposed
  7209. if (token === self._progressiveToken && self.storage) {
  7210. self._doPaintList(self.storage.getDisplayList());
  7211. if (self._furtherProgressive) {
  7212. self._progress++;
  7213. requestAnimationFrame(step);
  7214. } else {
  7215. self._progressiveToken = -1;
  7216. }
  7217. }
  7218. }
  7219. },
  7220. _clearProgressive: function () {
  7221. this._progressiveToken = -1;
  7222. this._progress = 0;
  7223. each$1(this._progressiveLayers, function (layer) {
  7224. layer.__dirty && layer.clear();
  7225. });
  7226. },
  7227. _paintList: function (list, paintAll) {
  7228. if (paintAll == null) {
  7229. paintAll = false;
  7230. }
  7231. this._updateLayerStatus(list);
  7232. this._clearProgressive();
  7233. this.eachBuiltinLayer(preProcessLayer);
  7234. this._doPaintList(list, paintAll);
  7235. this.eachBuiltinLayer(postProcessLayer);
  7236. },
  7237. _doPaintList: function (list, paintAll) {
  7238. var currentLayer;
  7239. var currentZLevel;
  7240. var ctx; // var invTransform = [];
  7241. var scope;
  7242. var progressiveLayerIdx = 0;
  7243. var currentProgressiveLayer;
  7244. var width = this._width;
  7245. var height = this._height;
  7246. var layerProgress;
  7247. var frame = this._progress;
  7248. function flushProgressiveLayer(layer) {
  7249. var dpr = ctx.dpr || 1;
  7250. ctx.save();
  7251. ctx.globalAlpha = 1;
  7252. ctx.shadowBlur = 0; // Avoid layer don't clear in next progressive frame
  7253. currentLayer.__dirty = true;
  7254. ctx.setTransform(1, 0, 0, 1, 0, 0);
  7255. ctx.drawImage(layer.dom, 0, 0, width * dpr, height * dpr);
  7256. ctx.restore();
  7257. }
  7258. for (var i = 0, l = list.length; i < l; i++) {
  7259. var el = list[i];
  7260. var elZLevel = this._singleCanvas ? 0 : el.zlevel;
  7261. var elFrame = el.__frame; // Flush at current context
  7262. // PENDING
  7263. if (elFrame < 0 && currentProgressiveLayer) {
  7264. flushProgressiveLayer(currentProgressiveLayer);
  7265. currentProgressiveLayer = null;
  7266. } // Change draw layer
  7267. if (currentZLevel !== elZLevel) {
  7268. if (ctx) {
  7269. ctx.restore();
  7270. } // Reset scope
  7271. scope = {}; // Only 0 zlevel if only has one canvas
  7272. currentZLevel = elZLevel;
  7273. currentLayer = this.getLayer(currentZLevel);
  7274. if (!currentLayer.__builtin__) {
  7275. zrLog('ZLevel ' + currentZLevel + ' has been used by unkown layer ' + currentLayer.id);
  7276. }
  7277. ctx = currentLayer.ctx;
  7278. ctx.save(); // Reset the count
  7279. currentLayer.__unusedCount = 0;
  7280. if (currentLayer.__dirty || paintAll) {
  7281. currentLayer.clear();
  7282. }
  7283. }
  7284. if (!(currentLayer.__dirty || paintAll)) {
  7285. continue;
  7286. }
  7287. if (elFrame >= 0) {
  7288. // Progressive layer changed
  7289. if (!currentProgressiveLayer) {
  7290. currentProgressiveLayer = this._progressiveLayers[Math.min(progressiveLayerIdx++, MAX_PROGRESSIVE_LAYER_NUMBER - 1)];
  7291. currentProgressiveLayer.ctx.save();
  7292. currentProgressiveLayer.renderScope = {};
  7293. if (currentProgressiveLayer && currentProgressiveLayer.__progress > currentProgressiveLayer.__maxProgress) {
  7294. // flushProgressiveLayer(currentProgressiveLayer);
  7295. // Quick jump all progressive elements
  7296. // All progressive element are not dirty, jump over and flush directly
  7297. i = currentProgressiveLayer.__nextIdxNotProg - 1; // currentProgressiveLayer = null;
  7298. continue;
  7299. }
  7300. layerProgress = currentProgressiveLayer.__progress;
  7301. if (!currentProgressiveLayer.__dirty) {
  7302. // Keep rendering
  7303. frame = layerProgress;
  7304. }
  7305. currentProgressiveLayer.__progress = frame + 1;
  7306. }
  7307. if (elFrame === frame) {
  7308. this._doPaintEl(el, currentProgressiveLayer, true, currentProgressiveLayer.renderScope);
  7309. }
  7310. } else {
  7311. this._doPaintEl(el, currentLayer, paintAll, scope);
  7312. }
  7313. el.__dirty = false;
  7314. }
  7315. if (currentProgressiveLayer) {
  7316. flushProgressiveLayer(currentProgressiveLayer);
  7317. } // Restore the lastLayer ctx
  7318. ctx && ctx.restore(); // If still has clipping state
  7319. // if (scope.prevElClipPaths) {
  7320. // ctx.restore();
  7321. // }
  7322. this._furtherProgressive = false;
  7323. each$1(this._progressiveLayers, function (layer) {
  7324. if (layer.__maxProgress >= layer.__progress) {
  7325. this._furtherProgressive = true;
  7326. }
  7327. }, this);
  7328. },
  7329. _doPaintEl: function (el, currentLayer, forcePaint, scope) {
  7330. var ctx = currentLayer.ctx;
  7331. var m = el.transform;
  7332. if ((currentLayer.__dirty || forcePaint) && // Ignore invisible element
  7333. !el.invisible // Ignore transparent element
  7334. && el.style.opacity !== 0 // Ignore scale 0 element, in some environment like node-canvas
  7335. // Draw a scale 0 element can cause all following draw wrong
  7336. // And setTransform with scale 0 will cause set back transform failed.
  7337. && !(m && !m[0] && !m[3]) // Ignore culled element
  7338. && !(el.culling && isDisplayableCulled(el, this._width, this._height))) {
  7339. var clipPaths = el.__clipPaths; // Optimize when clipping on group with several elements
  7340. if (scope.prevClipLayer !== currentLayer || isClipPathChanged(clipPaths, scope.prevElClipPaths)) {
  7341. // If has previous clipping state, restore from it
  7342. if (scope.prevElClipPaths) {
  7343. scope.prevClipLayer.ctx.restore();
  7344. scope.prevClipLayer = scope.prevElClipPaths = null; // Reset prevEl since context has been restored
  7345. scope.prevEl = null;
  7346. } // New clipping state
  7347. if (clipPaths) {
  7348. ctx.save();
  7349. doClip(clipPaths, ctx);
  7350. scope.prevClipLayer = currentLayer;
  7351. scope.prevElClipPaths = clipPaths;
  7352. }
  7353. }
  7354. el.beforeBrush && el.beforeBrush(ctx);
  7355. el.brush(ctx, scope.prevEl || null);
  7356. scope.prevEl = el;
  7357. el.afterBrush && el.afterBrush(ctx);
  7358. }
  7359. },
  7360. /**
  7361. * 获取 zlevel 所在层,如果不存在则会创建一个新的层
  7362. * @param {number} zlevel
  7363. * @return {module:zrender/Layer}
  7364. */
  7365. getLayer: function (zlevel) {
  7366. if (this._singleCanvas) {
  7367. return this._layers[0];
  7368. }
  7369. var layer = this._layers[zlevel];
  7370. if (!layer) {
  7371. // Create a new layer
  7372. layer = new Layer('zr_' + zlevel, this, this.dpr);
  7373. layer.__builtin__ = true;
  7374. if (this._layerConfig[zlevel]) {
  7375. merge(layer, this._layerConfig[zlevel], true);
  7376. }
  7377. this.insertLayer(zlevel, layer); // Context is created after dom inserted to document
  7378. // Or excanvas will get 0px clientWidth and clientHeight
  7379. layer.initContext();
  7380. }
  7381. return layer;
  7382. },
  7383. insertLayer: function (zlevel, layer) {
  7384. var layersMap = this._layers;
  7385. var zlevelList = this._zlevelList;
  7386. var len = zlevelList.length;
  7387. var prevLayer = null;
  7388. var i = -1;
  7389. var domRoot = this._domRoot;
  7390. if (layersMap[zlevel]) {
  7391. zrLog('ZLevel ' + zlevel + ' has been used already');
  7392. return;
  7393. } // Check if is a valid layer
  7394. if (!isLayerValid(layer)) {
  7395. zrLog('Layer of zlevel ' + zlevel + ' is not valid');
  7396. return;
  7397. }
  7398. if (len > 0 && zlevel > zlevelList[0]) {
  7399. for (i = 0; i < len - 1; i++) {
  7400. if (zlevelList[i] < zlevel && zlevelList[i + 1] > zlevel) {
  7401. break;
  7402. }
  7403. }
  7404. prevLayer = layersMap[zlevelList[i]];
  7405. }
  7406. zlevelList.splice(i + 1, 0, zlevel);
  7407. layersMap[zlevel] = layer; // Vitual layer will not directly show on the screen.
  7408. // (It can be a WebGL layer and assigned to a ZImage element)
  7409. // But it still under management of zrender.
  7410. if (!layer.virtual) {
  7411. if (prevLayer) {
  7412. var prevDom = prevLayer.dom;
  7413. if (prevDom.nextSibling) {
  7414. domRoot.insertBefore(layer.dom, prevDom.nextSibling);
  7415. } else {
  7416. domRoot.appendChild(layer.dom);
  7417. }
  7418. } else {
  7419. if (domRoot.firstChild) {
  7420. domRoot.insertBefore(layer.dom, domRoot.firstChild);
  7421. } else {
  7422. domRoot.appendChild(layer.dom);
  7423. }
  7424. }
  7425. }
  7426. },
  7427. // Iterate each layer
  7428. eachLayer: function (cb, context) {
  7429. var zlevelList = this._zlevelList;
  7430. var z;
  7431. var i;
  7432. for (i = 0; i < zlevelList.length; i++) {
  7433. z = zlevelList[i];
  7434. cb.call(context, this._layers[z], z);
  7435. }
  7436. },
  7437. // Iterate each buildin layer
  7438. eachBuiltinLayer: function (cb, context) {
  7439. var zlevelList = this._zlevelList;
  7440. var layer;
  7441. var z;
  7442. var i;
  7443. for (i = 0; i < zlevelList.length; i++) {
  7444. z = zlevelList[i];
  7445. layer = this._layers[z];
  7446. if (layer.__builtin__) {
  7447. cb.call(context, layer, z);
  7448. }
  7449. }
  7450. },
  7451. // Iterate each other layer except buildin layer
  7452. eachOtherLayer: function (cb, context) {
  7453. var zlevelList = this._zlevelList;
  7454. var layer;
  7455. var z;
  7456. var i;
  7457. for (i = 0; i < zlevelList.length; i++) {
  7458. z = zlevelList[i];
  7459. layer = this._layers[z];
  7460. if (!layer.__builtin__) {
  7461. cb.call(context, layer, z);
  7462. }
  7463. }
  7464. },
  7465. /**
  7466. * 获取所有已创建的层
  7467. * @param {Array.<module:zrender/Layer>} [prevLayer]
  7468. */
  7469. getLayers: function () {
  7470. return this._layers;
  7471. },
  7472. _updateLayerStatus: function (list) {
  7473. var layers = this._layers;
  7474. var progressiveLayers = this._progressiveLayers;
  7475. var elCountsLastFrame = {};
  7476. var progressiveElCountsLastFrame = {};
  7477. this.eachBuiltinLayer(function (layer, z) {
  7478. elCountsLastFrame[z] = layer.elCount;
  7479. layer.elCount = 0;
  7480. layer.__dirty = false;
  7481. });
  7482. each$1(progressiveLayers, function (layer, idx) {
  7483. progressiveElCountsLastFrame[idx] = layer.elCount;
  7484. layer.elCount = 0;
  7485. layer.__dirty = false;
  7486. });
  7487. var progressiveLayerCount = 0;
  7488. var currentProgressiveLayer;
  7489. var lastProgressiveKey;
  7490. var frameCount = 0;
  7491. for (var i = 0, l = list.length; i < l; i++) {
  7492. var el = list[i];
  7493. var zlevel = this._singleCanvas ? 0 : el.zlevel;
  7494. var layer = layers[zlevel];
  7495. var elProgress = el.progressive;
  7496. if (layer) {
  7497. layer.elCount++;
  7498. layer.__dirty = layer.__dirty || el.__dirty;
  7499. } /////// Update progressive
  7500. if (elProgress >= 0) {
  7501. // Fix wrong progressive sequence problem.
  7502. if (lastProgressiveKey !== elProgress) {
  7503. lastProgressiveKey = elProgress;
  7504. frameCount++;
  7505. }
  7506. var elFrame = el.__frame = frameCount - 1;
  7507. if (!currentProgressiveLayer) {
  7508. var idx = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER - 1);
  7509. currentProgressiveLayer = progressiveLayers[idx];
  7510. if (!currentProgressiveLayer) {
  7511. currentProgressiveLayer = progressiveLayers[idx] = new Layer('progressive', this, this.dpr);
  7512. currentProgressiveLayer.initContext();
  7513. }
  7514. currentProgressiveLayer.__maxProgress = 0;
  7515. }
  7516. currentProgressiveLayer.__dirty = currentProgressiveLayer.__dirty || el.__dirty;
  7517. currentProgressiveLayer.elCount++;
  7518. currentProgressiveLayer.__maxProgress = Math.max(currentProgressiveLayer.__maxProgress, elFrame);
  7519. if (currentProgressiveLayer.__maxProgress >= currentProgressiveLayer.__progress) {
  7520. // Should keep rendering this layer because progressive rendering is not finished yet
  7521. layer.__dirty = true;
  7522. }
  7523. } else {
  7524. el.__frame = -1;
  7525. if (currentProgressiveLayer) {
  7526. currentProgressiveLayer.__nextIdxNotProg = i;
  7527. progressiveLayerCount++;
  7528. currentProgressiveLayer = null;
  7529. }
  7530. }
  7531. }
  7532. if (currentProgressiveLayer) {
  7533. progressiveLayerCount++;
  7534. currentProgressiveLayer.__nextIdxNotProg = i;
  7535. } // 层中的元素数量有发生变化
  7536. this.eachBuiltinLayer(function (layer, z) {
  7537. if (elCountsLastFrame[z] !== layer.elCount) {
  7538. layer.__dirty = true;
  7539. }
  7540. });
  7541. progressiveLayers.length = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER);
  7542. each$1(progressiveLayers, function (layer, idx) {
  7543. if (progressiveElCountsLastFrame[idx] !== layer.elCount) {
  7544. el.__dirty = true;
  7545. }
  7546. if (layer.__dirty) {
  7547. layer.__progress = 0;
  7548. }
  7549. });
  7550. },
  7551. /**
  7552. * 清除hover层外所有内容
  7553. */
  7554. clear: function () {
  7555. this.eachBuiltinLayer(this._clearLayer);
  7556. return this;
  7557. },
  7558. _clearLayer: function (layer) {
  7559. layer.clear();
  7560. },
  7561. /**
  7562. * 修改指定zlevel的绘制参数
  7563. *
  7564. * @param {string} zlevel
  7565. * @param {Object} config 配置对象
  7566. * @param {string} [config.clearColor=0] 每次清空画布的颜色
  7567. * @param {string} [config.motionBlur=false] 是否开启动态模糊
  7568. * @param {number} [config.lastFrameAlpha=0.7]
  7569. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  7570. */
  7571. configLayer: function (zlevel, config) {
  7572. if (config) {
  7573. var layerConfig = this._layerConfig;
  7574. if (!layerConfig[zlevel]) {
  7575. layerConfig[zlevel] = config;
  7576. } else {
  7577. merge(layerConfig[zlevel], config, true);
  7578. }
  7579. var layer = this._layers[zlevel];
  7580. if (layer) {
  7581. merge(layer, layerConfig[zlevel], true);
  7582. }
  7583. }
  7584. },
  7585. /**
  7586. * 删除指定层
  7587. * @param {number} zlevel 层所在的zlevel
  7588. */
  7589. delLayer: function (zlevel) {
  7590. var layers = this._layers;
  7591. var zlevelList = this._zlevelList;
  7592. var layer = layers[zlevel];
  7593. if (!layer) {
  7594. return;
  7595. }
  7596. layer.dom.parentNode.removeChild(layer.dom);
  7597. delete layers[zlevel];
  7598. zlevelList.splice(indexOf(zlevelList, zlevel), 1);
  7599. },
  7600. /**
  7601. * 区域大小变化后重绘
  7602. */
  7603. resize: function (width, height) {
  7604. var domRoot = this._domRoot; // FIXME Why ?
  7605. domRoot.style.display = 'none'; // Save input w/h
  7606. var opts = this._opts;
  7607. width != null && (opts.width = width);
  7608. height != null && (opts.height = height);
  7609. width = this._getSize(0);
  7610. height = this._getSize(1);
  7611. domRoot.style.display = ''; // 优化没有实际改变的resize
  7612. if (this._width != width || height != this._height) {
  7613. domRoot.style.width = width + 'px';
  7614. domRoot.style.height = height + 'px';
  7615. for (var id in this._layers) {
  7616. if (this._layers.hasOwnProperty(id)) {
  7617. this._layers[id].resize(width, height);
  7618. }
  7619. }
  7620. each$1(this._progressiveLayers, function (layer) {
  7621. layer.resize(width, height);
  7622. });
  7623. this.refresh(true);
  7624. }
  7625. this._width = width;
  7626. this._height = height;
  7627. return this;
  7628. },
  7629. /**
  7630. * 清除单独的一个层
  7631. * @param {number} zlevel
  7632. */
  7633. clearLayer: function (zlevel) {
  7634. var layer = this._layers[zlevel];
  7635. if (layer) {
  7636. layer.clear();
  7637. }
  7638. },
  7639. /**
  7640. * 释放
  7641. */
  7642. dispose: function () {
  7643. this.root.innerHTML = '';
  7644. this.root = this.storage = this._domRoot = this._layers = null;
  7645. },
  7646. /**
  7647. * Get canvas which has all thing rendered
  7648. * @param {Object} opts
  7649. * @param {string} [opts.backgroundColor]
  7650. * @param {number} [opts.pixelRatio]
  7651. */
  7652. getRenderedCanvas: function (opts) {
  7653. opts = opts || {};
  7654. if (this._singleCanvas) {
  7655. return this._layers[0].dom;
  7656. }
  7657. var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
  7658. imageLayer.initContext();
  7659. imageLayer.clearColor = opts.backgroundColor;
  7660. imageLayer.clear();
  7661. var displayList = this.storage.getDisplayList(true);
  7662. var scope = {};
  7663. var zlevel;
  7664. var self = this;
  7665. function findAndDrawOtherLayer(smaller, larger) {
  7666. var zlevelList = self._zlevelList;
  7667. if (smaller == null) {
  7668. smaller = -Infinity;
  7669. }
  7670. var intermediateLayer;
  7671. for (var i = 0; i < zlevelList.length; i++) {
  7672. var z = zlevelList[i];
  7673. var layer = self._layers[z];
  7674. if (!layer.__builtin__ && z > smaller && z < larger) {
  7675. intermediateLayer = layer;
  7676. break;
  7677. }
  7678. }
  7679. if (intermediateLayer && intermediateLayer.renderToCanvas) {
  7680. imageLayer.ctx.save();
  7681. intermediateLayer.renderToCanvas(imageLayer.ctx);
  7682. imageLayer.ctx.restore();
  7683. }
  7684. }
  7685. for (var i = 0; i < displayList.length; i++) {
  7686. var el = displayList[i];
  7687. if (el.zlevel !== zlevel) {
  7688. findAndDrawOtherLayer(zlevel, el.zlevel);
  7689. zlevel = el.zlevel;
  7690. }
  7691. this._doPaintEl(el, imageLayer, true, scope);
  7692. }
  7693. findAndDrawOtherLayer(zlevel, Infinity);
  7694. return imageLayer.dom;
  7695. },
  7696. /**
  7697. * 获取绘图区域宽度
  7698. */
  7699. getWidth: function () {
  7700. return this._width;
  7701. },
  7702. /**
  7703. * 获取绘图区域高度
  7704. */
  7705. getHeight: function () {
  7706. return this._height;
  7707. },
  7708. _getSize: function (whIdx) {
  7709. var opts = this._opts;
  7710. var wh = ['width', 'height'][whIdx];
  7711. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  7712. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  7713. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  7714. if (opts[wh] != null && opts[wh] !== 'auto') {
  7715. return parseFloat(opts[wh]);
  7716. }
  7717. var root = this.root; // IE8 does not support getComputedStyle, but it use VML.
  7718. var stl = document.defaultView.getComputedStyle(root);
  7719. return (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) - (parseInt10(stl[plt]) || 0) - (parseInt10(stl[prb]) || 0) | 0;
  7720. },
  7721. pathToImage: function (path, dpr) {
  7722. dpr = dpr || this.dpr;
  7723. var canvas = document.createElement('canvas');
  7724. var ctx = canvas.getContext('2d');
  7725. var rect = path.getBoundingRect();
  7726. var style = path.style;
  7727. var shadowBlurSize = style.shadowBlur;
  7728. var shadowOffsetX = style.shadowOffsetX;
  7729. var shadowOffsetY = style.shadowOffsetY;
  7730. var lineWidth = style.hasStroke() ? style.lineWidth : 0;
  7731. var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
  7732. var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
  7733. var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
  7734. var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
  7735. var width = rect.width + leftMargin + rightMargin;
  7736. var height = rect.height + topMargin + bottomMargin;
  7737. canvas.width = width * dpr;
  7738. canvas.height = height * dpr;
  7739. ctx.scale(dpr, dpr);
  7740. ctx.clearRect(0, 0, width, height);
  7741. ctx.dpr = dpr;
  7742. var pathTransform = {
  7743. position: path.position,
  7744. rotation: path.rotation,
  7745. scale: path.scale
  7746. };
  7747. path.position = [leftMargin - rect.x, topMargin - rect.y];
  7748. path.rotation = 0;
  7749. path.scale = [1, 1];
  7750. path.updateTransform();
  7751. if (path) {
  7752. path.brush(ctx);
  7753. }
  7754. var ImageShape = ZImage;
  7755. var imgShape = new ImageShape({
  7756. style: {
  7757. x: 0,
  7758. y: 0,
  7759. image: canvas
  7760. }
  7761. });
  7762. if (pathTransform.position != null) {
  7763. imgShape.position = path.position = pathTransform.position;
  7764. }
  7765. if (pathTransform.rotation != null) {
  7766. imgShape.rotation = path.rotation = pathTransform.rotation;
  7767. }
  7768. if (pathTransform.scale != null) {
  7769. imgShape.scale = path.scale = pathTransform.scale;
  7770. }
  7771. return imgShape;
  7772. }
  7773. };
  7774. /**
  7775. * 事件辅助类
  7776. * @module zrender/core/event
  7777. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  7778. */
  7779. var isDomLevel2 = typeof window !== 'undefined' && !!window.addEventListener;
  7780. var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;
  7781. function getBoundingClientRect(el) {
  7782. // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect
  7783. return el.getBoundingClientRect ? el.getBoundingClientRect() : {
  7784. left: 0,
  7785. top: 0
  7786. };
  7787. } // `calculate` is optional, default false
  7788. function clientToLocal(el, e, out, calculate) {
  7789. out = out || {}; // According to the W3C Working Draft, offsetX and offsetY should be relative
  7790. // to the padding edge of the target element. The only browser using this convention
  7791. // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does
  7792. // not support the properties.
  7793. // (see http://www.jacklmoore.com/notes/mouse-position/)
  7794. // In zr painter.dom, padding edge equals to border edge.
  7795. // FIXME
  7796. // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and
  7797. // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y
  7798. // is too complex. So css-transfrom dont support in this case temporarily.
  7799. if (calculate || !env$1.canvasSupported) {
  7800. defaultGetZrXY(el, e, out);
  7801. } // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned
  7802. // ancestor element, so we should make sure el is positioned (e.g., not position:static).
  7803. // BTW1, Webkit don't return the same results as FF in non-simple cases (like add
  7804. // zoom-factor, overflow / opacity layers, transforms ...)
  7805. // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.
  7806. // <https://bugs.jquery.com/ticket/8523#comment:14>
  7807. // BTW3, In ff, offsetX/offsetY is always 0.
  7808. else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) {
  7809. out.zrX = e.layerX;
  7810. out.zrY = e.layerY;
  7811. } // For IE6+, chrome, safari, opera. (When will ff support offsetX?)
  7812. else if (e.offsetX != null) {
  7813. out.zrX = e.offsetX;
  7814. out.zrY = e.offsetY;
  7815. } // For some other device, e.g., IOS safari.
  7816. else {
  7817. defaultGetZrXY(el, e, out);
  7818. }
  7819. return out;
  7820. }
  7821. function defaultGetZrXY(el, e, out) {
  7822. // This well-known method below does not support css transform.
  7823. var box = getBoundingClientRect(el);
  7824. out.zrX = e.clientX - box.left;
  7825. out.zrY = e.clientY - box.top;
  7826. }
  7827. /**
  7828. * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标.
  7829. * `calculate` is optional, default false.
  7830. */
  7831. function normalizeEvent(el, e, calculate) {
  7832. e = e || window.event;
  7833. if (e.zrX != null) {
  7834. return e;
  7835. }
  7836. var eventType = e.type;
  7837. var isTouch = eventType && eventType.indexOf('touch') >= 0;
  7838. if (!isTouch) {
  7839. clientToLocal(el, e, e, calculate);
  7840. e.zrDelta = e.wheelDelta ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
  7841. } else {
  7842. var touch = eventType != 'touchend' ? e.targetTouches[0] : e.changedTouches[0];
  7843. touch && clientToLocal(el, touch, e, calculate);
  7844. } // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;
  7845. // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js
  7846. // If e.which has been defined, if may be readonly,
  7847. // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
  7848. var button = e.button;
  7849. if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {
  7850. e.which = button & 1 ? 1 : button & 2 ? 3 : button & 4 ? 2 : 0;
  7851. }
  7852. return e;
  7853. }
  7854. function addEventListener(el, name, handler) {
  7855. if (isDomLevel2) {
  7856. el.addEventListener(name, handler);
  7857. } else {
  7858. el.attachEvent('on' + name, handler);
  7859. }
  7860. }
  7861. function removeEventListener(el, name, handler) {
  7862. if (isDomLevel2) {
  7863. el.removeEventListener(name, handler);
  7864. } else {
  7865. el.detachEvent('on' + name, handler);
  7866. }
  7867. }
  7868. /**
  7869. * preventDefault and stopPropagation.
  7870. * Notice: do not do that in zrender. Upper application
  7871. * do that if necessary.
  7872. *
  7873. * @memberOf module:zrender/core/event
  7874. * @method
  7875. * @param {Event} e : event对象
  7876. */
  7877. var stop = isDomLevel2 ? function (e) {
  7878. e.preventDefault();
  7879. e.stopPropagation();
  7880. e.cancelBubble = true;
  7881. } : function (e) {
  7882. e.returnValue = false;
  7883. e.cancelBubble = true;
  7884. };
  7885. /**
  7886. * 动画主类, 调度和管理所有动画控制器
  7887. *
  7888. * @module zrender/animation/Animation
  7889. * @author pissang(https://github.com/pissang)
  7890. */
  7891. // TODO Additive animation
  7892. // http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
  7893. // https://developer.apple.com/videos/wwdc2014/#236
  7894. /**
  7895. * @typedef {Object} IZRenderStage
  7896. * @property {Function} update
  7897. */
  7898. /**
  7899. * @alias module:zrender/animation/Animation
  7900. * @constructor
  7901. * @param {Object} [options]
  7902. * @param {Function} [options.onframe]
  7903. * @param {IZRenderStage} [options.stage]
  7904. * @example
  7905. * var animation = new Animation();
  7906. * var obj = {
  7907. * x: 100,
  7908. * y: 100
  7909. * };
  7910. * animation.animate(node.position)
  7911. * .when(1000, {
  7912. * x: 500,
  7913. * y: 500
  7914. * })
  7915. * .when(2000, {
  7916. * x: 100,
  7917. * y: 100
  7918. * })
  7919. * .start('spline');
  7920. */
  7921. var Animation = function (options) {
  7922. options = options || {};
  7923. this.stage = options.stage || {};
  7924. this.onframe = options.onframe || function () {}; // private properties
  7925. this._clips = [];
  7926. this._running = false;
  7927. this._time;
  7928. this._pausedTime;
  7929. this._pauseStart;
  7930. this._paused = false;
  7931. Eventful.call(this);
  7932. };
  7933. Animation.prototype = {
  7934. constructor: Animation,
  7935. /**
  7936. * 添加 clip
  7937. * @param {module:zrender/animation/Clip} clip
  7938. */
  7939. addClip: function (clip) {
  7940. this._clips.push(clip);
  7941. },
  7942. /**
  7943. * 添加 animator
  7944. * @param {module:zrender/animation/Animator} animator
  7945. */
  7946. addAnimator: function (animator) {
  7947. animator.animation = this;
  7948. var clips = animator.getClips();
  7949. for (var i = 0; i < clips.length; i++) {
  7950. this.addClip(clips[i]);
  7951. }
  7952. },
  7953. /**
  7954. * 删除动画片段
  7955. * @param {module:zrender/animation/Clip} clip
  7956. */
  7957. removeClip: function (clip) {
  7958. var idx = indexOf(this._clips, clip);
  7959. if (idx >= 0) {
  7960. this._clips.splice(idx, 1);
  7961. }
  7962. },
  7963. /**
  7964. * 删除动画片段
  7965. * @param {module:zrender/animation/Animator} animator
  7966. */
  7967. removeAnimator: function (animator) {
  7968. var clips = animator.getClips();
  7969. for (var i = 0; i < clips.length; i++) {
  7970. this.removeClip(clips[i]);
  7971. }
  7972. animator.animation = null;
  7973. },
  7974. _update: function () {
  7975. var time = new Date().getTime() - this._pausedTime;
  7976. var delta = time - this._time;
  7977. var clips = this._clips;
  7978. var len = clips.length;
  7979. var deferredEvents = [];
  7980. var deferredClips = [];
  7981. for (var i = 0; i < len; i++) {
  7982. var clip = clips[i];
  7983. var e = clip.step(time, delta); // Throw out the events need to be called after
  7984. // stage.update, like destroy
  7985. if (e) {
  7986. deferredEvents.push(e);
  7987. deferredClips.push(clip);
  7988. }
  7989. } // Remove the finished clip
  7990. for (var i = 0; i < len;) {
  7991. if (clips[i]._needsRemove) {
  7992. clips[i] = clips[len - 1];
  7993. clips.pop();
  7994. len--;
  7995. } else {
  7996. i++;
  7997. }
  7998. }
  7999. len = deferredEvents.length;
  8000. for (var i = 0; i < len; i++) {
  8001. deferredClips[i].fire(deferredEvents[i]);
  8002. }
  8003. this._time = time;
  8004. this.onframe(delta);
  8005. this.trigger('frame', delta);
  8006. if (this.stage.update) {
  8007. this.stage.update();
  8008. }
  8009. },
  8010. _startLoop: function () {
  8011. var self = this;
  8012. this._running = true;
  8013. function step() {
  8014. if (self._running) {
  8015. requestAnimationFrame(step);
  8016. !self._paused && self._update();
  8017. }
  8018. }
  8019. requestAnimationFrame(step);
  8020. },
  8021. /**
  8022. * 开始运行动画
  8023. */
  8024. start: function () {
  8025. this._time = new Date().getTime();
  8026. this._pausedTime = 0;
  8027. this._startLoop();
  8028. },
  8029. /**
  8030. * 停止运行动画
  8031. */
  8032. stop: function () {
  8033. this._running = false;
  8034. },
  8035. /**
  8036. * Pause
  8037. */
  8038. pause: function () {
  8039. if (!this._paused) {
  8040. this._pauseStart = new Date().getTime();
  8041. this._paused = true;
  8042. }
  8043. },
  8044. /**
  8045. * Resume
  8046. */
  8047. resume: function () {
  8048. if (this._paused) {
  8049. this._pausedTime += new Date().getTime() - this._pauseStart;
  8050. this._paused = false;
  8051. }
  8052. },
  8053. /**
  8054. * 清除所有动画片段
  8055. */
  8056. clear: function () {
  8057. this._clips = [];
  8058. },
  8059. /**
  8060. * 对一个目标创建一个animator对象,可以指定目标中的属性使用动画
  8061. * @param {Object} target
  8062. * @param {Object} options
  8063. * @param {boolean} [options.loop=false] 是否循环播放动画
  8064. * @param {Function} [options.getter=null]
  8065. * 如果指定getter函数,会通过getter函数取属性值
  8066. * @param {Function} [options.setter=null]
  8067. * 如果指定setter函数,会通过setter函数设置属性值
  8068. * @return {module:zrender/animation/Animation~Animator}
  8069. */
  8070. // TODO Gap
  8071. animate: function (target, options) {
  8072. options = options || {};
  8073. var animator = new Animator(target, options.loop, options.getter, options.setter);
  8074. this.addAnimator(animator);
  8075. return animator;
  8076. }
  8077. };
  8078. mixin(Animation, Eventful);
  8079. /**
  8080. * Only implements needed gestures for mobile.
  8081. */
  8082. var GestureMgr = function () {
  8083. /**
  8084. * @private
  8085. * @type {Array.<Object>}
  8086. */
  8087. this._track = [];
  8088. };
  8089. GestureMgr.prototype = {
  8090. constructor: GestureMgr,
  8091. recognize: function (event, target, root) {
  8092. this._doTrack(event, target, root);
  8093. return this._recognize(event);
  8094. },
  8095. clear: function () {
  8096. this._track.length = 0;
  8097. return this;
  8098. },
  8099. _doTrack: function (event, target, root) {
  8100. var touches = event.touches;
  8101. if (!touches) {
  8102. return;
  8103. }
  8104. var trackItem = {
  8105. points: [],
  8106. touches: [],
  8107. target: target,
  8108. event: event
  8109. };
  8110. for (var i = 0, len = touches.length; i < len; i++) {
  8111. var touch = touches[i];
  8112. var pos = clientToLocal(root, touch, {});
  8113. trackItem.points.push([pos.zrX, pos.zrY]);
  8114. trackItem.touches.push(touch);
  8115. }
  8116. this._track.push(trackItem);
  8117. },
  8118. _recognize: function (event) {
  8119. for (var eventName in recognizers) {
  8120. if (recognizers.hasOwnProperty(eventName)) {
  8121. var gestureInfo = recognizers[eventName](this._track, event);
  8122. if (gestureInfo) {
  8123. return gestureInfo;
  8124. }
  8125. }
  8126. }
  8127. }
  8128. };
  8129. function dist$1(pointPair) {
  8130. var dx = pointPair[1][0] - pointPair[0][0];
  8131. var dy = pointPair[1][1] - pointPair[0][1];
  8132. return Math.sqrt(dx * dx + dy * dy);
  8133. }
  8134. function center(pointPair) {
  8135. return [(pointPair[0][0] + pointPair[1][0]) / 2, (pointPair[0][1] + pointPair[1][1]) / 2];
  8136. }
  8137. var recognizers = {
  8138. pinch: function (track, event) {
  8139. var trackLen = track.length;
  8140. if (!trackLen) {
  8141. return;
  8142. }
  8143. var pinchEnd = (track[trackLen - 1] || {}).points;
  8144. var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;
  8145. if (pinchPre && pinchPre.length > 1 && pinchEnd && pinchEnd.length > 1) {
  8146. var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);
  8147. !isFinite(pinchScale) && (pinchScale = 1);
  8148. event.pinchScale = pinchScale;
  8149. var pinchCenter = center(pinchEnd);
  8150. event.pinchX = pinchCenter[0];
  8151. event.pinchY = pinchCenter[1];
  8152. return {
  8153. type: 'pinch',
  8154. target: track[0].target,
  8155. event: event
  8156. };
  8157. }
  8158. } // Only pinch currently.
  8159. };
  8160. var TOUCH_CLICK_DELAY = 300;
  8161. var mouseHandlerNames = ['click', 'dblclick', 'mousewheel', 'mouseout', 'mouseup', 'mousedown', 'mousemove', 'contextmenu'];
  8162. var touchHandlerNames = ['touchstart', 'touchend', 'touchmove'];
  8163. var pointerEventNames = {
  8164. pointerdown: 1,
  8165. pointerup: 1,
  8166. pointermove: 1,
  8167. pointerout: 1
  8168. };
  8169. var pointerHandlerNames = map(mouseHandlerNames, function (name) {
  8170. var nm = name.replace('mouse', 'pointer');
  8171. return pointerEventNames[nm] ? nm : name;
  8172. });
  8173. function eventNameFix(name) {
  8174. return name === 'mousewheel' && env$1.browser.firefox ? 'DOMMouseScroll' : name;
  8175. }
  8176. function processGesture(proxy, event, stage) {
  8177. var gestureMgr = proxy._gestureMgr;
  8178. stage === 'start' && gestureMgr.clear();
  8179. var gestureInfo = gestureMgr.recognize(event, proxy.handler.findHover(event.zrX, event.zrY, null).target, proxy.dom);
  8180. stage === 'end' && gestureMgr.clear(); // Do not do any preventDefault here. Upper application do that if necessary.
  8181. if (gestureInfo) {
  8182. var type = gestureInfo.type;
  8183. event.gestureEvent = type;
  8184. proxy.handler.dispatchToElement({
  8185. target: gestureInfo.target
  8186. }, type, gestureInfo.event);
  8187. }
  8188. } // function onMSGestureChange(proxy, event) {
  8189. // if (event.translationX || event.translationY) {
  8190. // // mousemove is carried by MSGesture to reduce the sensitivity.
  8191. // proxy.handler.dispatchToElement(event.target, 'mousemove', event);
  8192. // }
  8193. // if (event.scale !== 1) {
  8194. // event.pinchX = event.offsetX;
  8195. // event.pinchY = event.offsetY;
  8196. // event.pinchScale = event.scale;
  8197. // proxy.handler.dispatchToElement(event.target, 'pinch', event);
  8198. // }
  8199. // }
  8200. /**
  8201. * Prevent mouse event from being dispatched after Touch Events action
  8202. * @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>
  8203. * 1. Mobile browsers dispatch mouse events 300ms after touchend.
  8204. * 2. Chrome for Android dispatch mousedown for long-touch about 650ms
  8205. * Result: Blocking Mouse Events for 700ms.
  8206. */
  8207. function setTouchTimer(instance) {
  8208. instance._touching = true;
  8209. clearTimeout(instance._touchTimer);
  8210. instance._touchTimer = setTimeout(function () {
  8211. instance._touching = false;
  8212. }, 700);
  8213. }
  8214. var domHandlers = {
  8215. /**
  8216. * Mouse move handler
  8217. * @inner
  8218. * @param {Event} event
  8219. */
  8220. mousemove: function (event) {
  8221. event = normalizeEvent(this.dom, event);
  8222. this.trigger('mousemove', event);
  8223. },
  8224. /**
  8225. * Mouse out handler
  8226. * @inner
  8227. * @param {Event} event
  8228. */
  8229. mouseout: function (event) {
  8230. event = normalizeEvent(this.dom, event);
  8231. var element = event.toElement || event.relatedTarget;
  8232. if (element != this.dom) {
  8233. while (element && element.nodeType != 9) {
  8234. // 忽略包含在root中的dom引起的mouseOut
  8235. if (element === this.dom) {
  8236. return;
  8237. }
  8238. element = element.parentNode;
  8239. }
  8240. }
  8241. this.trigger('mouseout', event);
  8242. },
  8243. /**
  8244. * Touch开始响应函数
  8245. * @inner
  8246. * @param {Event} event
  8247. */
  8248. touchstart: function (event) {
  8249. // Default mouse behaviour should not be disabled here.
  8250. // For example, page may needs to be slided.
  8251. event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and
  8252. // mouse event in upper applicatoin.
  8253. event.zrByTouch = true;
  8254. this._lastTouchMoment = new Date();
  8255. processGesture(this, event, 'start'); // In touch device, trigger `mousemove`(`mouseover`) should
  8256. // be triggered, and must before `mousedown` triggered.
  8257. domHandlers.mousemove.call(this, event);
  8258. domHandlers.mousedown.call(this, event);
  8259. setTouchTimer(this);
  8260. },
  8261. /**
  8262. * Touch移动响应函数
  8263. * @inner
  8264. * @param {Event} event
  8265. */
  8266. touchmove: function (event) {
  8267. event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and
  8268. // mouse event in upper applicatoin.
  8269. event.zrByTouch = true;
  8270. processGesture(this, event, 'change'); // Mouse move should always be triggered no matter whether
  8271. // there is gestrue event, because mouse move and pinch may
  8272. // be used at the same time.
  8273. domHandlers.mousemove.call(this, event);
  8274. setTouchTimer(this);
  8275. },
  8276. /**
  8277. * Touch结束响应函数
  8278. * @inner
  8279. * @param {Event} event
  8280. */
  8281. touchend: function (event) {
  8282. event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and
  8283. // mouse event in upper applicatoin.
  8284. event.zrByTouch = true;
  8285. processGesture(this, event, 'end');
  8286. domHandlers.mouseup.call(this, event); // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is
  8287. // triggered in `touchstart`. This seems to be illogical, but by this mechanism,
  8288. // we can conveniently implement "hover style" in both PC and touch device just
  8289. // by listening to `mouseover` to add "hover style" and listening to `mouseout`
  8290. // to remove "hover style" on an element, without any additional code for
  8291. // compatibility. (`mouseout` will not be triggered in `touchend`, so "hover
  8292. // style" will remain for user view)
  8293. // click event should always be triggered no matter whether
  8294. // there is gestrue event. System click can not be prevented.
  8295. if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {
  8296. domHandlers.click.call(this, event);
  8297. }
  8298. setTouchTimer(this);
  8299. },
  8300. pointerdown: function (event) {
  8301. domHandlers.mousedown.call(this, event); // if (useMSGuesture(this, event)) {
  8302. // this._msGesture.addPointer(event.pointerId);
  8303. // }
  8304. },
  8305. pointermove: function (event) {
  8306. // FIXME
  8307. // pointermove is so sensitive that it always triggered when
  8308. // tap(click) on touch screen, which affect some judgement in
  8309. // upper application. So, we dont support mousemove on MS touch
  8310. // device yet.
  8311. if (!isPointerFromTouch(event)) {
  8312. domHandlers.mousemove.call(this, event);
  8313. }
  8314. },
  8315. pointerup: function (event) {
  8316. domHandlers.mouseup.call(this, event);
  8317. },
  8318. pointerout: function (event) {
  8319. // pointerout will be triggered when tap on touch screen
  8320. // (IE11+/Edge on MS Surface) after click event triggered,
  8321. // which is inconsistent with the mousout behavior we defined
  8322. // in touchend. So we unify them.
  8323. // (check domHandlers.touchend for detailed explanation)
  8324. if (!isPointerFromTouch(event)) {
  8325. domHandlers.mouseout.call(this, event);
  8326. }
  8327. }
  8328. };
  8329. function isPointerFromTouch(event) {
  8330. var pointerType = event.pointerType;
  8331. return pointerType === 'pen' || pointerType === 'touch';
  8332. } // function useMSGuesture(handlerProxy, event) {
  8333. // return isPointerFromTouch(event) && !!handlerProxy._msGesture;
  8334. // }
  8335. // Common handlers
  8336. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  8337. domHandlers[name] = function (event) {
  8338. event = normalizeEvent(this.dom, event);
  8339. this.trigger(name, event);
  8340. };
  8341. });
  8342. /**
  8343. * 为控制类实例初始化dom 事件处理函数
  8344. *
  8345. * @inner
  8346. * @param {module:zrender/Handler} instance 控制类实例
  8347. */
  8348. function initDomHandler(instance) {
  8349. each$1(touchHandlerNames, function (name) {
  8350. instance._handlers[name] = bind(domHandlers[name], instance);
  8351. });
  8352. each$1(pointerHandlerNames, function (name) {
  8353. instance._handlers[name] = bind(domHandlers[name], instance);
  8354. });
  8355. each$1(mouseHandlerNames, function (name) {
  8356. instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);
  8357. });
  8358. function makeMouseHandler(fn, instance) {
  8359. return function () {
  8360. if (instance._touching) {
  8361. return;
  8362. }
  8363. return fn.apply(instance, arguments);
  8364. };
  8365. }
  8366. }
  8367. function HandlerDomProxy(dom) {
  8368. Eventful.call(this);
  8369. this.dom = dom;
  8370. /**
  8371. * @private
  8372. * @type {boolean}
  8373. */
  8374. this._touching = false;
  8375. /**
  8376. * @private
  8377. * @type {number}
  8378. */
  8379. this._touchTimer;
  8380. /**
  8381. * @private
  8382. * @type {module:zrender/core/GestureMgr}
  8383. */
  8384. this._gestureMgr = new GestureMgr();
  8385. this._handlers = {};
  8386. initDomHandler(this);
  8387. if (env$1.pointerEventsSupported) {
  8388. // Only IE11+/Edge
  8389. // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),
  8390. // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event
  8391. // at the same time.
  8392. // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on
  8393. // screen, which do not occurs in pointer event.
  8394. // So we use pointer event to both detect touch gesture and mouse behavior.
  8395. mountHandlers(pointerHandlerNames, this); // FIXME
  8396. // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,
  8397. // which does not prevent defuault behavior occasionally (which may cause view port
  8398. // zoomed in but use can not zoom it back). And event.preventDefault() does not work.
  8399. // So we have to not to use MSGesture and not to support touchmove and pinch on MS
  8400. // touch screen. And we only support click behavior on MS touch screen now.
  8401. // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.
  8402. // We dont support touch on IE on win7.
  8403. // See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>
  8404. // if (typeof MSGesture === 'function') {
  8405. // (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line
  8406. // dom.addEventListener('MSGestureChange', onMSGestureChange);
  8407. // }
  8408. } else {
  8409. if (env$1.touchEventsSupported) {
  8410. mountHandlers(touchHandlerNames, this); // Handler of 'mouseout' event is needed in touch mode, which will be mounted below.
  8411. // addEventListener(root, 'mouseout', this._mouseoutHandler);
  8412. } // 1. Considering some devices that both enable touch and mouse event (like on MS Surface
  8413. // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise
  8414. // mouse event can not be handle in those devices.
  8415. // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent
  8416. // mouseevent after touch event triggered, see `setTouchTimer`.
  8417. mountHandlers(mouseHandlerNames, this);
  8418. }
  8419. function mountHandlers(handlerNames, instance) {
  8420. each$1(handlerNames, function (name) {
  8421. addEventListener(dom, eventNameFix(name), instance._handlers[name]);
  8422. }, instance);
  8423. }
  8424. }
  8425. var handlerDomProxyProto = HandlerDomProxy.prototype;
  8426. handlerDomProxyProto.dispose = function () {
  8427. var handlerNames = mouseHandlerNames.concat(touchHandlerNames);
  8428. for (var i = 0; i < handlerNames.length; i++) {
  8429. var name = handlerNames[i];
  8430. removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);
  8431. }
  8432. };
  8433. handlerDomProxyProto.setCursor = function (cursorStyle) {
  8434. this.dom.style.cursor = cursorStyle || 'default';
  8435. };
  8436. mixin(HandlerDomProxy, Eventful);
  8437. /*!
  8438. * ZRender, a high performance 2d drawing library.
  8439. *
  8440. * Copyright (c) 2013, Baidu Inc.
  8441. * All rights reserved.
  8442. *
  8443. * LICENSE
  8444. * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt
  8445. */
  8446. var useVML = !env$1.canvasSupported;
  8447. var painterCtors = {
  8448. canvas: Painter
  8449. };
  8450. var instances$1 = {}; // ZRender实例map索引
  8451. /**
  8452. * @type {string}
  8453. */
  8454. var version$1 = '3.7.4';
  8455. /**
  8456. * Initializing a zrender instance
  8457. * @param {HTMLElement} dom
  8458. * @param {Object} opts
  8459. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  8460. * @param {number} [opts.devicePixelRatio]
  8461. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  8462. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  8463. * @return {module:zrender/ZRender}
  8464. */
  8465. function init$1(dom, opts) {
  8466. var zr = new ZRender(guid(), dom, opts);
  8467. instances$1[zr.id] = zr;
  8468. return zr;
  8469. }
  8470. /**
  8471. * Dispose zrender instance
  8472. * @param {module:zrender/ZRender} zr
  8473. */
  8474. function dispose$1(zr) {
  8475. if (zr) {
  8476. zr.dispose();
  8477. } else {
  8478. for (var key in instances$1) {
  8479. if (instances$1.hasOwnProperty(key)) {
  8480. instances$1[key].dispose();
  8481. }
  8482. }
  8483. instances$1 = {};
  8484. }
  8485. return this;
  8486. }
  8487. /**
  8488. * Get zrender instance by id
  8489. * @param {string} id zrender instance id
  8490. * @return {module:zrender/ZRender}
  8491. */
  8492. function getInstance(id) {
  8493. return instances$1[id];
  8494. }
  8495. function registerPainter(name, Ctor) {
  8496. painterCtors[name] = Ctor;
  8497. }
  8498. function delInstance(id) {
  8499. delete instances$1[id];
  8500. }
  8501. /**
  8502. * @module zrender/ZRender
  8503. */
  8504. /**
  8505. * @constructor
  8506. * @alias module:zrender/ZRender
  8507. * @param {string} id
  8508. * @param {HTMLElement} dom
  8509. * @param {Object} opts
  8510. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  8511. * @param {number} [opts.devicePixelRatio]
  8512. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  8513. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  8514. */
  8515. var ZRender = function (id, dom, opts) {
  8516. opts = opts || {};
  8517. /**
  8518. * @type {HTMLDomElement}
  8519. */
  8520. this.dom = dom;
  8521. /**
  8522. * @type {string}
  8523. */
  8524. this.id = id;
  8525. var self = this;
  8526. var storage = new Storage();
  8527. var rendererType = opts.renderer; // TODO WebGL
  8528. if (useVML) {
  8529. if (!painterCtors.vml) {
  8530. throw new Error('You need to require \'zrender/vml/vml\' to support IE8');
  8531. }
  8532. rendererType = 'vml';
  8533. } else if (!rendererType || !painterCtors[rendererType]) {
  8534. rendererType = 'canvas';
  8535. }
  8536. var painter = new painterCtors[rendererType](dom, storage, opts);
  8537. this.storage = storage;
  8538. this.painter = painter;
  8539. var handerProxy = !env$1.node ? new HandlerDomProxy(painter.getViewportRoot()) : null;
  8540. this.handler = new Handler(storage, painter, handerProxy, painter.root);
  8541. /**
  8542. * @type {module:zrender/animation/Animation}
  8543. */
  8544. this.animation = new Animation({
  8545. stage: {
  8546. update: bind(this.flush, this)
  8547. }
  8548. });
  8549. this.animation.start();
  8550. /**
  8551. * @type {boolean}
  8552. * @private
  8553. */
  8554. this._needsRefresh; // 修改 storage.delFromStorage, 每次删除元素之前删除动画
  8555. // FIXME 有点ugly
  8556. var oldDelFromStorage = storage.delFromStorage;
  8557. var oldAddToStorage = storage.addToStorage;
  8558. storage.delFromStorage = function (el) {
  8559. oldDelFromStorage.call(storage, el);
  8560. el && el.removeSelfFromZr(self);
  8561. };
  8562. storage.addToStorage = function (el) {
  8563. oldAddToStorage.call(storage, el);
  8564. el.addSelfToZr(self);
  8565. };
  8566. };
  8567. ZRender.prototype = {
  8568. constructor: ZRender,
  8569. /**
  8570. * 获取实例唯一标识
  8571. * @return {string}
  8572. */
  8573. getId: function () {
  8574. return this.id;
  8575. },
  8576. /**
  8577. * 添加元素
  8578. * @param {module:zrender/Element} el
  8579. */
  8580. add: function (el) {
  8581. this.storage.addRoot(el);
  8582. this._needsRefresh = true;
  8583. },
  8584. /**
  8585. * 删除元素
  8586. * @param {module:zrender/Element} el
  8587. */
  8588. remove: function (el) {
  8589. this.storage.delRoot(el);
  8590. this._needsRefresh = true;
  8591. },
  8592. /**
  8593. * Change configuration of layer
  8594. * @param {string} zLevel
  8595. * @param {Object} config
  8596. * @param {string} [config.clearColor=0] Clear color
  8597. * @param {string} [config.motionBlur=false] If enable motion blur
  8598. * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer
  8599. */
  8600. configLayer: function (zLevel, config) {
  8601. this.painter.configLayer(zLevel, config);
  8602. this._needsRefresh = true;
  8603. },
  8604. /**
  8605. * Repaint the canvas immediately
  8606. */
  8607. refreshImmediately: function () {
  8608. // var start = new Date();
  8609. // Clear needsRefresh ahead to avoid something wrong happens in refresh
  8610. // Or it will cause zrender refreshes again and again.
  8611. this._needsRefresh = false;
  8612. this.painter.refresh();
  8613. /**
  8614. * Avoid trigger zr.refresh in Element#beforeUpdate hook
  8615. */
  8616. this._needsRefresh = false; // var end = new Date();
  8617. // var log = document.getElementById('log');
  8618. // if (log) {
  8619. // log.innerHTML = log.innerHTML + '<br>' + (end - start);
  8620. // }
  8621. },
  8622. /**
  8623. * Mark and repaint the canvas in the next frame of browser
  8624. */
  8625. refresh: function () {
  8626. this._needsRefresh = true;
  8627. },
  8628. /**
  8629. * Perform all refresh
  8630. */
  8631. flush: function () {
  8632. if (this._needsRefresh) {
  8633. this.refreshImmediately();
  8634. }
  8635. if (this._needsRefreshHover) {
  8636. this.refreshHoverImmediately();
  8637. }
  8638. },
  8639. /**
  8640. * Add element to hover layer
  8641. * @param {module:zrender/Element} el
  8642. * @param {Object} style
  8643. */
  8644. addHover: function (el, style) {
  8645. if (this.painter.addHover) {
  8646. this.painter.addHover(el, style);
  8647. this.refreshHover();
  8648. }
  8649. },
  8650. /**
  8651. * Add element from hover layer
  8652. * @param {module:zrender/Element} el
  8653. */
  8654. removeHover: function (el) {
  8655. if (this.painter.removeHover) {
  8656. this.painter.removeHover(el);
  8657. this.refreshHover();
  8658. }
  8659. },
  8660. /**
  8661. * Clear all hover elements in hover layer
  8662. * @param {module:zrender/Element} el
  8663. */
  8664. clearHover: function () {
  8665. if (this.painter.clearHover) {
  8666. this.painter.clearHover();
  8667. this.refreshHover();
  8668. }
  8669. },
  8670. /**
  8671. * Refresh hover in next frame
  8672. */
  8673. refreshHover: function () {
  8674. this._needsRefreshHover = true;
  8675. },
  8676. /**
  8677. * Refresh hover immediately
  8678. */
  8679. refreshHoverImmediately: function () {
  8680. this._needsRefreshHover = false;
  8681. this.painter.refreshHover && this.painter.refreshHover();
  8682. },
  8683. /**
  8684. * Resize the canvas.
  8685. * Should be invoked when container size is changed
  8686. * @param {Object} [opts]
  8687. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  8688. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  8689. */
  8690. resize: function (opts) {
  8691. opts = opts || {};
  8692. this.painter.resize(opts.width, opts.height);
  8693. this.handler.resize();
  8694. },
  8695. /**
  8696. * Stop and clear all animation immediately
  8697. */
  8698. clearAnimation: function () {
  8699. this.animation.clear();
  8700. },
  8701. /**
  8702. * Get container width
  8703. */
  8704. getWidth: function () {
  8705. return this.painter.getWidth();
  8706. },
  8707. /**
  8708. * Get container height
  8709. */
  8710. getHeight: function () {
  8711. return this.painter.getHeight();
  8712. },
  8713. /**
  8714. * Export the canvas as Base64 URL
  8715. * @param {string} type
  8716. * @param {string} [backgroundColor='#fff']
  8717. * @return {string} Base64 URL
  8718. */
  8719. // toDataURL: function(type, backgroundColor) {
  8720. // return this.painter.getRenderedCanvas({
  8721. // backgroundColor: backgroundColor
  8722. // }).toDataURL(type);
  8723. // },
  8724. /**
  8725. * Converting a path to image.
  8726. * It has much better performance of drawing image rather than drawing a vector path.
  8727. * @param {module:zrender/graphic/Path} e
  8728. * @param {number} width
  8729. * @param {number} height
  8730. */
  8731. pathToImage: function (e, dpr) {
  8732. return this.painter.pathToImage(e, dpr);
  8733. },
  8734. /**
  8735. * Set default cursor
  8736. * @param {string} [cursorStyle='default'] 例如 crosshair
  8737. */
  8738. setCursorStyle: function (cursorStyle) {
  8739. this.handler.setCursorStyle(cursorStyle);
  8740. },
  8741. /**
  8742. * Find hovered element
  8743. * @param {number} x
  8744. * @param {number} y
  8745. * @return {Object} {target, topTarget}
  8746. */
  8747. findHover: function (x, y) {
  8748. return this.handler.findHover(x, y);
  8749. },
  8750. /**
  8751. * Bind event
  8752. *
  8753. * @param {string} eventName Event name
  8754. * @param {Function} eventHandler Handler function
  8755. * @param {Object} [context] Context object
  8756. */
  8757. on: function (eventName, eventHandler, context) {
  8758. this.handler.on(eventName, eventHandler, context);
  8759. },
  8760. /**
  8761. * Unbind event
  8762. * @param {string} eventName Event name
  8763. * @param {Function} [eventHandler] Handler function
  8764. */
  8765. off: function (eventName, eventHandler) {
  8766. this.handler.off(eventName, eventHandler);
  8767. },
  8768. /**
  8769. * Trigger event manually
  8770. *
  8771. * @param {string} eventName Event name
  8772. * @param {event=} event Event object
  8773. */
  8774. trigger: function (eventName, event) {
  8775. this.handler.trigger(eventName, event);
  8776. },
  8777. /**
  8778. * Clear all objects and the canvas.
  8779. */
  8780. clear: function () {
  8781. this.storage.delRoot();
  8782. this.painter.clear();
  8783. },
  8784. /**
  8785. * Dispose self.
  8786. */
  8787. dispose: function () {
  8788. this.animation.stop();
  8789. this.clear();
  8790. this.storage.dispose();
  8791. this.painter.dispose();
  8792. this.handler.dispose();
  8793. this.animation = this.storage = this.painter = this.handler = null;
  8794. delInstance(this.id);
  8795. }
  8796. };
  8797. var zrender = (Object.freeze || Object)({
  8798. version: version$1,
  8799. init: init$1,
  8800. dispose: dispose$1,
  8801. getInstance: getInstance,
  8802. registerPainter: registerPainter
  8803. });
  8804. var RADIAN_EPSILON = 1e-4;
  8805. function _trim(str) {
  8806. return str.replace(/^\s+/, '').replace(/\s+$/, '');
  8807. }
  8808. /**
  8809. * Linear mapping a value from domain to range
  8810. * @memberOf module:echarts/util/number
  8811. * @param {(number|Array.<number>)} val
  8812. * @param {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1]
  8813. * @param {Array.<number>} range Range extent range[0] can be bigger than range[1]
  8814. * @param {boolean} clamp
  8815. * @return {(number|Array.<number>}
  8816. */
  8817. function linearMap(val, domain, range, clamp) {
  8818. var subDomain = domain[1] - domain[0];
  8819. var subRange = range[1] - range[0];
  8820. if (subDomain === 0) {
  8821. return subRange === 0 ? range[0] : (range[0] + range[1]) / 2;
  8822. } // Avoid accuracy problem in edge, such as
  8823. // 146.39 - 62.83 === 83.55999999999999.
  8824. // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
  8825. // It is a little verbose for efficiency considering this method
  8826. // is a hotspot.
  8827. if (clamp) {
  8828. if (subDomain > 0) {
  8829. if (val <= domain[0]) {
  8830. return range[0];
  8831. } else if (val >= domain[1]) {
  8832. return range[1];
  8833. }
  8834. } else {
  8835. if (val >= domain[0]) {
  8836. return range[0];
  8837. } else if (val <= domain[1]) {
  8838. return range[1];
  8839. }
  8840. }
  8841. } else {
  8842. if (val === domain[0]) {
  8843. return range[0];
  8844. }
  8845. if (val === domain[1]) {
  8846. return range[1];
  8847. }
  8848. }
  8849. return (val - domain[0]) / subDomain * subRange + range[0];
  8850. }
  8851. /**
  8852. * Convert a percent string to absolute number.
  8853. * Returns NaN if percent is not a valid string or number
  8854. * @memberOf module:echarts/util/number
  8855. * @param {string|number} percent
  8856. * @param {number} all
  8857. * @return {number}
  8858. */
  8859. function parsePercent$1(percent, all) {
  8860. switch (percent) {
  8861. case 'center':
  8862. case 'middle':
  8863. percent = '50%';
  8864. break;
  8865. case 'left':
  8866. case 'top':
  8867. percent = '0%';
  8868. break;
  8869. case 'right':
  8870. case 'bottom':
  8871. percent = '100%';
  8872. break;
  8873. }
  8874. if (typeof percent === 'string') {
  8875. if (_trim(percent).match(/%$/)) {
  8876. return parseFloat(percent) / 100 * all;
  8877. }
  8878. return parseFloat(percent);
  8879. }
  8880. return percent == null ? NaN : +percent;
  8881. }
  8882. /**
  8883. * (1) Fix rounding error of float numbers.
  8884. * (2) Support return string to avoid scientific notation like '3.5e-7'.
  8885. *
  8886. * @param {number} x
  8887. * @param {number} [precision]
  8888. * @param {boolean} [returnStr]
  8889. * @return {number|string}
  8890. */
  8891. function round(x, precision, returnStr) {
  8892. if (precision == null) {
  8893. precision = 10;
  8894. } // Avoid range error
  8895. precision = Math.min(Math.max(0, precision), 20);
  8896. x = (+x).toFixed(precision);
  8897. return returnStr ? x : +x;
  8898. }
  8899. function asc(arr) {
  8900. arr.sort(function (a, b) {
  8901. return a - b;
  8902. });
  8903. return arr;
  8904. }
  8905. /**
  8906. * Get precision
  8907. * @param {number} val
  8908. */
  8909. function getPrecision(val) {
  8910. val = +val;
  8911. if (isNaN(val)) {
  8912. return 0;
  8913. } // It is much faster than methods converting number to string as follows
  8914. // var tmp = val.toString();
  8915. // return tmp.length - 1 - tmp.indexOf('.');
  8916. // especially when precision is low
  8917. var e = 1;
  8918. var count = 0;
  8919. while (Math.round(val * e) / e !== val) {
  8920. e *= 10;
  8921. count++;
  8922. }
  8923. return count;
  8924. }
  8925. /**
  8926. * @param {string|number} val
  8927. * @return {number}
  8928. */
  8929. function getPrecisionSafe(val) {
  8930. var str = val.toString(); // Consider scientific notation: '3.4e-12' '3.4e+12'
  8931. var eIndex = str.indexOf('e');
  8932. if (eIndex > 0) {
  8933. var precision = +str.slice(eIndex + 1);
  8934. return precision < 0 ? -precision : 0;
  8935. } else {
  8936. var dotIndex = str.indexOf('.');
  8937. return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;
  8938. }
  8939. }
  8940. /**
  8941. * Minimal dicernible data precisioin according to a single pixel.
  8942. *
  8943. * @param {Array.<number>} dataExtent
  8944. * @param {Array.<number>} pixelExtent
  8945. * @return {number} precision
  8946. */
  8947. function getPixelPrecision(dataExtent, pixelExtent) {
  8948. var log = Math.log;
  8949. var LN10 = Math.LN10;
  8950. var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);
  8951. var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); // toFixed() digits argument must be between 0 and 20.
  8952. var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);
  8953. return !isFinite(precision) ? 20 : precision;
  8954. }
  8955. /**
  8956. * Get a data of given precision, assuring the sum of percentages
  8957. * in valueList is 1.
  8958. * The largest remainer method is used.
  8959. * https://en.wikipedia.org/wiki/Largest_remainder_method
  8960. *
  8961. * @param {Array.<number>} valueList a list of all data
  8962. * @param {number} idx index of the data to be processed in valueList
  8963. * @param {number} precision integer number showing digits of precision
  8964. * @return {number} percent ranging from 0 to 100
  8965. */
  8966. function getPercentWithPrecision(valueList, idx, precision) {
  8967. if (!valueList[idx]) {
  8968. return 0;
  8969. }
  8970. var sum = reduce(valueList, function (acc, val) {
  8971. return acc + (isNaN(val) ? 0 : val);
  8972. }, 0);
  8973. if (sum === 0) {
  8974. return 0;
  8975. }
  8976. var digits = Math.pow(10, precision);
  8977. var votesPerQuota = map(valueList, function (val) {
  8978. return (isNaN(val) ? 0 : val) / sum * digits * 100;
  8979. });
  8980. var targetSeats = digits * 100;
  8981. var seats = map(votesPerQuota, function (votes) {
  8982. // Assign automatic seats.
  8983. return Math.floor(votes);
  8984. });
  8985. var currentSum = reduce(seats, function (acc, val) {
  8986. return acc + val;
  8987. }, 0);
  8988. var remainder = map(votesPerQuota, function (votes, idx) {
  8989. return votes - seats[idx];
  8990. }); // Has remainding votes.
  8991. while (currentSum < targetSeats) {
  8992. // Find next largest remainder.
  8993. var max = Number.NEGATIVE_INFINITY;
  8994. var maxId = null;
  8995. for (var i = 0, len = remainder.length; i < len; ++i) {
  8996. if (remainder[i] > max) {
  8997. max = remainder[i];
  8998. maxId = i;
  8999. }
  9000. } // Add a vote to max remainder.
  9001. ++seats[maxId];
  9002. remainder[maxId] = 0;
  9003. ++currentSum;
  9004. }
  9005. return seats[idx] / digits;
  9006. } // Number.MAX_SAFE_INTEGER, ie do not support.
  9007. var MAX_SAFE_INTEGER = 9007199254740991;
  9008. /**
  9009. * To 0 - 2 * PI, considering negative radian.
  9010. * @param {number} radian
  9011. * @return {number}
  9012. */
  9013. function remRadian(radian) {
  9014. var pi2 = Math.PI * 2;
  9015. return (radian % pi2 + pi2) % pi2;
  9016. }
  9017. /**
  9018. * @param {type} radian
  9019. * @return {boolean}
  9020. */
  9021. function isRadianAroundZero(val) {
  9022. return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
  9023. }
  9024. var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line
  9025. /**
  9026. * @param {string|Date|number} value These values can be accepted:
  9027. * + An instance of Date, represent a time in its own time zone.
  9028. * + Or string in a subset of ISO 8601, only including:
  9029. * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
  9030. * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
  9031. * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
  9032. * all of which will be treated as local time if time zone is not specified
  9033. * (see <https://momentjs.com/>).
  9034. * + Or other string format, including (all of which will be treated as loacal time):
  9035. * '2012', '2012-3-1', '2012/3/1', '2012/03/01',
  9036. * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
  9037. * + a timestamp, which represent a time in UTC.
  9038. * @return {Date} date
  9039. */
  9040. function parseDate(value) {
  9041. if (value instanceof Date) {
  9042. return value;
  9043. } else if (typeof value === 'string') {
  9044. // Different browsers parse date in different way, so we parse it manually.
  9045. // Some other issues:
  9046. // new Date('1970-01-01') is UTC,
  9047. // new Date('1970/01/01') and new Date('1970-1-01') is local.
  9048. // See issue #3623
  9049. var match = TIME_REG.exec(value);
  9050. if (!match) {
  9051. // return Invalid Date.
  9052. return new Date(NaN);
  9053. } // Use local time when no timezone offset specifed.
  9054. if (!match[8]) {
  9055. // match[n] can only be string or undefined.
  9056. // But take care of '12' + 1 => '121'.
  9057. return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, +match[7] || 0);
  9058. } // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
  9059. // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
  9060. // For example, system timezone is set as "Time Zone: America/Toronto",
  9061. // then these code will get different result:
  9062. // `new Date(1478411999999).getTimezoneOffset(); // get 240`
  9063. // `new Date(1478412000000).getTimezoneOffset(); // get 300`
  9064. // So we should not use `new Date`, but use `Date.UTC`.
  9065. else {
  9066. var hour = +match[4] || 0;
  9067. if (match[8].toUpperCase() !== 'Z') {
  9068. hour -= match[8].slice(0, 3);
  9069. }
  9070. return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, +match[7] || 0));
  9071. }
  9072. } else if (value == null) {
  9073. return new Date(NaN);
  9074. }
  9075. return new Date(Math.round(value));
  9076. }
  9077. /**
  9078. * Quantity of a number. e.g. 0.1, 1, 10, 100
  9079. *
  9080. * @param {number} val
  9081. * @return {number}
  9082. */
  9083. function quantity(val) {
  9084. return Math.pow(10, quantityExponent(val));
  9085. }
  9086. function quantityExponent(val) {
  9087. return Math.floor(Math.log(val) / Math.LN10);
  9088. }
  9089. /**
  9090. * find a “nice” number approximately equal to x. Round the number if round = true,
  9091. * take ceiling if round = false. The primary observation is that the “nicest”
  9092. * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
  9093. *
  9094. * See "Nice Numbers for Graph Labels" of Graphic Gems.
  9095. *
  9096. * @param {number} val Non-negative value.
  9097. * @param {boolean} round
  9098. * @return {number}
  9099. */
  9100. function nice(val, round) {
  9101. var exponent = quantityExponent(val);
  9102. var exp10 = Math.pow(10, exponent);
  9103. var f = val / exp10; // 1 <= f < 10
  9104. var nf;
  9105. if (round) {
  9106. if (f < 1.5) {
  9107. nf = 1;
  9108. } else if (f < 2.5) {
  9109. nf = 2;
  9110. } else if (f < 4) {
  9111. nf = 3;
  9112. } else if (f < 7) {
  9113. nf = 5;
  9114. } else {
  9115. nf = 10;
  9116. }
  9117. } else {
  9118. if (f < 1) {
  9119. nf = 1;
  9120. } else if (f < 2) {
  9121. nf = 2;
  9122. } else if (f < 3) {
  9123. nf = 3;
  9124. } else if (f < 5) {
  9125. nf = 5;
  9126. } else {
  9127. nf = 10;
  9128. }
  9129. }
  9130. val = nf * exp10; // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).
  9131. // 20 is the uppper bound of toFixed.
  9132. return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
  9133. }
  9134. /**
  9135. * Order intervals asc, and split them when overlap.
  9136. * expect(numberUtil.reformIntervals([
  9137. * {interval: [18, 62], close: [1, 1]},
  9138. * {interval: [-Infinity, -70], close: [0, 0]},
  9139. * {interval: [-70, -26], close: [1, 1]},
  9140. * {interval: [-26, 18], close: [1, 1]},
  9141. * {interval: [62, 150], close: [1, 1]},
  9142. * {interval: [106, 150], close: [1, 1]},
  9143. * {interval: [150, Infinity], close: [0, 0]}
  9144. * ])).toEqual([
  9145. * {interval: [-Infinity, -70], close: [0, 0]},
  9146. * {interval: [-70, -26], close: [1, 1]},
  9147. * {interval: [-26, 18], close: [0, 1]},
  9148. * {interval: [18, 62], close: [0, 1]},
  9149. * {interval: [62, 150], close: [0, 1]},
  9150. * {interval: [150, Infinity], close: [0, 0]}
  9151. * ]);
  9152. * @param {Array.<Object>} list, where `close` mean open or close
  9153. * of the interval, and Infinity can be used.
  9154. * @return {Array.<Object>} The origin list, which has been reformed.
  9155. */
  9156. function reformIntervals(list) {
  9157. list.sort(function (a, b) {
  9158. return littleThan(a, b, 0) ? -1 : 1;
  9159. });
  9160. var curr = -Infinity;
  9161. var currClose = 1;
  9162. for (var i = 0; i < list.length;) {
  9163. var interval = list[i].interval;
  9164. var close = list[i].close;
  9165. for (var lg = 0; lg < 2; lg++) {
  9166. if (interval[lg] <= curr) {
  9167. interval[lg] = curr;
  9168. close[lg] = !lg ? 1 - currClose : 1;
  9169. }
  9170. curr = interval[lg];
  9171. currClose = close[lg];
  9172. }
  9173. if (interval[0] === interval[1] && close[0] * close[1] !== 1) {
  9174. list.splice(i, 1);
  9175. } else {
  9176. i++;
  9177. }
  9178. }
  9179. return list;
  9180. function littleThan(a, b, lg) {
  9181. return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1));
  9182. }
  9183. }
  9184. /**
  9185. * parseFloat NaNs numeric-cast false positives (null|true|false|"")
  9186. * ...but misinterprets leading-number strings, particularly hex literals ("0x...")
  9187. * subtraction forces infinities to NaN
  9188. *
  9189. * @param {*} v
  9190. * @return {boolean}
  9191. */
  9192. function isNumeric(v) {
  9193. return v - parseFloat(v) >= 0;
  9194. }
  9195. var number = (Object.freeze || Object)({
  9196. linearMap: linearMap,
  9197. parsePercent: parsePercent$1,
  9198. round: round,
  9199. asc: asc,
  9200. getPrecision: getPrecision,
  9201. getPrecisionSafe: getPrecisionSafe,
  9202. getPixelPrecision: getPixelPrecision,
  9203. getPercentWithPrecision: getPercentWithPrecision,
  9204. MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,
  9205. remRadian: remRadian,
  9206. isRadianAroundZero: isRadianAroundZero,
  9207. parseDate: parseDate,
  9208. quantity: quantity,
  9209. nice: nice,
  9210. reformIntervals: reformIntervals,
  9211. isNumeric: isNumeric
  9212. });
  9213. /**
  9214. * 每三位默认加,格式化
  9215. * @param {string|number} x
  9216. * @return {string}
  9217. */
  9218. function addCommas(x) {
  9219. if (isNaN(x)) {
  9220. return '-';
  9221. }
  9222. x = (x + '').split('.');
  9223. return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, '$1,') + (x.length > 1 ? '.' + x[1] : '');
  9224. }
  9225. /**
  9226. * @param {string} str
  9227. * @param {boolean} [upperCaseFirst=false]
  9228. * @return {string} str
  9229. */
  9230. function toCamelCase(str, upperCaseFirst) {
  9231. str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {
  9232. return group1.toUpperCase();
  9233. });
  9234. if (upperCaseFirst && str) {
  9235. str = str.charAt(0).toUpperCase() + str.slice(1);
  9236. }
  9237. return str;
  9238. }
  9239. var normalizeCssArray$1 = normalizeCssArray;
  9240. function encodeHTML(source) {
  9241. return String(source).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
  9242. }
  9243. var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
  9244. var wrapVar = function (varName, seriesIdx) {
  9245. return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';
  9246. };
  9247. /**
  9248. * Template formatter
  9249. * @param {string} tpl
  9250. * @param {Array.<Object>|Object} paramsList
  9251. * @param {boolean} [encode=false]
  9252. * @return {string}
  9253. */
  9254. function formatTpl(tpl, paramsList, encode) {
  9255. if (!isArray(paramsList)) {
  9256. paramsList = [paramsList];
  9257. }
  9258. var seriesLen = paramsList.length;
  9259. if (!seriesLen) {
  9260. return '';
  9261. }
  9262. var $vars = paramsList[0].$vars || [];
  9263. for (var i = 0; i < $vars.length; i++) {
  9264. var alias = TPL_VAR_ALIAS[i];
  9265. var val = wrapVar(alias, 0);
  9266. tpl = tpl.replace(wrapVar(alias), encode ? encodeHTML(val) : val);
  9267. }
  9268. for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {
  9269. for (var k = 0; k < $vars.length; k++) {
  9270. var val = paramsList[seriesIdx][$vars[k]];
  9271. tpl = tpl.replace(wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val);
  9272. }
  9273. }
  9274. return tpl;
  9275. }
  9276. /**
  9277. * simple Template formatter
  9278. *
  9279. * @param {string} tpl
  9280. * @param {Object} param
  9281. * @param {boolean} [encode=false]
  9282. * @return {string}
  9283. */
  9284. function formatTplSimple(tpl, param, encode) {
  9285. each$1(param, function (value, key) {
  9286. tpl = tpl.replace('{' + key + '}', encode ? encodeHTML(value) : value);
  9287. });
  9288. return tpl;
  9289. }
  9290. /**
  9291. * @param {string} color
  9292. * @param {string} [extraCssText]
  9293. * @return {string}
  9294. */
  9295. function getTooltipMarker(color, extraCssText) {
  9296. return color ? '<span style="display:inline-block;margin-right:5px;' + 'border-radius:10px;width:9px;height:9px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>' : '';
  9297. }
  9298. /**
  9299. * @param {string} str
  9300. * @return {string}
  9301. * @inner
  9302. */
  9303. var s2d = function (str) {
  9304. return str < 10 ? '0' + str : str;
  9305. };
  9306. /**
  9307. * ISO Date format
  9308. * @param {string} tpl
  9309. * @param {number} value
  9310. * @param {boolean} [isUTC=false] Default in local time.
  9311. * see `module:echarts/scale/Time`
  9312. * and `module:echarts/util/number#parseDate`.
  9313. * @inner
  9314. */
  9315. function formatTime(tpl, value, isUTC) {
  9316. if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year') {
  9317. tpl = 'MM-dd\nyyyy';
  9318. }
  9319. var date = parseDate(value);
  9320. var utc = isUTC ? 'UTC' : '';
  9321. var y = date['get' + utc + 'FullYear']();
  9322. var M = date['get' + utc + 'Month']() + 1;
  9323. var d = date['get' + utc + 'Date']();
  9324. var h = date['get' + utc + 'Hours']();
  9325. var m = date['get' + utc + 'Minutes']();
  9326. var s = date['get' + utc + 'Seconds']();
  9327. tpl = tpl.replace('MM', s2d(M)).replace('M', M).replace('yyyy', y).replace('yy', y % 100).replace('dd', s2d(d)).replace('d', d).replace('hh', s2d(h)).replace('h', h).replace('mm', s2d(m)).replace('m', m).replace('ss', s2d(s)).replace('s', s);
  9328. return tpl;
  9329. }
  9330. /**
  9331. * Capital first
  9332. * @param {string} str
  9333. * @return {string}
  9334. */
  9335. function capitalFirst(str) {
  9336. return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;
  9337. }
  9338. var truncateText$1 = truncateText;
  9339. var getTextRect = getBoundingRect;
  9340. var format = (Object.freeze || Object)({
  9341. addCommas: addCommas,
  9342. toCamelCase: toCamelCase,
  9343. normalizeCssArray: normalizeCssArray$1,
  9344. encodeHTML: encodeHTML,
  9345. formatTpl: formatTpl,
  9346. formatTplSimple: formatTplSimple,
  9347. getTooltipMarker: getTooltipMarker,
  9348. formatTime: formatTime,
  9349. capitalFirst: capitalFirst,
  9350. truncateText: truncateText$1,
  9351. getTextRect: getTextRect
  9352. });
  9353. var TYPE_DELIMITER = '.';
  9354. var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
  9355. var MEMBER_PRIFIX = '\0ec_\0';
  9356. /**
  9357. * Hide private class member.
  9358. * The same behavior as `host[name] = value;` (can be right-value)
  9359. * @public
  9360. */
  9361. function set$1(host, name, value) {
  9362. return host[MEMBER_PRIFIX + name] = value;
  9363. }
  9364. /**
  9365. * Hide private class member.
  9366. * The same behavior as `host[name];`
  9367. * @public
  9368. */
  9369. function get(host, name) {
  9370. return host[MEMBER_PRIFIX + name];
  9371. }
  9372. /**
  9373. * For hidden private class member.
  9374. * The same behavior as `host.hasOwnProperty(name);`
  9375. * @public
  9376. */
  9377. function hasOwn(host, name) {
  9378. return host.hasOwnProperty(MEMBER_PRIFIX + name);
  9379. }
  9380. /**
  9381. * Notice, parseClassType('') should returns {main: '', sub: ''}
  9382. * @public
  9383. */
  9384. function parseClassType$1(componentType) {
  9385. var ret = {
  9386. main: '',
  9387. sub: ''
  9388. };
  9389. if (componentType) {
  9390. componentType = componentType.split(TYPE_DELIMITER);
  9391. ret.main = componentType[0] || '';
  9392. ret.sub = componentType[1] || '';
  9393. }
  9394. return ret;
  9395. }
  9396. /**
  9397. * @public
  9398. */
  9399. function checkClassType(componentType) {
  9400. assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType "' + componentType + '" illegal');
  9401. }
  9402. /**
  9403. * @public
  9404. */
  9405. function enableClassExtend(RootClass, mandatoryMethods) {
  9406. RootClass.$constructor = RootClass;
  9407. RootClass.extend = function (proto) {
  9408. if (true) {
  9409. each$1(mandatoryMethods, function (method) {
  9410. if (!proto[method]) {
  9411. console.warn('Method `' + method + '` should be implemented' + (proto.type ? ' in ' + proto.type : '') + '.');
  9412. }
  9413. });
  9414. }
  9415. var superClass = this;
  9416. var ExtendedClass = function () {
  9417. if (!proto.$constructor) {
  9418. superClass.apply(this, arguments);
  9419. } else {
  9420. proto.$constructor.apply(this, arguments);
  9421. }
  9422. };
  9423. extend(ExtendedClass.prototype, proto);
  9424. ExtendedClass.extend = this.extend;
  9425. ExtendedClass.superCall = superCall;
  9426. ExtendedClass.superApply = superApply;
  9427. inherits(ExtendedClass, this);
  9428. ExtendedClass.superClass = superClass;
  9429. return ExtendedClass;
  9430. };
  9431. } // superCall should have class info, which can not be fetch from 'this'.
  9432. // Consider this case:
  9433. // class A has method f,
  9434. // class B inherits class A, overrides method f, f call superApply('f'),
  9435. // class C inherits class B, do not overrides method f,
  9436. // then when method of class C is called, dead loop occured.
  9437. function superCall(context, methodName) {
  9438. var args = slice(arguments, 2);
  9439. return this.superClass.prototype[methodName].apply(context, args);
  9440. }
  9441. function superApply(context, methodName, args) {
  9442. return this.superClass.prototype[methodName].apply(context, args);
  9443. }
  9444. /**
  9445. * @param {Object} entity
  9446. * @param {Object} options
  9447. * @param {boolean} [options.registerWhenExtend]
  9448. * @public
  9449. */
  9450. function enableClassManagement(entity, options) {
  9451. options = options || {};
  9452. /**
  9453. * Component model classes
  9454. * key: componentType,
  9455. * value:
  9456. * componentClass, when componentType is 'xxx'
  9457. * or Object.<subKey, componentClass>, when componentType is 'xxx.yy'
  9458. * @type {Object}
  9459. */
  9460. var storage = {};
  9461. entity.registerClass = function (Clazz, componentType) {
  9462. if (componentType) {
  9463. checkClassType(componentType);
  9464. componentType = parseClassType$1(componentType);
  9465. if (!componentType.sub) {
  9466. if (true) {
  9467. if (storage[componentType.main]) {
  9468. console.warn(componentType.main + ' exists.');
  9469. }
  9470. }
  9471. storage[componentType.main] = Clazz;
  9472. } else if (componentType.sub !== IS_CONTAINER) {
  9473. var container = makeContainer(componentType);
  9474. container[componentType.sub] = Clazz;
  9475. }
  9476. }
  9477. return Clazz;
  9478. };
  9479. entity.getClass = function (componentMainType, subType, throwWhenNotFound) {
  9480. var Clazz = storage[componentMainType];
  9481. if (Clazz && Clazz[IS_CONTAINER]) {
  9482. Clazz = subType ? Clazz[subType] : null;
  9483. }
  9484. if (throwWhenNotFound && !Clazz) {
  9485. throw new Error(!subType ? componentMainType + '.' + 'type should be specified.' : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.');
  9486. }
  9487. return Clazz;
  9488. };
  9489. entity.getClassesByMainType = function (componentType) {
  9490. componentType = parseClassType$1(componentType);
  9491. var result = [];
  9492. var obj = storage[componentType.main];
  9493. if (obj && obj[IS_CONTAINER]) {
  9494. each$1(obj, function (o, type) {
  9495. type !== IS_CONTAINER && result.push(o);
  9496. });
  9497. } else {
  9498. result.push(obj);
  9499. }
  9500. return result;
  9501. };
  9502. entity.hasClass = function (componentType) {
  9503. // Just consider componentType.main.
  9504. componentType = parseClassType$1(componentType);
  9505. return !!storage[componentType.main];
  9506. };
  9507. /**
  9508. * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx']
  9509. */
  9510. entity.getAllClassMainTypes = function () {
  9511. var types = [];
  9512. each$1(storage, function (obj, type) {
  9513. types.push(type);
  9514. });
  9515. return types;
  9516. };
  9517. /**
  9518. * If a main type is container and has sub types
  9519. * @param {string} mainType
  9520. * @return {boolean}
  9521. */
  9522. entity.hasSubTypes = function (componentType) {
  9523. componentType = parseClassType$1(componentType);
  9524. var obj = storage[componentType.main];
  9525. return obj && obj[IS_CONTAINER];
  9526. };
  9527. entity.parseClassType = parseClassType$1;
  9528. function makeContainer(componentType) {
  9529. var container = storage[componentType.main];
  9530. if (!container || !container[IS_CONTAINER]) {
  9531. container = storage[componentType.main] = {};
  9532. container[IS_CONTAINER] = true;
  9533. }
  9534. return container;
  9535. }
  9536. if (options.registerWhenExtend) {
  9537. var originalExtend = entity.extend;
  9538. if (originalExtend) {
  9539. entity.extend = function (proto) {
  9540. var ExtendedClass = originalExtend.call(this, proto);
  9541. return entity.registerClass(ExtendedClass, proto.type);
  9542. };
  9543. }
  9544. }
  9545. return entity;
  9546. }
  9547. /**
  9548. * @param {string|Array.<string>} properties
  9549. */
  9550. // TODO Parse shadow style
  9551. // TODO Only shallow path support
  9552. var makeStyleMapper = function (properties) {
  9553. // Normalize
  9554. for (var i = 0; i < properties.length; i++) {
  9555. if (!properties[i][1]) {
  9556. properties[i][1] = properties[i][0];
  9557. }
  9558. }
  9559. return function (model, excludes, includes) {
  9560. var style = {};
  9561. for (var i = 0; i < properties.length; i++) {
  9562. var propName = properties[i][1];
  9563. if (excludes && indexOf(excludes, propName) >= 0 || includes && indexOf(includes, propName) < 0) {
  9564. continue;
  9565. }
  9566. var val = model.getShallow(propName);
  9567. if (val != null) {
  9568. style[properties[i][0]] = val;
  9569. }
  9570. }
  9571. return style;
  9572. };
  9573. };
  9574. var getLineStyle = makeStyleMapper([['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor']]);
  9575. var lineStyleMixin = {
  9576. getLineStyle: function (excludes) {
  9577. var style = getLineStyle(this, excludes);
  9578. var lineDash = this.getLineDash(style.lineWidth);
  9579. lineDash && (style.lineDash = lineDash);
  9580. return style;
  9581. },
  9582. getLineDash: function (lineWidth) {
  9583. if (lineWidth == null) {
  9584. lineWidth = 1;
  9585. }
  9586. var lineType = this.get('type');
  9587. var dotSize = Math.max(lineWidth, 2);
  9588. var dashSize = lineWidth * 4;
  9589. return lineType === 'solid' || lineType == null ? null : lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize];
  9590. }
  9591. };
  9592. var getAreaStyle = makeStyleMapper([['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor']]);
  9593. var areaStyleMixin = {
  9594. getAreaStyle: function (excludes, includes) {
  9595. return getAreaStyle(this, excludes, includes);
  9596. }
  9597. };
  9598. /**
  9599. * 曲线辅助模块
  9600. * @module zrender/core/curve
  9601. * @author pissang(https://www.github.com/pissang)
  9602. */
  9603. var mathPow = Math.pow;
  9604. var mathSqrt$2 = Math.sqrt;
  9605. var EPSILON$1 = 1e-8;
  9606. var EPSILON_NUMERIC = 1e-4;
  9607. var THREE_SQRT = mathSqrt$2(3);
  9608. var ONE_THIRD = 1 / 3; // 临时变量
  9609. var _v0 = create();
  9610. var _v1 = create();
  9611. var _v2 = create();
  9612. function isAroundZero(val) {
  9613. return val > -EPSILON$1 && val < EPSILON$1;
  9614. }
  9615. function isNotAroundZero$1(val) {
  9616. return val > EPSILON$1 || val < -EPSILON$1;
  9617. }
  9618. /**
  9619. * 计算三次贝塞尔值
  9620. * @memberOf module:zrender/core/curve
  9621. * @param {number} p0
  9622. * @param {number} p1
  9623. * @param {number} p2
  9624. * @param {number} p3
  9625. * @param {number} t
  9626. * @return {number}
  9627. */
  9628. function cubicAt(p0, p1, p2, p3, t) {
  9629. var onet = 1 - t;
  9630. return onet * onet * (onet * p0 + 3 * t * p1) + t * t * (t * p3 + 3 * onet * p2);
  9631. }
  9632. /**
  9633. * 计算三次贝塞尔导数值
  9634. * @memberOf module:zrender/core/curve
  9635. * @param {number} p0
  9636. * @param {number} p1
  9637. * @param {number} p2
  9638. * @param {number} p3
  9639. * @param {number} t
  9640. * @return {number}
  9641. */
  9642. function cubicDerivativeAt(p0, p1, p2, p3, t) {
  9643. var onet = 1 - t;
  9644. return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet + (p3 - p2) * t * t);
  9645. }
  9646. /**
  9647. * 计算三次贝塞尔方程根,使用盛金公式
  9648. * @memberOf module:zrender/core/curve
  9649. * @param {number} p0
  9650. * @param {number} p1
  9651. * @param {number} p2
  9652. * @param {number} p3
  9653. * @param {number} val
  9654. * @param {Array.<number>} roots
  9655. * @return {number} 有效根数目
  9656. */
  9657. function cubicRootAt(p0, p1, p2, p3, val, roots) {
  9658. // Evaluate roots of cubic functions
  9659. var a = p3 + 3 * (p1 - p2) - p0;
  9660. var b = 3 * (p2 - p1 * 2 + p0);
  9661. var c = 3 * (p1 - p0);
  9662. var d = p0 - val;
  9663. var A = b * b - 3 * a * c;
  9664. var B = b * c - 9 * a * d;
  9665. var C = c * c - 3 * b * d;
  9666. var n = 0;
  9667. if (isAroundZero(A) && isAroundZero(B)) {
  9668. if (isAroundZero(b)) {
  9669. roots[0] = 0;
  9670. } else {
  9671. var t1 = -c / b; //t1, t2, t3, b is not zero
  9672. if (t1 >= 0 && t1 <= 1) {
  9673. roots[n++] = t1;
  9674. }
  9675. }
  9676. } else {
  9677. var disc = B * B - 4 * A * C;
  9678. if (isAroundZero(disc)) {
  9679. var K = B / A;
  9680. var t1 = -b / a + K; // t1, a is not zero
  9681. var t2 = -K / 2; // t2, t3
  9682. if (t1 >= 0 && t1 <= 1) {
  9683. roots[n++] = t1;
  9684. }
  9685. if (t2 >= 0 && t2 <= 1) {
  9686. roots[n++] = t2;
  9687. }
  9688. } else if (disc > 0) {
  9689. var discSqrt = mathSqrt$2(disc);
  9690. var Y1 = A * b + 1.5 * a * (-B + discSqrt);
  9691. var Y2 = A * b + 1.5 * a * (-B - discSqrt);
  9692. if (Y1 < 0) {
  9693. Y1 = -mathPow(-Y1, ONE_THIRD);
  9694. } else {
  9695. Y1 = mathPow(Y1, ONE_THIRD);
  9696. }
  9697. if (Y2 < 0) {
  9698. Y2 = -mathPow(-Y2, ONE_THIRD);
  9699. } else {
  9700. Y2 = mathPow(Y2, ONE_THIRD);
  9701. }
  9702. var t1 = (-b - (Y1 + Y2)) / (3 * a);
  9703. if (t1 >= 0 && t1 <= 1) {
  9704. roots[n++] = t1;
  9705. }
  9706. } else {
  9707. var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt$2(A * A * A));
  9708. var theta = Math.acos(T) / 3;
  9709. var ASqrt = mathSqrt$2(A);
  9710. var tmp = Math.cos(theta);
  9711. var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);
  9712. var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);
  9713. var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
  9714. if (t1 >= 0 && t1 <= 1) {
  9715. roots[n++] = t1;
  9716. }
  9717. if (t2 >= 0 && t2 <= 1) {
  9718. roots[n++] = t2;
  9719. }
  9720. if (t3 >= 0 && t3 <= 1) {
  9721. roots[n++] = t3;
  9722. }
  9723. }
  9724. }
  9725. return n;
  9726. }
  9727. /**
  9728. * 计算三次贝塞尔方程极限值的位置
  9729. * @memberOf module:zrender/core/curve
  9730. * @param {number} p0
  9731. * @param {number} p1
  9732. * @param {number} p2
  9733. * @param {number} p3
  9734. * @param {Array.<number>} extrema
  9735. * @return {number} 有效数目
  9736. */
  9737. function cubicExtrema(p0, p1, p2, p3, extrema) {
  9738. var b = 6 * p2 - 12 * p1 + 6 * p0;
  9739. var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;
  9740. var c = 3 * p1 - 3 * p0;
  9741. var n = 0;
  9742. if (isAroundZero(a)) {
  9743. if (isNotAroundZero$1(b)) {
  9744. var t1 = -c / b;
  9745. if (t1 >= 0 && t1 <= 1) {
  9746. extrema[n++] = t1;
  9747. }
  9748. }
  9749. } else {
  9750. var disc = b * b - 4 * a * c;
  9751. if (isAroundZero(disc)) {
  9752. extrema[0] = -b / (2 * a);
  9753. } else if (disc > 0) {
  9754. var discSqrt = mathSqrt$2(disc);
  9755. var t1 = (-b + discSqrt) / (2 * a);
  9756. var t2 = (-b - discSqrt) / (2 * a);
  9757. if (t1 >= 0 && t1 <= 1) {
  9758. extrema[n++] = t1;
  9759. }
  9760. if (t2 >= 0 && t2 <= 1) {
  9761. extrema[n++] = t2;
  9762. }
  9763. }
  9764. }
  9765. return n;
  9766. }
  9767. /**
  9768. * 细分三次贝塞尔曲线
  9769. * @memberOf module:zrender/core/curve
  9770. * @param {number} p0
  9771. * @param {number} p1
  9772. * @param {number} p2
  9773. * @param {number} p3
  9774. * @param {number} t
  9775. * @param {Array.<number>} out
  9776. */
  9777. function cubicSubdivide(p0, p1, p2, p3, t, out) {
  9778. var p01 = (p1 - p0) * t + p0;
  9779. var p12 = (p2 - p1) * t + p1;
  9780. var p23 = (p3 - p2) * t + p2;
  9781. var p012 = (p12 - p01) * t + p01;
  9782. var p123 = (p23 - p12) * t + p12;
  9783. var p0123 = (p123 - p012) * t + p012; // Seg0
  9784. out[0] = p0;
  9785. out[1] = p01;
  9786. out[2] = p012;
  9787. out[3] = p0123; // Seg1
  9788. out[4] = p0123;
  9789. out[5] = p123;
  9790. out[6] = p23;
  9791. out[7] = p3;
  9792. }
  9793. /**
  9794. * 投射点到三次贝塞尔曲线上,返回投射距离。
  9795. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  9796. * @param {number} x0
  9797. * @param {number} y0
  9798. * @param {number} x1
  9799. * @param {number} y1
  9800. * @param {number} x2
  9801. * @param {number} y2
  9802. * @param {number} x3
  9803. * @param {number} y3
  9804. * @param {number} x
  9805. * @param {number} y
  9806. * @param {Array.<number>} [out] 投射点
  9807. * @return {number}
  9808. */
  9809. function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) {
  9810. // http://pomax.github.io/bezierinfo/#projections
  9811. var t;
  9812. var interval = 0.005;
  9813. var d = Infinity;
  9814. var prev;
  9815. var next;
  9816. var d1;
  9817. var d2;
  9818. _v0[0] = x;
  9819. _v0[1] = y; // 先粗略估计一下可能的最小距离的 t 值
  9820. // PENDING
  9821. for (var _t = 0; _t < 1; _t += 0.05) {
  9822. _v1[0] = cubicAt(x0, x1, x2, x3, _t);
  9823. _v1[1] = cubicAt(y0, y1, y2, y3, _t);
  9824. d1 = distSquare(_v0, _v1);
  9825. if (d1 < d) {
  9826. t = _t;
  9827. d = d1;
  9828. }
  9829. }
  9830. d = Infinity; // At most 32 iteration
  9831. for (var i = 0; i < 32; i++) {
  9832. if (interval < EPSILON_NUMERIC) {
  9833. break;
  9834. }
  9835. prev = t - interval;
  9836. next = t + interval; // t - interval
  9837. _v1[0] = cubicAt(x0, x1, x2, x3, prev);
  9838. _v1[1] = cubicAt(y0, y1, y2, y3, prev);
  9839. d1 = distSquare(_v1, _v0);
  9840. if (prev >= 0 && d1 < d) {
  9841. t = prev;
  9842. d = d1;
  9843. } else {
  9844. // t + interval
  9845. _v2[0] = cubicAt(x0, x1, x2, x3, next);
  9846. _v2[1] = cubicAt(y0, y1, y2, y3, next);
  9847. d2 = distSquare(_v2, _v0);
  9848. if (next <= 1 && d2 < d) {
  9849. t = next;
  9850. d = d2;
  9851. } else {
  9852. interval *= 0.5;
  9853. }
  9854. }
  9855. } // t
  9856. if (out) {
  9857. out[0] = cubicAt(x0, x1, x2, x3, t);
  9858. out[1] = cubicAt(y0, y1, y2, y3, t);
  9859. } // console.log(interval, i);
  9860. return mathSqrt$2(d);
  9861. }
  9862. /**
  9863. * 计算二次方贝塞尔值
  9864. * @param {number} p0
  9865. * @param {number} p1
  9866. * @param {number} p2
  9867. * @param {number} t
  9868. * @return {number}
  9869. */
  9870. function quadraticAt(p0, p1, p2, t) {
  9871. var onet = 1 - t;
  9872. return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
  9873. }
  9874. /**
  9875. * 计算二次方贝塞尔导数值
  9876. * @param {number} p0
  9877. * @param {number} p1
  9878. * @param {number} p2
  9879. * @param {number} t
  9880. * @return {number}
  9881. */
  9882. function quadraticDerivativeAt(p0, p1, p2, t) {
  9883. return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));
  9884. }
  9885. /**
  9886. * 计算二次方贝塞尔方程根
  9887. * @param {number} p0
  9888. * @param {number} p1
  9889. * @param {number} p2
  9890. * @param {number} t
  9891. * @param {Array.<number>} roots
  9892. * @return {number} 有效根数目
  9893. */
  9894. function quadraticRootAt(p0, p1, p2, val, roots) {
  9895. var a = p0 - 2 * p1 + p2;
  9896. var b = 2 * (p1 - p0);
  9897. var c = p0 - val;
  9898. var n = 0;
  9899. if (isAroundZero(a)) {
  9900. if (isNotAroundZero$1(b)) {
  9901. var t1 = -c / b;
  9902. if (t1 >= 0 && t1 <= 1) {
  9903. roots[n++] = t1;
  9904. }
  9905. }
  9906. } else {
  9907. var disc = b * b - 4 * a * c;
  9908. if (isAroundZero(disc)) {
  9909. var t1 = -b / (2 * a);
  9910. if (t1 >= 0 && t1 <= 1) {
  9911. roots[n++] = t1;
  9912. }
  9913. } else if (disc > 0) {
  9914. var discSqrt = mathSqrt$2(disc);
  9915. var t1 = (-b + discSqrt) / (2 * a);
  9916. var t2 = (-b - discSqrt) / (2 * a);
  9917. if (t1 >= 0 && t1 <= 1) {
  9918. roots[n++] = t1;
  9919. }
  9920. if (t2 >= 0 && t2 <= 1) {
  9921. roots[n++] = t2;
  9922. }
  9923. }
  9924. }
  9925. return n;
  9926. }
  9927. /**
  9928. * 计算二次贝塞尔方程极限值
  9929. * @memberOf module:zrender/core/curve
  9930. * @param {number} p0
  9931. * @param {number} p1
  9932. * @param {number} p2
  9933. * @return {number}
  9934. */
  9935. function quadraticExtremum(p0, p1, p2) {
  9936. var divider = p0 + p2 - 2 * p1;
  9937. if (divider === 0) {
  9938. // p1 is center of p0 and p2
  9939. return 0.5;
  9940. } else {
  9941. return (p0 - p1) / divider;
  9942. }
  9943. }
  9944. /**
  9945. * 细分二次贝塞尔曲线
  9946. * @memberOf module:zrender/core/curve
  9947. * @param {number} p0
  9948. * @param {number} p1
  9949. * @param {number} p2
  9950. * @param {number} t
  9951. * @param {Array.<number>} out
  9952. */
  9953. function quadraticSubdivide(p0, p1, p2, t, out) {
  9954. var p01 = (p1 - p0) * t + p0;
  9955. var p12 = (p2 - p1) * t + p1;
  9956. var p012 = (p12 - p01) * t + p01; // Seg0
  9957. out[0] = p0;
  9958. out[1] = p01;
  9959. out[2] = p012; // Seg1
  9960. out[3] = p012;
  9961. out[4] = p12;
  9962. out[5] = p2;
  9963. }
  9964. /**
  9965. * 投射点到二次贝塞尔曲线上,返回投射距离。
  9966. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  9967. * @param {number} x0
  9968. * @param {number} y0
  9969. * @param {number} x1
  9970. * @param {number} y1
  9971. * @param {number} x2
  9972. * @param {number} y2
  9973. * @param {number} x
  9974. * @param {number} y
  9975. * @param {Array.<number>} out 投射点
  9976. * @return {number}
  9977. */
  9978. function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) {
  9979. // http://pomax.github.io/bezierinfo/#projections
  9980. var t;
  9981. var interval = 0.005;
  9982. var d = Infinity;
  9983. _v0[0] = x;
  9984. _v0[1] = y; // 先粗略估计一下可能的最小距离的 t 值
  9985. // PENDING
  9986. for (var _t = 0; _t < 1; _t += 0.05) {
  9987. _v1[0] = quadraticAt(x0, x1, x2, _t);
  9988. _v1[1] = quadraticAt(y0, y1, y2, _t);
  9989. var d1 = distSquare(_v0, _v1);
  9990. if (d1 < d) {
  9991. t = _t;
  9992. d = d1;
  9993. }
  9994. }
  9995. d = Infinity; // At most 32 iteration
  9996. for (var i = 0; i < 32; i++) {
  9997. if (interval < EPSILON_NUMERIC) {
  9998. break;
  9999. }
  10000. var prev = t - interval;
  10001. var next = t + interval; // t - interval
  10002. _v1[0] = quadraticAt(x0, x1, x2, prev);
  10003. _v1[1] = quadraticAt(y0, y1, y2, prev);
  10004. var d1 = distSquare(_v1, _v0);
  10005. if (prev >= 0 && d1 < d) {
  10006. t = prev;
  10007. d = d1;
  10008. } else {
  10009. // t + interval
  10010. _v2[0] = quadraticAt(x0, x1, x2, next);
  10011. _v2[1] = quadraticAt(y0, y1, y2, next);
  10012. var d2 = distSquare(_v2, _v0);
  10013. if (next <= 1 && d2 < d) {
  10014. t = next;
  10015. d = d2;
  10016. } else {
  10017. interval *= 0.5;
  10018. }
  10019. }
  10020. } // t
  10021. if (out) {
  10022. out[0] = quadraticAt(x0, x1, x2, t);
  10023. out[1] = quadraticAt(y0, y1, y2, t);
  10024. } // console.log(interval, i);
  10025. return mathSqrt$2(d);
  10026. }
  10027. /**
  10028. * @author Yi Shen(https://github.com/pissang)
  10029. */
  10030. var mathMin$3 = Math.min;
  10031. var mathMax$3 = Math.max;
  10032. var mathSin$2 = Math.sin;
  10033. var mathCos$2 = Math.cos;
  10034. var PI2 = Math.PI * 2;
  10035. var start = create();
  10036. var end = create();
  10037. var extremity = create();
  10038. /**
  10039. * 从顶点数组中计算出最小包围盒,写入`min`和`max`中
  10040. * @module zrender/core/bbox
  10041. * @param {Array<Object>} points 顶点数组
  10042. * @param {number} min
  10043. * @param {number} max
  10044. */
  10045. /**
  10046. * @memberOf module:zrender/core/bbox
  10047. * @param {number} x0
  10048. * @param {number} y0
  10049. * @param {number} x1
  10050. * @param {number} y1
  10051. * @param {Array.<number>} min
  10052. * @param {Array.<number>} max
  10053. */
  10054. function fromLine(x0, y0, x1, y1, min$$1, max$$1) {
  10055. min$$1[0] = mathMin$3(x0, x1);
  10056. min$$1[1] = mathMin$3(y0, y1);
  10057. max$$1[0] = mathMax$3(x0, x1);
  10058. max$$1[1] = mathMax$3(y0, y1);
  10059. }
  10060. var xDim = [];
  10061. var yDim = [];
  10062. /**
  10063. * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中
  10064. * @memberOf module:zrender/core/bbox
  10065. * @param {number} x0
  10066. * @param {number} y0
  10067. * @param {number} x1
  10068. * @param {number} y1
  10069. * @param {number} x2
  10070. * @param {number} y2
  10071. * @param {number} x3
  10072. * @param {number} y3
  10073. * @param {Array.<number>} min
  10074. * @param {Array.<number>} max
  10075. */
  10076. function fromCubic(x0, y0, x1, y1, x2, y2, x3, y3, min$$1, max$$1) {
  10077. var cubicExtrema$$1 = cubicExtrema;
  10078. var cubicAt$$1 = cubicAt;
  10079. var i;
  10080. var n = cubicExtrema$$1(x0, x1, x2, x3, xDim);
  10081. min$$1[0] = Infinity;
  10082. min$$1[1] = Infinity;
  10083. max$$1[0] = -Infinity;
  10084. max$$1[1] = -Infinity;
  10085. for (i = 0; i < n; i++) {
  10086. var x = cubicAt$$1(x0, x1, x2, x3, xDim[i]);
  10087. min$$1[0] = mathMin$3(x, min$$1[0]);
  10088. max$$1[0] = mathMax$3(x, max$$1[0]);
  10089. }
  10090. n = cubicExtrema$$1(y0, y1, y2, y3, yDim);
  10091. for (i = 0; i < n; i++) {
  10092. var y = cubicAt$$1(y0, y1, y2, y3, yDim[i]);
  10093. min$$1[1] = mathMin$3(y, min$$1[1]);
  10094. max$$1[1] = mathMax$3(y, max$$1[1]);
  10095. }
  10096. min$$1[0] = mathMin$3(x0, min$$1[0]);
  10097. max$$1[0] = mathMax$3(x0, max$$1[0]);
  10098. min$$1[0] = mathMin$3(x3, min$$1[0]);
  10099. max$$1[0] = mathMax$3(x3, max$$1[0]);
  10100. min$$1[1] = mathMin$3(y0, min$$1[1]);
  10101. max$$1[1] = mathMax$3(y0, max$$1[1]);
  10102. min$$1[1] = mathMin$3(y3, min$$1[1]);
  10103. max$$1[1] = mathMax$3(y3, max$$1[1]);
  10104. }
  10105. /**
  10106. * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中
  10107. * @memberOf module:zrender/core/bbox
  10108. * @param {number} x0
  10109. * @param {number} y0
  10110. * @param {number} x1
  10111. * @param {number} y1
  10112. * @param {number} x2
  10113. * @param {number} y2
  10114. * @param {Array.<number>} min
  10115. * @param {Array.<number>} max
  10116. */
  10117. function fromQuadratic(x0, y0, x1, y1, x2, y2, min$$1, max$$1) {
  10118. var quadraticExtremum$$1 = quadraticExtremum;
  10119. var quadraticAt$$1 = quadraticAt; // Find extremities, where derivative in x dim or y dim is zero
  10120. var tx = mathMax$3(mathMin$3(quadraticExtremum$$1(x0, x1, x2), 1), 0);
  10121. var ty = mathMax$3(mathMin$3(quadraticExtremum$$1(y0, y1, y2), 1), 0);
  10122. var x = quadraticAt$$1(x0, x1, x2, tx);
  10123. var y = quadraticAt$$1(y0, y1, y2, ty);
  10124. min$$1[0] = mathMin$3(x0, x2, x);
  10125. min$$1[1] = mathMin$3(y0, y2, y);
  10126. max$$1[0] = mathMax$3(x0, x2, x);
  10127. max$$1[1] = mathMax$3(y0, y2, y);
  10128. }
  10129. /**
  10130. * 从圆弧中计算出最小包围盒,写入`min`和`max`中
  10131. * @method
  10132. * @memberOf module:zrender/core/bbox
  10133. * @param {number} x
  10134. * @param {number} y
  10135. * @param {number} rx
  10136. * @param {number} ry
  10137. * @param {number} startAngle
  10138. * @param {number} endAngle
  10139. * @param {number} anticlockwise
  10140. * @param {Array.<number>} min
  10141. * @param {Array.<number>} max
  10142. */
  10143. function fromArc(x, y, rx, ry, startAngle, endAngle, anticlockwise, min$$1, max$$1) {
  10144. var vec2Min = min;
  10145. var vec2Max = max;
  10146. var diff = Math.abs(startAngle - endAngle);
  10147. if (diff % PI2 < 1e-4 && diff > 1e-4) {
  10148. // Is a circle
  10149. min$$1[0] = x - rx;
  10150. min$$1[1] = y - ry;
  10151. max$$1[0] = x + rx;
  10152. max$$1[1] = y + ry;
  10153. return;
  10154. }
  10155. start[0] = mathCos$2(startAngle) * rx + x;
  10156. start[1] = mathSin$2(startAngle) * ry + y;
  10157. end[0] = mathCos$2(endAngle) * rx + x;
  10158. end[1] = mathSin$2(endAngle) * ry + y;
  10159. vec2Min(min$$1, start, end);
  10160. vec2Max(max$$1, start, end); // Thresh to [0, Math.PI * 2]
  10161. startAngle = startAngle % PI2;
  10162. if (startAngle < 0) {
  10163. startAngle = startAngle + PI2;
  10164. }
  10165. endAngle = endAngle % PI2;
  10166. if (endAngle < 0) {
  10167. endAngle = endAngle + PI2;
  10168. }
  10169. if (startAngle > endAngle && !anticlockwise) {
  10170. endAngle += PI2;
  10171. } else if (startAngle < endAngle && anticlockwise) {
  10172. startAngle += PI2;
  10173. }
  10174. if (anticlockwise) {
  10175. var tmp = endAngle;
  10176. endAngle = startAngle;
  10177. startAngle = tmp;
  10178. } // var number = 0;
  10179. // var step = (anticlockwise ? -Math.PI : Math.PI) / 2;
  10180. for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
  10181. if (angle > startAngle) {
  10182. extremity[0] = mathCos$2(angle) * rx + x;
  10183. extremity[1] = mathSin$2(angle) * ry + y;
  10184. vec2Min(min$$1, extremity, min$$1);
  10185. vec2Max(max$$1, extremity, max$$1);
  10186. }
  10187. }
  10188. }
  10189. /**
  10190. * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
  10191. * 可以用于 isInsidePath 判断以及获取boundingRect
  10192. *
  10193. * @module zrender/core/PathProxy
  10194. * @author Yi Shen (http://www.github.com/pissang)
  10195. */
  10196. // TODO getTotalLength, getPointAtLength
  10197. var CMD = {
  10198. M: 1,
  10199. L: 2,
  10200. C: 3,
  10201. Q: 4,
  10202. A: 5,
  10203. Z: 6,
  10204. // Rect
  10205. R: 7
  10206. }; // var CMD_MEM_SIZE = {
  10207. // M: 3,
  10208. // L: 3,
  10209. // C: 7,
  10210. // Q: 5,
  10211. // A: 9,
  10212. // R: 5,
  10213. // Z: 1
  10214. // };
  10215. var min$1 = [];
  10216. var max$1 = [];
  10217. var min2 = [];
  10218. var max2 = [];
  10219. var mathMin$2 = Math.min;
  10220. var mathMax$2 = Math.max;
  10221. var mathCos$1 = Math.cos;
  10222. var mathSin$1 = Math.sin;
  10223. var mathSqrt$1 = Math.sqrt;
  10224. var mathAbs = Math.abs;
  10225. var hasTypedArray = typeof Float32Array != 'undefined';
  10226. /**
  10227. * @alias module:zrender/core/PathProxy
  10228. * @constructor
  10229. */
  10230. var PathProxy = function (notSaveData) {
  10231. this._saveData = !(notSaveData || false);
  10232. if (this._saveData) {
  10233. /**
  10234. * Path data. Stored as flat array
  10235. * @type {Array.<Object>}
  10236. */
  10237. this.data = [];
  10238. }
  10239. this._ctx = null;
  10240. };
  10241. /**
  10242. * 快速计算Path包围盒(并不是最小包围盒)
  10243. * @return {Object}
  10244. */
  10245. PathProxy.prototype = {
  10246. constructor: PathProxy,
  10247. _xi: 0,
  10248. _yi: 0,
  10249. _x0: 0,
  10250. _y0: 0,
  10251. // Unit x, Unit y. Provide for avoiding drawing that too short line segment
  10252. _ux: 0,
  10253. _uy: 0,
  10254. _len: 0,
  10255. _lineDash: null,
  10256. _dashOffset: 0,
  10257. _dashIdx: 0,
  10258. _dashSum: 0,
  10259. /**
  10260. * @readOnly
  10261. */
  10262. setScale: function (sx, sy) {
  10263. this._ux = mathAbs(1 / devicePixelRatio / sx) || 0;
  10264. this._uy = mathAbs(1 / devicePixelRatio / sy) || 0;
  10265. },
  10266. getContext: function () {
  10267. return this._ctx;
  10268. },
  10269. /**
  10270. * @param {CanvasRenderingContext2D} ctx
  10271. * @return {module:zrender/core/PathProxy}
  10272. */
  10273. beginPath: function (ctx) {
  10274. this._ctx = ctx;
  10275. ctx && ctx.beginPath();
  10276. ctx && (this.dpr = ctx.dpr); // Reset
  10277. if (this._saveData) {
  10278. this._len = 0;
  10279. }
  10280. if (this._lineDash) {
  10281. this._lineDash = null;
  10282. this._dashOffset = 0;
  10283. }
  10284. return this;
  10285. },
  10286. /**
  10287. * @param {number} x
  10288. * @param {number} y
  10289. * @return {module:zrender/core/PathProxy}
  10290. */
  10291. moveTo: function (x, y) {
  10292. this.addData(CMD.M, x, y);
  10293. this._ctx && this._ctx.moveTo(x, y); // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
  10294. // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
  10295. // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要
  10296. // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持
  10297. this._x0 = x;
  10298. this._y0 = y;
  10299. this._xi = x;
  10300. this._yi = y;
  10301. return this;
  10302. },
  10303. /**
  10304. * @param {number} x
  10305. * @param {number} y
  10306. * @return {module:zrender/core/PathProxy}
  10307. */
  10308. lineTo: function (x, y) {
  10309. var exceedUnit = mathAbs(x - this._xi) > this._ux || mathAbs(y - this._yi) > this._uy // Force draw the first segment
  10310. || this._len < 5;
  10311. this.addData(CMD.L, x, y);
  10312. if (this._ctx && exceedUnit) {
  10313. this._needsDash() ? this._dashedLineTo(x, y) : this._ctx.lineTo(x, y);
  10314. }
  10315. if (exceedUnit) {
  10316. this._xi = x;
  10317. this._yi = y;
  10318. }
  10319. return this;
  10320. },
  10321. /**
  10322. * @param {number} x1
  10323. * @param {number} y1
  10324. * @param {number} x2
  10325. * @param {number} y2
  10326. * @param {number} x3
  10327. * @param {number} y3
  10328. * @return {module:zrender/core/PathProxy}
  10329. */
  10330. bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
  10331. this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
  10332. if (this._ctx) {
  10333. this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  10334. }
  10335. this._xi = x3;
  10336. this._yi = y3;
  10337. return this;
  10338. },
  10339. /**
  10340. * @param {number} x1
  10341. * @param {number} y1
  10342. * @param {number} x2
  10343. * @param {number} y2
  10344. * @return {module:zrender/core/PathProxy}
  10345. */
  10346. quadraticCurveTo: function (x1, y1, x2, y2) {
  10347. this.addData(CMD.Q, x1, y1, x2, y2);
  10348. if (this._ctx) {
  10349. this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2) : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
  10350. }
  10351. this._xi = x2;
  10352. this._yi = y2;
  10353. return this;
  10354. },
  10355. /**
  10356. * @param {number} cx
  10357. * @param {number} cy
  10358. * @param {number} r
  10359. * @param {number} startAngle
  10360. * @param {number} endAngle
  10361. * @param {boolean} anticlockwise
  10362. * @return {module:zrender/core/PathProxy}
  10363. */
  10364. arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
  10365. this.addData(CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1);
  10366. this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
  10367. this._xi = mathCos$1(endAngle) * r + cx;
  10368. this._yi = mathSin$1(endAngle) * r + cx;
  10369. return this;
  10370. },
  10371. // TODO
  10372. arcTo: function (x1, y1, x2, y2, radius) {
  10373. if (this._ctx) {
  10374. this._ctx.arcTo(x1, y1, x2, y2, radius);
  10375. }
  10376. return this;
  10377. },
  10378. // TODO
  10379. rect: function (x, y, w, h) {
  10380. this._ctx && this._ctx.rect(x, y, w, h);
  10381. this.addData(CMD.R, x, y, w, h);
  10382. return this;
  10383. },
  10384. /**
  10385. * @return {module:zrender/core/PathProxy}
  10386. */
  10387. closePath: function () {
  10388. this.addData(CMD.Z);
  10389. var ctx = this._ctx;
  10390. var x0 = this._x0;
  10391. var y0 = this._y0;
  10392. if (ctx) {
  10393. this._needsDash() && this._dashedLineTo(x0, y0);
  10394. ctx.closePath();
  10395. }
  10396. this._xi = x0;
  10397. this._yi = y0;
  10398. return this;
  10399. },
  10400. /**
  10401. * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。
  10402. * stroke 同样
  10403. * @param {CanvasRenderingContext2D} ctx
  10404. * @return {module:zrender/core/PathProxy}
  10405. */
  10406. fill: function (ctx) {
  10407. ctx && ctx.fill();
  10408. this.toStatic();
  10409. },
  10410. /**
  10411. * @param {CanvasRenderingContext2D} ctx
  10412. * @return {module:zrender/core/PathProxy}
  10413. */
  10414. stroke: function (ctx) {
  10415. ctx && ctx.stroke();
  10416. this.toStatic();
  10417. },
  10418. /**
  10419. * 必须在其它绘制命令前调用
  10420. * Must be invoked before all other path drawing methods
  10421. * @return {module:zrender/core/PathProxy}
  10422. */
  10423. setLineDash: function (lineDash) {
  10424. if (lineDash instanceof Array) {
  10425. this._lineDash = lineDash;
  10426. this._dashIdx = 0;
  10427. var lineDashSum = 0;
  10428. for (var i = 0; i < lineDash.length; i++) {
  10429. lineDashSum += lineDash[i];
  10430. }
  10431. this._dashSum = lineDashSum;
  10432. }
  10433. return this;
  10434. },
  10435. /**
  10436. * 必须在其它绘制命令前调用
  10437. * Must be invoked before all other path drawing methods
  10438. * @return {module:zrender/core/PathProxy}
  10439. */
  10440. setLineDashOffset: function (offset) {
  10441. this._dashOffset = offset;
  10442. return this;
  10443. },
  10444. /**
  10445. *
  10446. * @return {boolean}
  10447. */
  10448. len: function () {
  10449. return this._len;
  10450. },
  10451. /**
  10452. * 直接设置 Path 数据
  10453. */
  10454. setData: function (data) {
  10455. var len$$1 = data.length;
  10456. if (!(this.data && this.data.length == len$$1) && hasTypedArray) {
  10457. this.data = new Float32Array(len$$1);
  10458. }
  10459. for (var i = 0; i < len$$1; i++) {
  10460. this.data[i] = data[i];
  10461. }
  10462. this._len = len$$1;
  10463. },
  10464. /**
  10465. * 添加子路径
  10466. * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
  10467. */
  10468. appendPath: function (path) {
  10469. if (!(path instanceof Array)) {
  10470. path = [path];
  10471. }
  10472. var len$$1 = path.length;
  10473. var appendSize = 0;
  10474. var offset = this._len;
  10475. for (var i = 0; i < len$$1; i++) {
  10476. appendSize += path[i].len();
  10477. }
  10478. if (hasTypedArray && this.data instanceof Float32Array) {
  10479. this.data = new Float32Array(offset + appendSize);
  10480. }
  10481. for (var i = 0; i < len$$1; i++) {
  10482. var appendPathData = path[i].data;
  10483. for (var k = 0; k < appendPathData.length; k++) {
  10484. this.data[offset++] = appendPathData[k];
  10485. }
  10486. }
  10487. this._len = offset;
  10488. },
  10489. /**
  10490. * 填充 Path 数据。
  10491. * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
  10492. */
  10493. addData: function (cmd) {
  10494. if (!this._saveData) {
  10495. return;
  10496. }
  10497. var data = this.data;
  10498. if (this._len + arguments.length > data.length) {
  10499. // 因为之前的数组已经转换成静态的 Float32Array
  10500. // 所以不够用时需要扩展一个新的动态数组
  10501. this._expandData();
  10502. data = this.data;
  10503. }
  10504. for (var i = 0; i < arguments.length; i++) {
  10505. data[this._len++] = arguments[i];
  10506. }
  10507. this._prevCmd = cmd;
  10508. },
  10509. _expandData: function () {
  10510. // Only if data is Float32Array
  10511. if (!(this.data instanceof Array)) {
  10512. var newData = [];
  10513. for (var i = 0; i < this._len; i++) {
  10514. newData[i] = this.data[i];
  10515. }
  10516. this.data = newData;
  10517. }
  10518. },
  10519. /**
  10520. * If needs js implemented dashed line
  10521. * @return {boolean}
  10522. * @private
  10523. */
  10524. _needsDash: function () {
  10525. return this._lineDash;
  10526. },
  10527. _dashedLineTo: function (x1, y1) {
  10528. var dashSum = this._dashSum;
  10529. var offset = this._dashOffset;
  10530. var lineDash = this._lineDash;
  10531. var ctx = this._ctx;
  10532. var x0 = this._xi;
  10533. var y0 = this._yi;
  10534. var dx = x1 - x0;
  10535. var dy = y1 - y0;
  10536. var dist$$1 = mathSqrt$1(dx * dx + dy * dy);
  10537. var x = x0;
  10538. var y = y0;
  10539. var dash;
  10540. var nDash = lineDash.length;
  10541. var idx;
  10542. dx /= dist$$1;
  10543. dy /= dist$$1;
  10544. if (offset < 0) {
  10545. // Convert to positive offset
  10546. offset = dashSum + offset;
  10547. }
  10548. offset %= dashSum;
  10549. x -= offset * dx;
  10550. y -= offset * dy;
  10551. while (dx > 0 && x <= x1 || dx < 0 && x >= x1 || dx == 0 && (dy > 0 && y <= y1 || dy < 0 && y >= y1)) {
  10552. idx = this._dashIdx;
  10553. dash = lineDash[idx];
  10554. x += dx * dash;
  10555. y += dy * dash;
  10556. this._dashIdx = (idx + 1) % nDash; // Skip positive offset
  10557. if (dx > 0 && x < x0 || dx < 0 && x > x0 || dy > 0 && y < y0 || dy < 0 && y > y0) {
  10558. continue;
  10559. }
  10560. ctx[idx % 2 ? 'moveTo' : 'lineTo'](dx >= 0 ? mathMin$2(x, x1) : mathMax$2(x, x1), dy >= 0 ? mathMin$2(y, y1) : mathMax$2(y, y1));
  10561. } // Offset for next lineTo
  10562. dx = x - x1;
  10563. dy = y - y1;
  10564. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  10565. },
  10566. // Not accurate dashed line to
  10567. _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
  10568. var dashSum = this._dashSum;
  10569. var offset = this._dashOffset;
  10570. var lineDash = this._lineDash;
  10571. var ctx = this._ctx;
  10572. var x0 = this._xi;
  10573. var y0 = this._yi;
  10574. var t;
  10575. var dx;
  10576. var dy;
  10577. var cubicAt$$1 = cubicAt;
  10578. var bezierLen = 0;
  10579. var idx = this._dashIdx;
  10580. var nDash = lineDash.length;
  10581. var x;
  10582. var y;
  10583. var tmpLen = 0;
  10584. if (offset < 0) {
  10585. // Convert to positive offset
  10586. offset = dashSum + offset;
  10587. }
  10588. offset %= dashSum; // Bezier approx length
  10589. for (t = 0; t < 1; t += 0.1) {
  10590. dx = cubicAt$$1(x0, x1, x2, x3, t + 0.1) - cubicAt$$1(x0, x1, x2, x3, t);
  10591. dy = cubicAt$$1(y0, y1, y2, y3, t + 0.1) - cubicAt$$1(y0, y1, y2, y3, t);
  10592. bezierLen += mathSqrt$1(dx * dx + dy * dy);
  10593. } // Find idx after add offset
  10594. for (; idx < nDash; idx++) {
  10595. tmpLen += lineDash[idx];
  10596. if (tmpLen > offset) {
  10597. break;
  10598. }
  10599. }
  10600. t = (tmpLen - offset) / bezierLen;
  10601. while (t <= 1) {
  10602. x = cubicAt$$1(x0, x1, x2, x3, t);
  10603. y = cubicAt$$1(y0, y1, y2, y3, t); // Use line to approximate dashed bezier
  10604. // Bad result if dash is long
  10605. idx % 2 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
  10606. t += lineDash[idx] / bezierLen;
  10607. idx = (idx + 1) % nDash;
  10608. } // Finish the last segment and calculate the new offset
  10609. idx % 2 !== 0 && ctx.lineTo(x3, y3);
  10610. dx = x3 - x;
  10611. dy = y3 - y;
  10612. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  10613. },
  10614. _dashedQuadraticTo: function (x1, y1, x2, y2) {
  10615. // Convert quadratic to cubic using degree elevation
  10616. var x3 = x2;
  10617. var y3 = y2;
  10618. x2 = (x2 + 2 * x1) / 3;
  10619. y2 = (y2 + 2 * y1) / 3;
  10620. x1 = (this._xi + 2 * x1) / 3;
  10621. y1 = (this._yi + 2 * y1) / 3;
  10622. this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
  10623. },
  10624. /**
  10625. * 转成静态的 Float32Array 减少堆内存占用
  10626. * Convert dynamic array to static Float32Array
  10627. */
  10628. toStatic: function () {
  10629. var data = this.data;
  10630. if (data instanceof Array) {
  10631. data.length = this._len;
  10632. if (hasTypedArray) {
  10633. this.data = new Float32Array(data);
  10634. }
  10635. }
  10636. },
  10637. /**
  10638. * @return {module:zrender/core/BoundingRect}
  10639. */
  10640. getBoundingRect: function () {
  10641. min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;
  10642. max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
  10643. var data = this.data;
  10644. var xi = 0;
  10645. var yi = 0;
  10646. var x0 = 0;
  10647. var y0 = 0;
  10648. for (var i = 0; i < data.length;) {
  10649. var cmd = data[i++];
  10650. if (i == 1) {
  10651. // 如果第一个命令是 L, C, Q
  10652. // 则 previous point 同绘制命令的第一个 point
  10653. //
  10654. // 第一个命令为 Arc 的情况下会在后面特殊处理
  10655. xi = data[i];
  10656. yi = data[i + 1];
  10657. x0 = xi;
  10658. y0 = yi;
  10659. }
  10660. switch (cmd) {
  10661. case CMD.M:
  10662. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  10663. // 在 closePath 的时候使用
  10664. x0 = data[i++];
  10665. y0 = data[i++];
  10666. xi = x0;
  10667. yi = y0;
  10668. min2[0] = x0;
  10669. min2[1] = y0;
  10670. max2[0] = x0;
  10671. max2[1] = y0;
  10672. break;
  10673. case CMD.L:
  10674. fromLine(xi, yi, data[i], data[i + 1], min2, max2);
  10675. xi = data[i++];
  10676. yi = data[i++];
  10677. break;
  10678. case CMD.C:
  10679. fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);
  10680. xi = data[i++];
  10681. yi = data[i++];
  10682. break;
  10683. case CMD.Q:
  10684. fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);
  10685. xi = data[i++];
  10686. yi = data[i++];
  10687. break;
  10688. case CMD.A:
  10689. // TODO Arc 判断的开销比较大
  10690. var cx = data[i++];
  10691. var cy = data[i++];
  10692. var rx = data[i++];
  10693. var ry = data[i++];
  10694. var startAngle = data[i++];
  10695. var endAngle = data[i++] + startAngle; // TODO Arc 旋转
  10696. var psi = data[i++];
  10697. var anticlockwise = 1 - data[i++];
  10698. if (i == 1) {
  10699. // 直接使用 arc 命令
  10700. // 第一个命令起点还未定义
  10701. x0 = mathCos$1(startAngle) * rx + cx;
  10702. y0 = mathSin$1(startAngle) * ry + cy;
  10703. }
  10704. fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);
  10705. xi = mathCos$1(endAngle) * rx + cx;
  10706. yi = mathSin$1(endAngle) * ry + cy;
  10707. break;
  10708. case CMD.R:
  10709. x0 = xi = data[i++];
  10710. y0 = yi = data[i++];
  10711. var width = data[i++];
  10712. var height = data[i++]; // Use fromLine
  10713. fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
  10714. break;
  10715. case CMD.Z:
  10716. xi = x0;
  10717. yi = y0;
  10718. break;
  10719. } // Union
  10720. min(min$1, min$1, min2);
  10721. max(max$1, max$1, max2);
  10722. } // No data
  10723. if (i === 0) {
  10724. min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;
  10725. }
  10726. return new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]);
  10727. },
  10728. /**
  10729. * Rebuild path from current data
  10730. * Rebuild path will not consider javascript implemented line dash.
  10731. * @param {CanvasRenderingContext2D} ctx
  10732. */
  10733. rebuildPath: function (ctx) {
  10734. var d = this.data;
  10735. var x0, y0;
  10736. var xi, yi;
  10737. var x, y;
  10738. var ux = this._ux;
  10739. var uy = this._uy;
  10740. var len$$1 = this._len;
  10741. for (var i = 0; i < len$$1;) {
  10742. var cmd = d[i++];
  10743. if (i == 1) {
  10744. // 如果第一个命令是 L, C, Q
  10745. // 则 previous point 同绘制命令的第一个 point
  10746. //
  10747. // 第一个命令为 Arc 的情况下会在后面特殊处理
  10748. xi = d[i];
  10749. yi = d[i + 1];
  10750. x0 = xi;
  10751. y0 = yi;
  10752. }
  10753. switch (cmd) {
  10754. case CMD.M:
  10755. x0 = xi = d[i++];
  10756. y0 = yi = d[i++];
  10757. ctx.moveTo(xi, yi);
  10758. break;
  10759. case CMD.L:
  10760. x = d[i++];
  10761. y = d[i++]; // Not draw too small seg between
  10762. if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len$$1 - 1) {
  10763. ctx.lineTo(x, y);
  10764. xi = x;
  10765. yi = y;
  10766. }
  10767. break;
  10768. case CMD.C:
  10769. ctx.bezierCurveTo(d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]);
  10770. xi = d[i - 2];
  10771. yi = d[i - 1];
  10772. break;
  10773. case CMD.Q:
  10774. ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
  10775. xi = d[i - 2];
  10776. yi = d[i - 1];
  10777. break;
  10778. case CMD.A:
  10779. var cx = d[i++];
  10780. var cy = d[i++];
  10781. var rx = d[i++];
  10782. var ry = d[i++];
  10783. var theta = d[i++];
  10784. var dTheta = d[i++];
  10785. var psi = d[i++];
  10786. var fs = d[i++];
  10787. var r = rx > ry ? rx : ry;
  10788. var scaleX = rx > ry ? 1 : rx / ry;
  10789. var scaleY = rx > ry ? ry / rx : 1;
  10790. var isEllipse = Math.abs(rx - ry) > 1e-3;
  10791. var endAngle = theta + dTheta;
  10792. if (isEllipse) {
  10793. ctx.translate(cx, cy);
  10794. ctx.rotate(psi);
  10795. ctx.scale(scaleX, scaleY);
  10796. ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
  10797. ctx.scale(1 / scaleX, 1 / scaleY);
  10798. ctx.rotate(-psi);
  10799. ctx.translate(-cx, -cy);
  10800. } else {
  10801. ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
  10802. }
  10803. if (i == 1) {
  10804. // 直接使用 arc 命令
  10805. // 第一个命令起点还未定义
  10806. x0 = mathCos$1(theta) * rx + cx;
  10807. y0 = mathSin$1(theta) * ry + cy;
  10808. }
  10809. xi = mathCos$1(endAngle) * rx + cx;
  10810. yi = mathSin$1(endAngle) * ry + cy;
  10811. break;
  10812. case CMD.R:
  10813. x0 = xi = d[i];
  10814. y0 = yi = d[i + 1];
  10815. ctx.rect(d[i++], d[i++], d[i++], d[i++]);
  10816. break;
  10817. case CMD.Z:
  10818. ctx.closePath();
  10819. xi = x0;
  10820. yi = y0;
  10821. }
  10822. }
  10823. }
  10824. };
  10825. PathProxy.CMD = CMD;
  10826. /**
  10827. * 线段包含判断
  10828. * @param {number} x0
  10829. * @param {number} y0
  10830. * @param {number} x1
  10831. * @param {number} y1
  10832. * @param {number} lineWidth
  10833. * @param {number} x
  10834. * @param {number} y
  10835. * @return {boolean}
  10836. */
  10837. function containStroke$1(x0, y0, x1, y1, lineWidth, x, y) {
  10838. if (lineWidth === 0) {
  10839. return false;
  10840. }
  10841. var _l = lineWidth;
  10842. var _a = 0;
  10843. var _b = x0; // Quick reject
  10844. if (y > y0 + _l && y > y1 + _l || y < y0 - _l && y < y1 - _l || x > x0 + _l && x > x1 + _l || x < x0 - _l && x < x1 - _l) {
  10845. return false;
  10846. }
  10847. if (x0 !== x1) {
  10848. _a = (y0 - y1) / (x0 - x1);
  10849. _b = (x0 * y1 - x1 * y0) / (x0 - x1);
  10850. } else {
  10851. return Math.abs(x - x0) <= _l / 2;
  10852. }
  10853. var tmp = _a * x - y + _b;
  10854. var _s = tmp * tmp / (_a * _a + 1);
  10855. return _s <= _l / 2 * _l / 2;
  10856. }
  10857. /**
  10858. * 三次贝塞尔曲线描边包含判断
  10859. * @param {number} x0
  10860. * @param {number} y0
  10861. * @param {number} x1
  10862. * @param {number} y1
  10863. * @param {number} x2
  10864. * @param {number} y2
  10865. * @param {number} x3
  10866. * @param {number} y3
  10867. * @param {number} lineWidth
  10868. * @param {number} x
  10869. * @param {number} y
  10870. * @return {boolean}
  10871. */
  10872. function containStroke$2(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
  10873. if (lineWidth === 0) {
  10874. return false;
  10875. }
  10876. var _l = lineWidth; // Quick reject
  10877. if (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l || y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l || x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l || x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l) {
  10878. return false;
  10879. }
  10880. var d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null);
  10881. return d <= _l / 2;
  10882. }
  10883. /**
  10884. * 二次贝塞尔曲线描边包含判断
  10885. * @param {number} x0
  10886. * @param {number} y0
  10887. * @param {number} x1
  10888. * @param {number} y1
  10889. * @param {number} x2
  10890. * @param {number} y2
  10891. * @param {number} lineWidth
  10892. * @param {number} x
  10893. * @param {number} y
  10894. * @return {boolean}
  10895. */
  10896. function containStroke$3(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
  10897. if (lineWidth === 0) {
  10898. return false;
  10899. }
  10900. var _l = lineWidth; // Quick reject
  10901. if (y > y0 + _l && y > y1 + _l && y > y2 + _l || y < y0 - _l && y < y1 - _l && y < y2 - _l || x > x0 + _l && x > x1 + _l && x > x2 + _l || x < x0 - _l && x < x1 - _l && x < x2 - _l) {
  10902. return false;
  10903. }
  10904. var d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null);
  10905. return d <= _l / 2;
  10906. }
  10907. var PI2$3 = Math.PI * 2;
  10908. function normalizeRadian(angle) {
  10909. angle %= PI2$3;
  10910. if (angle < 0) {
  10911. angle += PI2$3;
  10912. }
  10913. return angle;
  10914. }
  10915. var PI2$2 = Math.PI * 2;
  10916. /**
  10917. * 圆弧描边包含判断
  10918. * @param {number} cx
  10919. * @param {number} cy
  10920. * @param {number} r
  10921. * @param {number} startAngle
  10922. * @param {number} endAngle
  10923. * @param {boolean} anticlockwise
  10924. * @param {number} lineWidth
  10925. * @param {number} x
  10926. * @param {number} y
  10927. * @return {Boolean}
  10928. */
  10929. function containStroke$4(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) {
  10930. if (lineWidth === 0) {
  10931. return false;
  10932. }
  10933. var _l = lineWidth;
  10934. x -= cx;
  10935. y -= cy;
  10936. var d = Math.sqrt(x * x + y * y);
  10937. if (d - _l > r || d + _l < r) {
  10938. return false;
  10939. }
  10940. if (Math.abs(startAngle - endAngle) % PI2$2 < 1e-4) {
  10941. // Is a circle
  10942. return true;
  10943. }
  10944. if (anticlockwise) {
  10945. var tmp = startAngle;
  10946. startAngle = normalizeRadian(endAngle);
  10947. endAngle = normalizeRadian(tmp);
  10948. } else {
  10949. startAngle = normalizeRadian(startAngle);
  10950. endAngle = normalizeRadian(endAngle);
  10951. }
  10952. if (startAngle > endAngle) {
  10953. endAngle += PI2$2;
  10954. }
  10955. var angle = Math.atan2(y, x);
  10956. if (angle < 0) {
  10957. angle += PI2$2;
  10958. }
  10959. return angle >= startAngle && angle <= endAngle || angle + PI2$2 >= startAngle && angle + PI2$2 <= endAngle;
  10960. }
  10961. function windingLine(x0, y0, x1, y1, x, y) {
  10962. if (y > y0 && y > y1 || y < y0 && y < y1) {
  10963. return 0;
  10964. } // Ignore horizontal line
  10965. if (y1 === y0) {
  10966. return 0;
  10967. }
  10968. var dir = y1 < y0 ? 1 : -1;
  10969. var t = (y - y0) / (y1 - y0); // Avoid winding error when intersection point is the connect point of two line of polygon
  10970. if (t === 1 || t === 0) {
  10971. dir = y1 < y0 ? 0.5 : -0.5;
  10972. }
  10973. var x_ = t * (x1 - x0) + x0;
  10974. return x_ > x ? dir : 0;
  10975. }
  10976. var CMD$1 = PathProxy.CMD;
  10977. var PI2$1 = Math.PI * 2;
  10978. var EPSILON$2 = 1e-4;
  10979. function isAroundEqual(a, b) {
  10980. return Math.abs(a - b) < EPSILON$2;
  10981. } // 临时数组
  10982. var roots = [-1, -1, -1];
  10983. var extrema = [-1, -1];
  10984. function swapExtrema() {
  10985. var tmp = extrema[0];
  10986. extrema[0] = extrema[1];
  10987. extrema[1] = tmp;
  10988. }
  10989. function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
  10990. // Quick reject
  10991. if (y > y0 && y > y1 && y > y2 && y > y3 || y < y0 && y < y1 && y < y2 && y < y3) {
  10992. return 0;
  10993. }
  10994. var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);
  10995. if (nRoots === 0) {
  10996. return 0;
  10997. } else {
  10998. var w = 0;
  10999. var nExtrema = -1;
  11000. var y0_, y1_;
  11001. for (var i = 0; i < nRoots; i++) {
  11002. var t = roots[i]; // Avoid winding error when intersection point is the connect point of two line of polygon
  11003. var unit = t === 0 || t === 1 ? 0.5 : 1;
  11004. var x_ = cubicAt(x0, x1, x2, x3, t);
  11005. if (x_ < x) {
  11006. // Quick reject
  11007. continue;
  11008. }
  11009. if (nExtrema < 0) {
  11010. nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);
  11011. if (extrema[1] < extrema[0] && nExtrema > 1) {
  11012. swapExtrema();
  11013. }
  11014. y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);
  11015. if (nExtrema > 1) {
  11016. y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);
  11017. }
  11018. }
  11019. if (nExtrema == 2) {
  11020. // 分成三段单调函数
  11021. if (t < extrema[0]) {
  11022. w += y0_ < y0 ? unit : -unit;
  11023. } else if (t < extrema[1]) {
  11024. w += y1_ < y0_ ? unit : -unit;
  11025. } else {
  11026. w += y3 < y1_ ? unit : -unit;
  11027. }
  11028. } else {
  11029. // 分成两段单调函数
  11030. if (t < extrema[0]) {
  11031. w += y0_ < y0 ? unit : -unit;
  11032. } else {
  11033. w += y3 < y0_ ? unit : -unit;
  11034. }
  11035. }
  11036. }
  11037. return w;
  11038. }
  11039. }
  11040. function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
  11041. // Quick reject
  11042. if (y > y0 && y > y1 && y > y2 || y < y0 && y < y1 && y < y2) {
  11043. return 0;
  11044. }
  11045. var nRoots = quadraticRootAt(y0, y1, y2, y, roots);
  11046. if (nRoots === 0) {
  11047. return 0;
  11048. } else {
  11049. var t = quadraticExtremum(y0, y1, y2);
  11050. if (t >= 0 && t <= 1) {
  11051. var w = 0;
  11052. var y_ = quadraticAt(y0, y1, y2, t);
  11053. for (var i = 0; i < nRoots; i++) {
  11054. // Remove one endpoint.
  11055. var unit = roots[i] === 0 || roots[i] === 1 ? 0.5 : 1;
  11056. var x_ = quadraticAt(x0, x1, x2, roots[i]);
  11057. if (x_ < x) {
  11058. // Quick reject
  11059. continue;
  11060. }
  11061. if (roots[i] < t) {
  11062. w += y_ < y0 ? unit : -unit;
  11063. } else {
  11064. w += y2 < y_ ? unit : -unit;
  11065. }
  11066. }
  11067. return w;
  11068. } else {
  11069. // Remove one endpoint.
  11070. var unit = roots[0] === 0 || roots[0] === 1 ? 0.5 : 1;
  11071. var x_ = quadraticAt(x0, x1, x2, roots[0]);
  11072. if (x_ < x) {
  11073. // Quick reject
  11074. return 0;
  11075. }
  11076. return y2 < y0 ? unit : -unit;
  11077. }
  11078. }
  11079. } // TODO
  11080. // Arc 旋转
  11081. function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {
  11082. y -= cy;
  11083. if (y > r || y < -r) {
  11084. return 0;
  11085. }
  11086. var tmp = Math.sqrt(r * r - y * y);
  11087. roots[0] = -tmp;
  11088. roots[1] = tmp;
  11089. var diff = Math.abs(startAngle - endAngle);
  11090. if (diff < 1e-4) {
  11091. return 0;
  11092. }
  11093. if (diff % PI2$1 < 1e-4) {
  11094. // Is a circle
  11095. startAngle = 0;
  11096. endAngle = PI2$1;
  11097. var dir = anticlockwise ? 1 : -1;
  11098. if (x >= roots[0] + cx && x <= roots[1] + cx) {
  11099. return dir;
  11100. } else {
  11101. return 0;
  11102. }
  11103. }
  11104. if (anticlockwise) {
  11105. var tmp = startAngle;
  11106. startAngle = normalizeRadian(endAngle);
  11107. endAngle = normalizeRadian(tmp);
  11108. } else {
  11109. startAngle = normalizeRadian(startAngle);
  11110. endAngle = normalizeRadian(endAngle);
  11111. }
  11112. if (startAngle > endAngle) {
  11113. endAngle += PI2$1;
  11114. }
  11115. var w = 0;
  11116. for (var i = 0; i < 2; i++) {
  11117. var x_ = roots[i];
  11118. if (x_ + cx > x) {
  11119. var angle = Math.atan2(y, x_);
  11120. var dir = anticlockwise ? 1 : -1;
  11121. if (angle < 0) {
  11122. angle = PI2$1 + angle;
  11123. }
  11124. if (angle >= startAngle && angle <= endAngle || angle + PI2$1 >= startAngle && angle + PI2$1 <= endAngle) {
  11125. if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
  11126. dir = -dir;
  11127. }
  11128. w += dir;
  11129. }
  11130. }
  11131. }
  11132. return w;
  11133. }
  11134. function containPath(data, lineWidth, isStroke, x, y) {
  11135. var w = 0;
  11136. var xi = 0;
  11137. var yi = 0;
  11138. var x0 = 0;
  11139. var y0 = 0;
  11140. for (var i = 0; i < data.length;) {
  11141. var cmd = data[i++]; // Begin a new subpath
  11142. if (cmd === CMD$1.M && i > 1) {
  11143. // Close previous subpath
  11144. if (!isStroke) {
  11145. w += windingLine(xi, yi, x0, y0, x, y);
  11146. } // 如果被任何一个 subpath 包含
  11147. // if (w !== 0) {
  11148. // return true;
  11149. // }
  11150. }
  11151. if (i == 1) {
  11152. // 如果第一个命令是 L, C, Q
  11153. // 则 previous point 同绘制命令的第一个 point
  11154. //
  11155. // 第一个命令为 Arc 的情况下会在后面特殊处理
  11156. xi = data[i];
  11157. yi = data[i + 1];
  11158. x0 = xi;
  11159. y0 = yi;
  11160. }
  11161. switch (cmd) {
  11162. case CMD$1.M:
  11163. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  11164. // 在 closePath 的时候使用
  11165. x0 = data[i++];
  11166. y0 = data[i++];
  11167. xi = x0;
  11168. yi = y0;
  11169. break;
  11170. case CMD$1.L:
  11171. if (isStroke) {
  11172. if (containStroke$1(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
  11173. return true;
  11174. }
  11175. } else {
  11176. // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN
  11177. w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
  11178. }
  11179. xi = data[i++];
  11180. yi = data[i++];
  11181. break;
  11182. case CMD$1.C:
  11183. if (isStroke) {
  11184. if (containStroke$2(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
  11185. return true;
  11186. }
  11187. } else {
  11188. w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
  11189. }
  11190. xi = data[i++];
  11191. yi = data[i++];
  11192. break;
  11193. case CMD$1.Q:
  11194. if (isStroke) {
  11195. if (containStroke$3(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
  11196. return true;
  11197. }
  11198. } else {
  11199. w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
  11200. }
  11201. xi = data[i++];
  11202. yi = data[i++];
  11203. break;
  11204. case CMD$1.A:
  11205. // TODO Arc 判断的开销比较大
  11206. var cx = data[i++];
  11207. var cy = data[i++];
  11208. var rx = data[i++];
  11209. var ry = data[i++];
  11210. var theta = data[i++];
  11211. var dTheta = data[i++]; // TODO Arc 旋转
  11212. var psi = data[i++];
  11213. var anticlockwise = 1 - data[i++];
  11214. var x1 = Math.cos(theta) * rx + cx;
  11215. var y1 = Math.sin(theta) * ry + cy; // 不是直接使用 arc 命令
  11216. if (i > 1) {
  11217. w += windingLine(xi, yi, x1, y1, x, y);
  11218. } else {
  11219. // 第一个命令起点还未定义
  11220. x0 = x1;
  11221. y0 = y1;
  11222. } // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
  11223. var _x = (x - cx) * ry / rx + cx;
  11224. if (isStroke) {
  11225. if (containStroke$4(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {
  11226. return true;
  11227. }
  11228. } else {
  11229. w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);
  11230. }
  11231. xi = Math.cos(theta + dTheta) * rx + cx;
  11232. yi = Math.sin(theta + dTheta) * ry + cy;
  11233. break;
  11234. case CMD$1.R:
  11235. x0 = xi = data[i++];
  11236. y0 = yi = data[i++];
  11237. var width = data[i++];
  11238. var height = data[i++];
  11239. var x1 = x0 + width;
  11240. var y1 = y0 + height;
  11241. if (isStroke) {
  11242. if (containStroke$1(x0, y0, x1, y0, lineWidth, x, y) || containStroke$1(x1, y0, x1, y1, lineWidth, x, y) || containStroke$1(x1, y1, x0, y1, lineWidth, x, y) || containStroke$1(x0, y1, x0, y0, lineWidth, x, y)) {
  11243. return true;
  11244. }
  11245. } else {
  11246. // FIXME Clockwise ?
  11247. w += windingLine(x1, y0, x1, y1, x, y);
  11248. w += windingLine(x0, y1, x0, y0, x, y);
  11249. }
  11250. break;
  11251. case CMD$1.Z:
  11252. if (isStroke) {
  11253. if (containStroke$1(xi, yi, x0, y0, lineWidth, x, y)) {
  11254. return true;
  11255. }
  11256. } else {
  11257. // Close a subpath
  11258. w += windingLine(xi, yi, x0, y0, x, y); // 如果被任何一个 subpath 包含
  11259. // FIXME subpaths may overlap
  11260. // if (w !== 0) {
  11261. // return true;
  11262. // }
  11263. }
  11264. xi = x0;
  11265. yi = y0;
  11266. break;
  11267. }
  11268. }
  11269. if (!isStroke && !isAroundEqual(yi, y0)) {
  11270. w += windingLine(xi, yi, x0, y0, x, y) || 0;
  11271. }
  11272. return w !== 0;
  11273. }
  11274. function contain(pathData, x, y) {
  11275. return containPath(pathData, 0, false, x, y);
  11276. }
  11277. function containStroke(pathData, lineWidth, x, y) {
  11278. return containPath(pathData, lineWidth, true, x, y);
  11279. }
  11280. var getCanvasPattern = Pattern.prototype.getCanvasPattern;
  11281. var abs = Math.abs;
  11282. var pathProxyForDraw = new PathProxy(true);
  11283. /**
  11284. * @alias module:zrender/graphic/Path
  11285. * @extends module:zrender/graphic/Displayable
  11286. * @constructor
  11287. * @param {Object} opts
  11288. */
  11289. function Path(opts) {
  11290. Displayable.call(this, opts);
  11291. /**
  11292. * @type {module:zrender/core/PathProxy}
  11293. * @readOnly
  11294. */
  11295. this.path = null;
  11296. }
  11297. Path.prototype = {
  11298. constructor: Path,
  11299. type: 'path',
  11300. __dirtyPath: true,
  11301. strokeContainThreshold: 5,
  11302. brush: function (ctx, prevEl) {
  11303. var style = this.style;
  11304. var path = this.path || pathProxyForDraw;
  11305. var hasStroke = style.hasStroke();
  11306. var hasFill = style.hasFill();
  11307. var fill = style.fill;
  11308. var stroke = style.stroke;
  11309. var hasFillGradient = hasFill && !!fill.colorStops;
  11310. var hasStrokeGradient = hasStroke && !!stroke.colorStops;
  11311. var hasFillPattern = hasFill && !!fill.image;
  11312. var hasStrokePattern = hasStroke && !!stroke.image;
  11313. style.bind(ctx, this, prevEl);
  11314. this.setTransform(ctx);
  11315. if (this.__dirty) {
  11316. var rect; // Update gradient because bounding rect may changed
  11317. if (hasFillGradient) {
  11318. rect = rect || this.getBoundingRect();
  11319. this._fillGradient = style.getGradient(ctx, fill, rect);
  11320. }
  11321. if (hasStrokeGradient) {
  11322. rect = rect || this.getBoundingRect();
  11323. this._strokeGradient = style.getGradient(ctx, stroke, rect);
  11324. }
  11325. } // Use the gradient or pattern
  11326. if (hasFillGradient) {
  11327. // PENDING If may have affect the state
  11328. ctx.fillStyle = this._fillGradient;
  11329. } else if (hasFillPattern) {
  11330. ctx.fillStyle = getCanvasPattern.call(fill, ctx);
  11331. }
  11332. if (hasStrokeGradient) {
  11333. ctx.strokeStyle = this._strokeGradient;
  11334. } else if (hasStrokePattern) {
  11335. ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);
  11336. }
  11337. var lineDash = style.lineDash;
  11338. var lineDashOffset = style.lineDashOffset;
  11339. var ctxLineDash = !!ctx.setLineDash; // Update path sx, sy
  11340. var scale = this.getGlobalScale();
  11341. path.setScale(scale[0], scale[1]); // Proxy context
  11342. // Rebuild path in following 2 cases
  11343. // 1. Path is dirty
  11344. // 2. Path needs javascript implemented lineDash stroking.
  11345. // In this case, lineDash information will not be saved in PathProxy
  11346. if (this.__dirtyPath || lineDash && !ctxLineDash && hasStroke) {
  11347. path.beginPath(ctx); // Setting line dash before build path
  11348. if (lineDash && !ctxLineDash) {
  11349. path.setLineDash(lineDash);
  11350. path.setLineDashOffset(lineDashOffset);
  11351. }
  11352. this.buildPath(path, this.shape, false); // Clear path dirty flag
  11353. if (this.path) {
  11354. this.__dirtyPath = false;
  11355. }
  11356. } else {
  11357. // Replay path building
  11358. ctx.beginPath();
  11359. this.path.rebuildPath(ctx);
  11360. }
  11361. hasFill && path.fill(ctx);
  11362. if (lineDash && ctxLineDash) {
  11363. ctx.setLineDash(lineDash);
  11364. ctx.lineDashOffset = lineDashOffset;
  11365. }
  11366. hasStroke && path.stroke(ctx);
  11367. if (lineDash && ctxLineDash) {
  11368. // PENDING
  11369. // Remove lineDash
  11370. ctx.setLineDash([]);
  11371. }
  11372. this.restoreTransform(ctx); // Draw rect text
  11373. if (style.text != null) {
  11374. this.drawRectText(ctx, this.getBoundingRect());
  11375. }
  11376. },
  11377. // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath
  11378. // Like in circle
  11379. buildPath: function (ctx, shapeCfg, inBundle) {},
  11380. createPathProxy: function () {
  11381. this.path = new PathProxy();
  11382. },
  11383. getBoundingRect: function () {
  11384. var rect = this._rect;
  11385. var style = this.style;
  11386. var needsUpdateRect = !rect;
  11387. if (needsUpdateRect) {
  11388. var path = this.path;
  11389. if (!path) {
  11390. // Create path on demand.
  11391. path = this.path = new PathProxy();
  11392. }
  11393. if (this.__dirtyPath) {
  11394. path.beginPath();
  11395. this.buildPath(path, this.shape, false);
  11396. }
  11397. rect = path.getBoundingRect();
  11398. }
  11399. this._rect = rect;
  11400. if (style.hasStroke()) {
  11401. // Needs update rect with stroke lineWidth when
  11402. // 1. Element changes scale or lineWidth
  11403. // 2. Shape is changed
  11404. var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());
  11405. if (this.__dirty || needsUpdateRect) {
  11406. rectWithStroke.copy(rect); // FIXME Must after updateTransform
  11407. var w = style.lineWidth; // PENDING, Min line width is needed when line is horizontal or vertical
  11408. var lineScale = style.strokeNoScale ? this.getLineScale() : 1; // Only add extra hover lineWidth when there are no fill
  11409. if (!style.hasFill()) {
  11410. w = Math.max(w, this.strokeContainThreshold || 4);
  11411. } // Consider line width
  11412. // Line scale can't be 0;
  11413. if (lineScale > 1e-10) {
  11414. rectWithStroke.width += w / lineScale;
  11415. rectWithStroke.height += w / lineScale;
  11416. rectWithStroke.x -= w / lineScale / 2;
  11417. rectWithStroke.y -= w / lineScale / 2;
  11418. }
  11419. } // Return rect with stroke
  11420. return rectWithStroke;
  11421. }
  11422. return rect;
  11423. },
  11424. contain: function (x, y) {
  11425. var localPos = this.transformCoordToLocal(x, y);
  11426. var rect = this.getBoundingRect();
  11427. var style = this.style;
  11428. x = localPos[0];
  11429. y = localPos[1];
  11430. if (rect.contain(x, y)) {
  11431. var pathData = this.path.data;
  11432. if (style.hasStroke()) {
  11433. var lineWidth = style.lineWidth;
  11434. var lineScale = style.strokeNoScale ? this.getLineScale() : 1; // Line scale can't be 0;
  11435. if (lineScale > 1e-10) {
  11436. // Only add extra hover lineWidth when there are no fill
  11437. if (!style.hasFill()) {
  11438. lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
  11439. }
  11440. if (containStroke(pathData, lineWidth / lineScale, x, y)) {
  11441. return true;
  11442. }
  11443. }
  11444. }
  11445. if (style.hasFill()) {
  11446. return contain(pathData, x, y);
  11447. }
  11448. }
  11449. return false;
  11450. },
  11451. /**
  11452. * @param {boolean} dirtyPath
  11453. */
  11454. dirty: function (dirtyPath) {
  11455. if (dirtyPath == null) {
  11456. dirtyPath = true;
  11457. } // Only mark dirty, not mark clean
  11458. if (dirtyPath) {
  11459. this.__dirtyPath = dirtyPath;
  11460. this._rect = null;
  11461. }
  11462. this.__dirty = true;
  11463. this.__zr && this.__zr.refresh(); // Used as a clipping path
  11464. if (this.__clipTarget) {
  11465. this.__clipTarget.dirty();
  11466. }
  11467. },
  11468. /**
  11469. * Alias for animate('shape')
  11470. * @param {boolean} loop
  11471. */
  11472. animateShape: function (loop) {
  11473. return this.animate('shape', loop);
  11474. },
  11475. // Overwrite attrKV
  11476. attrKV: function (key, value) {
  11477. // FIXME
  11478. if (key === 'shape') {
  11479. this.setShape(value);
  11480. this.__dirtyPath = true;
  11481. this._rect = null;
  11482. } else {
  11483. Displayable.prototype.attrKV.call(this, key, value);
  11484. }
  11485. },
  11486. /**
  11487. * @param {Object|string} key
  11488. * @param {*} value
  11489. */
  11490. setShape: function (key, value) {
  11491. var shape = this.shape; // Path from string may not have shape
  11492. if (shape) {
  11493. if (isObject(key)) {
  11494. for (var name in key) {
  11495. if (key.hasOwnProperty(name)) {
  11496. shape[name] = key[name];
  11497. }
  11498. }
  11499. } else {
  11500. shape[key] = value;
  11501. }
  11502. this.dirty(true);
  11503. }
  11504. return this;
  11505. },
  11506. getLineScale: function () {
  11507. var m = this.transform; // Get the line scale.
  11508. // Determinant of `m` means how much the area is enlarged by the
  11509. // transformation. So its square root can be used as a scale factor
  11510. // for width.
  11511. return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10 ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1])) : 1;
  11512. }
  11513. };
  11514. /**
  11515. * 扩展一个 Path element, 比如星形,圆等。
  11516. * Extend a path element
  11517. * @param {Object} props
  11518. * @param {string} props.type Path type
  11519. * @param {Function} props.init Initialize
  11520. * @param {Function} props.buildPath Overwrite buildPath method
  11521. * @param {Object} [props.style] Extended default style config
  11522. * @param {Object} [props.shape] Extended default shape config
  11523. */
  11524. Path.extend = function (defaults$$1) {
  11525. var Sub = function (opts) {
  11526. Path.call(this, opts);
  11527. if (defaults$$1.style) {
  11528. // Extend default style
  11529. this.style.extendFrom(defaults$$1.style, false);
  11530. } // Extend default shape
  11531. var defaultShape = defaults$$1.shape;
  11532. if (defaultShape) {
  11533. this.shape = this.shape || {};
  11534. var thisShape = this.shape;
  11535. for (var name in defaultShape) {
  11536. if (!thisShape.hasOwnProperty(name) && defaultShape.hasOwnProperty(name)) {
  11537. thisShape[name] = defaultShape[name];
  11538. }
  11539. }
  11540. }
  11541. defaults$$1.init && defaults$$1.init.call(this, opts);
  11542. };
  11543. inherits(Sub, Path); // FIXME 不能 extend position, rotation 等引用对象
  11544. for (var name in defaults$$1) {
  11545. // Extending prototype values and methods
  11546. if (name !== 'style' && name !== 'shape') {
  11547. Sub.prototype[name] = defaults$$1[name];
  11548. }
  11549. }
  11550. return Sub;
  11551. };
  11552. inherits(Path, Displayable);
  11553. var CMD$2 = PathProxy.CMD;
  11554. var points = [[], [], []];
  11555. var mathSqrt$3 = Math.sqrt;
  11556. var mathAtan2 = Math.atan2;
  11557. var transformPath = function (path, m) {
  11558. var data = path.data;
  11559. var cmd;
  11560. var nPoint;
  11561. var i;
  11562. var j;
  11563. var k;
  11564. var p;
  11565. var M = CMD$2.M;
  11566. var C = CMD$2.C;
  11567. var L = CMD$2.L;
  11568. var R = CMD$2.R;
  11569. var A = CMD$2.A;
  11570. var Q = CMD$2.Q;
  11571. for (i = 0, j = 0; i < data.length;) {
  11572. cmd = data[i++];
  11573. j = i;
  11574. nPoint = 0;
  11575. switch (cmd) {
  11576. case M:
  11577. nPoint = 1;
  11578. break;
  11579. case L:
  11580. nPoint = 1;
  11581. break;
  11582. case C:
  11583. nPoint = 3;
  11584. break;
  11585. case Q:
  11586. nPoint = 2;
  11587. break;
  11588. case A:
  11589. var x = m[4];
  11590. var y = m[5];
  11591. var sx = mathSqrt$3(m[0] * m[0] + m[1] * m[1]);
  11592. var sy = mathSqrt$3(m[2] * m[2] + m[3] * m[3]);
  11593. var angle = mathAtan2(-m[1] / sy, m[0] / sx); // cx
  11594. data[i] *= sx;
  11595. data[i++] += x; // cy
  11596. data[i] *= sy;
  11597. data[i++] += y; // Scale rx and ry
  11598. // FIXME Assume psi is 0 here
  11599. data[i++] *= sx;
  11600. data[i++] *= sy; // Start angle
  11601. data[i++] += angle; // end angle
  11602. data[i++] += angle; // FIXME psi
  11603. i += 2;
  11604. j = i;
  11605. break;
  11606. case R:
  11607. // x0, y0
  11608. p[0] = data[i++];
  11609. p[1] = data[i++];
  11610. applyTransform(p, p, m);
  11611. data[j++] = p[0];
  11612. data[j++] = p[1]; // x1, y1
  11613. p[0] += data[i++];
  11614. p[1] += data[i++];
  11615. applyTransform(p, p, m);
  11616. data[j++] = p[0];
  11617. data[j++] = p[1];
  11618. }
  11619. for (k = 0; k < nPoint; k++) {
  11620. var p = points[k];
  11621. p[0] = data[i++];
  11622. p[1] = data[i++];
  11623. applyTransform(p, p, m); // Write back
  11624. data[j++] = p[0];
  11625. data[j++] = p[1];
  11626. }
  11627. }
  11628. }; // command chars
  11629. var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'];
  11630. var mathSqrt = Math.sqrt;
  11631. var mathSin = Math.sin;
  11632. var mathCos = Math.cos;
  11633. var PI = Math.PI;
  11634. var vMag = function (v) {
  11635. return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
  11636. };
  11637. var vRatio = function (u, v) {
  11638. return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
  11639. };
  11640. var vAngle = function (u, v) {
  11641. return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
  11642. };
  11643. function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {
  11644. var psi = psiDeg * (PI / 180.0);
  11645. var xp = mathCos(psi) * (x1 - x2) / 2.0 + mathSin(psi) * (y1 - y2) / 2.0;
  11646. var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0 + mathCos(psi) * (y1 - y2) / 2.0;
  11647. var lambda = xp * xp / (rx * rx) + yp * yp / (ry * ry);
  11648. if (lambda > 1) {
  11649. rx *= mathSqrt(lambda);
  11650. ry *= mathSqrt(lambda);
  11651. }
  11652. var f = (fa === fs ? -1 : 1) * mathSqrt((rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) / (rx * rx * (yp * yp) + ry * ry * (xp * xp))) || 0;
  11653. var cxp = f * rx * yp / ry;
  11654. var cyp = f * -ry * xp / rx;
  11655. var cx = (x1 + x2) / 2.0 + mathCos(psi) * cxp - mathSin(psi) * cyp;
  11656. var cy = (y1 + y2) / 2.0 + mathSin(psi) * cxp + mathCos(psi) * cyp;
  11657. var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
  11658. var u = [(xp - cxp) / rx, (yp - cyp) / ry];
  11659. var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
  11660. var dTheta = vAngle(u, v);
  11661. if (vRatio(u, v) <= -1) {
  11662. dTheta = PI;
  11663. }
  11664. if (vRatio(u, v) >= 1) {
  11665. dTheta = 0;
  11666. }
  11667. if (fs === 0 && dTheta > 0) {
  11668. dTheta = dTheta - 2 * PI;
  11669. }
  11670. if (fs === 1 && dTheta < 0) {
  11671. dTheta = dTheta + 2 * PI;
  11672. }
  11673. path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);
  11674. }
  11675. function createPathProxyFromString(data) {
  11676. if (!data) {
  11677. return [];
  11678. } // command string
  11679. var cs = data.replace(/-/g, ' -').replace(/ /g, ' ').replace(/ /g, ',').replace(/,,/g, ',');
  11680. var n; // create pipes so that we can split the data
  11681. for (n = 0; n < cc.length; n++) {
  11682. cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
  11683. } // create array
  11684. var arr = cs.split('|'); // init context point
  11685. var cpx = 0;
  11686. var cpy = 0;
  11687. var path = new PathProxy();
  11688. var CMD = PathProxy.CMD;
  11689. var prevCmd;
  11690. for (n = 1; n < arr.length; n++) {
  11691. var str = arr[n];
  11692. var c = str.charAt(0);
  11693. var off = 0;
  11694. var p = str.slice(1).replace(/e,-/g, 'e-').split(',');
  11695. var cmd;
  11696. if (p.length > 0 && p[0] === '') {
  11697. p.shift();
  11698. }
  11699. for (var i = 0; i < p.length; i++) {
  11700. p[i] = parseFloat(p[i]);
  11701. }
  11702. while (off < p.length && !isNaN(p[off])) {
  11703. if (isNaN(p[0])) {
  11704. break;
  11705. }
  11706. var ctlPtx;
  11707. var ctlPty;
  11708. var rx;
  11709. var ry;
  11710. var psi;
  11711. var fa;
  11712. var fs;
  11713. var x1 = cpx;
  11714. var y1 = cpy; // convert l, H, h, V, and v to L
  11715. switch (c) {
  11716. case 'l':
  11717. cpx += p[off++];
  11718. cpy += p[off++];
  11719. cmd = CMD.L;
  11720. path.addData(cmd, cpx, cpy);
  11721. break;
  11722. case 'L':
  11723. cpx = p[off++];
  11724. cpy = p[off++];
  11725. cmd = CMD.L;
  11726. path.addData(cmd, cpx, cpy);
  11727. break;
  11728. case 'm':
  11729. cpx += p[off++];
  11730. cpy += p[off++];
  11731. cmd = CMD.M;
  11732. path.addData(cmd, cpx, cpy);
  11733. c = 'l';
  11734. break;
  11735. case 'M':
  11736. cpx = p[off++];
  11737. cpy = p[off++];
  11738. cmd = CMD.M;
  11739. path.addData(cmd, cpx, cpy);
  11740. c = 'L';
  11741. break;
  11742. case 'h':
  11743. cpx += p[off++];
  11744. cmd = CMD.L;
  11745. path.addData(cmd, cpx, cpy);
  11746. break;
  11747. case 'H':
  11748. cpx = p[off++];
  11749. cmd = CMD.L;
  11750. path.addData(cmd, cpx, cpy);
  11751. break;
  11752. case 'v':
  11753. cpy += p[off++];
  11754. cmd = CMD.L;
  11755. path.addData(cmd, cpx, cpy);
  11756. break;
  11757. case 'V':
  11758. cpy = p[off++];
  11759. cmd = CMD.L;
  11760. path.addData(cmd, cpx, cpy);
  11761. break;
  11762. case 'C':
  11763. cmd = CMD.C;
  11764. path.addData(cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]);
  11765. cpx = p[off - 2];
  11766. cpy = p[off - 1];
  11767. break;
  11768. case 'c':
  11769. cmd = CMD.C;
  11770. path.addData(cmd, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy);
  11771. cpx += p[off - 2];
  11772. cpy += p[off - 1];
  11773. break;
  11774. case 'S':
  11775. ctlPtx = cpx;
  11776. ctlPty = cpy;
  11777. var len = path.len();
  11778. var pathData = path.data;
  11779. if (prevCmd === CMD.C) {
  11780. ctlPtx += cpx - pathData[len - 4];
  11781. ctlPty += cpy - pathData[len - 3];
  11782. }
  11783. cmd = CMD.C;
  11784. x1 = p[off++];
  11785. y1 = p[off++];
  11786. cpx = p[off++];
  11787. cpy = p[off++];
  11788. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  11789. break;
  11790. case 's':
  11791. ctlPtx = cpx;
  11792. ctlPty = cpy;
  11793. var len = path.len();
  11794. var pathData = path.data;
  11795. if (prevCmd === CMD.C) {
  11796. ctlPtx += cpx - pathData[len - 4];
  11797. ctlPty += cpy - pathData[len - 3];
  11798. }
  11799. cmd = CMD.C;
  11800. x1 = cpx + p[off++];
  11801. y1 = cpy + p[off++];
  11802. cpx += p[off++];
  11803. cpy += p[off++];
  11804. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  11805. break;
  11806. case 'Q':
  11807. x1 = p[off++];
  11808. y1 = p[off++];
  11809. cpx = p[off++];
  11810. cpy = p[off++];
  11811. cmd = CMD.Q;
  11812. path.addData(cmd, x1, y1, cpx, cpy);
  11813. break;
  11814. case 'q':
  11815. x1 = p[off++] + cpx;
  11816. y1 = p[off++] + cpy;
  11817. cpx += p[off++];
  11818. cpy += p[off++];
  11819. cmd = CMD.Q;
  11820. path.addData(cmd, x1, y1, cpx, cpy);
  11821. break;
  11822. case 'T':
  11823. ctlPtx = cpx;
  11824. ctlPty = cpy;
  11825. var len = path.len();
  11826. var pathData = path.data;
  11827. if (prevCmd === CMD.Q) {
  11828. ctlPtx += cpx - pathData[len - 4];
  11829. ctlPty += cpy - pathData[len - 3];
  11830. }
  11831. cpx = p[off++];
  11832. cpy = p[off++];
  11833. cmd = CMD.Q;
  11834. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  11835. break;
  11836. case 't':
  11837. ctlPtx = cpx;
  11838. ctlPty = cpy;
  11839. var len = path.len();
  11840. var pathData = path.data;
  11841. if (prevCmd === CMD.Q) {
  11842. ctlPtx += cpx - pathData[len - 4];
  11843. ctlPty += cpy - pathData[len - 3];
  11844. }
  11845. cpx += p[off++];
  11846. cpy += p[off++];
  11847. cmd = CMD.Q;
  11848. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  11849. break;
  11850. case 'A':
  11851. rx = p[off++];
  11852. ry = p[off++];
  11853. psi = p[off++];
  11854. fa = p[off++];
  11855. fs = p[off++];
  11856. x1 = cpx, y1 = cpy;
  11857. cpx = p[off++];
  11858. cpy = p[off++];
  11859. cmd = CMD.A;
  11860. processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);
  11861. break;
  11862. case 'a':
  11863. rx = p[off++];
  11864. ry = p[off++];
  11865. psi = p[off++];
  11866. fa = p[off++];
  11867. fs = p[off++];
  11868. x1 = cpx, y1 = cpy;
  11869. cpx += p[off++];
  11870. cpy += p[off++];
  11871. cmd = CMD.A;
  11872. processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path);
  11873. break;
  11874. }
  11875. }
  11876. if (c === 'z' || c === 'Z') {
  11877. cmd = CMD.Z;
  11878. path.addData(cmd);
  11879. }
  11880. prevCmd = cmd;
  11881. }
  11882. path.toStatic();
  11883. return path;
  11884. } // TODO Optimize double memory cost problem
  11885. function createPathOptions(str, opts) {
  11886. var pathProxy = createPathProxyFromString(str);
  11887. opts = opts || {};
  11888. opts.buildPath = function (path) {
  11889. if (path.setData) {
  11890. path.setData(pathProxy.data); // Svg and vml renderer don't have context
  11891. var ctx = path.getContext();
  11892. if (ctx) {
  11893. path.rebuildPath(ctx);
  11894. }
  11895. } else {
  11896. var ctx = path;
  11897. pathProxy.rebuildPath(ctx);
  11898. }
  11899. };
  11900. opts.applyTransform = function (m) {
  11901. transformPath(pathProxy, m);
  11902. this.dirty(true);
  11903. };
  11904. return opts;
  11905. }
  11906. /**
  11907. * Create a Path object from path string data
  11908. * http://www.w3.org/TR/SVG/paths.html#PathData
  11909. * @param {Object} opts Other options
  11910. */
  11911. function createFromString(str, opts) {
  11912. return new Path(createPathOptions(str, opts));
  11913. }
  11914. /**
  11915. * Create a Path class from path string data
  11916. * @param {string} str
  11917. * @param {Object} opts Other options
  11918. */
  11919. function extendFromString(str, opts) {
  11920. return Path.extend(createPathOptions(str, opts));
  11921. }
  11922. /**
  11923. * Merge multiple paths
  11924. */
  11925. // TODO Apply transform
  11926. // TODO stroke dash
  11927. // TODO Optimize double memory cost problem
  11928. function mergePath$1(pathEls, opts) {
  11929. var pathList = [];
  11930. var len = pathEls.length;
  11931. for (var i = 0; i < len; i++) {
  11932. var pathEl = pathEls[i];
  11933. if (!pathEl.path) {
  11934. pathEl.createPathProxy();
  11935. }
  11936. if (pathEl.__dirtyPath) {
  11937. pathEl.buildPath(pathEl.path, pathEl.shape, true);
  11938. }
  11939. pathList.push(pathEl.path);
  11940. }
  11941. var pathBundle = new Path(opts); // Need path proxy.
  11942. pathBundle.createPathProxy();
  11943. pathBundle.buildPath = function (path) {
  11944. path.appendPath(pathList); // Svg and vml renderer don't have context
  11945. var ctx = path.getContext();
  11946. if (ctx) {
  11947. path.rebuildPath(ctx);
  11948. }
  11949. };
  11950. return pathBundle;
  11951. }
  11952. /**
  11953. * @alias zrender/graphic/Text
  11954. * @extends module:zrender/graphic/Displayable
  11955. * @constructor
  11956. * @param {Object} opts
  11957. */
  11958. var Text = function (opts) {
  11959. // jshint ignore:line
  11960. Displayable.call(this, opts);
  11961. };
  11962. Text.prototype = {
  11963. constructor: Text,
  11964. type: 'text',
  11965. brush: function (ctx, prevEl) {
  11966. var style = this.style; // Optimize, avoid normalize every time.
  11967. this.__dirty && normalizeTextStyle(style, true); // Use props with prefix 'text'.
  11968. style.fill = style.stroke = style.shadowBlur = style.shadowColor = style.shadowOffsetX = style.shadowOffsetY = null;
  11969. var text = style.text; // Convert to string
  11970. text != null && (text += ''); // Always bind style
  11971. style.bind(ctx, this, prevEl);
  11972. if (!needDrawText(text, style)) {
  11973. return;
  11974. }
  11975. this.setTransform(ctx);
  11976. renderText(this, ctx, text, style);
  11977. this.restoreTransform(ctx);
  11978. },
  11979. getBoundingRect: function () {
  11980. var style = this.style; // Optimize, avoid normalize every time.
  11981. this.__dirty && normalizeTextStyle(style, true);
  11982. if (!this._rect) {
  11983. var text = style.text;
  11984. text != null ? text += '' : text = '';
  11985. var rect = getBoundingRect(style.text + '', style.font, style.textAlign, style.textVerticalAlign, style.textPadding, style.rich);
  11986. rect.x += style.x || 0;
  11987. rect.y += style.y || 0;
  11988. if (getStroke(style.textStroke, style.textStrokeWidth)) {
  11989. var w = style.textStrokeWidth;
  11990. rect.x -= w / 2;
  11991. rect.y -= w / 2;
  11992. rect.width += w;
  11993. rect.height += w;
  11994. }
  11995. this._rect = rect;
  11996. }
  11997. return this._rect;
  11998. }
  11999. };
  12000. inherits(Text, Displayable);
  12001. /**
  12002. * 圆形
  12003. * @module zrender/shape/Circle
  12004. */
  12005. var Circle = Path.extend({
  12006. type: 'circle',
  12007. shape: {
  12008. cx: 0,
  12009. cy: 0,
  12010. r: 0
  12011. },
  12012. buildPath: function (ctx, shape, inBundle) {
  12013. // Better stroking in ShapeBundle
  12014. // Always do it may have performence issue ( fill may be 2x more cost)
  12015. if (inBundle) {
  12016. ctx.moveTo(shape.cx + shape.r, shape.cy);
  12017. } // else {
  12018. // if (ctx.allocate && !ctx.data.length) {
  12019. // ctx.allocate(ctx.CMD_MEM_SIZE.A);
  12020. // }
  12021. // }
  12022. // Better stroking in ShapeBundle
  12023. // ctx.moveTo(shape.cx + shape.r, shape.cy);
  12024. ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true);
  12025. }
  12026. }); // Fix weird bug in some version of IE11 (like 11.0.9600.178**),
  12027. // where exception "unexpected call to method or property access"
  12028. // might be thrown when calling ctx.fill or ctx.stroke after a path
  12029. // whose area size is zero is drawn and ctx.clip() is called and
  12030. // shadowBlur is set. See #4572, #3112, #5777.
  12031. // (e.g.,
  12032. // ctx.moveTo(10, 10);
  12033. // ctx.lineTo(20, 10);
  12034. // ctx.closePath();
  12035. // ctx.clip();
  12036. // ctx.shadowBlur = 10;
  12037. // ...
  12038. // ctx.fill();
  12039. // )
  12040. var shadowTemp = [['shadowBlur', 0], ['shadowColor', '#000'], ['shadowOffsetX', 0], ['shadowOffsetY', 0]];
  12041. var fixClipWithShadow = function (orignalBrush) {
  12042. // version string can be: '11.0'
  12043. return env$1.browser.ie && env$1.browser.version >= 11 ? function () {
  12044. var clipPaths = this.__clipPaths;
  12045. var style = this.style;
  12046. var modified;
  12047. if (clipPaths) {
  12048. for (var i = 0; i < clipPaths.length; i++) {
  12049. var clipPath = clipPaths[i];
  12050. var shape = clipPath && clipPath.shape;
  12051. var type = clipPath && clipPath.type;
  12052. if (shape && (type === 'sector' && shape.startAngle === shape.endAngle || type === 'rect' && (!shape.width || !shape.height))) {
  12053. for (var j = 0; j < shadowTemp.length; j++) {
  12054. // It is save to put shadowTemp static, because shadowTemp
  12055. // will be all modified each item brush called.
  12056. shadowTemp[j][2] = style[shadowTemp[j][0]];
  12057. style[shadowTemp[j][0]] = shadowTemp[j][1];
  12058. }
  12059. modified = true;
  12060. break;
  12061. }
  12062. }
  12063. }
  12064. orignalBrush.apply(this, arguments);
  12065. if (modified) {
  12066. for (var j = 0; j < shadowTemp.length; j++) {
  12067. style[shadowTemp[j][0]] = shadowTemp[j][2];
  12068. }
  12069. }
  12070. } : orignalBrush;
  12071. };
  12072. /**
  12073. * 扇形
  12074. * @module zrender/graphic/shape/Sector
  12075. */
  12076. var Sector = Path.extend({
  12077. type: 'sector',
  12078. shape: {
  12079. cx: 0,
  12080. cy: 0,
  12081. r0: 0,
  12082. r: 0,
  12083. startAngle: 0,
  12084. endAngle: Math.PI * 2,
  12085. clockwise: true
  12086. },
  12087. brush: fixClipWithShadow(Path.prototype.brush),
  12088. buildPath: function (ctx, shape) {
  12089. var x = shape.cx;
  12090. var y = shape.cy;
  12091. var r0 = Math.max(shape.r0 || 0, 0);
  12092. var r = Math.max(shape.r, 0);
  12093. var startAngle = shape.startAngle;
  12094. var endAngle = shape.endAngle;
  12095. var clockwise = shape.clockwise;
  12096. var unitX = Math.cos(startAngle);
  12097. var unitY = Math.sin(startAngle);
  12098. ctx.moveTo(unitX * r0 + x, unitY * r0 + y);
  12099. ctx.lineTo(unitX * r + x, unitY * r + y);
  12100. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  12101. ctx.lineTo(Math.cos(endAngle) * r0 + x, Math.sin(endAngle) * r0 + y);
  12102. if (r0 !== 0) {
  12103. ctx.arc(x, y, r0, endAngle, startAngle, clockwise);
  12104. }
  12105. ctx.closePath();
  12106. }
  12107. });
  12108. /**
  12109. * 圆环
  12110. * @module zrender/graphic/shape/Ring
  12111. */
  12112. var Ring = Path.extend({
  12113. type: 'ring',
  12114. shape: {
  12115. cx: 0,
  12116. cy: 0,
  12117. r: 0,
  12118. r0: 0
  12119. },
  12120. buildPath: function (ctx, shape) {
  12121. var x = shape.cx;
  12122. var y = shape.cy;
  12123. var PI2 = Math.PI * 2;
  12124. ctx.moveTo(x + shape.r, y);
  12125. ctx.arc(x, y, shape.r, 0, PI2, false);
  12126. ctx.moveTo(x + shape.r0, y);
  12127. ctx.arc(x, y, shape.r0, 0, PI2, true);
  12128. }
  12129. });
  12130. /**
  12131. * Catmull-Rom spline 插值折线
  12132. * @module zrender/shape/util/smoothSpline
  12133. * @author pissang (https://www.github.com/pissang)
  12134. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  12135. * errorrik (errorrik@gmail.com)
  12136. */
  12137. /**
  12138. * @inner
  12139. */
  12140. function interpolate(p0, p1, p2, p3, t, t2, t3) {
  12141. var v0 = (p2 - p0) * 0.5;
  12142. var v1 = (p3 - p1) * 0.5;
  12143. return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1;
  12144. }
  12145. /**
  12146. * @alias module:zrender/shape/util/smoothSpline
  12147. * @param {Array} points 线段顶点数组
  12148. * @param {boolean} isLoop
  12149. * @return {Array}
  12150. */
  12151. var smoothSpline = function (points, isLoop) {
  12152. var len$$1 = points.length;
  12153. var ret = [];
  12154. var distance$$1 = 0;
  12155. for (var i = 1; i < len$$1; i++) {
  12156. distance$$1 += distance(points[i - 1], points[i]);
  12157. }
  12158. var segs = distance$$1 / 2;
  12159. segs = segs < len$$1 ? len$$1 : segs;
  12160. for (var i = 0; i < segs; i++) {
  12161. var pos = i / (segs - 1) * (isLoop ? len$$1 : len$$1 - 1);
  12162. var idx = Math.floor(pos);
  12163. var w = pos - idx;
  12164. var p0;
  12165. var p1 = points[idx % len$$1];
  12166. var p2;
  12167. var p3;
  12168. if (!isLoop) {
  12169. p0 = points[idx === 0 ? idx : idx - 1];
  12170. p2 = points[idx > len$$1 - 2 ? len$$1 - 1 : idx + 1];
  12171. p3 = points[idx > len$$1 - 3 ? len$$1 - 1 : idx + 2];
  12172. } else {
  12173. p0 = points[(idx - 1 + len$$1) % len$$1];
  12174. p2 = points[(idx + 1) % len$$1];
  12175. p3 = points[(idx + 2) % len$$1];
  12176. }
  12177. var w2 = w * w;
  12178. var w3 = w * w2;
  12179. ret.push([interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3), interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)]);
  12180. }
  12181. return ret;
  12182. };
  12183. /**
  12184. * 贝塞尔平滑曲线
  12185. * @module zrender/shape/util/smoothBezier
  12186. * @author pissang (https://www.github.com/pissang)
  12187. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  12188. * errorrik (errorrik@gmail.com)
  12189. */
  12190. /**
  12191. * 贝塞尔平滑曲线
  12192. * @alias module:zrender/shape/util/smoothBezier
  12193. * @param {Array} points 线段顶点数组
  12194. * @param {number} smooth 平滑等级, 0-1
  12195. * @param {boolean} isLoop
  12196. * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内
  12197. * 比如 [[0, 0], [100, 100]], 这个包围盒会与
  12198. * 整个折线的包围盒做一个并集用来约束控制点。
  12199. * @param {Array} 计算出来的控制点数组
  12200. */
  12201. var smoothBezier = function (points, smooth, isLoop, constraint) {
  12202. var cps = [];
  12203. var v = [];
  12204. var v1 = [];
  12205. var v2 = [];
  12206. var prevPoint;
  12207. var nextPoint;
  12208. var min$$1, max$$1;
  12209. if (constraint) {
  12210. min$$1 = [Infinity, Infinity];
  12211. max$$1 = [-Infinity, -Infinity];
  12212. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  12213. min(min$$1, min$$1, points[i]);
  12214. max(max$$1, max$$1, points[i]);
  12215. } // 与指定的包围盒做并集
  12216. min(min$$1, min$$1, constraint[0]);
  12217. max(max$$1, max$$1, constraint[1]);
  12218. }
  12219. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  12220. var point = points[i];
  12221. if (isLoop) {
  12222. prevPoint = points[i ? i - 1 : len$$1 - 1];
  12223. nextPoint = points[(i + 1) % len$$1];
  12224. } else {
  12225. if (i === 0 || i === len$$1 - 1) {
  12226. cps.push(clone$1(points[i]));
  12227. continue;
  12228. } else {
  12229. prevPoint = points[i - 1];
  12230. nextPoint = points[i + 1];
  12231. }
  12232. }
  12233. sub(v, nextPoint, prevPoint); // use degree to scale the handle length
  12234. scale(v, v, smooth);
  12235. var d0 = distance(point, prevPoint);
  12236. var d1 = distance(point, nextPoint);
  12237. var sum = d0 + d1;
  12238. if (sum !== 0) {
  12239. d0 /= sum;
  12240. d1 /= sum;
  12241. }
  12242. scale(v1, v, -d0);
  12243. scale(v2, v, d1);
  12244. var cp0 = add([], point, v1);
  12245. var cp1 = add([], point, v2);
  12246. if (constraint) {
  12247. max(cp0, cp0, min$$1);
  12248. min(cp0, cp0, max$$1);
  12249. max(cp1, cp1, min$$1);
  12250. min(cp1, cp1, max$$1);
  12251. }
  12252. cps.push(cp0);
  12253. cps.push(cp1);
  12254. }
  12255. if (isLoop) {
  12256. cps.push(cps.shift());
  12257. }
  12258. return cps;
  12259. };
  12260. function buildPath$1(ctx, shape, closePath) {
  12261. var points = shape.points;
  12262. var smooth = shape.smooth;
  12263. if (points && points.length >= 2) {
  12264. if (smooth && smooth !== 'spline') {
  12265. var controlPoints = smoothBezier(points, smooth, closePath, shape.smoothConstraint);
  12266. ctx.moveTo(points[0][0], points[0][1]);
  12267. var len = points.length;
  12268. for (var i = 0; i < (closePath ? len : len - 1); i++) {
  12269. var cp1 = controlPoints[i * 2];
  12270. var cp2 = controlPoints[i * 2 + 1];
  12271. var p = points[(i + 1) % len];
  12272. ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]);
  12273. }
  12274. } else {
  12275. if (smooth === 'spline') {
  12276. points = smoothSpline(points, closePath);
  12277. }
  12278. ctx.moveTo(points[0][0], points[0][1]);
  12279. for (var i = 1, l = points.length; i < l; i++) {
  12280. ctx.lineTo(points[i][0], points[i][1]);
  12281. }
  12282. }
  12283. closePath && ctx.closePath();
  12284. }
  12285. }
  12286. /**
  12287. * 多边形
  12288. * @module zrender/shape/Polygon
  12289. */
  12290. var Polygon = Path.extend({
  12291. type: 'polygon',
  12292. shape: {
  12293. points: null,
  12294. smooth: false,
  12295. smoothConstraint: null
  12296. },
  12297. buildPath: function (ctx, shape) {
  12298. buildPath$1(ctx, shape, true);
  12299. }
  12300. });
  12301. /**
  12302. * @module zrender/graphic/shape/Polyline
  12303. */
  12304. var Polyline = Path.extend({
  12305. type: 'polyline',
  12306. shape: {
  12307. points: null,
  12308. smooth: false,
  12309. smoothConstraint: null
  12310. },
  12311. style: {
  12312. stroke: '#000',
  12313. fill: null
  12314. },
  12315. buildPath: function (ctx, shape) {
  12316. buildPath$1(ctx, shape, false);
  12317. }
  12318. });
  12319. /**
  12320. * 矩形
  12321. * @module zrender/graphic/shape/Rect
  12322. */
  12323. var Rect = Path.extend({
  12324. type: 'rect',
  12325. shape: {
  12326. // 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4
  12327. // r缩写为1 相当于 [1, 1, 1, 1]
  12328. // r缩写为[1] 相当于 [1, 1, 1, 1]
  12329. // r缩写为[1, 2] 相当于 [1, 2, 1, 2]
  12330. // r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]
  12331. r: 0,
  12332. x: 0,
  12333. y: 0,
  12334. width: 0,
  12335. height: 0
  12336. },
  12337. buildPath: function (ctx, shape) {
  12338. var x = shape.x;
  12339. var y = shape.y;
  12340. var width = shape.width;
  12341. var height = shape.height;
  12342. if (!shape.r) {
  12343. ctx.rect(x, y, width, height);
  12344. } else {
  12345. buildPath(ctx, shape);
  12346. }
  12347. ctx.closePath();
  12348. return;
  12349. }
  12350. });
  12351. /**
  12352. * 直线
  12353. * @module zrender/graphic/shape/Line
  12354. */
  12355. var Line = Path.extend({
  12356. type: 'line',
  12357. shape: {
  12358. // Start point
  12359. x1: 0,
  12360. y1: 0,
  12361. // End point
  12362. x2: 0,
  12363. y2: 0,
  12364. percent: 1
  12365. },
  12366. style: {
  12367. stroke: '#000',
  12368. fill: null
  12369. },
  12370. buildPath: function (ctx, shape) {
  12371. var x1 = shape.x1;
  12372. var y1 = shape.y1;
  12373. var x2 = shape.x2;
  12374. var y2 = shape.y2;
  12375. var percent = shape.percent;
  12376. if (percent === 0) {
  12377. return;
  12378. }
  12379. ctx.moveTo(x1, y1);
  12380. if (percent < 1) {
  12381. x2 = x1 * (1 - percent) + x2 * percent;
  12382. y2 = y1 * (1 - percent) + y2 * percent;
  12383. }
  12384. ctx.lineTo(x2, y2);
  12385. },
  12386. /**
  12387. * Get point at percent
  12388. * @param {number} percent
  12389. * @return {Array.<number>}
  12390. */
  12391. pointAt: function (p) {
  12392. var shape = this.shape;
  12393. return [shape.x1 * (1 - p) + shape.x2 * p, shape.y1 * (1 - p) + shape.y2 * p];
  12394. }
  12395. });
  12396. /**
  12397. * 贝塞尔曲线
  12398. * @module zrender/shape/BezierCurve
  12399. */
  12400. var out = [];
  12401. function someVectorAt(shape, t, isTangent) {
  12402. var cpx2 = shape.cpx2;
  12403. var cpy2 = shape.cpy2;
  12404. if (cpx2 === null || cpy2 === null) {
  12405. return [(isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t), (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)];
  12406. } else {
  12407. return [(isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t), (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)];
  12408. }
  12409. }
  12410. var BezierCurve = Path.extend({
  12411. type: 'bezier-curve',
  12412. shape: {
  12413. x1: 0,
  12414. y1: 0,
  12415. x2: 0,
  12416. y2: 0,
  12417. cpx1: 0,
  12418. cpy1: 0,
  12419. // cpx2: 0,
  12420. // cpy2: 0
  12421. // Curve show percent, for animating
  12422. percent: 1
  12423. },
  12424. style: {
  12425. stroke: '#000',
  12426. fill: null
  12427. },
  12428. buildPath: function (ctx, shape) {
  12429. var x1 = shape.x1;
  12430. var y1 = shape.y1;
  12431. var x2 = shape.x2;
  12432. var y2 = shape.y2;
  12433. var cpx1 = shape.cpx1;
  12434. var cpy1 = shape.cpy1;
  12435. var cpx2 = shape.cpx2;
  12436. var cpy2 = shape.cpy2;
  12437. var percent = shape.percent;
  12438. if (percent === 0) {
  12439. return;
  12440. }
  12441. ctx.moveTo(x1, y1);
  12442. if (cpx2 == null || cpy2 == null) {
  12443. if (percent < 1) {
  12444. quadraticSubdivide(x1, cpx1, x2, percent, out);
  12445. cpx1 = out[1];
  12446. x2 = out[2];
  12447. quadraticSubdivide(y1, cpy1, y2, percent, out);
  12448. cpy1 = out[1];
  12449. y2 = out[2];
  12450. }
  12451. ctx.quadraticCurveTo(cpx1, cpy1, x2, y2);
  12452. } else {
  12453. if (percent < 1) {
  12454. cubicSubdivide(x1, cpx1, cpx2, x2, percent, out);
  12455. cpx1 = out[1];
  12456. cpx2 = out[2];
  12457. x2 = out[3];
  12458. cubicSubdivide(y1, cpy1, cpy2, y2, percent, out);
  12459. cpy1 = out[1];
  12460. cpy2 = out[2];
  12461. y2 = out[3];
  12462. }
  12463. ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2);
  12464. }
  12465. },
  12466. /**
  12467. * Get point at percent
  12468. * @param {number} t
  12469. * @return {Array.<number>}
  12470. */
  12471. pointAt: function (t) {
  12472. return someVectorAt(this.shape, t, false);
  12473. },
  12474. /**
  12475. * Get tangent at percent
  12476. * @param {number} t
  12477. * @return {Array.<number>}
  12478. */
  12479. tangentAt: function (t) {
  12480. var p = someVectorAt(this.shape, t, true);
  12481. return normalize(p, p);
  12482. }
  12483. });
  12484. /**
  12485. * 圆弧
  12486. * @module zrender/graphic/shape/Arc
  12487. */
  12488. var Arc = Path.extend({
  12489. type: 'arc',
  12490. shape: {
  12491. cx: 0,
  12492. cy: 0,
  12493. r: 0,
  12494. startAngle: 0,
  12495. endAngle: Math.PI * 2,
  12496. clockwise: true
  12497. },
  12498. style: {
  12499. stroke: '#000',
  12500. fill: null
  12501. },
  12502. buildPath: function (ctx, shape) {
  12503. var x = shape.cx;
  12504. var y = shape.cy;
  12505. var r = Math.max(shape.r, 0);
  12506. var startAngle = shape.startAngle;
  12507. var endAngle = shape.endAngle;
  12508. var clockwise = shape.clockwise;
  12509. var unitX = Math.cos(startAngle);
  12510. var unitY = Math.sin(startAngle);
  12511. ctx.moveTo(unitX * r + x, unitY * r + y);
  12512. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  12513. }
  12514. }); // CompoundPath to improve performance
  12515. var CompoundPath = Path.extend({
  12516. type: 'compound',
  12517. shape: {
  12518. paths: null
  12519. },
  12520. _updatePathDirty: function () {
  12521. var dirtyPath = this.__dirtyPath;
  12522. var paths = this.shape.paths;
  12523. for (var i = 0; i < paths.length; i++) {
  12524. // Mark as dirty if any subpath is dirty
  12525. dirtyPath = dirtyPath || paths[i].__dirtyPath;
  12526. }
  12527. this.__dirtyPath = dirtyPath;
  12528. this.__dirty = this.__dirty || dirtyPath;
  12529. },
  12530. beforeBrush: function () {
  12531. this._updatePathDirty();
  12532. var paths = this.shape.paths || [];
  12533. var scale = this.getGlobalScale(); // Update path scale
  12534. for (var i = 0; i < paths.length; i++) {
  12535. if (!paths[i].path) {
  12536. paths[i].createPathProxy();
  12537. }
  12538. paths[i].path.setScale(scale[0], scale[1]);
  12539. }
  12540. },
  12541. buildPath: function (ctx, shape) {
  12542. var paths = shape.paths || [];
  12543. for (var i = 0; i < paths.length; i++) {
  12544. paths[i].buildPath(ctx, paths[i].shape, true);
  12545. }
  12546. },
  12547. afterBrush: function () {
  12548. var paths = this.shape.paths || [];
  12549. for (var i = 0; i < paths.length; i++) {
  12550. paths[i].__dirtyPath = false;
  12551. }
  12552. },
  12553. getBoundingRect: function () {
  12554. this._updatePathDirty();
  12555. return Path.prototype.getBoundingRect.call(this);
  12556. }
  12557. });
  12558. /**
  12559. * @param {Array.<Object>} colorStops
  12560. */
  12561. var Gradient = function (colorStops) {
  12562. this.colorStops = colorStops || [];
  12563. };
  12564. Gradient.prototype = {
  12565. constructor: Gradient,
  12566. addColorStop: function (offset, color) {
  12567. this.colorStops.push({
  12568. offset: offset,
  12569. color: color
  12570. });
  12571. }
  12572. };
  12573. /**
  12574. * x, y, x2, y2 are all percent from 0 to 1
  12575. * @param {number} [x=0]
  12576. * @param {number} [y=0]
  12577. * @param {number} [x2=1]
  12578. * @param {number} [y2=0]
  12579. * @param {Array.<Object>} colorStops
  12580. * @param {boolean} [globalCoord=false]
  12581. */
  12582. var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) {
  12583. // Should do nothing more in this constructor. Because gradient can be
  12584. // declard by `color: {type: 'linear', colorStops: ...}`, where
  12585. // this constructor will not be called.
  12586. this.x = x == null ? 0 : x;
  12587. this.y = y == null ? 0 : y;
  12588. this.x2 = x2 == null ? 1 : x2;
  12589. this.y2 = y2 == null ? 0 : y2; // Can be cloned
  12590. this.type = 'linear'; // If use global coord
  12591. this.global = globalCoord || false;
  12592. Gradient.call(this, colorStops);
  12593. };
  12594. LinearGradient.prototype = {
  12595. constructor: LinearGradient
  12596. };
  12597. inherits(LinearGradient, Gradient);
  12598. /**
  12599. * x, y, r are all percent from 0 to 1
  12600. * @param {number} [x=0.5]
  12601. * @param {number} [y=0.5]
  12602. * @param {number} [r=0.5]
  12603. * @param {Array.<Object>} [colorStops]
  12604. * @param {boolean} [globalCoord=false]
  12605. */
  12606. var RadialGradient = function (x, y, r, colorStops, globalCoord) {
  12607. // Should do nothing more in this constructor. Because gradient can be
  12608. // declard by `color: {type: 'radial', colorStops: ...}`, where
  12609. // this constructor will not be called.
  12610. this.x = x == null ? 0.5 : x;
  12611. this.y = y == null ? 0.5 : y;
  12612. this.r = r == null ? 0.5 : r; // Can be cloned
  12613. this.type = 'radial'; // If use global coord
  12614. this.global = globalCoord || false;
  12615. Gradient.call(this, colorStops);
  12616. };
  12617. RadialGradient.prototype = {
  12618. constructor: RadialGradient
  12619. };
  12620. inherits(RadialGradient, Gradient);
  12621. var round$1 = Math.round;
  12622. var mathMax$1 = Math.max;
  12623. var mathMin$1 = Math.min;
  12624. var EMPTY_OBJ = {};
  12625. /**
  12626. * Extend shape with parameters
  12627. */
  12628. function extendShape(opts) {
  12629. return Path.extend(opts);
  12630. }
  12631. /**
  12632. * Extend path
  12633. */
  12634. function extendPath(pathData, opts) {
  12635. return extendFromString(pathData, opts);
  12636. }
  12637. /**
  12638. * Create a path element from path data string
  12639. * @param {string} pathData
  12640. * @param {Object} opts
  12641. * @param {module:zrender/core/BoundingRect} rect
  12642. * @param {string} [layout=cover] 'center' or 'cover'
  12643. */
  12644. function makePath(pathData, opts, rect, layout) {
  12645. var path = createFromString(pathData, opts);
  12646. var boundingRect = path.getBoundingRect();
  12647. if (rect) {
  12648. if (layout === 'center') {
  12649. rect = centerGraphic(rect, boundingRect);
  12650. }
  12651. resizePath(path, rect);
  12652. }
  12653. return path;
  12654. }
  12655. /**
  12656. * Create a image element from image url
  12657. * @param {string} imageUrl image url
  12658. * @param {Object} opts options
  12659. * @param {module:zrender/core/BoundingRect} rect constrain rect
  12660. * @param {string} [layout=cover] 'center' or 'cover'
  12661. */
  12662. function makeImage(imageUrl, rect, layout) {
  12663. var path = new ZImage({
  12664. style: {
  12665. image: imageUrl,
  12666. x: rect.x,
  12667. y: rect.y,
  12668. width: rect.width,
  12669. height: rect.height
  12670. },
  12671. onload: function (img) {
  12672. if (layout === 'center') {
  12673. var boundingRect = {
  12674. width: img.width,
  12675. height: img.height
  12676. };
  12677. path.setStyle(centerGraphic(rect, boundingRect));
  12678. }
  12679. }
  12680. });
  12681. return path;
  12682. }
  12683. /**
  12684. * Get position of centered element in bounding box.
  12685. *
  12686. * @param {Object} rect element local bounding box
  12687. * @param {Object} boundingRect constraint bounding box
  12688. * @return {Object} element position containing x, y, width, and height
  12689. */
  12690. function centerGraphic(rect, boundingRect) {
  12691. // Set rect to center, keep width / height ratio.
  12692. var aspect = boundingRect.width / boundingRect.height;
  12693. var width = rect.height * aspect;
  12694. var height;
  12695. if (width <= rect.width) {
  12696. height = rect.height;
  12697. } else {
  12698. width = rect.width;
  12699. height = width / aspect;
  12700. }
  12701. var cx = rect.x + rect.width / 2;
  12702. var cy = rect.y + rect.height / 2;
  12703. return {
  12704. x: cx - width / 2,
  12705. y: cy - height / 2,
  12706. width: width,
  12707. height: height
  12708. };
  12709. }
  12710. var mergePath = mergePath$1;
  12711. /**
  12712. * Resize a path to fit the rect
  12713. * @param {module:zrender/graphic/Path} path
  12714. * @param {Object} rect
  12715. */
  12716. function resizePath(path, rect) {
  12717. if (!path.applyTransform) {
  12718. return;
  12719. }
  12720. var pathRect = path.getBoundingRect();
  12721. var m = pathRect.calculateTransform(rect);
  12722. path.applyTransform(m);
  12723. }
  12724. /**
  12725. * Sub pixel optimize line for canvas
  12726. *
  12727. * @param {Object} param
  12728. * @param {Object} [param.shape]
  12729. * @param {number} [param.shape.x1]
  12730. * @param {number} [param.shape.y1]
  12731. * @param {number} [param.shape.x2]
  12732. * @param {number} [param.shape.y2]
  12733. * @param {Object} [param.style]
  12734. * @param {number} [param.style.lineWidth]
  12735. * @return {Object} Modified param
  12736. */
  12737. function subPixelOptimizeLine(param) {
  12738. var shape = param.shape;
  12739. var lineWidth = param.style.lineWidth;
  12740. if (round$1(shape.x1 * 2) === round$1(shape.x2 * 2)) {
  12741. shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true);
  12742. }
  12743. if (round$1(shape.y1 * 2) === round$1(shape.y2 * 2)) {
  12744. shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true);
  12745. }
  12746. return param;
  12747. }
  12748. /**
  12749. * Sub pixel optimize rect for canvas
  12750. *
  12751. * @param {Object} param
  12752. * @param {Object} [param.shape]
  12753. * @param {number} [param.shape.x]
  12754. * @param {number} [param.shape.y]
  12755. * @param {number} [param.shape.width]
  12756. * @param {number} [param.shape.height]
  12757. * @param {Object} [param.style]
  12758. * @param {number} [param.style.lineWidth]
  12759. * @return {Object} Modified param
  12760. */
  12761. function subPixelOptimizeRect(param) {
  12762. var shape = param.shape;
  12763. var lineWidth = param.style.lineWidth;
  12764. var originX = shape.x;
  12765. var originY = shape.y;
  12766. var originWidth = shape.width;
  12767. var originHeight = shape.height;
  12768. shape.x = subPixelOptimize(shape.x, lineWidth, true);
  12769. shape.y = subPixelOptimize(shape.y, lineWidth, true);
  12770. shape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x, originWidth === 0 ? 0 : 1);
  12771. shape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y, originHeight === 0 ? 0 : 1);
  12772. return param;
  12773. }
  12774. /**
  12775. * Sub pixel optimize for canvas
  12776. *
  12777. * @param {number} position Coordinate, such as x, y
  12778. * @param {number} lineWidth Should be nonnegative integer.
  12779. * @param {boolean=} positiveOrNegative Default false (negative).
  12780. * @return {number} Optimized position.
  12781. */
  12782. function subPixelOptimize(position, lineWidth, positiveOrNegative) {
  12783. // Assure that (position + lineWidth / 2) is near integer edge,
  12784. // otherwise line will be fuzzy in canvas.
  12785. var doubledPosition = round$1(position * 2);
  12786. return (doubledPosition + round$1(lineWidth)) % 2 === 0 ? doubledPosition / 2 : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
  12787. }
  12788. function hasFillOrStroke(fillOrStroke) {
  12789. return fillOrStroke != null && fillOrStroke != 'none';
  12790. }
  12791. function liftColor(color) {
  12792. return typeof color === 'string' ? lift(color, -0.1) : color;
  12793. }
  12794. /**
  12795. * @private
  12796. */
  12797. function cacheElementStl(el) {
  12798. if (el.__hoverStlDirty) {
  12799. var stroke = el.style.stroke;
  12800. var fill = el.style.fill; // Create hoverStyle on mouseover
  12801. var hoverStyle = el.__hoverStl;
  12802. hoverStyle.fill = hoverStyle.fill || (hasFillOrStroke(fill) ? liftColor(fill) : null);
  12803. hoverStyle.stroke = hoverStyle.stroke || (hasFillOrStroke(stroke) ? liftColor(stroke) : null);
  12804. var normalStyle = {};
  12805. for (var name in hoverStyle) {
  12806. // See comment in `doSingleEnterHover`.
  12807. if (hoverStyle[name] != null) {
  12808. normalStyle[name] = el.style[name];
  12809. }
  12810. }
  12811. el.__normalStl = normalStyle;
  12812. el.__hoverStlDirty = false;
  12813. }
  12814. }
  12815. /**
  12816. * @private
  12817. */
  12818. function doSingleEnterHover(el) {
  12819. if (el.__isHover) {
  12820. return;
  12821. }
  12822. cacheElementStl(el);
  12823. if (el.useHoverLayer) {
  12824. el.__zr && el.__zr.addHover(el, el.__hoverStl);
  12825. } else {
  12826. var style = el.style;
  12827. var insideRollbackOpt = style.insideRollbackOpt; // Consider case: only `position: 'top'` is set on emphasis, then text
  12828. // color should be returned to `autoColor`, rather than remain '#fff'.
  12829. // So we should rollback then apply again after style merging.
  12830. insideRollbackOpt && rollbackInsideStyle(style); // styles can be:
  12831. // {
  12832. // label: {
  12833. // normal: {
  12834. // show: false,
  12835. // position: 'outside',
  12836. // fontSize: 18
  12837. // },
  12838. // emphasis: {
  12839. // show: true
  12840. // }
  12841. // }
  12842. // },
  12843. // where properties of `emphasis` may not appear in `normal`. We previously use
  12844. // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.
  12845. // But consider rich text and setOption in merge mode, it is impossible to cover
  12846. // all properties in merge. So we use merge mode when setting style here, where
  12847. // only properties that is not `null/undefined` can be set. The disadventage:
  12848. // null/undefined can not be used to remove style any more in `emphasis`.
  12849. style.extendFrom(el.__hoverStl); // Do not save `insideRollback`.
  12850. if (insideRollbackOpt) {
  12851. applyInsideStyle(style, style.insideOriginalTextPosition, insideRollbackOpt); // textFill may be rollbacked to null.
  12852. if (style.textFill == null) {
  12853. style.textFill = insideRollbackOpt.autoColor;
  12854. }
  12855. }
  12856. el.dirty(false);
  12857. el.z2 += 1;
  12858. }
  12859. el.__isHover = true;
  12860. }
  12861. /**
  12862. * @inner
  12863. */
  12864. function doSingleLeaveHover(el) {
  12865. if (!el.__isHover) {
  12866. return;
  12867. }
  12868. var normalStl = el.__normalStl;
  12869. if (el.useHoverLayer) {
  12870. el.__zr && el.__zr.removeHover(el);
  12871. } else {
  12872. // Consider null/undefined value, should use
  12873. // `setStyle` but not `extendFrom(stl, true)`.
  12874. normalStl && el.setStyle(normalStl);
  12875. el.z2 -= 1;
  12876. }
  12877. el.__isHover = false;
  12878. }
  12879. /**
  12880. * @inner
  12881. */
  12882. function doEnterHover(el) {
  12883. el.type === 'group' ? el.traverse(function (child) {
  12884. if (child.type !== 'group') {
  12885. doSingleEnterHover(child);
  12886. }
  12887. }) : doSingleEnterHover(el);
  12888. }
  12889. function doLeaveHover(el) {
  12890. el.type === 'group' ? el.traverse(function (child) {
  12891. if (child.type !== 'group') {
  12892. doSingleLeaveHover(child);
  12893. }
  12894. }) : doSingleLeaveHover(el);
  12895. }
  12896. /**
  12897. * @inner
  12898. */
  12899. function setElementHoverStl(el, hoverStl) {
  12900. // If element has sepcified hoverStyle, then use it instead of given hoverStyle
  12901. // Often used when item group has a label element and it's hoverStyle is different
  12902. el.__hoverStl = el.hoverStyle || hoverStl || {};
  12903. el.__hoverStlDirty = true;
  12904. if (el.__isHover) {
  12905. cacheElementStl(el);
  12906. }
  12907. }
  12908. /**
  12909. * @inner
  12910. */
  12911. function onElementMouseOver(e) {
  12912. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  12913. return;
  12914. } // Only if element is not in emphasis status
  12915. !this.__isEmphasis && doEnterHover(this);
  12916. }
  12917. /**
  12918. * @inner
  12919. */
  12920. function onElementMouseOut(e) {
  12921. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  12922. return;
  12923. } // Only if element is not in emphasis status
  12924. !this.__isEmphasis && doLeaveHover(this);
  12925. }
  12926. /**
  12927. * @inner
  12928. */
  12929. function enterEmphasis() {
  12930. this.__isEmphasis = true;
  12931. doEnterHover(this);
  12932. }
  12933. /**
  12934. * @inner
  12935. */
  12936. function leaveEmphasis() {
  12937. this.__isEmphasis = false;
  12938. doLeaveHover(this);
  12939. }
  12940. /**
  12941. * Set hover style of element.
  12942. * This method can be called repeatly without side-effects.
  12943. * @param {module:zrender/Element} el
  12944. * @param {Object} [hoverStyle]
  12945. * @param {Object} [opt]
  12946. * @param {boolean} [opt.hoverSilentOnTouch=false]
  12947. * In touch device, mouseover event will be trigger on touchstart event
  12948. * (see module:zrender/dom/HandlerProxy). By this mechanism, we can
  12949. * conviniently use hoverStyle when tap on touch screen without additional
  12950. * code for compatibility.
  12951. * But if the chart/component has select feature, which usually also use
  12952. * hoverStyle, there might be conflict between 'select-highlight' and
  12953. * 'hover-highlight' especially when roam is enabled (see geo for example).
  12954. * In this case, hoverSilentOnTouch should be used to disable hover-highlight
  12955. * on touch device.
  12956. */
  12957. function setHoverStyle(el, hoverStyle, opt) {
  12958. el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch;
  12959. el.type === 'group' ? el.traverse(function (child) {
  12960. if (child.type !== 'group') {
  12961. setElementHoverStl(child, hoverStyle);
  12962. }
  12963. }) : setElementHoverStl(el, hoverStyle); // Duplicated function will be auto-ignored, see Eventful.js.
  12964. el.on('mouseover', onElementMouseOver).on('mouseout', onElementMouseOut); // Emphasis, normal can be triggered manually
  12965. el.on('emphasis', enterEmphasis).on('normal', leaveEmphasis);
  12966. }
  12967. /**
  12968. * @param {Object|module:zrender/graphic/Style} normalStyle
  12969. * @param {Object} emphasisStyle
  12970. * @param {module:echarts/model/Model} normalModel
  12971. * @param {module:echarts/model/Model} emphasisModel
  12972. * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.
  12973. * @param {Object} [opt.defaultText]
  12974. * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by
  12975. * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  12976. * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by
  12977. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  12978. * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by
  12979. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  12980. * @param {Object} [normalSpecified]
  12981. * @param {Object} [emphasisSpecified]
  12982. */
  12983. function setLabelStyle(normalStyle, emphasisStyle, normalModel, emphasisModel, opt, normalSpecified, emphasisSpecified) {
  12984. opt = opt || EMPTY_OBJ;
  12985. var labelFetcher = opt.labelFetcher;
  12986. var labelDataIndex = opt.labelDataIndex;
  12987. var labelDimIndex = opt.labelDimIndex; // This scenario, `label.normal.show = true; label.emphasis.show = false`,
  12988. // is not supported util someone requests.
  12989. var showNormal = normalModel.getShallow('show');
  12990. var showEmphasis = emphasisModel.getShallow('show'); // Consider performance, only fetch label when necessary.
  12991. // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,
  12992. // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.
  12993. var baseText = showNormal || showEmphasis ? retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex) : null, opt.defaultText) : null;
  12994. var normalStyleText = showNormal ? baseText : null;
  12995. var emphasisStyleText = showEmphasis ? retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex) : null, baseText) : null; // Optimize: If style.text is null, text will not be drawn.
  12996. if (normalStyleText != null || emphasisStyleText != null) {
  12997. // Always set `textStyle` even if `normalStyle.text` is null, because default
  12998. // values have to be set on `normalStyle`.
  12999. // If we set default values on `emphasisStyle`, consider case:
  13000. // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`
  13001. // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`
  13002. // Then the 'red' will not work on emphasis.
  13003. setTextStyle(normalStyle, normalModel, normalSpecified, opt);
  13004. setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);
  13005. }
  13006. normalStyle.text = normalStyleText;
  13007. emphasisStyle.text = emphasisStyleText;
  13008. }
  13009. /**
  13010. * Set basic textStyle properties.
  13011. * @param {Object|module:zrender/graphic/Style} textStyle
  13012. * @param {module:echarts/model/Model} model
  13013. * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.
  13014. * @param {Object} [opt] See `opt` of `setTextStyleCommon`.
  13015. * @param {boolean} [isEmphasis]
  13016. */
  13017. function setTextStyle(textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis) {
  13018. setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);
  13019. specifiedTextStyle && extend(textStyle, specifiedTextStyle);
  13020. textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  13021. return textStyle;
  13022. }
  13023. /**
  13024. * Set text option in the style.
  13025. * @deprecated
  13026. * @param {Object} textStyle
  13027. * @param {module:echarts/model/Model} labelModel
  13028. * @param {string|boolean} defaultColor Default text color.
  13029. * If set as false, it will be processed as a emphasis style.
  13030. */
  13031. function setText(textStyle, labelModel, defaultColor) {
  13032. var opt = {
  13033. isRectText: true
  13034. };
  13035. var isEmphasis;
  13036. if (defaultColor === false) {
  13037. isEmphasis = true;
  13038. } else {
  13039. // Support setting color as 'auto' to get visual color.
  13040. opt.autoColor = defaultColor;
  13041. }
  13042. setTextStyleCommon(textStyle, labelModel, opt, isEmphasis);
  13043. textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  13044. }
  13045. /**
  13046. * {
  13047. * disableBox: boolean, Whether diable drawing box of block (outer most).
  13048. * isRectText: boolean,
  13049. * autoColor: string, specify a color when color is 'auto',
  13050. * for textFill, textStroke, textBackgroundColor, and textBorderColor.
  13051. * If autoColor specified, it is used as default textFill.
  13052. * useInsideStyle:
  13053. * `true`: Use inside style (textFill, textStroke, textStrokeWidth)
  13054. * if `textFill` is not specified.
  13055. * `false`: Do not use inside style.
  13056. * `null/undefined`: use inside style if `isRectText` is true and
  13057. * `textFill` is not specified and textPosition contains `'inside'`.
  13058. * forceRich: boolean
  13059. * }
  13060. */
  13061. function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {
  13062. // Consider there will be abnormal when merge hover style to normal style if given default value.
  13063. opt = opt || EMPTY_OBJ;
  13064. if (opt.isRectText) {
  13065. var textPosition = textStyleModel.getShallow('position') || (isEmphasis ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used
  13066. // in bar series, and magric type should be considered.
  13067. textPosition === 'outside' && (textPosition = 'top');
  13068. textStyle.textPosition = textPosition;
  13069. textStyle.textOffset = textStyleModel.getShallow('offset');
  13070. var labelRotate = textStyleModel.getShallow('rotate');
  13071. labelRotate != null && (labelRotate *= Math.PI / 180);
  13072. textStyle.textRotation = labelRotate;
  13073. textStyle.textDistance = retrieve2(textStyleModel.getShallow('distance'), isEmphasis ? null : 5);
  13074. }
  13075. var ecModel = textStyleModel.ecModel;
  13076. var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:
  13077. // {
  13078. // data: [{
  13079. // value: 12,
  13080. // label: {
  13081. // normal: {
  13082. // rich: {
  13083. // // no 'a' here but using parent 'a'.
  13084. // }
  13085. // }
  13086. // }
  13087. // }],
  13088. // rich: {
  13089. // a: { ... }
  13090. // }
  13091. // }
  13092. var richItemNames = getRichItemNames(textStyleModel);
  13093. var richResult;
  13094. if (richItemNames) {
  13095. richResult = {};
  13096. for (var name in richItemNames) {
  13097. if (richItemNames.hasOwnProperty(name)) {
  13098. // Cascade is supported in rich.
  13099. var richTextStyle = textStyleModel.getModel(['rich', name]); // In rich, never `disableBox`.
  13100. setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);
  13101. }
  13102. }
  13103. }
  13104. textStyle.rich = richResult;
  13105. setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);
  13106. if (opt.forceRich && !opt.textStyle) {
  13107. opt.textStyle = {};
  13108. }
  13109. return textStyle;
  13110. } // Consider case:
  13111. // {
  13112. // data: [{
  13113. // value: 12,
  13114. // label: {
  13115. // normal: {
  13116. // rich: {
  13117. // // no 'a' here but using parent 'a'.
  13118. // }
  13119. // }
  13120. // }
  13121. // }],
  13122. // rich: {
  13123. // a: { ... }
  13124. // }
  13125. // }
  13126. function getRichItemNames(textStyleModel) {
  13127. // Use object to remove duplicated names.
  13128. var richItemNameMap;
  13129. while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
  13130. var rich = (textStyleModel.option || EMPTY_OBJ).rich;
  13131. if (rich) {
  13132. richItemNameMap = richItemNameMap || {};
  13133. for (var name in rich) {
  13134. if (rich.hasOwnProperty(name)) {
  13135. richItemNameMap[name] = 1;
  13136. }
  13137. }
  13138. }
  13139. textStyleModel = textStyleModel.parentModel;
  13140. }
  13141. return richItemNameMap;
  13142. }
  13143. function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {
  13144. // In merge mode, default value should not be given.
  13145. globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
  13146. textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt) || globalTextStyle.color;
  13147. textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt) || globalTextStyle.textBorderColor;
  13148. textStyle.textStrokeWidth = retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth);
  13149. if (!isEmphasis) {
  13150. if (isBlock) {
  13151. // Always set `insideRollback`, for clearing previous.
  13152. var originalTextPosition = textStyle.textPosition;
  13153. textStyle.insideRollback = applyInsideStyle(textStyle, originalTextPosition, opt); // Save original textPosition, because style.textPosition will be repalced by
  13154. // real location (like [10, 30]) in zrender.
  13155. textStyle.insideOriginalTextPosition = originalTextPosition;
  13156. textStyle.insideRollbackOpt = opt;
  13157. } // Set default finally.
  13158. if (textStyle.textFill == null) {
  13159. textStyle.textFill = opt.autoColor;
  13160. }
  13161. } // Do not use `getFont` here, because merge should be supported, where
  13162. // part of these properties may be changed in emphasis style, and the
  13163. // others should remain their original value got from normal style.
  13164. textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;
  13165. textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;
  13166. textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;
  13167. textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;
  13168. textStyle.textAlign = textStyleModel.getShallow('align');
  13169. textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign') || textStyleModel.getShallow('baseline');
  13170. textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');
  13171. textStyle.textWidth = textStyleModel.getShallow('width');
  13172. textStyle.textHeight = textStyleModel.getShallow('height');
  13173. textStyle.textTag = textStyleModel.getShallow('tag');
  13174. if (!isBlock || !opt.disableBox) {
  13175. textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);
  13176. textStyle.textPadding = textStyleModel.getShallow('padding');
  13177. textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);
  13178. textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');
  13179. textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');
  13180. textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');
  13181. textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');
  13182. textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');
  13183. textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');
  13184. }
  13185. textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor') || globalTextStyle.textShadowColor;
  13186. textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur') || globalTextStyle.textShadowBlur;
  13187. textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX') || globalTextStyle.textShadowOffsetX;
  13188. textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY') || globalTextStyle.textShadowOffsetY;
  13189. }
  13190. function getAutoColor(color, opt) {
  13191. return color !== 'auto' ? color : opt && opt.autoColor ? opt.autoColor : null;
  13192. }
  13193. function applyInsideStyle(textStyle, textPosition, opt) {
  13194. var useInsideStyle = opt.useInsideStyle;
  13195. var insideRollback;
  13196. if (textStyle.textFill == null && useInsideStyle !== false && (useInsideStyle === true || opt.isRectText && textPosition // textPosition can be [10, 30]
  13197. && typeof textPosition === 'string' && textPosition.indexOf('inside') >= 0)) {
  13198. insideRollback = {
  13199. textFill: null,
  13200. textStroke: textStyle.textStroke,
  13201. textStrokeWidth: textStyle.textStrokeWidth
  13202. };
  13203. textStyle.textFill = '#fff'; // Consider text with #fff overflow its container.
  13204. if (textStyle.textStroke == null) {
  13205. textStyle.textStroke = opt.autoColor;
  13206. textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);
  13207. }
  13208. }
  13209. return insideRollback;
  13210. }
  13211. function rollbackInsideStyle(style) {
  13212. var insideRollback = style.insideRollback;
  13213. if (insideRollback) {
  13214. style.textFill = insideRollback.textFill;
  13215. style.textStroke = insideRollback.textStroke;
  13216. style.textStrokeWidth = insideRollback.textStrokeWidth;
  13217. }
  13218. }
  13219. function getFont(opt, ecModel) {
  13220. // ecModel or default text style model.
  13221. var gTextStyleModel = ecModel || ecModel.getModel('textStyle');
  13222. return [// FIXME in node-canvas fontWeight is before fontStyle
  13223. opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' ');
  13224. }
  13225. function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {
  13226. if (typeof dataIndex === 'function') {
  13227. cb = dataIndex;
  13228. dataIndex = null;
  13229. } // Do not check 'animation' property directly here. Consider this case:
  13230. // animation model is an `itemModel`, whose does not have `isAnimationEnabled`
  13231. // but its parent model (`seriesModel`) does.
  13232. var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
  13233. if (animationEnabled) {
  13234. var postfix = isUpdate ? 'Update' : '';
  13235. var duration = animatableModel.getShallow('animationDuration' + postfix);
  13236. var animationEasing = animatableModel.getShallow('animationEasing' + postfix);
  13237. var animationDelay = animatableModel.getShallow('animationDelay' + postfix);
  13238. if (typeof animationDelay === 'function') {
  13239. animationDelay = animationDelay(dataIndex, animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);
  13240. }
  13241. if (typeof duration === 'function') {
  13242. duration = duration(dataIndex);
  13243. }
  13244. duration > 0 ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb) : (el.stopAnimation(), el.attr(props), cb && cb());
  13245. } else {
  13246. el.stopAnimation();
  13247. el.attr(props);
  13248. cb && cb();
  13249. }
  13250. }
  13251. /**
  13252. * Update graphic element properties with or without animation according to the
  13253. * configuration in series.
  13254. *
  13255. * Caution: this method will stop previous animation.
  13256. * So if do not use this method to one element twice before
  13257. * animation starts, unless you know what you are doing.
  13258. *
  13259. * @param {module:zrender/Element} el
  13260. * @param {Object} props
  13261. * @param {module:echarts/model/Model} [animatableModel]
  13262. * @param {number} [dataIndex]
  13263. * @param {Function} [cb]
  13264. * @example
  13265. * graphic.updateProps(el, {
  13266. * position: [100, 100]
  13267. * }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
  13268. * // Or
  13269. * graphic.updateProps(el, {
  13270. * position: [100, 100]
  13271. * }, seriesModel, function () { console.log('Animation done!'); });
  13272. */
  13273. function updateProps(el, props, animatableModel, dataIndex, cb) {
  13274. animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
  13275. }
  13276. /**
  13277. * Init graphic element properties with or without animation according to the
  13278. * configuration in series.
  13279. *
  13280. * Caution: this method will stop previous animation.
  13281. * So if do not use this method to one element twice before
  13282. * animation starts, unless you know what you are doing.
  13283. *
  13284. * @param {module:zrender/Element} el
  13285. * @param {Object} props
  13286. * @param {module:echarts/model/Model} [animatableModel]
  13287. * @param {number} [dataIndex]
  13288. * @param {Function} cb
  13289. */
  13290. function initProps(el, props, animatableModel, dataIndex, cb) {
  13291. animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
  13292. }
  13293. /**
  13294. * Get transform matrix of target (param target),
  13295. * in coordinate of its ancestor (param ancestor)
  13296. *
  13297. * @param {module:zrender/mixin/Transformable} target
  13298. * @param {module:zrender/mixin/Transformable} [ancestor]
  13299. */
  13300. function getTransform(target, ancestor) {
  13301. var mat = identity([]);
  13302. while (target && target !== ancestor) {
  13303. mul$1(mat, target.getLocalTransform(), mat);
  13304. target = target.parent;
  13305. }
  13306. return mat;
  13307. }
  13308. /**
  13309. * Apply transform to an vertex.
  13310. * @param {Array.<number>} target [x, y]
  13311. * @param {Array.<number>|TypedArray.<number>|Object} transform Can be:
  13312. * + Transform matrix: like [1, 0, 0, 1, 0, 0]
  13313. * + {position, rotation, scale}, the same as `zrender/Transformable`.
  13314. * @param {boolean=} invert Whether use invert matrix.
  13315. * @return {Array.<number>} [x, y]
  13316. */
  13317. function applyTransform$1(target, transform, invert$$1) {
  13318. if (transform && !isArrayLike(transform)) {
  13319. transform = Transformable.getLocalTransform(transform);
  13320. }
  13321. if (invert$$1) {
  13322. transform = invert([], transform);
  13323. }
  13324. return applyTransform([], target, transform);
  13325. }
  13326. /**
  13327. * @param {string} direction 'left' 'right' 'top' 'bottom'
  13328. * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]
  13329. * @param {boolean=} invert Whether use invert matrix.
  13330. * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'
  13331. */
  13332. function transformDirection(direction, transform, invert$$1) {
  13333. // Pick a base, ensure that transform result will not be (0, 0).
  13334. var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]);
  13335. var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]);
  13336. var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0];
  13337. vertex = applyTransform$1(vertex, transform, invert$$1);
  13338. return Math.abs(vertex[0]) > Math.abs(vertex[1]) ? vertex[0] > 0 ? 'right' : 'left' : vertex[1] > 0 ? 'bottom' : 'top';
  13339. }
  13340. /**
  13341. * Apply group transition animation from g1 to g2.
  13342. * If no animatableModel, no animation.
  13343. */
  13344. function groupTransition(g1, g2, animatableModel, cb) {
  13345. if (!g1 || !g2) {
  13346. return;
  13347. }
  13348. function getElMap(g) {
  13349. var elMap = {};
  13350. g.traverse(function (el) {
  13351. if (!el.isGroup && el.anid) {
  13352. elMap[el.anid] = el;
  13353. }
  13354. });
  13355. return elMap;
  13356. }
  13357. function getAnimatableProps(el) {
  13358. var obj = {
  13359. position: clone$1(el.position),
  13360. rotation: el.rotation
  13361. };
  13362. if (el.shape) {
  13363. obj.shape = extend({}, el.shape);
  13364. }
  13365. return obj;
  13366. }
  13367. var elMap1 = getElMap(g1);
  13368. g2.traverse(function (el) {
  13369. if (!el.isGroup && el.anid) {
  13370. var oldEl = elMap1[el.anid];
  13371. if (oldEl) {
  13372. var newProp = getAnimatableProps(el);
  13373. el.attr(getAnimatableProps(oldEl));
  13374. updateProps(el, newProp, animatableModel, el.dataIndex);
  13375. } // else {
  13376. // if (el.previousProps) {
  13377. // graphic.updateProps
  13378. // }
  13379. // }
  13380. }
  13381. });
  13382. }
  13383. /**
  13384. * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]
  13385. * @param {Object} rect {x, y, width, height}
  13386. * @return {Array.<Array.<number>>} A new clipped points.
  13387. */
  13388. function clipPointsByRect(points, rect) {
  13389. return map(points, function (point) {
  13390. var x = point[0];
  13391. x = mathMax$1(x, rect.x);
  13392. x = mathMin$1(x, rect.x + rect.width);
  13393. var y = point[1];
  13394. y = mathMax$1(y, rect.y);
  13395. y = mathMin$1(y, rect.y + rect.height);
  13396. return [x, y];
  13397. });
  13398. }
  13399. /**
  13400. * @param {Object} targetRect {x, y, width, height}
  13401. * @param {Object} rect {x, y, width, height}
  13402. * @return {Object} A new clipped rect. If rect size are negative, return undefined.
  13403. */
  13404. function clipRectByRect(targetRect, rect) {
  13405. var x = mathMax$1(targetRect.x, rect.x);
  13406. var x2 = mathMin$1(targetRect.x + targetRect.width, rect.x + rect.width);
  13407. var y = mathMax$1(targetRect.y, rect.y);
  13408. var y2 = mathMin$1(targetRect.y + targetRect.height, rect.y + rect.height);
  13409. if (x2 >= x && y2 >= y) {
  13410. return {
  13411. x: x,
  13412. y: y,
  13413. width: x2 - x,
  13414. height: y2 - y
  13415. };
  13416. }
  13417. }
  13418. /**
  13419. * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.
  13420. * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.
  13421. * @param {Object} [rect] {x, y, width, height}
  13422. * @return {module:zrender/Element} Icon path or image element.
  13423. */
  13424. function createIcon(iconStr, opt, rect) {
  13425. opt = extend({
  13426. rectHover: true
  13427. }, opt);
  13428. var style = opt.style = {
  13429. strokeNoScale: true
  13430. };
  13431. rect = rect || {
  13432. x: -1,
  13433. y: -1,
  13434. width: 2,
  13435. height: 2
  13436. };
  13437. if (iconStr) {
  13438. return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), defaults(style, rect), new ZImage(opt)) : makePath(iconStr.replace('path://', ''), opt, rect, 'center');
  13439. }
  13440. }
  13441. var graphic = (Object.freeze || Object)({
  13442. extendShape: extendShape,
  13443. extendPath: extendPath,
  13444. makePath: makePath,
  13445. makeImage: makeImage,
  13446. mergePath: mergePath,
  13447. resizePath: resizePath,
  13448. subPixelOptimizeLine: subPixelOptimizeLine,
  13449. subPixelOptimizeRect: subPixelOptimizeRect,
  13450. subPixelOptimize: subPixelOptimize,
  13451. setHoverStyle: setHoverStyle,
  13452. setLabelStyle: setLabelStyle,
  13453. setTextStyle: setTextStyle,
  13454. setText: setText,
  13455. getFont: getFont,
  13456. updateProps: updateProps,
  13457. initProps: initProps,
  13458. getTransform: getTransform,
  13459. applyTransform: applyTransform$1,
  13460. transformDirection: transformDirection,
  13461. groupTransition: groupTransition,
  13462. clipPointsByRect: clipPointsByRect,
  13463. clipRectByRect: clipRectByRect,
  13464. createIcon: createIcon,
  13465. Group: Group,
  13466. Image: ZImage,
  13467. Text: Text,
  13468. Circle: Circle,
  13469. Sector: Sector,
  13470. Ring: Ring,
  13471. Polygon: Polygon,
  13472. Polyline: Polyline,
  13473. Rect: Rect,
  13474. Line: Line,
  13475. BezierCurve: BezierCurve,
  13476. Arc: Arc,
  13477. CompoundPath: CompoundPath,
  13478. LinearGradient: LinearGradient,
  13479. RadialGradient: RadialGradient,
  13480. BoundingRect: BoundingRect
  13481. });
  13482. var PATH_COLOR = ['textStyle', 'color'];
  13483. var textStyleMixin = {
  13484. /**
  13485. * Get color property or get color from option.textStyle.color
  13486. * @param {boolean} [isEmphasis]
  13487. * @return {string}
  13488. */
  13489. getTextColor: function (isEmphasis) {
  13490. var ecModel = this.ecModel;
  13491. return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null);
  13492. },
  13493. /**
  13494. * Create font string from fontStyle, fontWeight, fontSize, fontFamily
  13495. * @return {string}
  13496. */
  13497. getFont: function () {
  13498. return getFont({
  13499. fontStyle: this.getShallow('fontStyle'),
  13500. fontWeight: this.getShallow('fontWeight'),
  13501. fontSize: this.getShallow('fontSize'),
  13502. fontFamily: this.getShallow('fontFamily')
  13503. }, this.ecModel);
  13504. },
  13505. getTextRect: function (text) {
  13506. return getBoundingRect(text, this.getFont(), this.getShallow('align'), this.getShallow('verticalAlign') || this.getShallow('baseline'), this.getShallow('padding'), this.getShallow('rich'), this.getShallow('truncateText'));
  13507. }
  13508. };
  13509. var getItemStyle = makeStyleMapper([['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['textPosition'], ['textAlign']]);
  13510. var itemStyleMixin = {
  13511. getItemStyle: function (excludes, includes) {
  13512. var style = getItemStyle(this, excludes, includes);
  13513. var lineDash = this.getBorderLineDash();
  13514. lineDash && (style.lineDash = lineDash);
  13515. return style;
  13516. },
  13517. getBorderLineDash: function () {
  13518. var lineType = this.get('borderType');
  13519. return lineType === 'solid' || lineType == null ? null : lineType === 'dashed' ? [5, 5] : [1, 1];
  13520. }
  13521. };
  13522. /**
  13523. * @module echarts/model/Model
  13524. */
  13525. var mixin$1 = mixin;
  13526. /**
  13527. * @alias module:echarts/model/Model
  13528. * @constructor
  13529. * @param {Object} option
  13530. * @param {module:echarts/model/Model} [parentModel]
  13531. * @param {module:echarts/model/Global} [ecModel]
  13532. */
  13533. function Model(option, parentModel, ecModel) {
  13534. /**
  13535. * @type {module:echarts/model/Model}
  13536. * @readOnly
  13537. */
  13538. this.parentModel = parentModel;
  13539. /**
  13540. * @type {module:echarts/model/Global}
  13541. * @readOnly
  13542. */
  13543. this.ecModel = ecModel;
  13544. /**
  13545. * @type {Object}
  13546. * @protected
  13547. */
  13548. this.option = option; // Simple optimization
  13549. // if (this.init) {
  13550. // if (arguments.length <= 4) {
  13551. // this.init(option, parentModel, ecModel, extraOpt);
  13552. // }
  13553. // else {
  13554. // this.init.apply(this, arguments);
  13555. // }
  13556. // }
  13557. }
  13558. Model.prototype = {
  13559. constructor: Model,
  13560. /**
  13561. * Model 的初始化函数
  13562. * @param {Object} option
  13563. */
  13564. init: null,
  13565. /**
  13566. * 从新的 Option merge
  13567. */
  13568. mergeOption: function (option) {
  13569. merge(this.option, option, true);
  13570. },
  13571. /**
  13572. * @param {string|Array.<string>} path
  13573. * @param {boolean} [ignoreParent=false]
  13574. * @return {*}
  13575. */
  13576. get: function (path, ignoreParent) {
  13577. if (path == null) {
  13578. return this.option;
  13579. }
  13580. return doGet(this.option, this.parsePath(path), !ignoreParent && getParent(this, path));
  13581. },
  13582. /**
  13583. * @param {string} key
  13584. * @param {boolean} [ignoreParent=false]
  13585. * @return {*}
  13586. */
  13587. getShallow: function (key, ignoreParent) {
  13588. var option = this.option;
  13589. var val = option == null ? option : option[key];
  13590. var parentModel = !ignoreParent && getParent(this, key);
  13591. if (val == null && parentModel) {
  13592. val = parentModel.getShallow(key);
  13593. }
  13594. return val;
  13595. },
  13596. /**
  13597. * @param {string|Array.<string>} [path]
  13598. * @param {module:echarts/model/Model} [parentModel]
  13599. * @return {module:echarts/model/Model}
  13600. */
  13601. getModel: function (path, parentModel) {
  13602. var obj = path == null ? this.option : doGet(this.option, path = this.parsePath(path));
  13603. var thisParentModel;
  13604. parentModel = parentModel || (thisParentModel = getParent(this, path)) && thisParentModel.getModel(path);
  13605. return new Model(obj, parentModel, this.ecModel);
  13606. },
  13607. /**
  13608. * If model has option
  13609. */
  13610. isEmpty: function () {
  13611. return this.option == null;
  13612. },
  13613. restoreData: function () {},
  13614. // Pending
  13615. clone: function () {
  13616. var Ctor = this.constructor;
  13617. return new Ctor(clone(this.option));
  13618. },
  13619. setReadOnly: function (properties) {},
  13620. // If path is null/undefined, return null/undefined.
  13621. parsePath: function (path) {
  13622. if (typeof path === 'string') {
  13623. path = path.split('.');
  13624. }
  13625. return path;
  13626. },
  13627. /**
  13628. * @param {Function} getParentMethod
  13629. * param {Array.<string>|string} path
  13630. * return {module:echarts/model/Model}
  13631. */
  13632. customizeGetParent: function (getParentMethod) {
  13633. set$1(this, 'getParent', getParentMethod);
  13634. },
  13635. isAnimationEnabled: function () {
  13636. if (!env$1.node) {
  13637. if (this.option.animation != null) {
  13638. return !!this.option.animation;
  13639. } else if (this.parentModel) {
  13640. return this.parentModel.isAnimationEnabled();
  13641. }
  13642. }
  13643. }
  13644. };
  13645. function doGet(obj, pathArr, parentModel) {
  13646. for (var i = 0; i < pathArr.length; i++) {
  13647. // Ignore empty
  13648. if (!pathArr[i]) {
  13649. continue;
  13650. } // obj could be number/string/... (like 0)
  13651. obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null;
  13652. if (obj == null) {
  13653. break;
  13654. }
  13655. }
  13656. if (obj == null && parentModel) {
  13657. obj = parentModel.get(pathArr);
  13658. }
  13659. return obj;
  13660. } // `path` can be null/undefined
  13661. function getParent(model, path) {
  13662. var getParentMethod = get(model, 'getParent');
  13663. return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;
  13664. } // Enable Model.extend.
  13665. enableClassExtend(Model);
  13666. mixin$1(Model, lineStyleMixin);
  13667. mixin$1(Model, areaStyleMixin);
  13668. mixin$1(Model, textStyleMixin);
  13669. mixin$1(Model, itemStyleMixin);
  13670. var each$3 = each$1;
  13671. var isObject$2 = isObject;
  13672. /**
  13673. * If value is not array, then translate it to array.
  13674. * @param {*} value
  13675. * @return {Array} [value] or value
  13676. */
  13677. function normalizeToArray(value) {
  13678. return value instanceof Array ? value : value == null ? [] : [value];
  13679. }
  13680. /**
  13681. * Sync default option between normal and emphasis like `position` and `show`
  13682. * In case some one will write code like
  13683. * label: {
  13684. * normal: {
  13685. * show: false,
  13686. * position: 'outside',
  13687. * fontSize: 18
  13688. * },
  13689. * emphasis: {
  13690. * show: true
  13691. * }
  13692. * }
  13693. * @param {Object} opt
  13694. * @param {Array.<string>} subOpts
  13695. */
  13696. function defaultEmphasis(opt, subOpts) {
  13697. if (opt) {
  13698. var emphasisOpt = opt.emphasis = opt.emphasis || {};
  13699. var normalOpt = opt.normal = opt.normal || {}; // Default emphasis option from normal
  13700. for (var i = 0, len = subOpts.length; i < len; i++) {
  13701. var subOptName = subOpts[i];
  13702. if (!emphasisOpt.hasOwnProperty(subOptName) && normalOpt.hasOwnProperty(subOptName)) {
  13703. emphasisOpt[subOptName] = normalOpt[subOptName];
  13704. }
  13705. }
  13706. }
  13707. }
  13708. var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
  13709. // 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
  13710. // 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  13711. // // FIXME: deprecated, check and remove it.
  13712. // 'textStyle'
  13713. // ]);
  13714. /**
  13715. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  13716. * This helper method retieves value from data.
  13717. * @param {string|number|Date|Array|Object} dataItem
  13718. * @return {number|string|Date|Array.<number|string|Date>}
  13719. */
  13720. function getDataItemValue(dataItem) {
  13721. // Performance sensitive.
  13722. return dataItem && (dataItem.value == null ? dataItem : dataItem.value);
  13723. }
  13724. /**
  13725. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  13726. * This helper method determine if dataItem has extra option besides value
  13727. * @param {string|number|Date|Array|Object} dataItem
  13728. */
  13729. function isDataItemOption(dataItem) {
  13730. return isObject$2(dataItem) && !(dataItem instanceof Array); // // markLine data can be array
  13731. // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
  13732. }
  13733. /**
  13734. * This helper method convert value in data.
  13735. * @param {string|number|Date} value
  13736. * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
  13737. */
  13738. function converDataValue(value, dimInfo) {
  13739. // Performance sensitive.
  13740. var dimType = dimInfo && dimInfo.type;
  13741. if (dimType === 'ordinal') {
  13742. return value;
  13743. }
  13744. if (dimType === 'time' // spead up when using timestamp
  13745. && typeof value !== 'number' && value != null && value !== '-') {
  13746. value = +parseDate(value);
  13747. } // dimType defaults 'number'.
  13748. // If dimType is not ordinal and value is null or undefined or NaN or '-',
  13749. // parse to NaN.
  13750. return value == null || value === '' ? NaN : +value; // If string (like '-'), using '+' parse to NaN
  13751. }
  13752. /**
  13753. * Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data.
  13754. * @param {module:echarts/data/List} data
  13755. * @param {Object} opt
  13756. * @param {string} [opt.seriesIndex]
  13757. * @param {Object} [opt.name]
  13758. * @param {Object} [opt.mainType]
  13759. * @param {Object} [opt.subType]
  13760. */
  13761. // PENDING A little ugly
  13762. var dataFormatMixin = {
  13763. /**
  13764. * Get params for formatter
  13765. * @param {number} dataIndex
  13766. * @param {string} [dataType]
  13767. * @return {Object}
  13768. */
  13769. getDataParams: function (dataIndex, dataType) {
  13770. var data = this.getData(dataType);
  13771. var rawValue = this.getRawValue(dataIndex, dataType);
  13772. var rawDataIndex = data.getRawIndex(dataIndex);
  13773. var name = data.getName(dataIndex, true);
  13774. var itemOpt = data.getRawDataItem(dataIndex);
  13775. var color = data.getItemVisual(dataIndex, 'color');
  13776. return {
  13777. componentType: this.mainType,
  13778. componentSubType: this.subType,
  13779. seriesType: this.mainType === 'series' ? this.subType : null,
  13780. seriesIndex: this.seriesIndex,
  13781. seriesId: this.id,
  13782. seriesName: this.name,
  13783. name: name,
  13784. dataIndex: rawDataIndex,
  13785. data: itemOpt,
  13786. dataType: dataType,
  13787. value: rawValue,
  13788. color: color,
  13789. marker: getTooltipMarker(color),
  13790. // Param name list for mapping `a`, `b`, `c`, `d`, `e`
  13791. $vars: ['seriesName', 'name', 'value']
  13792. };
  13793. },
  13794. /**
  13795. * Format label
  13796. * @param {number} dataIndex
  13797. * @param {string} [status='normal'] 'normal' or 'emphasis'
  13798. * @param {string} [dataType]
  13799. * @param {number} [dimIndex]
  13800. * @param {string} [labelProp='label']
  13801. * @return {string}
  13802. */
  13803. getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) {
  13804. status = status || 'normal';
  13805. var data = this.getData(dataType);
  13806. var itemModel = data.getItemModel(dataIndex);
  13807. var params = this.getDataParams(dataIndex, dataType);
  13808. if (dimIndex != null && params.value instanceof Array) {
  13809. params.value = params.value[dimIndex];
  13810. }
  13811. var formatter = itemModel.get([labelProp || 'label', status, 'formatter']);
  13812. if (typeof formatter === 'function') {
  13813. params.status = status;
  13814. return formatter(params);
  13815. } else if (typeof formatter === 'string') {
  13816. return formatTpl(formatter, params);
  13817. }
  13818. },
  13819. /**
  13820. * Get raw value in option
  13821. * @param {number} idx
  13822. * @param {string} [dataType]
  13823. * @return {Object}
  13824. */
  13825. getRawValue: function (idx, dataType) {
  13826. var data = this.getData(dataType);
  13827. var dataItem = data.getRawDataItem(idx);
  13828. if (dataItem != null) {
  13829. return isObject$2(dataItem) && !(dataItem instanceof Array) ? dataItem.value : dataItem;
  13830. }
  13831. },
  13832. /**
  13833. * Should be implemented.
  13834. * @param {number} dataIndex
  13835. * @param {boolean} [multipleSeries=false]
  13836. * @param {number} [dataType]
  13837. * @return {string} tooltip string
  13838. */
  13839. formatTooltip: noop
  13840. };
  13841. /**
  13842. * Mapping to exists for merge.
  13843. *
  13844. * @public
  13845. * @param {Array.<Object>|Array.<module:echarts/model/Component>} exists
  13846. * @param {Object|Array.<Object>} newCptOptions
  13847. * @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  13848. * index of which is the same as exists.
  13849. */
  13850. function mappingToExists(exists, newCptOptions) {
  13851. // Mapping by the order by original option (but not order of
  13852. // new option) in merge mode. Because we should ensure
  13853. // some specified index (like xAxisIndex) is consistent with
  13854. // original option, which is easy to understand, espatially in
  13855. // media query. And in most case, merge option is used to
  13856. // update partial option but not be expected to change order.
  13857. newCptOptions = (newCptOptions || []).slice();
  13858. var result = map(exists || [], function (obj, index) {
  13859. return {
  13860. exist: obj
  13861. };
  13862. }); // Mapping by id or name if specified.
  13863. each$3(newCptOptions, function (cptOption, index) {
  13864. if (!isObject$2(cptOption)) {
  13865. return;
  13866. } // id has highest priority.
  13867. for (var i = 0; i < result.length; i++) {
  13868. if (!result[i].option // Consider name: two map to one.
  13869. && cptOption.id != null && result[i].exist.id === cptOption.id + '') {
  13870. result[i].option = cptOption;
  13871. newCptOptions[index] = null;
  13872. return;
  13873. }
  13874. }
  13875. for (var i = 0; i < result.length; i++) {
  13876. var exist = result[i].exist;
  13877. if (!result[i].option // Consider name: two map to one.
  13878. // Can not match when both ids exist but different.
  13879. && (exist.id == null || cptOption.id == null) && cptOption.name != null && !isIdInner(cptOption) && !isIdInner(exist) && exist.name === cptOption.name + '') {
  13880. result[i].option = cptOption;
  13881. newCptOptions[index] = null;
  13882. return;
  13883. }
  13884. }
  13885. }); // Otherwise mapping by index.
  13886. each$3(newCptOptions, function (cptOption, index) {
  13887. if (!isObject$2(cptOption)) {
  13888. return;
  13889. }
  13890. var i = 0;
  13891. for (; i < result.length; i++) {
  13892. var exist = result[i].exist;
  13893. if (!result[i].option // Existing model that already has id should be able to
  13894. // mapped to (because after mapping performed model may
  13895. // be assigned with a id, whish should not affect next
  13896. // mapping), except those has inner id.
  13897. && !isIdInner(exist) // Caution:
  13898. // Do not overwrite id. But name can be overwritten,
  13899. // because axis use name as 'show label text'.
  13900. // 'exist' always has id and name and we dont
  13901. // need to check it.
  13902. && cptOption.id == null) {
  13903. result[i].option = cptOption;
  13904. break;
  13905. }
  13906. }
  13907. if (i >= result.length) {
  13908. result.push({
  13909. option: cptOption
  13910. });
  13911. }
  13912. });
  13913. return result;
  13914. }
  13915. /**
  13916. * Make id and name for mapping result (result of mappingToExists)
  13917. * into `keyInfo` field.
  13918. *
  13919. * @public
  13920. * @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  13921. * which order is the same as exists.
  13922. * @return {Array.<Object>} The input.
  13923. */
  13924. function makeIdAndName(mapResult) {
  13925. // We use this id to hash component models and view instances
  13926. // in echarts. id can be specified by user, or auto generated.
  13927. // The id generation rule ensures new view instance are able
  13928. // to mapped to old instance when setOption are called in
  13929. // no-merge mode. So we generate model id by name and plus
  13930. // type in view id.
  13931. // name can be duplicated among components, which is convenient
  13932. // to specify multi components (like series) by one name.
  13933. // Ensure that each id is distinct.
  13934. var idMap = createHashMap();
  13935. each$3(mapResult, function (item, index) {
  13936. var existCpt = item.exist;
  13937. existCpt && idMap.set(existCpt.id, item);
  13938. });
  13939. each$3(mapResult, function (item, index) {
  13940. var opt = item.option;
  13941. assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));
  13942. opt && opt.id != null && idMap.set(opt.id, item);
  13943. !item.keyInfo && (item.keyInfo = {});
  13944. }); // Make name and id.
  13945. each$3(mapResult, function (item, index) {
  13946. var existCpt = item.exist;
  13947. var opt = item.option;
  13948. var keyInfo = item.keyInfo;
  13949. if (!isObject$2(opt)) {
  13950. return;
  13951. } // name can be overwitten. Consider case: axis.name = '20km'.
  13952. // But id generated by name will not be changed, which affect
  13953. // only in that case: setOption with 'not merge mode' and view
  13954. // instance will be recreated, which can be accepted.
  13955. keyInfo.name = opt.name != null ? opt.name + '' : existCpt ? existCpt.name : '\0-'; // name may be displayed on screen, so use '-'.
  13956. if (existCpt) {
  13957. keyInfo.id = existCpt.id;
  13958. } else if (opt.id != null) {
  13959. keyInfo.id = opt.id + '';
  13960. } else {
  13961. // Consider this situatoin:
  13962. // optionA: [{name: 'a'}, {name: 'a'}, {..}]
  13963. // optionB [{..}, {name: 'a'}, {name: 'a'}]
  13964. // Series with the same name between optionA and optionB
  13965. // should be mapped.
  13966. var idNum = 0;
  13967. do {
  13968. keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
  13969. } while (idMap.get(keyInfo.id));
  13970. }
  13971. idMap.set(keyInfo.id, item);
  13972. });
  13973. }
  13974. /**
  13975. * @public
  13976. * @param {Object} cptOption
  13977. * @return {boolean}
  13978. */
  13979. function isIdInner(cptOption) {
  13980. return isObject$2(cptOption) && cptOption.id && (cptOption.id + '').indexOf('\0_ec_\0') === 0;
  13981. }
  13982. /**
  13983. * A helper for removing duplicate items between batchA and batchB,
  13984. * and in themselves, and categorize by series.
  13985. *
  13986. * @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  13987. * @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  13988. * @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB]
  13989. */
  13990. /**
  13991. * @param {module:echarts/data/List} data
  13992. * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name
  13993. * each of which can be Array or primary type.
  13994. * @return {number|Array.<number>} dataIndex If not found, return undefined/null.
  13995. */
  13996. function queryDataIndex(data, payload) {
  13997. if (payload.dataIndexInside != null) {
  13998. return payload.dataIndexInside;
  13999. } else if (payload.dataIndex != null) {
  14000. return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) {
  14001. return data.indexOfRawIndex(value);
  14002. }) : data.indexOfRawIndex(payload.dataIndex);
  14003. } else if (payload.name != null) {
  14004. return isArray(payload.name) ? map(payload.name, function (value) {
  14005. return data.indexOfName(value);
  14006. }) : data.indexOfName(payload.name);
  14007. }
  14008. }
  14009. /**
  14010. * Enable property storage to any host object.
  14011. * Notice: Serialization is not supported.
  14012. *
  14013. * For example:
  14014. * var get = modelUitl.makeGetter();
  14015. *
  14016. * function some(hostObj) {
  14017. * get(hostObj)._someProperty = 1212;
  14018. * ...
  14019. * }
  14020. *
  14021. * @return {Function}
  14022. */
  14023. var makeGetter = function () {
  14024. var index = 0;
  14025. return function () {
  14026. var key = '\0__ec_prop_getter_' + index++;
  14027. return function (hostObj) {
  14028. return hostObj[key] || (hostObj[key] = {});
  14029. };
  14030. };
  14031. }();
  14032. /**
  14033. * @param {module:echarts/model/Global} ecModel
  14034. * @param {string|Object} finder
  14035. * If string, e.g., 'geo', means {geoIndex: 0}.
  14036. * If Object, could contain some of these properties below:
  14037. * {
  14038. * seriesIndex, seriesId, seriesName,
  14039. * geoIndex, geoId, geoName,
  14040. * bmapIndex, bmapId, bmapName,
  14041. * xAxisIndex, xAxisId, xAxisName,
  14042. * yAxisIndex, yAxisId, yAxisName,
  14043. * gridIndex, gridId, gridName,
  14044. * ... (can be extended)
  14045. * }
  14046. * Each properties can be number|string|Array.<number>|Array.<string>
  14047. * For example, a finder could be
  14048. * {
  14049. * seriesIndex: 3,
  14050. * geoId: ['aa', 'cc'],
  14051. * gridName: ['xx', 'rr']
  14052. * }
  14053. * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
  14054. * If nothing or null/undefined specified, return nothing.
  14055. * @param {Object} [opt]
  14056. * @param {string} [opt.defaultMainType]
  14057. * @param {Array.<string>} [opt.includeMainTypes]
  14058. * @return {Object} result like:
  14059. * {
  14060. * seriesModels: [seriesModel1, seriesModel2],
  14061. * seriesModel: seriesModel1, // The first model
  14062. * geoModels: [geoModel1, geoModel2],
  14063. * geoModel: geoModel1, // The first model
  14064. * ...
  14065. * }
  14066. */
  14067. function parseFinder(ecModel, finder, opt) {
  14068. if (isString(finder)) {
  14069. var obj = {};
  14070. obj[finder + 'Index'] = 0;
  14071. finder = obj;
  14072. }
  14073. var defaultMainType = opt && opt.defaultMainType;
  14074. if (defaultMainType && !has(finder, defaultMainType + 'Index') && !has(finder, defaultMainType + 'Id') && !has(finder, defaultMainType + 'Name')) {
  14075. finder[defaultMainType + 'Index'] = 0;
  14076. }
  14077. var result = {};
  14078. each$3(finder, function (value, key) {
  14079. var value = finder[key]; // Exclude 'dataIndex' and other illgal keys.
  14080. if (key === 'dataIndex' || key === 'dataIndexInside') {
  14081. result[key] = value;
  14082. return;
  14083. }
  14084. var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
  14085. var mainType = parsedKey[1];
  14086. var queryType = (parsedKey[2] || '').toLowerCase();
  14087. if (!mainType || !queryType || value == null || queryType === 'index' && value === 'none' || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) {
  14088. return;
  14089. }
  14090. var queryParam = {
  14091. mainType: mainType
  14092. };
  14093. if (queryType !== 'index' || value !== 'all') {
  14094. queryParam[queryType] = value;
  14095. }
  14096. var models = ecModel.queryComponents(queryParam);
  14097. result[mainType + 'Models'] = models;
  14098. result[mainType + 'Model'] = models[0];
  14099. });
  14100. return result;
  14101. }
  14102. /**
  14103. * @see {module:echarts/data/helper/completeDimensions}
  14104. * @param {module:echarts/data/List} data
  14105. * @param {string|number} dataDim
  14106. * @return {string}
  14107. */
  14108. function dataDimToCoordDim(data, dataDim) {
  14109. var dimensions = data.dimensions;
  14110. dataDim = data.getDimension(dataDim);
  14111. for (var i = 0; i < dimensions.length; i++) {
  14112. var dimItem = data.getDimensionInfo(dimensions[i]);
  14113. if (dimItem.name === dataDim) {
  14114. return dimItem.coordDim;
  14115. }
  14116. }
  14117. }
  14118. /**
  14119. * @see {module:echarts/data/helper/completeDimensions}
  14120. * @param {module:echarts/data/List} data
  14121. * @param {string} coordDim
  14122. * @return {Array.<string>} data dimensions on the coordDim.
  14123. */
  14124. function coordDimToDataDim(data, coordDim) {
  14125. var dataDim = [];
  14126. each$3(data.dimensions, function (dimName) {
  14127. var dimItem = data.getDimensionInfo(dimName);
  14128. if (dimItem.coordDim === coordDim) {
  14129. dataDim[dimItem.coordDimIndex] = dimItem.name;
  14130. }
  14131. });
  14132. return dataDim;
  14133. }
  14134. /**
  14135. * @see {module:echarts/data/helper/completeDimensions}
  14136. * @param {module:echarts/data/List} data
  14137. * @param {string} otherDim Can be `otherDims`
  14138. * like 'label' or 'tooltip'.
  14139. * @return {Array.<string>} data dimensions on the otherDim.
  14140. */
  14141. function otherDimToDataDim(data, otherDim) {
  14142. var dataDim = [];
  14143. each$3(data.dimensions, function (dimName) {
  14144. var dimItem = data.getDimensionInfo(dimName);
  14145. var otherDims = dimItem.otherDims;
  14146. var dimIndex = otherDims[otherDim];
  14147. if (dimIndex != null && dimIndex !== false) {
  14148. dataDim[dimIndex] = dimItem.name;
  14149. }
  14150. });
  14151. return dataDim;
  14152. }
  14153. function has(obj, prop) {
  14154. return obj && obj.hasOwnProperty(prop);
  14155. }
  14156. var base = 0;
  14157. var DELIMITER = '_';
  14158. /**
  14159. * @public
  14160. * @param {string} type
  14161. * @return {string}
  14162. */
  14163. function getUID(type) {
  14164. // Considering the case of crossing js context,
  14165. // use Math.random to make id as unique as possible.
  14166. return [type || '', base++, Math.random()].join(DELIMITER);
  14167. }
  14168. /**
  14169. * @inner
  14170. */
  14171. function enableSubTypeDefaulter(entity) {
  14172. var subTypeDefaulters = {};
  14173. entity.registerSubTypeDefaulter = function (componentType, defaulter) {
  14174. componentType = parseClassType$1(componentType);
  14175. subTypeDefaulters[componentType.main] = defaulter;
  14176. };
  14177. entity.determineSubType = function (componentType, option) {
  14178. var type = option.type;
  14179. if (!type) {
  14180. var componentTypeMain = parseClassType$1(componentType).main;
  14181. if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {
  14182. type = subTypeDefaulters[componentTypeMain](option);
  14183. }
  14184. }
  14185. return type;
  14186. };
  14187. return entity;
  14188. }
  14189. /**
  14190. * Topological travel on Activity Network (Activity On Vertices).
  14191. * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
  14192. *
  14193. * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
  14194. *
  14195. * If there is circle dependencey, Error will be thrown.
  14196. *
  14197. */
  14198. function enableTopologicalTravel(entity, dependencyGetter) {
  14199. /**
  14200. * @public
  14201. * @param {Array.<string>} targetNameList Target Component type list.
  14202. * Can be ['aa', 'bb', 'aa.xx']
  14203. * @param {Array.<string>} fullNameList By which we can build dependency graph.
  14204. * @param {Function} callback Params: componentType, dependencies.
  14205. * @param {Object} context Scope of callback.
  14206. */
  14207. entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
  14208. if (!targetNameList.length) {
  14209. return;
  14210. }
  14211. var result = makeDepndencyGraph(fullNameList);
  14212. var graph = result.graph;
  14213. var stack = result.noEntryList;
  14214. var targetNameSet = {};
  14215. each$1(targetNameList, function (name) {
  14216. targetNameSet[name] = true;
  14217. });
  14218. while (stack.length) {
  14219. var currComponentType = stack.pop();
  14220. var currVertex = graph[currComponentType];
  14221. var isInTargetNameSet = !!targetNameSet[currComponentType];
  14222. if (isInTargetNameSet) {
  14223. callback.call(context, currComponentType, currVertex.originalDeps.slice());
  14224. delete targetNameSet[currComponentType];
  14225. }
  14226. each$1(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge);
  14227. }
  14228. each$1(targetNameSet, function () {
  14229. throw new Error('Circle dependency may exists');
  14230. });
  14231. function removeEdge(succComponentType) {
  14232. graph[succComponentType].entryCount--;
  14233. if (graph[succComponentType].entryCount === 0) {
  14234. stack.push(succComponentType);
  14235. }
  14236. } // Consider this case: legend depends on series, and we call
  14237. // chart.setOption({series: [...]}), where only series is in option.
  14238. // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
  14239. // not be called, but only sereis.mergeOption is called. Thus legend
  14240. // have no chance to update its local record about series (like which
  14241. // name of series is available in legend).
  14242. function removeEdgeAndAdd(succComponentType) {
  14243. targetNameSet[succComponentType] = true;
  14244. removeEdge(succComponentType);
  14245. }
  14246. };
  14247. /**
  14248. * DepndencyGraph: {Object}
  14249. * key: conponentType,
  14250. * value: {
  14251. * successor: [conponentTypes...],
  14252. * originalDeps: [conponentTypes...],
  14253. * entryCount: {number}
  14254. * }
  14255. */
  14256. function makeDepndencyGraph(fullNameList) {
  14257. var graph = {};
  14258. var noEntryList = [];
  14259. each$1(fullNameList, function (name) {
  14260. var thisItem = createDependencyGraphItem(graph, name);
  14261. var originalDeps = thisItem.originalDeps = dependencyGetter(name);
  14262. var availableDeps = getAvailableDependencies(originalDeps, fullNameList);
  14263. thisItem.entryCount = availableDeps.length;
  14264. if (thisItem.entryCount === 0) {
  14265. noEntryList.push(name);
  14266. }
  14267. each$1(availableDeps, function (dependentName) {
  14268. if (indexOf(thisItem.predecessor, dependentName) < 0) {
  14269. thisItem.predecessor.push(dependentName);
  14270. }
  14271. var thatItem = createDependencyGraphItem(graph, dependentName);
  14272. if (indexOf(thatItem.successor, dependentName) < 0) {
  14273. thatItem.successor.push(name);
  14274. }
  14275. });
  14276. });
  14277. return {
  14278. graph: graph,
  14279. noEntryList: noEntryList
  14280. };
  14281. }
  14282. function createDependencyGraphItem(graph, name) {
  14283. if (!graph[name]) {
  14284. graph[name] = {
  14285. predecessor: [],
  14286. successor: []
  14287. };
  14288. }
  14289. return graph[name];
  14290. }
  14291. function getAvailableDependencies(originalDeps, fullNameList) {
  14292. var availableDeps = [];
  14293. each$1(originalDeps, function (dep) {
  14294. indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);
  14295. });
  14296. return availableDeps;
  14297. }
  14298. } // Layout helpers for each component positioning
  14299. var each$4 = each$1;
  14300. /**
  14301. * @public
  14302. */
  14303. var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height'];
  14304. /**
  14305. * @public
  14306. */
  14307. var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']];
  14308. function boxLayout(orient, group, gap, maxWidth, maxHeight) {
  14309. var x = 0;
  14310. var y = 0;
  14311. if (maxWidth == null) {
  14312. maxWidth = Infinity;
  14313. }
  14314. if (maxHeight == null) {
  14315. maxHeight = Infinity;
  14316. }
  14317. var currentLineMaxSize = 0;
  14318. group.eachChild(function (child, idx) {
  14319. var position = child.position;
  14320. var rect = child.getBoundingRect();
  14321. var nextChild = group.childAt(idx + 1);
  14322. var nextChildRect = nextChild && nextChild.getBoundingRect();
  14323. var nextX;
  14324. var nextY;
  14325. if (orient === 'horizontal') {
  14326. var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0);
  14327. nextX = x + moveX; // Wrap when width exceeds maxWidth or meet a `newline` group
  14328. // FIXME compare before adding gap?
  14329. if (nextX > maxWidth || child.newline) {
  14330. x = 0;
  14331. nextX = moveX;
  14332. y += currentLineMaxSize + gap;
  14333. currentLineMaxSize = rect.height;
  14334. } else {
  14335. // FIXME: consider rect.y is not `0`?
  14336. currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
  14337. }
  14338. } else {
  14339. var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0);
  14340. nextY = y + moveY; // Wrap when width exceeds maxHeight or meet a `newline` group
  14341. if (nextY > maxHeight || child.newline) {
  14342. x += currentLineMaxSize + gap;
  14343. y = 0;
  14344. nextY = moveY;
  14345. currentLineMaxSize = rect.width;
  14346. } else {
  14347. currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
  14348. }
  14349. }
  14350. if (child.newline) {
  14351. return;
  14352. }
  14353. position[0] = x;
  14354. position[1] = y;
  14355. orient === 'horizontal' ? x = nextX + gap : y = nextY + gap;
  14356. });
  14357. }
  14358. /**
  14359. * VBox or HBox layouting
  14360. * @param {string} orient
  14361. * @param {module:zrender/container/Group} group
  14362. * @param {number} gap
  14363. * @param {number} [width=Infinity]
  14364. * @param {number} [height=Infinity]
  14365. */
  14366. var box = boxLayout;
  14367. /**
  14368. * VBox layouting
  14369. * @param {module:zrender/container/Group} group
  14370. * @param {number} gap
  14371. * @param {number} [width=Infinity]
  14372. * @param {number} [height=Infinity]
  14373. */
  14374. var vbox = curry(boxLayout, 'vertical');
  14375. /**
  14376. * HBox layouting
  14377. * @param {module:zrender/container/Group} group
  14378. * @param {number} gap
  14379. * @param {number} [width=Infinity]
  14380. * @param {number} [height=Infinity]
  14381. */
  14382. var hbox = curry(boxLayout, 'horizontal');
  14383. /**
  14384. * If x or x2 is not specified or 'center' 'left' 'right',
  14385. * the width would be as long as possible.
  14386. * If y or y2 is not specified or 'middle' 'top' 'bottom',
  14387. * the height would be as long as possible.
  14388. *
  14389. * @param {Object} positionInfo
  14390. * @param {number|string} [positionInfo.x]
  14391. * @param {number|string} [positionInfo.y]
  14392. * @param {number|string} [positionInfo.x2]
  14393. * @param {number|string} [positionInfo.y2]
  14394. * @param {Object} containerRect {width, height}
  14395. * @param {string|number} margin
  14396. * @return {Object} {width, height}
  14397. */
  14398. /**
  14399. * Parse position info.
  14400. *
  14401. * @param {Object} positionInfo
  14402. * @param {number|string} [positionInfo.left]
  14403. * @param {number|string} [positionInfo.top]
  14404. * @param {number|string} [positionInfo.right]
  14405. * @param {number|string} [positionInfo.bottom]
  14406. * @param {number|string} [positionInfo.width]
  14407. * @param {number|string} [positionInfo.height]
  14408. * @param {number|string} [positionInfo.aspect] Aspect is width / height
  14409. * @param {Object} containerRect
  14410. * @param {string|number} [margin]
  14411. *
  14412. * @return {module:zrender/core/BoundingRect}
  14413. */
  14414. function getLayoutRect(positionInfo, containerRect, margin) {
  14415. margin = normalizeCssArray$1(margin || 0);
  14416. var containerWidth = containerRect.width;
  14417. var containerHeight = containerRect.height;
  14418. var left = parsePercent$1(positionInfo.left, containerWidth);
  14419. var top = parsePercent$1(positionInfo.top, containerHeight);
  14420. var right = parsePercent$1(positionInfo.right, containerWidth);
  14421. var bottom = parsePercent$1(positionInfo.bottom, containerHeight);
  14422. var width = parsePercent$1(positionInfo.width, containerWidth);
  14423. var height = parsePercent$1(positionInfo.height, containerHeight);
  14424. var verticalMargin = margin[2] + margin[0];
  14425. var horizontalMargin = margin[1] + margin[3];
  14426. var aspect = positionInfo.aspect; // If width is not specified, calculate width from left and right
  14427. if (isNaN(width)) {
  14428. width = containerWidth - right - horizontalMargin - left;
  14429. }
  14430. if (isNaN(height)) {
  14431. height = containerHeight - bottom - verticalMargin - top;
  14432. }
  14433. if (aspect != null) {
  14434. // If width and height are not given
  14435. // 1. Graph should not exceeds the container
  14436. // 2. Aspect must be keeped
  14437. // 3. Graph should take the space as more as possible
  14438. // FIXME
  14439. // Margin is not considered, because there is no case that both
  14440. // using margin and aspect so far.
  14441. if (isNaN(width) && isNaN(height)) {
  14442. if (aspect > containerWidth / containerHeight) {
  14443. width = containerWidth * 0.8;
  14444. } else {
  14445. height = containerHeight * 0.8;
  14446. }
  14447. } // Calculate width or height with given aspect
  14448. if (isNaN(width)) {
  14449. width = aspect * height;
  14450. }
  14451. if (isNaN(height)) {
  14452. height = width / aspect;
  14453. }
  14454. } // If left is not specified, calculate left from right and width
  14455. if (isNaN(left)) {
  14456. left = containerWidth - right - width - horizontalMargin;
  14457. }
  14458. if (isNaN(top)) {
  14459. top = containerHeight - bottom - height - verticalMargin;
  14460. } // Align left and top
  14461. switch (positionInfo.left || positionInfo.right) {
  14462. case 'center':
  14463. left = containerWidth / 2 - width / 2 - margin[3];
  14464. break;
  14465. case 'right':
  14466. left = containerWidth - width - horizontalMargin;
  14467. break;
  14468. }
  14469. switch (positionInfo.top || positionInfo.bottom) {
  14470. case 'middle':
  14471. case 'center':
  14472. top = containerHeight / 2 - height / 2 - margin[0];
  14473. break;
  14474. case 'bottom':
  14475. top = containerHeight - height - verticalMargin;
  14476. break;
  14477. } // If something is wrong and left, top, width, height are calculated as NaN
  14478. left = left || 0;
  14479. top = top || 0;
  14480. if (isNaN(width)) {
  14481. // Width may be NaN if only one value is given except width
  14482. width = containerWidth - horizontalMargin - left - (right || 0);
  14483. }
  14484. if (isNaN(height)) {
  14485. // Height may be NaN if only one value is given except height
  14486. height = containerHeight - verticalMargin - top - (bottom || 0);
  14487. }
  14488. var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);
  14489. rect.margin = margin;
  14490. return rect;
  14491. }
  14492. /**
  14493. * Position a zr element in viewport
  14494. * Group position is specified by either
  14495. * {left, top}, {right, bottom}
  14496. * If all properties exists, right and bottom will be igonred.
  14497. *
  14498. * Logic:
  14499. * 1. Scale (against origin point in parent coord)
  14500. * 2. Rotate (against origin point in parent coord)
  14501. * 3. Traslate (with el.position by this method)
  14502. * So this method only fixes the last step 'Traslate', which does not affect
  14503. * scaling and rotating.
  14504. *
  14505. * If be called repeatly with the same input el, the same result will be gotten.
  14506. *
  14507. * @param {module:zrender/Element} el Should have `getBoundingRect` method.
  14508. * @param {Object} positionInfo
  14509. * @param {number|string} [positionInfo.left]
  14510. * @param {number|string} [positionInfo.top]
  14511. * @param {number|string} [positionInfo.right]
  14512. * @param {number|string} [positionInfo.bottom]
  14513. * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw'
  14514. * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw'
  14515. * @param {Object} containerRect
  14516. * @param {string|number} margin
  14517. * @param {Object} [opt]
  14518. * @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical.
  14519. * @param {Array.<number>} [opt.boundingMode='all']
  14520. * Specify how to calculate boundingRect when locating.
  14521. * 'all': Position the boundingRect that is transformed and uioned
  14522. * both itself and its descendants.
  14523. * This mode simplies confine the elements in the bounding
  14524. * of their container (e.g., using 'right: 0').
  14525. * 'raw': Position the boundingRect that is not transformed and only itself.
  14526. * This mode is useful when you want a element can overflow its
  14527. * container. (Consider a rotated circle needs to be located in a corner.)
  14528. * In this mode positionInfo.width/height can only be number.
  14529. */
  14530. /**
  14531. * @param {Object} option Contains some of the properties in HV_NAMES.
  14532. * @param {number} hvIdx 0: horizontal; 1: vertical.
  14533. */
  14534. /**
  14535. * Consider Case:
  14536. * When defulat option has {left: 0, width: 100}, and we set {right: 0}
  14537. * through setOption or media query, using normal zrUtil.merge will cause
  14538. * {right: 0} does not take effect.
  14539. *
  14540. * @example
  14541. * ComponentModel.extend({
  14542. * init: function () {
  14543. * ...
  14544. * var inputPositionParams = layout.getLayoutParams(option);
  14545. * this.mergeOption(inputPositionParams);
  14546. * },
  14547. * mergeOption: function (newOption) {
  14548. * newOption && zrUtil.merge(thisOption, newOption, true);
  14549. * layout.mergeLayoutParam(thisOption, newOption);
  14550. * }
  14551. * });
  14552. *
  14553. * @param {Object} targetOption
  14554. * @param {Object} newOption
  14555. * @param {Object|string} [opt]
  14556. * @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Used for the components
  14557. * that width (or height) should not be calculated by left and right (or top and bottom).
  14558. */
  14559. function mergeLayoutParam(targetOption, newOption, opt) {
  14560. !isObject(opt) && (opt = {});
  14561. var ignoreSize = opt.ignoreSize;
  14562. !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
  14563. var hResult = merge$$1(HV_NAMES[0], 0);
  14564. var vResult = merge$$1(HV_NAMES[1], 1);
  14565. copy(HV_NAMES[0], targetOption, hResult);
  14566. copy(HV_NAMES[1], targetOption, vResult);
  14567. function merge$$1(names, hvIdx) {
  14568. var newParams = {};
  14569. var newValueCount = 0;
  14570. var merged = {};
  14571. var mergedValueCount = 0;
  14572. var enoughParamNumber = 2;
  14573. each$4(names, function (name) {
  14574. merged[name] = targetOption[name];
  14575. });
  14576. each$4(names, function (name) {
  14577. // Consider case: newOption.width is null, which is
  14578. // set by user for removing width setting.
  14579. hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
  14580. hasValue(newParams, name) && newValueCount++;
  14581. hasValue(merged, name) && mergedValueCount++;
  14582. });
  14583. if (ignoreSize[hvIdx]) {
  14584. // Only one of left/right is premitted to exist.
  14585. if (hasValue(newOption, names[1])) {
  14586. merged[names[2]] = null;
  14587. } else if (hasValue(newOption, names[2])) {
  14588. merged[names[1]] = null;
  14589. }
  14590. return merged;
  14591. } // Case: newOption: {width: ..., right: ...},
  14592. // or targetOption: {right: ...} and newOption: {width: ...},
  14593. // There is no conflict when merged only has params count
  14594. // little than enoughParamNumber.
  14595. if (mergedValueCount === enoughParamNumber || !newValueCount) {
  14596. return merged;
  14597. } // Case: newOption: {width: ..., right: ...},
  14598. // Than we can make sure user only want those two, and ignore
  14599. // all origin params in targetOption.
  14600. else if (newValueCount >= enoughParamNumber) {
  14601. return newParams;
  14602. } else {
  14603. // Chose another param from targetOption by priority.
  14604. for (var i = 0; i < names.length; i++) {
  14605. var name = names[i];
  14606. if (!hasProp(newParams, name) && hasProp(targetOption, name)) {
  14607. newParams[name] = targetOption[name];
  14608. break;
  14609. }
  14610. }
  14611. return newParams;
  14612. }
  14613. }
  14614. function hasProp(obj, name) {
  14615. return obj.hasOwnProperty(name);
  14616. }
  14617. function hasValue(obj, name) {
  14618. return obj[name] != null && obj[name] !== 'auto';
  14619. }
  14620. function copy(names, target, source) {
  14621. each$4(names, function (name) {
  14622. target[name] = source[name];
  14623. });
  14624. }
  14625. }
  14626. /**
  14627. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  14628. * @param {Object} source
  14629. * @return {Object} Result contains those props.
  14630. */
  14631. function getLayoutParams(source) {
  14632. return copyLayoutParams({}, source);
  14633. }
  14634. /**
  14635. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  14636. * @param {Object} source
  14637. * @return {Object} Result contains those props.
  14638. */
  14639. function copyLayoutParams(target, source) {
  14640. source && target && each$4(LOCATION_PARAMS, function (name) {
  14641. source.hasOwnProperty(name) && (target[name] = source[name]);
  14642. });
  14643. return target;
  14644. }
  14645. var boxLayoutMixin = {
  14646. getBoxLayoutParams: function () {
  14647. return {
  14648. left: this.get('left'),
  14649. top: this.get('top'),
  14650. right: this.get('right'),
  14651. bottom: this.get('bottom'),
  14652. width: this.get('width'),
  14653. height: this.get('height')
  14654. };
  14655. }
  14656. };
  14657. /**
  14658. * Component model
  14659. *
  14660. * @module echarts/model/Component
  14661. */
  14662. var arrayPush = Array.prototype.push;
  14663. /**
  14664. * @alias module:echarts/model/Component
  14665. * @constructor
  14666. * @param {Object} option
  14667. * @param {module:echarts/model/Model} parentModel
  14668. * @param {module:echarts/model/Model} ecModel
  14669. */
  14670. var ComponentModel = Model.extend({
  14671. type: 'component',
  14672. /**
  14673. * @readOnly
  14674. * @type {string}
  14675. */
  14676. id: '',
  14677. /**
  14678. * @readOnly
  14679. */
  14680. name: '',
  14681. /**
  14682. * @readOnly
  14683. * @type {string}
  14684. */
  14685. mainType: '',
  14686. /**
  14687. * @readOnly
  14688. * @type {string}
  14689. */
  14690. subType: '',
  14691. /**
  14692. * @readOnly
  14693. * @type {number}
  14694. */
  14695. componentIndex: 0,
  14696. /**
  14697. * @type {Object}
  14698. * @protected
  14699. */
  14700. defaultOption: null,
  14701. /**
  14702. * @type {module:echarts/model/Global}
  14703. * @readOnly
  14704. */
  14705. ecModel: null,
  14706. /**
  14707. * key: componentType
  14708. * value: Component model list, can not be null.
  14709. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  14710. * @readOnly
  14711. */
  14712. dependentModels: [],
  14713. /**
  14714. * @type {string}
  14715. * @readOnly
  14716. */
  14717. uid: null,
  14718. /**
  14719. * Support merge layout params.
  14720. * Only support 'box' now (left/right/top/bottom/width/height).
  14721. * @type {string|Object} Object can be {ignoreSize: true}
  14722. * @readOnly
  14723. */
  14724. layoutMode: null,
  14725. $constructor: function (option, parentModel, ecModel, extraOpt) {
  14726. Model.call(this, option, parentModel, ecModel, extraOpt);
  14727. this.uid = getUID('componentModel');
  14728. },
  14729. init: function (option, parentModel, ecModel, extraOpt) {
  14730. this.mergeDefaultAndTheme(option, ecModel);
  14731. },
  14732. mergeDefaultAndTheme: function (option, ecModel) {
  14733. var layoutMode = this.layoutMode;
  14734. var inputPositionParams = layoutMode ? getLayoutParams(option) : {};
  14735. var themeModel = ecModel.getTheme();
  14736. merge(option, themeModel.get(this.mainType));
  14737. merge(option, this.getDefaultOption());
  14738. if (layoutMode) {
  14739. mergeLayoutParam(option, inputPositionParams, layoutMode);
  14740. }
  14741. },
  14742. mergeOption: function (option, extraOpt) {
  14743. merge(this.option, option, true);
  14744. var layoutMode = this.layoutMode;
  14745. if (layoutMode) {
  14746. mergeLayoutParam(this.option, option, layoutMode);
  14747. }
  14748. },
  14749. // Hooker after init or mergeOption
  14750. optionUpdated: function (newCptOption, isInit) {},
  14751. getDefaultOption: function () {
  14752. if (!hasOwn(this, '__defaultOption')) {
  14753. var optList = [];
  14754. var Class = this.constructor;
  14755. while (Class) {
  14756. var opt = Class.prototype.defaultOption;
  14757. opt && optList.push(opt);
  14758. Class = Class.superClass;
  14759. }
  14760. var defaultOption = {};
  14761. for (var i = optList.length - 1; i >= 0; i--) {
  14762. defaultOption = merge(defaultOption, optList[i], true);
  14763. }
  14764. set$1(this, '__defaultOption', defaultOption);
  14765. }
  14766. return get(this, '__defaultOption');
  14767. },
  14768. getReferringComponents: function (mainType) {
  14769. return this.ecModel.queryComponents({
  14770. mainType: mainType,
  14771. index: this.get(mainType + 'Index', true),
  14772. id: this.get(mainType + 'Id', true)
  14773. });
  14774. }
  14775. }); // Reset ComponentModel.extend, add preConstruct.
  14776. // clazzUtil.enableClassExtend(
  14777. // ComponentModel,
  14778. // function (option, parentModel, ecModel, extraOpt) {
  14779. // // Set dependentModels, componentIndex, name, id, mainType, subType.
  14780. // zrUtil.extend(this, extraOpt);
  14781. // this.uid = componentUtil.getUID('componentModel');
  14782. // // this.setReadOnly([
  14783. // // 'type', 'id', 'uid', 'name', 'mainType', 'subType',
  14784. // // 'dependentModels', 'componentIndex'
  14785. // // ]);
  14786. // }
  14787. // );
  14788. // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  14789. enableClassManagement(ComponentModel, {
  14790. registerWhenExtend: true
  14791. });
  14792. enableSubTypeDefaulter(ComponentModel); // Add capability of ComponentModel.topologicalTravel.
  14793. enableTopologicalTravel(ComponentModel, getDependencies);
  14794. function getDependencies(componentType) {
  14795. var deps = [];
  14796. each$1(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
  14797. arrayPush.apply(deps, Clazz.prototype.dependencies || []);
  14798. }); // Ensure main type
  14799. return map(deps, function (type) {
  14800. return parseClassType$1(type).main;
  14801. });
  14802. }
  14803. mixin(ComponentModel, boxLayoutMixin);
  14804. var platform = ''; // Navigator not exists in node
  14805. if (typeof navigator !== 'undefined') {
  14806. platform = navigator.platform || '';
  14807. }
  14808. var globalDefault = {
  14809. // 全图默认背景
  14810. // backgroundColor: 'rgba(0,0,0,0)',
  14811. // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization
  14812. // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],
  14813. // 浅色
  14814. // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],
  14815. // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],
  14816. // 深色
  14817. color: ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'],
  14818. // 默认需要 Grid 配置项
  14819. // grid: {},
  14820. // 主题,主题
  14821. textStyle: {
  14822. // color: '#000',
  14823. // decoration: 'none',
  14824. // PENDING
  14825. fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',
  14826. // fontFamily: 'Arial, Verdana, sans-serif',
  14827. fontSize: 12,
  14828. fontStyle: 'normal',
  14829. fontWeight: 'normal'
  14830. },
  14831. // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/
  14832. // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  14833. // Default is source-over
  14834. blendMode: null,
  14835. animation: 'auto',
  14836. animationDuration: 1000,
  14837. animationDurationUpdate: 300,
  14838. animationEasing: 'exponentialOut',
  14839. animationEasingUpdate: 'cubicOut',
  14840. animationThreshold: 2000,
  14841. // Configuration for progressive/incremental rendering
  14842. progressiveThreshold: 3000,
  14843. progressive: 400,
  14844. // Threshold of if use single hover layer to optimize.
  14845. // It is recommended that `hoverLayerThreshold` is equivalent to or less than
  14846. // `progressiveThreshold`, otherwise hover will cause restart of progressive,
  14847. // which is unexpected.
  14848. // see example <echarts/test/heatmap-large.html>.
  14849. hoverLayerThreshold: 3000,
  14850. // See: module:echarts/scale/Time
  14851. useUTC: false
  14852. };
  14853. var colorPaletteMixin = {
  14854. clearColorPalette: function () {
  14855. set$1(this, 'colorIdx', 0);
  14856. set$1(this, 'colorNameMap', {});
  14857. },
  14858. getColorFromPalette: function (name, scope) {
  14859. scope = scope || this;
  14860. var colorIdx = get(scope, 'colorIdx') || 0;
  14861. var colorNameMap = get(scope, 'colorNameMap') || set$1(scope, 'colorNameMap', {}); // Use `hasOwnProperty` to avoid conflict with Object.prototype.
  14862. if (colorNameMap.hasOwnProperty(name)) {
  14863. return colorNameMap[name];
  14864. }
  14865. var colorPalette = this.get('color', true) || [];
  14866. if (!colorPalette.length) {
  14867. return;
  14868. }
  14869. var color = colorPalette[colorIdx];
  14870. if (name) {
  14871. colorNameMap[name] = color;
  14872. }
  14873. set$1(scope, 'colorIdx', (colorIdx + 1) % colorPalette.length);
  14874. return color;
  14875. }
  14876. };
  14877. /**
  14878. * ECharts global model
  14879. *
  14880. * @module {echarts/model/Global}
  14881. */
  14882. /**
  14883. * Caution: If the mechanism should be changed some day, these cases
  14884. * should be considered:
  14885. *
  14886. * (1) In `merge option` mode, if using the same option to call `setOption`
  14887. * many times, the result should be the same (try our best to ensure that).
  14888. * (2) In `merge option` mode, if a component has no id/name specified, it
  14889. * will be merged by index, and the result sequence of the components is
  14890. * consistent to the original sequence.
  14891. * (3) `reset` feature (in toolbox). Find detailed info in comments about
  14892. * `mergeOption` in module:echarts/model/OptionManager.
  14893. */
  14894. var each$2 = each$1;
  14895. var filter$1 = filter;
  14896. var map$1 = map;
  14897. var isArray$1 = isArray;
  14898. var indexOf$1 = indexOf;
  14899. var isObject$1 = isObject;
  14900. var OPTION_INNER_KEY = '\0_ec_inner';
  14901. /**
  14902. * @alias module:echarts/model/Global
  14903. *
  14904. * @param {Object} option
  14905. * @param {module:echarts/model/Model} parentModel
  14906. * @param {Object} theme
  14907. */
  14908. var GlobalModel = Model.extend({
  14909. constructor: GlobalModel,
  14910. init: function (option, parentModel, theme, optionManager) {
  14911. theme = theme || {};
  14912. this.option = null; // Mark as not initialized.
  14913. /**
  14914. * @type {module:echarts/model/Model}
  14915. * @private
  14916. */
  14917. this._theme = new Model(theme);
  14918. /**
  14919. * @type {module:echarts/model/OptionManager}
  14920. */
  14921. this._optionManager = optionManager;
  14922. },
  14923. setOption: function (option, optionPreprocessorFuncs) {
  14924. assert(!(OPTION_INNER_KEY in option), 'please use chart.getOption()');
  14925. this._optionManager.setOption(option, optionPreprocessorFuncs);
  14926. this.resetOption(null);
  14927. },
  14928. /**
  14929. * @param {string} type null/undefined: reset all.
  14930. * 'recreate': force recreate all.
  14931. * 'timeline': only reset timeline option
  14932. * 'media': only reset media query option
  14933. * @return {boolean} Whether option changed.
  14934. */
  14935. resetOption: function (type) {
  14936. var optionChanged = false;
  14937. var optionManager = this._optionManager;
  14938. if (!type || type === 'recreate') {
  14939. var baseOption = optionManager.mountOption(type === 'recreate');
  14940. if (!this.option || type === 'recreate') {
  14941. initBase.call(this, baseOption);
  14942. } else {
  14943. this.restoreData();
  14944. this.mergeOption(baseOption);
  14945. }
  14946. optionChanged = true;
  14947. }
  14948. if (type === 'timeline' || type === 'media') {
  14949. this.restoreData();
  14950. }
  14951. if (!type || type === 'recreate' || type === 'timeline') {
  14952. var timelineOption = optionManager.getTimelineOption(this);
  14953. timelineOption && (this.mergeOption(timelineOption), optionChanged = true);
  14954. }
  14955. if (!type || type === 'recreate' || type === 'media') {
  14956. var mediaOptions = optionManager.getMediaOption(this, this._api);
  14957. if (mediaOptions.length) {
  14958. each$2(mediaOptions, function (mediaOption) {
  14959. this.mergeOption(mediaOption, optionChanged = true);
  14960. }, this);
  14961. }
  14962. }
  14963. return optionChanged;
  14964. },
  14965. /**
  14966. * @protected
  14967. */
  14968. mergeOption: function (newOption) {
  14969. var option = this.option;
  14970. var componentsMap = this._componentsMap;
  14971. var newCptTypes = []; // 如果不存在对应的 component model 则直接 merge
  14972. each$2(newOption, function (componentOption, mainType) {
  14973. if (componentOption == null) {
  14974. return;
  14975. }
  14976. if (!ComponentModel.hasClass(mainType)) {
  14977. option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true);
  14978. } else {
  14979. newCptTypes.push(mainType);
  14980. }
  14981. }); // FIXME OPTION 同步是否要改回原来的
  14982. ComponentModel.topologicalTravel(newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this);
  14983. this._seriesIndices = this._seriesIndices || [];
  14984. function visitComponent(mainType, dependencies) {
  14985. var newCptOptionList = normalizeToArray(newOption[mainType]);
  14986. var mapResult = mappingToExists(componentsMap.get(mainType), newCptOptionList);
  14987. makeIdAndName(mapResult); // Set mainType and complete subType.
  14988. each$2(mapResult, function (item, index) {
  14989. var opt = item.option;
  14990. if (isObject$1(opt)) {
  14991. item.keyInfo.mainType = mainType;
  14992. item.keyInfo.subType = determineSubType(mainType, opt, item.exist);
  14993. }
  14994. });
  14995. var dependentModels = getComponentsByTypes(componentsMap, dependencies);
  14996. option[mainType] = [];
  14997. componentsMap.set(mainType, []);
  14998. each$2(mapResult, function (resultItem, index) {
  14999. var componentModel = resultItem.exist;
  15000. var newCptOption = resultItem.option;
  15001. assert(isObject$1(newCptOption) || componentModel, 'Empty component definition'); // Consider where is no new option and should be merged using {},
  15002. // see removeEdgeAndAdd in topologicalTravel and
  15003. // ComponentModel.getAllClassMainTypes.
  15004. if (!newCptOption) {
  15005. componentModel.mergeOption({}, this);
  15006. componentModel.optionUpdated({}, false);
  15007. } else {
  15008. var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, true);
  15009. if (componentModel && componentModel instanceof ComponentModelClass) {
  15010. componentModel.name = resultItem.keyInfo.name;
  15011. componentModel.mergeOption(newCptOption, this);
  15012. componentModel.optionUpdated(newCptOption, false);
  15013. } else {
  15014. // PENDING Global as parent ?
  15015. var extraOpt = extend({
  15016. dependentModels: dependentModels,
  15017. componentIndex: index
  15018. }, resultItem.keyInfo);
  15019. componentModel = new ComponentModelClass(newCptOption, this, this, extraOpt);
  15020. extend(componentModel, extraOpt);
  15021. componentModel.init(newCptOption, this, this, extraOpt); // Call optionUpdated after init.
  15022. // newCptOption has been used as componentModel.option
  15023. // and may be merged with theme and default, so pass null
  15024. // to avoid confusion.
  15025. componentModel.optionUpdated(null, true);
  15026. }
  15027. }
  15028. componentsMap.get(mainType)[index] = componentModel;
  15029. option[mainType][index] = componentModel.option;
  15030. }, this); // Backup series for filtering.
  15031. if (mainType === 'series') {
  15032. this._seriesIndices = createSeriesIndices(componentsMap.get('series'));
  15033. }
  15034. }
  15035. },
  15036. /**
  15037. * Get option for output (cloned option and inner info removed)
  15038. * @public
  15039. * @return {Object}
  15040. */
  15041. getOption: function () {
  15042. var option = clone(this.option);
  15043. each$2(option, function (opts, mainType) {
  15044. if (ComponentModel.hasClass(mainType)) {
  15045. var opts = normalizeToArray(opts);
  15046. for (var i = opts.length - 1; i >= 0; i--) {
  15047. // Remove options with inner id.
  15048. if (isIdInner(opts[i])) {
  15049. opts.splice(i, 1);
  15050. }
  15051. }
  15052. option[mainType] = opts;
  15053. }
  15054. });
  15055. delete option[OPTION_INNER_KEY];
  15056. return option;
  15057. },
  15058. /**
  15059. * @return {module:echarts/model/Model}
  15060. */
  15061. getTheme: function () {
  15062. return this._theme;
  15063. },
  15064. /**
  15065. * @param {string} mainType
  15066. * @param {number} [idx=0]
  15067. * @return {module:echarts/model/Component}
  15068. */
  15069. getComponent: function (mainType, idx) {
  15070. var list = this._componentsMap.get(mainType);
  15071. if (list) {
  15072. return list[idx || 0];
  15073. }
  15074. },
  15075. /**
  15076. * If none of index and id and name used, return all components with mainType.
  15077. * @param {Object} condition
  15078. * @param {string} condition.mainType
  15079. * @param {string} [condition.subType] If ignore, only query by mainType
  15080. * @param {number|Array.<number>} [condition.index] Either input index or id or name.
  15081. * @param {string|Array.<string>} [condition.id] Either input index or id or name.
  15082. * @param {string|Array.<string>} [condition.name] Either input index or id or name.
  15083. * @return {Array.<module:echarts/model/Component>}
  15084. */
  15085. queryComponents: function (condition) {
  15086. var mainType = condition.mainType;
  15087. if (!mainType) {
  15088. return [];
  15089. }
  15090. var index = condition.index;
  15091. var id = condition.id;
  15092. var name = condition.name;
  15093. var cpts = this._componentsMap.get(mainType);
  15094. if (!cpts || !cpts.length) {
  15095. return [];
  15096. }
  15097. var result;
  15098. if (index != null) {
  15099. if (!isArray$1(index)) {
  15100. index = [index];
  15101. }
  15102. result = filter$1(map$1(index, function (idx) {
  15103. return cpts[idx];
  15104. }), function (val) {
  15105. return !!val;
  15106. });
  15107. } else if (id != null) {
  15108. var isIdArray = isArray$1(id);
  15109. result = filter$1(cpts, function (cpt) {
  15110. return isIdArray && indexOf$1(id, cpt.id) >= 0 || !isIdArray && cpt.id === id;
  15111. });
  15112. } else if (name != null) {
  15113. var isNameArray = isArray$1(name);
  15114. result = filter$1(cpts, function (cpt) {
  15115. return isNameArray && indexOf$1(name, cpt.name) >= 0 || !isNameArray && cpt.name === name;
  15116. });
  15117. } else {
  15118. // Return all components with mainType
  15119. result = cpts.slice();
  15120. }
  15121. return filterBySubType(result, condition);
  15122. },
  15123. /**
  15124. * The interface is different from queryComponents,
  15125. * which is convenient for inner usage.
  15126. *
  15127. * @usage
  15128. * var result = findComponents(
  15129. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}
  15130. * );
  15131. * var result = findComponents(
  15132. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}
  15133. * );
  15134. * var result = findComponents(
  15135. * {mainType: 'series'},
  15136. * function (model, index) {...}
  15137. * );
  15138. * // result like [component0, componnet1, ...]
  15139. *
  15140. * @param {Object} condition
  15141. * @param {string} condition.mainType Mandatory.
  15142. * @param {string} [condition.subType] Optional.
  15143. * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},
  15144. * where xxx is mainType.
  15145. * If query attribute is null/undefined or has no index/id/name,
  15146. * do not filtering by query conditions, which is convenient for
  15147. * no-payload situations or when target of action is global.
  15148. * @param {Function} [condition.filter] parameter: component, return boolean.
  15149. * @return {Array.<module:echarts/model/Component>}
  15150. */
  15151. findComponents: function (condition) {
  15152. var query = condition.query;
  15153. var mainType = condition.mainType;
  15154. var queryCond = getQueryCond(query);
  15155. var result = queryCond ? this.queryComponents(queryCond) : this._componentsMap.get(mainType);
  15156. return doFilter(filterBySubType(result, condition));
  15157. function getQueryCond(q) {
  15158. var indexAttr = mainType + 'Index';
  15159. var idAttr = mainType + 'Id';
  15160. var nameAttr = mainType + 'Name';
  15161. return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? {
  15162. mainType: mainType,
  15163. // subType will be filtered finally.
  15164. index: q[indexAttr],
  15165. id: q[idAttr],
  15166. name: q[nameAttr]
  15167. } : null;
  15168. }
  15169. function doFilter(res) {
  15170. return condition.filter ? filter$1(res, condition.filter) : res;
  15171. }
  15172. },
  15173. /**
  15174. * @usage
  15175. * eachComponent('legend', function (legendModel, index) {
  15176. * ...
  15177. * });
  15178. * eachComponent(function (componentType, model, index) {
  15179. * // componentType does not include subType
  15180. * // (componentType is 'xxx' but not 'xxx.aa')
  15181. * });
  15182. * eachComponent(
  15183. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},
  15184. * function (model, index) {...}
  15185. * );
  15186. * eachComponent(
  15187. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},
  15188. * function (model, index) {...}
  15189. * );
  15190. *
  15191. * @param {string|Object=} mainType When mainType is object, the definition
  15192. * is the same as the method 'findComponents'.
  15193. * @param {Function} cb
  15194. * @param {*} context
  15195. */
  15196. eachComponent: function (mainType, cb, context) {
  15197. var componentsMap = this._componentsMap;
  15198. if (typeof mainType === 'function') {
  15199. context = cb;
  15200. cb = mainType;
  15201. componentsMap.each(function (components, componentType) {
  15202. each$2(components, function (component, index) {
  15203. cb.call(context, componentType, component, index);
  15204. });
  15205. });
  15206. } else if (isString(mainType)) {
  15207. each$2(componentsMap.get(mainType), cb, context);
  15208. } else if (isObject$1(mainType)) {
  15209. var queryResult = this.findComponents(mainType);
  15210. each$2(queryResult, cb, context);
  15211. }
  15212. },
  15213. /**
  15214. * @param {string} name
  15215. * @return {Array.<module:echarts/model/Series>}
  15216. */
  15217. getSeriesByName: function (name) {
  15218. var series = this._componentsMap.get('series');
  15219. return filter$1(series, function (oneSeries) {
  15220. return oneSeries.name === name;
  15221. });
  15222. },
  15223. /**
  15224. * @param {number} seriesIndex
  15225. * @return {module:echarts/model/Series}
  15226. */
  15227. getSeriesByIndex: function (seriesIndex) {
  15228. return this._componentsMap.get('series')[seriesIndex];
  15229. },
  15230. /**
  15231. * @param {string} subType
  15232. * @return {Array.<module:echarts/model/Series>}
  15233. */
  15234. getSeriesByType: function (subType) {
  15235. var series = this._componentsMap.get('series');
  15236. return filter$1(series, function (oneSeries) {
  15237. return oneSeries.subType === subType;
  15238. });
  15239. },
  15240. /**
  15241. * @return {Array.<module:echarts/model/Series>}
  15242. */
  15243. getSeries: function () {
  15244. return this._componentsMap.get('series').slice();
  15245. },
  15246. /**
  15247. * After filtering, series may be different
  15248. * frome raw series.
  15249. *
  15250. * @param {Function} cb
  15251. * @param {*} context
  15252. */
  15253. eachSeries: function (cb, context) {
  15254. assertSeriesInitialized(this);
  15255. each$2(this._seriesIndices, function (rawSeriesIndex) {
  15256. var series = this._componentsMap.get('series')[rawSeriesIndex];
  15257. cb.call(context, series, rawSeriesIndex);
  15258. }, this);
  15259. },
  15260. /**
  15261. * Iterate raw series before filtered.
  15262. *
  15263. * @param {Function} cb
  15264. * @param {*} context
  15265. */
  15266. eachRawSeries: function (cb, context) {
  15267. each$2(this._componentsMap.get('series'), cb, context);
  15268. },
  15269. /**
  15270. * After filtering, series may be different.
  15271. * frome raw series.
  15272. *
  15273. * @parma {string} subType
  15274. * @param {Function} cb
  15275. * @param {*} context
  15276. */
  15277. eachSeriesByType: function (subType, cb, context) {
  15278. assertSeriesInitialized(this);
  15279. each$2(this._seriesIndices, function (rawSeriesIndex) {
  15280. var series = this._componentsMap.get('series')[rawSeriesIndex];
  15281. if (series.subType === subType) {
  15282. cb.call(context, series, rawSeriesIndex);
  15283. }
  15284. }, this);
  15285. },
  15286. /**
  15287. * Iterate raw series before filtered of given type.
  15288. *
  15289. * @parma {string} subType
  15290. * @param {Function} cb
  15291. * @param {*} context
  15292. */
  15293. eachRawSeriesByType: function (subType, cb, context) {
  15294. return each$2(this.getSeriesByType(subType), cb, context);
  15295. },
  15296. /**
  15297. * @param {module:echarts/model/Series} seriesModel
  15298. */
  15299. isSeriesFiltered: function (seriesModel) {
  15300. assertSeriesInitialized(this);
  15301. return indexOf(this._seriesIndices, seriesModel.componentIndex) < 0;
  15302. },
  15303. /**
  15304. * @return {Array.<number>}
  15305. */
  15306. getCurrentSeriesIndices: function () {
  15307. return (this._seriesIndices || []).slice();
  15308. },
  15309. /**
  15310. * @param {Function} cb
  15311. * @param {*} context
  15312. */
  15313. filterSeries: function (cb, context) {
  15314. assertSeriesInitialized(this);
  15315. var filteredSeries = filter$1(this._componentsMap.get('series'), cb, context);
  15316. this._seriesIndices = createSeriesIndices(filteredSeries);
  15317. },
  15318. restoreData: function () {
  15319. var componentsMap = this._componentsMap;
  15320. this._seriesIndices = createSeriesIndices(componentsMap.get('series'));
  15321. var componentTypes = [];
  15322. componentsMap.each(function (components, componentType) {
  15323. componentTypes.push(componentType);
  15324. });
  15325. ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType, dependencies) {
  15326. each$2(componentsMap.get(componentType), function (component) {
  15327. component.restoreData();
  15328. });
  15329. });
  15330. }
  15331. });
  15332. /**
  15333. * @inner
  15334. */
  15335. function mergeTheme(option, theme) {
  15336. each$1(theme, function (themeItem, name) {
  15337. // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理
  15338. if (!ComponentModel.hasClass(name)) {
  15339. if (typeof themeItem === 'object') {
  15340. option[name] = !option[name] ? clone(themeItem) : merge(option[name], themeItem, false);
  15341. } else {
  15342. if (option[name] == null) {
  15343. option[name] = themeItem;
  15344. }
  15345. }
  15346. }
  15347. });
  15348. }
  15349. function initBase(baseOption) {
  15350. baseOption = baseOption; // Using OPTION_INNER_KEY to mark that this option can not be used outside,
  15351. // i.e. `chart.setOption(chart.getModel().option);` is forbiden.
  15352. this.option = {};
  15353. this.option[OPTION_INNER_KEY] = 1;
  15354. /**
  15355. * Init with series: [], in case of calling findSeries method
  15356. * before series initialized.
  15357. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  15358. * @private
  15359. */
  15360. this._componentsMap = createHashMap({
  15361. series: []
  15362. });
  15363. /**
  15364. * Mapping between filtered series list and raw series list.
  15365. * key: filtered series indices, value: raw series indices.
  15366. * @type {Array.<nubmer>}
  15367. * @private
  15368. */
  15369. this._seriesIndices = null;
  15370. mergeTheme(baseOption, this._theme.option); // TODO Needs clone when merging to the unexisted property
  15371. merge(baseOption, globalDefault, false);
  15372. this.mergeOption(baseOption);
  15373. }
  15374. /**
  15375. * @inner
  15376. * @param {Array.<string>|string} types model types
  15377. * @return {Object} key: {string} type, value: {Array.<Object>} models
  15378. */
  15379. function getComponentsByTypes(componentsMap, types) {
  15380. if (!isArray(types)) {
  15381. types = types ? [types] : [];
  15382. }
  15383. var ret = {};
  15384. each$2(types, function (type) {
  15385. ret[type] = (componentsMap.get(type) || []).slice();
  15386. });
  15387. return ret;
  15388. }
  15389. /**
  15390. * @inner
  15391. */
  15392. function determineSubType(mainType, newCptOption, existComponent) {
  15393. var subType = newCptOption.type ? newCptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.
  15394. : ComponentModel.determineSubType(mainType, newCptOption); // tooltip, markline, markpoint may always has no subType
  15395. return subType;
  15396. }
  15397. /**
  15398. * @inner
  15399. */
  15400. function createSeriesIndices(seriesModels) {
  15401. return map$1(seriesModels, function (series) {
  15402. return series.componentIndex;
  15403. }) || [];
  15404. }
  15405. /**
  15406. * @inner
  15407. */
  15408. function filterBySubType(components, condition) {
  15409. // Using hasOwnProperty for restrict. Consider
  15410. // subType is undefined in user payload.
  15411. return condition.hasOwnProperty('subType') ? filter$1(components, function (cpt) {
  15412. return cpt.subType === condition.subType;
  15413. }) : components;
  15414. }
  15415. /**
  15416. * @inner
  15417. */
  15418. function assertSeriesInitialized(ecModel) {
  15419. // Components that use _seriesIndices should depends on series component,
  15420. // which make sure that their initialization is after series.
  15421. if (true) {
  15422. if (!ecModel._seriesIndices) {
  15423. throw new Error('Option should contains series.');
  15424. }
  15425. }
  15426. }
  15427. mixin(GlobalModel, colorPaletteMixin);
  15428. var echartsAPIList = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption', 'getViewOfComponentModel', 'getViewOfSeriesModel']; // And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js
  15429. function ExtensionAPI(chartInstance) {
  15430. each$1(echartsAPIList, function (name) {
  15431. this[name] = bind(chartInstance[name], chartInstance);
  15432. }, this);
  15433. }
  15434. var coordinateSystemCreators = {};
  15435. function CoordinateSystemManager() {
  15436. this._coordinateSystems = [];
  15437. }
  15438. CoordinateSystemManager.prototype = {
  15439. constructor: CoordinateSystemManager,
  15440. create: function (ecModel, api) {
  15441. var coordinateSystems = [];
  15442. each$1(coordinateSystemCreators, function (creater, type) {
  15443. var list = creater.create(ecModel, api);
  15444. coordinateSystems = coordinateSystems.concat(list || []);
  15445. });
  15446. this._coordinateSystems = coordinateSystems;
  15447. },
  15448. update: function (ecModel, api) {
  15449. each$1(this._coordinateSystems, function (coordSys) {
  15450. // FIXME MUST have
  15451. coordSys.update && coordSys.update(ecModel, api);
  15452. });
  15453. },
  15454. getCoordinateSystems: function () {
  15455. return this._coordinateSystems.slice();
  15456. }
  15457. };
  15458. CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
  15459. coordinateSystemCreators[type] = coordinateSystemCreator;
  15460. };
  15461. CoordinateSystemManager.get = function (type) {
  15462. return coordinateSystemCreators[type];
  15463. };
  15464. /**
  15465. * ECharts option manager
  15466. *
  15467. * @module {echarts/model/OptionManager}
  15468. */
  15469. var each$5 = each$1;
  15470. var clone$2 = clone;
  15471. var map$2 = map;
  15472. var merge$1 = merge;
  15473. var QUERY_REG = /^(min|max)?(.+)$/;
  15474. /**
  15475. * TERM EXPLANATIONS:
  15476. *
  15477. * [option]:
  15478. *
  15479. * An object that contains definitions of components. For example:
  15480. * var option = {
  15481. * title: {...},
  15482. * legend: {...},
  15483. * visualMap: {...},
  15484. * series: [
  15485. * {data: [...]},
  15486. * {data: [...]},
  15487. * ...
  15488. * ]
  15489. * };
  15490. *
  15491. * [rawOption]:
  15492. *
  15493. * An object input to echarts.setOption. 'rawOption' may be an
  15494. * 'option', or may be an object contains multi-options. For example:
  15495. * var option = {
  15496. * baseOption: {
  15497. * title: {...},
  15498. * legend: {...},
  15499. * series: [
  15500. * {data: [...]},
  15501. * {data: [...]},
  15502. * ...
  15503. * ]
  15504. * },
  15505. * timeline: {...},
  15506. * options: [
  15507. * {title: {...}, series: {data: [...]}},
  15508. * {title: {...}, series: {data: [...]}},
  15509. * ...
  15510. * ],
  15511. * media: [
  15512. * {
  15513. * query: {maxWidth: 320},
  15514. * option: {series: {x: 20}, visualMap: {show: false}}
  15515. * },
  15516. * {
  15517. * query: {minWidth: 320, maxWidth: 720},
  15518. * option: {series: {x: 500}, visualMap: {show: true}}
  15519. * },
  15520. * {
  15521. * option: {series: {x: 1200}, visualMap: {show: true}}
  15522. * }
  15523. * ]
  15524. * };
  15525. *
  15526. * @alias module:echarts/model/OptionManager
  15527. * @param {module:echarts/ExtensionAPI} api
  15528. */
  15529. function OptionManager(api) {
  15530. /**
  15531. * @private
  15532. * @type {module:echarts/ExtensionAPI}
  15533. */
  15534. this._api = api;
  15535. /**
  15536. * @private
  15537. * @type {Array.<number>}
  15538. */
  15539. this._timelineOptions = [];
  15540. /**
  15541. * @private
  15542. * @type {Array.<Object>}
  15543. */
  15544. this._mediaList = [];
  15545. /**
  15546. * @private
  15547. * @type {Object}
  15548. */
  15549. this._mediaDefault;
  15550. /**
  15551. * -1, means default.
  15552. * empty means no media.
  15553. * @private
  15554. * @type {Array.<number>}
  15555. */
  15556. this._currentMediaIndices = [];
  15557. /**
  15558. * @private
  15559. * @type {Object}
  15560. */
  15561. this._optionBackup;
  15562. /**
  15563. * @private
  15564. * @type {Object}
  15565. */
  15566. this._newBaseOption;
  15567. } // timeline.notMerge is not supported in ec3. Firstly there is rearly
  15568. // case that notMerge is needed. Secondly supporting 'notMerge' requires
  15569. // rawOption cloned and backuped when timeline changed, which does no
  15570. // good to performance. What's more, that both timeline and setOption
  15571. // method supply 'notMerge' brings complex and some problems.
  15572. // Consider this case:
  15573. // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);
  15574. // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);
  15575. OptionManager.prototype = {
  15576. constructor: OptionManager,
  15577. /**
  15578. * @public
  15579. * @param {Object} rawOption Raw option.
  15580. * @param {module:echarts/model/Global} ecModel
  15581. * @param {Array.<Function>} optionPreprocessorFuncs
  15582. * @return {Object} Init option
  15583. */
  15584. setOption: function (rawOption, optionPreprocessorFuncs) {
  15585. rawOption = clone$2(rawOption, true); // FIXME
  15586. // 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。
  15587. var oldOptionBackup = this._optionBackup;
  15588. var newParsedOption = parseRawOption.call(this, rawOption, optionPreprocessorFuncs, !oldOptionBackup);
  15589. this._newBaseOption = newParsedOption.baseOption; // For setOption at second time (using merge mode);
  15590. if (oldOptionBackup) {
  15591. // Only baseOption can be merged.
  15592. mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption); // For simplicity, timeline options and media options do not support merge,
  15593. // that is, if you `setOption` twice and both has timeline options, the latter
  15594. // timeline opitons will not be merged to the formers, but just substitude them.
  15595. if (newParsedOption.timelineOptions.length) {
  15596. oldOptionBackup.timelineOptions = newParsedOption.timelineOptions;
  15597. }
  15598. if (newParsedOption.mediaList.length) {
  15599. oldOptionBackup.mediaList = newParsedOption.mediaList;
  15600. }
  15601. if (newParsedOption.mediaDefault) {
  15602. oldOptionBackup.mediaDefault = newParsedOption.mediaDefault;
  15603. }
  15604. } else {
  15605. this._optionBackup = newParsedOption;
  15606. }
  15607. },
  15608. /**
  15609. * @param {boolean} isRecreate
  15610. * @return {Object}
  15611. */
  15612. mountOption: function (isRecreate) {
  15613. var optionBackup = this._optionBackup; // TODO
  15614. // 如果没有reset功能则不clone。
  15615. this._timelineOptions = map$2(optionBackup.timelineOptions, clone$2);
  15616. this._mediaList = map$2(optionBackup.mediaList, clone$2);
  15617. this._mediaDefault = clone$2(optionBackup.mediaDefault);
  15618. this._currentMediaIndices = [];
  15619. return clone$2(isRecreate // this._optionBackup.baseOption, which is created at the first `setOption`
  15620. // called, and is merged into every new option by inner method `mergeOption`
  15621. // each time `setOption` called, can be only used in `isRecreate`, because
  15622. // its reliability is under suspicion. In other cases option merge is
  15623. // performed by `model.mergeOption`.
  15624. ? optionBackup.baseOption : this._newBaseOption);
  15625. },
  15626. /**
  15627. * @param {module:echarts/model/Global} ecModel
  15628. * @return {Object}
  15629. */
  15630. getTimelineOption: function (ecModel) {
  15631. var option;
  15632. var timelineOptions = this._timelineOptions;
  15633. if (timelineOptions.length) {
  15634. // getTimelineOption can only be called after ecModel inited,
  15635. // so we can get currentIndex from timelineModel.
  15636. var timelineModel = ecModel.getComponent('timeline');
  15637. if (timelineModel) {
  15638. option = clone$2(timelineOptions[timelineModel.getCurrentIndex()], true);
  15639. }
  15640. }
  15641. return option;
  15642. },
  15643. /**
  15644. * @param {module:echarts/model/Global} ecModel
  15645. * @return {Array.<Object>}
  15646. */
  15647. getMediaOption: function (ecModel) {
  15648. var ecWidth = this._api.getWidth();
  15649. var ecHeight = this._api.getHeight();
  15650. var mediaList = this._mediaList;
  15651. var mediaDefault = this._mediaDefault;
  15652. var indices = [];
  15653. var result = []; // No media defined.
  15654. if (!mediaList.length && !mediaDefault) {
  15655. return result;
  15656. } // Multi media may be applied, the latter defined media has higher priority.
  15657. for (var i = 0, len = mediaList.length; i < len; i++) {
  15658. if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {
  15659. indices.push(i);
  15660. }
  15661. } // FIXME
  15662. // 是否mediaDefault应该强制用户设置,否则可能修改不能回归。
  15663. if (!indices.length && mediaDefault) {
  15664. indices = [-1];
  15665. }
  15666. if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {
  15667. result = map$2(indices, function (index) {
  15668. return clone$2(index === -1 ? mediaDefault.option : mediaList[index].option);
  15669. });
  15670. } // Otherwise return nothing.
  15671. this._currentMediaIndices = indices;
  15672. return result;
  15673. }
  15674. };
  15675. function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {
  15676. var timelineOptions = [];
  15677. var mediaList = [];
  15678. var mediaDefault;
  15679. var baseOption; // Compatible with ec2.
  15680. var timelineOpt = rawOption.timeline;
  15681. if (rawOption.baseOption) {
  15682. baseOption = rawOption.baseOption;
  15683. } // For timeline
  15684. if (timelineOpt || rawOption.options) {
  15685. baseOption = baseOption || {};
  15686. timelineOptions = (rawOption.options || []).slice();
  15687. } // For media query
  15688. if (rawOption.media) {
  15689. baseOption = baseOption || {};
  15690. var media = rawOption.media;
  15691. each$5(media, function (singleMedia) {
  15692. if (singleMedia && singleMedia.option) {
  15693. if (singleMedia.query) {
  15694. mediaList.push(singleMedia);
  15695. } else if (!mediaDefault) {
  15696. // Use the first media default.
  15697. mediaDefault = singleMedia;
  15698. }
  15699. }
  15700. });
  15701. } // For normal option
  15702. if (!baseOption) {
  15703. baseOption = rawOption;
  15704. } // Set timelineOpt to baseOption in ec3,
  15705. // which is convenient for merge option.
  15706. if (!baseOption.timeline) {
  15707. baseOption.timeline = timelineOpt;
  15708. } // Preprocess.
  15709. each$5([baseOption].concat(timelineOptions).concat(map(mediaList, function (media) {
  15710. return media.option;
  15711. })), function (option) {
  15712. each$5(optionPreprocessorFuncs, function (preProcess) {
  15713. preProcess(option, isNew);
  15714. });
  15715. });
  15716. return {
  15717. baseOption: baseOption,
  15718. timelineOptions: timelineOptions,
  15719. mediaDefault: mediaDefault,
  15720. mediaList: mediaList
  15721. };
  15722. }
  15723. /**
  15724. * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>
  15725. * Support: width, height, aspectRatio
  15726. * Can use max or min as prefix.
  15727. */
  15728. function applyMediaQuery(query, ecWidth, ecHeight) {
  15729. var realMap = {
  15730. width: ecWidth,
  15731. height: ecHeight,
  15732. aspectratio: ecWidth / ecHeight // lowser case for convenientce.
  15733. };
  15734. var applicatable = true;
  15735. each$1(query, function (value, attr) {
  15736. var matched = attr.match(QUERY_REG);
  15737. if (!matched || !matched[1] || !matched[2]) {
  15738. return;
  15739. }
  15740. var operator = matched[1];
  15741. var realAttr = matched[2].toLowerCase();
  15742. if (!compare(realMap[realAttr], value, operator)) {
  15743. applicatable = false;
  15744. }
  15745. });
  15746. return applicatable;
  15747. }
  15748. function compare(real, expect, operator) {
  15749. if (operator === 'min') {
  15750. return real >= expect;
  15751. } else if (operator === 'max') {
  15752. return real <= expect;
  15753. } else {
  15754. // Equals
  15755. return real === expect;
  15756. }
  15757. }
  15758. function indicesEquals(indices1, indices2) {
  15759. // indices is always order by asc and has only finite number.
  15760. return indices1.join(',') === indices2.join(',');
  15761. }
  15762. /**
  15763. * Consider case:
  15764. * `chart.setOption(opt1);`
  15765. * Then user do some interaction like dataZoom, dataView changing.
  15766. * `chart.setOption(opt2);`
  15767. * Then user press 'reset button' in toolbox.
  15768. *
  15769. * After doing that all of the interaction effects should be reset, the
  15770. * chart should be the same as the result of invoke
  15771. * `chart.setOption(opt1); chart.setOption(opt2);`.
  15772. *
  15773. * Although it is not able ensure that
  15774. * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to
  15775. * `chart.setOption(merge(opt1, opt2));` exactly,
  15776. * this might be the only simple way to implement that feature.
  15777. *
  15778. * MEMO: We've considered some other approaches:
  15779. * 1. Each model handle its self restoration but not uniform treatment.
  15780. * (Too complex in logic and error-prone)
  15781. * 2. Use a shadow ecModel. (Performace expensive)
  15782. */
  15783. function mergeOption(oldOption, newOption) {
  15784. newOption = newOption || {};
  15785. each$5(newOption, function (newCptOpt, mainType) {
  15786. if (newCptOpt == null) {
  15787. return;
  15788. }
  15789. var oldCptOpt = oldOption[mainType];
  15790. if (!ComponentModel.hasClass(mainType)) {
  15791. oldOption[mainType] = merge$1(oldCptOpt, newCptOpt, true);
  15792. } else {
  15793. newCptOpt = normalizeToArray(newCptOpt);
  15794. oldCptOpt = normalizeToArray(oldCptOpt);
  15795. var mapResult = mappingToExists(oldCptOpt, newCptOpt);
  15796. oldOption[mainType] = map$2(mapResult, function (item) {
  15797. return item.option && item.exist ? merge$1(item.exist, item.option, true) : item.exist || item.option;
  15798. });
  15799. }
  15800. });
  15801. }
  15802. var each$6 = each$1;
  15803. var isObject$3 = isObject;
  15804. var POSSIBLE_STYLES = ['areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', 'chordStyle', 'label', 'labelLine'];
  15805. function compatItemStyle(opt) {
  15806. var itemStyleOpt = opt && opt.itemStyle;
  15807. if (!itemStyleOpt) {
  15808. return;
  15809. }
  15810. for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {
  15811. var styleName = POSSIBLE_STYLES[i];
  15812. var normalItemStyleOpt = itemStyleOpt.normal;
  15813. var emphasisItemStyleOpt = itemStyleOpt.emphasis;
  15814. if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {
  15815. opt[styleName] = opt[styleName] || {};
  15816. if (!opt[styleName].normal) {
  15817. opt[styleName].normal = normalItemStyleOpt[styleName];
  15818. } else {
  15819. merge(opt[styleName].normal, normalItemStyleOpt[styleName]);
  15820. }
  15821. normalItemStyleOpt[styleName] = null;
  15822. }
  15823. if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {
  15824. opt[styleName] = opt[styleName] || {};
  15825. if (!opt[styleName].emphasis) {
  15826. opt[styleName].emphasis = emphasisItemStyleOpt[styleName];
  15827. } else {
  15828. merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);
  15829. }
  15830. emphasisItemStyleOpt[styleName] = null;
  15831. }
  15832. }
  15833. }
  15834. function compatTextStyle(opt, propName) {
  15835. var labelOptSingle = isObject$3(opt) && opt[propName];
  15836. var textStyle = isObject$3(labelOptSingle) && labelOptSingle.textStyle;
  15837. if (textStyle) {
  15838. for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {
  15839. var propName = TEXT_STYLE_OPTIONS[i];
  15840. if (textStyle.hasOwnProperty(propName)) {
  15841. labelOptSingle[propName] = textStyle[propName];
  15842. }
  15843. }
  15844. }
  15845. }
  15846. function compatLabelTextStyle(labelOpt) {
  15847. if (isObject$3(labelOpt)) {
  15848. compatTextStyle(labelOpt, 'normal');
  15849. compatTextStyle(labelOpt, 'emphasis');
  15850. }
  15851. }
  15852. function processSeries(seriesOpt) {
  15853. if (!isObject$3(seriesOpt)) {
  15854. return;
  15855. }
  15856. compatItemStyle(seriesOpt);
  15857. compatLabelTextStyle(seriesOpt.label); // treemap
  15858. compatLabelTextStyle(seriesOpt.upperLabel); // graph
  15859. compatLabelTextStyle(seriesOpt.edgeLabel);
  15860. var markPoint = seriesOpt.markPoint;
  15861. compatItemStyle(markPoint);
  15862. compatLabelTextStyle(markPoint && markPoint.label);
  15863. var markLine = seriesOpt.markLine;
  15864. compatItemStyle(seriesOpt.markLine);
  15865. compatLabelTextStyle(markLine && markLine.label);
  15866. var markArea = seriesOpt.markArea;
  15867. compatLabelTextStyle(markArea && markArea.label); // For gauge
  15868. compatTextStyle(seriesOpt, 'axisLabel');
  15869. compatTextStyle(seriesOpt, 'title');
  15870. compatTextStyle(seriesOpt, 'detail');
  15871. var data = seriesOpt.data;
  15872. if (data) {
  15873. for (var i = 0; i < data.length; i++) {
  15874. compatItemStyle(data[i]);
  15875. compatLabelTextStyle(data[i] && data[i].label);
  15876. }
  15877. } // mark point data
  15878. var markPoint = seriesOpt.markPoint;
  15879. if (markPoint && markPoint.data) {
  15880. var mpData = markPoint.data;
  15881. for (var i = 0; i < mpData.length; i++) {
  15882. compatItemStyle(mpData[i]);
  15883. compatLabelTextStyle(mpData[i] && mpData[i].label);
  15884. }
  15885. } // mark line data
  15886. var markLine = seriesOpt.markLine;
  15887. if (markLine && markLine.data) {
  15888. var mlData = markLine.data;
  15889. for (var i = 0; i < mlData.length; i++) {
  15890. if (isArray(mlData[i])) {
  15891. compatItemStyle(mlData[i][0]);
  15892. compatLabelTextStyle(mlData[i][0] && mlData[i][0].label);
  15893. compatItemStyle(mlData[i][1]);
  15894. compatLabelTextStyle(mlData[i][1] && mlData[i][1].label);
  15895. } else {
  15896. compatItemStyle(mlData[i]);
  15897. compatLabelTextStyle(mlData[i] && mlData[i].label);
  15898. }
  15899. }
  15900. }
  15901. }
  15902. function toArr(o) {
  15903. return isArray(o) ? o : o ? [o] : [];
  15904. }
  15905. function toObj(o) {
  15906. return (isArray(o) ? o[0] : o) || {};
  15907. }
  15908. var compatStyle = function (option, isTheme) {
  15909. each$6(toArr(option.series), function (seriesOpt) {
  15910. isObject$3(seriesOpt) && processSeries(seriesOpt);
  15911. });
  15912. var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];
  15913. isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');
  15914. each$6(axes, function (axisName) {
  15915. each$6(toArr(option[axisName]), function (axisOpt) {
  15916. if (axisOpt) {
  15917. compatTextStyle(axisOpt, 'axisLabel');
  15918. compatTextStyle(axisOpt.axisPointer, 'label');
  15919. }
  15920. });
  15921. });
  15922. each$6(toArr(option.parallel), function (parallelOpt) {
  15923. var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;
  15924. compatTextStyle(parallelAxisDefault, 'axisLabel');
  15925. compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');
  15926. });
  15927. each$6(toArr(option.calendar), function (calendarOpt) {
  15928. compatTextStyle(calendarOpt, 'dayLabel');
  15929. compatTextStyle(calendarOpt, 'monthLabel');
  15930. compatTextStyle(calendarOpt, 'yearLabel');
  15931. }); // radar.name.textStyle
  15932. each$6(toArr(option.radar), function (radarOpt) {
  15933. compatTextStyle(radarOpt, 'name');
  15934. });
  15935. each$6(toArr(option.geo), function (geoOpt) {
  15936. if (isObject$3(geoOpt)) {
  15937. compatLabelTextStyle(geoOpt.label);
  15938. each$6(toArr(geoOpt.regions), function (regionObj) {
  15939. compatLabelTextStyle(regionObj.label);
  15940. });
  15941. }
  15942. });
  15943. compatLabelTextStyle(toObj(option.timeline).label);
  15944. compatTextStyle(toObj(option.axisPointer), 'label');
  15945. compatTextStyle(toObj(option.tooltip).axisPointer, 'label');
  15946. }; // Compatitable with 2.0
  15947. function get$1(opt, path) {
  15948. path = path.split(',');
  15949. var obj = opt;
  15950. for (var i = 0; i < path.length; i++) {
  15951. obj = obj && obj[path[i]];
  15952. if (obj == null) {
  15953. break;
  15954. }
  15955. }
  15956. return obj;
  15957. }
  15958. function set$2(opt, path, val, overwrite) {
  15959. path = path.split(',');
  15960. var obj = opt;
  15961. var key;
  15962. for (var i = 0; i < path.length - 1; i++) {
  15963. key = path[i];
  15964. if (obj[key] == null) {
  15965. obj[key] = {};
  15966. }
  15967. obj = obj[key];
  15968. }
  15969. if (overwrite || obj[path[i]] == null) {
  15970. obj[path[i]] = val;
  15971. }
  15972. }
  15973. function compatLayoutProperties(option) {
  15974. each$1(LAYOUT_PROPERTIES, function (prop) {
  15975. if (prop[0] in option && !(prop[1] in option)) {
  15976. option[prop[1]] = option[prop[0]];
  15977. }
  15978. });
  15979. }
  15980. var LAYOUT_PROPERTIES = [['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']];
  15981. var COMPATITABLE_COMPONENTS = ['grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'];
  15982. var COMPATITABLE_SERIES = ['bar', 'boxplot', 'candlestick', 'chord', 'effectScatter', 'funnel', 'gauge', 'lines', 'graph', 'heatmap', 'line', 'map', 'parallel', 'pie', 'radar', 'sankey', 'scatter', 'treemap'];
  15983. var backwardCompat = function (option, isTheme) {
  15984. compatStyle(option, isTheme); // Make sure series array for model initialization.
  15985. option.series = normalizeToArray(option.series);
  15986. each$1(option.series, function (seriesOpt) {
  15987. if (!isObject(seriesOpt)) {
  15988. return;
  15989. }
  15990. var seriesType = seriesOpt.type;
  15991. if (seriesType === 'pie' || seriesType === 'gauge') {
  15992. if (seriesOpt.clockWise != null) {
  15993. seriesOpt.clockwise = seriesOpt.clockWise;
  15994. }
  15995. }
  15996. if (seriesType === 'gauge') {
  15997. var pointerColor = get$1(seriesOpt, 'pointer.color');
  15998. pointerColor != null && set$2(seriesOpt, 'itemStyle.normal.color', pointerColor);
  15999. }
  16000. for (var i = 0; i < COMPATITABLE_SERIES.length; i++) {
  16001. if (COMPATITABLE_SERIES[i] === seriesOpt.type) {
  16002. compatLayoutProperties(seriesOpt);
  16003. break;
  16004. }
  16005. }
  16006. }); // dataRange has changed to visualMap
  16007. if (option.dataRange) {
  16008. option.visualMap = option.dataRange;
  16009. }
  16010. each$1(COMPATITABLE_COMPONENTS, function (componentName) {
  16011. var options = option[componentName];
  16012. if (options) {
  16013. if (!isArray(options)) {
  16014. options = [options];
  16015. }
  16016. each$1(options, function (option) {
  16017. compatLayoutProperties(option);
  16018. });
  16019. }
  16020. });
  16021. };
  16022. var SeriesModel = ComponentModel.extend({
  16023. type: 'series.__base__',
  16024. /**
  16025. * @readOnly
  16026. */
  16027. seriesIndex: 0,
  16028. // coodinateSystem will be injected in the echarts/CoordinateSystem
  16029. coordinateSystem: null,
  16030. /**
  16031. * @type {Object}
  16032. * @protected
  16033. */
  16034. defaultOption: null,
  16035. /**
  16036. * Data provided for legend
  16037. * @type {Function}
  16038. */
  16039. // PENDING
  16040. legendDataProvider: null,
  16041. /**
  16042. * Access path of color for visual
  16043. */
  16044. visualColorAccessPath: 'itemStyle.normal.color',
  16045. /**
  16046. * Support merge layout params.
  16047. * Only support 'box' now (left/right/top/bottom/width/height).
  16048. * @type {string|Object} Object can be {ignoreSize: true}
  16049. * @readOnly
  16050. */
  16051. layoutMode: null,
  16052. init: function (option, parentModel, ecModel, extraOpt) {
  16053. /**
  16054. * @type {number}
  16055. * @readOnly
  16056. */
  16057. this.seriesIndex = this.componentIndex;
  16058. this.mergeDefaultAndTheme(option, ecModel);
  16059. var data = this.getInitialData(option, ecModel);
  16060. if (true) {
  16061. assert(data, 'getInitialData returned invalid data.');
  16062. }
  16063. /**
  16064. * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}
  16065. * @private
  16066. */
  16067. set$1(this, 'dataBeforeProcessed', data); // If we reverse the order (make data firstly, and then make
  16068. // dataBeforeProcessed by cloneShallow), cloneShallow will
  16069. // cause data.graph.data !== data when using
  16070. // module:echarts/data/Graph or module:echarts/data/Tree.
  16071. // See module:echarts/data/helper/linkList
  16072. this.restoreData();
  16073. },
  16074. /**
  16075. * Util for merge default and theme to option
  16076. * @param {Object} option
  16077. * @param {module:echarts/model/Global} ecModel
  16078. */
  16079. mergeDefaultAndTheme: function (option, ecModel) {
  16080. var layoutMode = this.layoutMode;
  16081. var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme.
  16082. // But if name duplicate between series subType
  16083. // (for example: parallel) add component mainType,
  16084. // add suffix 'Series'.
  16085. var themeSubType = this.subType;
  16086. if (ComponentModel.hasClass(themeSubType)) {
  16087. themeSubType += 'Series';
  16088. }
  16089. merge(option, ecModel.getTheme().get(this.subType));
  16090. merge(option, this.getDefaultOption()); // Default label emphasis `show`
  16091. defaultEmphasis(option.label, ['show']);
  16092. this.fillDataTextStyle(option.data);
  16093. if (layoutMode) {
  16094. mergeLayoutParam(option, inputPositionParams, layoutMode);
  16095. }
  16096. },
  16097. mergeOption: function (newSeriesOption, ecModel) {
  16098. newSeriesOption = merge(this.option, newSeriesOption, true);
  16099. this.fillDataTextStyle(newSeriesOption.data);
  16100. var layoutMode = this.layoutMode;
  16101. if (layoutMode) {
  16102. mergeLayoutParam(this.option, newSeriesOption, layoutMode);
  16103. }
  16104. var data = this.getInitialData(newSeriesOption, ecModel); // TODO Merge data?
  16105. if (data) {
  16106. set$1(this, 'data', data);
  16107. set$1(this, 'dataBeforeProcessed', data.cloneShallow());
  16108. }
  16109. },
  16110. fillDataTextStyle: function (data) {
  16111. // Default data label emphasis `show`
  16112. // FIXME Tree structure data ?
  16113. // FIXME Performance ?
  16114. if (data) {
  16115. var props = ['show'];
  16116. for (var i = 0; i < data.length; i++) {
  16117. if (data[i] && data[i].label) {
  16118. defaultEmphasis(data[i].label, props);
  16119. }
  16120. }
  16121. }
  16122. },
  16123. /**
  16124. * Init a data structure from data related option in series
  16125. * Must be overwritten
  16126. */
  16127. getInitialData: function () {},
  16128. /**
  16129. * @param {string} [dataType]
  16130. * @return {module:echarts/data/List}
  16131. */
  16132. getData: function (dataType) {
  16133. var data = get(this, 'data');
  16134. return dataType == null ? data : data.getLinkedData(dataType);
  16135. },
  16136. /**
  16137. * @param {module:echarts/data/List} data
  16138. */
  16139. setData: function (data) {
  16140. set$1(this, 'data', data);
  16141. },
  16142. /**
  16143. * Get data before processed
  16144. * @return {module:echarts/data/List}
  16145. */
  16146. getRawData: function () {
  16147. return get(this, 'dataBeforeProcessed');
  16148. },
  16149. /**
  16150. * Coord dimension to data dimension.
  16151. *
  16152. * By default the result is the same as dimensions of series data.
  16153. * But in some series data dimensions are different from coord dimensions (i.e.
  16154. * candlestick and boxplot). Override this method to handle those cases.
  16155. *
  16156. * Coord dimension to data dimension can be one-to-many
  16157. *
  16158. * @param {string} coordDim
  16159. * @return {Array.<string>} dimensions on the axis.
  16160. */
  16161. coordDimToDataDim: function (coordDim) {
  16162. return coordDimToDataDim(this.getData(), coordDim);
  16163. },
  16164. /**
  16165. * Convert data dimension to coord dimension.
  16166. *
  16167. * @param {string|number} dataDim
  16168. * @return {string}
  16169. */
  16170. dataDimToCoordDim: function (dataDim) {
  16171. return dataDimToCoordDim(this.getData(), dataDim);
  16172. },
  16173. /**
  16174. * Get base axis if has coordinate system and has axis.
  16175. * By default use coordSys.getBaseAxis();
  16176. * Can be overrided for some chart.
  16177. * @return {type} description
  16178. */
  16179. getBaseAxis: function () {
  16180. var coordSys = this.coordinateSystem;
  16181. return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();
  16182. },
  16183. // FIXME
  16184. /**
  16185. * Default tooltip formatter
  16186. *
  16187. * @param {number} dataIndex
  16188. * @param {boolean} [multipleSeries=false]
  16189. * @param {number} [dataType]
  16190. */
  16191. formatTooltip: function (dataIndex, multipleSeries, dataType) {
  16192. function formatArrayValue(value) {
  16193. var vertially = reduce(value, function (vertially, val, idx) {
  16194. var dimItem = data.getDimensionInfo(idx);
  16195. return vertially |= dimItem && dimItem.tooltip !== false && dimItem.tooltipName != null;
  16196. }, 0);
  16197. var result = [];
  16198. var tooltipDims = otherDimToDataDim(data, 'tooltip');
  16199. tooltipDims.length ? each$1(tooltipDims, function (dimIdx) {
  16200. setEachItem(data.get(dimIdx, dataIndex), dimIdx);
  16201. }) // By default, all dims is used on tooltip.
  16202. : each$1(value, setEachItem);
  16203. function setEachItem(val, dimIdx) {
  16204. var dimInfo = data.getDimensionInfo(dimIdx); // If `dimInfo.tooltip` is not set, show tooltip.
  16205. if (!dimInfo || dimInfo.otherDims.tooltip === false) {
  16206. return;
  16207. }
  16208. var dimType = dimInfo.type;
  16209. var valStr = (vertially ? '- ' + (dimInfo.tooltipName || dimInfo.name) + ': ' : '') + (dimType === 'ordinal' ? val + '' : dimType === 'time' ? multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val) : addCommas(val));
  16210. valStr && result.push(encodeHTML(valStr));
  16211. }
  16212. return (vertially ? '<br/>' : '') + result.join(vertially ? '<br/>' : ', ');
  16213. }
  16214. var data = get(this, 'data');
  16215. var value = this.getRawValue(dataIndex);
  16216. var formattedValue = isArray(value) ? formatArrayValue(value) : encodeHTML(addCommas(value));
  16217. var name = data.getName(dataIndex);
  16218. var color = data.getItemVisual(dataIndex, 'color');
  16219. if (isObject(color) && color.colorStops) {
  16220. color = (color.colorStops[0] || {}).color;
  16221. }
  16222. color = color || 'transparent';
  16223. var colorEl = getTooltipMarker(color);
  16224. var seriesName = this.name; // FIXME
  16225. if (seriesName === '\0-') {
  16226. // Not show '-'
  16227. seriesName = '';
  16228. }
  16229. seriesName = seriesName ? encodeHTML(seriesName) + (!multipleSeries ? '<br/>' : ': ') : '';
  16230. return !multipleSeries ? seriesName + colorEl + (name ? encodeHTML(name) + ': ' + formattedValue : formattedValue) : colorEl + seriesName + formattedValue;
  16231. },
  16232. /**
  16233. * @return {boolean}
  16234. */
  16235. isAnimationEnabled: function () {
  16236. if (env$1.node) {
  16237. return false;
  16238. }
  16239. var animationEnabled = this.getShallow('animation');
  16240. if (animationEnabled) {
  16241. if (this.getData().count() > this.getShallow('animationThreshold')) {
  16242. animationEnabled = false;
  16243. }
  16244. }
  16245. return animationEnabled;
  16246. },
  16247. restoreData: function () {
  16248. set$1(this, 'data', get(this, 'dataBeforeProcessed').cloneShallow());
  16249. },
  16250. getColorFromPalette: function (name, scope) {
  16251. var ecModel = this.ecModel; // PENDING
  16252. var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope);
  16253. if (!color) {
  16254. color = ecModel.getColorFromPalette(name, scope);
  16255. }
  16256. return color;
  16257. },
  16258. /**
  16259. * Get data indices for show tooltip content. See tooltip.
  16260. * @abstract
  16261. * @param {Array.<string>|string} dim
  16262. * @param {Array.<number>} value
  16263. * @param {module:echarts/coord/single/SingleAxis} baseAxis
  16264. * @return {Object} {dataIndices, nestestValue}.
  16265. */
  16266. getAxisTooltipData: null,
  16267. /**
  16268. * See tooltip.
  16269. * @abstract
  16270. * @param {number} dataIndex
  16271. * @return {Array.<number>} Point of tooltip. null/undefined can be returned.
  16272. */
  16273. getTooltipPosition: null
  16274. });
  16275. mixin(SeriesModel, dataFormatMixin);
  16276. mixin(SeriesModel, colorPaletteMixin);
  16277. var Component$1 = function () {
  16278. /**
  16279. * @type {module:zrender/container/Group}
  16280. * @readOnly
  16281. */
  16282. this.group = new Group();
  16283. /**
  16284. * @type {string}
  16285. * @readOnly
  16286. */
  16287. this.uid = getUID('viewComponent');
  16288. };
  16289. Component$1.prototype = {
  16290. constructor: Component$1,
  16291. init: function (ecModel, api) {},
  16292. render: function (componentModel, ecModel, api, payload) {},
  16293. dispose: function () {}
  16294. };
  16295. var componentProto = Component$1.prototype;
  16296. componentProto.updateView = componentProto.updateLayout = componentProto.updateVisual = function (seriesModel, ecModel, api, payload) {// Do nothing;
  16297. }; // Enable Component.extend.
  16298. enableClassExtend(Component$1); // Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  16299. enableClassManagement(Component$1, {
  16300. registerWhenExtend: true
  16301. });
  16302. function Chart() {
  16303. /**
  16304. * @type {module:zrender/container/Group}
  16305. * @readOnly
  16306. */
  16307. this.group = new Group();
  16308. /**
  16309. * @type {string}
  16310. * @readOnly
  16311. */
  16312. this.uid = getUID('viewChart');
  16313. }
  16314. Chart.prototype = {
  16315. type: 'chart',
  16316. /**
  16317. * Init the chart
  16318. * @param {module:echarts/model/Global} ecModel
  16319. * @param {module:echarts/ExtensionAPI} api
  16320. */
  16321. init: function (ecModel, api) {},
  16322. /**
  16323. * Render the chart
  16324. * @param {module:echarts/model/Series} seriesModel
  16325. * @param {module:echarts/model/Global} ecModel
  16326. * @param {module:echarts/ExtensionAPI} api
  16327. * @param {Object} payload
  16328. */
  16329. render: function (seriesModel, ecModel, api, payload) {},
  16330. /**
  16331. * Highlight series or specified data item
  16332. * @param {module:echarts/model/Series} seriesModel
  16333. * @param {module:echarts/model/Global} ecModel
  16334. * @param {module:echarts/ExtensionAPI} api
  16335. * @param {Object} payload
  16336. */
  16337. highlight: function (seriesModel, ecModel, api, payload) {
  16338. toggleHighlight(seriesModel.getData(), payload, 'emphasis');
  16339. },
  16340. /**
  16341. * Downplay series or specified data item
  16342. * @param {module:echarts/model/Series} seriesModel
  16343. * @param {module:echarts/model/Global} ecModel
  16344. * @param {module:echarts/ExtensionAPI} api
  16345. * @param {Object} payload
  16346. */
  16347. downplay: function (seriesModel, ecModel, api, payload) {
  16348. toggleHighlight(seriesModel.getData(), payload, 'normal');
  16349. },
  16350. /**
  16351. * Remove self
  16352. * @param {module:echarts/model/Global} ecModel
  16353. * @param {module:echarts/ExtensionAPI} api
  16354. */
  16355. remove: function (ecModel, api) {
  16356. this.group.removeAll();
  16357. },
  16358. /**
  16359. * Dispose self
  16360. * @param {module:echarts/model/Global} ecModel
  16361. * @param {module:echarts/ExtensionAPI} api
  16362. */
  16363. dispose: function () {}
  16364. /**
  16365. * The view contains the given point.
  16366. * @interface
  16367. * @param {Array.<number>} point
  16368. * @return {boolean}
  16369. */
  16370. // containPoint: function () {}
  16371. };
  16372. var chartProto = Chart.prototype;
  16373. chartProto.updateView = chartProto.updateLayout = chartProto.updateVisual = function (seriesModel, ecModel, api, payload) {
  16374. this.render(seriesModel, ecModel, api, payload);
  16375. };
  16376. /**
  16377. * Set state of single element
  16378. * @param {module:zrender/Element} el
  16379. * @param {string} state
  16380. */
  16381. function elSetState(el, state) {
  16382. if (el) {
  16383. el.trigger(state);
  16384. if (el.type === 'group') {
  16385. for (var i = 0; i < el.childCount(); i++) {
  16386. elSetState(el.childAt(i), state);
  16387. }
  16388. }
  16389. }
  16390. }
  16391. /**
  16392. * @param {module:echarts/data/List} data
  16393. * @param {Object} payload
  16394. * @param {string} state 'normal'|'emphasis'
  16395. * @inner
  16396. */
  16397. function toggleHighlight(data, payload, state) {
  16398. var dataIndex = queryDataIndex(data, payload);
  16399. if (dataIndex != null) {
  16400. each$1(normalizeToArray(dataIndex), function (dataIdx) {
  16401. elSetState(data.getItemGraphicEl(dataIdx), state);
  16402. });
  16403. } else {
  16404. data.eachItemGraphicEl(function (el) {
  16405. elSetState(el, state);
  16406. });
  16407. }
  16408. } // Enable Chart.extend.
  16409. enableClassExtend(Chart, ['dispose']); // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  16410. enableClassManagement(Chart, {
  16411. registerWhenExtend: true
  16412. });
  16413. var ORIGIN_METHOD = '\0__throttleOriginMethod';
  16414. var RATE = '\0__throttleRate';
  16415. var THROTTLE_TYPE = '\0__throttleType';
  16416. /**
  16417. * @public
  16418. * @param {(Function)} fn
  16419. * @param {number} [delay=0] Unit: ms.
  16420. * @param {boolean} [debounce=false]
  16421. * true: If call interval less than `delay`, only the last call works.
  16422. * false: If call interval less than `delay, call works on fixed rate.
  16423. * @return {(Function)} throttled fn.
  16424. */
  16425. function throttle(fn, delay, debounce) {
  16426. var currCall;
  16427. var lastCall = 0;
  16428. var lastExec = 0;
  16429. var timer = null;
  16430. var diff;
  16431. var scope;
  16432. var args;
  16433. var debounceNextCall;
  16434. delay = delay || 0;
  16435. function exec() {
  16436. lastExec = new Date().getTime();
  16437. timer = null;
  16438. fn.apply(scope, args || []);
  16439. }
  16440. var cb = function () {
  16441. currCall = new Date().getTime();
  16442. scope = this;
  16443. args = arguments;
  16444. var thisDelay = debounceNextCall || delay;
  16445. var thisDebounce = debounceNextCall || debounce;
  16446. debounceNextCall = null;
  16447. diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
  16448. clearTimeout(timer);
  16449. if (thisDebounce) {
  16450. timer = setTimeout(exec, thisDelay);
  16451. } else {
  16452. if (diff >= 0) {
  16453. exec();
  16454. } else {
  16455. timer = setTimeout(exec, -diff);
  16456. }
  16457. }
  16458. lastCall = currCall;
  16459. };
  16460. /**
  16461. * Clear throttle.
  16462. * @public
  16463. */
  16464. cb.clear = function () {
  16465. if (timer) {
  16466. clearTimeout(timer);
  16467. timer = null;
  16468. }
  16469. };
  16470. /**
  16471. * Enable debounce once.
  16472. */
  16473. cb.debounceNextCall = function (debounceDelay) {
  16474. debounceNextCall = debounceDelay;
  16475. };
  16476. return cb;
  16477. }
  16478. /**
  16479. * Create throttle method or update throttle rate.
  16480. *
  16481. * @example
  16482. * ComponentView.prototype.render = function () {
  16483. * ...
  16484. * throttle.createOrUpdate(
  16485. * this,
  16486. * '_dispatchAction',
  16487. * this.model.get('throttle'),
  16488. * 'fixRate'
  16489. * );
  16490. * };
  16491. * ComponentView.prototype.remove = function () {
  16492. * throttle.clear(this, '_dispatchAction');
  16493. * };
  16494. * ComponentView.prototype.dispose = function () {
  16495. * throttle.clear(this, '_dispatchAction');
  16496. * };
  16497. *
  16498. * @public
  16499. * @param {Object} obj
  16500. * @param {string} fnAttr
  16501. * @param {number} [rate]
  16502. * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce'
  16503. * @return {Function} throttled function.
  16504. */
  16505. function createOrUpdate(obj, fnAttr, rate, throttleType) {
  16506. var fn = obj[fnAttr];
  16507. if (!fn) {
  16508. return;
  16509. }
  16510. var originFn = fn[ORIGIN_METHOD] || fn;
  16511. var lastThrottleType = fn[THROTTLE_TYPE];
  16512. var lastRate = fn[RATE];
  16513. if (lastRate !== rate || lastThrottleType !== throttleType) {
  16514. if (rate == null || !throttleType) {
  16515. return obj[fnAttr] = originFn;
  16516. }
  16517. fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce');
  16518. fn[ORIGIN_METHOD] = originFn;
  16519. fn[THROTTLE_TYPE] = throttleType;
  16520. fn[RATE] = rate;
  16521. }
  16522. return fn;
  16523. }
  16524. /**
  16525. * Clear throttle. Example see throttle.createOrUpdate.
  16526. *
  16527. * @public
  16528. * @param {Object} obj
  16529. * @param {string} fnAttr
  16530. */
  16531. var seriesColor = function (ecModel) {
  16532. function encodeColor(seriesModel) {
  16533. var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.normal.color').split('.');
  16534. var data = seriesModel.getData();
  16535. var color = seriesModel.get(colorAccessPath) // Set in itemStyle
  16536. || seriesModel.getColorFromPalette(seriesModel.get('name')); // Default color
  16537. // FIXME Set color function or use the platte color
  16538. data.setVisual('color', color); // Only visible series has each data be visual encoded
  16539. if (!ecModel.isSeriesFiltered(seriesModel)) {
  16540. if (typeof color === 'function' && !(color instanceof Gradient)) {
  16541. data.each(function (idx) {
  16542. data.setItemVisual(idx, 'color', color(seriesModel.getDataParams(idx)));
  16543. });
  16544. } // itemStyle in each data item
  16545. data.each(function (idx) {
  16546. var itemModel = data.getItemModel(idx);
  16547. var color = itemModel.get(colorAccessPath, true);
  16548. if (color != null) {
  16549. data.setItemVisual(idx, 'color', color);
  16550. }
  16551. });
  16552. }
  16553. }
  16554. ecModel.eachRawSeries(encodeColor);
  16555. };
  16556. var PI$1 = Math.PI;
  16557. /**
  16558. * @param {module:echarts/ExtensionAPI} api
  16559. * @param {Object} [opts]
  16560. * @param {string} [opts.text]
  16561. * @param {string} [opts.color]
  16562. * @param {string} [opts.textColor]
  16563. * @return {module:zrender/Element}
  16564. */
  16565. var loadingDefault = function (api, opts) {
  16566. opts = opts || {};
  16567. defaults(opts, {
  16568. text: 'loading',
  16569. color: '#c23531',
  16570. textColor: '#000',
  16571. maskColor: 'rgba(255, 255, 255, 0.8)',
  16572. zlevel: 0
  16573. });
  16574. var mask = new Rect({
  16575. style: {
  16576. fill: opts.maskColor
  16577. },
  16578. zlevel: opts.zlevel,
  16579. z: 10000
  16580. });
  16581. var arc = new Arc({
  16582. shape: {
  16583. startAngle: -PI$1 / 2,
  16584. endAngle: -PI$1 / 2 + 0.1,
  16585. r: 10
  16586. },
  16587. style: {
  16588. stroke: opts.color,
  16589. lineCap: 'round',
  16590. lineWidth: 5
  16591. },
  16592. zlevel: opts.zlevel,
  16593. z: 10001
  16594. });
  16595. var labelRect = new Rect({
  16596. style: {
  16597. fill: 'none',
  16598. text: opts.text,
  16599. textPosition: 'right',
  16600. textDistance: 10,
  16601. textFill: opts.textColor
  16602. },
  16603. zlevel: opts.zlevel,
  16604. z: 10001
  16605. });
  16606. arc.animateShape(true).when(1000, {
  16607. endAngle: PI$1 * 3 / 2
  16608. }).start('circularInOut');
  16609. arc.animateShape(true).when(1000, {
  16610. startAngle: PI$1 * 3 / 2
  16611. }).delay(300).start('circularInOut');
  16612. var group = new Group();
  16613. group.add(arc);
  16614. group.add(labelRect);
  16615. group.add(mask); // Inject resize
  16616. group.resize = function () {
  16617. var cx = api.getWidth() / 2;
  16618. var cy = api.getHeight() / 2;
  16619. arc.setShape({
  16620. cx: cx,
  16621. cy: cy
  16622. });
  16623. var r = arc.shape.r;
  16624. labelRect.setShape({
  16625. x: cx - r,
  16626. y: cy - r,
  16627. width: r * 2,
  16628. height: r * 2
  16629. });
  16630. mask.setShape({
  16631. x: 0,
  16632. y: 0,
  16633. width: api.getWidth(),
  16634. height: api.getHeight()
  16635. });
  16636. };
  16637. group.resize();
  16638. return group;
  16639. };
  16640. /*!
  16641. * ECharts, a javascript interactive chart library.
  16642. *
  16643. * Copyright (c) 2015, Baidu Inc.
  16644. * All rights reserved.
  16645. *
  16646. * LICENSE
  16647. * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
  16648. */
  16649. var each = each$1;
  16650. var parseClassType = ComponentModel.parseClassType;
  16651. var version = '3.8.0';
  16652. var dependencies = {
  16653. zrender: '3.7.0'
  16654. };
  16655. var PRIORITY_PROCESSOR_FILTER = 1000;
  16656. var PRIORITY_PROCESSOR_STATISTIC = 5000;
  16657. var PRIORITY_VISUAL_LAYOUT = 1000;
  16658. var PRIORITY_VISUAL_GLOBAL = 2000;
  16659. var PRIORITY_VISUAL_CHART = 3000;
  16660. var PRIORITY_VISUAL_COMPONENT = 4000; // FIXME
  16661. // necessary?
  16662. var PRIORITY_VISUAL_BRUSH = 5000;
  16663. var PRIORITY = {
  16664. PROCESSOR: {
  16665. FILTER: PRIORITY_PROCESSOR_FILTER,
  16666. STATISTIC: PRIORITY_PROCESSOR_STATISTIC
  16667. },
  16668. VISUAL: {
  16669. LAYOUT: PRIORITY_VISUAL_LAYOUT,
  16670. GLOBAL: PRIORITY_VISUAL_GLOBAL,
  16671. CHART: PRIORITY_VISUAL_CHART,
  16672. COMPONENT: PRIORITY_VISUAL_COMPONENT,
  16673. BRUSH: PRIORITY_VISUAL_BRUSH
  16674. }
  16675. }; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
  16676. // where they must not be invoked nestedly, except the only case: invoke
  16677. // dispatchAction with updateMethod "none" in main process.
  16678. // This flag is used to carry out this rule.
  16679. // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
  16680. var IN_MAIN_PROCESS = '__flagInMainProcess';
  16681. var HAS_GRADIENT_OR_PATTERN_BG = '__hasGradientOrPatternBg';
  16682. var OPTION_UPDATED = '__optionUpdated';
  16683. var ACTION_REG = /^[a-zA-Z0-9_]+$/;
  16684. function createRegisterEventWithLowercaseName(method) {
  16685. return function (eventName, handler, context) {
  16686. // Event name is all lowercase
  16687. eventName = eventName && eventName.toLowerCase();
  16688. Eventful.prototype[method].call(this, eventName, handler, context);
  16689. };
  16690. }
  16691. /**
  16692. * @module echarts~MessageCenter
  16693. */
  16694. function MessageCenter() {
  16695. Eventful.call(this);
  16696. }
  16697. MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
  16698. MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
  16699. MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
  16700. mixin(MessageCenter, Eventful);
  16701. /**
  16702. * @module echarts~ECharts
  16703. */
  16704. function ECharts(dom, theme, opts) {
  16705. opts = opts || {}; // Get theme by name
  16706. if (typeof theme === 'string') {
  16707. theme = themeStorage[theme];
  16708. }
  16709. /**
  16710. * @type {string}
  16711. */
  16712. this.id;
  16713. /**
  16714. * Group id
  16715. * @type {string}
  16716. */
  16717. this.group;
  16718. /**
  16719. * @type {HTMLElement}
  16720. * @private
  16721. */
  16722. this._dom = dom;
  16723. var defaultRenderer = 'canvas';
  16724. if (true) {
  16725. defaultRenderer = (typeof window === 'undefined' ? global : window).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
  16726. }
  16727. /**
  16728. * @type {module:zrender/ZRender}
  16729. * @private
  16730. */
  16731. var zr = this._zr = init$1(dom, {
  16732. renderer: opts.renderer || defaultRenderer,
  16733. devicePixelRatio: opts.devicePixelRatio,
  16734. width: opts.width,
  16735. height: opts.height
  16736. });
  16737. /**
  16738. * Expect 60 pfs.
  16739. * @type {Function}
  16740. * @private
  16741. */
  16742. this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);
  16743. var theme = clone(theme);
  16744. theme && backwardCompat(theme, true);
  16745. /**
  16746. * @type {Object}
  16747. * @private
  16748. */
  16749. this._theme = theme;
  16750. /**
  16751. * @type {Array.<module:echarts/view/Chart>}
  16752. * @private
  16753. */
  16754. this._chartsViews = [];
  16755. /**
  16756. * @type {Object.<string, module:echarts/view/Chart>}
  16757. * @private
  16758. */
  16759. this._chartsMap = {};
  16760. /**
  16761. * @type {Array.<module:echarts/view/Component>}
  16762. * @private
  16763. */
  16764. this._componentsViews = [];
  16765. /**
  16766. * @type {Object.<string, module:echarts/view/Component>}
  16767. * @private
  16768. */
  16769. this._componentsMap = {};
  16770. /**
  16771. * @type {module:echarts/CoordinateSystem}
  16772. * @private
  16773. */
  16774. this._coordSysMgr = new CoordinateSystemManager();
  16775. /**
  16776. * @type {module:echarts/ExtensionAPI}
  16777. * @private
  16778. */
  16779. this._api = createExtensionAPI(this);
  16780. Eventful.call(this);
  16781. /**
  16782. * @type {module:echarts~MessageCenter}
  16783. * @private
  16784. */
  16785. this._messageCenter = new MessageCenter(); // Init mouse events
  16786. this._initEvents(); // In case some people write `window.onresize = chart.resize`
  16787. this.resize = bind(this.resize, this); // Can't dispatch action during rendering procedure
  16788. this._pendingActions = []; // Sort on demand
  16789. function prioritySortFunc(a, b) {
  16790. return a.prio - b.prio;
  16791. }
  16792. sort(visualFuncs, prioritySortFunc);
  16793. sort(dataProcessorFuncs, prioritySortFunc);
  16794. zr.animation.on('frame', this._onframe, this); // ECharts instance can be used as value.
  16795. setAsPrimitive(this);
  16796. }
  16797. var echartsProto = ECharts.prototype;
  16798. echartsProto._onframe = function () {
  16799. // Lazy update
  16800. if (this[OPTION_UPDATED]) {
  16801. var silent = this[OPTION_UPDATED].silent;
  16802. this[IN_MAIN_PROCESS] = true;
  16803. updateMethods.prepareAndUpdate.call(this);
  16804. this[IN_MAIN_PROCESS] = false;
  16805. this[OPTION_UPDATED] = false;
  16806. flushPendingActions.call(this, silent);
  16807. triggerUpdatedEvent.call(this, silent);
  16808. }
  16809. };
  16810. /**
  16811. * @return {HTMLElement}
  16812. */
  16813. echartsProto.getDom = function () {
  16814. return this._dom;
  16815. };
  16816. /**
  16817. * @return {module:zrender~ZRender}
  16818. */
  16819. echartsProto.getZr = function () {
  16820. return this._zr;
  16821. };
  16822. /**
  16823. * Usage:
  16824. * chart.setOption(option, notMerge, lazyUpdate);
  16825. * chart.setOption(option, {
  16826. * notMerge: ...,
  16827. * lazyUpdate: ...,
  16828. * silent: ...
  16829. * });
  16830. *
  16831. * @param {Object} option
  16832. * @param {Object|boolean} [opts] opts or notMerge.
  16833. * @param {boolean} [opts.notMerge=false]
  16834. * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
  16835. */
  16836. echartsProto.setOption = function (option, notMerge, lazyUpdate) {
  16837. if (true) {
  16838. assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
  16839. }
  16840. var silent;
  16841. if (isObject(notMerge)) {
  16842. lazyUpdate = notMerge.lazyUpdate;
  16843. silent = notMerge.silent;
  16844. notMerge = notMerge.notMerge;
  16845. }
  16846. this[IN_MAIN_PROCESS] = true;
  16847. if (!this._model || notMerge) {
  16848. var optionManager = new OptionManager(this._api);
  16849. var theme = this._theme;
  16850. var ecModel = this._model = new GlobalModel(null, null, theme, optionManager);
  16851. ecModel.init(null, null, theme, optionManager);
  16852. }
  16853. this._model.setOption(option, optionPreprocessorFuncs);
  16854. if (lazyUpdate) {
  16855. this[OPTION_UPDATED] = {
  16856. silent: silent
  16857. };
  16858. this[IN_MAIN_PROCESS] = false;
  16859. } else {
  16860. updateMethods.prepareAndUpdate.call(this); // Ensure zr refresh sychronously, and then pixel in canvas can be
  16861. // fetched after `setOption`.
  16862. this._zr.flush();
  16863. this[OPTION_UPDATED] = false;
  16864. this[IN_MAIN_PROCESS] = false;
  16865. flushPendingActions.call(this, silent);
  16866. triggerUpdatedEvent.call(this, silent);
  16867. }
  16868. };
  16869. /**
  16870. * @DEPRECATED
  16871. */
  16872. echartsProto.setTheme = function () {
  16873. console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
  16874. };
  16875. /**
  16876. * @return {module:echarts/model/Global}
  16877. */
  16878. echartsProto.getModel = function () {
  16879. return this._model;
  16880. };
  16881. /**
  16882. * @return {Object}
  16883. */
  16884. echartsProto.getOption = function () {
  16885. return this._model && this._model.getOption();
  16886. };
  16887. /**
  16888. * @return {number}
  16889. */
  16890. echartsProto.getWidth = function () {
  16891. return this._zr.getWidth();
  16892. };
  16893. /**
  16894. * @return {number}
  16895. */
  16896. echartsProto.getHeight = function () {
  16897. return this._zr.getHeight();
  16898. };
  16899. /**
  16900. * @return {number}
  16901. */
  16902. echartsProto.getDevicePixelRatio = function () {
  16903. return this._zr.painter.dpr || window.devicePixelRatio || 1;
  16904. };
  16905. /**
  16906. * Get canvas which has all thing rendered
  16907. * @param {Object} opts
  16908. * @param {string} [opts.backgroundColor]
  16909. * @return {string}
  16910. */
  16911. echartsProto.getRenderedCanvas = function (opts) {
  16912. if (!env$1.canvasSupported) {
  16913. return;
  16914. }
  16915. opts = opts || {};
  16916. opts.pixelRatio = opts.pixelRatio || 1;
  16917. opts.backgroundColor = opts.backgroundColor || this._model.get('backgroundColor');
  16918. var zr = this._zr;
  16919. var list = zr.storage.getDisplayList(); // Stop animations
  16920. each$1(list, function (el) {
  16921. el.stopAnimation(true);
  16922. });
  16923. return zr.painter.getRenderedCanvas(opts);
  16924. };
  16925. /**
  16926. * Get svg data url
  16927. * @return {string}
  16928. */
  16929. echartsProto.getSvgDataUrl = function () {
  16930. if (!env$1.svgSupported) {
  16931. return;
  16932. }
  16933. var zr = this._zr;
  16934. var list = zr.storage.getDisplayList(); // Stop animations
  16935. each$1(list, function (el) {
  16936. el.stopAnimation(true);
  16937. });
  16938. return zr.painter.pathToSvg();
  16939. };
  16940. /**
  16941. * @return {string}
  16942. * @param {Object} opts
  16943. * @param {string} [opts.type='png']
  16944. * @param {string} [opts.pixelRatio=1]
  16945. * @param {string} [opts.backgroundColor]
  16946. * @param {string} [opts.excludeComponents]
  16947. */
  16948. echartsProto.getDataURL = function (opts) {
  16949. opts = opts || {};
  16950. var excludeComponents = opts.excludeComponents;
  16951. var ecModel = this._model;
  16952. var excludesComponentViews = [];
  16953. var self = this;
  16954. each(excludeComponents, function (componentType) {
  16955. ecModel.eachComponent({
  16956. mainType: componentType
  16957. }, function (component) {
  16958. var view = self._componentsMap[component.__viewId];
  16959. if (!view.group.ignore) {
  16960. excludesComponentViews.push(view);
  16961. view.group.ignore = true;
  16962. }
  16963. });
  16964. });
  16965. var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataUrl() : this.getRenderedCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));
  16966. each(excludesComponentViews, function (view) {
  16967. view.group.ignore = false;
  16968. });
  16969. return url;
  16970. };
  16971. /**
  16972. * @return {string}
  16973. * @param {Object} opts
  16974. * @param {string} [opts.type='png']
  16975. * @param {string} [opts.pixelRatio=1]
  16976. * @param {string} [opts.backgroundColor]
  16977. */
  16978. echartsProto.getConnectedDataURL = function (opts) {
  16979. if (!env$1.canvasSupported) {
  16980. return;
  16981. }
  16982. var groupId = this.group;
  16983. var mathMin = Math.min;
  16984. var mathMax = Math.max;
  16985. var MAX_NUMBER = Infinity;
  16986. if (connectedGroups[groupId]) {
  16987. var left = MAX_NUMBER;
  16988. var top = MAX_NUMBER;
  16989. var right = -MAX_NUMBER;
  16990. var bottom = -MAX_NUMBER;
  16991. var canvasList = [];
  16992. var dpr = opts && opts.pixelRatio || 1;
  16993. each$1(instances, function (chart, id) {
  16994. if (chart.group === groupId) {
  16995. var canvas = chart.getRenderedCanvas(clone(opts));
  16996. var boundingRect = chart.getDom().getBoundingClientRect();
  16997. left = mathMin(boundingRect.left, left);
  16998. top = mathMin(boundingRect.top, top);
  16999. right = mathMax(boundingRect.right, right);
  17000. bottom = mathMax(boundingRect.bottom, bottom);
  17001. canvasList.push({
  17002. dom: canvas,
  17003. left: boundingRect.left,
  17004. top: boundingRect.top
  17005. });
  17006. }
  17007. });
  17008. left *= dpr;
  17009. top *= dpr;
  17010. right *= dpr;
  17011. bottom *= dpr;
  17012. var width = right - left;
  17013. var height = bottom - top;
  17014. var targetCanvas = createCanvas();
  17015. targetCanvas.width = width;
  17016. targetCanvas.height = height;
  17017. var zr = init$1(targetCanvas);
  17018. each(canvasList, function (item) {
  17019. var img = new ZImage({
  17020. style: {
  17021. x: item.left * dpr - left,
  17022. y: item.top * dpr - top,
  17023. image: item.dom
  17024. }
  17025. });
  17026. zr.add(img);
  17027. });
  17028. zr.refreshImmediately();
  17029. return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
  17030. } else {
  17031. return this.getDataURL(opts);
  17032. }
  17033. };
  17034. /**
  17035. * Convert from logical coordinate system to pixel coordinate system.
  17036. * See CoordinateSystem#convertToPixel.
  17037. * @param {string|Object} finder
  17038. * If string, e.g., 'geo', means {geoIndex: 0}.
  17039. * If Object, could contain some of these properties below:
  17040. * {
  17041. * seriesIndex / seriesId / seriesName,
  17042. * geoIndex / geoId, geoName,
  17043. * bmapIndex / bmapId / bmapName,
  17044. * xAxisIndex / xAxisId / xAxisName,
  17045. * yAxisIndex / yAxisId / yAxisName,
  17046. * gridIndex / gridId / gridName,
  17047. * ... (can be extended)
  17048. * }
  17049. * @param {Array|number} value
  17050. * @return {Array|number} result
  17051. */
  17052. echartsProto.convertToPixel = curry(doConvertPixel, 'convertToPixel');
  17053. /**
  17054. * Convert from pixel coordinate system to logical coordinate system.
  17055. * See CoordinateSystem#convertFromPixel.
  17056. * @param {string|Object} finder
  17057. * If string, e.g., 'geo', means {geoIndex: 0}.
  17058. * If Object, could contain some of these properties below:
  17059. * {
  17060. * seriesIndex / seriesId / seriesName,
  17061. * geoIndex / geoId / geoName,
  17062. * bmapIndex / bmapId / bmapName,
  17063. * xAxisIndex / xAxisId / xAxisName,
  17064. * yAxisIndex / yAxisId / yAxisName
  17065. * gridIndex / gridId / gridName,
  17066. * ... (can be extended)
  17067. * }
  17068. * @param {Array|number} value
  17069. * @return {Array|number} result
  17070. */
  17071. echartsProto.convertFromPixel = curry(doConvertPixel, 'convertFromPixel');
  17072. function doConvertPixel(methodName, finder, value) {
  17073. var ecModel = this._model;
  17074. var coordSysList = this._coordSysMgr.getCoordinateSystems();
  17075. var result;
  17076. finder = parseFinder(ecModel, finder);
  17077. for (var i = 0; i < coordSysList.length; i++) {
  17078. var coordSys = coordSysList[i];
  17079. if (coordSys[methodName] && (result = coordSys[methodName](ecModel, finder, value)) != null) {
  17080. return result;
  17081. }
  17082. }
  17083. if (true) {
  17084. console.warn('No coordinate system that supports ' + methodName + ' found by the given finder.');
  17085. }
  17086. }
  17087. /**
  17088. * Is the specified coordinate systems or components contain the given pixel point.
  17089. * @param {string|Object} finder
  17090. * If string, e.g., 'geo', means {geoIndex: 0}.
  17091. * If Object, could contain some of these properties below:
  17092. * {
  17093. * seriesIndex / seriesId / seriesName,
  17094. * geoIndex / geoId / geoName,
  17095. * bmapIndex / bmapId / bmapName,
  17096. * xAxisIndex / xAxisId / xAxisName,
  17097. * yAxisIndex / yAxisId / yAxisName,
  17098. * gridIndex / gridId / gridName,
  17099. * ... (can be extended)
  17100. * }
  17101. * @param {Array|number} value
  17102. * @return {boolean} result
  17103. */
  17104. echartsProto.containPixel = function (finder, value) {
  17105. var ecModel = this._model;
  17106. var result;
  17107. finder = parseFinder(ecModel, finder);
  17108. each$1(finder, function (models, key) {
  17109. key.indexOf('Models') >= 0 && each$1(models, function (model) {
  17110. var coordSys = model.coordinateSystem;
  17111. if (coordSys && coordSys.containPoint) {
  17112. result |= !!coordSys.containPoint(value);
  17113. } else if (key === 'seriesModels') {
  17114. var view = this._chartsMap[model.__viewId];
  17115. if (view && view.containPoint) {
  17116. result |= view.containPoint(value, model);
  17117. } else {
  17118. if (true) {
  17119. console.warn(key + ': ' + (view ? 'The found component do not support containPoint.' : 'No view mapping to the found component.'));
  17120. }
  17121. }
  17122. } else {
  17123. if (true) {
  17124. console.warn(key + ': containPoint is not supported');
  17125. }
  17126. }
  17127. }, this);
  17128. }, this);
  17129. return !!result;
  17130. };
  17131. /**
  17132. * Get visual from series or data.
  17133. * @param {string|Object} finder
  17134. * If string, e.g., 'series', means {seriesIndex: 0}.
  17135. * If Object, could contain some of these properties below:
  17136. * {
  17137. * seriesIndex / seriesId / seriesName,
  17138. * dataIndex / dataIndexInside
  17139. * }
  17140. * If dataIndex is not specified, series visual will be fetched,
  17141. * but not data item visual.
  17142. * If all of seriesIndex, seriesId, seriesName are not specified,
  17143. * visual will be fetched from first series.
  17144. * @param {string} visualType 'color', 'symbol', 'symbolSize'
  17145. */
  17146. echartsProto.getVisual = function (finder, visualType) {
  17147. var ecModel = this._model;
  17148. finder = parseFinder(ecModel, finder, {
  17149. defaultMainType: 'series'
  17150. });
  17151. var seriesModel = finder.seriesModel;
  17152. if (true) {
  17153. if (!seriesModel) {
  17154. console.warn('There is no specified seires model');
  17155. }
  17156. }
  17157. var data = seriesModel.getData();
  17158. var dataIndexInside = finder.hasOwnProperty('dataIndexInside') ? finder.dataIndexInside : finder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(finder.dataIndex) : null;
  17159. return dataIndexInside != null ? data.getItemVisual(dataIndexInside, visualType) : data.getVisual(visualType);
  17160. };
  17161. /**
  17162. * Get view of corresponding component model
  17163. * @param {module:echarts/model/Component} componentModel
  17164. * @return {module:echarts/view/Component}
  17165. */
  17166. echartsProto.getViewOfComponentModel = function (componentModel) {
  17167. return this._componentsMap[componentModel.__viewId];
  17168. };
  17169. /**
  17170. * Get view of corresponding series model
  17171. * @param {module:echarts/model/Series} seriesModel
  17172. * @return {module:echarts/view/Chart}
  17173. */
  17174. echartsProto.getViewOfSeriesModel = function (seriesModel) {
  17175. return this._chartsMap[seriesModel.__viewId];
  17176. };
  17177. var updateMethods = {
  17178. /**
  17179. * @param {Object} payload
  17180. * @private
  17181. */
  17182. update: function (payload) {
  17183. // console.profile && console.profile('update');
  17184. var ecModel = this._model;
  17185. var api = this._api;
  17186. var coordSysMgr = this._coordSysMgr;
  17187. var zr = this._zr; // update before setOption
  17188. if (!ecModel) {
  17189. return;
  17190. } // Fixme First time update ?
  17191. ecModel.restoreData(); // TODO
  17192. // Save total ecModel here for undo/redo (after restoring data and before processing data).
  17193. // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
  17194. // Create new coordinate system each update
  17195. // In LineView may save the old coordinate system and use it to get the orignal point
  17196. coordSysMgr.create(this._model, this._api);
  17197. processData.call(this, ecModel, api);
  17198. stackSeriesData.call(this, ecModel);
  17199. coordSysMgr.update(ecModel, api);
  17200. doVisualEncoding.call(this, ecModel, payload);
  17201. doRender.call(this, ecModel, payload); // Set background
  17202. var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
  17203. var painter = zr.painter; // TODO all use clearColor ?
  17204. if (painter.isSingleCanvas && painter.isSingleCanvas()) {
  17205. zr.configLayer(0, {
  17206. clearColor: backgroundColor
  17207. });
  17208. } else {
  17209. // In IE8
  17210. if (!env$1.canvasSupported) {
  17211. var colorArr = parse(backgroundColor);
  17212. backgroundColor = stringify(colorArr, 'rgb');
  17213. if (colorArr[3] === 0) {
  17214. backgroundColor = 'transparent';
  17215. }
  17216. }
  17217. if (backgroundColor.colorStops || backgroundColor.image) {
  17218. // Gradient background
  17219. // FIXME Fixed layer?
  17220. zr.configLayer(0, {
  17221. clearColor: backgroundColor
  17222. });
  17223. this[HAS_GRADIENT_OR_PATTERN_BG] = true;
  17224. this._dom.style.background = 'transparent';
  17225. } else {
  17226. if (this[HAS_GRADIENT_OR_PATTERN_BG]) {
  17227. zr.configLayer(0, {
  17228. clearColor: null
  17229. });
  17230. }
  17231. this[HAS_GRADIENT_OR_PATTERN_BG] = false;
  17232. this._dom.style.background = backgroundColor;
  17233. }
  17234. }
  17235. each(postUpdateFuncs, function (func) {
  17236. func(ecModel, api);
  17237. }); // console.profile && console.profileEnd('update');
  17238. },
  17239. /**
  17240. * @param {Object} payload
  17241. * @private
  17242. */
  17243. updateView: function (payload) {
  17244. var ecModel = this._model; // update before setOption
  17245. if (!ecModel) {
  17246. return;
  17247. }
  17248. ecModel.eachSeries(function (seriesModel) {
  17249. seriesModel.getData().clearAllVisual();
  17250. });
  17251. doVisualEncoding.call(this, ecModel, payload);
  17252. invokeUpdateMethod.call(this, 'updateView', ecModel, payload);
  17253. },
  17254. /**
  17255. * @param {Object} payload
  17256. * @private
  17257. */
  17258. updateVisual: function (payload) {
  17259. var ecModel = this._model; // update before setOption
  17260. if (!ecModel) {
  17261. return;
  17262. }
  17263. ecModel.eachSeries(function (seriesModel) {
  17264. seriesModel.getData().clearAllVisual();
  17265. });
  17266. doVisualEncoding.call(this, ecModel, payload, true);
  17267. invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload);
  17268. },
  17269. /**
  17270. * @param {Object} payload
  17271. * @private
  17272. */
  17273. updateLayout: function (payload) {
  17274. var ecModel = this._model; // update before setOption
  17275. if (!ecModel) {
  17276. return;
  17277. }
  17278. doLayout.call(this, ecModel, payload);
  17279. invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload);
  17280. },
  17281. /**
  17282. * @param {Object} payload
  17283. * @private
  17284. */
  17285. prepareAndUpdate: function (payload) {
  17286. var ecModel = this._model;
  17287. prepareView.call(this, 'component', ecModel);
  17288. prepareView.call(this, 'chart', ecModel);
  17289. updateMethods.update.call(this, payload);
  17290. }
  17291. };
  17292. /**
  17293. * @private
  17294. */
  17295. function updateDirectly(ecIns, method, payload, mainType, subType) {
  17296. var ecModel = ecIns._model; // broadcast
  17297. if (!mainType) {
  17298. each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
  17299. return;
  17300. }
  17301. var query = {};
  17302. query[mainType + 'Id'] = payload[mainType + 'Id'];
  17303. query[mainType + 'Index'] = payload[mainType + 'Index'];
  17304. query[mainType + 'Name'] = payload[mainType + 'Name'];
  17305. var condition = {
  17306. mainType: mainType,
  17307. query: query
  17308. };
  17309. subType && (condition.subType = subType); // subType may be '' by parseClassType;
  17310. // If dispatchAction before setOption, do nothing.
  17311. ecModel && ecModel.eachComponent(condition, function (model, index) {
  17312. callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]);
  17313. }, ecIns);
  17314. function callView(view) {
  17315. view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload);
  17316. }
  17317. }
  17318. /**
  17319. * Resize the chart
  17320. * @param {Object} opts
  17321. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  17322. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  17323. * @param {boolean} [opts.silent=false]
  17324. */
  17325. echartsProto.resize = function (opts) {
  17326. if (true) {
  17327. assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
  17328. }
  17329. this[IN_MAIN_PROCESS] = true;
  17330. this._zr.resize(opts);
  17331. var optionChanged = this._model && this._model.resetOption('media');
  17332. var updateMethod = optionChanged ? 'prepareAndUpdate' : 'update';
  17333. updateMethods[updateMethod].call(this); // Resize loading effect
  17334. this._loadingFX && this._loadingFX.resize();
  17335. this[IN_MAIN_PROCESS] = false;
  17336. var silent = opts && opts.silent;
  17337. flushPendingActions.call(this, silent);
  17338. triggerUpdatedEvent.call(this, silent);
  17339. };
  17340. /**
  17341. * Show loading effect
  17342. * @param {string} [name='default']
  17343. * @param {Object} [cfg]
  17344. */
  17345. echartsProto.showLoading = function (name, cfg) {
  17346. if (isObject(name)) {
  17347. cfg = name;
  17348. name = '';
  17349. }
  17350. name = name || 'default';
  17351. this.hideLoading();
  17352. if (!loadingEffects[name]) {
  17353. if (true) {
  17354. console.warn('Loading effects ' + name + ' not exists.');
  17355. }
  17356. return;
  17357. }
  17358. var el = loadingEffects[name](this._api, cfg);
  17359. var zr = this._zr;
  17360. this._loadingFX = el;
  17361. zr.add(el);
  17362. };
  17363. /**
  17364. * Hide loading effect
  17365. */
  17366. echartsProto.hideLoading = function () {
  17367. this._loadingFX && this._zr.remove(this._loadingFX);
  17368. this._loadingFX = null;
  17369. };
  17370. /**
  17371. * @param {Object} eventObj
  17372. * @return {Object}
  17373. */
  17374. echartsProto.makeActionFromEvent = function (eventObj) {
  17375. var payload = extend({}, eventObj);
  17376. payload.type = eventActionMap[eventObj.type];
  17377. return payload;
  17378. };
  17379. /**
  17380. * @pubilc
  17381. * @param {Object} payload
  17382. * @param {string} [payload.type] Action type
  17383. * @param {Object|boolean} [opt] If pass boolean, means opt.silent
  17384. * @param {boolean} [opt.silent=false] Whether trigger events.
  17385. * @param {boolean} [opt.flush=undefined]
  17386. * true: Flush immediately, and then pixel in canvas can be fetched
  17387. * immediately. Caution: it might affect performance.
  17388. * false: Not not flush.
  17389. * undefined: Auto decide whether perform flush.
  17390. */
  17391. echartsProto.dispatchAction = function (payload, opt) {
  17392. if (!isObject(opt)) {
  17393. opt = {
  17394. silent: !!opt
  17395. };
  17396. }
  17397. if (!actions[payload.type]) {
  17398. return;
  17399. } // Avoid dispatch action before setOption. Especially in `connect`.
  17400. if (!this._model) {
  17401. return;
  17402. } // May dispatchAction in rendering procedure
  17403. if (this[IN_MAIN_PROCESS]) {
  17404. this._pendingActions.push(payload);
  17405. return;
  17406. }
  17407. doDispatchAction.call(this, payload, opt.silent);
  17408. if (opt.flush) {
  17409. this._zr.flush(true);
  17410. } else if (opt.flush !== false && env$1.browser.weChat) {
  17411. // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
  17412. // hang when sliding page (on touch event), which cause that zr does not
  17413. // refresh util user interaction finished, which is not expected.
  17414. // But `dispatchAction` may be called too frequently when pan on touch
  17415. // screen, which impacts performance if do not throttle them.
  17416. this._throttledZrFlush();
  17417. }
  17418. flushPendingActions.call(this, opt.silent);
  17419. triggerUpdatedEvent.call(this, opt.silent);
  17420. };
  17421. function doDispatchAction(payload, silent) {
  17422. var payloadType = payload.type;
  17423. var escapeConnect = payload.escapeConnect;
  17424. var actionWrap = actions[payloadType];
  17425. var actionInfo = actionWrap.actionInfo;
  17426. var cptType = (actionInfo.update || 'update').split(':');
  17427. var updateMethod = cptType.pop();
  17428. cptType = cptType[0] != null && parseClassType(cptType[0]);
  17429. this[IN_MAIN_PROCESS] = true;
  17430. var payloads = [payload];
  17431. var batched = false; // Batch action
  17432. if (payload.batch) {
  17433. batched = true;
  17434. payloads = map(payload.batch, function (item) {
  17435. item = defaults(extend({}, item), payload);
  17436. item.batch = null;
  17437. return item;
  17438. });
  17439. }
  17440. var eventObjBatch = [];
  17441. var eventObj;
  17442. var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
  17443. each(payloads, function (batchItem) {
  17444. // Action can specify the event by return it.
  17445. eventObj = actionWrap.action(batchItem, this._model, this._api); // Emit event outside
  17446. eventObj = eventObj || extend({}, batchItem); // Convert type to eventType
  17447. eventObj.type = actionInfo.event || eventObj.type;
  17448. eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.
  17449. if (isHighDown) {
  17450. // method, payload, mainType, subType
  17451. updateDirectly(this, updateMethod, batchItem, 'series');
  17452. } else if (cptType) {
  17453. updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
  17454. }
  17455. }, this);
  17456. if (updateMethod !== 'none' && !isHighDown && !cptType) {
  17457. // Still dirty
  17458. if (this[OPTION_UPDATED]) {
  17459. // FIXME Pass payload ?
  17460. updateMethods.prepareAndUpdate.call(this, payload);
  17461. this[OPTION_UPDATED] = false;
  17462. } else {
  17463. updateMethods[updateMethod].call(this, payload);
  17464. }
  17465. } // Follow the rule of action batch
  17466. if (batched) {
  17467. eventObj = {
  17468. type: actionInfo.event || payloadType,
  17469. escapeConnect: escapeConnect,
  17470. batch: eventObjBatch
  17471. };
  17472. } else {
  17473. eventObj = eventObjBatch[0];
  17474. }
  17475. this[IN_MAIN_PROCESS] = false;
  17476. !silent && this._messageCenter.trigger(eventObj.type, eventObj);
  17477. }
  17478. function flushPendingActions(silent) {
  17479. var pendingActions = this._pendingActions;
  17480. while (pendingActions.length) {
  17481. var payload = pendingActions.shift();
  17482. doDispatchAction.call(this, payload, silent);
  17483. }
  17484. }
  17485. function triggerUpdatedEvent(silent) {
  17486. !silent && this.trigger('updated');
  17487. }
  17488. /**
  17489. * Register event
  17490. * @method
  17491. */
  17492. echartsProto.on = createRegisterEventWithLowercaseName('on');
  17493. echartsProto.off = createRegisterEventWithLowercaseName('off');
  17494. echartsProto.one = createRegisterEventWithLowercaseName('one');
  17495. /**
  17496. * @param {string} methodName
  17497. * @private
  17498. */
  17499. function invokeUpdateMethod(methodName, ecModel, payload) {
  17500. var api = this._api; // Update all components
  17501. each(this._componentsViews, function (component) {
  17502. var componentModel = component.__model;
  17503. component[methodName](componentModel, ecModel, api, payload);
  17504. updateZ(componentModel, component);
  17505. }, this); // Upate all charts
  17506. ecModel.eachSeries(function (seriesModel, idx) {
  17507. var chart = this._chartsMap[seriesModel.__viewId];
  17508. chart[methodName](seriesModel, ecModel, api, payload);
  17509. updateZ(seriesModel, chart);
  17510. updateProgressiveAndBlend(seriesModel, chart);
  17511. }, this); // If use hover layer
  17512. updateHoverLayerStatus(this._zr, ecModel); // Post render
  17513. each(postUpdateFuncs, function (func) {
  17514. func(ecModel, api);
  17515. });
  17516. }
  17517. /**
  17518. * Prepare view instances of charts and components
  17519. * @param {module:echarts/model/Global} ecModel
  17520. * @private
  17521. */
  17522. function prepareView(type, ecModel) {
  17523. var isComponent = type === 'component';
  17524. var viewList = isComponent ? this._componentsViews : this._chartsViews;
  17525. var viewMap = isComponent ? this._componentsMap : this._chartsMap;
  17526. var zr = this._zr;
  17527. for (var i = 0; i < viewList.length; i++) {
  17528. viewList[i].__alive = false;
  17529. }
  17530. ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) {
  17531. if (isComponent) {
  17532. if (componentType === 'series') {
  17533. return;
  17534. }
  17535. } else {
  17536. model = componentType;
  17537. } // Consider: id same and type changed.
  17538. var viewId = '_ec_' + model.id + '_' + model.type;
  17539. var view = viewMap[viewId];
  17540. if (!view) {
  17541. var classType = parseClassType(model.type);
  17542. var Clazz = isComponent ? Component$1.getClass(classType.main, classType.sub) : Chart.getClass(classType.sub);
  17543. if (Clazz) {
  17544. view = new Clazz();
  17545. view.init(ecModel, this._api);
  17546. viewMap[viewId] = view;
  17547. viewList.push(view);
  17548. zr.add(view.group);
  17549. } else {
  17550. // Error
  17551. return;
  17552. }
  17553. }
  17554. model.__viewId = view.__id = viewId;
  17555. view.__alive = true;
  17556. view.__model = model;
  17557. view.group.__ecComponentInfo = {
  17558. mainType: model.mainType,
  17559. index: model.componentIndex
  17560. };
  17561. }, this);
  17562. for (var i = 0; i < viewList.length;) {
  17563. var view = viewList[i];
  17564. if (!view.__alive) {
  17565. zr.remove(view.group);
  17566. view.dispose(ecModel, this._api);
  17567. viewList.splice(i, 1);
  17568. delete viewMap[view.__id];
  17569. view.__id = view.group.__ecComponentInfo = null;
  17570. } else {
  17571. i++;
  17572. }
  17573. }
  17574. }
  17575. /**
  17576. * Processor data in each series
  17577. *
  17578. * @param {module:echarts/model/Global} ecModel
  17579. * @private
  17580. */
  17581. function processData(ecModel, api) {
  17582. each(dataProcessorFuncs, function (process) {
  17583. process.func(ecModel, api);
  17584. });
  17585. }
  17586. /**
  17587. * @private
  17588. */
  17589. function stackSeriesData(ecModel) {
  17590. var stackedDataMap = {};
  17591. ecModel.eachSeries(function (series) {
  17592. var stack = series.get('stack');
  17593. var data = series.getData();
  17594. if (stack && data.type === 'list') {
  17595. var previousStack = stackedDataMap[stack]; // Avoid conflict with Object.prototype
  17596. if (stackedDataMap.hasOwnProperty(stack) && previousStack) {
  17597. data.stackedOn = previousStack;
  17598. }
  17599. stackedDataMap[stack] = data;
  17600. }
  17601. });
  17602. }
  17603. /**
  17604. * Layout before each chart render there series, special visual encoding stage
  17605. *
  17606. * @param {module:echarts/model/Global} ecModel
  17607. * @private
  17608. */
  17609. function doLayout(ecModel, payload) {
  17610. var api = this._api;
  17611. each(visualFuncs, function (visual) {
  17612. if (visual.isLayout) {
  17613. visual.func(ecModel, api, payload);
  17614. }
  17615. });
  17616. }
  17617. /**
  17618. * Encode visual infomation from data after data processing
  17619. *
  17620. * @param {module:echarts/model/Global} ecModel
  17621. * @param {object} layout
  17622. * @param {boolean} [excludesLayout]
  17623. * @private
  17624. */
  17625. function doVisualEncoding(ecModel, payload, excludesLayout) {
  17626. var api = this._api;
  17627. ecModel.clearColorPalette();
  17628. ecModel.eachSeries(function (seriesModel) {
  17629. seriesModel.clearColorPalette();
  17630. });
  17631. each(visualFuncs, function (visual) {
  17632. (!excludesLayout || !visual.isLayout) && visual.func(ecModel, api, payload);
  17633. });
  17634. }
  17635. /**
  17636. * Render each chart and component
  17637. * @private
  17638. */
  17639. function doRender(ecModel, payload) {
  17640. var api = this._api; // Render all components
  17641. each(this._componentsViews, function (componentView) {
  17642. var componentModel = componentView.__model;
  17643. componentView.render(componentModel, ecModel, api, payload);
  17644. updateZ(componentModel, componentView);
  17645. }, this);
  17646. each(this._chartsViews, function (chart) {
  17647. chart.__alive = false;
  17648. }, this); // Render all charts
  17649. ecModel.eachSeries(function (seriesModel, idx) {
  17650. var chartView = this._chartsMap[seriesModel.__viewId];
  17651. chartView.__alive = true;
  17652. chartView.render(seriesModel, ecModel, api, payload);
  17653. chartView.group.silent = !!seriesModel.get('silent');
  17654. updateZ(seriesModel, chartView);
  17655. updateProgressiveAndBlend(seriesModel, chartView);
  17656. }, this); // If use hover layer
  17657. updateHoverLayerStatus(this._zr, ecModel); // Remove groups of unrendered charts
  17658. each(this._chartsViews, function (chart) {
  17659. if (!chart.__alive) {
  17660. chart.remove(ecModel, api);
  17661. }
  17662. }, this);
  17663. }
  17664. var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];
  17665. /**
  17666. * @private
  17667. */
  17668. echartsProto._initEvents = function () {
  17669. each(MOUSE_EVENT_NAMES, function (eveName) {
  17670. this._zr.on(eveName, function (e) {
  17671. var ecModel = this.getModel();
  17672. var el = e.target;
  17673. var params; // no e.target when 'globalout'.
  17674. if (eveName === 'globalout') {
  17675. params = {};
  17676. } else if (el && el.dataIndex != null) {
  17677. var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
  17678. params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};
  17679. } // If element has custom eventData of components
  17680. else if (el && el.eventData) {
  17681. params = extend({}, el.eventData);
  17682. }
  17683. if (params) {
  17684. params.event = e;
  17685. params.type = eveName;
  17686. this.trigger(eveName, params);
  17687. }
  17688. }, this);
  17689. }, this);
  17690. each(eventActionMap, function (actionType, eventType) {
  17691. this._messageCenter.on(eventType, function (event) {
  17692. this.trigger(eventType, event);
  17693. }, this);
  17694. }, this);
  17695. };
  17696. /**
  17697. * @return {boolean}
  17698. */
  17699. echartsProto.isDisposed = function () {
  17700. return this._disposed;
  17701. };
  17702. /**
  17703. * Clear
  17704. */
  17705. echartsProto.clear = function () {
  17706. this.setOption({
  17707. series: []
  17708. }, true);
  17709. };
  17710. /**
  17711. * Dispose instance
  17712. */
  17713. echartsProto.dispose = function () {
  17714. if (this._disposed) {
  17715. if (true) {
  17716. console.warn('Instance ' + this.id + ' has been disposed');
  17717. }
  17718. return;
  17719. }
  17720. this._disposed = true;
  17721. var api = this._api;
  17722. var ecModel = this._model;
  17723. each(this._componentsViews, function (component) {
  17724. component.dispose(ecModel, api);
  17725. });
  17726. each(this._chartsViews, function (chart) {
  17727. chart.dispose(ecModel, api);
  17728. }); // Dispose after all views disposed
  17729. this._zr.dispose();
  17730. delete instances[this.id];
  17731. };
  17732. mixin(ECharts, Eventful);
  17733. function updateHoverLayerStatus(zr, ecModel) {
  17734. var storage = zr.storage;
  17735. var elCount = 0;
  17736. storage.traverse(function (el) {
  17737. if (!el.isGroup) {
  17738. elCount++;
  17739. }
  17740. });
  17741. if (elCount > ecModel.get('hoverLayerThreshold') && !env$1.node) {
  17742. storage.traverse(function (el) {
  17743. if (!el.isGroup) {
  17744. el.useHoverLayer = true;
  17745. }
  17746. });
  17747. }
  17748. }
  17749. /**
  17750. * Update chart progressive and blend.
  17751. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  17752. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  17753. */
  17754. function updateProgressiveAndBlend(seriesModel, chartView) {
  17755. // Progressive configuration
  17756. var elCount = 0;
  17757. chartView.group.traverse(function (el) {
  17758. if (el.type !== 'group' && !el.ignore) {
  17759. elCount++;
  17760. }
  17761. });
  17762. var frameDrawNum = +seriesModel.get('progressive');
  17763. var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env$1.node;
  17764. if (needProgressive) {
  17765. chartView.group.traverse(function (el) {
  17766. // FIXME marker and other components
  17767. if (!el.isGroup) {
  17768. el.progressive = needProgressive ? Math.floor(elCount++ / frameDrawNum) : -1;
  17769. if (needProgressive) {
  17770. el.stopAnimation(true);
  17771. }
  17772. }
  17773. });
  17774. } // Blend configration
  17775. var blendMode = seriesModel.get('blendMode') || null;
  17776. if (true) {
  17777. if (!env$1.canvasSupported && blendMode && blendMode !== 'source-over') {
  17778. console.warn('Only canvas support blendMode');
  17779. }
  17780. }
  17781. chartView.group.traverse(function (el) {
  17782. // FIXME marker and other components
  17783. if (!el.isGroup) {
  17784. el.setStyle('blend', blendMode);
  17785. }
  17786. });
  17787. }
  17788. /**
  17789. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  17790. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  17791. */
  17792. function updateZ(model, view) {
  17793. var z = model.get('z');
  17794. var zlevel = model.get('zlevel'); // Set z and zlevel
  17795. view.group.traverse(function (el) {
  17796. if (el.type !== 'group') {
  17797. z != null && (el.z = z);
  17798. zlevel != null && (el.zlevel = zlevel);
  17799. }
  17800. });
  17801. }
  17802. function createExtensionAPI(ecInstance) {
  17803. var coordSysMgr = ecInstance._coordSysMgr;
  17804. return extend(new ExtensionAPI(ecInstance), {
  17805. // Inject methods
  17806. getCoordinateSystems: bind(coordSysMgr.getCoordinateSystems, coordSysMgr),
  17807. getComponentByElement: function (el) {
  17808. while (el) {
  17809. var modelInfo = el.__ecComponentInfo;
  17810. if (modelInfo != null) {
  17811. return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
  17812. }
  17813. el = el.parent;
  17814. }
  17815. }
  17816. });
  17817. }
  17818. /**
  17819. * @type {Object} key: actionType.
  17820. * @inner
  17821. */
  17822. var actions = {};
  17823. /**
  17824. * Map eventType to actionType
  17825. * @type {Object}
  17826. */
  17827. var eventActionMap = {};
  17828. /**
  17829. * Data processor functions of each stage
  17830. * @type {Array.<Object.<string, Function>>}
  17831. * @inner
  17832. */
  17833. var dataProcessorFuncs = [];
  17834. /**
  17835. * @type {Array.<Function>}
  17836. * @inner
  17837. */
  17838. var optionPreprocessorFuncs = [];
  17839. /**
  17840. * @type {Array.<Function>}
  17841. * @inner
  17842. */
  17843. var postUpdateFuncs = [];
  17844. /**
  17845. * Visual encoding functions of each stage
  17846. * @type {Array.<Object.<string, Function>>}
  17847. * @inner
  17848. */
  17849. var visualFuncs = [];
  17850. /**
  17851. * Theme storage
  17852. * @type {Object.<key, Object>}
  17853. */
  17854. var themeStorage = {};
  17855. /**
  17856. * Loading effects
  17857. */
  17858. var loadingEffects = {};
  17859. var instances = {};
  17860. var connectedGroups = {};
  17861. var idBase = new Date() - 0;
  17862. var groupIdBase = new Date() - 0;
  17863. var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
  17864. function enableConnect(chart) {
  17865. var STATUS_PENDING = 0;
  17866. var STATUS_UPDATING = 1;
  17867. var STATUS_UPDATED = 2;
  17868. var STATUS_KEY = '__connectUpdateStatus';
  17869. function updateConnectedChartsStatus(charts, status) {
  17870. for (var i = 0; i < charts.length; i++) {
  17871. var otherChart = charts[i];
  17872. otherChart[STATUS_KEY] = status;
  17873. }
  17874. }
  17875. each$1(eventActionMap, function (actionType, eventType) {
  17876. chart._messageCenter.on(eventType, function (event) {
  17877. if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
  17878. if (event && event.escapeConnect) {
  17879. return;
  17880. }
  17881. var action = chart.makeActionFromEvent(event);
  17882. var otherCharts = [];
  17883. each$1(instances, function (otherChart) {
  17884. if (otherChart !== chart && otherChart.group === chart.group) {
  17885. otherCharts.push(otherChart);
  17886. }
  17887. });
  17888. updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
  17889. each(otherCharts, function (otherChart) {
  17890. if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
  17891. otherChart.dispatchAction(action);
  17892. }
  17893. });
  17894. updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
  17895. }
  17896. });
  17897. });
  17898. }
  17899. /**
  17900. * @param {HTMLElement} dom
  17901. * @param {Object} [theme]
  17902. * @param {Object} opts
  17903. * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
  17904. * @param {string} [opts.renderer] Currently only 'canvas' is supported.
  17905. * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
  17906. * Can be 'auto' (the same as null/undefined)
  17907. * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
  17908. * Can be 'auto' (the same as null/undefined)
  17909. */
  17910. function init(dom, theme, opts) {
  17911. if (true) {
  17912. // Check version
  17913. if (version$1.replace('.', '') - 0 < dependencies.zrender.replace('.', '') - 0) {
  17914. throw new Error('zrender/src ' + version$1 + ' is too old for ECharts ' + version + '. Current version need ZRender ' + dependencies.zrender + '+');
  17915. }
  17916. if (!dom) {
  17917. throw new Error('Initialize failed: invalid dom.');
  17918. }
  17919. }
  17920. var existInstance = getInstanceByDom(dom);
  17921. if (existInstance) {
  17922. if (true) {
  17923. console.warn('There is a chart instance already initialized on the dom.');
  17924. }
  17925. return existInstance;
  17926. }
  17927. if (true) {
  17928. if (isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth && (!opts || opts.width == null) || !dom.clientHeight && (!opts || opts.height == null))) {
  17929. console.warn('Can\'t get dom width or height');
  17930. }
  17931. }
  17932. var chart = new ECharts(dom, theme, opts);
  17933. chart.id = 'ec_' + idBase++;
  17934. instances[chart.id] = chart;
  17935. if (dom.setAttribute) {
  17936. dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id);
  17937. } else {
  17938. dom[DOM_ATTRIBUTE_KEY] = chart.id;
  17939. }
  17940. enableConnect(chart);
  17941. return chart;
  17942. }
  17943. /**
  17944. * @return {string|Array.<module:echarts~ECharts>} groupId
  17945. */
  17946. function connect(groupId) {
  17947. // Is array of charts
  17948. if (isArray(groupId)) {
  17949. var charts = groupId;
  17950. groupId = null; // If any chart has group
  17951. each$1(charts, function (chart) {
  17952. if (chart.group != null) {
  17953. groupId = chart.group;
  17954. }
  17955. });
  17956. groupId = groupId || 'g_' + groupIdBase++;
  17957. each$1(charts, function (chart) {
  17958. chart.group = groupId;
  17959. });
  17960. }
  17961. connectedGroups[groupId] = true;
  17962. return groupId;
  17963. }
  17964. /**
  17965. * @DEPRECATED
  17966. * @return {string} groupId
  17967. */
  17968. function disConnect(groupId) {
  17969. connectedGroups[groupId] = false;
  17970. }
  17971. /**
  17972. * @return {string} groupId
  17973. */
  17974. var disconnect = disConnect;
  17975. /**
  17976. * Dispose a chart instance
  17977. * @param {module:echarts~ECharts|HTMLDomElement|string} chart
  17978. */
  17979. function dispose(chart) {
  17980. if (typeof chart === 'string') {
  17981. chart = instances[chart];
  17982. } else if (!(chart instanceof ECharts)) {
  17983. // Try to treat as dom
  17984. chart = getInstanceByDom(chart);
  17985. }
  17986. if (chart instanceof ECharts && !chart.isDisposed()) {
  17987. chart.dispose();
  17988. }
  17989. }
  17990. /**
  17991. * @param {HTMLElement} dom
  17992. * @return {echarts~ECharts}
  17993. */
  17994. function getInstanceByDom(dom) {
  17995. var key;
  17996. if (dom.getAttribute) {
  17997. key = dom.getAttribute(DOM_ATTRIBUTE_KEY);
  17998. } else {
  17999. key = dom[DOM_ATTRIBUTE_KEY];
  18000. }
  18001. return instances[key];
  18002. }
  18003. /**
  18004. * @param {string} key
  18005. * @return {echarts~ECharts}
  18006. */
  18007. function getInstanceById(key) {
  18008. return instances[key];
  18009. }
  18010. /**
  18011. * Register theme
  18012. */
  18013. function registerTheme(name, theme) {
  18014. themeStorage[name] = theme;
  18015. }
  18016. /**
  18017. * Register option preprocessor
  18018. * @param {Function} preprocessorFunc
  18019. */
  18020. function registerPreprocessor(preprocessorFunc) {
  18021. optionPreprocessorFuncs.push(preprocessorFunc);
  18022. }
  18023. /**
  18024. * @param {number} [priority=1000]
  18025. * @param {Function} processorFunc
  18026. */
  18027. function registerProcessor(priority, processorFunc) {
  18028. if (typeof priority === 'function') {
  18029. processorFunc = priority;
  18030. priority = PRIORITY_PROCESSOR_FILTER;
  18031. }
  18032. if (true) {
  18033. if (isNaN(priority)) {
  18034. throw new Error('Unkown processor priority');
  18035. }
  18036. }
  18037. dataProcessorFuncs.push({
  18038. prio: priority,
  18039. func: processorFunc
  18040. });
  18041. }
  18042. /**
  18043. * Register postUpdater
  18044. * @param {Function} postUpdateFunc
  18045. */
  18046. function registerPostUpdate(postUpdateFunc) {
  18047. postUpdateFuncs.push(postUpdateFunc);
  18048. }
  18049. /**
  18050. * Usage:
  18051. * registerAction('someAction', 'someEvent', function () { ... });
  18052. * registerAction('someAction', function () { ... });
  18053. * registerAction(
  18054. * {type: 'someAction', event: 'someEvent', update: 'updateView'},
  18055. * function () { ... }
  18056. * );
  18057. *
  18058. * @param {(string|Object)} actionInfo
  18059. * @param {string} actionInfo.type
  18060. * @param {string} [actionInfo.event]
  18061. * @param {string} [actionInfo.update]
  18062. * @param {string} [eventName]
  18063. * @param {Function} action
  18064. */
  18065. function registerAction(actionInfo, eventName, action) {
  18066. if (typeof eventName === 'function') {
  18067. action = eventName;
  18068. eventName = '';
  18069. }
  18070. var actionType = isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = {
  18071. event: eventName
  18072. }][0]; // Event name is all lowercase
  18073. actionInfo.event = (actionInfo.event || actionType).toLowerCase();
  18074. eventName = actionInfo.event; // Validate action type and event name.
  18075. assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
  18076. if (!actions[actionType]) {
  18077. actions[actionType] = {
  18078. action: action,
  18079. actionInfo: actionInfo
  18080. };
  18081. }
  18082. eventActionMap[eventName] = actionType;
  18083. }
  18084. /**
  18085. * @param {string} type
  18086. * @param {*} CoordinateSystem
  18087. */
  18088. function registerCoordinateSystem(type, CoordinateSystem$$1) {
  18089. CoordinateSystemManager.register(type, CoordinateSystem$$1);
  18090. }
  18091. /**
  18092. * Get dimensions of specified coordinate system.
  18093. * @param {string} type
  18094. * @return {Array.<string|Object>}
  18095. */
  18096. function getCoordinateSystemDimensions(type) {
  18097. var coordSysCreator = CoordinateSystemManager.get(type);
  18098. if (coordSysCreator) {
  18099. return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice();
  18100. }
  18101. }
  18102. /**
  18103. * Layout is a special stage of visual encoding
  18104. * Most visual encoding like color are common for different chart
  18105. * But each chart has it's own layout algorithm
  18106. *
  18107. * @param {number} [priority=1000]
  18108. * @param {Function} layoutFunc
  18109. */
  18110. function registerLayout(priority, layoutFunc) {
  18111. if (typeof priority === 'function') {
  18112. layoutFunc = priority;
  18113. priority = PRIORITY_VISUAL_LAYOUT;
  18114. }
  18115. if (true) {
  18116. if (isNaN(priority)) {
  18117. throw new Error('Unkown layout priority');
  18118. }
  18119. }
  18120. visualFuncs.push({
  18121. prio: priority,
  18122. func: layoutFunc,
  18123. isLayout: true
  18124. });
  18125. }
  18126. /**
  18127. * @param {number} [priority=3000]
  18128. * @param {Function} visualFunc
  18129. */
  18130. function registerVisual(priority, visualFunc) {
  18131. if (typeof priority === 'function') {
  18132. visualFunc = priority;
  18133. priority = PRIORITY_VISUAL_CHART;
  18134. }
  18135. if (true) {
  18136. if (isNaN(priority)) {
  18137. throw new Error('Unkown visual priority');
  18138. }
  18139. }
  18140. visualFuncs.push({
  18141. prio: priority,
  18142. func: visualFunc
  18143. });
  18144. }
  18145. /**
  18146. * @param {string} name
  18147. */
  18148. function registerLoading(name, loadingFx) {
  18149. loadingEffects[name] = loadingFx;
  18150. }
  18151. /**
  18152. * @param {Object} opts
  18153. * @param {string} [superClass]
  18154. */
  18155. function extendComponentModel(opts
  18156. /*, superClass*/
  18157. ) {
  18158. // var Clazz = ComponentModel;
  18159. // if (superClass) {
  18160. // var classType = parseClassType(superClass);
  18161. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  18162. // }
  18163. return ComponentModel.extend(opts);
  18164. }
  18165. /**
  18166. * @param {Object} opts
  18167. * @param {string} [superClass]
  18168. */
  18169. function extendComponentView(opts
  18170. /*, superClass*/
  18171. ) {
  18172. // var Clazz = ComponentView;
  18173. // if (superClass) {
  18174. // var classType = parseClassType(superClass);
  18175. // Clazz = ComponentView.getClass(classType.main, classType.sub, true);
  18176. // }
  18177. return Component$1.extend(opts);
  18178. }
  18179. /**
  18180. * @param {Object} opts
  18181. * @param {string} [superClass]
  18182. */
  18183. function extendSeriesModel(opts
  18184. /*, superClass*/
  18185. ) {
  18186. // var Clazz = SeriesModel;
  18187. // if (superClass) {
  18188. // superClass = 'series.' + superClass.replace('series.', '');
  18189. // var classType = parseClassType(superClass);
  18190. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  18191. // }
  18192. return SeriesModel.extend(opts);
  18193. }
  18194. /**
  18195. * @param {Object} opts
  18196. * @param {string} [superClass]
  18197. */
  18198. function extendChartView(opts
  18199. /*, superClass*/
  18200. ) {
  18201. // var Clazz = ChartView;
  18202. // if (superClass) {
  18203. // superClass = superClass.replace('series.', '');
  18204. // var classType = parseClassType(superClass);
  18205. // Clazz = ChartView.getClass(classType.main, true);
  18206. // }
  18207. return Chart.extend(opts);
  18208. }
  18209. /**
  18210. * ZRender need a canvas context to do measureText.
  18211. * But in node environment canvas may be created by node-canvas.
  18212. * So we need to specify how to create a canvas instead of using document.createElement('canvas')
  18213. *
  18214. * Be careful of using it in the browser.
  18215. *
  18216. * @param {Function} creator
  18217. * @example
  18218. * var Canvas = require('canvas');
  18219. * var echarts = require('echarts');
  18220. * echarts.setCanvasCreator(function () {
  18221. * // Small size is enough.
  18222. * return new Canvas(32, 32);
  18223. * });
  18224. */
  18225. function setCanvasCreator(creator) {
  18226. undefined(creator);
  18227. }
  18228. registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
  18229. registerPreprocessor(backwardCompat);
  18230. registerLoading('default', loadingDefault); // Default actions
  18231. registerAction({
  18232. type: 'highlight',
  18233. event: 'highlight',
  18234. update: 'highlight'
  18235. }, noop);
  18236. registerAction({
  18237. type: 'downplay',
  18238. event: 'downplay',
  18239. update: 'downplay'
  18240. }, noop); // --------
  18241. // Exports
  18242. // --------
  18243. var $inject = {
  18244. registerMap: function (f) {
  18245. exports.registerMap = f;
  18246. },
  18247. getMap: function (f) {
  18248. exports.getMap = f;
  18249. },
  18250. parseGeoJSON: function (f) {
  18251. exports.parseGeoJSON = f;
  18252. }
  18253. };
  18254. function defaultKeyGetter(item) {
  18255. return item;
  18256. }
  18257. /**
  18258. * @param {Array} oldArr
  18259. * @param {Array} newArr
  18260. * @param {Function} oldKeyGetter
  18261. * @param {Function} newKeyGetter
  18262. * @param {Object} [context] Can be visited by this.context in callback.
  18263. */
  18264. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
  18265. this._old = oldArr;
  18266. this._new = newArr;
  18267. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  18268. this._newKeyGetter = newKeyGetter || defaultKeyGetter;
  18269. this.context = context;
  18270. }
  18271. DataDiffer.prototype = {
  18272. constructor: DataDiffer,
  18273. /**
  18274. * Callback function when add a data
  18275. */
  18276. add: function (func) {
  18277. this._add = func;
  18278. return this;
  18279. },
  18280. /**
  18281. * Callback function when update a data
  18282. */
  18283. update: function (func) {
  18284. this._update = func;
  18285. return this;
  18286. },
  18287. /**
  18288. * Callback function when remove a data
  18289. */
  18290. remove: function (func) {
  18291. this._remove = func;
  18292. return this;
  18293. },
  18294. execute: function () {
  18295. var oldArr = this._old;
  18296. var newArr = this._new;
  18297. var oldDataIndexMap = {};
  18298. var newDataIndexMap = {};
  18299. var oldDataKeyArr = [];
  18300. var newDataKeyArr = [];
  18301. var i;
  18302. initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
  18303. initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this); // Travel by inverted order to make sure order consistency
  18304. // when duplicate keys exists (consider newDataIndex.pop() below).
  18305. // For performance consideration, these code below do not look neat.
  18306. for (i = 0; i < oldArr.length; i++) {
  18307. var key = oldDataKeyArr[i];
  18308. var idx = newDataIndexMap[key]; // idx can never be empty array here. see 'set null' logic below.
  18309. if (idx != null) {
  18310. // Consider there is duplicate key (for example, use dataItem.name as key).
  18311. // We should make sure every item in newArr and oldArr can be visited.
  18312. var len = idx.length;
  18313. if (len) {
  18314. len === 1 && (newDataIndexMap[key] = null);
  18315. idx = idx.unshift();
  18316. } else {
  18317. newDataIndexMap[key] = null;
  18318. }
  18319. this._update && this._update(idx, i);
  18320. } else {
  18321. this._remove && this._remove(i);
  18322. }
  18323. }
  18324. for (var i = 0; i < newDataKeyArr.length; i++) {
  18325. var key = newDataKeyArr[i];
  18326. if (newDataIndexMap.hasOwnProperty(key)) {
  18327. var idx = newDataIndexMap[key];
  18328. if (idx == null) {
  18329. continue;
  18330. } // idx can never be empty array here. see 'set null' logic above.
  18331. if (!idx.length) {
  18332. this._add && this._add(idx);
  18333. } else {
  18334. for (var j = 0, len = idx.length; j < len; j++) {
  18335. this._add && this._add(idx[j]);
  18336. }
  18337. }
  18338. }
  18339. }
  18340. }
  18341. };
  18342. function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
  18343. for (var i = 0; i < arr.length; i++) {
  18344. // Add prefix to avoid conflict with Object.prototype.
  18345. var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
  18346. var existence = map[key];
  18347. if (existence == null) {
  18348. keyArr.push(key);
  18349. map[key] = i;
  18350. } else {
  18351. if (!existence.length) {
  18352. map[key] = existence = [existence];
  18353. }
  18354. existence.push(i);
  18355. }
  18356. }
  18357. }
  18358. /**
  18359. * List for data storage
  18360. * @module echarts/data/List
  18361. */
  18362. var isObject$4 = isObject;
  18363. var UNDEFINED = 'undefined';
  18364. var globalObj = typeof window === UNDEFINED ? global : window;
  18365. var dataCtors = {
  18366. 'float': typeof globalObj.Float64Array === UNDEFINED ? Array : globalObj.Float64Array,
  18367. 'int': typeof globalObj.Int32Array === UNDEFINED ? Array : globalObj.Int32Array,
  18368. // Ordinal data type can be string or int
  18369. 'ordinal': Array,
  18370. 'number': Array,
  18371. 'time': Array
  18372. };
  18373. var TRANSFERABLE_PROPERTIES = ['stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData'];
  18374. function transferProperties(a, b) {
  18375. each$1(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {
  18376. if (b.hasOwnProperty(propName)) {
  18377. a[propName] = b[propName];
  18378. }
  18379. });
  18380. a.__wrappedMethods = b.__wrappedMethods;
  18381. }
  18382. function DefaultDataProvider(dataArray) {
  18383. this._array = dataArray || [];
  18384. }
  18385. DefaultDataProvider.prototype.pure = false;
  18386. DefaultDataProvider.prototype.count = function () {
  18387. return this._array.length;
  18388. };
  18389. DefaultDataProvider.prototype.getItem = function (idx) {
  18390. return this._array[idx];
  18391. };
  18392. /**
  18393. * @constructor
  18394. * @alias module:echarts/data/List
  18395. *
  18396. * @param {Array.<string|Object>} dimensions
  18397. * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
  18398. * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
  18399. * @param {module:echarts/model/Model} hostModel
  18400. */
  18401. var List = function (dimensions, hostModel) {
  18402. dimensions = dimensions || ['x', 'y'];
  18403. var dimensionInfos = {};
  18404. var dimensionNames = [];
  18405. for (var i = 0; i < dimensions.length; i++) {
  18406. var dimensionName;
  18407. var dimensionInfo = {};
  18408. if (typeof dimensions[i] === 'string') {
  18409. dimensionName = dimensions[i];
  18410. dimensionInfo = {
  18411. name: dimensionName,
  18412. coordDim: dimensionName,
  18413. coordDimIndex: 0,
  18414. stackable: false,
  18415. // Type can be 'float', 'int', 'number'
  18416. // Default is number, Precision of float may not enough
  18417. type: 'number'
  18418. };
  18419. } else {
  18420. dimensionInfo = dimensions[i];
  18421. dimensionName = dimensionInfo.name;
  18422. dimensionInfo.type = dimensionInfo.type || 'number';
  18423. if (!dimensionInfo.coordDim) {
  18424. dimensionInfo.coordDim = dimensionName;
  18425. dimensionInfo.coordDimIndex = 0;
  18426. }
  18427. }
  18428. dimensionInfo.otherDims = dimensionInfo.otherDims || {};
  18429. dimensionNames.push(dimensionName);
  18430. dimensionInfos[dimensionName] = dimensionInfo;
  18431. }
  18432. /**
  18433. * @readOnly
  18434. * @type {Array.<string>}
  18435. */
  18436. this.dimensions = dimensionNames;
  18437. /**
  18438. * Infomation of each data dimension, like data type.
  18439. * @type {Object}
  18440. */
  18441. this._dimensionInfos = dimensionInfos;
  18442. /**
  18443. * @type {module:echarts/model/Model}
  18444. */
  18445. this.hostModel = hostModel;
  18446. /**
  18447. * @type {module:echarts/model/Model}
  18448. */
  18449. this.dataType;
  18450. /**
  18451. * Indices stores the indices of data subset after filtered.
  18452. * This data subset will be used in chart.
  18453. * @type {Array.<number>}
  18454. * @readOnly
  18455. */
  18456. this.indices = [];
  18457. /**
  18458. * Data storage
  18459. * @type {Object.<key, TypedArray|Array>}
  18460. * @private
  18461. */
  18462. this._storage = {};
  18463. /**
  18464. * @type {Array.<string>}
  18465. */
  18466. this._nameList = [];
  18467. /**
  18468. * @type {Array.<string>}
  18469. */
  18470. this._idList = [];
  18471. /**
  18472. * Models of data option is stored sparse for optimizing memory cost
  18473. * @type {Array.<module:echarts/model/Model>}
  18474. * @private
  18475. */
  18476. this._optionModels = [];
  18477. /**
  18478. * @param {module:echarts/data/List}
  18479. */
  18480. this.stackedOn = null;
  18481. /**
  18482. * Global visual properties after visual coding
  18483. * @type {Object}
  18484. * @private
  18485. */
  18486. this._visual = {};
  18487. /**
  18488. * Globel layout properties.
  18489. * @type {Object}
  18490. * @private
  18491. */
  18492. this._layout = {};
  18493. /**
  18494. * Item visual properties after visual coding
  18495. * @type {Array.<Object>}
  18496. * @private
  18497. */
  18498. this._itemVisuals = [];
  18499. /**
  18500. * Item layout properties after layout
  18501. * @type {Array.<Object>}
  18502. * @private
  18503. */
  18504. this._itemLayouts = [];
  18505. /**
  18506. * Graphic elemnents
  18507. * @type {Array.<module:zrender/Element>}
  18508. * @private
  18509. */
  18510. this._graphicEls = [];
  18511. /**
  18512. * @type {Array.<Array|Object>}
  18513. * @private
  18514. */
  18515. this._rawData;
  18516. /**
  18517. * @type {Object}
  18518. * @private
  18519. */
  18520. this._extent;
  18521. };
  18522. var listProto = List.prototype;
  18523. listProto.type = 'list';
  18524. /**
  18525. * If each data item has it's own option
  18526. * @type {boolean}
  18527. */
  18528. listProto.hasItemOption = true;
  18529. /**
  18530. * Get dimension name
  18531. * @param {string|number} dim
  18532. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  18533. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  18534. * @return {string} Concrete dim name.
  18535. */
  18536. listProto.getDimension = function (dim) {
  18537. if (!isNaN(dim)) {
  18538. dim = this.dimensions[dim] || dim;
  18539. }
  18540. return dim;
  18541. };
  18542. /**
  18543. * Get type and stackable info of particular dimension
  18544. * @param {string|number} dim
  18545. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  18546. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  18547. */
  18548. listProto.getDimensionInfo = function (dim) {
  18549. return clone(this._dimensionInfos[this.getDimension(dim)]);
  18550. };
  18551. /**
  18552. * Initialize from data
  18553. * @param {Array.<Object|number|Array>} data
  18554. * @param {Array.<string>} [nameList]
  18555. * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
  18556. */
  18557. listProto.initData = function (data, nameList, dimValueGetter) {
  18558. data = data || [];
  18559. var isDataArray = isArray(data);
  18560. if (isDataArray) {
  18561. data = new DefaultDataProvider(data);
  18562. }
  18563. if (true) {
  18564. if (!isDataArray && (typeof data.getItem != 'function' || typeof data.count != 'function')) {
  18565. throw new Error('Inavlid data provider.');
  18566. }
  18567. }
  18568. this._rawData = data; // Clear
  18569. var storage = this._storage = {};
  18570. var indices = this.indices = [];
  18571. var dimensions = this.dimensions;
  18572. var dimensionInfoMap = this._dimensionInfos;
  18573. var size = data.count();
  18574. var idList = [];
  18575. var nameRepeatCount = {};
  18576. var nameDimIdx;
  18577. nameList = nameList || []; // Init storage
  18578. for (var i = 0; i < dimensions.length; i++) {
  18579. var dimInfo = dimensionInfoMap[dimensions[i]];
  18580. dimInfo.otherDims.itemName === 0 && (nameDimIdx = i);
  18581. var DataCtor = dataCtors[dimInfo.type];
  18582. storage[dimensions[i]] = new DataCtor(size);
  18583. }
  18584. var self = this;
  18585. if (!dimValueGetter) {
  18586. self.hasItemOption = false;
  18587. } // Default dim value getter
  18588. dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
  18589. var value = getDataItemValue(dataItem); // If any dataItem is like { value: 10 }
  18590. if (isDataItemOption(dataItem)) {
  18591. self.hasItemOption = true;
  18592. }
  18593. return converDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.
  18594. : value, dimensionInfoMap[dimName]);
  18595. };
  18596. for (var i = 0; i < size; i++) {
  18597. // NOTICE: Try not to write things into dataItem
  18598. var dataItem = data.getItem(i); // Each data item is value
  18599. // [1, 2]
  18600. // 2
  18601. // Bar chart, line chart which uses category axis
  18602. // only gives the 'y' value. 'x' value is the indices of cateogry
  18603. // Use a tempValue to normalize the value to be a (x, y) value
  18604. // Store the data by dimensions
  18605. for (var k = 0; k < dimensions.length; k++) {
  18606. var dim = dimensions[k];
  18607. var dimStorage = storage[dim]; // PENDING NULL is empty or zero
  18608. dimStorage[i] = dimValueGetter(dataItem, dim, i, k);
  18609. }
  18610. indices.push(i);
  18611. } // Use the name in option and create id
  18612. for (var i = 0; i < size; i++) {
  18613. var dataItem = data.getItem(i);
  18614. if (!nameList[i] && dataItem) {
  18615. if (dataItem.name != null) {
  18616. nameList[i] = dataItem.name;
  18617. } else if (nameDimIdx != null) {
  18618. nameList[i] = storage[dimensions[nameDimIdx]][i];
  18619. }
  18620. }
  18621. var name = nameList[i] || ''; // Try using the id in option
  18622. var id = dataItem && dataItem.id;
  18623. if (!id && name) {
  18624. // Use name as id and add counter to avoid same name
  18625. nameRepeatCount[name] = nameRepeatCount[name] || 0;
  18626. id = name;
  18627. if (nameRepeatCount[name] > 0) {
  18628. id += '__ec__' + nameRepeatCount[name];
  18629. }
  18630. nameRepeatCount[name]++;
  18631. }
  18632. id && (idList[i] = id);
  18633. }
  18634. this._nameList = nameList;
  18635. this._idList = idList;
  18636. };
  18637. /**
  18638. * @return {number}
  18639. */
  18640. listProto.count = function () {
  18641. return this.indices.length;
  18642. };
  18643. /**
  18644. * Get value. Return NaN if idx is out of range.
  18645. * @param {string} dim Dim must be concrete name.
  18646. * @param {number} idx
  18647. * @param {boolean} stack
  18648. * @return {number}
  18649. */
  18650. listProto.get = function (dim, idx, stack) {
  18651. var storage = this._storage;
  18652. var dataIndex = this.indices[idx]; // If value not exists
  18653. if (dataIndex == null || !storage[dim]) {
  18654. return NaN;
  18655. }
  18656. var value = storage[dim][dataIndex]; // FIXME ordinal data type is not stackable
  18657. if (stack) {
  18658. var dimensionInfo = this._dimensionInfos[dim];
  18659. if (dimensionInfo && dimensionInfo.stackable) {
  18660. var stackedOn = this.stackedOn;
  18661. while (stackedOn) {
  18662. // Get no stacked data of stacked on
  18663. var stackedValue = stackedOn.get(dim, idx); // Considering positive stack, negative stack and empty data
  18664. if (value >= 0 && stackedValue > 0 || // Positive stack
  18665. value <= 0 && stackedValue < 0 // Negative stack
  18666. ) {
  18667. value += stackedValue;
  18668. }
  18669. stackedOn = stackedOn.stackedOn;
  18670. }
  18671. }
  18672. }
  18673. return value;
  18674. };
  18675. /**
  18676. * Get value for multi dimensions.
  18677. * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
  18678. * @param {number} idx
  18679. * @param {boolean} stack
  18680. * @return {number}
  18681. */
  18682. listProto.getValues = function (dimensions, idx, stack) {
  18683. var values = [];
  18684. if (!isArray(dimensions)) {
  18685. stack = idx;
  18686. idx = dimensions;
  18687. dimensions = this.dimensions;
  18688. }
  18689. for (var i = 0, len = dimensions.length; i < len; i++) {
  18690. values.push(this.get(dimensions[i], idx, stack));
  18691. }
  18692. return values;
  18693. };
  18694. /**
  18695. * If value is NaN. Inlcuding '-'
  18696. * @param {string} dim
  18697. * @param {number} idx
  18698. * @return {number}
  18699. */
  18700. listProto.hasValue = function (idx) {
  18701. var dimensions = this.dimensions;
  18702. var dimensionInfos = this._dimensionInfos;
  18703. for (var i = 0, len = dimensions.length; i < len; i++) {
  18704. if ( // Ordinal type can be string or number
  18705. dimensionInfos[dimensions[i]].type !== 'ordinal' && isNaN(this.get(dimensions[i], idx))) {
  18706. return false;
  18707. }
  18708. }
  18709. return true;
  18710. };
  18711. /**
  18712. * Get extent of data in one dimension
  18713. * @param {string} dim
  18714. * @param {boolean} stack
  18715. * @param {Function} filter
  18716. */
  18717. listProto.getDataExtent = function (dim, stack, filter$$1) {
  18718. dim = this.getDimension(dim);
  18719. var dimData = this._storage[dim];
  18720. var dimInfo = this.getDimensionInfo(dim);
  18721. stack = dimInfo && dimInfo.stackable && stack;
  18722. var dimExtent = (this._extent || (this._extent = {}))[dim + !!stack];
  18723. var value;
  18724. if (dimExtent) {
  18725. return dimExtent;
  18726. } // var dimInfo = this._dimensionInfos[dim];
  18727. if (dimData) {
  18728. var min = Infinity;
  18729. var max = -Infinity; // var isOrdinal = dimInfo.type === 'ordinal';
  18730. for (var i = 0, len = this.count(); i < len; i++) {
  18731. value = this.get(dim, i, stack); // FIXME
  18732. // if (isOrdinal && typeof value === 'string') {
  18733. // value = zrUtil.indexOf(dimData, value);
  18734. // }
  18735. if (!filter$$1 || filter$$1(value, dim, i)) {
  18736. value < min && (min = value);
  18737. value > max && (max = value);
  18738. }
  18739. }
  18740. return this._extent[dim + !!stack] = [min, max];
  18741. } else {
  18742. return [Infinity, -Infinity];
  18743. }
  18744. };
  18745. /**
  18746. * Get sum of data in one dimension
  18747. * @param {string} dim
  18748. * @param {boolean} stack
  18749. */
  18750. listProto.getSum = function (dim, stack) {
  18751. var dimData = this._storage[dim];
  18752. var sum = 0;
  18753. if (dimData) {
  18754. for (var i = 0, len = this.count(); i < len; i++) {
  18755. var value = this.get(dim, i, stack);
  18756. if (!isNaN(value)) {
  18757. sum += value;
  18758. }
  18759. }
  18760. }
  18761. return sum;
  18762. };
  18763. /**
  18764. * Retreive the index with given value
  18765. * @param {number} idx
  18766. * @param {number} value
  18767. * @return {number}
  18768. */
  18769. // FIXME Precision of float value
  18770. listProto.indexOf = function (dim, value) {
  18771. var storage = this._storage;
  18772. var dimData = storage[dim];
  18773. var indices = this.indices;
  18774. if (dimData) {
  18775. for (var i = 0, len = indices.length; i < len; i++) {
  18776. var rawIndex = indices[i];
  18777. if (dimData[rawIndex] === value) {
  18778. return i;
  18779. }
  18780. }
  18781. }
  18782. return -1;
  18783. };
  18784. /**
  18785. * Retreive the index with given name
  18786. * @param {number} idx
  18787. * @param {number} name
  18788. * @return {number}
  18789. */
  18790. listProto.indexOfName = function (name) {
  18791. var indices = this.indices;
  18792. var nameList = this._nameList;
  18793. for (var i = 0, len = indices.length; i < len; i++) {
  18794. var rawIndex = indices[i];
  18795. if (nameList[rawIndex] === name) {
  18796. return i;
  18797. }
  18798. }
  18799. return -1;
  18800. };
  18801. /**
  18802. * Retreive the index with given raw data index
  18803. * @param {number} idx
  18804. * @param {number} name
  18805. * @return {number}
  18806. */
  18807. listProto.indexOfRawIndex = function (rawIndex) {
  18808. // Indices are ascending
  18809. var indices = this.indices; // If rawIndex === dataIndex
  18810. var rawDataIndex = indices[rawIndex];
  18811. if (rawDataIndex != null && rawDataIndex === rawIndex) {
  18812. return rawIndex;
  18813. }
  18814. var left = 0;
  18815. var right = indices.length - 1;
  18816. while (left <= right) {
  18817. var mid = (left + right) / 2 | 0;
  18818. if (indices[mid] < rawIndex) {
  18819. left = mid + 1;
  18820. } else if (indices[mid] > rawIndex) {
  18821. right = mid - 1;
  18822. } else {
  18823. return mid;
  18824. }
  18825. }
  18826. return -1;
  18827. };
  18828. /**
  18829. * Retreive the index of nearest value
  18830. * @param {string} dim
  18831. * @param {number} value
  18832. * @param {boolean} stack If given value is after stacked
  18833. * @param {number} [maxDistance=Infinity]
  18834. * @return {Array.<number>} Considere multiple points has the same value.
  18835. */
  18836. listProto.indicesOfNearest = function (dim, value, stack, maxDistance) {
  18837. var storage = this._storage;
  18838. var dimData = storage[dim];
  18839. var nearestIndices = [];
  18840. if (!dimData) {
  18841. return nearestIndices;
  18842. }
  18843. if (maxDistance == null) {
  18844. maxDistance = Infinity;
  18845. }
  18846. var minDist = Number.MAX_VALUE;
  18847. var minDiff = -1;
  18848. for (var i = 0, len = this.count(); i < len; i++) {
  18849. var diff = value - this.get(dim, i, stack);
  18850. var dist = Math.abs(diff);
  18851. if (diff <= maxDistance && dist <= minDist) {
  18852. // For the case of two data are same on xAxis, which has sequence data.
  18853. // Show the nearest index
  18854. // https://github.com/ecomfe/echarts/issues/2869
  18855. if (dist < minDist || diff >= 0 && minDiff < 0) {
  18856. minDist = dist;
  18857. minDiff = diff;
  18858. nearestIndices.length = 0;
  18859. }
  18860. nearestIndices.push(i);
  18861. }
  18862. }
  18863. return nearestIndices;
  18864. };
  18865. /**
  18866. * Get raw data index
  18867. * @param {number} idx
  18868. * @return {number}
  18869. */
  18870. listProto.getRawIndex = function (idx) {
  18871. var rawIdx = this.indices[idx];
  18872. return rawIdx == null ? -1 : rawIdx;
  18873. };
  18874. /**
  18875. * Get raw data item
  18876. * @param {number} idx
  18877. * @return {number}
  18878. */
  18879. listProto.getRawDataItem = function (idx) {
  18880. return this._rawData.getItem(this.getRawIndex(idx));
  18881. };
  18882. /**
  18883. * @param {number} idx
  18884. * @param {boolean} [notDefaultIdx=false]
  18885. * @return {string}
  18886. */
  18887. listProto.getName = function (idx) {
  18888. return this._nameList[this.indices[idx]] || '';
  18889. };
  18890. /**
  18891. * @param {number} idx
  18892. * @param {boolean} [notDefaultIdx=false]
  18893. * @return {string}
  18894. */
  18895. listProto.getId = function (idx) {
  18896. return this._idList[this.indices[idx]] || this.getRawIndex(idx) + '';
  18897. };
  18898. function normalizeDimensions(dimensions) {
  18899. if (!isArray(dimensions)) {
  18900. dimensions = [dimensions];
  18901. }
  18902. return dimensions;
  18903. }
  18904. /**
  18905. * Data iteration
  18906. * @param {string|Array.<string>}
  18907. * @param {Function} cb
  18908. * @param {boolean} [stack=false]
  18909. * @param {*} [context=this]
  18910. *
  18911. * @example
  18912. * list.each('x', function (x, idx) {});
  18913. * list.each(['x', 'y'], function (x, y, idx) {});
  18914. * list.each(function (idx) {})
  18915. */
  18916. listProto.each = function (dims, cb, stack, context) {
  18917. if (typeof dims === 'function') {
  18918. context = stack;
  18919. stack = cb;
  18920. cb = dims;
  18921. dims = [];
  18922. }
  18923. dims = map(normalizeDimensions(dims), this.getDimension, this);
  18924. var value = [];
  18925. var dimSize = dims.length;
  18926. var indices = this.indices;
  18927. context = context || this;
  18928. for (var i = 0; i < indices.length; i++) {
  18929. // Simple optimization
  18930. switch (dimSize) {
  18931. case 0:
  18932. cb.call(context, i);
  18933. break;
  18934. case 1:
  18935. cb.call(context, this.get(dims[0], i, stack), i);
  18936. break;
  18937. case 2:
  18938. cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i);
  18939. break;
  18940. default:
  18941. for (var k = 0; k < dimSize; k++) {
  18942. value[k] = this.get(dims[k], i, stack);
  18943. } // Index
  18944. value[k] = i;
  18945. cb.apply(context, value);
  18946. }
  18947. }
  18948. };
  18949. /**
  18950. * Data filter
  18951. * @param {string|Array.<string>}
  18952. * @param {Function} cb
  18953. * @param {boolean} [stack=false]
  18954. * @param {*} [context=this]
  18955. */
  18956. listProto.filterSelf = function (dimensions, cb, stack, context) {
  18957. if (typeof dimensions === 'function') {
  18958. context = stack;
  18959. stack = cb;
  18960. cb = dimensions;
  18961. dimensions = [];
  18962. }
  18963. dimensions = map(normalizeDimensions(dimensions), this.getDimension, this);
  18964. var newIndices = [];
  18965. var value = [];
  18966. var dimSize = dimensions.length;
  18967. var indices = this.indices;
  18968. context = context || this;
  18969. for (var i = 0; i < indices.length; i++) {
  18970. var keep; // Simple optimization
  18971. if (!dimSize) {
  18972. keep = cb.call(context, i);
  18973. } else if (dimSize === 1) {
  18974. keep = cb.call(context, this.get(dimensions[0], i, stack), i);
  18975. } else {
  18976. for (var k = 0; k < dimSize; k++) {
  18977. value[k] = this.get(dimensions[k], i, stack);
  18978. }
  18979. value[k] = i;
  18980. keep = cb.apply(context, value);
  18981. }
  18982. if (keep) {
  18983. newIndices.push(indices[i]);
  18984. }
  18985. }
  18986. this.indices = newIndices; // Reset data extent
  18987. this._extent = {};
  18988. return this;
  18989. };
  18990. /**
  18991. * Data mapping to a plain array
  18992. * @param {string|Array.<string>} [dimensions]
  18993. * @param {Function} cb
  18994. * @param {boolean} [stack=false]
  18995. * @param {*} [context=this]
  18996. * @return {Array}
  18997. */
  18998. listProto.mapArray = function (dimensions, cb, stack, context) {
  18999. if (typeof dimensions === 'function') {
  19000. context = stack;
  19001. stack = cb;
  19002. cb = dimensions;
  19003. dimensions = [];
  19004. }
  19005. var result = [];
  19006. this.each(dimensions, function () {
  19007. result.push(cb && cb.apply(this, arguments));
  19008. }, stack, context);
  19009. return result;
  19010. };
  19011. function cloneListForMapAndSample(original, excludeDimensions) {
  19012. var allDimensions = original.dimensions;
  19013. var list = new List(map(allDimensions, original.getDimensionInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked
  19014. transferProperties(list, original);
  19015. var storage = list._storage = {};
  19016. var originalStorage = original._storage; // Init storage
  19017. for (var i = 0; i < allDimensions.length; i++) {
  19018. var dim = allDimensions[i];
  19019. var dimStore = originalStorage[dim];
  19020. if (indexOf(excludeDimensions, dim) >= 0) {
  19021. storage[dim] = new dimStore.constructor(originalStorage[dim].length);
  19022. } else {
  19023. // Direct reference for other dimensions
  19024. storage[dim] = originalStorage[dim];
  19025. }
  19026. }
  19027. return list;
  19028. }
  19029. /**
  19030. * Data mapping to a new List with given dimensions
  19031. * @param {string|Array.<string>} dimensions
  19032. * @param {Function} cb
  19033. * @param {boolean} [stack=false]
  19034. * @param {*} [context=this]
  19035. * @return {Array}
  19036. */
  19037. listProto.map = function (dimensions, cb, stack, context) {
  19038. dimensions = map(normalizeDimensions(dimensions), this.getDimension, this);
  19039. var list = cloneListForMapAndSample(this, dimensions); // Following properties are all immutable.
  19040. // So we can reference to the same value
  19041. var indices = list.indices = this.indices;
  19042. var storage = list._storage;
  19043. var tmpRetValue = [];
  19044. this.each(dimensions, function () {
  19045. var idx = arguments[arguments.length - 1];
  19046. var retValue = cb && cb.apply(this, arguments);
  19047. if (retValue != null) {
  19048. // a number
  19049. if (typeof retValue === 'number') {
  19050. tmpRetValue[0] = retValue;
  19051. retValue = tmpRetValue;
  19052. }
  19053. for (var i = 0; i < retValue.length; i++) {
  19054. var dim = dimensions[i];
  19055. var dimStore = storage[dim];
  19056. var rawIdx = indices[idx];
  19057. if (dimStore) {
  19058. dimStore[rawIdx] = retValue[i];
  19059. }
  19060. }
  19061. }
  19062. }, stack, context);
  19063. return list;
  19064. };
  19065. /**
  19066. * Large data down sampling on given dimension
  19067. * @param {string} dimension
  19068. * @param {number} rate
  19069. * @param {Function} sampleValue
  19070. * @param {Function} sampleIndex Sample index for name and id
  19071. */
  19072. listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  19073. var list = cloneListForMapAndSample(this, [dimension]);
  19074. var storage = this._storage;
  19075. var targetStorage = list._storage;
  19076. var originalIndices = this.indices;
  19077. var indices = list.indices = [];
  19078. var frameValues = [];
  19079. var frameIndices = [];
  19080. var frameSize = Math.floor(1 / rate);
  19081. var dimStore = targetStorage[dimension];
  19082. var len = this.count(); // Copy data from original data
  19083. for (var i = 0; i < storage[dimension].length; i++) {
  19084. targetStorage[dimension][i] = storage[dimension][i];
  19085. }
  19086. for (var i = 0; i < len; i += frameSize) {
  19087. // Last frame
  19088. if (frameSize > len - i) {
  19089. frameSize = len - i;
  19090. frameValues.length = frameSize;
  19091. }
  19092. for (var k = 0; k < frameSize; k++) {
  19093. var idx = originalIndices[i + k];
  19094. frameValues[k] = dimStore[idx];
  19095. frameIndices[k] = idx;
  19096. }
  19097. var value = sampleValue(frameValues);
  19098. var idx = frameIndices[sampleIndex(frameValues, value) || 0]; // Only write value on the filtered data
  19099. dimStore[idx] = value;
  19100. indices.push(idx);
  19101. }
  19102. return list;
  19103. };
  19104. /**
  19105. * Get model of one data item.
  19106. *
  19107. * @param {number} idx
  19108. */
  19109. // FIXME Model proxy ?
  19110. listProto.getItemModel = function (idx) {
  19111. var hostModel = this.hostModel;
  19112. idx = this.indices[idx];
  19113. return new Model(this._rawData.getItem(idx), hostModel, hostModel && hostModel.ecModel);
  19114. };
  19115. /**
  19116. * Create a data differ
  19117. * @param {module:echarts/data/List} otherList
  19118. * @return {module:echarts/data/DataDiffer}
  19119. */
  19120. listProto.diff = function (otherList) {
  19121. var idList = this._idList;
  19122. var otherIdList = otherList && otherList._idList;
  19123. var val; // Use prefix to avoid index to be the same as otherIdList[idx],
  19124. // which will cause weird udpate animation.
  19125. var prefix = 'e\0\0';
  19126. return new DataDiffer(otherList ? otherList.indices : [], this.indices, function (idx) {
  19127. return (val = otherIdList[idx]) != null ? val : prefix + idx;
  19128. }, function (idx) {
  19129. return (val = idList[idx]) != null ? val : prefix + idx;
  19130. });
  19131. };
  19132. /**
  19133. * Get visual property.
  19134. * @param {string} key
  19135. */
  19136. listProto.getVisual = function (key) {
  19137. var visual = this._visual;
  19138. return visual && visual[key];
  19139. };
  19140. /**
  19141. * Set visual property
  19142. * @param {string|Object} key
  19143. * @param {*} [value]
  19144. *
  19145. * @example
  19146. * setVisual('color', color);
  19147. * setVisual({
  19148. * 'color': color
  19149. * });
  19150. */
  19151. listProto.setVisual = function (key, val) {
  19152. if (isObject$4(key)) {
  19153. for (var name in key) {
  19154. if (key.hasOwnProperty(name)) {
  19155. this.setVisual(name, key[name]);
  19156. }
  19157. }
  19158. return;
  19159. }
  19160. this._visual = this._visual || {};
  19161. this._visual[key] = val;
  19162. };
  19163. /**
  19164. * Set layout property.
  19165. * @param {string|Object} key
  19166. * @param {*} [val]
  19167. */
  19168. listProto.setLayout = function (key, val) {
  19169. if (isObject$4(key)) {
  19170. for (var name in key) {
  19171. if (key.hasOwnProperty(name)) {
  19172. this.setLayout(name, key[name]);
  19173. }
  19174. }
  19175. return;
  19176. }
  19177. this._layout[key] = val;
  19178. };
  19179. /**
  19180. * Get layout property.
  19181. * @param {string} key.
  19182. * @return {*}
  19183. */
  19184. listProto.getLayout = function (key) {
  19185. return this._layout[key];
  19186. };
  19187. /**
  19188. * Get layout of single data item
  19189. * @param {number} idx
  19190. */
  19191. listProto.getItemLayout = function (idx) {
  19192. return this._itemLayouts[idx];
  19193. };
  19194. /**
  19195. * Set layout of single data item
  19196. * @param {number} idx
  19197. * @param {Object} layout
  19198. * @param {boolean=} [merge=false]
  19199. */
  19200. listProto.setItemLayout = function (idx, layout, merge$$1) {
  19201. this._itemLayouts[idx] = merge$$1 ? extend(this._itemLayouts[idx] || {}, layout) : layout;
  19202. };
  19203. /**
  19204. * Clear all layout of single data item
  19205. */
  19206. listProto.clearItemLayouts = function () {
  19207. this._itemLayouts.length = 0;
  19208. };
  19209. /**
  19210. * Get visual property of single data item
  19211. * @param {number} idx
  19212. * @param {string} key
  19213. * @param {boolean} [ignoreParent=false]
  19214. */
  19215. listProto.getItemVisual = function (idx, key, ignoreParent) {
  19216. var itemVisual = this._itemVisuals[idx];
  19217. var val = itemVisual && itemVisual[key];
  19218. if (val == null && !ignoreParent) {
  19219. // Use global visual property
  19220. return this.getVisual(key);
  19221. }
  19222. return val;
  19223. };
  19224. /**
  19225. * Set visual property of single data item
  19226. *
  19227. * @param {number} idx
  19228. * @param {string|Object} key
  19229. * @param {*} [value]
  19230. *
  19231. * @example
  19232. * setItemVisual(0, 'color', color);
  19233. * setItemVisual(0, {
  19234. * 'color': color
  19235. * });
  19236. */
  19237. listProto.setItemVisual = function (idx, key, value) {
  19238. var itemVisual = this._itemVisuals[idx] || {};
  19239. this._itemVisuals[idx] = itemVisual;
  19240. if (isObject$4(key)) {
  19241. for (var name in key) {
  19242. if (key.hasOwnProperty(name)) {
  19243. itemVisual[name] = key[name];
  19244. }
  19245. }
  19246. return;
  19247. }
  19248. itemVisual[key] = value;
  19249. };
  19250. /**
  19251. * Clear itemVisuals and list visual.
  19252. */
  19253. listProto.clearAllVisual = function () {
  19254. this._visual = {};
  19255. this._itemVisuals = [];
  19256. };
  19257. var setItemDataAndSeriesIndex = function (child) {
  19258. child.seriesIndex = this.seriesIndex;
  19259. child.dataIndex = this.dataIndex;
  19260. child.dataType = this.dataType;
  19261. };
  19262. /**
  19263. * Set graphic element relative to data. It can be set as null
  19264. * @param {number} idx
  19265. * @param {module:zrender/Element} [el]
  19266. */
  19267. listProto.setItemGraphicEl = function (idx, el) {
  19268. var hostModel = this.hostModel;
  19269. if (el) {
  19270. // Add data index and series index for indexing the data by element
  19271. // Useful in tooltip
  19272. el.dataIndex = idx;
  19273. el.dataType = this.dataType;
  19274. el.seriesIndex = hostModel && hostModel.seriesIndex;
  19275. if (el.type === 'group') {
  19276. el.traverse(setItemDataAndSeriesIndex, el);
  19277. }
  19278. }
  19279. this._graphicEls[idx] = el;
  19280. };
  19281. /**
  19282. * @param {number} idx
  19283. * @return {module:zrender/Element}
  19284. */
  19285. listProto.getItemGraphicEl = function (idx) {
  19286. return this._graphicEls[idx];
  19287. };
  19288. /**
  19289. * @param {Function} cb
  19290. * @param {*} context
  19291. */
  19292. listProto.eachItemGraphicEl = function (cb, context) {
  19293. each$1(this._graphicEls, function (el, idx) {
  19294. if (el) {
  19295. cb && cb.call(context, el, idx);
  19296. }
  19297. });
  19298. };
  19299. /**
  19300. * Shallow clone a new list except visual and layout properties, and graph elements.
  19301. * New list only change the indices.
  19302. */
  19303. listProto.cloneShallow = function () {
  19304. var dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
  19305. var list = new List(dimensionInfoList, this.hostModel); // FIXME
  19306. list._storage = this._storage;
  19307. transferProperties(list, this); // Clone will not change the data extent and indices
  19308. list.indices = this.indices.slice();
  19309. if (this._extent) {
  19310. list._extent = extend({}, this._extent);
  19311. }
  19312. return list;
  19313. };
  19314. /**
  19315. * Wrap some method to add more feature
  19316. * @param {string} methodName
  19317. * @param {Function} injectFunction
  19318. */
  19319. listProto.wrapMethod = function (methodName, injectFunction) {
  19320. var originalMethod = this[methodName];
  19321. if (typeof originalMethod !== 'function') {
  19322. return;
  19323. }
  19324. this.__wrappedMethods = this.__wrappedMethods || [];
  19325. this.__wrappedMethods.push(methodName);
  19326. this[methodName] = function () {
  19327. var res = originalMethod.apply(this, arguments);
  19328. return injectFunction.apply(this, [res].concat(slice(arguments)));
  19329. };
  19330. }; // Methods that create a new list based on this list should be listed here.
  19331. // Notice that those method should `RETURN` the new list.
  19332. listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map']; // Methods that change indices of this list should be listed here.
  19333. listProto.CHANGABLE_METHODS = ['filterSelf'];
  19334. /**
  19335. * Complete dimensions by data (guess dimension).
  19336. */
  19337. var each$7 = each$1;
  19338. var isString$1 = isString;
  19339. var defaults$1 = defaults;
  19340. var OTHER_DIMS = {
  19341. tooltip: 1,
  19342. label: 1,
  19343. itemName: 1
  19344. };
  19345. /**
  19346. * Complete the dimensions array, by user defined `dimension` and `encode`,
  19347. * and guessing from the data structure.
  19348. * If no 'value' dimension specified, the first no-named dimension will be
  19349. * named as 'value'.
  19350. *
  19351. * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which
  19352. * provides not only dim template, but also default order.
  19353. * `name` of each item provides default coord name.
  19354. * [{dimsDef: []}, ...] can be specified to give names.
  19355. * @param {Array} data Data list. [[1, 2, 3], [2, 3, 4]].
  19356. * @param {Object} [opt]
  19357. * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions
  19358. * For example: ['asdf', {name, type}, ...].
  19359. * @param {Object} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3}
  19360. * @param {string} [opt.extraPrefix] Prefix of name when filling the left dimensions.
  19361. * @param {string} [opt.extraFromZero] If specified, extra dim names will be:
  19362. * extraPrefix + 0, extraPrefix + extraBaseIndex + 1 ...
  19363. * If not specified, extra dim names will be:
  19364. * extraPrefix, extraPrefix + 0, extraPrefix + 1 ...
  19365. * @param {number} [opt.dimCount] If not specified, guess by the first data item.
  19366. * @return {Array.<Object>} [{
  19367. * name: string mandatory,
  19368. * coordDim: string mandatory,
  19369. * coordDimIndex: number mandatory,
  19370. * type: string optional,
  19371. * tooltipName: string optional,
  19372. * otherDims: {
  19373. * tooltip: number optional,
  19374. * label: number optional
  19375. * },
  19376. * isExtraCoord: boolean true or undefined.
  19377. * other props ...
  19378. * }]
  19379. */
  19380. function completeDimensions(sysDims, data, opt) {
  19381. data = data || [];
  19382. opt = opt || {};
  19383. sysDims = (sysDims || []).slice();
  19384. var dimsDef = (opt.dimsDef || []).slice();
  19385. var encodeDef = createHashMap(opt.encodeDef);
  19386. var dataDimNameMap = createHashMap();
  19387. var coordDimNameMap = createHashMap(); // var valueCandidate;
  19388. var result = [];
  19389. var dimCount = opt.dimCount;
  19390. if (dimCount == null) {
  19391. var value0 = retrieveValue(data[0]);
  19392. dimCount = Math.max(isArray(value0) && value0.length || 1, sysDims.length, dimsDef.length);
  19393. each$7(sysDims, function (sysDimItem) {
  19394. var sysDimItemDimsDef = sysDimItem.dimsDef;
  19395. sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length));
  19396. });
  19397. } // Apply user defined dims (`name` and `type`) and init result.
  19398. for (var i = 0; i < dimCount; i++) {
  19399. var dimDefItem = isString$1(dimsDef[i]) ? {
  19400. name: dimsDef[i]
  19401. } : dimsDef[i] || {};
  19402. var userDimName = dimDefItem.name;
  19403. var resultItem = result[i] = {
  19404. otherDims: {}
  19405. }; // Name will be applied later for avoiding duplication.
  19406. if (userDimName != null && dataDimNameMap.get(userDimName) == null) {
  19407. // Only if `series.dimensions` is defined in option, tooltipName
  19408. // will be set, and dimension will be diplayed vertically in
  19409. // tooltip by default.
  19410. resultItem.name = resultItem.tooltipName = userDimName;
  19411. dataDimNameMap.set(userDimName, i);
  19412. }
  19413. dimDefItem.type != null && (resultItem.type = dimDefItem.type);
  19414. } // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`.
  19415. encodeDef.each(function (dataDims, coordDim) {
  19416. dataDims = encodeDef.set(coordDim, normalizeToArray(dataDims).slice());
  19417. each$7(dataDims, function (resultDimIdx, coordDimIndex) {
  19418. // The input resultDimIdx can be dim name or index.
  19419. isString$1(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx));
  19420. if (resultDimIdx != null && resultDimIdx < dimCount) {
  19421. dataDims[coordDimIndex] = resultDimIdx;
  19422. applyDim(result[resultDimIdx], coordDim, coordDimIndex);
  19423. }
  19424. });
  19425. }); // Apply templetes and default order from `sysDims`.
  19426. var availDimIdx = 0;
  19427. each$7(sysDims, function (sysDimItem, sysDimIndex) {
  19428. var coordDim;
  19429. var sysDimItem;
  19430. var sysDimItemDimsDef;
  19431. var sysDimItemOtherDims;
  19432. if (isString$1(sysDimItem)) {
  19433. coordDim = sysDimItem;
  19434. sysDimItem = {};
  19435. } else {
  19436. coordDim = sysDimItem.name;
  19437. sysDimItem = clone(sysDimItem); // `coordDimIndex` should not be set directly.
  19438. sysDimItemDimsDef = sysDimItem.dimsDef;
  19439. sysDimItemOtherDims = sysDimItem.otherDims;
  19440. sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null;
  19441. }
  19442. var dataDims = normalizeToArray(encodeDef.get(coordDim)); // dimensions provides default dim sequences.
  19443. if (!dataDims.length) {
  19444. for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
  19445. while (availDimIdx < result.length && result[availDimIdx].coordDim != null) {
  19446. availDimIdx++;
  19447. }
  19448. availDimIdx < result.length && dataDims.push(availDimIdx++);
  19449. }
  19450. } // Apply templates.
  19451. each$7(dataDims, function (resultDimIdx, coordDimIndex) {
  19452. var resultItem = result[resultDimIdx];
  19453. applyDim(defaults$1(resultItem, sysDimItem), coordDim, coordDimIndex);
  19454. if (resultItem.name == null && sysDimItemDimsDef) {
  19455. resultItem.name = resultItem.tooltipName = sysDimItemDimsDef[coordDimIndex];
  19456. }
  19457. sysDimItemOtherDims && defaults$1(resultItem.otherDims, sysDimItemOtherDims);
  19458. });
  19459. }); // Make sure the first extra dim is 'value'.
  19460. var extra = opt.extraPrefix || 'value'; // Set dim `name` and other `coordDim` and other props.
  19461. for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
  19462. var resultItem = result[resultDimIdx] = result[resultDimIdx] || {};
  19463. var coordDim = resultItem.coordDim;
  19464. coordDim == null && (resultItem.coordDim = genName(extra, coordDimNameMap, opt.extraFromZero), resultItem.coordDimIndex = 0, resultItem.isExtraCoord = true);
  19465. resultItem.name == null && (resultItem.name = genName(resultItem.coordDim, dataDimNameMap));
  19466. resultItem.type == null && guessOrdinal(data, resultDimIdx) && (resultItem.type = 'ordinal');
  19467. }
  19468. return result;
  19469. function applyDim(resultItem, coordDim, coordDimIndex) {
  19470. if (OTHER_DIMS[coordDim]) {
  19471. resultItem.otherDims[coordDim] = coordDimIndex;
  19472. } else {
  19473. resultItem.coordDim = coordDim;
  19474. resultItem.coordDimIndex = coordDimIndex;
  19475. coordDimNameMap.set(coordDim, true);
  19476. }
  19477. }
  19478. function genName(name, map$$1, fromZero) {
  19479. if (fromZero || map$$1.get(name) != null) {
  19480. var i = 0;
  19481. while (map$$1.get(name + i) != null) {
  19482. i++;
  19483. }
  19484. name += i;
  19485. }
  19486. map$$1.set(name, true);
  19487. return name;
  19488. }
  19489. } // The rule should not be complex, otherwise user might not
  19490. // be able to known where the data is wrong.
  19491. var guessOrdinal = completeDimensions.guessOrdinal = function (data, dimIndex) {
  19492. for (var i = 0, len = data.length; i < len; i++) {
  19493. var value = retrieveValue(data[i]);
  19494. if (!isArray(value)) {
  19495. return false;
  19496. }
  19497. var value = value[dimIndex]; // Consider usage convenience, '1', '2' will be treated as "number".
  19498. // `isFinit('')` get `true`.
  19499. if (value != null && isFinite(value) && value !== '') {
  19500. return false;
  19501. } else if (isString$1(value) && value !== '-') {
  19502. return true;
  19503. }
  19504. }
  19505. return false;
  19506. };
  19507. function retrieveValue(o) {
  19508. return isArray(o) ? o : isObject(o) ? o.value : o;
  19509. }
  19510. function firstDataNotNull(data) {
  19511. var i = 0;
  19512. while (i < data.length && data[i] == null) {
  19513. i++;
  19514. }
  19515. return data[i];
  19516. }
  19517. function ifNeedCompleteOrdinalData(data) {
  19518. var sampleItem = firstDataNotNull(data);
  19519. return sampleItem != null && !isArray(getDataItemValue(sampleItem));
  19520. }
  19521. /**
  19522. * Helper function to create a list from option data
  19523. */
  19524. function createListFromArray(data, seriesModel, ecModel) {
  19525. // If data is undefined
  19526. data = data || [];
  19527. if (true) {
  19528. if (!isArray(data)) {
  19529. throw new Error('Invalid data.');
  19530. }
  19531. }
  19532. var coordSysName = seriesModel.get('coordinateSystem');
  19533. var creator = creators[coordSysName];
  19534. var registeredCoordSys = CoordinateSystemManager.get(coordSysName);
  19535. var completeDimOpt = {
  19536. encodeDef: seriesModel.get('encode'),
  19537. dimsDef: seriesModel.get('dimensions')
  19538. }; // FIXME
  19539. var axesInfo = creator && creator(data, seriesModel, ecModel, completeDimOpt);
  19540. var dimensions = axesInfo && axesInfo.dimensions;
  19541. if (!dimensions) {
  19542. // Get dimensions from registered coordinate system
  19543. dimensions = registeredCoordSys && (registeredCoordSys.getDimensionsInfo ? registeredCoordSys.getDimensionsInfo() : registeredCoordSys.dimensions.slice()) || ['x', 'y'];
  19544. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  19545. }
  19546. var categoryIndex = axesInfo ? axesInfo.categoryIndex : -1;
  19547. var list = new List(dimensions, seriesModel);
  19548. var nameList = createNameList(axesInfo, data);
  19549. var categories = {};
  19550. var dimValueGetter = categoryIndex >= 0 && ifNeedCompleteOrdinalData(data) ? function (itemOpt, dimName, dataIndex, dimIndex) {
  19551. // If any dataItem is like { value: 10 }
  19552. if (isDataItemOption(itemOpt)) {
  19553. list.hasItemOption = true;
  19554. } // Use dataIndex as ordinal value in categoryAxis
  19555. return dimIndex === categoryIndex ? dataIndex : converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]);
  19556. } : function (itemOpt, dimName, dataIndex, dimIndex) {
  19557. var value = getDataItemValue(itemOpt);
  19558. var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]); // If any dataItem is like { value: 10 }
  19559. if (isDataItemOption(itemOpt)) {
  19560. list.hasItemOption = true;
  19561. }
  19562. var categoryAxesModels = axesInfo && axesInfo.categoryAxesModels;
  19563. if (categoryAxesModels && categoryAxesModels[dimName]) {
  19564. // If given value is a category string
  19565. if (typeof val === 'string') {
  19566. // Lazy get categories
  19567. categories[dimName] = categories[dimName] || categoryAxesModels[dimName].getCategories();
  19568. val = indexOf(categories[dimName], val);
  19569. if (val < 0 && !isNaN(val)) {
  19570. // In case some one write '1', '2' istead of 1, 2
  19571. val = +val;
  19572. }
  19573. }
  19574. }
  19575. return val;
  19576. };
  19577. list.hasItemOption = false;
  19578. list.initData(data, nameList, dimValueGetter);
  19579. return list;
  19580. }
  19581. function isStackable(axisType) {
  19582. return axisType !== 'category' && axisType !== 'time';
  19583. }
  19584. function getDimTypeByAxis(axisType) {
  19585. return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float';
  19586. }
  19587. /**
  19588. * Creaters for each coord system.
  19589. */
  19590. var creators = {
  19591. cartesian2d: function (data, seriesModel, ecModel, completeDimOpt) {
  19592. var axesModels = map(['xAxis', 'yAxis'], function (name) {
  19593. return ecModel.queryComponents({
  19594. mainType: name,
  19595. index: seriesModel.get(name + 'Index'),
  19596. id: seriesModel.get(name + 'Id')
  19597. })[0];
  19598. });
  19599. var xAxisModel = axesModels[0];
  19600. var yAxisModel = axesModels[1];
  19601. if (true) {
  19602. if (!xAxisModel) {
  19603. throw new Error('xAxis "' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('xAxisId'), 0) + '" not found');
  19604. }
  19605. if (!yAxisModel) {
  19606. throw new Error('yAxis "' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('yAxisId'), 0) + '" not found');
  19607. }
  19608. }
  19609. var xAxisType = xAxisModel.get('type');
  19610. var yAxisType = yAxisModel.get('type');
  19611. var dimensions = [{
  19612. name: 'x',
  19613. type: getDimTypeByAxis(xAxisType),
  19614. stackable: isStackable(xAxisType)
  19615. }, {
  19616. name: 'y',
  19617. // If two category axes
  19618. type: getDimTypeByAxis(yAxisType),
  19619. stackable: isStackable(yAxisType)
  19620. }];
  19621. var isXAxisCateogry = xAxisType === 'category';
  19622. var isYAxisCategory = yAxisType === 'category';
  19623. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  19624. var categoryAxesModels = {};
  19625. if (isXAxisCateogry) {
  19626. categoryAxesModels.x = xAxisModel;
  19627. }
  19628. if (isYAxisCategory) {
  19629. categoryAxesModels.y = yAxisModel;
  19630. }
  19631. return {
  19632. dimensions: dimensions,
  19633. categoryIndex: isXAxisCateogry ? 0 : isYAxisCategory ? 1 : -1,
  19634. categoryAxesModels: categoryAxesModels
  19635. };
  19636. },
  19637. singleAxis: function (data, seriesModel, ecModel, completeDimOpt) {
  19638. var singleAxisModel = ecModel.queryComponents({
  19639. mainType: 'singleAxis',
  19640. index: seriesModel.get('singleAxisIndex'),
  19641. id: seriesModel.get('singleAxisId')
  19642. })[0];
  19643. if (true) {
  19644. if (!singleAxisModel) {
  19645. throw new Error('singleAxis should be specified.');
  19646. }
  19647. }
  19648. var singleAxisType = singleAxisModel.get('type');
  19649. var isCategory = singleAxisType === 'category';
  19650. var dimensions = [{
  19651. name: 'single',
  19652. type: getDimTypeByAxis(singleAxisType),
  19653. stackable: isStackable(singleAxisType)
  19654. }];
  19655. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  19656. var categoryAxesModels = {};
  19657. if (isCategory) {
  19658. categoryAxesModels.single = singleAxisModel;
  19659. }
  19660. return {
  19661. dimensions: dimensions,
  19662. categoryIndex: isCategory ? 0 : -1,
  19663. categoryAxesModels: categoryAxesModels
  19664. };
  19665. },
  19666. polar: function (data, seriesModel, ecModel, completeDimOpt) {
  19667. var polarModel = ecModel.queryComponents({
  19668. mainType: 'polar',
  19669. index: seriesModel.get('polarIndex'),
  19670. id: seriesModel.get('polarId')
  19671. })[0];
  19672. var angleAxisModel = polarModel.findAxisModel('angleAxis');
  19673. var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
  19674. if (true) {
  19675. if (!angleAxisModel) {
  19676. throw new Error('angleAxis option not found');
  19677. }
  19678. if (!radiusAxisModel) {
  19679. throw new Error('radiusAxis option not found');
  19680. }
  19681. }
  19682. var radiusAxisType = radiusAxisModel.get('type');
  19683. var angleAxisType = angleAxisModel.get('type');
  19684. var dimensions = [{
  19685. name: 'radius',
  19686. type: getDimTypeByAxis(radiusAxisType),
  19687. stackable: isStackable(radiusAxisType)
  19688. }, {
  19689. name: 'angle',
  19690. type: getDimTypeByAxis(angleAxisType),
  19691. stackable: isStackable(angleAxisType)
  19692. }];
  19693. var isAngleAxisCateogry = angleAxisType === 'category';
  19694. var isRadiusAxisCateogry = radiusAxisType === 'category';
  19695. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  19696. var categoryAxesModels = {};
  19697. if (isRadiusAxisCateogry) {
  19698. categoryAxesModels.radius = radiusAxisModel;
  19699. }
  19700. if (isAngleAxisCateogry) {
  19701. categoryAxesModels.angle = angleAxisModel;
  19702. }
  19703. return {
  19704. dimensions: dimensions,
  19705. categoryIndex: isAngleAxisCateogry ? 1 : isRadiusAxisCateogry ? 0 : -1,
  19706. categoryAxesModels: categoryAxesModels
  19707. };
  19708. },
  19709. geo: function (data, seriesModel, ecModel, completeDimOpt) {
  19710. // TODO Region
  19711. // 多个散点图系列在同一个地区的时候
  19712. return {
  19713. dimensions: completeDimensions([{
  19714. name: 'lng'
  19715. }, {
  19716. name: 'lat'
  19717. }], data, completeDimOpt)
  19718. };
  19719. }
  19720. };
  19721. function createNameList(result, data) {
  19722. var nameList = [];
  19723. var categoryDim = result && result.dimensions[result.categoryIndex];
  19724. var categoryAxisModel;
  19725. if (categoryDim) {
  19726. categoryAxisModel = result.categoryAxesModels[categoryDim.name];
  19727. }
  19728. if (categoryAxisModel) {
  19729. // FIXME Two category axis
  19730. var categories = categoryAxisModel.getCategories();
  19731. if (categories) {
  19732. var dataLen = data.length; // Ordered data is given explicitly like
  19733. // [[3, 0.2], [1, 0.3], [2, 0.15]]
  19734. // or given scatter data,
  19735. // pick the category
  19736. if (isArray(data[0]) && data[0].length > 1) {
  19737. nameList = [];
  19738. for (var i = 0; i < dataLen; i++) {
  19739. nameList[i] = categories[data[i][result.categoryIndex || 0]];
  19740. }
  19741. } else {
  19742. nameList = categories.slice(0);
  19743. }
  19744. }
  19745. }
  19746. return nameList;
  19747. }
  19748. /**
  19749. * // Scale class management
  19750. * @module echarts/scale/Scale
  19751. */
  19752. /**
  19753. * @param {Object} [setting]
  19754. */
  19755. function Scale(setting) {
  19756. this._setting = setting || {};
  19757. /**
  19758. * Extent
  19759. * @type {Array.<number>}
  19760. * @protected
  19761. */
  19762. this._extent = [Infinity, -Infinity];
  19763. /**
  19764. * Step is calculated in adjustExtent
  19765. * @type {Array.<number>}
  19766. * @protected
  19767. */
  19768. this._interval = 0;
  19769. this.init && this.init.apply(this, arguments);
  19770. }
  19771. var scaleProto$1 = Scale.prototype;
  19772. /**
  19773. * Parse input val to valid inner number.
  19774. * @param {*} val
  19775. * @return {number}
  19776. */
  19777. scaleProto$1.parse = function (val) {
  19778. // Notice: This would be a trap here, If the implementation
  19779. // of this method depends on extent, and this method is used
  19780. // before extent set (like in dataZoom), it would be wrong.
  19781. // Nevertheless, parse does not depend on extent generally.
  19782. return val;
  19783. };
  19784. scaleProto$1.getSetting = function (name) {
  19785. return this._setting[name];
  19786. };
  19787. scaleProto$1.contain = function (val) {
  19788. var extent = this._extent;
  19789. return val >= extent[0] && val <= extent[1];
  19790. };
  19791. /**
  19792. * Normalize value to linear [0, 1], return 0.5 if extent span is 0
  19793. * @param {number} val
  19794. * @return {number}
  19795. */
  19796. scaleProto$1.normalize = function (val) {
  19797. var extent = this._extent;
  19798. if (extent[1] === extent[0]) {
  19799. return 0.5;
  19800. }
  19801. return (val - extent[0]) / (extent[1] - extent[0]);
  19802. };
  19803. /**
  19804. * Scale normalized value
  19805. * @param {number} val
  19806. * @return {number}
  19807. */
  19808. scaleProto$1.scale = function (val) {
  19809. var extent = this._extent;
  19810. return val * (extent[1] - extent[0]) + extent[0];
  19811. };
  19812. /**
  19813. * Set extent from data
  19814. * @param {Array.<number>} other
  19815. */
  19816. scaleProto$1.unionExtent = function (other) {
  19817. var extent = this._extent;
  19818. other[0] < extent[0] && (extent[0] = other[0]);
  19819. other[1] > extent[1] && (extent[1] = other[1]); // not setExtent because in log axis it may transformed to power
  19820. // this.setExtent(extent[0], extent[1]);
  19821. };
  19822. /**
  19823. * Set extent from data
  19824. * @param {module:echarts/data/List} data
  19825. * @param {string} dim
  19826. */
  19827. scaleProto$1.unionExtentFromData = function (data, dim) {
  19828. this.unionExtent(data.getDataExtent(dim, true));
  19829. };
  19830. /**
  19831. * Get extent
  19832. * @return {Array.<number>}
  19833. */
  19834. scaleProto$1.getExtent = function () {
  19835. return this._extent.slice();
  19836. };
  19837. /**
  19838. * Set extent
  19839. * @param {number} start
  19840. * @param {number} end
  19841. */
  19842. scaleProto$1.setExtent = function (start, end) {
  19843. var thisExtent = this._extent;
  19844. if (!isNaN(start)) {
  19845. thisExtent[0] = start;
  19846. }
  19847. if (!isNaN(end)) {
  19848. thisExtent[1] = end;
  19849. }
  19850. };
  19851. /**
  19852. * @return {Array.<string>}
  19853. */
  19854. scaleProto$1.getTicksLabels = function () {
  19855. var labels = [];
  19856. var ticks = this.getTicks();
  19857. for (var i = 0; i < ticks.length; i++) {
  19858. labels.push(this.getLabel(ticks[i]));
  19859. }
  19860. return labels;
  19861. };
  19862. /**
  19863. * When axis extent depends on data and no data exists,
  19864. * axis ticks should not be drawn, which is named 'blank'.
  19865. */
  19866. scaleProto$1.isBlank = function () {
  19867. return this._isBlank;
  19868. },
  19869. /**
  19870. * When axis extent depends on data and no data exists,
  19871. * axis ticks should not be drawn, which is named 'blank'.
  19872. */
  19873. scaleProto$1.setBlank = function (isBlank) {
  19874. this._isBlank = isBlank;
  19875. };
  19876. enableClassExtend(Scale);
  19877. enableClassManagement(Scale, {
  19878. registerWhenExtend: true
  19879. });
  19880. /**
  19881. * Linear continuous scale
  19882. * @module echarts/coord/scale/Ordinal
  19883. *
  19884. * http://en.wikipedia.org/wiki/Level_of_measurement
  19885. */
  19886. // FIXME only one data
  19887. var scaleProto = Scale.prototype;
  19888. var OrdinalScale = Scale.extend({
  19889. type: 'ordinal',
  19890. init: function (data, extent) {
  19891. this._data = data;
  19892. this._extent = extent || [0, data.length - 1];
  19893. },
  19894. parse: function (val) {
  19895. return typeof val === 'string' ? indexOf(this._data, val) // val might be float.
  19896. : Math.round(val);
  19897. },
  19898. contain: function (rank) {
  19899. rank = this.parse(rank);
  19900. return scaleProto.contain.call(this, rank) && this._data[rank] != null;
  19901. },
  19902. /**
  19903. * Normalize given rank or name to linear [0, 1]
  19904. * @param {number|string} [val]
  19905. * @return {number}
  19906. */
  19907. normalize: function (val) {
  19908. return scaleProto.normalize.call(this, this.parse(val));
  19909. },
  19910. scale: function (val) {
  19911. return Math.round(scaleProto.scale.call(this, val));
  19912. },
  19913. /**
  19914. * @return {Array}
  19915. */
  19916. getTicks: function () {
  19917. var ticks = [];
  19918. var extent = this._extent;
  19919. var rank = extent[0];
  19920. while (rank <= extent[1]) {
  19921. ticks.push(rank);
  19922. rank++;
  19923. }
  19924. return ticks;
  19925. },
  19926. /**
  19927. * Get item on rank n
  19928. * @param {number} n
  19929. * @return {string}
  19930. */
  19931. getLabel: function (n) {
  19932. return this._data[n];
  19933. },
  19934. /**
  19935. * @return {number}
  19936. */
  19937. count: function () {
  19938. return this._extent[1] - this._extent[0] + 1;
  19939. },
  19940. /**
  19941. * @override
  19942. */
  19943. unionExtentFromData: function (data, dim) {
  19944. this.unionExtent(data.getDataExtent(dim, false));
  19945. },
  19946. niceTicks: noop,
  19947. niceExtent: noop
  19948. });
  19949. /**
  19950. * @return {module:echarts/scale/Time}
  19951. */
  19952. OrdinalScale.create = function () {
  19953. return new OrdinalScale();
  19954. };
  19955. /**
  19956. * For testable.
  19957. */
  19958. var roundNumber$1 = round;
  19959. /**
  19960. * @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number.
  19961. * Should be extent[0] < extent[1].
  19962. * @param {number} splitNumber splitNumber should be >= 1.
  19963. * @param {number} [minInterval]
  19964. * @param {number} [maxInterval]
  19965. * @return {Object} {interval, intervalPrecision, niceTickExtent}
  19966. */
  19967. function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {
  19968. var result = {};
  19969. var span = extent[1] - extent[0];
  19970. var interval = result.interval = nice(span / splitNumber, true);
  19971. if (minInterval != null && interval < minInterval) {
  19972. interval = result.interval = minInterval;
  19973. }
  19974. if (maxInterval != null && interval > maxInterval) {
  19975. interval = result.interval = maxInterval;
  19976. } // Tow more digital for tick.
  19977. var precision = result.intervalPrecision = getIntervalPrecision(interval); // Niced extent inside original extent
  19978. var niceTickExtent = result.niceTickExtent = [roundNumber$1(Math.ceil(extent[0] / interval) * interval, precision), roundNumber$1(Math.floor(extent[1] / interval) * interval, precision)];
  19979. fixExtent(niceTickExtent, extent);
  19980. return result;
  19981. }
  19982. /**
  19983. * @param {number} interval
  19984. * @return {number} interval precision
  19985. */
  19986. function getIntervalPrecision(interval) {
  19987. // Tow more digital for tick.
  19988. return getPrecisionSafe(interval) + 2;
  19989. }
  19990. function clamp(niceTickExtent, idx, extent) {
  19991. niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);
  19992. } // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
  19993. function fixExtent(niceTickExtent, extent) {
  19994. !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
  19995. !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
  19996. clamp(niceTickExtent, 0, extent);
  19997. clamp(niceTickExtent, 1, extent);
  19998. if (niceTickExtent[0] > niceTickExtent[1]) {
  19999. niceTickExtent[0] = niceTickExtent[1];
  20000. }
  20001. }
  20002. function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) {
  20003. var ticks = []; // If interval is 0, return [];
  20004. if (!interval) {
  20005. return ticks;
  20006. } // Consider this case: using dataZoom toolbox, zoom and zoom.
  20007. var safeLimit = 10000;
  20008. if (extent[0] < niceTickExtent[0]) {
  20009. ticks.push(extent[0]);
  20010. }
  20011. var tick = niceTickExtent[0];
  20012. while (tick <= niceTickExtent[1]) {
  20013. ticks.push(tick); // Avoid rounding error
  20014. tick = roundNumber$1(tick + interval, intervalPrecision);
  20015. if (tick === ticks[ticks.length - 1]) {
  20016. // Consider out of safe float point, e.g.,
  20017. // -3711126.9907707 + 2e-10 === -3711126.9907707
  20018. break;
  20019. }
  20020. if (ticks.length > safeLimit) {
  20021. return [];
  20022. }
  20023. } // Consider this case: the last item of ticks is smaller
  20024. // than niceTickExtent[1] and niceTickExtent[1] === extent[1].
  20025. if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {
  20026. ticks.push(extent[1]);
  20027. }
  20028. return ticks;
  20029. }
  20030. /**
  20031. * Interval scale
  20032. * @module echarts/scale/Interval
  20033. */
  20034. var roundNumber = round;
  20035. /**
  20036. * @alias module:echarts/coord/scale/Interval
  20037. * @constructor
  20038. */
  20039. var IntervalScale = Scale.extend({
  20040. type: 'interval',
  20041. _interval: 0,
  20042. _intervalPrecision: 2,
  20043. setExtent: function (start, end) {
  20044. var thisExtent = this._extent; //start,end may be a Number like '25',so...
  20045. if (!isNaN(start)) {
  20046. thisExtent[0] = parseFloat(start);
  20047. }
  20048. if (!isNaN(end)) {
  20049. thisExtent[1] = parseFloat(end);
  20050. }
  20051. },
  20052. unionExtent: function (other) {
  20053. var extent = this._extent;
  20054. other[0] < extent[0] && (extent[0] = other[0]);
  20055. other[1] > extent[1] && (extent[1] = other[1]); // unionExtent may called by it's sub classes
  20056. IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]);
  20057. },
  20058. /**
  20059. * Get interval
  20060. */
  20061. getInterval: function () {
  20062. return this._interval;
  20063. },
  20064. /**
  20065. * Set interval
  20066. */
  20067. setInterval: function (interval) {
  20068. this._interval = interval; // Dropped auto calculated niceExtent and use user setted extent
  20069. // We assume user wan't to set both interval, min, max to get a better result
  20070. this._niceExtent = this._extent.slice();
  20071. this._intervalPrecision = getIntervalPrecision(interval);
  20072. },
  20073. /**
  20074. * @return {Array.<number>}
  20075. */
  20076. getTicks: function () {
  20077. return intervalScaleGetTicks(this._interval, this._extent, this._niceExtent, this._intervalPrecision);
  20078. },
  20079. /**
  20080. * @return {Array.<string>}
  20081. */
  20082. getTicksLabels: function () {
  20083. var labels = [];
  20084. var ticks = this.getTicks();
  20085. for (var i = 0; i < ticks.length; i++) {
  20086. labels.push(this.getLabel(ticks[i]));
  20087. }
  20088. return labels;
  20089. },
  20090. /**
  20091. * @param {number} data
  20092. * @param {Object} [opt]
  20093. * @param {number|string} [opt.precision] If 'auto', use nice presision.
  20094. * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2.
  20095. * @return {string}
  20096. */
  20097. getLabel: function (data, opt) {
  20098. if (data == null) {
  20099. return '';
  20100. }
  20101. var precision = opt && opt.precision;
  20102. if (precision == null) {
  20103. precision = getPrecisionSafe(data) || 0;
  20104. } else if (precision === 'auto') {
  20105. // Should be more precise then tick.
  20106. precision = this._intervalPrecision;
  20107. } // (1) If `precision` is set, 12.005 should be display as '12.00500'.
  20108. // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.
  20109. data = roundNumber(data, precision, true);
  20110. return addCommas(data);
  20111. },
  20112. /**
  20113. * Update interval and extent of intervals for nice ticks
  20114. *
  20115. * @param {number} [splitNumber = 5] Desired number of ticks
  20116. * @param {number} [minInterval]
  20117. * @param {number} [maxInterval]
  20118. */
  20119. niceTicks: function (splitNumber, minInterval, maxInterval) {
  20120. splitNumber = splitNumber || 5;
  20121. var extent = this._extent;
  20122. var span = extent[1] - extent[0];
  20123. if (!isFinite(span)) {
  20124. return;
  20125. } // User may set axis min 0 and data are all negative
  20126. // FIXME If it needs to reverse ?
  20127. if (span < 0) {
  20128. span = -span;
  20129. extent.reverse();
  20130. }
  20131. var result = intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval);
  20132. this._intervalPrecision = result.intervalPrecision;
  20133. this._interval = result.interval;
  20134. this._niceExtent = result.niceTickExtent;
  20135. },
  20136. /**
  20137. * Nice extent.
  20138. * @param {Object} opt
  20139. * @param {number} [opt.splitNumber = 5] Given approx tick number
  20140. * @param {boolean} [opt.fixMin=false]
  20141. * @param {boolean} [opt.fixMax=false]
  20142. * @param {boolean} [opt.minInterval]
  20143. * @param {boolean} [opt.maxInterval]
  20144. */
  20145. niceExtent: function (opt) {
  20146. var extent = this._extent; // If extent start and end are same, expand them
  20147. if (extent[0] === extent[1]) {
  20148. if (extent[0] !== 0) {
  20149. // Expand extent
  20150. var expandSize = extent[0]; // In the fowllowing case
  20151. // Axis has been fixed max 100
  20152. // Plus data are all 100 and axis extent are [100, 100].
  20153. // Extend to the both side will cause expanded max is larger than fixed max.
  20154. // So only expand to the smaller side.
  20155. if (!opt.fixMax) {
  20156. extent[1] += expandSize / 2;
  20157. extent[0] -= expandSize / 2;
  20158. } else {
  20159. extent[0] -= expandSize / 2;
  20160. }
  20161. } else {
  20162. extent[1] = 1;
  20163. }
  20164. }
  20165. var span = extent[1] - extent[0]; // If there are no data and extent are [Infinity, -Infinity]
  20166. if (!isFinite(span)) {
  20167. extent[0] = 0;
  20168. extent[1] = 1;
  20169. }
  20170. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // var extent = this._extent;
  20171. var interval = this._interval;
  20172. if (!opt.fixMin) {
  20173. extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);
  20174. }
  20175. if (!opt.fixMax) {
  20176. extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);
  20177. }
  20178. }
  20179. });
  20180. /**
  20181. * @return {module:echarts/scale/Time}
  20182. */
  20183. IntervalScale.create = function () {
  20184. return new IntervalScale();
  20185. }; // [About UTC and local time zone]:
  20186. // In most cases, `number.parseDate` will treat input data string as local time
  20187. // (except time zone is specified in time string). And `format.formateTime` returns
  20188. // local time by default. option.useUTC is false by default. This design have
  20189. // concidered these common case:
  20190. // (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
  20191. // in local time by default.
  20192. // (2) By default, the input data string (e.g., '2011-01-02') should be displayed
  20193. // as its original time, without any time difference.
  20194. var intervalScaleProto = IntervalScale.prototype;
  20195. var mathCeil = Math.ceil;
  20196. var mathFloor = Math.floor;
  20197. var ONE_SECOND = 1000;
  20198. var ONE_MINUTE = ONE_SECOND * 60;
  20199. var ONE_HOUR = ONE_MINUTE * 60;
  20200. var ONE_DAY = ONE_HOUR * 24; // FIXME 公用?
  20201. var bisect = function (a, x, lo, hi) {
  20202. while (lo < hi) {
  20203. var mid = lo + hi >>> 1;
  20204. if (a[mid][1] < x) {
  20205. lo = mid + 1;
  20206. } else {
  20207. hi = mid;
  20208. }
  20209. }
  20210. return lo;
  20211. };
  20212. /**
  20213. * @alias module:echarts/coord/scale/Time
  20214. * @constructor
  20215. */
  20216. var TimeScale = IntervalScale.extend({
  20217. type: 'time',
  20218. /**
  20219. * @override
  20220. */
  20221. getLabel: function (val) {
  20222. var stepLvl = this._stepLvl;
  20223. var date = new Date(val);
  20224. return formatTime(stepLvl[0], date, this.getSetting('useUTC'));
  20225. },
  20226. /**
  20227. * @override
  20228. */
  20229. niceExtent: function (opt) {
  20230. var extent = this._extent; // If extent start and end are same, expand them
  20231. if (extent[0] === extent[1]) {
  20232. // Expand extent
  20233. extent[0] -= ONE_DAY;
  20234. extent[1] += ONE_DAY;
  20235. } // If there are no data and extent are [Infinity, -Infinity]
  20236. if (extent[1] === -Infinity && extent[0] === Infinity) {
  20237. var d = new Date();
  20238. extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
  20239. extent[0] = extent[1] - ONE_DAY;
  20240. }
  20241. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // var extent = this._extent;
  20242. var interval = this._interval;
  20243. if (!opt.fixMin) {
  20244. extent[0] = round(mathFloor(extent[0] / interval) * interval);
  20245. }
  20246. if (!opt.fixMax) {
  20247. extent[1] = round(mathCeil(extent[1] / interval) * interval);
  20248. }
  20249. },
  20250. /**
  20251. * @override
  20252. */
  20253. niceTicks: function (approxTickNum, minInterval, maxInterval) {
  20254. approxTickNum = approxTickNum || 10;
  20255. var extent = this._extent;
  20256. var span = extent[1] - extent[0];
  20257. var approxInterval = span / approxTickNum;
  20258. if (minInterval != null && approxInterval < minInterval) {
  20259. approxInterval = minInterval;
  20260. }
  20261. if (maxInterval != null && approxInterval > maxInterval) {
  20262. approxInterval = maxInterval;
  20263. }
  20264. var scaleLevelsLen = scaleLevels.length;
  20265. var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
  20266. var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
  20267. var interval = level[1]; // Same with interval scale if span is much larger than 1 year
  20268. if (level[0] === 'year') {
  20269. var yearSpan = span / interval; // From "Nice Numbers for Graph Labels" of Graphic Gems
  20270. // var niceYearSpan = numberUtil.nice(yearSpan, false);
  20271. var yearStep = nice(yearSpan / approxTickNum, true);
  20272. interval *= yearStep;
  20273. }
  20274. var timezoneOffset = this.getSetting('useUTC') ? 0 : new Date(+extent[0] || +extent[1]).getTimezoneOffset() * 60 * 1000;
  20275. var niceExtent = [Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset), Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)];
  20276. fixExtent(niceExtent, extent);
  20277. this._stepLvl = level; // Interval will be used in getTicks
  20278. this._interval = interval;
  20279. this._niceExtent = niceExtent;
  20280. },
  20281. parse: function (val) {
  20282. // val might be float.
  20283. return +parseDate(val);
  20284. }
  20285. });
  20286. each$1(['contain', 'normalize'], function (methodName) {
  20287. TimeScale.prototype[methodName] = function (val) {
  20288. return intervalScaleProto[methodName].call(this, this.parse(val));
  20289. };
  20290. }); // Steps from d3
  20291. var scaleLevels = [// Format interval
  20292. ['hh:mm:ss', ONE_SECOND], // 1s
  20293. ['hh:mm:ss', ONE_SECOND * 5], // 5s
  20294. ['hh:mm:ss', ONE_SECOND * 10], // 10s
  20295. ['hh:mm:ss', ONE_SECOND * 15], // 15s
  20296. ['hh:mm:ss', ONE_SECOND * 30], // 30s
  20297. ['hh:mm\nMM-dd', ONE_MINUTE], // 1m
  20298. ['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m
  20299. ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m
  20300. ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m
  20301. ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m
  20302. ['hh:mm\nMM-dd', ONE_HOUR], // 1h
  20303. ['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h
  20304. ['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h
  20305. ['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h
  20306. ['MM-dd\nyyyy', ONE_DAY], // 1d
  20307. ['MM-dd\nyyyy', ONE_DAY * 2], // 2d
  20308. ['MM-dd\nyyyy', ONE_DAY * 3], // 3d
  20309. ['MM-dd\nyyyy', ONE_DAY * 4], // 4d
  20310. ['MM-dd\nyyyy', ONE_DAY * 5], // 5d
  20311. ['MM-dd\nyyyy', ONE_DAY * 6], // 6d
  20312. ['week', ONE_DAY * 7], // 7d
  20313. ['MM-dd\nyyyy', ONE_DAY * 10], // 10d
  20314. ['week', ONE_DAY * 14], // 2w
  20315. ['week', ONE_DAY * 21], // 3w
  20316. ['month', ONE_DAY * 31], // 1M
  20317. ['week', ONE_DAY * 42], // 6w
  20318. ['month', ONE_DAY * 62], // 2M
  20319. ['week', ONE_DAY * 42], // 10w
  20320. ['quarter', ONE_DAY * 380 / 4], // 3M
  20321. ['month', ONE_DAY * 31 * 4], // 4M
  20322. ['month', ONE_DAY * 31 * 5], // 5M
  20323. ['half-year', ONE_DAY * 380 / 2], // 6M
  20324. ['month', ONE_DAY * 31 * 8], // 8M
  20325. ['month', ONE_DAY * 31 * 10], // 10M
  20326. ['year', ONE_DAY * 380] // 1Y
  20327. ];
  20328. /**
  20329. * @param {module:echarts/model/Model}
  20330. * @return {module:echarts/scale/Time}
  20331. */
  20332. TimeScale.create = function (model) {
  20333. return new TimeScale({
  20334. useUTC: model.ecModel.get('useUTC')
  20335. });
  20336. };
  20337. /**
  20338. * Log scale
  20339. * @module echarts/scale/Log
  20340. */
  20341. // Use some method of IntervalScale
  20342. var scaleProto$2 = Scale.prototype;
  20343. var intervalScaleProto$1 = IntervalScale.prototype;
  20344. var getPrecisionSafe$1 = getPrecisionSafe;
  20345. var roundingErrorFix = round;
  20346. var mathFloor$1 = Math.floor;
  20347. var mathCeil$1 = Math.ceil;
  20348. var mathPow$1 = Math.pow;
  20349. var mathLog = Math.log;
  20350. var LogScale = Scale.extend({
  20351. type: 'log',
  20352. base: 10,
  20353. $constructor: function () {
  20354. Scale.apply(this, arguments);
  20355. this._originalScale = new IntervalScale();
  20356. },
  20357. /**
  20358. * @return {Array.<number>}
  20359. */
  20360. getTicks: function () {
  20361. var originalScale = this._originalScale;
  20362. var extent = this._extent;
  20363. var originalExtent = originalScale.getExtent();
  20364. return map(intervalScaleProto$1.getTicks.call(this), function (val) {
  20365. var powVal = round(mathPow$1(this.base, val)); // Fix #4158
  20366. powVal = val === extent[0] && originalScale.__fixMin ? fixRoundingError(powVal, originalExtent[0]) : powVal;
  20367. powVal = val === extent[1] && originalScale.__fixMax ? fixRoundingError(powVal, originalExtent[1]) : powVal;
  20368. return powVal;
  20369. }, this);
  20370. },
  20371. /**
  20372. * @param {number} val
  20373. * @return {string}
  20374. */
  20375. getLabel: intervalScaleProto$1.getLabel,
  20376. /**
  20377. * @param {number} val
  20378. * @return {number}
  20379. */
  20380. scale: function (val) {
  20381. val = scaleProto$2.scale.call(this, val);
  20382. return mathPow$1(this.base, val);
  20383. },
  20384. /**
  20385. * @param {number} start
  20386. * @param {number} end
  20387. */
  20388. setExtent: function (start, end) {
  20389. var base = this.base;
  20390. start = mathLog(start) / mathLog(base);
  20391. end = mathLog(end) / mathLog(base);
  20392. intervalScaleProto$1.setExtent.call(this, start, end);
  20393. },
  20394. /**
  20395. * @return {number} end
  20396. */
  20397. getExtent: function () {
  20398. var base = this.base;
  20399. var extent = scaleProto$2.getExtent.call(this);
  20400. extent[0] = mathPow$1(base, extent[0]);
  20401. extent[1] = mathPow$1(base, extent[1]); // Fix #4158
  20402. var originalScale = this._originalScale;
  20403. var originalExtent = originalScale.getExtent();
  20404. originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));
  20405. originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));
  20406. return extent;
  20407. },
  20408. /**
  20409. * @param {Array.<number>} extent
  20410. */
  20411. unionExtent: function (extent) {
  20412. this._originalScale.unionExtent(extent);
  20413. var base = this.base;
  20414. extent[0] = mathLog(extent[0]) / mathLog(base);
  20415. extent[1] = mathLog(extent[1]) / mathLog(base);
  20416. scaleProto$2.unionExtent.call(this, extent);
  20417. },
  20418. /**
  20419. * @override
  20420. */
  20421. unionExtentFromData: function (data, dim) {
  20422. this.unionExtent(data.getDataExtent(dim, true, function (val) {
  20423. return val > 0;
  20424. }));
  20425. },
  20426. /**
  20427. * Update interval and extent of intervals for nice ticks
  20428. * @param {number} [approxTickNum = 10] Given approx tick number
  20429. */
  20430. niceTicks: function (approxTickNum) {
  20431. approxTickNum = approxTickNum || 10;
  20432. var extent = this._extent;
  20433. var span = extent[1] - extent[0];
  20434. if (span === Infinity || span <= 0) {
  20435. return;
  20436. }
  20437. var interval = quantity(span);
  20438. var err = approxTickNum / span * interval; // Filter ticks to get closer to the desired count.
  20439. if (err <= 0.5) {
  20440. interval *= 10;
  20441. } // Interval should be integer
  20442. while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {
  20443. interval *= 10;
  20444. }
  20445. var niceExtent = [round(mathCeil$1(extent[0] / interval) * interval), round(mathFloor$1(extent[1] / interval) * interval)];
  20446. this._interval = interval;
  20447. this._niceExtent = niceExtent;
  20448. },
  20449. /**
  20450. * Nice extent.
  20451. * @override
  20452. */
  20453. niceExtent: function (opt) {
  20454. intervalScaleProto$1.niceExtent.call(this, opt);
  20455. var originalScale = this._originalScale;
  20456. originalScale.__fixMin = opt.fixMin;
  20457. originalScale.__fixMax = opt.fixMax;
  20458. }
  20459. });
  20460. each$1(['contain', 'normalize'], function (methodName) {
  20461. LogScale.prototype[methodName] = function (val) {
  20462. val = mathLog(val) / mathLog(this.base);
  20463. return scaleProto$2[methodName].call(this, val);
  20464. };
  20465. });
  20466. LogScale.create = function () {
  20467. return new LogScale();
  20468. };
  20469. function fixRoundingError(val, originalVal) {
  20470. return roundingErrorFix(val, getPrecisionSafe$1(originalVal));
  20471. }
  20472. /**
  20473. * Get axis scale extent before niced.
  20474. * Item of returned array can only be number (including Infinity and NaN).
  20475. */
  20476. function getScaleExtent(scale, model) {
  20477. var scaleType = scale.type;
  20478. var min = model.getMin();
  20479. var max = model.getMax();
  20480. var fixMin = min != null;
  20481. var fixMax = max != null;
  20482. var originalExtent = scale.getExtent();
  20483. var axisDataLen;
  20484. var boundaryGap;
  20485. var span;
  20486. if (scaleType === 'ordinal') {
  20487. axisDataLen = (model.get('data') || []).length;
  20488. } else {
  20489. boundaryGap = model.get('boundaryGap');
  20490. if (!isArray(boundaryGap)) {
  20491. boundaryGap = [boundaryGap || 0, boundaryGap || 0];
  20492. }
  20493. if (typeof boundaryGap[0] === 'boolean') {
  20494. if (true) {
  20495. console.warn('Boolean type for boundaryGap is only ' + 'allowed for ordinal axis. Please use string in ' + 'percentage instead, e.g., "20%". Currently, ' + 'boundaryGap is set to be 0.');
  20496. }
  20497. boundaryGap = [0, 0];
  20498. }
  20499. boundaryGap[0] = parsePercent$1(boundaryGap[0], 1);
  20500. boundaryGap[1] = parsePercent$1(boundaryGap[1], 1);
  20501. span = originalExtent[1] - originalExtent[0] || Math.abs(originalExtent[0]);
  20502. } // Notice: When min/max is not set (that is, when there are null/undefined,
  20503. // which is the most common case), these cases should be ensured:
  20504. // (1) For 'ordinal', show all axis.data.
  20505. // (2) For others:
  20506. // + `boundaryGap` is applied (if min/max set, boundaryGap is
  20507. // disabled).
  20508. // + If `needCrossZero`, min/max should be zero, otherwise, min/max should
  20509. // be the result that originalExtent enlarged by boundaryGap.
  20510. // (3) If no data, it should be ensured that `scale.setBlank` is set.
  20511. // FIXME
  20512. // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used?
  20513. // (2) When `needCrossZero` and all data is positive/negative, should it be ensured
  20514. // that the results processed by boundaryGap are positive/negative?
  20515. if (min == null) {
  20516. min = scaleType === 'ordinal' ? axisDataLen ? 0 : NaN : originalExtent[0] - boundaryGap[0] * span;
  20517. }
  20518. if (max == null) {
  20519. max = scaleType === 'ordinal' ? axisDataLen ? axisDataLen - 1 : NaN : originalExtent[1] + boundaryGap[1] * span;
  20520. }
  20521. if (min === 'dataMin') {
  20522. min = originalExtent[0];
  20523. } else if (typeof min === 'function') {
  20524. min = min({
  20525. min: originalExtent[0],
  20526. max: originalExtent[1]
  20527. });
  20528. }
  20529. if (max === 'dataMax') {
  20530. max = originalExtent[1];
  20531. } else if (typeof max === 'function') {
  20532. max = max({
  20533. min: originalExtent[0],
  20534. max: originalExtent[1]
  20535. });
  20536. }
  20537. (min == null || !isFinite(min)) && (min = NaN);
  20538. (max == null || !isFinite(max)) && (max = NaN);
  20539. scale.setBlank(eqNaN(min) || eqNaN(max)); // Evaluate if axis needs cross zero
  20540. if (model.getNeedCrossZero()) {
  20541. // Axis is over zero and min is not set
  20542. if (min > 0 && max > 0 && !fixMin) {
  20543. min = 0;
  20544. } // Axis is under zero and max is not set
  20545. if (min < 0 && max < 0 && !fixMax) {
  20546. max = 0;
  20547. }
  20548. }
  20549. return [min, max];
  20550. }
  20551. function niceScaleExtent(scale, model) {
  20552. var extent = getScaleExtent(scale, model);
  20553. var fixMin = model.getMin() != null;
  20554. var fixMax = model.getMax() != null;
  20555. var splitNumber = model.get('splitNumber');
  20556. if (scale.type === 'log') {
  20557. scale.base = model.get('logBase');
  20558. }
  20559. var scaleType = scale.type;
  20560. scale.setExtent(extent[0], extent[1]);
  20561. scale.niceExtent({
  20562. splitNumber: splitNumber,
  20563. fixMin: fixMin,
  20564. fixMax: fixMax,
  20565. minInterval: scaleType === 'interval' || scaleType === 'time' ? model.get('minInterval') : null,
  20566. maxInterval: scaleType === 'interval' || scaleType === 'time' ? model.get('maxInterval') : null
  20567. }); // If some one specified the min, max. And the default calculated interval
  20568. // is not good enough. He can specify the interval. It is often appeared
  20569. // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard
  20570. // to be 60.
  20571. // FIXME
  20572. var interval = model.get('interval');
  20573. if (interval != null) {
  20574. scale.setInterval && scale.setInterval(interval);
  20575. }
  20576. }
  20577. /**
  20578. * @param {module:echarts/model/Model} model
  20579. * @param {string} [axisType] Default retrieve from model.type
  20580. * @return {module:echarts/scale/*}
  20581. */
  20582. function createScaleByModel(model, axisType) {
  20583. axisType = axisType || model.get('type');
  20584. if (axisType) {
  20585. switch (axisType) {
  20586. // Buildin scale
  20587. case 'category':
  20588. return new OrdinalScale(model.getCategories(), [Infinity, -Infinity]);
  20589. case 'value':
  20590. return new IntervalScale();
  20591. // Extended scale, like time and log
  20592. default:
  20593. return (Scale.getClass(axisType) || IntervalScale).create(model);
  20594. }
  20595. }
  20596. }
  20597. /**
  20598. * Check if the axis corss 0
  20599. */
  20600. function ifAxisCrossZero(axis) {
  20601. var dataExtent = axis.scale.getExtent();
  20602. var min = dataExtent[0];
  20603. var max = dataExtent[1];
  20604. return !(min > 0 && max > 0 || min < 0 && max < 0);
  20605. }
  20606. /**
  20607. * @param {Array.<number>} tickCoords In axis self coordinate.
  20608. * @param {Array.<string>} labels
  20609. * @param {string} font
  20610. * @param {number} axisRotate 0: towards right horizontally, clock-wise is negative.
  20611. * @param {number} [labelRotate=0] 0: towards right horizontally, clock-wise is negative.
  20612. * @return {number}
  20613. */
  20614. function getAxisLabelInterval(tickCoords, labels, font, axisRotate, labelRotate) {
  20615. var textSpaceTakenRect;
  20616. var autoLabelInterval = 0;
  20617. var accumulatedLabelInterval = 0;
  20618. var rotation = (axisRotate - labelRotate) / 180 * Math.PI;
  20619. var step = 1;
  20620. if (labels.length > 40) {
  20621. // Simple optimization for large amount of labels
  20622. step = Math.floor(labels.length / 40);
  20623. }
  20624. for (var i = 0; i < tickCoords.length; i += step) {
  20625. var tickCoord = tickCoords[i]; // Not precise, do not consider align and vertical align
  20626. // and each distance from axis line yet.
  20627. var rect = getBoundingRect(labels[i], font, 'center', 'top');
  20628. rect.x += tickCoord * Math.cos(rotation);
  20629. rect.y += tickCoord * Math.sin(rotation); // Magic number
  20630. rect.width *= 1.3;
  20631. rect.height *= 1.3;
  20632. if (!textSpaceTakenRect) {
  20633. textSpaceTakenRect = rect.clone();
  20634. } // There is no space for current label;
  20635. else if (textSpaceTakenRect.intersect(rect)) {
  20636. accumulatedLabelInterval++;
  20637. autoLabelInterval = Math.max(autoLabelInterval, accumulatedLabelInterval);
  20638. } else {
  20639. textSpaceTakenRect.union(rect); // Reset
  20640. accumulatedLabelInterval = 0;
  20641. }
  20642. }
  20643. if (autoLabelInterval === 0 && step > 1) {
  20644. return step;
  20645. }
  20646. return (autoLabelInterval + 1) * step - 1;
  20647. }
  20648. /**
  20649. * @param {Object} axis
  20650. * @param {Function} labelFormatter
  20651. * @return {Array.<string>}
  20652. */
  20653. function getFormattedLabels(axis, labelFormatter) {
  20654. var scale = axis.scale;
  20655. var labels = scale.getTicksLabels();
  20656. var ticks = scale.getTicks();
  20657. if (typeof labelFormatter === 'string') {
  20658. labelFormatter = function (tpl) {
  20659. return function (val) {
  20660. return tpl.replace('{value}', val != null ? val : '');
  20661. };
  20662. }(labelFormatter); // Consider empty array
  20663. return map(labels, labelFormatter);
  20664. } else if (typeof labelFormatter === 'function') {
  20665. return map(ticks, function (tick, idx) {
  20666. return labelFormatter(getAxisRawValue(axis, tick), idx);
  20667. }, this);
  20668. } else {
  20669. return labels;
  20670. }
  20671. }
  20672. function getAxisRawValue(axis, value) {
  20673. // In category axis with data zoom, tick is not the original
  20674. // index of axis.data. So tick should not be exposed to user
  20675. // in category axis.
  20676. return axis.type === 'category' ? axis.scale.getLabel(value) : value;
  20677. }
  20678. function getName(obj) {
  20679. if (isObject(obj) && obj.value != null) {
  20680. return obj.value;
  20681. } else {
  20682. return obj + '';
  20683. }
  20684. }
  20685. var axisModelCommonMixin = {
  20686. /**
  20687. * Format labels
  20688. * @return {Array.<string>}
  20689. */
  20690. getFormattedLabels: function () {
  20691. return getFormattedLabels(this.axis, this.get('axisLabel.formatter'));
  20692. },
  20693. /**
  20694. * Get categories
  20695. */
  20696. getCategories: function () {
  20697. return this.get('type') === 'category' && map(this.get('data'), getName);
  20698. },
  20699. /**
  20700. * @param {boolean} origin
  20701. * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN
  20702. */
  20703. getMin: function (origin) {
  20704. var option = this.option;
  20705. var min = !origin && option.rangeStart != null ? option.rangeStart : option.min;
  20706. if (this.axis && min != null && min !== 'dataMin' && typeof min !== 'function' && !eqNaN(min)) {
  20707. min = this.axis.scale.parse(min);
  20708. }
  20709. return min;
  20710. },
  20711. /**
  20712. * @param {boolean} origin
  20713. * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN
  20714. */
  20715. getMax: function (origin) {
  20716. var option = this.option;
  20717. var max = !origin && option.rangeEnd != null ? option.rangeEnd : option.max;
  20718. if (this.axis && max != null && max !== 'dataMax' && typeof max !== 'function' && !eqNaN(max)) {
  20719. max = this.axis.scale.parse(max);
  20720. }
  20721. return max;
  20722. },
  20723. /**
  20724. * @return {boolean}
  20725. */
  20726. getNeedCrossZero: function () {
  20727. var option = this.option;
  20728. return option.rangeStart != null || option.rangeEnd != null ? false : !option.scale;
  20729. },
  20730. /**
  20731. * Should be implemented by each axis model if necessary.
  20732. * @return {module:echarts/model/Component} coordinate system model
  20733. */
  20734. getCoordSysModel: noop,
  20735. /**
  20736. * @param {number} rangeStart Can only be finite number or null/undefined or NaN.
  20737. * @param {number} rangeEnd Can only be finite number or null/undefined or NaN.
  20738. */
  20739. setRange: function (rangeStart, rangeEnd) {
  20740. this.option.rangeStart = rangeStart;
  20741. this.option.rangeEnd = rangeEnd;
  20742. },
  20743. /**
  20744. * Reset range
  20745. */
  20746. resetRange: function () {
  20747. // rangeStart and rangeEnd is readonly.
  20748. this.option.rangeStart = this.option.rangeEnd = null;
  20749. }
  20750. }; // Symbol factory
  20751. /**
  20752. * Triangle shape
  20753. * @inner
  20754. */
  20755. var Triangle = extendShape({
  20756. type: 'triangle',
  20757. shape: {
  20758. cx: 0,
  20759. cy: 0,
  20760. width: 0,
  20761. height: 0
  20762. },
  20763. buildPath: function (path, shape) {
  20764. var cx = shape.cx;
  20765. var cy = shape.cy;
  20766. var width = shape.width / 2;
  20767. var height = shape.height / 2;
  20768. path.moveTo(cx, cy - height);
  20769. path.lineTo(cx + width, cy + height);
  20770. path.lineTo(cx - width, cy + height);
  20771. path.closePath();
  20772. }
  20773. });
  20774. /**
  20775. * Diamond shape
  20776. * @inner
  20777. */
  20778. var Diamond = extendShape({
  20779. type: 'diamond',
  20780. shape: {
  20781. cx: 0,
  20782. cy: 0,
  20783. width: 0,
  20784. height: 0
  20785. },
  20786. buildPath: function (path, shape) {
  20787. var cx = shape.cx;
  20788. var cy = shape.cy;
  20789. var width = shape.width / 2;
  20790. var height = shape.height / 2;
  20791. path.moveTo(cx, cy - height);
  20792. path.lineTo(cx + width, cy);
  20793. path.lineTo(cx, cy + height);
  20794. path.lineTo(cx - width, cy);
  20795. path.closePath();
  20796. }
  20797. });
  20798. /**
  20799. * Pin shape
  20800. * @inner
  20801. */
  20802. var Pin = extendShape({
  20803. type: 'pin',
  20804. shape: {
  20805. // x, y on the cusp
  20806. x: 0,
  20807. y: 0,
  20808. width: 0,
  20809. height: 0
  20810. },
  20811. buildPath: function (path, shape) {
  20812. var x = shape.x;
  20813. var y = shape.y;
  20814. var w = shape.width / 5 * 3; // Height must be larger than width
  20815. var h = Math.max(w, shape.height);
  20816. var r = w / 2; // Dist on y with tangent point and circle center
  20817. var dy = r * r / (h - r);
  20818. var cy = y - h + r + dy;
  20819. var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center
  20820. var dx = Math.cos(angle) * r;
  20821. var tanX = Math.sin(angle);
  20822. var tanY = Math.cos(angle);
  20823. var cpLen = r * 0.6;
  20824. var cpLen2 = r * 0.7;
  20825. path.moveTo(x - dx, cy + dy);
  20826. path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);
  20827. path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y);
  20828. path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy);
  20829. path.closePath();
  20830. }
  20831. });
  20832. /**
  20833. * Arrow shape
  20834. * @inner
  20835. */
  20836. var Arrow = extendShape({
  20837. type: 'arrow',
  20838. shape: {
  20839. x: 0,
  20840. y: 0,
  20841. width: 0,
  20842. height: 0
  20843. },
  20844. buildPath: function (ctx, shape) {
  20845. var height = shape.height;
  20846. var width = shape.width;
  20847. var x = shape.x;
  20848. var y = shape.y;
  20849. var dx = width / 3 * 2;
  20850. ctx.moveTo(x, y);
  20851. ctx.lineTo(x + dx, y + height);
  20852. ctx.lineTo(x, y + height / 4 * 3);
  20853. ctx.lineTo(x - dx, y + height);
  20854. ctx.lineTo(x, y);
  20855. ctx.closePath();
  20856. }
  20857. });
  20858. /**
  20859. * Map of path contructors
  20860. * @type {Object.<string, module:zrender/graphic/Path>}
  20861. */
  20862. var symbolCtors = {
  20863. line: Line,
  20864. rect: Rect,
  20865. roundRect: Rect,
  20866. square: Rect,
  20867. circle: Circle,
  20868. diamond: Diamond,
  20869. pin: Pin,
  20870. arrow: Arrow,
  20871. triangle: Triangle
  20872. };
  20873. var symbolShapeMakers = {
  20874. line: function (x, y, w, h, shape) {
  20875. // FIXME
  20876. shape.x1 = x;
  20877. shape.y1 = y + h / 2;
  20878. shape.x2 = x + w;
  20879. shape.y2 = y + h / 2;
  20880. },
  20881. rect: function (x, y, w, h, shape) {
  20882. shape.x = x;
  20883. shape.y = y;
  20884. shape.width = w;
  20885. shape.height = h;
  20886. },
  20887. roundRect: function (x, y, w, h, shape) {
  20888. shape.x = x;
  20889. shape.y = y;
  20890. shape.width = w;
  20891. shape.height = h;
  20892. shape.r = Math.min(w, h) / 4;
  20893. },
  20894. square: function (x, y, w, h, shape) {
  20895. var size = Math.min(w, h);
  20896. shape.x = x;
  20897. shape.y = y;
  20898. shape.width = size;
  20899. shape.height = size;
  20900. },
  20901. circle: function (x, y, w, h, shape) {
  20902. // Put circle in the center of square
  20903. shape.cx = x + w / 2;
  20904. shape.cy = y + h / 2;
  20905. shape.r = Math.min(w, h) / 2;
  20906. },
  20907. diamond: function (x, y, w, h, shape) {
  20908. shape.cx = x + w / 2;
  20909. shape.cy = y + h / 2;
  20910. shape.width = w;
  20911. shape.height = h;
  20912. },
  20913. pin: function (x, y, w, h, shape) {
  20914. shape.x = x + w / 2;
  20915. shape.y = y + h / 2;
  20916. shape.width = w;
  20917. shape.height = h;
  20918. },
  20919. arrow: function (x, y, w, h, shape) {
  20920. shape.x = x + w / 2;
  20921. shape.y = y + h / 2;
  20922. shape.width = w;
  20923. shape.height = h;
  20924. },
  20925. triangle: function (x, y, w, h, shape) {
  20926. shape.cx = x + w / 2;
  20927. shape.cy = y + h / 2;
  20928. shape.width = w;
  20929. shape.height = h;
  20930. }
  20931. };
  20932. var symbolBuildProxies = {};
  20933. each$1(symbolCtors, function (Ctor, name) {
  20934. symbolBuildProxies[name] = new Ctor();
  20935. });
  20936. var SymbolClz = extendShape({
  20937. type: 'symbol',
  20938. shape: {
  20939. symbolType: '',
  20940. x: 0,
  20941. y: 0,
  20942. width: 0,
  20943. height: 0
  20944. },
  20945. beforeBrush: function () {
  20946. var style = this.style;
  20947. var shape = this.shape; // FIXME
  20948. if (shape.symbolType === 'pin' && style.textPosition === 'inside') {
  20949. style.textPosition = ['50%', '40%'];
  20950. style.textAlign = 'center';
  20951. style.textVerticalAlign = 'middle';
  20952. }
  20953. },
  20954. buildPath: function (ctx, shape, inBundle) {
  20955. var symbolType = shape.symbolType;
  20956. var proxySymbol = symbolBuildProxies[symbolType];
  20957. if (shape.symbolType !== 'none') {
  20958. if (!proxySymbol) {
  20959. // Default rect
  20960. symbolType = 'rect';
  20961. proxySymbol = symbolBuildProxies[symbolType];
  20962. }
  20963. symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape);
  20964. proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
  20965. }
  20966. }
  20967. }); // Provide setColor helper method to avoid determine if set the fill or stroke outside
  20968. function symbolPathSetColor(color, innerColor) {
  20969. if (this.type !== 'image') {
  20970. var symbolStyle = this.style;
  20971. var symbolShape = this.shape;
  20972. if (symbolShape && symbolShape.symbolType === 'line') {
  20973. symbolStyle.stroke = color;
  20974. } else if (this.__isEmptyBrush) {
  20975. symbolStyle.stroke = color;
  20976. symbolStyle.fill = innerColor || '#fff';
  20977. } else {
  20978. // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
  20979. symbolStyle.fill && (symbolStyle.fill = color);
  20980. symbolStyle.stroke && (symbolStyle.stroke = color);
  20981. }
  20982. this.dirty(false);
  20983. }
  20984. }
  20985. /**
  20986. * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
  20987. * @param {string} symbolType
  20988. * @param {number} x
  20989. * @param {number} y
  20990. * @param {number} w
  20991. * @param {number} h
  20992. * @param {string} color
  20993. * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
  20994. * for path and image only.
  20995. */
  20996. function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
  20997. // TODO Support image object, DynamicImage.
  20998. var isEmpty = symbolType.indexOf('empty') === 0;
  20999. if (isEmpty) {
  21000. symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
  21001. }
  21002. var symbolPath;
  21003. if (symbolType.indexOf('image://') === 0) {
  21004. symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
  21005. } else if (symbolType.indexOf('path://') === 0) {
  21006. symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
  21007. } else {
  21008. symbolPath = new SymbolClz({
  21009. shape: {
  21010. symbolType: symbolType,
  21011. x: x,
  21012. y: y,
  21013. width: w,
  21014. height: h
  21015. }
  21016. });
  21017. }
  21018. symbolPath.__isEmptyBrush = isEmpty;
  21019. symbolPath.setColor = symbolPathSetColor;
  21020. symbolPath.setColor(color);
  21021. return symbolPath;
  21022. }
  21023. /**
  21024. * Create a muti dimension List structure from seriesModel.
  21025. * @param {module:echarts/model/Model} seriesModel
  21026. * @return {module:echarts/data/List} list
  21027. */
  21028. function createList(seriesModel) {
  21029. var data = seriesModel.get('data');
  21030. return createListFromArray(data, seriesModel, seriesModel.ecModel);
  21031. }
  21032. /**
  21033. * Create scale
  21034. * @param {Array.<number>} dataExtent
  21035. * @param {Object|module:echarts/Model} option
  21036. */
  21037. function createScale(dataExtent, option) {
  21038. var axisModel = option;
  21039. if (!(option instanceof Model)) {
  21040. axisModel = new Model(option);
  21041. mixin(axisModel, axisModelCommonMixin);
  21042. }
  21043. var scale = createScaleByModel(axisModel);
  21044. scale.setExtent(dataExtent[0], dataExtent[1]);
  21045. niceScaleExtent(scale, axisModel);
  21046. return scale;
  21047. }
  21048. /**
  21049. * Mixin common methods to axis model,
  21050. *
  21051. * Inlcude methods
  21052. * `getFormattedLabels() => Array.<string>`
  21053. * `getCategories() => Array.<string>`
  21054. * `getMin(origin: boolean) => number`
  21055. * `getMax(origin: boolean) => number`
  21056. * `getNeedCrossZero() => boolean`
  21057. * `setRange(start: number, end: number)`
  21058. * `resetRange()`
  21059. */
  21060. function mixinAxisModelCommonMethods(Model$$1) {
  21061. mixin(Model$$1, axisModelCommonMixin);
  21062. }
  21063. var helper = (Object.freeze || Object)({
  21064. createList: createList,
  21065. createScale: createScale,
  21066. mixinAxisModelCommonMethods: mixinAxisModelCommonMethods,
  21067. completeDimensions: completeDimensions,
  21068. createSymbol: createSymbol
  21069. });
  21070. var linearMap$1 = linearMap;
  21071. function fixExtentWithBands(extent, nTick) {
  21072. var size = extent[1] - extent[0];
  21073. var len = nTick;
  21074. var margin = size / len / 2;
  21075. extent[0] += margin;
  21076. extent[1] -= margin;
  21077. }
  21078. var normalizedExtent = [0, 1];
  21079. /**
  21080. * @name module:echarts/coord/CartesianAxis
  21081. * @constructor
  21082. */
  21083. var Axis = function (dim, scale, extent) {
  21084. /**
  21085. * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'
  21086. * @type {string}
  21087. */
  21088. this.dim = dim;
  21089. /**
  21090. * Axis scale
  21091. * @type {module:echarts/coord/scale/*}
  21092. */
  21093. this.scale = scale;
  21094. /**
  21095. * @type {Array.<number>}
  21096. * @private
  21097. */
  21098. this._extent = extent || [0, 0];
  21099. /**
  21100. * @type {boolean}
  21101. */
  21102. this.inverse = false;
  21103. /**
  21104. * Usually true when axis has a ordinal scale
  21105. * @type {boolean}
  21106. */
  21107. this.onBand = false;
  21108. /**
  21109. * @private
  21110. * @type {number}
  21111. */
  21112. this._labelInterval;
  21113. };
  21114. Axis.prototype = {
  21115. constructor: Axis,
  21116. /**
  21117. * If axis extent contain given coord
  21118. * @param {number} coord
  21119. * @return {boolean}
  21120. */
  21121. contain: function (coord) {
  21122. var extent = this._extent;
  21123. var min = Math.min(extent[0], extent[1]);
  21124. var max = Math.max(extent[0], extent[1]);
  21125. return coord >= min && coord <= max;
  21126. },
  21127. /**
  21128. * If axis extent contain given data
  21129. * @param {number} data
  21130. * @return {boolean}
  21131. */
  21132. containData: function (data) {
  21133. return this.contain(this.dataToCoord(data));
  21134. },
  21135. /**
  21136. * Get coord extent.
  21137. * @return {Array.<number>}
  21138. */
  21139. getExtent: function () {
  21140. return this._extent.slice();
  21141. },
  21142. /**
  21143. * Get precision used for formatting
  21144. * @param {Array.<number>} [dataExtent]
  21145. * @return {number}
  21146. */
  21147. getPixelPrecision: function (dataExtent) {
  21148. return getPixelPrecision(dataExtent || this.scale.getExtent(), this._extent);
  21149. },
  21150. /**
  21151. * Set coord extent
  21152. * @param {number} start
  21153. * @param {number} end
  21154. */
  21155. setExtent: function (start, end) {
  21156. var extent = this._extent;
  21157. extent[0] = start;
  21158. extent[1] = end;
  21159. },
  21160. /**
  21161. * Convert data to coord. Data is the rank if it has a ordinal scale
  21162. * @param {number} data
  21163. * @param {boolean} clamp
  21164. * @return {number}
  21165. */
  21166. dataToCoord: function (data, clamp) {
  21167. var extent = this._extent;
  21168. var scale = this.scale;
  21169. data = scale.normalize(data);
  21170. if (this.onBand && scale.type === 'ordinal') {
  21171. extent = extent.slice();
  21172. fixExtentWithBands(extent, scale.count());
  21173. }
  21174. return linearMap$1(data, normalizedExtent, extent, clamp);
  21175. },
  21176. /**
  21177. * Convert coord to data. Data is the rank if it has a ordinal scale
  21178. * @param {number} coord
  21179. * @param {boolean} clamp
  21180. * @return {number}
  21181. */
  21182. coordToData: function (coord, clamp) {
  21183. var extent = this._extent;
  21184. var scale = this.scale;
  21185. if (this.onBand && scale.type === 'ordinal') {
  21186. extent = extent.slice();
  21187. fixExtentWithBands(extent, scale.count());
  21188. }
  21189. var t = linearMap$1(coord, extent, normalizedExtent, clamp);
  21190. return this.scale.scale(t);
  21191. },
  21192. /**
  21193. * Convert pixel point to data in axis
  21194. * @param {Array.<number>} point
  21195. * @param {boolean} clamp
  21196. * @return {number} data
  21197. */
  21198. pointToData: function (point, clamp) {// Should be implemented in derived class if necessary.
  21199. },
  21200. /**
  21201. * @return {Array.<number>}
  21202. */
  21203. getTicksCoords: function (alignWithLabel) {
  21204. if (this.onBand && !alignWithLabel) {
  21205. var bands = this.getBands();
  21206. var coords = [];
  21207. for (var i = 0; i < bands.length; i++) {
  21208. coords.push(bands[i][0]);
  21209. }
  21210. if (bands[i - 1]) {
  21211. coords.push(bands[i - 1][1]);
  21212. }
  21213. return coords;
  21214. } else {
  21215. return map(this.scale.getTicks(), this.dataToCoord, this);
  21216. }
  21217. },
  21218. /**
  21219. * Coords of labels are on the ticks or on the middle of bands
  21220. * @return {Array.<number>}
  21221. */
  21222. getLabelsCoords: function () {
  21223. return map(this.scale.getTicks(), this.dataToCoord, this);
  21224. },
  21225. /**
  21226. * Get bands.
  21227. *
  21228. * If axis has labels [1, 2, 3, 4]. Bands on the axis are
  21229. * |---1---|---2---|---3---|---4---|.
  21230. *
  21231. * @return {Array}
  21232. */
  21233. // FIXME Situation when labels is on ticks
  21234. getBands: function () {
  21235. var extent = this.getExtent();
  21236. var bands = [];
  21237. var len = this.scale.count();
  21238. var start = extent[0];
  21239. var end = extent[1];
  21240. var span = end - start;
  21241. for (var i = 0; i < len; i++) {
  21242. bands.push([span * i / len + start, span * (i + 1) / len + start]);
  21243. }
  21244. return bands;
  21245. },
  21246. /**
  21247. * Get width of band
  21248. * @return {number}
  21249. */
  21250. getBandWidth: function () {
  21251. var axisExtent = this._extent;
  21252. var dataExtent = this.scale.getExtent();
  21253. var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); // Fix #2728, avoid NaN when only one data.
  21254. len === 0 && (len = 1);
  21255. var size = Math.abs(axisExtent[1] - axisExtent[0]);
  21256. return Math.abs(size) / len;
  21257. },
  21258. /**
  21259. * @abstract
  21260. * @return {boolean} Is horizontal
  21261. */
  21262. isHorizontal: null,
  21263. /**
  21264. * @abstract
  21265. * @return {number} Get axis rotate, by degree.
  21266. */
  21267. getRotate: null,
  21268. /**
  21269. * Get interval of the axis label.
  21270. * To get precise result, at least one of `getRotate` and `isHorizontal`
  21271. * should be implemented.
  21272. * @return {number}
  21273. */
  21274. getLabelInterval: function () {
  21275. var labelInterval = this._labelInterval;
  21276. if (!labelInterval) {
  21277. var axisModel = this.model;
  21278. var labelModel = axisModel.getModel('axisLabel');
  21279. labelInterval = labelModel.get('interval');
  21280. if (this.type === 'category' && (labelInterval == null || labelInterval === 'auto')) {
  21281. labelInterval = getAxisLabelInterval(map(this.scale.getTicks(), this.dataToCoord, this), axisModel.getFormattedLabels(), labelModel.getFont(), this.getRotate ? this.getRotate() : this.isHorizontal && !this.isHorizontal() ? 90 : 0, labelModel.get('rotate'));
  21282. }
  21283. this._labelInterval = labelInterval;
  21284. }
  21285. return labelInterval;
  21286. }
  21287. };
  21288. /**
  21289. * Do not mount those modules on 'src/echarts' for better tree shaking.
  21290. */
  21291. var ecUtil = {};
  21292. each$1(['map', 'each', 'filter', 'indexOf', 'inherits', 'reduce', 'filter', 'bind', 'curry', 'isArray', 'isString', 'isObject', 'isFunction', 'extend', 'defaults', 'clone', 'merge'], function (name) {
  21293. ecUtil[name] = zrUtil[name];
  21294. });
  21295. SeriesModel.extend({
  21296. type: 'series.line',
  21297. dependencies: ['grid', 'polar'],
  21298. getInitialData: function (option, ecModel) {
  21299. if (true) {
  21300. var coordSys = option.coordinateSystem;
  21301. if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
  21302. throw new Error('Line not support coordinateSystem besides cartesian and polar');
  21303. }
  21304. }
  21305. return createListFromArray(option.data, this, ecModel);
  21306. },
  21307. defaultOption: {
  21308. zlevel: 0,
  21309. // 一级层叠
  21310. z: 2,
  21311. // 二级层叠
  21312. coordinateSystem: 'cartesian2d',
  21313. legendHoverLink: true,
  21314. hoverAnimation: true,
  21315. // stack: null
  21316. // xAxisIndex: 0,
  21317. // yAxisIndex: 0,
  21318. // polarIndex: 0,
  21319. // If clip the overflow value
  21320. clipOverflow: true,
  21321. // cursor: null,
  21322. label: {
  21323. normal: {
  21324. position: 'top'
  21325. }
  21326. },
  21327. // itemStyle: {
  21328. // normal: {},
  21329. // emphasis: {}
  21330. // },
  21331. lineStyle: {
  21332. normal: {
  21333. width: 2,
  21334. type: 'solid'
  21335. }
  21336. },
  21337. // areaStyle: {},
  21338. // false, 'start', 'end', 'middle'
  21339. step: false,
  21340. // Disabled if step is true
  21341. smooth: false,
  21342. smoothMonotone: null,
  21343. // 拐点图形类型
  21344. symbol: 'emptyCircle',
  21345. // 拐点图形大小
  21346. symbolSize: 4,
  21347. // 拐点图形旋转控制
  21348. symbolRotate: null,
  21349. // 是否显示 symbol, 只有在 tooltip hover 的时候显示
  21350. showSymbol: true,
  21351. // 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略)
  21352. showAllSymbol: false,
  21353. // 是否连接断点
  21354. connectNulls: false,
  21355. // 数据过滤,'average', 'max', 'min', 'sum'
  21356. sampling: 'none',
  21357. animationEasing: 'linear',
  21358. // Disable progressive
  21359. progressive: 0,
  21360. hoverLayerThreshold: Infinity
  21361. }
  21362. });
  21363. /**
  21364. * @module echarts/chart/helper/Symbol
  21365. */
  21366. function findLabelValueDim(data) {
  21367. var valueDim;
  21368. var labelDims = otherDimToDataDim(data, 'label');
  21369. if (labelDims.length) {
  21370. valueDim = labelDims[0];
  21371. } else {
  21372. // Get last value dim
  21373. var dimensions = data.dimensions.slice();
  21374. var dataType;
  21375. while (dimensions.length && (valueDim = dimensions.pop(), dataType = data.getDimensionInfo(valueDim).type, dataType === 'ordinal' || dataType === 'time')) {} // jshint ignore:line
  21376. }
  21377. return valueDim;
  21378. }
  21379. /**
  21380. * @module echarts/chart/helper/Symbol
  21381. */
  21382. function getSymbolSize(data, idx) {
  21383. var symbolSize = data.getItemVisual(idx, 'symbolSize');
  21384. return symbolSize instanceof Array ? symbolSize.slice() : [+symbolSize, +symbolSize];
  21385. }
  21386. function getScale(symbolSize) {
  21387. return [symbolSize[0] / 2, symbolSize[1] / 2];
  21388. }
  21389. /**
  21390. * @constructor
  21391. * @alias {module:echarts/chart/helper/Symbol}
  21392. * @param {module:echarts/data/List} data
  21393. * @param {number} idx
  21394. * @extends {module:zrender/graphic/Group}
  21395. */
  21396. function SymbolClz$1(data, idx, seriesScope) {
  21397. Group.call(this);
  21398. this.updateData(data, idx, seriesScope);
  21399. }
  21400. var symbolProto = SymbolClz$1.prototype;
  21401. function driftSymbol(dx, dy) {
  21402. this.parent.drift(dx, dy);
  21403. }
  21404. symbolProto._createSymbol = function (symbolType, data, idx, symbolSize) {
  21405. // Remove paths created before
  21406. this.removeAll();
  21407. var color = data.getItemVisual(idx, 'color'); // var symbolPath = createSymbol(
  21408. // symbolType, -0.5, -0.5, 1, 1, color
  21409. // );
  21410. // If width/height are set too small (e.g., set to 1) on ios10
  21411. // and macOS Sierra, a circle stroke become a rect, no matter what
  21412. // the scale is set. So we set width/height as 2. See #4150.
  21413. var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, color);
  21414. symbolPath.attr({
  21415. z2: 100,
  21416. culling: true,
  21417. scale: getScale(symbolSize)
  21418. }); // Rewrite drift method
  21419. symbolPath.drift = driftSymbol;
  21420. this._symbolType = symbolType;
  21421. this.add(symbolPath);
  21422. };
  21423. /**
  21424. * Stop animation
  21425. * @param {boolean} toLastFrame
  21426. */
  21427. symbolProto.stopSymbolAnimation = function (toLastFrame) {
  21428. this.childAt(0).stopAnimation(toLastFrame);
  21429. };
  21430. /**
  21431. * FIXME:
  21432. * Caution: This method breaks the encapsulation of this module,
  21433. * but it indeed brings convenience. So do not use the method
  21434. * unless you detailedly know all the implements of `Symbol`,
  21435. * especially animation.
  21436. *
  21437. * Get symbol path element.
  21438. */
  21439. symbolProto.getSymbolPath = function () {
  21440. return this.childAt(0);
  21441. };
  21442. /**
  21443. * Get scale(aka, current symbol size).
  21444. * Including the change caused by animation
  21445. */
  21446. symbolProto.getScale = function () {
  21447. return this.childAt(0).scale;
  21448. };
  21449. /**
  21450. * Highlight symbol
  21451. */
  21452. symbolProto.highlight = function () {
  21453. this.childAt(0).trigger('emphasis');
  21454. };
  21455. /**
  21456. * Downplay symbol
  21457. */
  21458. symbolProto.downplay = function () {
  21459. this.childAt(0).trigger('normal');
  21460. };
  21461. /**
  21462. * @param {number} zlevel
  21463. * @param {number} z
  21464. */
  21465. symbolProto.setZ = function (zlevel, z) {
  21466. var symbolPath = this.childAt(0);
  21467. symbolPath.zlevel = zlevel;
  21468. symbolPath.z = z;
  21469. };
  21470. symbolProto.setDraggable = function (draggable) {
  21471. var symbolPath = this.childAt(0);
  21472. symbolPath.draggable = draggable;
  21473. symbolPath.cursor = draggable ? 'move' : 'pointer';
  21474. };
  21475. /**
  21476. * Update symbol properties
  21477. * @param {module:echarts/data/List} data
  21478. * @param {number} idx
  21479. * @param {Object} [seriesScope]
  21480. * @param {Object} [seriesScope.itemStyle]
  21481. * @param {Object} [seriesScope.hoverItemStyle]
  21482. * @param {Object} [seriesScope.symbolRotate]
  21483. * @param {Object} [seriesScope.symbolOffset]
  21484. * @param {module:echarts/model/Model} [seriesScope.labelModel]
  21485. * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel]
  21486. * @param {boolean} [seriesScope.hoverAnimation]
  21487. * @param {Object} [seriesScope.cursorStyle]
  21488. * @param {module:echarts/model/Model} [seriesScope.itemModel]
  21489. * @param {string} [seriesScope.symbolInnerColor]
  21490. * @param {Object} [seriesScope.fadeIn=false]
  21491. */
  21492. symbolProto.updateData = function (data, idx, seriesScope) {
  21493. this.silent = false;
  21494. var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
  21495. var seriesModel = data.hostModel;
  21496. var symbolSize = getSymbolSize(data, idx);
  21497. var isInit = symbolType !== this._symbolType;
  21498. if (isInit) {
  21499. this._createSymbol(symbolType, data, idx, symbolSize);
  21500. } else {
  21501. var symbolPath = this.childAt(0);
  21502. symbolPath.silent = false;
  21503. updateProps(symbolPath, {
  21504. scale: getScale(symbolSize)
  21505. }, seriesModel, idx);
  21506. }
  21507. this._updateCommon(data, idx, symbolSize, seriesScope);
  21508. if (isInit) {
  21509. var symbolPath = this.childAt(0);
  21510. var fadeIn = seriesScope && seriesScope.fadeIn;
  21511. var target = {
  21512. scale: symbolPath.scale.slice()
  21513. };
  21514. fadeIn && (target.style = {
  21515. opacity: symbolPath.style.opacity
  21516. });
  21517. symbolPath.scale = [0, 0];
  21518. fadeIn && (symbolPath.style.opacity = 0);
  21519. initProps(symbolPath, target, seriesModel, idx);
  21520. }
  21521. this._seriesModel = seriesModel;
  21522. }; // Update common properties
  21523. var normalStyleAccessPath = ['itemStyle', 'normal'];
  21524. var emphasisStyleAccessPath = ['itemStyle', 'emphasis'];
  21525. var normalLabelAccessPath = ['label', 'normal'];
  21526. var emphasisLabelAccessPath = ['label', 'emphasis'];
  21527. /**
  21528. * @param {module:echarts/data/List} data
  21529. * @param {number} idx
  21530. * @param {Array.<number>} symbolSize
  21531. * @param {Object} [seriesScope]
  21532. */
  21533. symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) {
  21534. var symbolPath = this.childAt(0);
  21535. var seriesModel = data.hostModel;
  21536. var color = data.getItemVisual(idx, 'color'); // Reset style
  21537. if (symbolPath.type !== 'image') {
  21538. symbolPath.useStyle({
  21539. strokeNoScale: true
  21540. });
  21541. }
  21542. var itemStyle = seriesScope && seriesScope.itemStyle;
  21543. var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle;
  21544. var symbolRotate = seriesScope && seriesScope.symbolRotate;
  21545. var symbolOffset = seriesScope && seriesScope.symbolOffset;
  21546. var labelModel = seriesScope && seriesScope.labelModel;
  21547. var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
  21548. var hoverAnimation = seriesScope && seriesScope.hoverAnimation;
  21549. var cursorStyle = seriesScope && seriesScope.cursorStyle;
  21550. if (!seriesScope || data.hasItemOption) {
  21551. var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx); // Color must be excluded.
  21552. // Because symbol provide setColor individually to set fill and stroke
  21553. itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']);
  21554. hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
  21555. symbolRotate = itemModel.getShallow('symbolRotate');
  21556. symbolOffset = itemModel.getShallow('symbolOffset');
  21557. labelModel = itemModel.getModel(normalLabelAccessPath);
  21558. hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);
  21559. hoverAnimation = itemModel.getShallow('hoverAnimation');
  21560. cursorStyle = itemModel.getShallow('cursor');
  21561. } else {
  21562. hoverItemStyle = extend({}, hoverItemStyle);
  21563. }
  21564. var elStyle = symbolPath.style;
  21565. symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
  21566. if (symbolOffset) {
  21567. symbolPath.attr('position', [parsePercent$1(symbolOffset[0], symbolSize[0]), parsePercent$1(symbolOffset[1], symbolSize[1])]);
  21568. }
  21569. cursorStyle && symbolPath.attr('cursor', cursorStyle); // PENDING setColor before setStyle!!!
  21570. symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor);
  21571. symbolPath.setStyle(itemStyle);
  21572. var opacity = data.getItemVisual(idx, 'opacity');
  21573. if (opacity != null) {
  21574. elStyle.opacity = opacity;
  21575. }
  21576. var useNameLabel = seriesScope && seriesScope.useNameLabel;
  21577. var valueDim = !useNameLabel && findLabelValueDim(data);
  21578. if (useNameLabel || valueDim != null) {
  21579. setLabelStyle(elStyle, hoverItemStyle, labelModel, hoverLabelModel, {
  21580. labelFetcher: seriesModel,
  21581. labelDataIndex: idx,
  21582. defaultText: useNameLabel ? data.getName(idx) : data.get(valueDim, idx),
  21583. isRectText: true,
  21584. autoColor: color
  21585. });
  21586. }
  21587. symbolPath.off('mouseover').off('mouseout').off('emphasis').off('normal');
  21588. symbolPath.hoverStyle = hoverItemStyle; // FIXME
  21589. // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead.
  21590. setHoverStyle(symbolPath);
  21591. var scale = getScale(symbolSize);
  21592. if (hoverAnimation && seriesModel.isAnimationEnabled()) {
  21593. var onEmphasis = function () {
  21594. var ratio = scale[1] / scale[0];
  21595. this.animateTo({
  21596. scale: [Math.max(scale[0] * 1.1, scale[0] + 3), Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)]
  21597. }, 400, 'elasticOut');
  21598. };
  21599. var onNormal = function () {
  21600. this.animateTo({
  21601. scale: scale
  21602. }, 400, 'elasticOut');
  21603. };
  21604. symbolPath.on('mouseover', onEmphasis).on('mouseout', onNormal).on('emphasis', onEmphasis).on('normal', onNormal);
  21605. }
  21606. };
  21607. /**
  21608. * @param {Function} cb
  21609. * @param {Object} [opt]
  21610. * @param {Object} [opt.keepLabel=true]
  21611. */
  21612. symbolProto.fadeOut = function (cb, opt) {
  21613. var symbolPath = this.childAt(0); // Avoid mistaken hover when fading out
  21614. this.silent = symbolPath.silent = true; // Not show text when animating
  21615. !(opt && opt.keepLabel) && (symbolPath.style.text = null);
  21616. updateProps(symbolPath, {
  21617. style: {
  21618. opacity: 0
  21619. },
  21620. scale: [0, 0]
  21621. }, this._seriesModel, this.dataIndex, cb);
  21622. };
  21623. inherits(SymbolClz$1, Group);
  21624. /**
  21625. * @module echarts/chart/helper/SymbolDraw
  21626. */
  21627. /**
  21628. * @constructor
  21629. * @alias module:echarts/chart/helper/SymbolDraw
  21630. * @param {module:zrender/graphic/Group} [symbolCtor]
  21631. */
  21632. function SymbolDraw(symbolCtor) {
  21633. this.group = new Group();
  21634. this._symbolCtor = symbolCtor || SymbolClz$1;
  21635. }
  21636. var symbolDrawProto = SymbolDraw.prototype;
  21637. function symbolNeedsDraw(data, idx, isIgnore) {
  21638. var point = data.getItemLayout(idx); // Is an object
  21639. // if (point && point.hasOwnProperty('point')) {
  21640. // point = point.point;
  21641. // }
  21642. return point && !isNaN(point[0]) && !isNaN(point[1]) && !(isIgnore && isIgnore(idx)) && data.getItemVisual(idx, 'symbol') !== 'none';
  21643. }
  21644. /**
  21645. * Update symbols draw by new data
  21646. * @param {module:echarts/data/List} data
  21647. * @param {Array.<boolean>} [isIgnore]
  21648. */
  21649. symbolDrawProto.updateData = function (data, isIgnore) {
  21650. var group = this.group;
  21651. var seriesModel = data.hostModel;
  21652. var oldData = this._data;
  21653. var SymbolCtor = this._symbolCtor;
  21654. var seriesScope = {
  21655. itemStyle: seriesModel.getModel('itemStyle.normal').getItemStyle(['color']),
  21656. hoverItemStyle: seriesModel.getModel('itemStyle.emphasis').getItemStyle(),
  21657. symbolRotate: seriesModel.get('symbolRotate'),
  21658. symbolOffset: seriesModel.get('symbolOffset'),
  21659. hoverAnimation: seriesModel.get('hoverAnimation'),
  21660. labelModel: seriesModel.getModel('label.normal'),
  21661. hoverLabelModel: seriesModel.getModel('label.emphasis'),
  21662. cursorStyle: seriesModel.get('cursor')
  21663. };
  21664. data.diff(oldData).add(function (newIdx) {
  21665. var point = data.getItemLayout(newIdx);
  21666. if (symbolNeedsDraw(data, newIdx, isIgnore)) {
  21667. var symbolEl = new SymbolCtor(data, newIdx, seriesScope);
  21668. symbolEl.attr('position', point);
  21669. data.setItemGraphicEl(newIdx, symbolEl);
  21670. group.add(symbolEl);
  21671. }
  21672. }).update(function (newIdx, oldIdx) {
  21673. var symbolEl = oldData.getItemGraphicEl(oldIdx);
  21674. var point = data.getItemLayout(newIdx);
  21675. if (!symbolNeedsDraw(data, newIdx, isIgnore)) {
  21676. group.remove(symbolEl);
  21677. return;
  21678. }
  21679. if (!symbolEl) {
  21680. symbolEl = new SymbolCtor(data, newIdx);
  21681. symbolEl.attr('position', point);
  21682. } else {
  21683. symbolEl.updateData(data, newIdx, seriesScope);
  21684. updateProps(symbolEl, {
  21685. position: point
  21686. }, seriesModel);
  21687. } // Add back
  21688. group.add(symbolEl);
  21689. data.setItemGraphicEl(newIdx, symbolEl);
  21690. }).remove(function (oldIdx) {
  21691. var el = oldData.getItemGraphicEl(oldIdx);
  21692. el && el.fadeOut(function () {
  21693. group.remove(el);
  21694. });
  21695. }).execute();
  21696. this._data = data;
  21697. };
  21698. symbolDrawProto.updateLayout = function () {
  21699. var data = this._data;
  21700. if (data) {
  21701. // Not use animation
  21702. data.eachItemGraphicEl(function (el, idx) {
  21703. var point = data.getItemLayout(idx);
  21704. el.attr('position', point);
  21705. });
  21706. }
  21707. };
  21708. symbolDrawProto.remove = function (enableAnimation) {
  21709. var group = this.group;
  21710. var data = this._data;
  21711. if (data) {
  21712. if (enableAnimation) {
  21713. data.eachItemGraphicEl(function (el) {
  21714. el.fadeOut(function () {
  21715. group.remove(el);
  21716. });
  21717. });
  21718. } else {
  21719. group.removeAll();
  21720. }
  21721. }
  21722. }; // var arrayDiff = require('zrender/src/core/arrayDiff');
  21723. // 'zrender/src/core/arrayDiff' has been used before, but it did
  21724. // not do well in performance when roam with fixed dataZoom window.
  21725. function sign$1(val) {
  21726. return val >= 0 ? 1 : -1;
  21727. }
  21728. function getStackedOnPoint(coordSys, data, idx) {
  21729. var baseAxis = coordSys.getBaseAxis();
  21730. var valueAxis = coordSys.getOtherAxis(baseAxis);
  21731. var valueStart = baseAxis.onZero ? 0 : valueAxis.scale.getExtent()[0];
  21732. var valueDim = valueAxis.dim;
  21733. var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
  21734. var stackedOnSameSign;
  21735. var stackedOn = data.stackedOn;
  21736. var val = data.get(valueDim, idx); // Find first stacked value with same sign
  21737. while (stackedOn && sign$1(stackedOn.get(valueDim, idx)) === sign$1(val)) {
  21738. stackedOnSameSign = stackedOn;
  21739. break;
  21740. }
  21741. var stackedData = [];
  21742. stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
  21743. stackedData[1 - baseDataOffset] = stackedOnSameSign ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
  21744. return coordSys.dataToPoint(stackedData);
  21745. } // function convertToIntId(newIdList, oldIdList) {
  21746. // // Generate int id instead of string id.
  21747. // // Compare string maybe slow in score function of arrDiff
  21748. // // Assume id in idList are all unique
  21749. // var idIndicesMap = {};
  21750. // var idx = 0;
  21751. // for (var i = 0; i < newIdList.length; i++) {
  21752. // idIndicesMap[newIdList[i]] = idx;
  21753. // newIdList[i] = idx++;
  21754. // }
  21755. // for (var i = 0; i < oldIdList.length; i++) {
  21756. // var oldId = oldIdList[i];
  21757. // // Same with newIdList
  21758. // if (idIndicesMap[oldId]) {
  21759. // oldIdList[i] = idIndicesMap[oldId];
  21760. // }
  21761. // else {
  21762. // oldIdList[i] = idx++;
  21763. // }
  21764. // }
  21765. // }
  21766. function diffData(oldData, newData) {
  21767. var diffResult = [];
  21768. newData.diff(oldData).add(function (idx) {
  21769. diffResult.push({
  21770. cmd: '+',
  21771. idx: idx
  21772. });
  21773. }).update(function (newIdx, oldIdx) {
  21774. diffResult.push({
  21775. cmd: '=',
  21776. idx: oldIdx,
  21777. idx1: newIdx
  21778. });
  21779. }).remove(function (idx) {
  21780. diffResult.push({
  21781. cmd: '-',
  21782. idx: idx
  21783. });
  21784. }).execute();
  21785. return diffResult;
  21786. }
  21787. var lineAnimationDiff = function (oldData, newData, oldStackedOnPoints, newStackedOnPoints, oldCoordSys, newCoordSys) {
  21788. var diff = diffData(oldData, newData); // var newIdList = newData.mapArray(newData.getId);
  21789. // var oldIdList = oldData.mapArray(oldData.getId);
  21790. // convertToIntId(newIdList, oldIdList);
  21791. // // FIXME One data ?
  21792. // diff = arrayDiff(oldIdList, newIdList);
  21793. var currPoints = [];
  21794. var nextPoints = []; // Points for stacking base line
  21795. var currStackedPoints = [];
  21796. var nextStackedPoints = [];
  21797. var status = [];
  21798. var sortedIndices = [];
  21799. var rawIndices = [];
  21800. var dims = newCoordSys.dimensions;
  21801. for (var i = 0; i < diff.length; i++) {
  21802. var diffItem = diff[i];
  21803. var pointAdded = true; // FIXME, animation is not so perfect when dataZoom window moves fast
  21804. // Which is in case remvoing or add more than one data in the tail or head
  21805. switch (diffItem.cmd) {
  21806. case '=':
  21807. var currentPt = oldData.getItemLayout(diffItem.idx);
  21808. var nextPt = newData.getItemLayout(diffItem.idx1); // If previous data is NaN, use next point directly
  21809. if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {
  21810. currentPt = nextPt.slice();
  21811. }
  21812. currPoints.push(currentPt);
  21813. nextPoints.push(nextPt);
  21814. currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);
  21815. nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);
  21816. rawIndices.push(newData.getRawIndex(diffItem.idx1));
  21817. break;
  21818. case '+':
  21819. var idx = diffItem.idx;
  21820. currPoints.push(oldCoordSys.dataToPoint([newData.get(dims[0], idx, true), newData.get(dims[1], idx, true)]));
  21821. nextPoints.push(newData.getItemLayout(idx).slice());
  21822. currStackedPoints.push(getStackedOnPoint(oldCoordSys, newData, idx));
  21823. nextStackedPoints.push(newStackedOnPoints[idx]);
  21824. rawIndices.push(newData.getRawIndex(idx));
  21825. break;
  21826. case '-':
  21827. var idx = diffItem.idx;
  21828. var rawIndex = oldData.getRawIndex(idx); // Data is replaced. In the case of dynamic data queue
  21829. // FIXME FIXME FIXME
  21830. if (rawIndex !== idx) {
  21831. currPoints.push(oldData.getItemLayout(idx));
  21832. nextPoints.push(newCoordSys.dataToPoint([oldData.get(dims[0], idx, true), oldData.get(dims[1], idx, true)]));
  21833. currStackedPoints.push(oldStackedOnPoints[idx]);
  21834. nextStackedPoints.push(getStackedOnPoint(newCoordSys, oldData, idx));
  21835. rawIndices.push(rawIndex);
  21836. } else {
  21837. pointAdded = false;
  21838. }
  21839. } // Original indices
  21840. if (pointAdded) {
  21841. status.push(diffItem);
  21842. sortedIndices.push(sortedIndices.length);
  21843. }
  21844. } // Diff result may be crossed if all items are changed
  21845. // Sort by data index
  21846. sortedIndices.sort(function (a, b) {
  21847. return rawIndices[a] - rawIndices[b];
  21848. });
  21849. var sortedCurrPoints = [];
  21850. var sortedNextPoints = [];
  21851. var sortedCurrStackedPoints = [];
  21852. var sortedNextStackedPoints = [];
  21853. var sortedStatus = [];
  21854. for (var i = 0; i < sortedIndices.length; i++) {
  21855. var idx = sortedIndices[i];
  21856. sortedCurrPoints[i] = currPoints[idx];
  21857. sortedNextPoints[i] = nextPoints[idx];
  21858. sortedCurrStackedPoints[i] = currStackedPoints[idx];
  21859. sortedNextStackedPoints[i] = nextStackedPoints[idx];
  21860. sortedStatus[i] = status[idx];
  21861. }
  21862. return {
  21863. current: sortedCurrPoints,
  21864. next: sortedNextPoints,
  21865. stackedOnCurrent: sortedCurrStackedPoints,
  21866. stackedOnNext: sortedNextStackedPoints,
  21867. status: sortedStatus
  21868. };
  21869. }; // Poly path support NaN point
  21870. var vec2Min = min;
  21871. var vec2Max = max;
  21872. var scaleAndAdd$1 = scaleAndAdd;
  21873. var v2Copy = copy; // Temporary variable
  21874. var v = [];
  21875. var cp0 = [];
  21876. var cp1 = [];
  21877. function isPointNull(p) {
  21878. return isNaN(p[0]) || isNaN(p[1]);
  21879. }
  21880. function drawSegment(ctx, points, start, segLen, allLen, dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls) {
  21881. var prevIdx = 0;
  21882. var idx = start;
  21883. for (var k = 0; k < segLen; k++) {
  21884. var p = points[idx];
  21885. if (idx >= allLen || idx < 0) {
  21886. break;
  21887. }
  21888. if (isPointNull(p)) {
  21889. if (connectNulls) {
  21890. idx += dir;
  21891. continue;
  21892. }
  21893. break;
  21894. }
  21895. if (idx === start) {
  21896. ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
  21897. v2Copy(cp0, p);
  21898. } else {
  21899. if (smooth > 0) {
  21900. var nextIdx = idx + dir;
  21901. var nextP = points[nextIdx];
  21902. if (connectNulls) {
  21903. // Find next point not null
  21904. while (nextP && isPointNull(points[nextIdx])) {
  21905. nextIdx += dir;
  21906. nextP = points[nextIdx];
  21907. }
  21908. }
  21909. var ratioNextSeg = 0.5;
  21910. var prevP = points[prevIdx];
  21911. var nextP = points[nextIdx]; // Last point
  21912. if (!nextP || isPointNull(nextP)) {
  21913. v2Copy(cp1, p);
  21914. } else {
  21915. // If next data is null in not connect case
  21916. if (isPointNull(nextP) && !connectNulls) {
  21917. nextP = p;
  21918. }
  21919. sub(v, nextP, prevP);
  21920. var lenPrevSeg;
  21921. var lenNextSeg;
  21922. if (smoothMonotone === 'x' || smoothMonotone === 'y') {
  21923. var dim = smoothMonotone === 'x' ? 0 : 1;
  21924. lenPrevSeg = Math.abs(p[dim] - prevP[dim]);
  21925. lenNextSeg = Math.abs(p[dim] - nextP[dim]);
  21926. } else {
  21927. lenPrevSeg = dist(p, prevP);
  21928. lenNextSeg = dist(p, nextP);
  21929. } // Use ratio of seg length
  21930. ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
  21931. scaleAndAdd$1(cp1, p, v, -smooth * (1 - ratioNextSeg));
  21932. } // Smooth constraint
  21933. vec2Min(cp0, cp0, smoothMax);
  21934. vec2Max(cp0, cp0, smoothMin);
  21935. vec2Min(cp1, cp1, smoothMax);
  21936. vec2Max(cp1, cp1, smoothMin);
  21937. ctx.bezierCurveTo(cp0[0], cp0[1], cp1[0], cp1[1], p[0], p[1]); // cp0 of next segment
  21938. scaleAndAdd$1(cp0, p, v, smooth * ratioNextSeg);
  21939. } else {
  21940. ctx.lineTo(p[0], p[1]);
  21941. }
  21942. }
  21943. prevIdx = idx;
  21944. idx += dir;
  21945. }
  21946. return k;
  21947. }
  21948. function getBoundingBox(points, smoothConstraint) {
  21949. var ptMin = [Infinity, Infinity];
  21950. var ptMax = [-Infinity, -Infinity];
  21951. if (smoothConstraint) {
  21952. for (var i = 0; i < points.length; i++) {
  21953. var pt = points[i];
  21954. if (pt[0] < ptMin[0]) {
  21955. ptMin[0] = pt[0];
  21956. }
  21957. if (pt[1] < ptMin[1]) {
  21958. ptMin[1] = pt[1];
  21959. }
  21960. if (pt[0] > ptMax[0]) {
  21961. ptMax[0] = pt[0];
  21962. }
  21963. if (pt[1] > ptMax[1]) {
  21964. ptMax[1] = pt[1];
  21965. }
  21966. }
  21967. }
  21968. return {
  21969. min: smoothConstraint ? ptMin : ptMax,
  21970. max: smoothConstraint ? ptMax : ptMin
  21971. };
  21972. }
  21973. var Polyline$1 = Path.extend({
  21974. type: 'ec-polyline',
  21975. shape: {
  21976. points: [],
  21977. smooth: 0,
  21978. smoothConstraint: true,
  21979. smoothMonotone: null,
  21980. connectNulls: false
  21981. },
  21982. style: {
  21983. fill: null,
  21984. stroke: '#000'
  21985. },
  21986. brush: fixClipWithShadow(Path.prototype.brush),
  21987. buildPath: function (ctx, shape) {
  21988. var points = shape.points;
  21989. var i = 0;
  21990. var len$$1 = points.length;
  21991. var result = getBoundingBox(points, shape.smoothConstraint);
  21992. if (shape.connectNulls) {
  21993. // Must remove first and last null values avoid draw error in polygon
  21994. for (; len$$1 > 0; len$$1--) {
  21995. if (!isPointNull(points[len$$1 - 1])) {
  21996. break;
  21997. }
  21998. }
  21999. for (; i < len$$1; i++) {
  22000. if (!isPointNull(points[i])) {
  22001. break;
  22002. }
  22003. }
  22004. }
  22005. while (i < len$$1) {
  22006. i += drawSegment(ctx, points, i, len$$1, len$$1, 1, result.min, result.max, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1;
  22007. }
  22008. }
  22009. });
  22010. var Polygon$1 = Path.extend({
  22011. type: 'ec-polygon',
  22012. shape: {
  22013. points: [],
  22014. // Offset between stacked base points and points
  22015. stackedOnPoints: [],
  22016. smooth: 0,
  22017. stackedOnSmooth: 0,
  22018. smoothConstraint: true,
  22019. smoothMonotone: null,
  22020. connectNulls: false
  22021. },
  22022. brush: fixClipWithShadow(Path.prototype.brush),
  22023. buildPath: function (ctx, shape) {
  22024. var points = shape.points;
  22025. var stackedOnPoints = shape.stackedOnPoints;
  22026. var i = 0;
  22027. var len$$1 = points.length;
  22028. var smoothMonotone = shape.smoothMonotone;
  22029. var bbox = getBoundingBox(points, shape.smoothConstraint);
  22030. var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint);
  22031. if (shape.connectNulls) {
  22032. // Must remove first and last null values avoid draw error in polygon
  22033. for (; len$$1 > 0; len$$1--) {
  22034. if (!isPointNull(points[len$$1 - 1])) {
  22035. break;
  22036. }
  22037. }
  22038. for (; i < len$$1; i++) {
  22039. if (!isPointNull(points[i])) {
  22040. break;
  22041. }
  22042. }
  22043. }
  22044. while (i < len$$1) {
  22045. var k = drawSegment(ctx, points, i, len$$1, len$$1, 1, bbox.min, bbox.max, shape.smooth, smoothMonotone, shape.connectNulls);
  22046. drawSegment(ctx, stackedOnPoints, i + k - 1, k, len$$1, -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls);
  22047. i += k + 1;
  22048. ctx.closePath();
  22049. }
  22050. }
  22051. }); // FIXME step not support polar
  22052. function isPointsSame(points1, points2) {
  22053. if (points1.length !== points2.length) {
  22054. return;
  22055. }
  22056. for (var i = 0; i < points1.length; i++) {
  22057. var p1 = points1[i];
  22058. var p2 = points2[i];
  22059. if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
  22060. return;
  22061. }
  22062. }
  22063. return true;
  22064. }
  22065. function getSmooth(smooth) {
  22066. return typeof smooth === 'number' ? smooth : smooth ? 0.3 : 0;
  22067. }
  22068. function getAxisExtentWithGap(axis) {
  22069. var extent = axis.getGlobalExtent();
  22070. if (axis.onBand) {
  22071. // Remove extra 1px to avoid line miter in clipped edge
  22072. var halfBandWidth = axis.getBandWidth() / 2 - 1;
  22073. var dir = extent[1] > extent[0] ? 1 : -1;
  22074. extent[0] += dir * halfBandWidth;
  22075. extent[1] -= dir * halfBandWidth;
  22076. }
  22077. return extent;
  22078. }
  22079. function sign(val) {
  22080. return val >= 0 ? 1 : -1;
  22081. }
  22082. /**
  22083. * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys
  22084. * @param {module:echarts/data/List} data
  22085. * @param {Array.<Array.<number>>} points
  22086. * @private
  22087. */
  22088. function getStackedOnPoints(coordSys, data) {
  22089. var baseAxis = coordSys.getBaseAxis();
  22090. var valueAxis = coordSys.getOtherAxis(baseAxis);
  22091. var valueStart = 0;
  22092. if (!baseAxis.onZero) {
  22093. var extent = valueAxis.scale.getExtent();
  22094. if (extent[0] > 0) {
  22095. // Both positive
  22096. valueStart = extent[0];
  22097. } else if (extent[1] < 0) {
  22098. // Both negative
  22099. valueStart = extent[1];
  22100. } // If is one positive, and one negative, onZero shall be true
  22101. }
  22102. var valueDim = valueAxis.dim;
  22103. var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
  22104. return data.mapArray([valueDim], function (val, idx) {
  22105. var stackedOnSameSign;
  22106. var stackedOn = data.stackedOn; // Find first stacked value with same sign
  22107. while (stackedOn && sign(stackedOn.get(valueDim, idx)) === sign(val)) {
  22108. stackedOnSameSign = stackedOn;
  22109. break;
  22110. }
  22111. var stackedData = [];
  22112. stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
  22113. stackedData[1 - baseDataOffset] = stackedOnSameSign ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
  22114. return coordSys.dataToPoint(stackedData);
  22115. }, true);
  22116. }
  22117. function createGridClipShape(cartesian, hasAnimation, seriesModel) {
  22118. var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
  22119. var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));
  22120. var isHorizontal = cartesian.getBaseAxis().isHorizontal();
  22121. var x = Math.min(xExtent[0], xExtent[1]);
  22122. var y = Math.min(yExtent[0], yExtent[1]);
  22123. var width = Math.max(xExtent[0], xExtent[1]) - x;
  22124. var height = Math.max(yExtent[0], yExtent[1]) - y;
  22125. var lineWidth = seriesModel.get('lineStyle.normal.width') || 2; // Expand clip shape to avoid clipping when line value exceeds axis
  22126. var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height);
  22127. if (isHorizontal) {
  22128. y -= expandSize;
  22129. height += expandSize * 2;
  22130. } else {
  22131. x -= expandSize;
  22132. width += expandSize * 2;
  22133. }
  22134. var clipPath = new Rect({
  22135. shape: {
  22136. x: x,
  22137. y: y,
  22138. width: width,
  22139. height: height
  22140. }
  22141. });
  22142. if (hasAnimation) {
  22143. clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;
  22144. initProps(clipPath, {
  22145. shape: {
  22146. width: width,
  22147. height: height
  22148. }
  22149. }, seriesModel);
  22150. }
  22151. return clipPath;
  22152. }
  22153. function createPolarClipShape(polar, hasAnimation, seriesModel) {
  22154. var angleAxis = polar.getAngleAxis();
  22155. var radiusAxis = polar.getRadiusAxis();
  22156. var radiusExtent = radiusAxis.getExtent();
  22157. var angleExtent = angleAxis.getExtent();
  22158. var RADIAN = Math.PI / 180;
  22159. var clipPath = new Sector({
  22160. shape: {
  22161. cx: polar.cx,
  22162. cy: polar.cy,
  22163. r0: radiusExtent[0],
  22164. r: radiusExtent[1],
  22165. startAngle: -angleExtent[0] * RADIAN,
  22166. endAngle: -angleExtent[1] * RADIAN,
  22167. clockwise: angleAxis.inverse
  22168. }
  22169. });
  22170. if (hasAnimation) {
  22171. clipPath.shape.endAngle = -angleExtent[0] * RADIAN;
  22172. initProps(clipPath, {
  22173. shape: {
  22174. endAngle: -angleExtent[1] * RADIAN
  22175. }
  22176. }, seriesModel);
  22177. }
  22178. return clipPath;
  22179. }
  22180. function createClipShape(coordSys, hasAnimation, seriesModel) {
  22181. return coordSys.type === 'polar' ? createPolarClipShape(coordSys, hasAnimation, seriesModel) : createGridClipShape(coordSys, hasAnimation, seriesModel);
  22182. }
  22183. function turnPointsIntoStep(points, coordSys, stepTurnAt) {
  22184. var baseAxis = coordSys.getBaseAxis();
  22185. var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;
  22186. var stepPoints = [];
  22187. for (var i = 0; i < points.length - 1; i++) {
  22188. var nextPt = points[i + 1];
  22189. var pt = points[i];
  22190. stepPoints.push(pt);
  22191. var stepPt = [];
  22192. switch (stepTurnAt) {
  22193. case 'end':
  22194. stepPt[baseIndex] = nextPt[baseIndex];
  22195. stepPt[1 - baseIndex] = pt[1 - baseIndex]; // default is start
  22196. stepPoints.push(stepPt);
  22197. break;
  22198. case 'middle':
  22199. // default is start
  22200. var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;
  22201. var stepPt2 = [];
  22202. stepPt[baseIndex] = stepPt2[baseIndex] = middle;
  22203. stepPt[1 - baseIndex] = pt[1 - baseIndex];
  22204. stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];
  22205. stepPoints.push(stepPt);
  22206. stepPoints.push(stepPt2);
  22207. break;
  22208. default:
  22209. stepPt[baseIndex] = pt[baseIndex];
  22210. stepPt[1 - baseIndex] = nextPt[1 - baseIndex]; // default is start
  22211. stepPoints.push(stepPt);
  22212. }
  22213. } // Last points
  22214. points[i] && stepPoints.push(points[i]);
  22215. return stepPoints;
  22216. }
  22217. function getVisualGradient(data, coordSys) {
  22218. var visualMetaList = data.getVisual('visualMeta');
  22219. if (!visualMetaList || !visualMetaList.length || !data.count()) {
  22220. // When data.count() is 0, gradient range can not be calculated.
  22221. return;
  22222. }
  22223. var visualMeta;
  22224. for (var i = visualMetaList.length - 1; i >= 0; i--) {
  22225. // Can only be x or y
  22226. if (visualMetaList[i].dimension < 2) {
  22227. visualMeta = visualMetaList[i];
  22228. break;
  22229. }
  22230. }
  22231. if (!visualMeta || coordSys.type !== 'cartesian2d') {
  22232. if (true) {
  22233. console.warn('Visual map on line style only support x or y dimension.');
  22234. }
  22235. return;
  22236. } // If the area to be rendered is bigger than area defined by LinearGradient,
  22237. // the canvas spec prescribes that the color of the first stop and the last
  22238. // stop should be used. But if two stops are added at offset 0, in effect
  22239. // browsers use the color of the second stop to render area outside
  22240. // LinearGradient. So we can only infinitesimally extend area defined in
  22241. // LinearGradient to render `outerColors`.
  22242. var dimension = visualMeta.dimension;
  22243. var dimName = data.dimensions[dimension];
  22244. var axis = coordSys.getAxis(dimName); // dataToCoor mapping may not be linear, but must be monotonic.
  22245. var colorStops = map(visualMeta.stops, function (stop) {
  22246. return {
  22247. coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
  22248. color: stop.color
  22249. };
  22250. });
  22251. var stopLen = colorStops.length;
  22252. var outerColors = visualMeta.outerColors.slice();
  22253. if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {
  22254. colorStops.reverse();
  22255. outerColors.reverse();
  22256. }
  22257. var tinyExtent = 10; // Arbitrary value: 10px
  22258. var minCoord = colorStops[0].coord - tinyExtent;
  22259. var maxCoord = colorStops[stopLen - 1].coord + tinyExtent;
  22260. var coordSpan = maxCoord - minCoord;
  22261. if (coordSpan < 1e-3) {
  22262. return 'transparent';
  22263. }
  22264. each$1(colorStops, function (stop) {
  22265. stop.offset = (stop.coord - minCoord) / coordSpan;
  22266. });
  22267. colorStops.push({
  22268. offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,
  22269. color: outerColors[1] || 'transparent'
  22270. });
  22271. colorStops.unshift({
  22272. // notice colorStops.length have been changed.
  22273. offset: stopLen ? colorStops[0].offset : 0.5,
  22274. color: outerColors[0] || 'transparent'
  22275. }); // zrUtil.each(colorStops, function (colorStop) {
  22276. // // Make sure each offset has rounded px to avoid not sharp edge
  22277. // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);
  22278. // });
  22279. var gradient = new LinearGradient(0, 0, 0, 0, colorStops, true);
  22280. gradient[dimName] = minCoord;
  22281. gradient[dimName + '2'] = maxCoord;
  22282. return gradient;
  22283. }
  22284. Chart.extend({
  22285. type: 'line',
  22286. init: function () {
  22287. var lineGroup = new Group();
  22288. var symbolDraw = new SymbolDraw();
  22289. this.group.add(symbolDraw.group);
  22290. this._symbolDraw = symbolDraw;
  22291. this._lineGroup = lineGroup;
  22292. },
  22293. render: function (seriesModel, ecModel, api) {
  22294. var coordSys = seriesModel.coordinateSystem;
  22295. var group = this.group;
  22296. var data = seriesModel.getData();
  22297. var lineStyleModel = seriesModel.getModel('lineStyle.normal');
  22298. var areaStyleModel = seriesModel.getModel('areaStyle.normal');
  22299. var points = data.mapArray(data.getItemLayout, true);
  22300. var isCoordSysPolar = coordSys.type === 'polar';
  22301. var prevCoordSys = this._coordSys;
  22302. var symbolDraw = this._symbolDraw;
  22303. var polyline = this._polyline;
  22304. var polygon = this._polygon;
  22305. var lineGroup = this._lineGroup;
  22306. var hasAnimation = seriesModel.get('animation');
  22307. var isAreaChart = !areaStyleModel.isEmpty();
  22308. var stackedOnPoints = getStackedOnPoints(coordSys, data);
  22309. var showSymbol = seriesModel.get('showSymbol');
  22310. var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol') && this._getSymbolIgnoreFunc(data, coordSys); // Remove temporary symbols
  22311. var oldData = this._data;
  22312. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  22313. if (el.__temp) {
  22314. group.remove(el);
  22315. oldData.setItemGraphicEl(idx, null);
  22316. }
  22317. }); // Remove previous created symbols if showSymbol changed to false
  22318. if (!showSymbol) {
  22319. symbolDraw.remove();
  22320. }
  22321. group.add(lineGroup); // FIXME step not support polar
  22322. var step = !isCoordSysPolar && seriesModel.get('step'); // Initialization animation or coordinate system changed
  22323. if (!(polyline && prevCoordSys.type === coordSys.type && step === this._step)) {
  22324. showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
  22325. if (step) {
  22326. // TODO If stacked series is not step
  22327. points = turnPointsIntoStep(points, coordSys, step);
  22328. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  22329. }
  22330. polyline = this._newPolyline(points, coordSys, hasAnimation);
  22331. if (isAreaChart) {
  22332. polygon = this._newPolygon(points, stackedOnPoints, coordSys, hasAnimation);
  22333. }
  22334. lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel));
  22335. } else {
  22336. if (isAreaChart && !polygon) {
  22337. // If areaStyle is added
  22338. polygon = this._newPolygon(points, stackedOnPoints, coordSys, hasAnimation);
  22339. } else if (polygon && !isAreaChart) {
  22340. // If areaStyle is removed
  22341. lineGroup.remove(polygon);
  22342. polygon = this._polygon = null;
  22343. } // Update clipPath
  22344. lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel)); // Always update, or it is wrong in the case turning on legend
  22345. // because points are not changed
  22346. showSymbol && symbolDraw.updateData(data, isSymbolIgnore); // Stop symbol animation and sync with line points
  22347. // FIXME performance?
  22348. data.eachItemGraphicEl(function (el) {
  22349. el.stopAnimation(true);
  22350. }); // In the case data zoom triggerred refreshing frequently
  22351. // Data may not change if line has a category axis. So it should animate nothing
  22352. if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) || !isPointsSame(this._points, points)) {
  22353. if (hasAnimation) {
  22354. this._updateAnimation(data, stackedOnPoints, coordSys, api, step);
  22355. } else {
  22356. // Not do it in update with animation
  22357. if (step) {
  22358. // TODO If stacked series is not step
  22359. points = turnPointsIntoStep(points, coordSys, step);
  22360. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  22361. }
  22362. polyline.setShape({
  22363. points: points
  22364. });
  22365. polygon && polygon.setShape({
  22366. points: points,
  22367. stackedOnPoints: stackedOnPoints
  22368. });
  22369. }
  22370. }
  22371. }
  22372. var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color');
  22373. polyline.useStyle(defaults( // Use color in lineStyle first
  22374. lineStyleModel.getLineStyle(), {
  22375. fill: 'none',
  22376. stroke: visualColor,
  22377. lineJoin: 'bevel'
  22378. }));
  22379. var smooth = seriesModel.get('smooth');
  22380. smooth = getSmooth(seriesModel.get('smooth'));
  22381. polyline.setShape({
  22382. smooth: smooth,
  22383. smoothMonotone: seriesModel.get('smoothMonotone'),
  22384. connectNulls: seriesModel.get('connectNulls')
  22385. });
  22386. if (polygon) {
  22387. var stackedOn = data.stackedOn;
  22388. var stackedOnSmooth = 0;
  22389. polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), {
  22390. fill: visualColor,
  22391. opacity: 0.7,
  22392. lineJoin: 'bevel'
  22393. }));
  22394. if (stackedOn) {
  22395. var stackedOnSeries = stackedOn.hostModel;
  22396. stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
  22397. }
  22398. polygon.setShape({
  22399. smooth: smooth,
  22400. stackedOnSmooth: stackedOnSmooth,
  22401. smoothMonotone: seriesModel.get('smoothMonotone'),
  22402. connectNulls: seriesModel.get('connectNulls')
  22403. });
  22404. }
  22405. this._data = data; // Save the coordinate system for transition animation when data changed
  22406. this._coordSys = coordSys;
  22407. this._stackedOnPoints = stackedOnPoints;
  22408. this._points = points;
  22409. this._step = step;
  22410. },
  22411. dispose: function () {},
  22412. highlight: function (seriesModel, ecModel, api, payload) {
  22413. var data = seriesModel.getData();
  22414. var dataIndex = queryDataIndex(data, payload);
  22415. if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {
  22416. var symbol = data.getItemGraphicEl(dataIndex);
  22417. if (!symbol) {
  22418. // Create a temporary symbol if it is not exists
  22419. var pt = data.getItemLayout(dataIndex);
  22420. if (!pt) {
  22421. // Null data
  22422. return;
  22423. }
  22424. symbol = new SymbolClz$1(data, dataIndex);
  22425. symbol.position = pt;
  22426. symbol.setZ(seriesModel.get('zlevel'), seriesModel.get('z'));
  22427. symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
  22428. symbol.__temp = true;
  22429. data.setItemGraphicEl(dataIndex, symbol); // Stop scale animation
  22430. symbol.stopSymbolAnimation(true);
  22431. this.group.add(symbol);
  22432. }
  22433. symbol.highlight();
  22434. } else {
  22435. // Highlight whole series
  22436. Chart.prototype.highlight.call(this, seriesModel, ecModel, api, payload);
  22437. }
  22438. },
  22439. downplay: function (seriesModel, ecModel, api, payload) {
  22440. var data = seriesModel.getData();
  22441. var dataIndex = queryDataIndex(data, payload);
  22442. if (dataIndex != null && dataIndex >= 0) {
  22443. var symbol = data.getItemGraphicEl(dataIndex);
  22444. if (symbol) {
  22445. if (symbol.__temp) {
  22446. data.setItemGraphicEl(dataIndex, null);
  22447. this.group.remove(symbol);
  22448. } else {
  22449. symbol.downplay();
  22450. }
  22451. }
  22452. } else {
  22453. // FIXME
  22454. // can not downplay completely.
  22455. // Downplay whole series
  22456. Chart.prototype.downplay.call(this, seriesModel, ecModel, api, payload);
  22457. }
  22458. },
  22459. /**
  22460. * @param {module:zrender/container/Group} group
  22461. * @param {Array.<Array.<number>>} points
  22462. * @private
  22463. */
  22464. _newPolyline: function (points) {
  22465. var polyline = this._polyline; // Remove previous created polyline
  22466. if (polyline) {
  22467. this._lineGroup.remove(polyline);
  22468. }
  22469. polyline = new Polyline$1({
  22470. shape: {
  22471. points: points
  22472. },
  22473. silent: true,
  22474. z2: 10
  22475. });
  22476. this._lineGroup.add(polyline);
  22477. this._polyline = polyline;
  22478. return polyline;
  22479. },
  22480. /**
  22481. * @param {module:zrender/container/Group} group
  22482. * @param {Array.<Array.<number>>} stackedOnPoints
  22483. * @param {Array.<Array.<number>>} points
  22484. * @private
  22485. */
  22486. _newPolygon: function (points, stackedOnPoints) {
  22487. var polygon = this._polygon; // Remove previous created polygon
  22488. if (polygon) {
  22489. this._lineGroup.remove(polygon);
  22490. }
  22491. polygon = new Polygon$1({
  22492. shape: {
  22493. points: points,
  22494. stackedOnPoints: stackedOnPoints
  22495. },
  22496. silent: true
  22497. });
  22498. this._lineGroup.add(polygon);
  22499. this._polygon = polygon;
  22500. return polygon;
  22501. },
  22502. /**
  22503. * @private
  22504. */
  22505. _getSymbolIgnoreFunc: function (data, coordSys) {
  22506. var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; // `getLabelInterval` is provided by echarts/component/axis
  22507. if (categoryAxis && categoryAxis.isLabelIgnored) {
  22508. return bind(categoryAxis.isLabelIgnored, categoryAxis);
  22509. }
  22510. },
  22511. /**
  22512. * @private
  22513. */
  22514. // FIXME Two value axis
  22515. _updateAnimation: function (data, stackedOnPoints, coordSys, api, step) {
  22516. var polyline = this._polyline;
  22517. var polygon = this._polygon;
  22518. var seriesModel = data.hostModel;
  22519. var diff = lineAnimationDiff(this._data, data, this._stackedOnPoints, stackedOnPoints, this._coordSys, coordSys);
  22520. var current = diff.current;
  22521. var stackedOnCurrent = diff.stackedOnCurrent;
  22522. var next = diff.next;
  22523. var stackedOnNext = diff.stackedOnNext;
  22524. if (step) {
  22525. // TODO If stacked series is not step
  22526. current = turnPointsIntoStep(diff.current, coordSys, step);
  22527. stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step);
  22528. next = turnPointsIntoStep(diff.next, coordSys, step);
  22529. stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);
  22530. } // `diff.current` is subset of `current` (which should be ensured by
  22531. // turnPointsIntoStep), so points in `__points` can be updated when
  22532. // points in `current` are update during animation.
  22533. polyline.shape.__points = diff.current;
  22534. polyline.shape.points = current;
  22535. updateProps(polyline, {
  22536. shape: {
  22537. points: next
  22538. }
  22539. }, seriesModel);
  22540. if (polygon) {
  22541. polygon.setShape({
  22542. points: current,
  22543. stackedOnPoints: stackedOnCurrent
  22544. });
  22545. updateProps(polygon, {
  22546. shape: {
  22547. points: next,
  22548. stackedOnPoints: stackedOnNext
  22549. }
  22550. }, seriesModel);
  22551. }
  22552. var updatedDataInfo = [];
  22553. var diffStatus = diff.status;
  22554. for (var i = 0; i < diffStatus.length; i++) {
  22555. var cmd = diffStatus[i].cmd;
  22556. if (cmd === '=') {
  22557. var el = data.getItemGraphicEl(diffStatus[i].idx1);
  22558. if (el) {
  22559. updatedDataInfo.push({
  22560. el: el,
  22561. ptIdx: i // Index of points
  22562. });
  22563. }
  22564. }
  22565. }
  22566. if (polyline.animators && polyline.animators.length) {
  22567. polyline.animators[0].during(function () {
  22568. for (var i = 0; i < updatedDataInfo.length; i++) {
  22569. var el = updatedDataInfo[i].el;
  22570. el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]);
  22571. }
  22572. });
  22573. }
  22574. },
  22575. remove: function (ecModel) {
  22576. var group = this.group;
  22577. var oldData = this._data;
  22578. this._lineGroup.removeAll();
  22579. this._symbolDraw.remove(true); // Remove temporary created elements when highlighting
  22580. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  22581. if (el.__temp) {
  22582. group.remove(el);
  22583. oldData.setItemGraphicEl(idx, null);
  22584. }
  22585. });
  22586. this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._data = null;
  22587. }
  22588. });
  22589. var visualSymbol = function (seriesType, defaultSymbolType, legendSymbol, ecModel, api) {
  22590. // Encoding visual for all series include which is filtered for legend drawing
  22591. ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {
  22592. var data = seriesModel.getData();
  22593. var symbolType = seriesModel.get('symbol') || defaultSymbolType;
  22594. var symbolSize = seriesModel.get('symbolSize');
  22595. data.setVisual({
  22596. legendSymbol: legendSymbol || symbolType,
  22597. symbol: symbolType,
  22598. symbolSize: symbolSize
  22599. }); // Only visible series has each data be visual encoded
  22600. if (!ecModel.isSeriesFiltered(seriesModel)) {
  22601. if (typeof symbolSize === 'function') {
  22602. data.each(function (idx) {
  22603. var rawValue = seriesModel.getRawValue(idx); // FIXME
  22604. var params = seriesModel.getDataParams(idx);
  22605. data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
  22606. });
  22607. }
  22608. data.each(function (idx) {
  22609. var itemModel = data.getItemModel(idx);
  22610. var itemSymbolType = itemModel.getShallow('symbol', true);
  22611. var itemSymbolSize = itemModel.getShallow('symbolSize', true); // If has item symbol
  22612. if (itemSymbolType != null) {
  22613. data.setItemVisual(idx, 'symbol', itemSymbolType);
  22614. }
  22615. if (itemSymbolSize != null) {
  22616. // PENDING Transform symbolSize ?
  22617. data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
  22618. }
  22619. });
  22620. }
  22621. });
  22622. };
  22623. var layoutPoints = function (seriesType, ecModel) {
  22624. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  22625. var data = seriesModel.getData();
  22626. var coordSys = seriesModel.coordinateSystem;
  22627. if (!coordSys) {
  22628. return;
  22629. }
  22630. var dims = [];
  22631. var coordDims = coordSys.dimensions;
  22632. for (var i = 0; i < coordDims.length; i++) {
  22633. dims.push(seriesModel.coordDimToDataDim(coordSys.dimensions[i])[0]);
  22634. }
  22635. if (dims.length === 1) {
  22636. data.each(dims[0], function (x, idx) {
  22637. // Also {Array.<number>}, not undefined to avoid if...else... statement
  22638. data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
  22639. });
  22640. } else if (dims.length === 2) {
  22641. data.each(dims, function (x, y, idx) {
  22642. // Also {Array.<number>}, not undefined to avoid if...else... statement
  22643. data.setItemLayout(idx, isNaN(x) || isNaN(y) ? [NaN, NaN] : coordSys.dataToPoint([x, y]));
  22644. }, true);
  22645. }
  22646. });
  22647. };
  22648. var samplers = {
  22649. average: function (frame) {
  22650. var sum = 0;
  22651. var count = 0;
  22652. for (var i = 0; i < frame.length; i++) {
  22653. if (!isNaN(frame[i])) {
  22654. sum += frame[i];
  22655. count++;
  22656. }
  22657. } // Return NaN if count is 0
  22658. return count === 0 ? NaN : sum / count;
  22659. },
  22660. sum: function (frame) {
  22661. var sum = 0;
  22662. for (var i = 0; i < frame.length; i++) {
  22663. // Ignore NaN
  22664. sum += frame[i] || 0;
  22665. }
  22666. return sum;
  22667. },
  22668. max: function (frame) {
  22669. var max = -Infinity;
  22670. for (var i = 0; i < frame.length; i++) {
  22671. frame[i] > max && (max = frame[i]);
  22672. }
  22673. return max;
  22674. },
  22675. min: function (frame) {
  22676. var min = Infinity;
  22677. for (var i = 0; i < frame.length; i++) {
  22678. frame[i] < min && (min = frame[i]);
  22679. }
  22680. return min;
  22681. },
  22682. // TODO
  22683. // Median
  22684. nearest: function (frame) {
  22685. return frame[0];
  22686. }
  22687. };
  22688. var indexSampler = function (frame, value) {
  22689. return Math.round(frame.length / 2);
  22690. };
  22691. var dataSample = function (seriesType, ecModel, api) {
  22692. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  22693. var data = seriesModel.getData();
  22694. var sampling = seriesModel.get('sampling');
  22695. var coordSys = seriesModel.coordinateSystem; // Only cartesian2d support down sampling
  22696. if (coordSys.type === 'cartesian2d' && sampling) {
  22697. var baseAxis = coordSys.getBaseAxis();
  22698. var valueAxis = coordSys.getOtherAxis(baseAxis);
  22699. var extent = baseAxis.getExtent(); // Coordinste system has been resized
  22700. var size = extent[1] - extent[0];
  22701. var rate = Math.round(data.count() / size);
  22702. if (rate > 1) {
  22703. var sampler;
  22704. if (typeof sampling === 'string') {
  22705. sampler = samplers[sampling];
  22706. } else if (typeof sampling === 'function') {
  22707. sampler = sampling;
  22708. }
  22709. if (sampler) {
  22710. data = data.downSample(valueAxis.dim, 1 / rate, sampler, indexSampler);
  22711. seriesModel.setData(data);
  22712. }
  22713. }
  22714. }
  22715. }, this);
  22716. };
  22717. /**
  22718. * Cartesian coordinate system
  22719. * @module echarts/coord/Cartesian
  22720. *
  22721. */
  22722. function dimAxisMapper(dim) {
  22723. return this._axes[dim];
  22724. }
  22725. /**
  22726. * @alias module:echarts/coord/Cartesian
  22727. * @constructor
  22728. */
  22729. var Cartesian = function (name) {
  22730. this._axes = {};
  22731. this._dimList = [];
  22732. /**
  22733. * @type {string}
  22734. */
  22735. this.name = name || '';
  22736. };
  22737. Cartesian.prototype = {
  22738. constructor: Cartesian,
  22739. type: 'cartesian',
  22740. /**
  22741. * Get axis
  22742. * @param {number|string} dim
  22743. * @return {module:echarts/coord/Cartesian~Axis}
  22744. */
  22745. getAxis: function (dim) {
  22746. return this._axes[dim];
  22747. },
  22748. /**
  22749. * Get axes list
  22750. * @return {Array.<module:echarts/coord/Cartesian~Axis>}
  22751. */
  22752. getAxes: function () {
  22753. return map(this._dimList, dimAxisMapper, this);
  22754. },
  22755. /**
  22756. * Get axes list by given scale type
  22757. */
  22758. getAxesByScale: function (scaleType) {
  22759. scaleType = scaleType.toLowerCase();
  22760. return filter(this.getAxes(), function (axis) {
  22761. return axis.scale.type === scaleType;
  22762. });
  22763. },
  22764. /**
  22765. * Add axis
  22766. * @param {module:echarts/coord/Cartesian.Axis}
  22767. */
  22768. addAxis: function (axis) {
  22769. var dim = axis.dim;
  22770. this._axes[dim] = axis;
  22771. this._dimList.push(dim);
  22772. },
  22773. /**
  22774. * Convert data to coord in nd space
  22775. * @param {Array.<number>|Object.<string, number>} val
  22776. * @return {Array.<number>|Object.<string, number>}
  22777. */
  22778. dataToCoord: function (val) {
  22779. return this._dataCoordConvert(val, 'dataToCoord');
  22780. },
  22781. /**
  22782. * Convert coord in nd space to data
  22783. * @param {Array.<number>|Object.<string, number>} val
  22784. * @return {Array.<number>|Object.<string, number>}
  22785. */
  22786. coordToData: function (val) {
  22787. return this._dataCoordConvert(val, 'coordToData');
  22788. },
  22789. _dataCoordConvert: function (input, method) {
  22790. var dimList = this._dimList;
  22791. var output = input instanceof Array ? [] : {};
  22792. for (var i = 0; i < dimList.length; i++) {
  22793. var dim = dimList[i];
  22794. var axis = this._axes[dim];
  22795. output[dim] = axis[method](input[dim]);
  22796. }
  22797. return output;
  22798. }
  22799. };
  22800. function Cartesian2D(name) {
  22801. Cartesian.call(this, name);
  22802. }
  22803. Cartesian2D.prototype = {
  22804. constructor: Cartesian2D,
  22805. type: 'cartesian2d',
  22806. /**
  22807. * @type {Array.<string>}
  22808. * @readOnly
  22809. */
  22810. dimensions: ['x', 'y'],
  22811. /**
  22812. * Base axis will be used on stacking.
  22813. *
  22814. * @return {module:echarts/coord/cartesian/Axis2D}
  22815. */
  22816. getBaseAxis: function () {
  22817. return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAxis('x');
  22818. },
  22819. /**
  22820. * If contain point
  22821. * @param {Array.<number>} point
  22822. * @return {boolean}
  22823. */
  22824. containPoint: function (point) {
  22825. var axisX = this.getAxis('x');
  22826. var axisY = this.getAxis('y');
  22827. return axisX.contain(axisX.toLocalCoord(point[0])) && axisY.contain(axisY.toLocalCoord(point[1]));
  22828. },
  22829. /**
  22830. * If contain data
  22831. * @param {Array.<number>} data
  22832. * @return {boolean}
  22833. */
  22834. containData: function (data) {
  22835. return this.getAxis('x').containData(data[0]) && this.getAxis('y').containData(data[1]);
  22836. },
  22837. /**
  22838. * @param {Array.<number>} data
  22839. * @param {boolean} [clamp=false]
  22840. * @return {Array.<number>}
  22841. */
  22842. dataToPoint: function (data, clamp) {
  22843. var xAxis = this.getAxis('x');
  22844. var yAxis = this.getAxis('y');
  22845. return [xAxis.toGlobalCoord(xAxis.dataToCoord(data[0], clamp)), yAxis.toGlobalCoord(yAxis.dataToCoord(data[1], clamp))];
  22846. },
  22847. /**
  22848. * @param {Array.<number>} point
  22849. * @param {boolean} [clamp=false]
  22850. * @return {Array.<number>}
  22851. */
  22852. pointToData: function (point, clamp) {
  22853. var xAxis = this.getAxis('x');
  22854. var yAxis = this.getAxis('y');
  22855. return [xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp), yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp)];
  22856. },
  22857. /**
  22858. * Get other axis
  22859. * @param {module:echarts/coord/cartesian/Axis2D} axis
  22860. */
  22861. getOtherAxis: function (axis) {
  22862. return this.getAxis(axis.dim === 'x' ? 'y' : 'x');
  22863. }
  22864. };
  22865. inherits(Cartesian2D, Cartesian);
  22866. /**
  22867. * Extend axis 2d
  22868. * @constructor module:echarts/coord/cartesian/Axis2D
  22869. * @extends {module:echarts/coord/cartesian/Axis}
  22870. * @param {string} dim
  22871. * @param {*} scale
  22872. * @param {Array.<number>} coordExtent
  22873. * @param {string} axisType
  22874. * @param {string} position
  22875. */
  22876. var Axis2D = function (dim, scale, coordExtent, axisType, position) {
  22877. Axis.call(this, dim, scale, coordExtent);
  22878. /**
  22879. * Axis type
  22880. * - 'category'
  22881. * - 'value'
  22882. * - 'time'
  22883. * - 'log'
  22884. * @type {string}
  22885. */
  22886. this.type = axisType || 'value';
  22887. /**
  22888. * Axis position
  22889. * - 'top'
  22890. * - 'bottom'
  22891. * - 'left'
  22892. * - 'right'
  22893. */
  22894. this.position = position || 'bottom';
  22895. };
  22896. Axis2D.prototype = {
  22897. constructor: Axis2D,
  22898. /**
  22899. * Index of axis, can be used as key
  22900. */
  22901. index: 0,
  22902. /**
  22903. * If axis is on the zero position of the other axis
  22904. * @type {boolean}
  22905. */
  22906. onZero: false,
  22907. /**
  22908. * Axis model
  22909. * @param {module:echarts/coord/cartesian/AxisModel}
  22910. */
  22911. model: null,
  22912. isHorizontal: function () {
  22913. var position = this.position;
  22914. return position === 'top' || position === 'bottom';
  22915. },
  22916. /**
  22917. * Each item cooresponds to this.getExtent(), which
  22918. * means globalExtent[0] may greater than globalExtent[1],
  22919. * unless `asc` is input.
  22920. *
  22921. * @param {boolean} [asc]
  22922. * @return {Array.<number>}
  22923. */
  22924. getGlobalExtent: function (asc) {
  22925. var ret = this.getExtent();
  22926. ret[0] = this.toGlobalCoord(ret[0]);
  22927. ret[1] = this.toGlobalCoord(ret[1]);
  22928. asc && ret[0] > ret[1] && ret.reverse();
  22929. return ret;
  22930. },
  22931. getOtherAxis: function () {
  22932. this.grid.getOtherAxis();
  22933. },
  22934. /**
  22935. * If label is ignored.
  22936. * Automatically used when axis is category and label can not be all shown
  22937. * @param {number} idx
  22938. * @return {boolean}
  22939. */
  22940. isLabelIgnored: function (idx) {
  22941. if (this.type === 'category') {
  22942. var labelInterval = this.getLabelInterval();
  22943. return typeof labelInterval === 'function' && !labelInterval(idx, this.scale.getLabel(idx)) || idx % (labelInterval + 1);
  22944. }
  22945. },
  22946. /**
  22947. * @override
  22948. */
  22949. pointToData: function (point, clamp) {
  22950. return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);
  22951. },
  22952. /**
  22953. * Transform global coord to local coord,
  22954. * i.e. var localCoord = axis.toLocalCoord(80);
  22955. * designate by module:echarts/coord/cartesian/Grid.
  22956. * @type {Function}
  22957. */
  22958. toLocalCoord: null,
  22959. /**
  22960. * Transform global coord to local coord,
  22961. * i.e. var globalCoord = axis.toLocalCoord(40);
  22962. * designate by module:echarts/coord/cartesian/Grid.
  22963. * @type {Function}
  22964. */
  22965. toGlobalCoord: null
  22966. };
  22967. inherits(Axis2D, Axis);
  22968. var defaultOption = {
  22969. show: true,
  22970. zlevel: 0,
  22971. // 一级层叠
  22972. z: 0,
  22973. // 二级层叠
  22974. // 反向坐标轴
  22975. inverse: false,
  22976. // 坐标轴名字,默认为空
  22977. name: '',
  22978. // 坐标轴名字位置,支持'start' | 'middle' | 'end'
  22979. nameLocation: 'end',
  22980. // 坐标轴名字旋转,degree。
  22981. nameRotate: null,
  22982. // Adapt to axis rotate, when nameLocation is 'middle'.
  22983. nameTruncate: {
  22984. maxWidth: null,
  22985. ellipsis: '...',
  22986. placeholder: '.'
  22987. },
  22988. // 坐标轴文字样式,默认取全局样式
  22989. nameTextStyle: {},
  22990. // 文字与轴线距离
  22991. nameGap: 15,
  22992. silent: false,
  22993. // Default false to support tooltip.
  22994. triggerEvent: false,
  22995. // Default false to avoid legacy user event listener fail.
  22996. tooltip: {
  22997. show: false
  22998. },
  22999. axisPointer: {},
  23000. // 坐标轴线
  23001. axisLine: {
  23002. // 默认显示,属性show控制显示与否
  23003. show: true,
  23004. onZero: true,
  23005. onZeroAxisIndex: null,
  23006. // 属性lineStyle控制线条样式
  23007. lineStyle: {
  23008. color: '#333',
  23009. width: 1,
  23010. type: 'solid'
  23011. },
  23012. // 坐标轴两端的箭头
  23013. symbol: ['none', 'none'],
  23014. symbolSize: [10, 15]
  23015. },
  23016. // 坐标轴小标记
  23017. axisTick: {
  23018. // 属性show控制显示与否,默认显示
  23019. show: true,
  23020. // 控制小标记是否在grid里
  23021. inside: false,
  23022. // 属性length控制线长
  23023. length: 5,
  23024. // 属性lineStyle控制线条样式
  23025. lineStyle: {
  23026. width: 1
  23027. }
  23028. },
  23029. // 坐标轴文本标签,详见axis.axisLabel
  23030. axisLabel: {
  23031. show: true,
  23032. // 控制文本标签是否在grid里
  23033. inside: false,
  23034. rotate: 0,
  23035. showMinLabel: null,
  23036. // true | false | null (auto)
  23037. showMaxLabel: null,
  23038. // true | false | null (auto)
  23039. margin: 8,
  23040. // formatter: null,
  23041. // 其余属性默认使用全局文本样式,详见TEXTSTYLE
  23042. fontSize: 12
  23043. },
  23044. // 分隔线
  23045. splitLine: {
  23046. // 默认显示,属性show控制显示与否
  23047. show: true,
  23048. // 属性lineStyle(详见lineStyle)控制线条样式
  23049. lineStyle: {
  23050. color: ['#ccc'],
  23051. width: 1,
  23052. type: 'solid'
  23053. }
  23054. },
  23055. // 分隔区域
  23056. splitArea: {
  23057. // 默认不显示,属性show控制显示与否
  23058. show: false,
  23059. // 属性areaStyle(详见areaStyle)控制区域样式
  23060. areaStyle: {
  23061. color: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)']
  23062. }
  23063. }
  23064. };
  23065. var axisDefault = {};
  23066. axisDefault.categoryAxis = merge({
  23067. // 类目起始和结束两端空白策略
  23068. boundaryGap: true,
  23069. // splitArea: {
  23070. // show: false
  23071. // },
  23072. splitLine: {
  23073. show: false
  23074. },
  23075. // 坐标轴小标记
  23076. axisTick: {
  23077. // If tick is align with label when boundaryGap is true
  23078. alignWithLabel: false,
  23079. interval: 'auto'
  23080. },
  23081. // 坐标轴文本标签,详见axis.axisLabel
  23082. axisLabel: {
  23083. interval: 'auto'
  23084. }
  23085. }, defaultOption);
  23086. axisDefault.valueAxis = merge({
  23087. // 数值起始和结束两端空白策略
  23088. boundaryGap: [0, 0],
  23089. // 最小值, 设置成 'dataMin' 则从数据中计算最小值
  23090. // min: null,
  23091. // 最大值,设置成 'dataMax' 则从数据中计算最大值
  23092. // max: null,
  23093. // Readonly prop, specifies start value of the range when using data zoom.
  23094. // rangeStart: null
  23095. // Readonly prop, specifies end value of the range when using data zoom.
  23096. // rangeEnd: null
  23097. // 脱离0值比例,放大聚焦到最终_min,_max区间
  23098. // scale: false,
  23099. // 分割段数,默认为5
  23100. splitNumber: 5 // Minimum interval
  23101. // minInterval: null
  23102. // maxInterval: null
  23103. }, defaultOption); // FIXME
  23104. axisDefault.timeAxis = defaults({
  23105. scale: true,
  23106. min: 'dataMin',
  23107. max: 'dataMax'
  23108. }, axisDefault.valueAxis);
  23109. axisDefault.logAxis = defaults({
  23110. scale: true,
  23111. logBase: 10
  23112. }, axisDefault.valueAxis); // FIXME axisType is fixed ?
  23113. var AXIS_TYPES = ['value', 'category', 'time', 'log'];
  23114. /**
  23115. * Generate sub axis model class
  23116. * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel'
  23117. * @param {module:echarts/model/Component} BaseAxisModelClass
  23118. * @param {Function} axisTypeDefaulter
  23119. * @param {Object} [extraDefaultOption]
  23120. */
  23121. var axisModelCreator = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) {
  23122. each$1(AXIS_TYPES, function (axisType) {
  23123. BaseAxisModelClass.extend({
  23124. type: axisName + 'Axis.' + axisType,
  23125. mergeDefaultAndTheme: function (option, ecModel) {
  23126. var layoutMode = this.layoutMode;
  23127. var inputPositionParams = layoutMode ? getLayoutParams(option) : {};
  23128. var themeModel = ecModel.getTheme();
  23129. merge(option, themeModel.get(axisType + 'Axis'));
  23130. merge(option, this.getDefaultOption());
  23131. option.type = axisTypeDefaulter(axisName, option);
  23132. if (layoutMode) {
  23133. mergeLayoutParam(option, inputPositionParams, layoutMode);
  23134. }
  23135. },
  23136. defaultOption: mergeAll([{}, axisDefault[axisType + 'Axis'], extraDefaultOption], true)
  23137. });
  23138. });
  23139. ComponentModel.registerSubTypeDefaulter(axisName + 'Axis', curry(axisTypeDefaulter, axisName));
  23140. };
  23141. var AxisModel = ComponentModel.extend({
  23142. type: 'cartesian2dAxis',
  23143. /**
  23144. * @type {module:echarts/coord/cartesian/Axis2D}
  23145. */
  23146. axis: null,
  23147. /**
  23148. * @override
  23149. */
  23150. init: function () {
  23151. AxisModel.superApply(this, 'init', arguments);
  23152. this.resetRange();
  23153. },
  23154. /**
  23155. * @override
  23156. */
  23157. mergeOption: function () {
  23158. AxisModel.superApply(this, 'mergeOption', arguments);
  23159. this.resetRange();
  23160. },
  23161. /**
  23162. * @override
  23163. */
  23164. restoreData: function () {
  23165. AxisModel.superApply(this, 'restoreData', arguments);
  23166. this.resetRange();
  23167. },
  23168. /**
  23169. * @override
  23170. * @return {module:echarts/model/Component}
  23171. */
  23172. getCoordSysModel: function () {
  23173. return this.ecModel.queryComponents({
  23174. mainType: 'grid',
  23175. index: this.option.gridIndex,
  23176. id: this.option.gridId
  23177. })[0];
  23178. }
  23179. });
  23180. function getAxisType(axisDim, option) {
  23181. // Default axis with data is category axis
  23182. return option.type || (option.data ? 'category' : 'value');
  23183. }
  23184. merge(AxisModel.prototype, axisModelCommonMixin);
  23185. var extraOption = {
  23186. // gridIndex: 0,
  23187. // gridId: '',
  23188. // Offset is for multiple axis on the same position
  23189. offset: 0
  23190. };
  23191. axisModelCreator('x', AxisModel, getAxisType, extraOption);
  23192. axisModelCreator('y', AxisModel, getAxisType, extraOption); // Grid 是在有直角坐标系的时候必须要存在的
  23193. // 所以这里也要被 Cartesian2D 依赖
  23194. ComponentModel.extend({
  23195. type: 'grid',
  23196. dependencies: ['xAxis', 'yAxis'],
  23197. layoutMode: 'box',
  23198. /**
  23199. * @type {module:echarts/coord/cartesian/Grid}
  23200. */
  23201. coordinateSystem: null,
  23202. defaultOption: {
  23203. show: false,
  23204. zlevel: 0,
  23205. z: 0,
  23206. left: '10%',
  23207. top: 60,
  23208. right: '10%',
  23209. bottom: 60,
  23210. // If grid size contain label
  23211. containLabel: false,
  23212. // width: {totalWidth} - left - right,
  23213. // height: {totalHeight} - top - bottom,
  23214. backgroundColor: 'rgba(0,0,0,0)',
  23215. borderWidth: 1,
  23216. borderColor: '#ccc'
  23217. }
  23218. });
  23219. /**
  23220. * Grid is a region which contains at most 4 cartesian systems
  23221. *
  23222. * TODO Default cartesian
  23223. */
  23224. // Depends on GridModel, AxisModel, which performs preprocess.
  23225. var each$8 = each$1;
  23226. var ifAxisCrossZero$1 = ifAxisCrossZero;
  23227. var niceScaleExtent$1 = niceScaleExtent;
  23228. /**
  23229. * Check if the axis is used in the specified grid
  23230. * @inner
  23231. */
  23232. function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {
  23233. return axisModel.getCoordSysModel() === gridModel;
  23234. }
  23235. function rotateTextRect(textRect, rotate) {
  23236. var rotateRadians = rotate * Math.PI / 180;
  23237. var boundingBox = textRect.plain();
  23238. var beforeWidth = boundingBox.width;
  23239. var beforeHeight = boundingBox.height;
  23240. var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);
  23241. var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians);
  23242. var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight);
  23243. return rotatedRect;
  23244. }
  23245. function getLabelUnionRect(axis) {
  23246. var axisModel = axis.model;
  23247. var labels = axisModel.getFormattedLabels();
  23248. var axisLabelModel = axisModel.getModel('axisLabel');
  23249. var rect;
  23250. var step = 1;
  23251. var labelCount = labels.length;
  23252. if (labelCount > 40) {
  23253. // Simple optimization for large amount of labels
  23254. step = Math.ceil(labelCount / 40);
  23255. }
  23256. for (var i = 0; i < labelCount; i += step) {
  23257. if (!axis.isLabelIgnored(i)) {
  23258. var unrotatedSingleRect = axisLabelModel.getTextRect(labels[i]);
  23259. var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);
  23260. rect ? rect.union(singleRect) : rect = singleRect;
  23261. }
  23262. }
  23263. return rect;
  23264. }
  23265. function Grid(gridModel, ecModel, api) {
  23266. /**
  23267. * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}
  23268. * @private
  23269. */
  23270. this._coordsMap = {};
  23271. /**
  23272. * @type {Array.<module:echarts/coord/cartesian/Cartesian>}
  23273. * @private
  23274. */
  23275. this._coordsList = [];
  23276. /**
  23277. * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}
  23278. * @private
  23279. */
  23280. this._axesMap = {};
  23281. /**
  23282. * @type {Array.<module:echarts/coord/cartesian/Axis2D>}
  23283. * @private
  23284. */
  23285. this._axesList = [];
  23286. this._initCartesian(gridModel, ecModel, api);
  23287. this.model = gridModel;
  23288. }
  23289. var gridProto = Grid.prototype;
  23290. gridProto.type = 'grid';
  23291. gridProto.axisPointerEnabled = true;
  23292. gridProto.getRect = function () {
  23293. return this._rect;
  23294. };
  23295. gridProto.update = function (ecModel, api) {
  23296. var axesMap = this._axesMap;
  23297. this._updateScale(ecModel, this.model);
  23298. each$8(axesMap.x, function (xAxis) {
  23299. niceScaleExtent$1(xAxis.scale, xAxis.model);
  23300. });
  23301. each$8(axesMap.y, function (yAxis) {
  23302. niceScaleExtent$1(yAxis.scale, yAxis.model);
  23303. });
  23304. each$8(axesMap.x, function (xAxis) {
  23305. fixAxisOnZero(axesMap, 'y', xAxis);
  23306. });
  23307. each$8(axesMap.y, function (yAxis) {
  23308. fixAxisOnZero(axesMap, 'x', yAxis);
  23309. }); // Resize again if containLabel is enabled
  23310. // FIXME It may cause getting wrong grid size in data processing stage
  23311. this.resize(this.model, api);
  23312. };
  23313. function fixAxisOnZero(axesMap, otherAxisDim, axis) {
  23314. // onZero can not be enabled in these two situations:
  23315. // 1. When any other axis is a category axis.
  23316. // 2. When no axis is cross 0 point.
  23317. var axes = axesMap[otherAxisDim];
  23318. if (!axis.onZero) {
  23319. return;
  23320. }
  23321. var onZeroAxisIndex = axis.onZeroAxisIndex; // If target axis is specified.
  23322. if (onZeroAxisIndex != null) {
  23323. var otherAxis = axes[onZeroAxisIndex];
  23324. if (otherAxis && canNotOnZeroToAxis(otherAxis)) {
  23325. axis.onZero = false;
  23326. }
  23327. return;
  23328. }
  23329. for (var idx in axes) {
  23330. if (axes.hasOwnProperty(idx)) {
  23331. var otherAxis = axes[idx];
  23332. if (otherAxis && !canNotOnZeroToAxis(otherAxis)) {
  23333. onZeroAxisIndex = +idx;
  23334. break;
  23335. }
  23336. }
  23337. }
  23338. if (onZeroAxisIndex == null) {
  23339. axis.onZero = false;
  23340. }
  23341. axis.onZeroAxisIndex = onZeroAxisIndex;
  23342. }
  23343. function canNotOnZeroToAxis(axis) {
  23344. return axis.type === 'category' || axis.type === 'time' || !ifAxisCrossZero$1(axis);
  23345. }
  23346. /**
  23347. * Resize the grid
  23348. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  23349. * @param {module:echarts/ExtensionAPI} api
  23350. */
  23351. gridProto.resize = function (gridModel, api, ignoreContainLabel) {
  23352. var gridRect = getLayoutRect(gridModel.getBoxLayoutParams(), {
  23353. width: api.getWidth(),
  23354. height: api.getHeight()
  23355. });
  23356. this._rect = gridRect;
  23357. var axesList = this._axesList;
  23358. adjustAxes(); // Minus label size
  23359. if (!ignoreContainLabel && gridModel.get('containLabel')) {
  23360. each$8(axesList, function (axis) {
  23361. if (!axis.model.get('axisLabel.inside')) {
  23362. var labelUnionRect = getLabelUnionRect(axis);
  23363. if (labelUnionRect) {
  23364. var dim = axis.isHorizontal() ? 'height' : 'width';
  23365. var margin = axis.model.get('axisLabel.margin');
  23366. gridRect[dim] -= labelUnionRect[dim] + margin;
  23367. if (axis.position === 'top') {
  23368. gridRect.y += labelUnionRect.height + margin;
  23369. } else if (axis.position === 'left') {
  23370. gridRect.x += labelUnionRect.width + margin;
  23371. }
  23372. }
  23373. }
  23374. });
  23375. adjustAxes();
  23376. }
  23377. function adjustAxes() {
  23378. each$8(axesList, function (axis) {
  23379. var isHorizontal = axis.isHorizontal();
  23380. var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
  23381. var idx = axis.inverse ? 1 : 0;
  23382. axis.setExtent(extent[idx], extent[1 - idx]);
  23383. updateAxisTransfrom(axis, isHorizontal ? gridRect.x : gridRect.y);
  23384. });
  23385. }
  23386. };
  23387. /**
  23388. * @param {string} axisType
  23389. * @param {number} [axisIndex]
  23390. */
  23391. gridProto.getAxis = function (axisType, axisIndex) {
  23392. var axesMapOnDim = this._axesMap[axisType];
  23393. if (axesMapOnDim != null) {
  23394. if (axisIndex == null) {
  23395. // Find first axis
  23396. for (var name in axesMapOnDim) {
  23397. if (axesMapOnDim.hasOwnProperty(name)) {
  23398. return axesMapOnDim[name];
  23399. }
  23400. }
  23401. }
  23402. return axesMapOnDim[axisIndex];
  23403. }
  23404. };
  23405. /**
  23406. * @return {Array.<module:echarts/coord/Axis>}
  23407. */
  23408. gridProto.getAxes = function () {
  23409. return this._axesList.slice();
  23410. };
  23411. /**
  23412. * Usage:
  23413. * grid.getCartesian(xAxisIndex, yAxisIndex);
  23414. * grid.getCartesian(xAxisIndex);
  23415. * grid.getCartesian(null, yAxisIndex);
  23416. * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});
  23417. *
  23418. * @param {number|Object} [xAxisIndex]
  23419. * @param {number} [yAxisIndex]
  23420. */
  23421. gridProto.getCartesian = function (xAxisIndex, yAxisIndex) {
  23422. if (xAxisIndex != null && yAxisIndex != null) {
  23423. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  23424. return this._coordsMap[key];
  23425. }
  23426. if (isObject(xAxisIndex)) {
  23427. yAxisIndex = xAxisIndex.yAxisIndex;
  23428. xAxisIndex = xAxisIndex.xAxisIndex;
  23429. } // When only xAxisIndex or yAxisIndex given, find its first cartesian.
  23430. for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
  23431. if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) {
  23432. return coordList[i];
  23433. }
  23434. }
  23435. };
  23436. gridProto.getCartesians = function () {
  23437. return this._coordsList.slice();
  23438. };
  23439. /**
  23440. * @implements
  23441. * see {module:echarts/CoodinateSystem}
  23442. */
  23443. gridProto.convertToPixel = function (ecModel, finder, value) {
  23444. var target = this._findConvertTarget(ecModel, finder);
  23445. return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null;
  23446. };
  23447. /**
  23448. * @implements
  23449. * see {module:echarts/CoodinateSystem}
  23450. */
  23451. gridProto.convertFromPixel = function (ecModel, finder, value) {
  23452. var target = this._findConvertTarget(ecModel, finder);
  23453. return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null;
  23454. };
  23455. /**
  23456. * @inner
  23457. */
  23458. gridProto._findConvertTarget = function (ecModel, finder) {
  23459. var seriesModel = finder.seriesModel;
  23460. var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis')[0];
  23461. var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis')[0];
  23462. var gridModel = finder.gridModel;
  23463. var coordsList = this._coordsList;
  23464. var cartesian;
  23465. var axis;
  23466. if (seriesModel) {
  23467. cartesian = seriesModel.coordinateSystem;
  23468. indexOf(coordsList, cartesian) < 0 && (cartesian = null);
  23469. } else if (xAxisModel && yAxisModel) {
  23470. cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  23471. } else if (xAxisModel) {
  23472. axis = this.getAxis('x', xAxisModel.componentIndex);
  23473. } else if (yAxisModel) {
  23474. axis = this.getAxis('y', yAxisModel.componentIndex);
  23475. } // Lowest priority.
  23476. else if (gridModel) {
  23477. var grid = gridModel.coordinateSystem;
  23478. if (grid === this) {
  23479. cartesian = this._coordsList[0];
  23480. }
  23481. }
  23482. return {
  23483. cartesian: cartesian,
  23484. axis: axis
  23485. };
  23486. };
  23487. /**
  23488. * @implements
  23489. * see {module:echarts/CoodinateSystem}
  23490. */
  23491. gridProto.containPoint = function (point) {
  23492. var coord = this._coordsList[0];
  23493. if (coord) {
  23494. return coord.containPoint(point);
  23495. }
  23496. };
  23497. /**
  23498. * Initialize cartesian coordinate systems
  23499. * @private
  23500. */
  23501. gridProto._initCartesian = function (gridModel, ecModel, api) {
  23502. var axisPositionUsed = {
  23503. left: false,
  23504. right: false,
  23505. top: false,
  23506. bottom: false
  23507. };
  23508. var axesMap = {
  23509. x: {},
  23510. y: {}
  23511. };
  23512. var axesCount = {
  23513. x: 0,
  23514. y: 0
  23515. }; /// Create axis
  23516. ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
  23517. ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
  23518. if (!axesCount.x || !axesCount.y) {
  23519. // Roll back when there no either x or y axis
  23520. this._axesMap = {};
  23521. this._axesList = [];
  23522. return;
  23523. }
  23524. this._axesMap = axesMap; /// Create cartesian2d
  23525. each$8(axesMap.x, function (xAxis, xAxisIndex) {
  23526. each$8(axesMap.y, function (yAxis, yAxisIndex) {
  23527. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  23528. var cartesian = new Cartesian2D(key);
  23529. cartesian.grid = this;
  23530. cartesian.model = gridModel;
  23531. this._coordsMap[key] = cartesian;
  23532. this._coordsList.push(cartesian);
  23533. cartesian.addAxis(xAxis);
  23534. cartesian.addAxis(yAxis);
  23535. }, this);
  23536. }, this);
  23537. function createAxisCreator(axisType) {
  23538. return function (axisModel, idx) {
  23539. if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {
  23540. return;
  23541. }
  23542. var axisPosition = axisModel.get('position');
  23543. if (axisType === 'x') {
  23544. // Fix position
  23545. if (axisPosition !== 'top' && axisPosition !== 'bottom') {
  23546. // Default bottom of X
  23547. axisPosition = 'bottom';
  23548. if (axisPositionUsed[axisPosition]) {
  23549. axisPosition = axisPosition === 'top' ? 'bottom' : 'top';
  23550. }
  23551. }
  23552. } else {
  23553. // Fix position
  23554. if (axisPosition !== 'left' && axisPosition !== 'right') {
  23555. // Default left of Y
  23556. axisPosition = 'left';
  23557. if (axisPositionUsed[axisPosition]) {
  23558. axisPosition = axisPosition === 'left' ? 'right' : 'left';
  23559. }
  23560. }
  23561. }
  23562. axisPositionUsed[axisPosition] = true;
  23563. var axis = new Axis2D(axisType, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition);
  23564. var isCategory = axis.type === 'category';
  23565. axis.onBand = isCategory && axisModel.get('boundaryGap');
  23566. axis.inverse = axisModel.get('inverse');
  23567. axis.onZero = axisModel.get('axisLine.onZero');
  23568. axis.onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex'); // Inject axis into axisModel
  23569. axisModel.axis = axis; // Inject axisModel into axis
  23570. axis.model = axisModel; // Inject grid info axis
  23571. axis.grid = this; // Index of axis, can be used as key
  23572. axis.index = idx;
  23573. this._axesList.push(axis);
  23574. axesMap[axisType][idx] = axis;
  23575. axesCount[axisType]++;
  23576. };
  23577. }
  23578. };
  23579. /**
  23580. * Update cartesian properties from series
  23581. * @param {module:echarts/model/Option} option
  23582. * @private
  23583. */
  23584. gridProto._updateScale = function (ecModel, gridModel) {
  23585. // Reset scale
  23586. each$1(this._axesList, function (axis) {
  23587. axis.scale.setExtent(Infinity, -Infinity);
  23588. });
  23589. ecModel.eachSeries(function (seriesModel) {
  23590. if (isCartesian2D(seriesModel)) {
  23591. var axesModels = findAxesModels(seriesModel, ecModel);
  23592. var xAxisModel = axesModels[0];
  23593. var yAxisModel = axesModels[1];
  23594. if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)) {
  23595. return;
  23596. }
  23597. var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  23598. var data = seriesModel.getData();
  23599. var xAxis = cartesian.getAxis('x');
  23600. var yAxis = cartesian.getAxis('y');
  23601. if (data.type === 'list') {
  23602. unionExtent(data, xAxis, seriesModel);
  23603. unionExtent(data, yAxis, seriesModel);
  23604. }
  23605. }
  23606. }, this);
  23607. function unionExtent(data, axis, seriesModel) {
  23608. each$8(seriesModel.coordDimToDataDim(axis.dim), function (dim) {
  23609. axis.scale.unionExtentFromData(data, dim);
  23610. });
  23611. }
  23612. };
  23613. /**
  23614. * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined
  23615. * @return {Object} {baseAxes: [], otherAxes: []}
  23616. */
  23617. gridProto.getTooltipAxes = function (dim) {
  23618. var baseAxes = [];
  23619. var otherAxes = [];
  23620. each$8(this.getCartesians(), function (cartesian) {
  23621. var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
  23622. var otherAxis = cartesian.getOtherAxis(baseAxis);
  23623. indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
  23624. indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
  23625. });
  23626. return {
  23627. baseAxes: baseAxes,
  23628. otherAxes: otherAxes
  23629. };
  23630. };
  23631. /**
  23632. * @inner
  23633. */
  23634. function updateAxisTransfrom(axis, coordBase) {
  23635. var axisExtent = axis.getExtent();
  23636. var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform
  23637. axis.toGlobalCoord = axis.dim === 'x' ? function (coord) {
  23638. return coord + coordBase;
  23639. } : function (coord) {
  23640. return axisExtentSum - coord + coordBase;
  23641. };
  23642. axis.toLocalCoord = axis.dim === 'x' ? function (coord) {
  23643. return coord - coordBase;
  23644. } : function (coord) {
  23645. return axisExtentSum - coord + coordBase;
  23646. };
  23647. }
  23648. var axesTypes = ['xAxis', 'yAxis'];
  23649. /**
  23650. * @inner
  23651. */
  23652. function findAxesModels(seriesModel, ecModel) {
  23653. return map(axesTypes, function (axisType) {
  23654. var axisModel = seriesModel.getReferringComponents(axisType)[0];
  23655. if (true) {
  23656. if (!axisModel) {
  23657. throw new Error(axisType + ' "' + retrieve(seriesModel.get(axisType + 'Index'), seriesModel.get(axisType + 'Id'), 0) + '" not found');
  23658. }
  23659. }
  23660. return axisModel;
  23661. });
  23662. }
  23663. /**
  23664. * @inner
  23665. */
  23666. function isCartesian2D(seriesModel) {
  23667. return seriesModel.get('coordinateSystem') === 'cartesian2d';
  23668. }
  23669. Grid.create = function (ecModel, api) {
  23670. var grids = [];
  23671. ecModel.eachComponent('grid', function (gridModel, idx) {
  23672. var grid = new Grid(gridModel, ecModel, api);
  23673. grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize
  23674. // should be performed in create stage.
  23675. grid.resize(gridModel, api, true);
  23676. gridModel.coordinateSystem = grid;
  23677. grids.push(grid);
  23678. }); // Inject the coordinateSystems into seriesModel
  23679. ecModel.eachSeries(function (seriesModel) {
  23680. if (!isCartesian2D(seriesModel)) {
  23681. return;
  23682. }
  23683. var axesModels = findAxesModels(seriesModel, ecModel);
  23684. var xAxisModel = axesModels[0];
  23685. var yAxisModel = axesModels[1];
  23686. var gridModel = xAxisModel.getCoordSysModel();
  23687. if (true) {
  23688. if (!gridModel) {
  23689. throw new Error('Grid "' + retrieve(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '" not found');
  23690. }
  23691. if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
  23692. throw new Error('xAxis and yAxis must use the same grid');
  23693. }
  23694. }
  23695. var grid = gridModel.coordinateSystem;
  23696. seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  23697. });
  23698. return grids;
  23699. }; // For deciding which dimensions to use when creating list data
  23700. Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;
  23701. CoordinateSystemManager.register('cartesian2d', Grid);
  23702. var PI$2 = Math.PI;
  23703. function makeAxisEventDataBase(axisModel) {
  23704. var eventData = {
  23705. componentType: axisModel.mainType
  23706. };
  23707. eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;
  23708. return eventData;
  23709. }
  23710. /**
  23711. * A final axis is translated and rotated from a "standard axis".
  23712. * So opt.position and opt.rotation is required.
  23713. *
  23714. * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],
  23715. * for example: (0, 0) ------------> (0, 50)
  23716. *
  23717. * nameDirection or tickDirection or labelDirection is 1 means tick
  23718. * or label is below the standard axis, whereas is -1 means above
  23719. * the standard axis. labelOffset means offset between label and axis,
  23720. * which is useful when 'onZero', where axisLabel is in the grid and
  23721. * label in outside grid.
  23722. *
  23723. * Tips: like always,
  23724. * positive rotation represents anticlockwise, and negative rotation
  23725. * represents clockwise.
  23726. * The direction of position coordinate is the same as the direction
  23727. * of screen coordinate.
  23728. *
  23729. * Do not need to consider axis 'inverse', which is auto processed by
  23730. * axis extent.
  23731. *
  23732. * @param {module:zrender/container/Group} group
  23733. * @param {Object} axisModel
  23734. * @param {Object} opt Standard axis parameters.
  23735. * @param {Array.<number>} opt.position [x, y]
  23736. * @param {number} opt.rotation by radian
  23737. * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle' or 'center'.
  23738. * @param {number} [opt.tickDirection=1] 1 or -1
  23739. * @param {number} [opt.labelDirection=1] 1 or -1
  23740. * @param {number} [opt.labelOffset=0] Usefull when onZero.
  23741. * @param {string} [opt.axisLabelShow] default get from axisModel.
  23742. * @param {string} [opt.axisName] default get from axisModel.
  23743. * @param {number} [opt.axisNameAvailableWidth]
  23744. * @param {number} [opt.labelRotate] by degree, default get from axisModel.
  23745. * @param {number} [opt.labelInterval] Default label interval when label
  23746. * interval from model is null or 'auto'.
  23747. * @param {number} [opt.strokeContainThreshold] Default label interval when label
  23748. * @param {number} [opt.nameTruncateMaxWidth]
  23749. */
  23750. var AxisBuilder = function (axisModel, opt) {
  23751. /**
  23752. * @readOnly
  23753. */
  23754. this.opt = opt;
  23755. /**
  23756. * @readOnly
  23757. */
  23758. this.axisModel = axisModel; // Default value
  23759. defaults(opt, {
  23760. labelOffset: 0,
  23761. nameDirection: 1,
  23762. tickDirection: 1,
  23763. labelDirection: 1,
  23764. silent: true
  23765. });
  23766. /**
  23767. * @readOnly
  23768. */
  23769. this.group = new Group(); // FIXME Not use a seperate text group?
  23770. var dumbGroup = new Group({
  23771. position: opt.position.slice(),
  23772. rotation: opt.rotation
  23773. }); // this.group.add(dumbGroup);
  23774. // this._dumbGroup = dumbGroup;
  23775. dumbGroup.updateTransform();
  23776. this._transform = dumbGroup.transform;
  23777. this._dumbGroup = dumbGroup;
  23778. };
  23779. AxisBuilder.prototype = {
  23780. constructor: AxisBuilder,
  23781. hasBuilder: function (name) {
  23782. return !!builders[name];
  23783. },
  23784. add: function (name) {
  23785. builders[name].call(this);
  23786. },
  23787. getGroup: function () {
  23788. return this.group;
  23789. }
  23790. };
  23791. var builders = {
  23792. /**
  23793. * @private
  23794. */
  23795. axisLine: function () {
  23796. var opt = this.opt;
  23797. var axisModel = this.axisModel;
  23798. if (!axisModel.get('axisLine.show')) {
  23799. return;
  23800. }
  23801. var extent = this.axisModel.axis.getExtent();
  23802. var matrix = this._transform;
  23803. var pt1 = [extent[0], 0];
  23804. var pt2 = [extent[1], 0];
  23805. if (matrix) {
  23806. applyTransform(pt1, pt1, matrix);
  23807. applyTransform(pt2, pt2, matrix);
  23808. }
  23809. var lineStyle = extend({
  23810. lineCap: 'round'
  23811. }, axisModel.getModel('axisLine.lineStyle').getLineStyle());
  23812. this.group.add(new Line(subPixelOptimizeLine({
  23813. // Id for animation
  23814. anid: 'line',
  23815. shape: {
  23816. x1: pt1[0],
  23817. y1: pt1[1],
  23818. x2: pt2[0],
  23819. y2: pt2[1]
  23820. },
  23821. style: lineStyle,
  23822. strokeContainThreshold: opt.strokeContainThreshold || 5,
  23823. silent: true,
  23824. z2: 1
  23825. })));
  23826. var arrows = axisModel.get('axisLine.symbol');
  23827. var arrowSize = axisModel.get('axisLine.symbolSize');
  23828. if (arrows != null) {
  23829. if (typeof arrows === 'string') {
  23830. // Use the same arrow for start and end point
  23831. arrows = [arrows, arrows];
  23832. }
  23833. if (typeof arrowSize === 'string' || typeof arrowSize === 'number') {
  23834. // Use the same size for width and height
  23835. arrowSize = [arrowSize, arrowSize];
  23836. }
  23837. var symbolWidth = arrowSize[0];
  23838. var symbolHeight = arrowSize[1];
  23839. each$1([[opt.rotation + Math.PI / 2, pt1], [opt.rotation - Math.PI / 2, pt2]], function (item, index) {
  23840. if (arrows[index] !== 'none' && arrows[index] != null) {
  23841. var symbol = createSymbol(arrows[index], -symbolWidth / 2, -symbolHeight / 2, symbolWidth, symbolHeight, lineStyle.stroke, true);
  23842. symbol.attr({
  23843. rotation: item[0],
  23844. position: item[1],
  23845. silent: true
  23846. });
  23847. this.group.add(symbol);
  23848. }
  23849. }, this);
  23850. }
  23851. },
  23852. /**
  23853. * @private
  23854. */
  23855. axisTickLabel: function () {
  23856. var axisModel = this.axisModel;
  23857. var opt = this.opt;
  23858. var tickEls = buildAxisTick(this, axisModel, opt);
  23859. var labelEls = buildAxisLabel(this, axisModel, opt);
  23860. fixMinMaxLabelShow(axisModel, labelEls, tickEls);
  23861. },
  23862. /**
  23863. * @private
  23864. */
  23865. axisName: function () {
  23866. var opt = this.opt;
  23867. var axisModel = this.axisModel;
  23868. var name = retrieve(opt.axisName, axisModel.get('name'));
  23869. if (!name) {
  23870. return;
  23871. }
  23872. var nameLocation = axisModel.get('nameLocation');
  23873. var nameDirection = opt.nameDirection;
  23874. var textStyleModel = axisModel.getModel('nameTextStyle');
  23875. var gap = axisModel.get('nameGap') || 0;
  23876. var extent = this.axisModel.axis.getExtent();
  23877. var gapSignal = extent[0] > extent[1] ? -1 : 1;
  23878. var pos = [nameLocation === 'start' ? extent[0] - gapSignal * gap : nameLocation === 'end' ? extent[1] + gapSignal * gap : (extent[0] + extent[1]) / 2, // 'middle'
  23879. // Reuse labelOffset.
  23880. isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0];
  23881. var labelLayout;
  23882. var nameRotation = axisModel.get('nameRotate');
  23883. if (nameRotation != null) {
  23884. nameRotation = nameRotation * PI$2 / 180; // To radian.
  23885. }
  23886. var axisNameAvailableWidth;
  23887. if (isNameLocationCenter(nameLocation)) {
  23888. labelLayout = innerTextLayout(opt.rotation, nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.
  23889. nameDirection);
  23890. } else {
  23891. labelLayout = endTextLayout(opt, nameLocation, nameRotation || 0, extent);
  23892. axisNameAvailableWidth = opt.axisNameAvailableWidth;
  23893. if (axisNameAvailableWidth != null) {
  23894. axisNameAvailableWidth = Math.abs(axisNameAvailableWidth / Math.sin(labelLayout.rotation));
  23895. !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);
  23896. }
  23897. }
  23898. var textFont = textStyleModel.getFont();
  23899. var truncateOpt = axisModel.get('nameTruncate', true) || {};
  23900. var ellipsis = truncateOpt.ellipsis;
  23901. var maxWidth = retrieve(opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth); // FIXME
  23902. // truncate rich text? (consider performance)
  23903. var truncatedText = ellipsis != null && maxWidth != null ? truncateText$1(name, maxWidth, textFont, ellipsis, {
  23904. minChar: 2,
  23905. placeholder: truncateOpt.placeholder
  23906. }) : name;
  23907. var tooltipOpt = axisModel.get('tooltip', true);
  23908. var mainType = axisModel.mainType;
  23909. var formatterParams = {
  23910. componentType: mainType,
  23911. name: name,
  23912. $vars: ['name']
  23913. };
  23914. formatterParams[mainType + 'Index'] = axisModel.componentIndex;
  23915. var textEl = new Text({
  23916. // Id for animation
  23917. anid: 'name',
  23918. __fullText: name,
  23919. __truncatedText: truncatedText,
  23920. position: pos,
  23921. rotation: labelLayout.rotation,
  23922. silent: isSilent(axisModel),
  23923. z2: 1,
  23924. tooltip: tooltipOpt && tooltipOpt.show ? extend({
  23925. content: name,
  23926. formatter: function () {
  23927. return name;
  23928. },
  23929. formatterParams: formatterParams
  23930. }, tooltipOpt) : null
  23931. });
  23932. setTextStyle(textEl.style, textStyleModel, {
  23933. text: truncatedText,
  23934. textFont: textFont,
  23935. textFill: textStyleModel.getTextColor() || axisModel.get('axisLine.lineStyle.color'),
  23936. textAlign: labelLayout.textAlign,
  23937. textVerticalAlign: labelLayout.textVerticalAlign
  23938. });
  23939. if (axisModel.get('triggerEvent')) {
  23940. textEl.eventData = makeAxisEventDataBase(axisModel);
  23941. textEl.eventData.targetType = 'axisName';
  23942. textEl.eventData.name = name;
  23943. } // FIXME
  23944. this._dumbGroup.add(textEl);
  23945. textEl.updateTransform();
  23946. this.group.add(textEl);
  23947. textEl.decomposeTransform();
  23948. }
  23949. };
  23950. /**
  23951. * @public
  23952. * @static
  23953. * @param {Object} opt
  23954. * @param {number} axisRotation in radian
  23955. * @param {number} textRotation in radian
  23956. * @param {number} direction
  23957. * @return {Object} {
  23958. * rotation, // according to axis
  23959. * textAlign,
  23960. * textVerticalAlign
  23961. * }
  23962. */
  23963. var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {
  23964. var rotationDiff = remRadian(textRotation - axisRotation);
  23965. var textAlign;
  23966. var textVerticalAlign;
  23967. if (isRadianAroundZero(rotationDiff)) {
  23968. // Label is parallel with axis line.
  23969. textVerticalAlign = direction > 0 ? 'top' : 'bottom';
  23970. textAlign = 'center';
  23971. } else if (isRadianAroundZero(rotationDiff - PI$2)) {
  23972. // Label is inverse parallel with axis line.
  23973. textVerticalAlign = direction > 0 ? 'bottom' : 'top';
  23974. textAlign = 'center';
  23975. } else {
  23976. textVerticalAlign = 'middle';
  23977. if (rotationDiff > 0 && rotationDiff < PI$2) {
  23978. textAlign = direction > 0 ? 'right' : 'left';
  23979. } else {
  23980. textAlign = direction > 0 ? 'left' : 'right';
  23981. }
  23982. }
  23983. return {
  23984. rotation: rotationDiff,
  23985. textAlign: textAlign,
  23986. textVerticalAlign: textVerticalAlign
  23987. };
  23988. };
  23989. function endTextLayout(opt, textPosition, textRotate, extent) {
  23990. var rotationDiff = remRadian(textRotate - opt.rotation);
  23991. var textAlign;
  23992. var textVerticalAlign;
  23993. var inverse = extent[0] > extent[1];
  23994. var onLeft = textPosition === 'start' && !inverse || textPosition !== 'start' && inverse;
  23995. if (isRadianAroundZero(rotationDiff - PI$2 / 2)) {
  23996. textVerticalAlign = onLeft ? 'bottom' : 'top';
  23997. textAlign = 'center';
  23998. } else if (isRadianAroundZero(rotationDiff - PI$2 * 1.5)) {
  23999. textVerticalAlign = onLeft ? 'top' : 'bottom';
  24000. textAlign = 'center';
  24001. } else {
  24002. textVerticalAlign = 'middle';
  24003. if (rotationDiff < PI$2 * 1.5 && rotationDiff > PI$2 / 2) {
  24004. textAlign = onLeft ? 'left' : 'right';
  24005. } else {
  24006. textAlign = onLeft ? 'right' : 'left';
  24007. }
  24008. }
  24009. return {
  24010. rotation: rotationDiff,
  24011. textAlign: textAlign,
  24012. textVerticalAlign: textVerticalAlign
  24013. };
  24014. }
  24015. function isSilent(axisModel) {
  24016. var tooltipOpt = axisModel.get('tooltip');
  24017. return axisModel.get('silent') // Consider mouse cursor, add these restrictions.
  24018. || !(axisModel.get('triggerEvent') || tooltipOpt && tooltipOpt.show);
  24019. }
  24020. function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {
  24021. // If min or max are user set, we need to check
  24022. // If the tick on min(max) are overlap on their neighbour tick
  24023. // If they are overlapped, we need to hide the min(max) tick label
  24024. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  24025. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel'); // FIXME
  24026. // Have not consider onBand yet, where tick els is more than label els.
  24027. labelEls = labelEls || [];
  24028. tickEls = tickEls || [];
  24029. var firstLabel = labelEls[0];
  24030. var nextLabel = labelEls[1];
  24031. var lastLabel = labelEls[labelEls.length - 1];
  24032. var prevLabel = labelEls[labelEls.length - 2];
  24033. var firstTick = tickEls[0];
  24034. var nextTick = tickEls[1];
  24035. var lastTick = tickEls[tickEls.length - 1];
  24036. var prevTick = tickEls[tickEls.length - 2];
  24037. if (showMinLabel === false) {
  24038. ignoreEl(firstLabel);
  24039. ignoreEl(firstTick);
  24040. } else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {
  24041. if (showMinLabel) {
  24042. ignoreEl(nextLabel);
  24043. ignoreEl(nextTick);
  24044. } else {
  24045. ignoreEl(firstLabel);
  24046. ignoreEl(firstTick);
  24047. }
  24048. }
  24049. if (showMaxLabel === false) {
  24050. ignoreEl(lastLabel);
  24051. ignoreEl(lastTick);
  24052. } else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {
  24053. if (showMaxLabel) {
  24054. ignoreEl(prevLabel);
  24055. ignoreEl(prevTick);
  24056. } else {
  24057. ignoreEl(lastLabel);
  24058. ignoreEl(lastTick);
  24059. }
  24060. }
  24061. }
  24062. function ignoreEl(el) {
  24063. el && (el.ignore = true);
  24064. }
  24065. function isTwoLabelOverlapped(current, next, labelLayout) {
  24066. // current and next has the same rotation.
  24067. var firstRect = current && current.getBoundingRect().clone();
  24068. var nextRect = next && next.getBoundingRect().clone();
  24069. if (!firstRect || !nextRect) {
  24070. return;
  24071. } // When checking intersect of two rotated labels, we use mRotationBack
  24072. // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.
  24073. var mRotationBack = identity([]);
  24074. rotate(mRotationBack, mRotationBack, -current.rotation);
  24075. firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));
  24076. nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));
  24077. return firstRect.intersect(nextRect);
  24078. }
  24079. function isNameLocationCenter(nameLocation) {
  24080. return nameLocation === 'middle' || nameLocation === 'center';
  24081. }
  24082. /**
  24083. * @static
  24084. */
  24085. var ifIgnoreOnTick$1 = AxisBuilder.ifIgnoreOnTick = function (axis, i, interval, ticksCnt, showMinLabel, showMaxLabel) {
  24086. if (i === 0 && showMinLabel || i === ticksCnt - 1 && showMaxLabel) {
  24087. return false;
  24088. } // FIXME
  24089. // Have not consider label overlap (if label is too long) yet.
  24090. var rawTick;
  24091. var scale$$1 = axis.scale;
  24092. return scale$$1.type === 'ordinal' && (typeof interval === 'function' ? (rawTick = scale$$1.getTicks()[i], !interval(rawTick, scale$$1.getLabel(rawTick))) : i % (interval + 1));
  24093. };
  24094. /**
  24095. * @static
  24096. */
  24097. var getInterval$1 = AxisBuilder.getInterval = function (model, labelInterval) {
  24098. var interval = model.get('interval');
  24099. if (interval == null || interval == 'auto') {
  24100. interval = labelInterval;
  24101. }
  24102. return interval;
  24103. };
  24104. function buildAxisTick(axisBuilder, axisModel, opt) {
  24105. var axis = axisModel.axis;
  24106. if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) {
  24107. return;
  24108. }
  24109. var tickModel = axisModel.getModel('axisTick');
  24110. var lineStyleModel = tickModel.getModel('lineStyle');
  24111. var tickLen = tickModel.get('length');
  24112. var tickInterval = getInterval$1(tickModel, opt.labelInterval);
  24113. var ticksCoords = axis.getTicksCoords(tickModel.get('alignWithLabel')); // FIXME
  24114. // Corresponds to ticksCoords ?
  24115. var ticks = axis.scale.getTicks();
  24116. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  24117. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  24118. var pt1 = [];
  24119. var pt2 = [];
  24120. var matrix = axisBuilder._transform;
  24121. var tickEls = [];
  24122. var ticksCnt = ticksCoords.length;
  24123. for (var i = 0; i < ticksCnt; i++) {
  24124. // Only ordinal scale support tick interval
  24125. if (ifIgnoreOnTick$1(axis, i, tickInterval, ticksCnt, showMinLabel, showMaxLabel)) {
  24126. continue;
  24127. }
  24128. var tickCoord = ticksCoords[i];
  24129. pt1[0] = tickCoord;
  24130. pt1[1] = 0;
  24131. pt2[0] = tickCoord;
  24132. pt2[1] = opt.tickDirection * tickLen;
  24133. if (matrix) {
  24134. applyTransform(pt1, pt1, matrix);
  24135. applyTransform(pt2, pt2, matrix);
  24136. } // Tick line, Not use group transform to have better line draw
  24137. var tickEl = new Line(subPixelOptimizeLine({
  24138. // Id for animation
  24139. anid: 'tick_' + ticks[i],
  24140. shape: {
  24141. x1: pt1[0],
  24142. y1: pt1[1],
  24143. x2: pt2[0],
  24144. y2: pt2[1]
  24145. },
  24146. style: defaults(lineStyleModel.getLineStyle(), {
  24147. stroke: axisModel.get('axisLine.lineStyle.color')
  24148. }),
  24149. z2: 2,
  24150. silent: true
  24151. }));
  24152. axisBuilder.group.add(tickEl);
  24153. tickEls.push(tickEl);
  24154. }
  24155. return tickEls;
  24156. }
  24157. function buildAxisLabel(axisBuilder, axisModel, opt) {
  24158. var axis = axisModel.axis;
  24159. var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show'));
  24160. if (!show || axis.scale.isBlank()) {
  24161. return;
  24162. }
  24163. var labelModel = axisModel.getModel('axisLabel');
  24164. var labelMargin = labelModel.get('margin');
  24165. var ticks = axis.scale.getTicks();
  24166. var labels = axisModel.getFormattedLabels(); // Special label rotate.
  24167. var labelRotation = (retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) * PI$2 / 180;
  24168. var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);
  24169. var categoryData = axisModel.get('data');
  24170. var labelEls = [];
  24171. var silent = isSilent(axisModel);
  24172. var triggerEvent = axisModel.get('triggerEvent');
  24173. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  24174. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  24175. each$1(ticks, function (tickVal, index) {
  24176. if (ifIgnoreOnTick$1(axis, index, opt.labelInterval, ticks.length, showMinLabel, showMaxLabel)) {
  24177. return;
  24178. }
  24179. var itemLabelModel = labelModel;
  24180. if (categoryData && categoryData[tickVal] && categoryData[tickVal].textStyle) {
  24181. itemLabelModel = new Model(categoryData[tickVal].textStyle, labelModel, axisModel.ecModel);
  24182. }
  24183. var textColor = itemLabelModel.getTextColor() || axisModel.get('axisLine.lineStyle.color');
  24184. var tickCoord = axis.dataToCoord(tickVal);
  24185. var pos = [tickCoord, opt.labelOffset + opt.labelDirection * labelMargin];
  24186. var labelStr = axis.scale.getLabel(tickVal);
  24187. var textEl = new Text({
  24188. // Id for animation
  24189. anid: 'label_' + tickVal,
  24190. position: pos,
  24191. rotation: labelLayout.rotation,
  24192. silent: silent,
  24193. z2: 10
  24194. });
  24195. setTextStyle(textEl.style, itemLabelModel, {
  24196. text: labels[index],
  24197. textAlign: itemLabelModel.getShallow('align', true) || labelLayout.textAlign,
  24198. textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true) || itemLabelModel.getShallow('baseline', true) || labelLayout.textVerticalAlign,
  24199. textFill: typeof textColor === 'function' ? textColor( // (1) In category axis with data zoom, tick is not the original
  24200. // index of axis.data. So tick should not be exposed to user
  24201. // in category axis.
  24202. // (2) Compatible with previous version, which always returns labelStr.
  24203. // But in interval scale labelStr is like '223,445', which maked
  24204. // user repalce ','. So we modify it to return original val but remain
  24205. // it as 'string' to avoid error in replacing.
  24206. axis.type === 'category' ? labelStr : axis.type === 'value' ? tickVal + '' : tickVal, index) : textColor
  24207. }); // Pack data for mouse event
  24208. if (triggerEvent) {
  24209. textEl.eventData = makeAxisEventDataBase(axisModel);
  24210. textEl.eventData.targetType = 'axisLabel';
  24211. textEl.eventData.value = labelStr;
  24212. } // FIXME
  24213. axisBuilder._dumbGroup.add(textEl);
  24214. textEl.updateTransform();
  24215. labelEls.push(textEl);
  24216. axisBuilder.group.add(textEl);
  24217. textEl.decomposeTransform();
  24218. });
  24219. return labelEls;
  24220. }
  24221. var each$9 = each$1;
  24222. var curry$1 = curry; // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
  24223. // allAxesInfo should be updated when setOption performed.
  24224. function collect(ecModel, api) {
  24225. var result = {
  24226. /**
  24227. * key: makeKey(axis.model)
  24228. * value: {
  24229. * axis,
  24230. * coordSys,
  24231. * axisPointerModel,
  24232. * triggerTooltip,
  24233. * involveSeries,
  24234. * snap,
  24235. * seriesModels,
  24236. * seriesDataCount
  24237. * }
  24238. */
  24239. axesInfo: {},
  24240. seriesInvolved: false,
  24241. /**
  24242. * key: makeKey(coordSys.model)
  24243. * value: Object: key makeKey(axis.model), value: axisInfo
  24244. */
  24245. coordSysAxesInfo: {},
  24246. coordSysMap: {}
  24247. };
  24248. collectAxesInfo(result, ecModel, api); // Check seriesInvolved for performance, in case too many series in some chart.
  24249. result.seriesInvolved && collectSeriesInfo(result, ecModel);
  24250. return result;
  24251. }
  24252. function collectAxesInfo(result, ecModel, api) {
  24253. var globalTooltipModel = ecModel.getComponent('tooltip');
  24254. var globalAxisPointerModel = ecModel.getComponent('axisPointer'); // links can only be set on global.
  24255. var linksOption = globalAxisPointerModel.get('link', true) || [];
  24256. var linkGroups = []; // Collect axes info.
  24257. each$9(api.getCoordinateSystems(), function (coordSys) {
  24258. // Some coordinate system do not support axes, like geo.
  24259. if (!coordSys.axisPointerEnabled) {
  24260. return;
  24261. }
  24262. var coordSysKey = makeKey(coordSys.model);
  24263. var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};
  24264. result.coordSysMap[coordSysKey] = coordSys; // Set tooltip (like 'cross') is a convienent way to show axisPointer
  24265. // for user. So we enable seting tooltip on coordSys model.
  24266. var coordSysModel = coordSys.model;
  24267. var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);
  24268. each$9(coordSys.getAxes(), curry$1(saveTooltipAxisInfo, false, null)); // If axis tooltip used, choose tooltip axis for each coordSys.
  24269. // Notice this case: coordSys is `grid` but not `cartesian2D` here.
  24270. if (coordSys.getTooltipAxes && globalTooltipModel // If tooltip.showContent is set as false, tooltip will not
  24271. // show but axisPointer will show as normal.
  24272. && baseTooltipModel.get('show')) {
  24273. // Compatible with previous logic. But series.tooltip.trigger: 'axis'
  24274. // or series.data[n].tooltip.trigger: 'axis' are not support any more.
  24275. var triggerAxis = baseTooltipModel.get('trigger') === 'axis';
  24276. var cross = baseTooltipModel.get('axisPointer.type') === 'cross';
  24277. var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis'));
  24278. if (triggerAxis || cross) {
  24279. each$9(tooltipAxes.baseAxes, curry$1(saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis));
  24280. }
  24281. if (cross) {
  24282. each$9(tooltipAxes.otherAxes, curry$1(saveTooltipAxisInfo, 'cross', false));
  24283. }
  24284. } // fromTooltip: true | false | 'cross'
  24285. // triggerTooltip: true | false | null
  24286. function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {
  24287. var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);
  24288. var axisPointerShow = axisPointerModel.get('show');
  24289. if (!axisPointerShow || axisPointerShow === 'auto' && !fromTooltip && !isHandleTrigger(axisPointerModel)) {
  24290. return;
  24291. }
  24292. if (triggerTooltip == null) {
  24293. triggerTooltip = axisPointerModel.get('triggerTooltip');
  24294. }
  24295. axisPointerModel = fromTooltip ? makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) : axisPointerModel;
  24296. var snap = axisPointerModel.get('snap');
  24297. var key = makeKey(axis.model);
  24298. var involveSeries = triggerTooltip || snap || axis.type === 'category'; // If result.axesInfo[key] exist, override it (tooltip has higher priority).
  24299. var axisInfo = result.axesInfo[key] = {
  24300. key: key,
  24301. axis: axis,
  24302. coordSys: coordSys,
  24303. axisPointerModel: axisPointerModel,
  24304. triggerTooltip: triggerTooltip,
  24305. involveSeries: involveSeries,
  24306. snap: snap,
  24307. useHandle: isHandleTrigger(axisPointerModel),
  24308. seriesModels: []
  24309. };
  24310. axesInfoInCoordSys[key] = axisInfo;
  24311. result.seriesInvolved |= involveSeries;
  24312. var groupIndex = getLinkGroupIndex(linksOption, axis);
  24313. if (groupIndex != null) {
  24314. var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {
  24315. axesInfo: {}
  24316. });
  24317. linkGroup.axesInfo[key] = axisInfo;
  24318. linkGroup.mapper = linksOption[groupIndex].mapper;
  24319. axisInfo.linkGroup = linkGroup;
  24320. }
  24321. }
  24322. });
  24323. }
  24324. function makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) {
  24325. var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');
  24326. var volatileOption = {};
  24327. each$9(['type', 'snap', 'lineStyle', 'shadowStyle', 'label', 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'], function (field) {
  24328. volatileOption[field] = clone(tooltipAxisPointerModel.get(field));
  24329. }); // category axis do not auto snap, otherwise some tick that do not
  24330. // has value can not be hovered. value/time/log axis default snap if
  24331. // triggered from tooltip and trigger tooltip.
  24332. volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; // Compatibel with previous behavior, tooltip axis do not show label by default.
  24333. // Only these properties can be overrided from tooltip to axisPointer.
  24334. if (tooltipAxisPointerModel.get('type') === 'cross') {
  24335. volatileOption.type = 'line';
  24336. }
  24337. var labelOption = volatileOption.label || (volatileOption.label = {}); // Follow the convention, do not show label when triggered by tooltip by default.
  24338. labelOption.show == null && (labelOption.show = false);
  24339. if (fromTooltip === 'cross') {
  24340. // When 'cross', both axes show labels.
  24341. labelOption.show = true; // If triggerTooltip, this is a base axis, which should better not use cross style
  24342. // (cross style is dashed by default)
  24343. if (!triggerTooltip) {
  24344. var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');
  24345. crossStyle && defaults(labelOption, crossStyle.textStyle);
  24346. }
  24347. }
  24348. return axis.model.getModel('axisPointer', new Model(volatileOption, globalAxisPointerModel, ecModel));
  24349. }
  24350. function collectSeriesInfo(result, ecModel) {
  24351. // Prepare data for axis trigger
  24352. ecModel.eachSeries(function (seriesModel) {
  24353. // Notice this case: this coordSys is `cartesian2D` but not `grid`.
  24354. var coordSys = seriesModel.coordinateSystem;
  24355. var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true);
  24356. var seriesTooltipShow = seriesModel.get('tooltip.show', true);
  24357. if (!coordSys || seriesTooltipTrigger === 'none' || seriesTooltipTrigger === false || seriesTooltipTrigger === 'item' || seriesTooltipShow === false || seriesModel.get('axisPointer.show', true) === false) {
  24358. return;
  24359. }
  24360. each$9(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {
  24361. var axis = axisInfo.axis;
  24362. if (coordSys.getAxis(axis.dim) === axis) {
  24363. axisInfo.seriesModels.push(seriesModel);
  24364. axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);
  24365. axisInfo.seriesDataCount += seriesModel.getData().count();
  24366. }
  24367. });
  24368. }, this);
  24369. }
  24370. /**
  24371. * For example:
  24372. * {
  24373. * axisPointer: {
  24374. * links: [{
  24375. * xAxisIndex: [2, 4],
  24376. * yAxisIndex: 'all'
  24377. * }, {
  24378. * xAxisId: ['a5', 'a7'],
  24379. * xAxisName: 'xxx'
  24380. * }]
  24381. * }
  24382. * }
  24383. */
  24384. function getLinkGroupIndex(linksOption, axis) {
  24385. var axisModel = axis.model;
  24386. var dim = axis.dim;
  24387. for (var i = 0; i < linksOption.length; i++) {
  24388. var linkOption = linksOption[i] || {};
  24389. if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)) {
  24390. return i;
  24391. }
  24392. }
  24393. }
  24394. function checkPropInLink(linkPropValue, axisPropValue) {
  24395. return linkPropValue === 'all' || isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0 || linkPropValue === axisPropValue;
  24396. }
  24397. function fixValue(axisModel) {
  24398. var axisInfo = getAxisInfo(axisModel);
  24399. if (!axisInfo) {
  24400. return;
  24401. }
  24402. var axisPointerModel = axisInfo.axisPointerModel;
  24403. var scale = axisInfo.axis.scale;
  24404. var option = axisPointerModel.option;
  24405. var status = axisPointerModel.get('status');
  24406. var value = axisPointerModel.get('value'); // Parse init value for category and time axis.
  24407. if (value != null) {
  24408. value = scale.parse(value);
  24409. }
  24410. var useHandle = isHandleTrigger(axisPointerModel); // If `handle` used, `axisPointer` will always be displayed, so value
  24411. // and status should be initialized.
  24412. if (status == null) {
  24413. option.status = useHandle ? 'show' : 'hide';
  24414. }
  24415. var extent = scale.getExtent().slice();
  24416. extent[0] > extent[1] && extent.reverse();
  24417. if ( // Pick a value on axis when initializing.
  24418. value == null // If both `handle` and `dataZoom` are used, value may be out of axis extent,
  24419. // where we should re-pick a value to keep `handle` displaying normally.
  24420. || value > extent[1]) {
  24421. // Make handle displayed on the end of the axis when init, which looks better.
  24422. value = extent[1];
  24423. }
  24424. if (value < extent[0]) {
  24425. value = extent[0];
  24426. }
  24427. option.value = value;
  24428. if (useHandle) {
  24429. option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';
  24430. }
  24431. }
  24432. function getAxisInfo(axisModel) {
  24433. var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;
  24434. return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];
  24435. }
  24436. function getAxisPointerModel(axisModel) {
  24437. var axisInfo = getAxisInfo(axisModel);
  24438. return axisInfo && axisInfo.axisPointerModel;
  24439. }
  24440. function isHandleTrigger(axisPointerModel) {
  24441. return !!axisPointerModel.get('handle.show');
  24442. }
  24443. /**
  24444. * @param {module:echarts/model/Model} model
  24445. * @return {string} unique key
  24446. */
  24447. function makeKey(model) {
  24448. return model.type + '||' + model.id;
  24449. }
  24450. /**
  24451. * Base class of AxisView.
  24452. */
  24453. var AxisView = extendComponentView({
  24454. type: 'axis',
  24455. /**
  24456. * @private
  24457. */
  24458. _axisPointer: null,
  24459. /**
  24460. * @protected
  24461. * @type {string}
  24462. */
  24463. axisPointerClass: null,
  24464. /**
  24465. * @override
  24466. */
  24467. render: function (axisModel, ecModel, api, payload) {
  24468. // FIXME
  24469. // This process should proformed after coordinate systems updated
  24470. // (axis scale updated), and should be performed each time update.
  24471. // So put it here temporarily, although it is not appropriate to
  24472. // put a model-writing procedure in `view`.
  24473. this.axisPointerClass && fixValue(axisModel);
  24474. AxisView.superApply(this, 'render', arguments);
  24475. updateAxisPointer(this, axisModel, ecModel, api, payload, true);
  24476. },
  24477. /**
  24478. * Action handler.
  24479. * @public
  24480. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  24481. * @param {module:echarts/model/Global} ecModel
  24482. * @param {module:echarts/ExtensionAPI} api
  24483. * @param {Object} payload
  24484. */
  24485. updateAxisPointer: function (axisModel, ecModel, api, payload, force) {
  24486. updateAxisPointer(this, axisModel, ecModel, api, payload, false);
  24487. },
  24488. /**
  24489. * @override
  24490. */
  24491. remove: function (ecModel, api) {
  24492. var axisPointer = this._axisPointer;
  24493. axisPointer && axisPointer.remove(api);
  24494. AxisView.superApply(this, 'remove', arguments);
  24495. },
  24496. /**
  24497. * @override
  24498. */
  24499. dispose: function (ecModel, api) {
  24500. disposeAxisPointer(this, api);
  24501. AxisView.superApply(this, 'dispose', arguments);
  24502. }
  24503. });
  24504. function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) {
  24505. var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass);
  24506. if (!Clazz) {
  24507. return;
  24508. }
  24509. var axisPointerModel = getAxisPointerModel(axisModel);
  24510. axisPointerModel ? (axisView._axisPointer || (axisView._axisPointer = new Clazz())).render(axisModel, axisPointerModel, api, forceRender) : disposeAxisPointer(axisView, api);
  24511. }
  24512. function disposeAxisPointer(axisView, ecModel, api) {
  24513. var axisPointer = axisView._axisPointer;
  24514. axisPointer && axisPointer.dispose(ecModel, api);
  24515. axisView._axisPointer = null;
  24516. }
  24517. var axisPointerClazz = [];
  24518. AxisView.registerAxisPointerClass = function (type, clazz) {
  24519. if (true) {
  24520. if (axisPointerClazz[type]) {
  24521. throw new Error('axisPointer ' + type + ' exists');
  24522. }
  24523. }
  24524. axisPointerClazz[type] = clazz;
  24525. };
  24526. AxisView.getAxisPointerClass = function (type) {
  24527. return type && axisPointerClazz[type];
  24528. };
  24529. /**
  24530. * @param {Object} opt {labelInside}
  24531. * @return {Object} {
  24532. * position, rotation, labelDirection, labelOffset,
  24533. * tickDirection, labelRotate, labelInterval, z2
  24534. * }
  24535. */
  24536. function layout(gridModel, axisModel, opt) {
  24537. opt = opt || {};
  24538. var grid = gridModel.coordinateSystem;
  24539. var axis = axisModel.axis;
  24540. var layout = {};
  24541. var rawAxisPosition = axis.position;
  24542. var axisPosition = axis.onZero ? 'onZero' : rawAxisPosition;
  24543. var axisDim = axis.dim;
  24544. var rect = grid.getRect();
  24545. var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];
  24546. var idx = {
  24547. left: 0,
  24548. right: 1,
  24549. top: 0,
  24550. bottom: 1,
  24551. onZero: 2
  24552. };
  24553. var axisOffset = axisModel.get('offset') || 0;
  24554. var posBound = axisDim === 'x' ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];
  24555. if (axis.onZero) {
  24556. var otherAxis = grid.getAxis(axisDim === 'x' ? 'y' : 'x', axis.onZeroAxisIndex);
  24557. var onZeroCoord = otherAxis.toGlobalCoord(otherAxis.dataToCoord(0));
  24558. posBound[idx['onZero']] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);
  24559. } // Axis position
  24560. layout.position = [axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]]; // Axis rotation
  24561. layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); // Tick and label direction, x y is axisDim
  24562. var dirMap = {
  24563. top: -1,
  24564. bottom: 1,
  24565. left: -1,
  24566. right: 1
  24567. };
  24568. layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];
  24569. layout.labelOffset = axis.onZero ? posBound[idx[rawAxisPosition]] - posBound[idx['onZero']] : 0;
  24570. if (axisModel.get('axisTick.inside')) {
  24571. layout.tickDirection = -layout.tickDirection;
  24572. }
  24573. if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {
  24574. layout.labelDirection = -layout.labelDirection;
  24575. } // Special label rotation
  24576. var labelRotate = axisModel.get('axisLabel.rotate');
  24577. layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; // label interval when auto mode.
  24578. layout.labelInterval = axis.getLabelInterval(); // Over splitLine and splitArea
  24579. layout.z2 = 1;
  24580. return layout;
  24581. }
  24582. var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick;
  24583. var getInterval = AxisBuilder.getInterval;
  24584. var axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName'];
  24585. var selfBuilderAttrs = ['splitArea', 'splitLine']; // function getAlignWithLabel(model, axisModel) {
  24586. // var alignWithLabel = model.get('alignWithLabel');
  24587. // if (alignWithLabel === 'auto') {
  24588. // alignWithLabel = axisModel.get('axisTick.alignWithLabel');
  24589. // }
  24590. // return alignWithLabel;
  24591. // }
  24592. var CartesianAxisView = AxisView.extend({
  24593. type: 'cartesianAxis',
  24594. axisPointerClass: 'CartesianAxisPointer',
  24595. /**
  24596. * @override
  24597. */
  24598. render: function (axisModel, ecModel, api, payload) {
  24599. this.group.removeAll();
  24600. var oldAxisGroup = this._axisGroup;
  24601. this._axisGroup = new Group();
  24602. this.group.add(this._axisGroup);
  24603. if (!axisModel.get('show')) {
  24604. return;
  24605. }
  24606. var gridModel = axisModel.getCoordSysModel();
  24607. var layout$$1 = layout(gridModel, axisModel);
  24608. var axisBuilder = new AxisBuilder(axisModel, layout$$1);
  24609. each$1(axisBuilderAttrs, axisBuilder.add, axisBuilder);
  24610. this._axisGroup.add(axisBuilder.getGroup());
  24611. each$1(selfBuilderAttrs, function (name) {
  24612. if (axisModel.get(name + '.show')) {
  24613. this['_' + name](axisModel, gridModel, layout$$1.labelInterval);
  24614. }
  24615. }, this);
  24616. groupTransition(oldAxisGroup, this._axisGroup, axisModel);
  24617. CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);
  24618. },
  24619. /**
  24620. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  24621. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  24622. * @param {number|Function} labelInterval
  24623. * @private
  24624. */
  24625. _splitLine: function (axisModel, gridModel, labelInterval) {
  24626. var axis = axisModel.axis;
  24627. if (axis.scale.isBlank()) {
  24628. return;
  24629. }
  24630. var splitLineModel = axisModel.getModel('splitLine');
  24631. var lineStyleModel = splitLineModel.getModel('lineStyle');
  24632. var lineColors = lineStyleModel.get('color');
  24633. var lineInterval = getInterval(splitLineModel, labelInterval);
  24634. lineColors = isArray(lineColors) ? lineColors : [lineColors];
  24635. var gridRect = gridModel.coordinateSystem.getRect();
  24636. var isHorizontal = axis.isHorizontal();
  24637. var lineCount = 0;
  24638. var ticksCoords = axis.getTicksCoords();
  24639. var ticks = axis.scale.getTicks();
  24640. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  24641. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  24642. var p1 = [];
  24643. var p2 = []; // Simple optimization
  24644. // Batching the lines if color are the same
  24645. var lineStyle = lineStyleModel.getLineStyle();
  24646. for (var i = 0; i < ticksCoords.length; i++) {
  24647. if (ifIgnoreOnTick(axis, i, lineInterval, ticksCoords.length, showMinLabel, showMaxLabel)) {
  24648. continue;
  24649. }
  24650. var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
  24651. if (isHorizontal) {
  24652. p1[0] = tickCoord;
  24653. p1[1] = gridRect.y;
  24654. p2[0] = tickCoord;
  24655. p2[1] = gridRect.y + gridRect.height;
  24656. } else {
  24657. p1[0] = gridRect.x;
  24658. p1[1] = tickCoord;
  24659. p2[0] = gridRect.x + gridRect.width;
  24660. p2[1] = tickCoord;
  24661. }
  24662. var colorIndex = lineCount++ % lineColors.length;
  24663. this._axisGroup.add(new Line(subPixelOptimizeLine({
  24664. anid: 'line_' + ticks[i],
  24665. shape: {
  24666. x1: p1[0],
  24667. y1: p1[1],
  24668. x2: p2[0],
  24669. y2: p2[1]
  24670. },
  24671. style: defaults({
  24672. stroke: lineColors[colorIndex]
  24673. }, lineStyle),
  24674. silent: true
  24675. })));
  24676. }
  24677. },
  24678. /**
  24679. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  24680. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  24681. * @param {number|Function} labelInterval
  24682. * @private
  24683. */
  24684. _splitArea: function (axisModel, gridModel, labelInterval) {
  24685. var axis = axisModel.axis;
  24686. if (axis.scale.isBlank()) {
  24687. return;
  24688. }
  24689. var splitAreaModel = axisModel.getModel('splitArea');
  24690. var areaStyleModel = splitAreaModel.getModel('areaStyle');
  24691. var areaColors = areaStyleModel.get('color');
  24692. var gridRect = gridModel.coordinateSystem.getRect();
  24693. var ticksCoords = axis.getTicksCoords();
  24694. var ticks = axis.scale.getTicks();
  24695. var prevX = axis.toGlobalCoord(ticksCoords[0]);
  24696. var prevY = axis.toGlobalCoord(ticksCoords[0]);
  24697. var count = 0;
  24698. var areaInterval = getInterval(splitAreaModel, labelInterval);
  24699. var areaStyle = areaStyleModel.getAreaStyle();
  24700. areaColors = isArray(areaColors) ? areaColors : [areaColors];
  24701. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  24702. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  24703. for (var i = 1; i < ticksCoords.length; i++) {
  24704. if (ifIgnoreOnTick(axis, i, areaInterval, ticksCoords.length, showMinLabel, showMaxLabel)) {
  24705. continue;
  24706. }
  24707. var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
  24708. var x;
  24709. var y;
  24710. var width;
  24711. var height;
  24712. if (axis.isHorizontal()) {
  24713. x = prevX;
  24714. y = gridRect.y;
  24715. width = tickCoord - x;
  24716. height = gridRect.height;
  24717. } else {
  24718. x = gridRect.x;
  24719. y = prevY;
  24720. width = gridRect.width;
  24721. height = tickCoord - y;
  24722. }
  24723. var colorIndex = count++ % areaColors.length;
  24724. this._axisGroup.add(new Rect({
  24725. anid: 'area_' + ticks[i],
  24726. shape: {
  24727. x: x,
  24728. y: y,
  24729. width: width,
  24730. height: height
  24731. },
  24732. style: defaults({
  24733. fill: areaColors[colorIndex]
  24734. }, areaStyle),
  24735. silent: true
  24736. }));
  24737. prevX = x + width;
  24738. prevY = y + height;
  24739. }
  24740. }
  24741. });
  24742. CartesianAxisView.extend({
  24743. type: 'xAxis'
  24744. });
  24745. CartesianAxisView.extend({
  24746. type: 'yAxis'
  24747. }); // Grid view
  24748. extendComponentView({
  24749. type: 'grid',
  24750. render: function (gridModel, ecModel) {
  24751. this.group.removeAll();
  24752. if (gridModel.get('show')) {
  24753. this.group.add(new Rect({
  24754. shape: gridModel.coordinateSystem.getRect(),
  24755. style: defaults({
  24756. fill: gridModel.get('backgroundColor')
  24757. }, gridModel.getItemStyle()),
  24758. silent: true,
  24759. z2: -1
  24760. }));
  24761. }
  24762. }
  24763. });
  24764. registerPreprocessor(function (option) {
  24765. // Only create grid when need
  24766. if (option.xAxis && option.yAxis && !option.grid) {
  24767. option.grid = {};
  24768. }
  24769. }); // In case developer forget to include grid component
  24770. registerVisual(curry(visualSymbol, 'line', 'circle', 'line'));
  24771. registerLayout(curry(layoutPoints, 'line')); // Down sample after filter
  24772. registerProcessor(PRIORITY.PROCESSOR.STATISTIC, curry(dataSample, 'line')); // Model
  24773. extendComponentModel({
  24774. type: 'title',
  24775. layoutMode: {
  24776. type: 'box',
  24777. ignoreSize: true
  24778. },
  24779. defaultOption: {
  24780. // 一级层叠
  24781. zlevel: 0,
  24782. // 二级层叠
  24783. z: 6,
  24784. show: true,
  24785. text: '',
  24786. // 超链接跳转
  24787. // link: null,
  24788. // 仅支持self | blank
  24789. target: 'blank',
  24790. subtext: '',
  24791. // 超链接跳转
  24792. // sublink: null,
  24793. // 仅支持self | blank
  24794. subtarget: 'blank',
  24795. // 'center' ¦ 'left' ¦ 'right'
  24796. // ¦ {number}(x坐标,单位px)
  24797. left: 0,
  24798. // 'top' ¦ 'bottom' ¦ 'center'
  24799. // ¦ {number}(y坐标,单位px)
  24800. top: 0,
  24801. // 水平对齐
  24802. // 'auto' | 'left' | 'right' | 'center'
  24803. // 默认根据 left 的位置判断是左对齐还是右对齐
  24804. // textAlign: null
  24805. //
  24806. // 垂直对齐
  24807. // 'auto' | 'top' | 'bottom' | 'middle'
  24808. // 默认根据 top 位置判断是上对齐还是下对齐
  24809. // textBaseline: null
  24810. backgroundColor: 'rgba(0,0,0,0)',
  24811. // 标题边框颜色
  24812. borderColor: '#ccc',
  24813. // 标题边框线宽,单位px,默认为0(无边框)
  24814. borderWidth: 0,
  24815. // 标题内边距,单位px,默认各方向内边距为5,
  24816. // 接受数组分别设定上右下左边距,同css
  24817. padding: 5,
  24818. // 主副标题纵向间隔,单位px,默认为10,
  24819. itemGap: 10,
  24820. textStyle: {
  24821. fontSize: 18,
  24822. fontWeight: 'bolder',
  24823. color: '#333'
  24824. },
  24825. subtextStyle: {
  24826. color: '#aaa'
  24827. }
  24828. }
  24829. }); // View
  24830. extendComponentView({
  24831. type: 'title',
  24832. render: function (titleModel, ecModel, api) {
  24833. this.group.removeAll();
  24834. if (!titleModel.get('show')) {
  24835. return;
  24836. }
  24837. var group = this.group;
  24838. var textStyleModel = titleModel.getModel('textStyle');
  24839. var subtextStyleModel = titleModel.getModel('subtextStyle');
  24840. var textAlign = titleModel.get('textAlign');
  24841. var textBaseline = titleModel.get('textBaseline');
  24842. var textEl = new Text({
  24843. style: setTextStyle({}, textStyleModel, {
  24844. text: titleModel.get('text'),
  24845. textFill: textStyleModel.getTextColor()
  24846. }, {
  24847. disableBox: true
  24848. }),
  24849. z2: 10
  24850. });
  24851. var textRect = textEl.getBoundingRect();
  24852. var subText = titleModel.get('subtext');
  24853. var subTextEl = new Text({
  24854. style: setTextStyle({}, subtextStyleModel, {
  24855. text: subText,
  24856. textFill: subtextStyleModel.getTextColor(),
  24857. y: textRect.height + titleModel.get('itemGap'),
  24858. textVerticalAlign: 'top'
  24859. }, {
  24860. disableBox: true
  24861. }),
  24862. z2: 10
  24863. });
  24864. var link = titleModel.get('link');
  24865. var sublink = titleModel.get('sublink');
  24866. textEl.silent = !link;
  24867. subTextEl.silent = !sublink;
  24868. if (link) {
  24869. textEl.on('click', function () {
  24870. window.open(link, '_' + titleModel.get('target'));
  24871. });
  24872. }
  24873. if (sublink) {
  24874. subTextEl.on('click', function () {
  24875. window.open(sublink, '_' + titleModel.get('subtarget'));
  24876. });
  24877. }
  24878. group.add(textEl);
  24879. subText && group.add(subTextEl); // If no subText, but add subTextEl, there will be an empty line.
  24880. var groupRect = group.getBoundingRect();
  24881. var layoutOption = titleModel.getBoxLayoutParams();
  24882. layoutOption.width = groupRect.width;
  24883. layoutOption.height = groupRect.height;
  24884. var layoutRect = getLayoutRect(layoutOption, {
  24885. width: api.getWidth(),
  24886. height: api.getHeight()
  24887. }, titleModel.get('padding')); // Adjust text align based on position
  24888. if (!textAlign) {
  24889. // Align left if title is on the left. center and right is same
  24890. textAlign = titleModel.get('left') || titleModel.get('right');
  24891. if (textAlign === 'middle') {
  24892. textAlign = 'center';
  24893. } // Adjust layout by text align
  24894. if (textAlign === 'right') {
  24895. layoutRect.x += layoutRect.width;
  24896. } else if (textAlign === 'center') {
  24897. layoutRect.x += layoutRect.width / 2;
  24898. }
  24899. }
  24900. if (!textBaseline) {
  24901. textBaseline = titleModel.get('top') || titleModel.get('bottom');
  24902. if (textBaseline === 'center') {
  24903. textBaseline = 'middle';
  24904. }
  24905. if (textBaseline === 'bottom') {
  24906. layoutRect.y += layoutRect.height;
  24907. } else if (textBaseline === 'middle') {
  24908. layoutRect.y += layoutRect.height / 2;
  24909. }
  24910. textBaseline = textBaseline || 'top';
  24911. }
  24912. group.attr('position', [layoutRect.x, layoutRect.y]);
  24913. var alignStyle = {
  24914. textAlign: textAlign,
  24915. textVerticalAlign: textBaseline
  24916. };
  24917. textEl.setStyle(alignStyle);
  24918. subTextEl.setStyle(alignStyle); // Render background
  24919. // Get groupRect again because textAlign has been changed
  24920. groupRect = group.getBoundingRect();
  24921. var padding = layoutRect.margin;
  24922. var style = titleModel.getItemStyle(['color', 'opacity']);
  24923. style.fill = titleModel.get('backgroundColor');
  24924. var rect = new Rect({
  24925. shape: {
  24926. x: groupRect.x - padding[3],
  24927. y: groupRect.y - padding[0],
  24928. width: groupRect.width + padding[1] + padding[3],
  24929. height: groupRect.height + padding[0] + padding[2],
  24930. r: titleModel.get('borderRadius')
  24931. },
  24932. style: style,
  24933. silent: true
  24934. });
  24935. subPixelOptimizeRect(rect);
  24936. group.add(rect);
  24937. }
  24938. });
  24939. var LegendModel = extendComponentModel({
  24940. type: 'legend.plain',
  24941. dependencies: ['series'],
  24942. layoutMode: {
  24943. type: 'box',
  24944. // legend.width/height are maxWidth/maxHeight actually,
  24945. // whereas realy width/height is calculated by its content.
  24946. // (Setting {left: 10, right: 10} does not make sense).
  24947. // So consider the case:
  24948. // `setOption({legend: {left: 10});`
  24949. // then `setOption({legend: {right: 10});`
  24950. // The previous `left` should be cleared by setting `ignoreSize`.
  24951. ignoreSize: true
  24952. },
  24953. init: function (option, parentModel, ecModel) {
  24954. this.mergeDefaultAndTheme(option, ecModel);
  24955. option.selected = option.selected || {};
  24956. },
  24957. mergeOption: function (option) {
  24958. LegendModel.superCall(this, 'mergeOption', option);
  24959. },
  24960. optionUpdated: function () {
  24961. this._updateData(this.ecModel);
  24962. var legendData = this._data; // If selectedMode is single, try to select one
  24963. if (legendData[0] && this.get('selectedMode') === 'single') {
  24964. var hasSelected = false; // If has any selected in option.selected
  24965. for (var i = 0; i < legendData.length; i++) {
  24966. var name = legendData[i].get('name');
  24967. if (this.isSelected(name)) {
  24968. // Force to unselect others
  24969. this.select(name);
  24970. hasSelected = true;
  24971. break;
  24972. }
  24973. } // Try select the first if selectedMode is single
  24974. !hasSelected && this.select(legendData[0].get('name'));
  24975. }
  24976. },
  24977. _updateData: function (ecModel) {
  24978. var legendData = map(this.get('data') || [], function (dataItem) {
  24979. // Can be string or number
  24980. if (typeof dataItem === 'string' || typeof dataItem === 'number') {
  24981. dataItem = {
  24982. name: dataItem
  24983. };
  24984. }
  24985. return new Model(dataItem, this, this.ecModel);
  24986. }, this);
  24987. this._data = legendData;
  24988. var availableNames = map(ecModel.getSeries(), function (series) {
  24989. return series.name;
  24990. });
  24991. ecModel.eachSeries(function (seriesModel) {
  24992. if (seriesModel.legendDataProvider) {
  24993. var data = seriesModel.legendDataProvider();
  24994. availableNames = availableNames.concat(data.mapArray(data.getName));
  24995. }
  24996. });
  24997. /**
  24998. * @type {Array.<string>}
  24999. * @private
  25000. */
  25001. this._availableNames = availableNames;
  25002. },
  25003. /**
  25004. * @return {Array.<module:echarts/model/Model>}
  25005. */
  25006. getData: function () {
  25007. return this._data;
  25008. },
  25009. /**
  25010. * @param {string} name
  25011. */
  25012. select: function (name) {
  25013. var selected = this.option.selected;
  25014. var selectedMode = this.get('selectedMode');
  25015. if (selectedMode === 'single') {
  25016. var data = this._data;
  25017. each$1(data, function (dataItem) {
  25018. selected[dataItem.get('name')] = false;
  25019. });
  25020. }
  25021. selected[name] = true;
  25022. },
  25023. /**
  25024. * @param {string} name
  25025. */
  25026. unSelect: function (name) {
  25027. if (this.get('selectedMode') !== 'single') {
  25028. this.option.selected[name] = false;
  25029. }
  25030. },
  25031. /**
  25032. * @param {string} name
  25033. */
  25034. toggleSelected: function (name) {
  25035. var selected = this.option.selected; // Default is true
  25036. if (!selected.hasOwnProperty(name)) {
  25037. selected[name] = true;
  25038. }
  25039. this[selected[name] ? 'unSelect' : 'select'](name);
  25040. },
  25041. /**
  25042. * @param {string} name
  25043. */
  25044. isSelected: function (name) {
  25045. var selected = this.option.selected;
  25046. return !(selected.hasOwnProperty(name) && !selected[name]) && indexOf(this._availableNames, name) >= 0;
  25047. },
  25048. defaultOption: {
  25049. // 一级层叠
  25050. zlevel: 0,
  25051. // 二级层叠
  25052. z: 4,
  25053. show: true,
  25054. // 布局方式,默认为水平布局,可选为:
  25055. // 'horizontal' | 'vertical'
  25056. orient: 'horizontal',
  25057. left: 'center',
  25058. // right: 'center',
  25059. top: 0,
  25060. // bottom: null,
  25061. // 水平对齐
  25062. // 'auto' | 'left' | 'right'
  25063. // 默认为 'auto', 根据 x 的位置判断是左对齐还是右对齐
  25064. align: 'auto',
  25065. backgroundColor: 'rgba(0,0,0,0)',
  25066. // 图例边框颜色
  25067. borderColor: '#ccc',
  25068. borderRadius: 0,
  25069. // 图例边框线宽,单位px,默认为0(无边框)
  25070. borderWidth: 0,
  25071. // 图例内边距,单位px,默认各方向内边距为5,
  25072. // 接受数组分别设定上右下左边距,同css
  25073. padding: 5,
  25074. // 各个item之间的间隔,单位px,默认为10,
  25075. // 横向布局时为水平间隔,纵向布局时为纵向间隔
  25076. itemGap: 10,
  25077. // 图例图形宽度
  25078. itemWidth: 25,
  25079. // 图例图形高度
  25080. itemHeight: 14,
  25081. // 图例关闭时候的颜色
  25082. inactiveColor: '#ccc',
  25083. textStyle: {
  25084. // 图例文字颜色
  25085. color: '#333'
  25086. },
  25087. // formatter: '',
  25088. // 选择模式,默认开启图例开关
  25089. selectedMode: true,
  25090. // 配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入
  25091. // selected: null,
  25092. // 图例内容(详见legend.data,数组中每一项代表一个item
  25093. // data: [],
  25094. // Tooltip 相关配置
  25095. tooltip: {
  25096. show: false
  25097. }
  25098. }
  25099. });
  25100. function legendSelectActionHandler(methodName, payload, ecModel) {
  25101. var selectedMap = {};
  25102. var isToggleSelect = methodName === 'toggleSelected';
  25103. var isSelected; // Update all legend components
  25104. ecModel.eachComponent('legend', function (legendModel) {
  25105. if (isToggleSelect && isSelected != null) {
  25106. // Force other legend has same selected status
  25107. // Or the first is toggled to true and other are toggled to false
  25108. // In the case one legend has some item unSelected in option. And if other legend
  25109. // doesn't has the item, they will assume it is selected.
  25110. legendModel[isSelected ? 'select' : 'unSelect'](payload.name);
  25111. } else {
  25112. legendModel[methodName](payload.name);
  25113. isSelected = legendModel.isSelected(payload.name);
  25114. }
  25115. var legendData = legendModel.getData();
  25116. each$1(legendData, function (model) {
  25117. var name = model.get('name'); // Wrap element
  25118. if (name === '\n' || name === '') {
  25119. return;
  25120. }
  25121. var isItemSelected = legendModel.isSelected(name);
  25122. if (selectedMap.hasOwnProperty(name)) {
  25123. // Unselected if any legend is unselected
  25124. selectedMap[name] = selectedMap[name] && isItemSelected;
  25125. } else {
  25126. selectedMap[name] = isItemSelected;
  25127. }
  25128. });
  25129. }); // Return the event explicitly
  25130. return {
  25131. name: payload.name,
  25132. selected: selectedMap
  25133. };
  25134. }
  25135. /**
  25136. * @event legendToggleSelect
  25137. * @type {Object}
  25138. * @property {string} type 'legendToggleSelect'
  25139. * @property {string} [from]
  25140. * @property {string} name Series name or data item name
  25141. */
  25142. registerAction('legendToggleSelect', 'legendselectchanged', curry(legendSelectActionHandler, 'toggleSelected'));
  25143. /**
  25144. * @event legendSelect
  25145. * @type {Object}
  25146. * @property {string} type 'legendSelect'
  25147. * @property {string} name Series name or data item name
  25148. */
  25149. registerAction('legendSelect', 'legendselected', curry(legendSelectActionHandler, 'select'));
  25150. /**
  25151. * @event legendUnSelect
  25152. * @type {Object}
  25153. * @property {string} type 'legendUnSelect'
  25154. * @property {string} name Series name or data item name
  25155. */
  25156. registerAction('legendUnSelect', 'legendunselected', curry(legendSelectActionHandler, 'unSelect'));
  25157. /**
  25158. * Layout list like component.
  25159. * It will box layout each items in group of component and then position the whole group in the viewport
  25160. * @param {module:zrender/group/Group} group
  25161. * @param {module:echarts/model/Component} componentModel
  25162. * @param {module:echarts/ExtensionAPI}
  25163. */
  25164. function makeBackground(rect, componentModel) {
  25165. var padding = normalizeCssArray$1(componentModel.get('padding'));
  25166. var style = componentModel.getItemStyle(['color', 'opacity']);
  25167. style.fill = componentModel.get('backgroundColor');
  25168. var rect = new Rect({
  25169. shape: {
  25170. x: rect.x - padding[3],
  25171. y: rect.y - padding[0],
  25172. width: rect.width + padding[1] + padding[3],
  25173. height: rect.height + padding[0] + padding[2],
  25174. r: componentModel.get('borderRadius')
  25175. },
  25176. style: style,
  25177. silent: true,
  25178. z2: -1
  25179. }); // FIXME
  25180. // `subPixelOptimizeRect` may bring some gap between edge of viewpart
  25181. // and background rect when setting like `left: 0`, `top: 0`.
  25182. // graphic.subPixelOptimizeRect(rect);
  25183. return rect;
  25184. }
  25185. var curry$2 = curry;
  25186. var each$10 = each$1;
  25187. var Group$2 = Group;
  25188. var LegendView = extendComponentView({
  25189. type: 'legend.plain',
  25190. newlineDisabled: false,
  25191. /**
  25192. * @override
  25193. */
  25194. init: function () {
  25195. /**
  25196. * @private
  25197. * @type {module:zrender/container/Group}
  25198. */
  25199. this.group.add(this._contentGroup = new Group$2());
  25200. /**
  25201. * @private
  25202. * @type {module:zrender/Element}
  25203. */
  25204. this._backgroundEl;
  25205. },
  25206. /**
  25207. * @protected
  25208. */
  25209. getContentGroup: function () {
  25210. return this._contentGroup;
  25211. },
  25212. /**
  25213. * @override
  25214. */
  25215. render: function (legendModel, ecModel, api) {
  25216. this.resetInner();
  25217. if (!legendModel.get('show', true)) {
  25218. return;
  25219. }
  25220. var itemAlign = legendModel.get('align');
  25221. if (!itemAlign || itemAlign === 'auto') {
  25222. itemAlign = legendModel.get('left') === 'right' && legendModel.get('orient') === 'vertical' ? 'right' : 'left';
  25223. }
  25224. this.renderInner(itemAlign, legendModel, ecModel, api); // Perform layout.
  25225. var positionInfo = legendModel.getBoxLayoutParams();
  25226. var viewportSize = {
  25227. width: api.getWidth(),
  25228. height: api.getHeight()
  25229. };
  25230. var padding = legendModel.get('padding');
  25231. var maxSize = getLayoutRect(positionInfo, viewportSize, padding);
  25232. var mainRect = this.layoutInner(legendModel, itemAlign, maxSize); // Place mainGroup, based on the calculated `mainRect`.
  25233. var layoutRect = getLayoutRect(defaults({
  25234. width: mainRect.width,
  25235. height: mainRect.height
  25236. }, positionInfo), viewportSize, padding);
  25237. this.group.attr('position', [layoutRect.x - mainRect.x, layoutRect.y - mainRect.y]); // Render background after group is layout.
  25238. this.group.add(this._backgroundEl = makeBackground(mainRect, legendModel));
  25239. },
  25240. /**
  25241. * @protected
  25242. */
  25243. resetInner: function () {
  25244. this.getContentGroup().removeAll();
  25245. this._backgroundEl && this.group.remove(this._backgroundEl);
  25246. },
  25247. /**
  25248. * @protected
  25249. */
  25250. renderInner: function (itemAlign, legendModel, ecModel, api) {
  25251. var contentGroup = this.getContentGroup();
  25252. var legendDrawnMap = createHashMap();
  25253. var selectMode = legendModel.get('selectedMode');
  25254. each$10(legendModel.getData(), function (itemModel, dataIndex) {
  25255. var name = itemModel.get('name'); // Use empty string or \n as a newline string
  25256. if (!this.newlineDisabled && (name === '' || name === '\n')) {
  25257. contentGroup.add(new Group$2({
  25258. newline: true
  25259. }));
  25260. return;
  25261. }
  25262. var seriesModel = ecModel.getSeriesByName(name)[0];
  25263. if (legendDrawnMap.get(name)) {
  25264. // Have been drawed
  25265. return;
  25266. } // Series legend
  25267. if (seriesModel) {
  25268. var data = seriesModel.getData();
  25269. var color = data.getVisual('color'); // If color is a callback function
  25270. if (typeof color === 'function') {
  25271. // Use the first data
  25272. color = color(seriesModel.getDataParams(0));
  25273. } // Using rect symbol defaultly
  25274. var legendSymbolType = data.getVisual('legendSymbol') || 'roundRect';
  25275. var symbolType = data.getVisual('symbol');
  25276. var itemGroup = this._createItem(name, dataIndex, itemModel, legendModel, legendSymbolType, symbolType, itemAlign, color, selectMode);
  25277. itemGroup.on('click', curry$2(dispatchSelectAction, name, api)).on('mouseover', curry$2(dispatchHighlightAction, seriesModel, null, api)).on('mouseout', curry$2(dispatchDownplayAction, seriesModel, null, api));
  25278. legendDrawnMap.set(name, true);
  25279. } else {
  25280. // Data legend of pie, funnel
  25281. ecModel.eachRawSeries(function (seriesModel) {
  25282. // In case multiple series has same data name
  25283. if (legendDrawnMap.get(name)) {
  25284. return;
  25285. }
  25286. if (seriesModel.legendDataProvider) {
  25287. var data = seriesModel.legendDataProvider();
  25288. var idx = data.indexOfName(name);
  25289. if (idx < 0) {
  25290. return;
  25291. }
  25292. var color = data.getItemVisual(idx, 'color');
  25293. var legendSymbolType = 'roundRect';
  25294. var itemGroup = this._createItem(name, dataIndex, itemModel, legendModel, legendSymbolType, null, itemAlign, color, selectMode);
  25295. itemGroup.on('click', curry$2(dispatchSelectAction, name, api)) // FIXME Should not specify the series name
  25296. .on('mouseover', curry$2(dispatchHighlightAction, seriesModel, name, api)).on('mouseout', curry$2(dispatchDownplayAction, seriesModel, name, api));
  25297. legendDrawnMap.set(name, true);
  25298. }
  25299. }, this);
  25300. }
  25301. if (true) {
  25302. if (!legendDrawnMap.get(name)) {
  25303. console.warn(name + ' series not exists. Legend data should be same with series name or data name.');
  25304. }
  25305. }
  25306. }, this);
  25307. },
  25308. _createItem: function (name, dataIndex, itemModel, legendModel, legendSymbolType, symbolType, itemAlign, color, selectMode) {
  25309. var itemWidth = legendModel.get('itemWidth');
  25310. var itemHeight = legendModel.get('itemHeight');
  25311. var inactiveColor = legendModel.get('inactiveColor');
  25312. var isSelected = legendModel.isSelected(name);
  25313. var itemGroup = new Group$2();
  25314. var textStyleModel = itemModel.getModel('textStyle');
  25315. var itemIcon = itemModel.get('icon');
  25316. var tooltipModel = itemModel.getModel('tooltip');
  25317. var legendGlobalTooltipModel = tooltipModel.parentModel; // Use user given icon first
  25318. legendSymbolType = itemIcon || legendSymbolType;
  25319. itemGroup.add(createSymbol(legendSymbolType, 0, 0, itemWidth, itemHeight, isSelected ? color : inactiveColor, true)); // Compose symbols
  25320. // PENDING
  25321. if (!itemIcon && symbolType // At least show one symbol, can't be all none
  25322. && (symbolType !== legendSymbolType || symbolType == 'none')) {
  25323. var size = itemHeight * 0.8;
  25324. if (symbolType === 'none') {
  25325. symbolType = 'circle';
  25326. } // Put symbol in the center
  25327. itemGroup.add(createSymbol(symbolType, (itemWidth - size) / 2, (itemHeight - size) / 2, size, size, isSelected ? color : inactiveColor));
  25328. }
  25329. var textX = itemAlign === 'left' ? itemWidth + 5 : -5;
  25330. var textAlign = itemAlign;
  25331. var formatter = legendModel.get('formatter');
  25332. var content = name;
  25333. if (typeof formatter === 'string' && formatter) {
  25334. content = formatter.replace('{name}', name != null ? name : '');
  25335. } else if (typeof formatter === 'function') {
  25336. content = formatter(name);
  25337. }
  25338. itemGroup.add(new Text({
  25339. style: setTextStyle({}, textStyleModel, {
  25340. text: content,
  25341. x: textX,
  25342. y: itemHeight / 2,
  25343. textFill: isSelected ? textStyleModel.getTextColor() : inactiveColor,
  25344. textAlign: textAlign,
  25345. textVerticalAlign: 'middle'
  25346. })
  25347. })); // Add a invisible rect to increase the area of mouse hover
  25348. var hitRect = new Rect({
  25349. shape: itemGroup.getBoundingRect(),
  25350. invisible: true,
  25351. tooltip: tooltipModel.get('show') ? extend({
  25352. content: name,
  25353. // Defaul formatter
  25354. formatter: legendGlobalTooltipModel.get('formatter', true) || function () {
  25355. return name;
  25356. },
  25357. formatterParams: {
  25358. componentType: 'legend',
  25359. legendIndex: legendModel.componentIndex,
  25360. name: name,
  25361. $vars: ['name']
  25362. }
  25363. }, tooltipModel.option) : null
  25364. });
  25365. itemGroup.add(hitRect);
  25366. itemGroup.eachChild(function (child) {
  25367. child.silent = true;
  25368. });
  25369. hitRect.silent = !selectMode;
  25370. this.getContentGroup().add(itemGroup);
  25371. setHoverStyle(itemGroup);
  25372. itemGroup.__legendDataIndex = dataIndex;
  25373. return itemGroup;
  25374. },
  25375. /**
  25376. * @protected
  25377. */
  25378. layoutInner: function (legendModel, itemAlign, maxSize) {
  25379. var contentGroup = this.getContentGroup(); // Place items in contentGroup.
  25380. box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), maxSize.width, maxSize.height);
  25381. var contentRect = contentGroup.getBoundingRect();
  25382. contentGroup.attr('position', [-contentRect.x, -contentRect.y]);
  25383. return this.group.getBoundingRect();
  25384. }
  25385. });
  25386. function dispatchSelectAction(name, api) {
  25387. api.dispatchAction({
  25388. type: 'legendToggleSelect',
  25389. name: name
  25390. });
  25391. }
  25392. function dispatchHighlightAction(seriesModel, dataName, api) {
  25393. // If element hover will move to a hoverLayer.
  25394. var el = api.getZr().storage.getDisplayList()[0];
  25395. if (!(el && el.useHoverLayer)) {
  25396. seriesModel.get('legendHoverLink') && api.dispatchAction({
  25397. type: 'highlight',
  25398. seriesName: seriesModel.name,
  25399. name: dataName
  25400. });
  25401. }
  25402. }
  25403. function dispatchDownplayAction(seriesModel, dataName, api) {
  25404. // If element hover will move to a hoverLayer.
  25405. var el = api.getZr().storage.getDisplayList()[0];
  25406. if (!(el && el.useHoverLayer)) {
  25407. seriesModel.get('legendHoverLink') && api.dispatchAction({
  25408. type: 'downplay',
  25409. seriesName: seriesModel.name,
  25410. name: dataName
  25411. });
  25412. }
  25413. }
  25414. var legendFilter = function (ecModel) {
  25415. var legendModels = ecModel.findComponents({
  25416. mainType: 'legend'
  25417. });
  25418. if (legendModels && legendModels.length) {
  25419. ecModel.filterSeries(function (series) {
  25420. // If in any legend component the status is not selected.
  25421. // Because in legend series is assumed selected when it is not in the legend data.
  25422. for (var i = 0; i < legendModels.length; i++) {
  25423. if (!legendModels[i].isSelected(series.name)) {
  25424. return false;
  25425. }
  25426. }
  25427. return true;
  25428. });
  25429. }
  25430. }; // Do not contain scrollable legend, for sake of file size.
  25431. // Series Filter
  25432. registerProcessor(legendFilter);
  25433. ComponentModel.registerSubTypeDefaulter('legend', function () {
  25434. // Default 'plain' when no type specified.
  25435. return 'plain';
  25436. });
  25437. var ScrollableLegendModel = LegendModel.extend({
  25438. type: 'legend.scroll',
  25439. /**
  25440. * @param {number} scrollDataIndex
  25441. */
  25442. setScrollDataIndex: function (scrollDataIndex) {
  25443. this.option.scrollDataIndex = scrollDataIndex;
  25444. },
  25445. defaultOption: {
  25446. scrollDataIndex: 0,
  25447. pageButtonItemGap: 5,
  25448. pageButtonGap: null,
  25449. pageButtonPosition: 'end',
  25450. // 'start' or 'end'
  25451. pageFormatter: '{current}/{total}',
  25452. // If null/undefined, do not show page.
  25453. pageIcons: {
  25454. horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'],
  25455. vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z']
  25456. },
  25457. pageIconColor: '#2f4554',
  25458. pageIconInactiveColor: '#aaa',
  25459. pageIconSize: 15,
  25460. // Can be [10, 3], which represents [width, height]
  25461. pageTextStyle: {
  25462. color: '#333'
  25463. },
  25464. animationDurationUpdate: 800
  25465. },
  25466. /**
  25467. * @override
  25468. */
  25469. init: function (option, parentModel, ecModel, extraOpt) {
  25470. var inputPositionParams = getLayoutParams(option);
  25471. ScrollableLegendModel.superCall(this, 'init', option, parentModel, ecModel, extraOpt);
  25472. mergeAndNormalizeLayoutParams(this, option, inputPositionParams);
  25473. },
  25474. /**
  25475. * @override
  25476. */
  25477. mergeOption: function (option, extraOpt) {
  25478. ScrollableLegendModel.superCall(this, 'mergeOption', option, extraOpt);
  25479. mergeAndNormalizeLayoutParams(this, this.option, option);
  25480. },
  25481. getOrient: function () {
  25482. return this.get('orient') === 'vertical' ? {
  25483. index: 1,
  25484. name: 'vertical'
  25485. } : {
  25486. index: 0,
  25487. name: 'horizontal'
  25488. };
  25489. }
  25490. }); // Do not `ignoreSize` to enable setting {left: 10, right: 10}.
  25491. function mergeAndNormalizeLayoutParams(legendModel, target, raw) {
  25492. var orient = legendModel.getOrient();
  25493. var ignoreSize = [1, 1];
  25494. ignoreSize[orient.index] = 0;
  25495. mergeLayoutParam(target, raw, {
  25496. type: 'box',
  25497. ignoreSize: ignoreSize
  25498. });
  25499. }
  25500. /**
  25501. * Separate legend and scrollable legend to reduce package size.
  25502. */
  25503. var Group$3 = Group;
  25504. var WH = ['width', 'height'];
  25505. var XY = ['x', 'y'];
  25506. var ScrollableLegendView = LegendView.extend({
  25507. type: 'legend.scroll',
  25508. newlineDisabled: true,
  25509. init: function () {
  25510. ScrollableLegendView.superCall(this, 'init');
  25511. /**
  25512. * @private
  25513. * @type {number} For `scroll`.
  25514. */
  25515. this._currentIndex = 0;
  25516. /**
  25517. * @private
  25518. * @type {module:zrender/container/Group}
  25519. */
  25520. this.group.add(this._containerGroup = new Group$3());
  25521. this._containerGroup.add(this.getContentGroup());
  25522. /**
  25523. * @private
  25524. * @type {module:zrender/container/Group}
  25525. */
  25526. this.group.add(this._controllerGroup = new Group$3());
  25527. /**
  25528. *
  25529. * @private
  25530. */
  25531. this._showController;
  25532. },
  25533. /**
  25534. * @override
  25535. */
  25536. resetInner: function () {
  25537. ScrollableLegendView.superCall(this, 'resetInner');
  25538. this._controllerGroup.removeAll();
  25539. this._containerGroup.removeClipPath();
  25540. this._containerGroup.__rectSize = null;
  25541. },
  25542. /**
  25543. * @override
  25544. */
  25545. renderInner: function (itemAlign, legendModel, ecModel, api) {
  25546. var me = this; // Render content items.
  25547. ScrollableLegendView.superCall(this, 'renderInner', itemAlign, legendModel, ecModel, api);
  25548. var controllerGroup = this._controllerGroup;
  25549. var pageIconSize = legendModel.get('pageIconSize', true);
  25550. if (!isArray(pageIconSize)) {
  25551. pageIconSize = [pageIconSize, pageIconSize];
  25552. }
  25553. createPageButton('pagePrev', 0);
  25554. var pageTextStyleModel = legendModel.getModel('pageTextStyle');
  25555. controllerGroup.add(new Text({
  25556. name: 'pageText',
  25557. style: {
  25558. textFill: pageTextStyleModel.getTextColor(),
  25559. font: pageTextStyleModel.getFont(),
  25560. textVerticalAlign: 'middle',
  25561. textAlign: 'center'
  25562. },
  25563. silent: true
  25564. }));
  25565. createPageButton('pageNext', 1);
  25566. function createPageButton(name, iconIdx) {
  25567. var pageDataIndexName = name + 'DataIndex';
  25568. var icon = createIcon(legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], {
  25569. // Buttons will be created in each render, so we do not need
  25570. // to worry about avoiding using legendModel kept in scope.
  25571. onclick: bind(me._pageGo, me, pageDataIndexName, legendModel, api)
  25572. }, {
  25573. x: -pageIconSize[0] / 2,
  25574. y: -pageIconSize[1] / 2,
  25575. width: pageIconSize[0],
  25576. height: pageIconSize[1]
  25577. });
  25578. icon.name = name;
  25579. controllerGroup.add(icon);
  25580. }
  25581. },
  25582. /**
  25583. * @override
  25584. */
  25585. layoutInner: function (legendModel, itemAlign, maxSize) {
  25586. var contentGroup = this.getContentGroup();
  25587. var containerGroup = this._containerGroup;
  25588. var controllerGroup = this._controllerGroup;
  25589. var orientIdx = legendModel.getOrient().index;
  25590. var wh = WH[orientIdx];
  25591. var hw = WH[1 - orientIdx];
  25592. var yx = XY[1 - orientIdx]; // Place items in contentGroup.
  25593. box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), !orientIdx ? null : maxSize.width, orientIdx ? null : maxSize.height);
  25594. box( // Buttons in controller are layout always horizontally.
  25595. 'horizontal', controllerGroup, legendModel.get('pageButtonItemGap', true));
  25596. var contentRect = contentGroup.getBoundingRect();
  25597. var controllerRect = controllerGroup.getBoundingRect();
  25598. var showController = this._showController = contentRect[wh] > maxSize[wh];
  25599. var contentPos = [-contentRect.x, -contentRect.y]; // Remain contentPos when scroll animation perfroming.
  25600. contentPos[orientIdx] = contentGroup.position[orientIdx]; // Layout container group based on 0.
  25601. var containerPos = [0, 0];
  25602. var controllerPos = [-controllerRect.x, -controllerRect.y];
  25603. var pageButtonGap = retrieve2(legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)); // Place containerGroup and controllerGroup and contentGroup.
  25604. if (showController) {
  25605. var pageButtonPosition = legendModel.get('pageButtonPosition', true); // controller is on the right / bottom.
  25606. if (pageButtonPosition === 'end') {
  25607. controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh];
  25608. } // controller is on the left / top.
  25609. else {
  25610. containerPos[orientIdx] += controllerRect[wh] + pageButtonGap;
  25611. }
  25612. } // Always align controller to content as 'middle'.
  25613. controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;
  25614. contentGroup.attr('position', contentPos);
  25615. containerGroup.attr('position', containerPos);
  25616. controllerGroup.attr('position', controllerPos); // Calculate `mainRect` and set `clipPath`.
  25617. // mainRect should not be calculated by `this.group.getBoundingRect()`
  25618. // for sake of the overflow.
  25619. var mainRect = this.group.getBoundingRect();
  25620. var mainRect = {
  25621. x: 0,
  25622. y: 0
  25623. }; // Consider content may be overflow (should be clipped).
  25624. mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];
  25625. mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); // `containerRect[yx] + containerPos[1 - orientIdx]` is 0.
  25626. mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]);
  25627. containerGroup.__rectSize = maxSize[wh];
  25628. if (showController) {
  25629. var clipShape = {
  25630. x: 0,
  25631. y: 0
  25632. };
  25633. clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0);
  25634. clipShape[hw] = mainRect[hw];
  25635. containerGroup.setClipPath(new Rect({
  25636. shape: clipShape
  25637. })); // Consider content may be larger than container, container rect
  25638. // can not be obtained from `containerGroup.getBoundingRect()`.
  25639. containerGroup.__rectSize = clipShape[wh];
  25640. } else {
  25641. // Do not remove or ignore controller. Keep them set as place holders.
  25642. controllerGroup.eachChild(function (child) {
  25643. child.attr({
  25644. invisible: true,
  25645. silent: true
  25646. });
  25647. });
  25648. } // Content translate animation.
  25649. var pageInfo = this._getPageInfo(legendModel);
  25650. pageInfo.pageIndex != null && updateProps(contentGroup, {
  25651. position: pageInfo.contentPosition
  25652. }, // When switch from "show controller" to "not show controller", view should be
  25653. // updated immediately without animation, otherwise causes weird efffect.
  25654. showController ? legendModel : false);
  25655. this._updatePageInfoView(legendModel, pageInfo);
  25656. return mainRect;
  25657. },
  25658. _pageGo: function (to, legendModel, api) {
  25659. var scrollDataIndex = this._getPageInfo(legendModel)[to];
  25660. scrollDataIndex != null && api.dispatchAction({
  25661. type: 'legendScroll',
  25662. scrollDataIndex: scrollDataIndex,
  25663. legendId: legendModel.id
  25664. });
  25665. },
  25666. _updatePageInfoView: function (legendModel, pageInfo) {
  25667. var controllerGroup = this._controllerGroup;
  25668. each$1(['pagePrev', 'pageNext'], function (name) {
  25669. var canJump = pageInfo[name + 'DataIndex'] != null;
  25670. var icon = controllerGroup.childOfName(name);
  25671. if (icon) {
  25672. icon.setStyle('fill', canJump ? legendModel.get('pageIconColor', true) : legendModel.get('pageIconInactiveColor', true));
  25673. icon.cursor = canJump ? 'pointer' : 'default';
  25674. }
  25675. });
  25676. var pageText = controllerGroup.childOfName('pageText');
  25677. var pageFormatter = legendModel.get('pageFormatter');
  25678. var pageIndex = pageInfo.pageIndex;
  25679. var current = pageIndex != null ? pageIndex + 1 : 0;
  25680. var total = pageInfo.pageCount;
  25681. pageText && pageFormatter && pageText.setStyle('text', isString(pageFormatter) ? pageFormatter.replace('{current}', current).replace('{total}', total) : pageFormatter({
  25682. current: current,
  25683. total: total
  25684. }));
  25685. },
  25686. /**
  25687. * @param {module:echarts/model/Model} legendModel
  25688. * @return {Object} {
  25689. * contentPosition: Array.<number>, null when data item not found.
  25690. * pageIndex: number, null when data item not found.
  25691. * pageCount: number, always be a number, can be 0.
  25692. * pagePrevDataIndex: number, null when no next page.
  25693. * pageNextDataIndex: number, null when no previous page.
  25694. * }
  25695. */
  25696. _getPageInfo: function (legendModel) {
  25697. // Align left or top by the current dataIndex.
  25698. var currDataIndex = legendModel.get('scrollDataIndex', true);
  25699. var contentGroup = this.getContentGroup();
  25700. var contentRect = contentGroup.getBoundingRect();
  25701. var containerRectSize = this._containerGroup.__rectSize;
  25702. var orientIdx = legendModel.getOrient().index;
  25703. var wh = WH[orientIdx];
  25704. var hw = WH[1 - orientIdx];
  25705. var xy = XY[orientIdx];
  25706. var contentPos = contentGroup.position.slice();
  25707. var pageIndex;
  25708. var pagePrevDataIndex;
  25709. var pageNextDataIndex;
  25710. var targetItemGroup;
  25711. if (this._showController) {
  25712. contentGroup.eachChild(function (child) {
  25713. if (child.__legendDataIndex === currDataIndex) {
  25714. targetItemGroup = child;
  25715. }
  25716. });
  25717. } else {
  25718. targetItemGroup = contentGroup.childAt(0);
  25719. }
  25720. var pageCount = containerRectSize ? Math.ceil(contentRect[wh] / containerRectSize) : 0;
  25721. if (targetItemGroup) {
  25722. var itemRect = targetItemGroup.getBoundingRect();
  25723. var itemLoc = targetItemGroup.position[orientIdx] + itemRect[xy];
  25724. contentPos[orientIdx] = -itemLoc - contentRect[xy];
  25725. pageIndex = Math.floor(pageCount * (itemLoc + itemRect[xy] + containerRectSize / 2) / contentRect[wh]);
  25726. pageIndex = contentRect[wh] && pageCount ? Math.max(0, Math.min(pageCount - 1, pageIndex)) : -1;
  25727. var winRect = {
  25728. x: 0,
  25729. y: 0
  25730. };
  25731. winRect[wh] = containerRectSize;
  25732. winRect[hw] = contentRect[hw];
  25733. winRect[xy] = -contentPos[orientIdx] - contentRect[xy];
  25734. var startIdx;
  25735. var children = contentGroup.children();
  25736. contentGroup.eachChild(function (child, index) {
  25737. var itemRect = getItemRect(child);
  25738. if (itemRect.intersect(winRect)) {
  25739. startIdx == null && (startIdx = index); // It is user-friendly that the last item shown in the
  25740. // current window is shown at the begining of next window.
  25741. pageNextDataIndex = child.__legendDataIndex;
  25742. } // If the last item is shown entirely, no next page.
  25743. if (index === children.length - 1 && itemRect[xy] + itemRect[wh] <= winRect[xy] + winRect[wh]) {
  25744. pageNextDataIndex = null;
  25745. }
  25746. }); // Always align based on the left/top most item, so the left/top most
  25747. // item in the previous window is needed to be found here.
  25748. if (startIdx != null) {
  25749. var startItem = children[startIdx];
  25750. var startRect = getItemRect(startItem);
  25751. winRect[xy] = startRect[xy] + startRect[wh] - winRect[wh]; // If the first item is shown entirely, no previous page.
  25752. if (startIdx <= 0 && startRect[xy] >= winRect[xy]) {
  25753. pagePrevDataIndex = null;
  25754. } else {
  25755. while (startIdx > 0 && getItemRect(children[startIdx - 1]).intersect(winRect)) {
  25756. startIdx--;
  25757. }
  25758. pagePrevDataIndex = children[startIdx].__legendDataIndex;
  25759. }
  25760. }
  25761. }
  25762. return {
  25763. contentPosition: contentPos,
  25764. pageIndex: pageIndex,
  25765. pageCount: pageCount,
  25766. pagePrevDataIndex: pagePrevDataIndex,
  25767. pageNextDataIndex: pageNextDataIndex
  25768. };
  25769. function getItemRect(el) {
  25770. var itemRect = el.getBoundingRect().clone();
  25771. itemRect[xy] += el.position[orientIdx];
  25772. return itemRect;
  25773. }
  25774. }
  25775. });
  25776. /**
  25777. * @event legendScroll
  25778. * @type {Object}
  25779. * @property {string} type 'legendScroll'
  25780. * @property {string} scrollDataIndex
  25781. */
  25782. registerAction('legendScroll', 'legendscroll', function (payload, ecModel) {
  25783. var scrollDataIndex = payload.scrollDataIndex;
  25784. scrollDataIndex != null && ecModel.eachComponent({
  25785. mainType: 'legend',
  25786. subType: 'scroll',
  25787. query: payload
  25788. }, function (legendModel) {
  25789. legendModel.setScrollDataIndex(scrollDataIndex);
  25790. });
  25791. });
  25792. /**
  25793. * Legend component entry file8
  25794. */
  25795. /**
  25796. * @param {Object} finder contains {seriesIndex, dataIndex, dataIndexInside}
  25797. * @param {module:echarts/model/Global} ecModel
  25798. * @return {Object} {point: [x, y], el: ...} point Will not be null.
  25799. */
  25800. var findPointFromSeries = function (finder, ecModel) {
  25801. var point = [];
  25802. var seriesIndex = finder.seriesIndex;
  25803. var seriesModel;
  25804. if (seriesIndex == null || !(seriesModel = ecModel.getSeriesByIndex(seriesIndex))) {
  25805. return {
  25806. point: []
  25807. };
  25808. }
  25809. var data = seriesModel.getData();
  25810. var dataIndex = queryDataIndex(data, finder);
  25811. if (dataIndex == null || isArray(dataIndex)) {
  25812. return {
  25813. point: []
  25814. };
  25815. }
  25816. var el = data.getItemGraphicEl(dataIndex);
  25817. var coordSys = seriesModel.coordinateSystem;
  25818. if (seriesModel.getTooltipPosition) {
  25819. point = seriesModel.getTooltipPosition(dataIndex) || [];
  25820. } else if (coordSys && coordSys.dataToPoint) {
  25821. point = coordSys.dataToPoint(data.getValues(map(coordSys.dimensions, function (dim) {
  25822. return seriesModel.coordDimToDataDim(dim)[0];
  25823. }), dataIndex, true)) || [];
  25824. } else if (el) {
  25825. // Use graphic bounding rect
  25826. var rect = el.getBoundingRect().clone();
  25827. rect.applyTransform(el.transform);
  25828. point = [rect.x + rect.width / 2, rect.y + rect.height / 2];
  25829. }
  25830. return {
  25831. point: point,
  25832. el: el
  25833. };
  25834. };
  25835. var each$11 = each$1;
  25836. var curry$3 = curry;
  25837. var get$2 = makeGetter();
  25838. /**
  25839. * Basic logic: check all axis, if they do not demand show/highlight,
  25840. * then hide/downplay them.
  25841. *
  25842. * @param {Object} coordSysAxesInfo
  25843. * @param {Object} payload
  25844. * @param {string} [payload.currTrigger] 'click' | 'mousemove' | 'leave'
  25845. * @param {Array.<number>} [payload.x] x and y, which are mandatory, specify a point to
  25846. * trigger axisPointer and tooltip.
  25847. * @param {Array.<number>} [payload.y] x and y, which are mandatory, specify a point to
  25848. * trigger axisPointer and tooltip.
  25849. * @param {Object} [payload.seriesIndex] finder, optional, restrict target axes.
  25850. * @param {Object} [payload.dataIndex] finder, restrict target axes.
  25851. * @param {Object} [payload.axesInfo] finder, restrict target axes.
  25852. * [{
  25853. * axisDim: 'x'|'y'|'angle'|...,
  25854. * axisIndex: ...,
  25855. * value: ...
  25856. * }, ...]
  25857. * @param {Function} [payload.dispatchAction]
  25858. * @param {Object} [payload.tooltipOption]
  25859. * @param {Object|Array.<number>|Function} [payload.position] Tooltip position,
  25860. * which can be specified in dispatchAction
  25861. * @param {module:echarts/model/Global} ecModel
  25862. * @param {module:echarts/ExtensionAPI} api
  25863. * @return {Object} content of event obj for echarts.connect.
  25864. */
  25865. var axisTrigger = function (payload, ecModel, api) {
  25866. var currTrigger = payload.currTrigger;
  25867. var point = [payload.x, payload.y];
  25868. var finder = payload;
  25869. var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);
  25870. var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; // Pending
  25871. // See #6121. But we are not able to reproduce it yet.
  25872. if (!coordSysAxesInfo) {
  25873. return;
  25874. }
  25875. if (illegalPoint(point)) {
  25876. // Used in the default behavior of `connection`: use the sample seriesIndex
  25877. // and dataIndex. And also used in the tooltipView trigger.
  25878. point = findPointFromSeries({
  25879. seriesIndex: finder.seriesIndex,
  25880. // Do not use dataIndexInside from other ec instance.
  25881. // FIXME: auto detect it?
  25882. dataIndex: finder.dataIndex
  25883. }, ecModel).point;
  25884. }
  25885. var isIllegalPoint = illegalPoint(point); // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).
  25886. // Notice: In this case, it is difficult to get the `point` (which is necessary to show
  25887. // tooltip, so if point is not given, we just use the point found by sample seriesIndex
  25888. // and dataIndex.
  25889. var inputAxesInfo = finder.axesInfo;
  25890. var axesInfo = coordSysAxesInfo.axesInfo;
  25891. var shouldHide = currTrigger === 'leave' || illegalPoint(point);
  25892. var outputFinder = {};
  25893. var showValueMap = {};
  25894. var dataByCoordSys = {
  25895. list: [],
  25896. map: {}
  25897. };
  25898. var updaters = {
  25899. showPointer: curry$3(showPointer, showValueMap),
  25900. showTooltip: curry$3(showTooltip, dataByCoordSys)
  25901. }; // Process for triggered axes.
  25902. each$11(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {
  25903. // If a point given, it must be contained by the coordinate system.
  25904. var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);
  25905. each$11(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {
  25906. var axis = axisInfo.axis;
  25907. var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); // If no inputAxesInfo, no axis is restricted.
  25908. if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {
  25909. var val = inputAxisInfo && inputAxisInfo.value;
  25910. if (val == null && !isIllegalPoint) {
  25911. val = axis.pointToData(point);
  25912. }
  25913. val != null && processOnAxis(axisInfo, val, updaters, false, outputFinder);
  25914. }
  25915. });
  25916. }); // Process for linked axes.
  25917. var linkTriggers = {};
  25918. each$11(axesInfo, function (tarAxisInfo, tarKey) {
  25919. var linkGroup = tarAxisInfo.linkGroup; // If axis has been triggered in the previous stage, it should not be triggered by link.
  25920. if (linkGroup && !showValueMap[tarKey]) {
  25921. each$11(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {
  25922. var srcValItem = showValueMap[srcKey]; // If srcValItem exist, source axis is triggered, so link to target axis.
  25923. if (srcAxisInfo !== tarAxisInfo && srcValItem) {
  25924. var val = srcValItem.value;
  25925. linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo))));
  25926. linkTriggers[tarAxisInfo.key] = val;
  25927. }
  25928. });
  25929. }
  25930. });
  25931. each$11(linkTriggers, function (val, tarKey) {
  25932. processOnAxis(axesInfo[tarKey], val, updaters, true, outputFinder);
  25933. });
  25934. updateModelActually(showValueMap, axesInfo, outputFinder);
  25935. dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);
  25936. dispatchHighDownActually(axesInfo, dispatchAction, api);
  25937. return outputFinder;
  25938. };
  25939. function processOnAxis(axisInfo, newValue, updaters, dontSnap, outputFinder) {
  25940. var axis = axisInfo.axis;
  25941. if (axis.scale.isBlank() || !axis.containData(newValue)) {
  25942. return;
  25943. }
  25944. if (!axisInfo.involveSeries) {
  25945. updaters.showPointer(axisInfo, newValue);
  25946. return;
  25947. } // Heavy calculation. So put it after axis.containData checking.
  25948. var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);
  25949. var payloadBatch = payloadInfo.payloadBatch;
  25950. var snapToValue = payloadInfo.snapToValue; // Fill content of event obj for echarts.connect.
  25951. // By defualt use the first involved series data as a sample to connect.
  25952. if (payloadBatch[0] && outputFinder.seriesIndex == null) {
  25953. extend(outputFinder, payloadBatch[0]);
  25954. } // If no linkSource input, this process is for collecting link
  25955. // target, where snap should not be accepted.
  25956. if (!dontSnap && axisInfo.snap) {
  25957. if (axis.containData(snapToValue) && snapToValue != null) {
  25958. newValue = snapToValue;
  25959. }
  25960. }
  25961. updaters.showPointer(axisInfo, newValue, payloadBatch, outputFinder); // Tooltip should always be snapToValue, otherwise there will be
  25962. // incorrect "axis value ~ series value" mapping displayed in tooltip.
  25963. updaters.showTooltip(axisInfo, payloadInfo, snapToValue);
  25964. }
  25965. function buildPayloadsBySeries(value, axisInfo) {
  25966. var axis = axisInfo.axis;
  25967. var dim = axis.dim;
  25968. var snapToValue = value;
  25969. var payloadBatch = [];
  25970. var minDist = Number.MAX_VALUE;
  25971. var minDiff = -1;
  25972. each$11(axisInfo.seriesModels, function (series, idx) {
  25973. var dataDim = series.coordDimToDataDim(dim);
  25974. var seriesNestestValue;
  25975. var dataIndices;
  25976. if (series.getAxisTooltipData) {
  25977. var result = series.getAxisTooltipData(dataDim, value, axis);
  25978. dataIndices = result.dataIndices;
  25979. seriesNestestValue = result.nestestValue;
  25980. } else {
  25981. dataIndices = series.getData().indicesOfNearest(dataDim[0], value, // Add a threshold to avoid find the wrong dataIndex
  25982. // when data length is not same.
  25983. false, axis.type === 'category' ? 0.5 : null);
  25984. if (!dataIndices.length) {
  25985. return;
  25986. }
  25987. seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);
  25988. }
  25989. if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {
  25990. return;
  25991. }
  25992. var diff = value - seriesNestestValue;
  25993. var dist = Math.abs(diff); // Consider category case
  25994. if (dist <= minDist) {
  25995. if (dist < minDist || diff >= 0 && minDiff < 0) {
  25996. minDist = dist;
  25997. minDiff = diff;
  25998. snapToValue = seriesNestestValue;
  25999. payloadBatch.length = 0;
  26000. }
  26001. each$11(dataIndices, function (dataIndex) {
  26002. payloadBatch.push({
  26003. seriesIndex: series.seriesIndex,
  26004. dataIndexInside: dataIndex,
  26005. dataIndex: series.getData().getRawIndex(dataIndex)
  26006. });
  26007. });
  26008. }
  26009. });
  26010. return {
  26011. payloadBatch: payloadBatch,
  26012. snapToValue: snapToValue
  26013. };
  26014. }
  26015. function showPointer(showValueMap, axisInfo, value, payloadBatch) {
  26016. showValueMap[axisInfo.key] = {
  26017. value: value,
  26018. payloadBatch: payloadBatch
  26019. };
  26020. }
  26021. function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {
  26022. var payloadBatch = payloadInfo.payloadBatch;
  26023. var axis = axisInfo.axis;
  26024. var axisModel = axis.model;
  26025. var axisPointerModel = axisInfo.axisPointerModel; // If no data, do not create anything in dataByCoordSys,
  26026. // whose length will be used to judge whether dispatch action.
  26027. if (!axisInfo.triggerTooltip || !payloadBatch.length) {
  26028. return;
  26029. }
  26030. var coordSysModel = axisInfo.coordSys.model;
  26031. var coordSysKey = makeKey(coordSysModel);
  26032. var coordSysItem = dataByCoordSys.map[coordSysKey];
  26033. if (!coordSysItem) {
  26034. coordSysItem = dataByCoordSys.map[coordSysKey] = {
  26035. coordSysId: coordSysModel.id,
  26036. coordSysIndex: coordSysModel.componentIndex,
  26037. coordSysType: coordSysModel.type,
  26038. coordSysMainType: coordSysModel.mainType,
  26039. dataByAxis: []
  26040. };
  26041. dataByCoordSys.list.push(coordSysItem);
  26042. }
  26043. coordSysItem.dataByAxis.push({
  26044. axisDim: axis.dim,
  26045. axisIndex: axisModel.componentIndex,
  26046. axisType: axisModel.type,
  26047. axisId: axisModel.id,
  26048. value: value,
  26049. // Caustion: viewHelper.getValueLabel is actually on "view stage", which
  26050. // depends that all models have been updated. So it should not be performed
  26051. // here. Considering axisPointerModel used here is volatile, which is hard
  26052. // to be retrieve in TooltipView, we prepare parameters here.
  26053. valueLabelOpt: {
  26054. precision: axisPointerModel.get('label.precision'),
  26055. formatter: axisPointerModel.get('label.formatter')
  26056. },
  26057. seriesDataIndices: payloadBatch.slice()
  26058. });
  26059. }
  26060. function updateModelActually(showValueMap, axesInfo, outputFinder) {
  26061. var outputAxesInfo = outputFinder.axesInfo = []; // Basic logic: If no 'show' required, 'hide' this axisPointer.
  26062. each$11(axesInfo, function (axisInfo, key) {
  26063. var option = axisInfo.axisPointerModel.option;
  26064. var valItem = showValueMap[key];
  26065. if (valItem) {
  26066. !axisInfo.useHandle && (option.status = 'show');
  26067. option.value = valItem.value; // For label formatter param and highlight.
  26068. option.seriesDataIndices = (valItem.payloadBatch || []).slice();
  26069. } // When always show (e.g., handle used), remain
  26070. // original value and status.
  26071. else {
  26072. // If hide, value still need to be set, consider
  26073. // click legend to toggle axis blank.
  26074. !axisInfo.useHandle && (option.status = 'hide');
  26075. } // If status is 'hide', should be no info in payload.
  26076. option.status === 'show' && outputAxesInfo.push({
  26077. axisDim: axisInfo.axis.dim,
  26078. axisIndex: axisInfo.axis.model.componentIndex,
  26079. value: option.value
  26080. });
  26081. });
  26082. }
  26083. function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {
  26084. // Basic logic: If no showTip required, hideTip will be dispatched.
  26085. if (illegalPoint(point) || !dataByCoordSys.list.length) {
  26086. dispatchAction({
  26087. type: 'hideTip'
  26088. });
  26089. return;
  26090. } // In most case only one axis (or event one series is used). It is
  26091. // convinient to fetch payload.seriesIndex and payload.dataIndex
  26092. // dirtectly. So put the first seriesIndex and dataIndex of the first
  26093. // axis on the payload.
  26094. var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};
  26095. dispatchAction({
  26096. type: 'showTip',
  26097. escapeConnect: true,
  26098. x: point[0],
  26099. y: point[1],
  26100. tooltipOption: payload.tooltipOption,
  26101. position: payload.position,
  26102. dataIndexInside: sampleItem.dataIndexInside,
  26103. dataIndex: sampleItem.dataIndex,
  26104. seriesIndex: sampleItem.seriesIndex,
  26105. dataByCoordSys: dataByCoordSys.list
  26106. });
  26107. }
  26108. function dispatchHighDownActually(axesInfo, dispatchAction, api) {
  26109. // FIXME
  26110. // highlight status modification shoule be a stage of main process?
  26111. // (Consider confilct (e.g., legend and axisPointer) and setOption)
  26112. var zr = api.getZr();
  26113. var highDownKey = 'axisPointerLastHighlights';
  26114. var lastHighlights = get$2(zr)[highDownKey] || {};
  26115. var newHighlights = get$2(zr)[highDownKey] = {}; // Update highlight/downplay status according to axisPointer model.
  26116. // Build hash map and remove duplicate incidentally.
  26117. each$11(axesInfo, function (axisInfo, key) {
  26118. var option = axisInfo.axisPointerModel.option;
  26119. option.status === 'show' && each$11(option.seriesDataIndices, function (batchItem) {
  26120. var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;
  26121. newHighlights[key] = batchItem;
  26122. });
  26123. }); // Diff.
  26124. var toHighlight = [];
  26125. var toDownplay = [];
  26126. each$1(lastHighlights, function (batchItem, key) {
  26127. !newHighlights[key] && toDownplay.push(batchItem);
  26128. });
  26129. each$1(newHighlights, function (batchItem, key) {
  26130. !lastHighlights[key] && toHighlight.push(batchItem);
  26131. });
  26132. toDownplay.length && api.dispatchAction({
  26133. type: 'downplay',
  26134. escapeConnect: true,
  26135. batch: toDownplay
  26136. });
  26137. toHighlight.length && api.dispatchAction({
  26138. type: 'highlight',
  26139. escapeConnect: true,
  26140. batch: toHighlight
  26141. });
  26142. }
  26143. function findInputAxisInfo(inputAxesInfo, axisInfo) {
  26144. for (var i = 0; i < (inputAxesInfo || []).length; i++) {
  26145. var inputAxisInfo = inputAxesInfo[i];
  26146. if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) {
  26147. return inputAxisInfo;
  26148. }
  26149. }
  26150. }
  26151. function makeMapperParam(axisInfo) {
  26152. var axisModel = axisInfo.axis.model;
  26153. var item = {};
  26154. var dim = item.axisDim = axisInfo.axis.dim;
  26155. item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;
  26156. item.axisName = item[dim + 'AxisName'] = axisModel.name;
  26157. item.axisId = item[dim + 'AxisId'] = axisModel.id;
  26158. return item;
  26159. }
  26160. function illegalPoint(point) {
  26161. return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);
  26162. }
  26163. var AxisPointerModel = extendComponentModel({
  26164. type: 'axisPointer',
  26165. coordSysAxesInfo: null,
  26166. defaultOption: {
  26167. // 'auto' means that show when triggered by tooltip or handle.
  26168. show: 'auto',
  26169. // 'click' | 'mousemove' | 'none'
  26170. triggerOn: null,
  26171. // set default in AxisPonterView.js
  26172. zlevel: 0,
  26173. z: 50,
  26174. type: 'line',
  26175. // axispointer triggered by tootip determine snap automatically,
  26176. // see `modelHelper`.
  26177. snap: false,
  26178. triggerTooltip: true,
  26179. value: null,
  26180. status: null,
  26181. // Init value depends on whether handle is used.
  26182. // [group0, group1, ...]
  26183. // Each group can be: {
  26184. // mapper: function () {},
  26185. // singleTooltip: 'multiple', // 'multiple' or 'single'
  26186. // xAxisId: ...,
  26187. // yAxisName: ...,
  26188. // angleAxisIndex: ...
  26189. // }
  26190. // mapper: can be ignored.
  26191. // input: {axisInfo, value}
  26192. // output: {axisInfo, value}
  26193. link: [],
  26194. // Do not set 'auto' here, otherwise global animation: false
  26195. // will not effect at this axispointer.
  26196. animation: null,
  26197. animationDurationUpdate: 200,
  26198. lineStyle: {
  26199. color: '#aaa',
  26200. width: 1,
  26201. type: 'solid'
  26202. },
  26203. shadowStyle: {
  26204. color: 'rgba(150,150,150,0.3)'
  26205. },
  26206. label: {
  26207. show: true,
  26208. formatter: null,
  26209. // string | Function
  26210. precision: 'auto',
  26211. // Or a number like 0, 1, 2 ...
  26212. margin: 3,
  26213. color: '#fff',
  26214. padding: [5, 7, 5, 7],
  26215. backgroundColor: 'auto',
  26216. // default: axis line color
  26217. borderColor: null,
  26218. borderWidth: 0,
  26219. shadowBlur: 3,
  26220. shadowColor: '#aaa' // Considering applicability, common style should
  26221. // better not have shadowOffset.
  26222. // shadowOffsetX: 0,
  26223. // shadowOffsetY: 2
  26224. },
  26225. handle: {
  26226. show: false,
  26227. icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z',
  26228. // jshint ignore:line
  26229. size: 45,
  26230. // handle margin is from symbol center to axis, which is stable when circular move.
  26231. margin: 50,
  26232. // color: '#1b8bbd'
  26233. // color: '#2f4554'
  26234. color: '#333',
  26235. shadowBlur: 3,
  26236. shadowColor: '#aaa',
  26237. shadowOffsetX: 0,
  26238. shadowOffsetY: 2,
  26239. // For mobile performance
  26240. throttle: 40
  26241. }
  26242. }
  26243. });
  26244. var get$3 = makeGetter();
  26245. var each$12 = each$1;
  26246. /**
  26247. * @param {string} key
  26248. * @param {module:echarts/ExtensionAPI} api
  26249. * @param {Function} handler
  26250. * param: {string} currTrigger
  26251. * param: {Array.<number>} point
  26252. */
  26253. function register(key, api, handler) {
  26254. if (env$1.node) {
  26255. return;
  26256. }
  26257. var zr = api.getZr();
  26258. get$3(zr).records || (get$3(zr).records = {});
  26259. initGlobalListeners(zr, api);
  26260. var record = get$3(zr).records[key] || (get$3(zr).records[key] = {});
  26261. record.handler = handler;
  26262. }
  26263. function initGlobalListeners(zr, api) {
  26264. if (get$3(zr).initialized) {
  26265. return;
  26266. }
  26267. get$3(zr).initialized = true;
  26268. useHandler('click', curry(doEnter, 'click'));
  26269. useHandler('mousemove', curry(doEnter, 'mousemove')); // useHandler('mouseout', onLeave);
  26270. useHandler('globalout', onLeave);
  26271. function useHandler(eventType, cb) {
  26272. zr.on(eventType, function (e) {
  26273. var dis = makeDispatchAction(api);
  26274. each$12(get$3(zr).records, function (record) {
  26275. record && cb(record, e, dis.dispatchAction);
  26276. });
  26277. dispatchTooltipFinally(dis.pendings, api);
  26278. });
  26279. }
  26280. }
  26281. function dispatchTooltipFinally(pendings, api) {
  26282. var showLen = pendings.showTip.length;
  26283. var hideLen = pendings.hideTip.length;
  26284. var actuallyPayload;
  26285. if (showLen) {
  26286. actuallyPayload = pendings.showTip[showLen - 1];
  26287. } else if (hideLen) {
  26288. actuallyPayload = pendings.hideTip[hideLen - 1];
  26289. }
  26290. if (actuallyPayload) {
  26291. actuallyPayload.dispatchAction = null;
  26292. api.dispatchAction(actuallyPayload);
  26293. }
  26294. }
  26295. function onLeave(record, e, dispatchAction) {
  26296. record.handler('leave', null, dispatchAction);
  26297. }
  26298. function doEnter(currTrigger, record, e, dispatchAction) {
  26299. record.handler(currTrigger, e, dispatchAction);
  26300. }
  26301. function makeDispatchAction(api) {
  26302. var pendings = {
  26303. showTip: [],
  26304. hideTip: []
  26305. }; // FIXME
  26306. // better approach?
  26307. // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,
  26308. // which may be conflict, (axisPointer call showTip but tooltip call hideTip);
  26309. // So we have to add "final stage" to merge those dispatched actions.
  26310. var dispatchAction = function (payload) {
  26311. var pendingList = pendings[payload.type];
  26312. if (pendingList) {
  26313. pendingList.push(payload);
  26314. } else {
  26315. payload.dispatchAction = dispatchAction;
  26316. api.dispatchAction(payload);
  26317. }
  26318. };
  26319. return {
  26320. dispatchAction: dispatchAction,
  26321. pendings: pendings
  26322. };
  26323. }
  26324. /**
  26325. * @param {string} key
  26326. * @param {module:echarts/ExtensionAPI} api
  26327. */
  26328. function unregister(key, api) {
  26329. if (env$1.node) {
  26330. return;
  26331. }
  26332. var zr = api.getZr();
  26333. var record = (get$3(zr).records || {})[key];
  26334. if (record) {
  26335. get$3(zr).records[key] = null;
  26336. }
  26337. }
  26338. var AxisPointerView = extendComponentView({
  26339. type: 'axisPointer',
  26340. render: function (globalAxisPointerModel, ecModel, api) {
  26341. var globalTooltipModel = ecModel.getComponent('tooltip');
  26342. var triggerOn = globalAxisPointerModel.get('triggerOn') || globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click'; // Register global listener in AxisPointerView to enable
  26343. // AxisPointerView to be independent to Tooltip.
  26344. register('axisPointer', api, function (currTrigger, e, dispatchAction) {
  26345. // If 'none', it is not controlled by mouse totally.
  26346. if (triggerOn !== 'none' && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)) {
  26347. dispatchAction({
  26348. type: 'updateAxisPointer',
  26349. currTrigger: currTrigger,
  26350. x: e && e.offsetX,
  26351. y: e && e.offsetY
  26352. });
  26353. }
  26354. });
  26355. },
  26356. /**
  26357. * @override
  26358. */
  26359. remove: function (ecModel, api) {
  26360. unregister(api.getZr(), 'axisPointer');
  26361. AxisPointerView.superApply(this._model, 'remove', arguments);
  26362. },
  26363. /**
  26364. * @override
  26365. */
  26366. dispose: function (ecModel, api) {
  26367. unregister('axisPointer', api);
  26368. AxisPointerView.superApply(this._model, 'dispose', arguments);
  26369. }
  26370. });
  26371. var get$4 = makeGetter();
  26372. var clone$3 = clone;
  26373. var bind$1 = bind;
  26374. /**
  26375. * Base axis pointer class in 2D.
  26376. * Implemenents {module:echarts/component/axis/IAxisPointer}.
  26377. */
  26378. function BaseAxisPointer() {}
  26379. BaseAxisPointer.prototype = {
  26380. /**
  26381. * @private
  26382. */
  26383. _group: null,
  26384. /**
  26385. * @private
  26386. */
  26387. _lastGraphicKey: null,
  26388. /**
  26389. * @private
  26390. */
  26391. _handle: null,
  26392. /**
  26393. * @private
  26394. */
  26395. _dragging: false,
  26396. /**
  26397. * @private
  26398. */
  26399. _lastValue: null,
  26400. /**
  26401. * @private
  26402. */
  26403. _lastStatus: null,
  26404. /**
  26405. * @private
  26406. */
  26407. _payloadInfo: null,
  26408. /**
  26409. * In px, arbitrary value. Do not set too small,
  26410. * no animation is ok for most cases.
  26411. * @protected
  26412. */
  26413. animationThreshold: 15,
  26414. /**
  26415. * @implement
  26416. */
  26417. render: function (axisModel, axisPointerModel, api, forceRender) {
  26418. var value = axisPointerModel.get('value');
  26419. var status = axisPointerModel.get('status'); // Bind them to `this`, not in closure, otherwise they will not
  26420. // be replaced when user calling setOption in not merge mode.
  26421. this._axisModel = axisModel;
  26422. this._axisPointerModel = axisPointerModel;
  26423. this._api = api; // Optimize: `render` will be called repeatly during mouse move.
  26424. // So it is power consuming if performing `render` each time,
  26425. // especially on mobile device.
  26426. if (!forceRender && this._lastValue === value && this._lastStatus === status) {
  26427. return;
  26428. }
  26429. this._lastValue = value;
  26430. this._lastStatus = status;
  26431. var group = this._group;
  26432. var handle = this._handle;
  26433. if (!status || status === 'hide') {
  26434. // Do not clear here, for animation better.
  26435. group && group.hide();
  26436. handle && handle.hide();
  26437. return;
  26438. }
  26439. group && group.show();
  26440. handle && handle.show(); // Otherwise status is 'show'
  26441. var elOption = {};
  26442. this.makeElOption(elOption, value, axisModel, axisPointerModel, api); // Enable change axis pointer type.
  26443. var graphicKey = elOption.graphicKey;
  26444. if (graphicKey !== this._lastGraphicKey) {
  26445. this.clear(api);
  26446. }
  26447. this._lastGraphicKey = graphicKey;
  26448. var moveAnimation = this._moveAnimation = this.determineAnimation(axisModel, axisPointerModel);
  26449. if (!group) {
  26450. group = this._group = new Group();
  26451. this.createPointerEl(group, elOption, axisModel, axisPointerModel);
  26452. this.createLabelEl(group, elOption, axisModel, axisPointerModel);
  26453. api.getZr().add(group);
  26454. } else {
  26455. var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation);
  26456. this.updatePointerEl(group, elOption, doUpdateProps, axisPointerModel);
  26457. this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);
  26458. }
  26459. updateMandatoryProps(group, axisPointerModel, true);
  26460. this._renderHandle(value);
  26461. },
  26462. /**
  26463. * @implement
  26464. */
  26465. remove: function (api) {
  26466. this.clear(api);
  26467. },
  26468. /**
  26469. * @implement
  26470. */
  26471. dispose: function (api) {
  26472. this.clear(api);
  26473. },
  26474. /**
  26475. * @protected
  26476. */
  26477. determineAnimation: function (axisModel, axisPointerModel) {
  26478. var animation = axisPointerModel.get('animation');
  26479. var axis = axisModel.axis;
  26480. var isCategoryAxis = axis.type === 'category';
  26481. var useSnap = axisPointerModel.get('snap'); // Value axis without snap always do not snap.
  26482. if (!useSnap && !isCategoryAxis) {
  26483. return false;
  26484. }
  26485. if (animation === 'auto' || animation == null) {
  26486. var animationThreshold = this.animationThreshold;
  26487. if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {
  26488. return true;
  26489. } // It is important to auto animation when snap used. Consider if there is
  26490. // a dataZoom, animation will be disabled when too many points exist, while
  26491. // it will be enabled for better visual effect when little points exist.
  26492. if (useSnap) {
  26493. var seriesDataCount = getAxisInfo(axisModel).seriesDataCount;
  26494. var axisExtent = axis.getExtent(); // Approximate band width
  26495. return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;
  26496. }
  26497. return false;
  26498. }
  26499. return animation === true;
  26500. },
  26501. /**
  26502. * add {pointer, label, graphicKey} to elOption
  26503. * @protected
  26504. */
  26505. makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {// Shoule be implemenented by sub-class.
  26506. },
  26507. /**
  26508. * @protected
  26509. */
  26510. createPointerEl: function (group, elOption, axisModel, axisPointerModel) {
  26511. var pointerOption = elOption.pointer;
  26512. if (pointerOption) {
  26513. var pointerEl = get$4(group).pointerEl = new graphic[pointerOption.type](clone$3(elOption.pointer));
  26514. group.add(pointerEl);
  26515. }
  26516. },
  26517. /**
  26518. * @protected
  26519. */
  26520. createLabelEl: function (group, elOption, axisModel, axisPointerModel) {
  26521. if (elOption.label) {
  26522. var labelEl = get$4(group).labelEl = new Rect(clone$3(elOption.label));
  26523. group.add(labelEl);
  26524. updateLabelShowHide(labelEl, axisPointerModel);
  26525. }
  26526. },
  26527. /**
  26528. * @protected
  26529. */
  26530. updatePointerEl: function (group, elOption, updateProps$$1) {
  26531. var pointerEl = get$4(group).pointerEl;
  26532. if (pointerEl) {
  26533. pointerEl.setStyle(elOption.pointer.style);
  26534. updateProps$$1(pointerEl, {
  26535. shape: elOption.pointer.shape
  26536. });
  26537. }
  26538. },
  26539. /**
  26540. * @protected
  26541. */
  26542. updateLabelEl: function (group, elOption, updateProps$$1, axisPointerModel) {
  26543. var labelEl = get$4(group).labelEl;
  26544. if (labelEl) {
  26545. labelEl.setStyle(elOption.label.style);
  26546. updateProps$$1(labelEl, {
  26547. // Consider text length change in vertical axis, animation should
  26548. // be used on shape, otherwise the effect will be weird.
  26549. shape: elOption.label.shape,
  26550. position: elOption.label.position
  26551. });
  26552. updateLabelShowHide(labelEl, axisPointerModel);
  26553. }
  26554. },
  26555. /**
  26556. * @private
  26557. */
  26558. _renderHandle: function (value) {
  26559. if (this._dragging || !this.updateHandleTransform) {
  26560. return;
  26561. }
  26562. var axisPointerModel = this._axisPointerModel;
  26563. var zr = this._api.getZr();
  26564. var handle = this._handle;
  26565. var handleModel = axisPointerModel.getModel('handle');
  26566. var status = axisPointerModel.get('status');
  26567. if (!handleModel.get('show') || !status || status === 'hide') {
  26568. handle && zr.remove(handle);
  26569. this._handle = null;
  26570. return;
  26571. }
  26572. var isInit;
  26573. if (!this._handle) {
  26574. isInit = true;
  26575. handle = this._handle = createIcon(handleModel.get('icon'), {
  26576. cursor: 'move',
  26577. draggable: true,
  26578. onmousemove: function (e) {
  26579. // Fot mobile devicem, prevent screen slider on the button.
  26580. stop(e.event);
  26581. },
  26582. onmousedown: bind$1(this._onHandleDragMove, this, 0, 0),
  26583. drift: bind$1(this._onHandleDragMove, this),
  26584. ondragend: bind$1(this._onHandleDragEnd, this)
  26585. });
  26586. zr.add(handle);
  26587. }
  26588. updateMandatoryProps(handle, axisPointerModel, false); // update style
  26589. var includeStyles = ['color', 'borderColor', 'borderWidth', 'opacity', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'];
  26590. handle.setStyle(handleModel.getItemStyle(null, includeStyles)); // update position
  26591. var handleSize = handleModel.get('size');
  26592. if (!isArray(handleSize)) {
  26593. handleSize = [handleSize, handleSize];
  26594. }
  26595. handle.attr('scale', [handleSize[0] / 2, handleSize[1] / 2]);
  26596. createOrUpdate(this, '_doDispatchAxisPointer', handleModel.get('throttle') || 0, 'fixRate');
  26597. this._moveHandleToValue(value, isInit);
  26598. },
  26599. /**
  26600. * @private
  26601. */
  26602. _moveHandleToValue: function (value, isInit) {
  26603. updateProps$1(this._axisPointerModel, !isInit && this._moveAnimation, this._handle, getHandleTransProps(this.getHandleTransform(value, this._axisModel, this._axisPointerModel)));
  26604. },
  26605. /**
  26606. * @private
  26607. */
  26608. _onHandleDragMove: function (dx, dy) {
  26609. var handle = this._handle;
  26610. if (!handle) {
  26611. return;
  26612. }
  26613. this._dragging = true; // Persistent for throttle.
  26614. var trans = this.updateHandleTransform(getHandleTransProps(handle), [dx, dy], this._axisModel, this._axisPointerModel);
  26615. this._payloadInfo = trans;
  26616. handle.stopAnimation();
  26617. handle.attr(getHandleTransProps(trans));
  26618. get$4(handle).lastProp = null;
  26619. this._doDispatchAxisPointer();
  26620. },
  26621. /**
  26622. * Throttled method.
  26623. * @private
  26624. */
  26625. _doDispatchAxisPointer: function () {
  26626. var handle = this._handle;
  26627. if (!handle) {
  26628. return;
  26629. }
  26630. var payloadInfo = this._payloadInfo;
  26631. var axisModel = this._axisModel;
  26632. this._api.dispatchAction({
  26633. type: 'updateAxisPointer',
  26634. x: payloadInfo.cursorPoint[0],
  26635. y: payloadInfo.cursorPoint[1],
  26636. tooltipOption: payloadInfo.tooltipOption,
  26637. axesInfo: [{
  26638. axisDim: axisModel.axis.dim,
  26639. axisIndex: axisModel.componentIndex
  26640. }]
  26641. });
  26642. },
  26643. /**
  26644. * @private
  26645. */
  26646. _onHandleDragEnd: function (moveAnimation) {
  26647. this._dragging = false;
  26648. var handle = this._handle;
  26649. if (!handle) {
  26650. return;
  26651. }
  26652. var value = this._axisPointerModel.get('value'); // Consider snap or categroy axis, handle may be not consistent with
  26653. // axisPointer. So move handle to align the exact value position when
  26654. // drag ended.
  26655. this._moveHandleToValue(value); // For the effect: tooltip will be shown when finger holding on handle
  26656. // button, and will be hidden after finger left handle button.
  26657. this._api.dispatchAction({
  26658. type: 'hideTip'
  26659. });
  26660. },
  26661. /**
  26662. * Should be implemenented by sub-class if support `handle`.
  26663. * @protected
  26664. * @param {number} value
  26665. * @param {module:echarts/model/Model} axisModel
  26666. * @param {module:echarts/model/Model} axisPointerModel
  26667. * @return {Object} {position: [x, y], rotation: 0}
  26668. */
  26669. getHandleTransform: null,
  26670. /**
  26671. * * Should be implemenented by sub-class if support `handle`.
  26672. * @protected
  26673. * @param {Object} transform {position, rotation}
  26674. * @param {Array.<number>} delta [dx, dy]
  26675. * @param {module:echarts/model/Model} axisModel
  26676. * @param {module:echarts/model/Model} axisPointerModel
  26677. * @return {Object} {position: [x, y], rotation: 0, cursorPoint: [x, y]}
  26678. */
  26679. updateHandleTransform: null,
  26680. /**
  26681. * @private
  26682. */
  26683. clear: function (api) {
  26684. this._lastValue = null;
  26685. this._lastStatus = null;
  26686. var zr = api.getZr();
  26687. var group = this._group;
  26688. var handle = this._handle;
  26689. if (zr && group) {
  26690. this._lastGraphicKey = null;
  26691. group && zr.remove(group);
  26692. handle && zr.remove(handle);
  26693. this._group = null;
  26694. this._handle = null;
  26695. this._payloadInfo = null;
  26696. }
  26697. },
  26698. /**
  26699. * @protected
  26700. */
  26701. doClear: function () {// Implemented by sub-class if necessary.
  26702. },
  26703. /**
  26704. * @protected
  26705. * @param {Array.<number>} xy
  26706. * @param {Array.<number>} wh
  26707. * @param {number} [xDimIndex=0] or 1
  26708. */
  26709. buildLabel: function (xy, wh, xDimIndex) {
  26710. xDimIndex = xDimIndex || 0;
  26711. return {
  26712. x: xy[xDimIndex],
  26713. y: xy[1 - xDimIndex],
  26714. width: wh[xDimIndex],
  26715. height: wh[1 - xDimIndex]
  26716. };
  26717. }
  26718. };
  26719. BaseAxisPointer.prototype.constructor = BaseAxisPointer;
  26720. function updateProps$1(animationModel, moveAnimation, el, props) {
  26721. // Animation optimize.
  26722. if (!propsEqual(get$4(el).lastProp, props)) {
  26723. get$4(el).lastProp = props;
  26724. moveAnimation ? updateProps(el, props, animationModel) : (el.stopAnimation(), el.attr(props));
  26725. }
  26726. }
  26727. function propsEqual(lastProps, newProps) {
  26728. if (isObject(lastProps) && isObject(newProps)) {
  26729. var equals = true;
  26730. each$1(newProps, function (item, key) {
  26731. equals = equals && propsEqual(lastProps[key], item);
  26732. });
  26733. return !!equals;
  26734. } else {
  26735. return lastProps === newProps;
  26736. }
  26737. }
  26738. function updateLabelShowHide(labelEl, axisPointerModel) {
  26739. labelEl[axisPointerModel.get('label.show') ? 'show' : 'hide']();
  26740. }
  26741. function getHandleTransProps(trans) {
  26742. return {
  26743. position: trans.position.slice(),
  26744. rotation: trans.rotation || 0
  26745. };
  26746. }
  26747. function updateMandatoryProps(group, axisPointerModel, silent) {
  26748. var z = axisPointerModel.get('z');
  26749. var zlevel = axisPointerModel.get('zlevel');
  26750. group && group.traverse(function (el) {
  26751. if (el.type !== 'group') {
  26752. z != null && (el.z = z);
  26753. zlevel != null && (el.zlevel = zlevel);
  26754. el.silent = silent;
  26755. }
  26756. });
  26757. }
  26758. enableClassExtend(BaseAxisPointer);
  26759. /**
  26760. * @param {module:echarts/model/Model} axisPointerModel
  26761. */
  26762. function buildElStyle(axisPointerModel) {
  26763. var axisPointerType = axisPointerModel.get('type');
  26764. var styleModel = axisPointerModel.getModel(axisPointerType + 'Style');
  26765. var style;
  26766. if (axisPointerType === 'line') {
  26767. style = styleModel.getLineStyle();
  26768. style.fill = null;
  26769. } else if (axisPointerType === 'shadow') {
  26770. style = styleModel.getAreaStyle();
  26771. style.stroke = null;
  26772. }
  26773. return style;
  26774. }
  26775. /**
  26776. * @param {Function} labelPos {align, verticalAlign, position}
  26777. */
  26778. function buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos) {
  26779. var value = axisPointerModel.get('value');
  26780. var text = getValueLabel(value, axisModel.axis, axisModel.ecModel, axisPointerModel.get('seriesDataIndices'), {
  26781. precision: axisPointerModel.get('label.precision'),
  26782. formatter: axisPointerModel.get('label.formatter')
  26783. });
  26784. var labelModel = axisPointerModel.getModel('label');
  26785. var paddings = normalizeCssArray$1(labelModel.get('padding') || 0);
  26786. var font = labelModel.getFont();
  26787. var textRect = getBoundingRect(text, font);
  26788. var position = labelPos.position;
  26789. var width = textRect.width + paddings[1] + paddings[3];
  26790. var height = textRect.height + paddings[0] + paddings[2]; // Adjust by align.
  26791. var align = labelPos.align;
  26792. align === 'right' && (position[0] -= width);
  26793. align === 'center' && (position[0] -= width / 2);
  26794. var verticalAlign = labelPos.verticalAlign;
  26795. verticalAlign === 'bottom' && (position[1] -= height);
  26796. verticalAlign === 'middle' && (position[1] -= height / 2); // Not overflow ec container
  26797. confineInContainer(position, width, height, api);
  26798. var bgColor = labelModel.get('backgroundColor');
  26799. if (!bgColor || bgColor === 'auto') {
  26800. bgColor = axisModel.get('axisLine.lineStyle.color');
  26801. }
  26802. elOption.label = {
  26803. shape: {
  26804. x: 0,
  26805. y: 0,
  26806. width: width,
  26807. height: height,
  26808. r: labelModel.get('borderRadius')
  26809. },
  26810. position: position.slice(),
  26811. // TODO: rich
  26812. style: {
  26813. text: text,
  26814. textFont: font,
  26815. textFill: labelModel.getTextColor(),
  26816. textPosition: 'inside',
  26817. fill: bgColor,
  26818. stroke: labelModel.get('borderColor') || 'transparent',
  26819. lineWidth: labelModel.get('borderWidth') || 0,
  26820. shadowBlur: labelModel.get('shadowBlur'),
  26821. shadowColor: labelModel.get('shadowColor'),
  26822. shadowOffsetX: labelModel.get('shadowOffsetX'),
  26823. shadowOffsetY: labelModel.get('shadowOffsetY')
  26824. },
  26825. // Lable should be over axisPointer.
  26826. z2: 10
  26827. };
  26828. } // Do not overflow ec container
  26829. function confineInContainer(position, width, height, api) {
  26830. var viewWidth = api.getWidth();
  26831. var viewHeight = api.getHeight();
  26832. position[0] = Math.min(position[0] + width, viewWidth) - width;
  26833. position[1] = Math.min(position[1] + height, viewHeight) - height;
  26834. position[0] = Math.max(position[0], 0);
  26835. position[1] = Math.max(position[1], 0);
  26836. }
  26837. /**
  26838. * @param {number} value
  26839. * @param {module:echarts/coord/Axis} axis
  26840. * @param {module:echarts/model/Global} ecModel
  26841. * @param {Object} opt
  26842. * @param {Array.<Object>} seriesDataIndices
  26843. * @param {number|string} opt.precision 'auto' or a number
  26844. * @param {string|Function} opt.formatter label formatter
  26845. */
  26846. function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) {
  26847. var text = axis.scale.getLabel( // If `precision` is set, width can be fixed (like '12.00500'), which
  26848. // helps to debounce when when moving label.
  26849. value, {
  26850. precision: opt.precision
  26851. });
  26852. var formatter = opt.formatter;
  26853. if (formatter) {
  26854. var params = {
  26855. value: getAxisRawValue(axis, value),
  26856. seriesData: []
  26857. };
  26858. each$1(seriesDataIndices, function (idxItem) {
  26859. var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
  26860. var dataIndex = idxItem.dataIndexInside;
  26861. var dataParams = series && series.getDataParams(dataIndex);
  26862. dataParams && params.seriesData.push(dataParams);
  26863. });
  26864. if (isString(formatter)) {
  26865. text = formatter.replace('{value}', text);
  26866. } else if (isFunction(formatter)) {
  26867. text = formatter(params);
  26868. }
  26869. }
  26870. return text;
  26871. }
  26872. /**
  26873. * @param {module:echarts/coord/Axis} axis
  26874. * @param {number} value
  26875. * @param {Object} layoutInfo {
  26876. * rotation, position, labelOffset, labelDirection, labelMargin
  26877. * }
  26878. */
  26879. function getTransformedPosition(axis, value, layoutInfo) {
  26880. var transform = create$1();
  26881. rotate(transform, transform, layoutInfo.rotation);
  26882. translate(transform, transform, layoutInfo.position);
  26883. return applyTransform$1([axis.dataToCoord(value), (layoutInfo.labelOffset || 0) + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)], transform);
  26884. }
  26885. function buildCartesianSingleLabelElOption(value, elOption, layoutInfo, axisModel, axisPointerModel, api) {
  26886. var textLayout = AxisBuilder.innerTextLayout(layoutInfo.rotation, 0, layoutInfo.labelDirection);
  26887. layoutInfo.labelMargin = axisPointerModel.get('label.margin');
  26888. buildLabelElOption(elOption, axisModel, axisPointerModel, api, {
  26889. position: getTransformedPosition(axisModel.axis, value, layoutInfo),
  26890. align: textLayout.textAlign,
  26891. verticalAlign: textLayout.textVerticalAlign
  26892. });
  26893. }
  26894. /**
  26895. * @param {Array.<number>} p1
  26896. * @param {Array.<number>} p2
  26897. * @param {number} [xDimIndex=0] or 1
  26898. */
  26899. function makeLineShape(p1, p2, xDimIndex) {
  26900. xDimIndex = xDimIndex || 0;
  26901. return {
  26902. x1: p1[xDimIndex],
  26903. y1: p1[1 - xDimIndex],
  26904. x2: p2[xDimIndex],
  26905. y2: p2[1 - xDimIndex]
  26906. };
  26907. }
  26908. /**
  26909. * @param {Array.<number>} xy
  26910. * @param {Array.<number>} wh
  26911. * @param {number} [xDimIndex=0] or 1
  26912. */
  26913. function makeRectShape(xy, wh, xDimIndex) {
  26914. xDimIndex = xDimIndex || 0;
  26915. return {
  26916. x: xy[xDimIndex],
  26917. y: xy[1 - xDimIndex],
  26918. width: wh[xDimIndex],
  26919. height: wh[1 - xDimIndex]
  26920. };
  26921. }
  26922. var CartesianAxisPointer = BaseAxisPointer.extend({
  26923. /**
  26924. * @override
  26925. */
  26926. makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
  26927. var axis = axisModel.axis;
  26928. var grid = axis.grid;
  26929. var axisPointerType = axisPointerModel.get('type');
  26930. var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
  26931. var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true));
  26932. if (axisPointerType && axisPointerType !== 'none') {
  26933. var elStyle = buildElStyle(axisPointerModel);
  26934. var pointerOption = pointerShapeBuilder[axisPointerType](axis, pixelValue, otherExtent, elStyle);
  26935. pointerOption.style = elStyle;
  26936. elOption.graphicKey = pointerOption.type;
  26937. elOption.pointer = pointerOption;
  26938. }
  26939. var layoutInfo = layout(grid.model, axisModel);
  26940. buildCartesianSingleLabelElOption(value, elOption, layoutInfo, axisModel, axisPointerModel, api);
  26941. },
  26942. /**
  26943. * @override
  26944. */
  26945. getHandleTransform: function (value, axisModel, axisPointerModel) {
  26946. var layoutInfo = layout(axisModel.axis.grid.model, axisModel, {
  26947. labelInside: false
  26948. });
  26949. layoutInfo.labelMargin = axisPointerModel.get('handle.margin');
  26950. return {
  26951. position: getTransformedPosition(axisModel.axis, value, layoutInfo),
  26952. rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)
  26953. };
  26954. },
  26955. /**
  26956. * @override
  26957. */
  26958. updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) {
  26959. var axis = axisModel.axis;
  26960. var grid = axis.grid;
  26961. var axisExtent = axis.getGlobalExtent(true);
  26962. var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
  26963. var dimIndex = axis.dim === 'x' ? 0 : 1;
  26964. var currPosition = transform.position;
  26965. currPosition[dimIndex] += delta[dimIndex];
  26966. currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);
  26967. currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);
  26968. var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;
  26969. var cursorPoint = [cursorOtherValue, cursorOtherValue];
  26970. cursorPoint[dimIndex] = currPosition[dimIndex]; // Make tooltip do not overlap axisPointer and in the middle of the grid.
  26971. var tooltipOptions = [{
  26972. verticalAlign: 'middle'
  26973. }, {
  26974. align: 'center'
  26975. }];
  26976. return {
  26977. position: currPosition,
  26978. rotation: transform.rotation,
  26979. cursorPoint: cursorPoint,
  26980. tooltipOption: tooltipOptions[dimIndex]
  26981. };
  26982. }
  26983. });
  26984. function getCartesian(grid, axis) {
  26985. var opt = {};
  26986. opt[axis.dim + 'AxisIndex'] = axis.index;
  26987. return grid.getCartesian(opt);
  26988. }
  26989. var pointerShapeBuilder = {
  26990. line: function (axis, pixelValue, otherExtent, elStyle) {
  26991. var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getAxisDimIndex(axis));
  26992. subPixelOptimizeLine({
  26993. shape: targetShape,
  26994. style: elStyle
  26995. });
  26996. return {
  26997. type: 'Line',
  26998. shape: targetShape
  26999. };
  27000. },
  27001. shadow: function (axis, pixelValue, otherExtent, elStyle) {
  27002. var bandWidth = axis.getBandWidth();
  27003. var span = otherExtent[1] - otherExtent[0];
  27004. return {
  27005. type: 'Rect',
  27006. shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getAxisDimIndex(axis))
  27007. };
  27008. }
  27009. };
  27010. function getAxisDimIndex(axis) {
  27011. return axis.dim === 'x' ? 0 : 1;
  27012. }
  27013. AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer); // CartesianAxisPointer is not supposed to be required here. But consider
  27014. // echarts.simple.js and online build tooltip, which only require gridSimple,
  27015. // CartesianAxisPointer should be able to required somewhere.
  27016. registerPreprocessor(function (option) {
  27017. // Always has a global axisPointerModel for default setting.
  27018. if (option) {
  27019. (!option.axisPointer || option.axisPointer.length === 0) && (option.axisPointer = {});
  27020. var link = option.axisPointer.link; // Normalize to array to avoid object mergin. But if link
  27021. // is not set, remain null/undefined, otherwise it will
  27022. // override existent link setting.
  27023. if (link && !isArray(link)) {
  27024. option.axisPointer.link = [link];
  27025. }
  27026. }
  27027. }); // This process should proformed after coordinate systems created
  27028. // and series data processed. So put it on statistic processing stage.
  27029. registerProcessor(PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) {
  27030. // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
  27031. // allAxesInfo should be updated when setOption performed.
  27032. ecModel.getComponent('axisPointer').coordSysAxesInfo = collect(ecModel, api);
  27033. }); // Broadcast to all views.
  27034. registerAction({
  27035. type: 'updateAxisPointer',
  27036. event: 'updateAxisPointer',
  27037. update: ':updateAxisPointer'
  27038. }, axisTrigger);
  27039. extendComponentModel({
  27040. type: 'tooltip',
  27041. dependencies: ['axisPointer'],
  27042. defaultOption: {
  27043. zlevel: 0,
  27044. z: 8,
  27045. show: true,
  27046. // tooltip主体内容
  27047. showContent: true,
  27048. // 'trigger' only works on coordinate system.
  27049. // 'item' | 'axis' | 'none'
  27050. trigger: 'item',
  27051. // 'click' | 'mousemove' | 'none'
  27052. triggerOn: 'mousemove|click',
  27053. alwaysShowContent: false,
  27054. displayMode: 'single',
  27055. // 'single' | 'multipleByCoordSys'
  27056. // 位置 {Array} | {Function}
  27057. // position: null
  27058. // Consider triggered from axisPointer handle, verticalAlign should be 'middle'
  27059. // align: null,
  27060. // verticalAlign: null,
  27061. // 是否约束 content 在 viewRect 中。默认 false 是为了兼容以前版本。
  27062. confine: false,
  27063. // 内容格式器:{string}(Template) ¦ {Function}
  27064. // formatter: null
  27065. showDelay: 0,
  27066. // 隐藏延迟,单位ms
  27067. hideDelay: 100,
  27068. // 动画变换时间,单位s
  27069. transitionDuration: 0.4,
  27070. enterable: false,
  27071. // 提示背景颜色,默认为透明度为0.7的黑色
  27072. backgroundColor: 'rgba(50,50,50,0.7)',
  27073. // 提示边框颜色
  27074. borderColor: '#333',
  27075. // 提示边框圆角,单位px,默认为4
  27076. borderRadius: 4,
  27077. // 提示边框线宽,单位px,默认为0(无边框)
  27078. borderWidth: 0,
  27079. // 提示内边距,单位px,默认各方向内边距为5,
  27080. // 接受数组分别设定上右下左边距,同css
  27081. padding: 5,
  27082. // Extra css text
  27083. extraCssText: '',
  27084. // 坐标轴指示器,坐标轴触发有效
  27085. axisPointer: {
  27086. // 默认为直线
  27087. // 可选为:'line' | 'shadow' | 'cross'
  27088. type: 'line',
  27089. // type 为 line 的时候有效,指定 tooltip line 所在的轴,可选
  27090. // 可选 'x' | 'y' | 'angle' | 'radius' | 'auto'
  27091. // 默认 'auto',会选择类型为 cateogry 的轴,对于双数值轴,笛卡尔坐标系会默认选择 x 轴
  27092. // 极坐标系会默认选择 angle 轴
  27093. axis: 'auto',
  27094. animation: 'auto',
  27095. animationDurationUpdate: 200,
  27096. animationEasingUpdate: 'exponentialOut',
  27097. crossStyle: {
  27098. color: '#999',
  27099. width: 1,
  27100. type: 'dashed',
  27101. // TODO formatter
  27102. textStyle: {}
  27103. } // lineStyle and shadowStyle should not be specified here,
  27104. // otherwise it will always override those styles on option.axisPointer.
  27105. },
  27106. textStyle: {
  27107. color: '#fff',
  27108. fontSize: 14
  27109. }
  27110. }
  27111. });
  27112. var each$14 = each$1;
  27113. var toCamelCase$1 = toCamelCase;
  27114. var vendors = ['', '-webkit-', '-moz-', '-o-'];
  27115. var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;';
  27116. /**
  27117. * @param {number} duration
  27118. * @return {string}
  27119. * @inner
  27120. */
  27121. function assembleTransition(duration) {
  27122. var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)';
  27123. var transitionText = 'left ' + duration + 's ' + transitionCurve + ',' + 'top ' + duration + 's ' + transitionCurve;
  27124. return map(vendors, function (vendorPrefix) {
  27125. return vendorPrefix + 'transition:' + transitionText;
  27126. }).join(';');
  27127. }
  27128. /**
  27129. * @param {Object} textStyle
  27130. * @return {string}
  27131. * @inner
  27132. */
  27133. function assembleFont(textStyleModel) {
  27134. var cssText = [];
  27135. var fontSize = textStyleModel.get('fontSize');
  27136. var color = textStyleModel.getTextColor();
  27137. color && cssText.push('color:' + color);
  27138. cssText.push('font:' + textStyleModel.getFont());
  27139. fontSize && cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');
  27140. each$14(['decoration', 'align'], function (name) {
  27141. var val = textStyleModel.get(name);
  27142. val && cssText.push('text-' + name + ':' + val);
  27143. });
  27144. return cssText.join(';');
  27145. }
  27146. /**
  27147. * @param {Object} tooltipModel
  27148. * @return {string}
  27149. * @inner
  27150. */
  27151. function assembleCssText(tooltipModel) {
  27152. var cssText = [];
  27153. var transitionDuration = tooltipModel.get('transitionDuration');
  27154. var backgroundColor = tooltipModel.get('backgroundColor');
  27155. var textStyleModel = tooltipModel.getModel('textStyle');
  27156. var padding = tooltipModel.get('padding'); // Animation transition. Do not animate when transitionDuration is 0.
  27157. transitionDuration && cssText.push(assembleTransition(transitionDuration));
  27158. if (backgroundColor) {
  27159. if (env$1.canvasSupported) {
  27160. cssText.push('background-Color:' + backgroundColor);
  27161. } else {
  27162. // for ie
  27163. cssText.push('background-Color:#' + toHex(backgroundColor));
  27164. cssText.push('filter:alpha(opacity=70)');
  27165. }
  27166. } // Border style
  27167. each$14(['width', 'color', 'radius'], function (name) {
  27168. var borderName = 'border-' + name;
  27169. var camelCase = toCamelCase$1(borderName);
  27170. var val = tooltipModel.get(camelCase);
  27171. val != null && cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));
  27172. }); // Text style
  27173. cssText.push(assembleFont(textStyleModel)); // Padding
  27174. if (padding != null) {
  27175. cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px');
  27176. }
  27177. return cssText.join(';') + ';';
  27178. }
  27179. /**
  27180. * @alias module:echarts/component/tooltip/TooltipContent
  27181. * @constructor
  27182. */
  27183. function TooltipContent(container, api) {
  27184. var el = document.createElement('div');
  27185. var zr = this._zr = api.getZr();
  27186. this.el = el;
  27187. this._x = api.getWidth() / 2;
  27188. this._y = api.getHeight() / 2;
  27189. container.appendChild(el);
  27190. this._container = container;
  27191. this._show = false;
  27192. /**
  27193. * @private
  27194. */
  27195. this._hideTimeout;
  27196. var self = this;
  27197. el.onmouseenter = function () {
  27198. // clear the timeout in hideLater and keep showing tooltip
  27199. if (self._enterable) {
  27200. clearTimeout(self._hideTimeout);
  27201. self._show = true;
  27202. }
  27203. self._inContent = true;
  27204. };
  27205. el.onmousemove = function (e) {
  27206. e = e || window.event;
  27207. if (!self._enterable) {
  27208. // Try trigger zrender event to avoid mouse
  27209. // in and out shape too frequently
  27210. var handler = zr.handler;
  27211. normalizeEvent(container, e, true);
  27212. handler.dispatch('mousemove', e);
  27213. }
  27214. };
  27215. el.onmouseleave = function () {
  27216. if (self._enterable) {
  27217. if (self._show) {
  27218. self.hideLater(self._hideDelay);
  27219. }
  27220. }
  27221. self._inContent = false;
  27222. };
  27223. }
  27224. TooltipContent.prototype = {
  27225. constructor: TooltipContent,
  27226. /**
  27227. * @private
  27228. * @type {boolean}
  27229. */
  27230. _enterable: true,
  27231. /**
  27232. * Update when tooltip is rendered
  27233. */
  27234. update: function () {
  27235. // FIXME
  27236. // Move this logic to ec main?
  27237. var container = this._container;
  27238. var stl = container.currentStyle || document.defaultView.getComputedStyle(container);
  27239. var domStyle = container.style;
  27240. if (domStyle.position !== 'absolute' && stl.position !== 'absolute') {
  27241. domStyle.position = 'relative';
  27242. } // Hide the tooltip
  27243. // PENDING
  27244. // this.hide();
  27245. },
  27246. show: function (tooltipModel) {
  27247. clearTimeout(this._hideTimeout);
  27248. var el = this.el;
  27249. el.style.cssText = gCssText + assembleCssText(tooltipModel) // http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore
  27250. + ';left:' + this._x + 'px;top:' + this._y + 'px;' + (tooltipModel.get('extraCssText') || '');
  27251. el.style.display = el.innerHTML ? 'block' : 'none';
  27252. this._show = true;
  27253. },
  27254. setContent: function (content) {
  27255. this.el.innerHTML = content == null ? '' : content;
  27256. },
  27257. setEnterable: function (enterable) {
  27258. this._enterable = enterable;
  27259. },
  27260. getSize: function () {
  27261. var el = this.el;
  27262. return [el.clientWidth, el.clientHeight];
  27263. },
  27264. moveTo: function (x, y) {
  27265. // xy should be based on canvas root. But tooltipContent is
  27266. // the sibling of canvas root. So padding of ec container
  27267. // should be considered here.
  27268. var zr = this._zr;
  27269. var viewportRootOffset;
  27270. if (zr && zr.painter && (viewportRootOffset = zr.painter.getViewportRootOffset())) {
  27271. x += viewportRootOffset.offsetLeft;
  27272. y += viewportRootOffset.offsetTop;
  27273. }
  27274. var style = this.el.style;
  27275. style.left = x + 'px';
  27276. style.top = y + 'px';
  27277. this._x = x;
  27278. this._y = y;
  27279. },
  27280. hide: function () {
  27281. this.el.style.display = 'none';
  27282. this._show = false;
  27283. },
  27284. hideLater: function (time) {
  27285. if (this._show && !(this._inContent && this._enterable)) {
  27286. if (time) {
  27287. this._hideDelay = time; // Set show false to avoid invoke hideLater mutiple times
  27288. this._show = false;
  27289. this._hideTimeout = setTimeout(bind(this.hide, this), time);
  27290. } else {
  27291. this.hide();
  27292. }
  27293. }
  27294. },
  27295. isShow: function () {
  27296. return this._show;
  27297. }
  27298. };
  27299. var bind$2 = bind;
  27300. var each$13 = each$1;
  27301. var parsePercent$2 = parsePercent$1;
  27302. var proxyRect = new Rect({
  27303. shape: {
  27304. x: -1,
  27305. y: -1,
  27306. width: 2,
  27307. height: 2
  27308. }
  27309. });
  27310. extendComponentView({
  27311. type: 'tooltip',
  27312. init: function (ecModel, api) {
  27313. if (env$1.node) {
  27314. return;
  27315. }
  27316. var tooltipContent = new TooltipContent(api.getDom(), api);
  27317. this._tooltipContent = tooltipContent;
  27318. },
  27319. render: function (tooltipModel, ecModel, api) {
  27320. if (env$1.node) {
  27321. return;
  27322. } // Reset
  27323. this.group.removeAll();
  27324. /**
  27325. * @private
  27326. * @type {module:echarts/component/tooltip/TooltipModel}
  27327. */
  27328. this._tooltipModel = tooltipModel;
  27329. /**
  27330. * @private
  27331. * @type {module:echarts/model/Global}
  27332. */
  27333. this._ecModel = ecModel;
  27334. /**
  27335. * @private
  27336. * @type {module:echarts/ExtensionAPI}
  27337. */
  27338. this._api = api;
  27339. /**
  27340. * Should be cleaned when render.
  27341. * @private
  27342. * @type {Array.<Array.<Object>>}
  27343. */
  27344. this._lastDataByCoordSys = null;
  27345. /**
  27346. * @private
  27347. * @type {boolean}
  27348. */
  27349. this._alwaysShowContent = tooltipModel.get('alwaysShowContent');
  27350. var tooltipContent = this._tooltipContent;
  27351. tooltipContent.update();
  27352. tooltipContent.setEnterable(tooltipModel.get('enterable'));
  27353. this._initGlobalListener();
  27354. this._keepShow();
  27355. },
  27356. _initGlobalListener: function () {
  27357. var tooltipModel = this._tooltipModel;
  27358. var triggerOn = tooltipModel.get('triggerOn');
  27359. register('itemTooltip', this._api, bind$2(function (currTrigger, e, dispatchAction) {
  27360. // If 'none', it is not controlled by mouse totally.
  27361. if (triggerOn !== 'none') {
  27362. if (triggerOn.indexOf(currTrigger) >= 0) {
  27363. this._tryShow(e, dispatchAction);
  27364. } else if (currTrigger === 'leave') {
  27365. this._hide(dispatchAction);
  27366. }
  27367. }
  27368. }, this));
  27369. },
  27370. _keepShow: function () {
  27371. var tooltipModel = this._tooltipModel;
  27372. var ecModel = this._ecModel;
  27373. var api = this._api; // Try to keep the tooltip show when refreshing
  27374. if (this._lastX != null && this._lastY != null // When user is willing to control tooltip totally using API,
  27375. // self.manuallyShowTip({x, y}) might cause tooltip hide,
  27376. // which is not expected.
  27377. && tooltipModel.get('triggerOn') !== 'none') {
  27378. var self = this;
  27379. clearTimeout(this._refreshUpdateTimeout);
  27380. this._refreshUpdateTimeout = setTimeout(function () {
  27381. // Show tip next tick after other charts are rendered
  27382. // In case highlight action has wrong result
  27383. // FIXME
  27384. self.manuallyShowTip(tooltipModel, ecModel, api, {
  27385. x: self._lastX,
  27386. y: self._lastY
  27387. });
  27388. });
  27389. }
  27390. },
  27391. /**
  27392. * Show tip manually by
  27393. * dispatchAction({
  27394. * type: 'showTip',
  27395. * x: 10,
  27396. * y: 10
  27397. * });
  27398. * Or
  27399. * dispatchAction({
  27400. * type: 'showTip',
  27401. * seriesIndex: 0,
  27402. * dataIndex or dataIndexInside or name
  27403. * });
  27404. *
  27405. * TODO Batch
  27406. */
  27407. manuallyShowTip: function (tooltipModel, ecModel, api, payload) {
  27408. if (payload.from === this.uid || env$1.node) {
  27409. return;
  27410. }
  27411. var dispatchAction = makeDispatchAction$1(payload, api); // Reset ticket
  27412. this._ticket = ''; // When triggered from axisPointer.
  27413. var dataByCoordSys = payload.dataByCoordSys;
  27414. if (payload.tooltip && payload.x != null && payload.y != null) {
  27415. var el = proxyRect;
  27416. el.position = [payload.x, payload.y];
  27417. el.update();
  27418. el.tooltip = payload.tooltip; // Manually show tooltip while view is not using zrender elements.
  27419. this._tryShow({
  27420. offsetX: payload.x,
  27421. offsetY: payload.y,
  27422. target: el
  27423. }, dispatchAction);
  27424. } else if (dataByCoordSys) {
  27425. this._tryShow({
  27426. offsetX: payload.x,
  27427. offsetY: payload.y,
  27428. position: payload.position,
  27429. event: {},
  27430. dataByCoordSys: payload.dataByCoordSys,
  27431. tooltipOption: payload.tooltipOption
  27432. }, dispatchAction);
  27433. } else if (payload.seriesIndex != null) {
  27434. if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) {
  27435. return;
  27436. }
  27437. var pointInfo = findPointFromSeries(payload, ecModel);
  27438. var cx = pointInfo.point[0];
  27439. var cy = pointInfo.point[1];
  27440. if (cx != null && cy != null) {
  27441. this._tryShow({
  27442. offsetX: cx,
  27443. offsetY: cy,
  27444. position: payload.position,
  27445. target: pointInfo.el,
  27446. event: {}
  27447. }, dispatchAction);
  27448. }
  27449. } else if (payload.x != null && payload.y != null) {
  27450. // FIXME
  27451. // should wrap dispatchAction like `axisPointer/globalListener` ?
  27452. api.dispatchAction({
  27453. type: 'updateAxisPointer',
  27454. x: payload.x,
  27455. y: payload.y
  27456. });
  27457. this._tryShow({
  27458. offsetX: payload.x,
  27459. offsetY: payload.y,
  27460. position: payload.position,
  27461. target: api.getZr().findHover(payload.x, payload.y).target,
  27462. event: {}
  27463. }, dispatchAction);
  27464. }
  27465. },
  27466. manuallyHideTip: function (tooltipModel, ecModel, api, payload) {
  27467. var tooltipContent = this._tooltipContent;
  27468. if (!this._alwaysShowContent) {
  27469. tooltipContent.hideLater(this._tooltipModel.get('hideDelay'));
  27470. }
  27471. this._lastX = this._lastY = null;
  27472. if (payload.from !== this.uid) {
  27473. this._hide(makeDispatchAction$1(payload, api));
  27474. }
  27475. },
  27476. // Be compatible with previous design, that is, when tooltip.type is 'axis' and
  27477. // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer
  27478. // and tooltip.
  27479. _manuallyAxisShowTip: function (tooltipModel, ecModel, api, payload) {
  27480. var seriesIndex = payload.seriesIndex;
  27481. var dataIndex = payload.dataIndex;
  27482. var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;
  27483. if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {
  27484. return;
  27485. }
  27486. var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
  27487. if (!seriesModel) {
  27488. return;
  27489. }
  27490. var data = seriesModel.getData();
  27491. var tooltipModel = buildTooltipModel([data.getItemModel(dataIndex), seriesModel, (seriesModel.coordinateSystem || {}).model, tooltipModel]);
  27492. if (tooltipModel.get('trigger') !== 'axis') {
  27493. return;
  27494. }
  27495. api.dispatchAction({
  27496. type: 'updateAxisPointer',
  27497. seriesIndex: seriesIndex,
  27498. dataIndex: dataIndex,
  27499. position: payload.position
  27500. });
  27501. return true;
  27502. },
  27503. _tryShow: function (e, dispatchAction) {
  27504. var el = e.target;
  27505. var tooltipModel = this._tooltipModel;
  27506. if (!tooltipModel) {
  27507. return;
  27508. } // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed
  27509. this._lastX = e.offsetX;
  27510. this._lastY = e.offsetY;
  27511. var dataByCoordSys = e.dataByCoordSys;
  27512. if (dataByCoordSys && dataByCoordSys.length) {
  27513. this._showAxisTooltip(dataByCoordSys, e);
  27514. } // Always show item tooltip if mouse is on the element with dataIndex
  27515. else if (el && el.dataIndex != null) {
  27516. this._lastDataByCoordSys = null;
  27517. this._showSeriesItemTooltip(e, el, dispatchAction);
  27518. } // Tooltip provided directly. Like legend.
  27519. else if (el && el.tooltip) {
  27520. this._lastDataByCoordSys = null;
  27521. this._showComponentItemTooltip(e, el, dispatchAction);
  27522. } else {
  27523. this._lastDataByCoordSys = null;
  27524. this._hide(dispatchAction);
  27525. }
  27526. },
  27527. _showOrMove: function (tooltipModel, cb) {
  27528. // showDelay is used in this case: tooltip.enterable is set
  27529. // as true. User intent to move mouse into tooltip and click
  27530. // something. `showDelay` makes it easyer to enter the content
  27531. // but tooltip do not move immediately.
  27532. var delay = tooltipModel.get('showDelay');
  27533. cb = bind(cb, this);
  27534. clearTimeout(this._showTimout);
  27535. delay > 0 ? this._showTimout = setTimeout(cb, delay) : cb();
  27536. },
  27537. _showAxisTooltip: function (dataByCoordSys, e) {
  27538. var ecModel = this._ecModel;
  27539. var globalTooltipModel = this._tooltipModel;
  27540. var point = [e.offsetX, e.offsetY];
  27541. var singleDefaultHTML = [];
  27542. var singleParamsList = [];
  27543. var singleTooltipModel = buildTooltipModel([e.tooltipOption, globalTooltipModel]);
  27544. each$13(dataByCoordSys, function (itemCoordSys) {
  27545. // var coordParamList = [];
  27546. // var coordDefaultHTML = [];
  27547. // var coordTooltipModel = buildTooltipModel([
  27548. // e.tooltipOption,
  27549. // itemCoordSys.tooltipOption,
  27550. // ecModel.getComponent(itemCoordSys.coordSysMainType, itemCoordSys.coordSysIndex),
  27551. // globalTooltipModel
  27552. // ]);
  27553. // var displayMode = coordTooltipModel.get('displayMode');
  27554. // var paramsList = displayMode === 'single' ? singleParamsList : [];
  27555. each$13(itemCoordSys.dataByAxis, function (item) {
  27556. var axisModel = ecModel.getComponent(item.axisDim + 'Axis', item.axisIndex);
  27557. var axisValue = item.value;
  27558. var seriesDefaultHTML = [];
  27559. if (!axisModel || axisValue == null) {
  27560. return;
  27561. }
  27562. var valueLabel = getValueLabel(axisValue, axisModel.axis, ecModel, item.seriesDataIndices, item.valueLabelOpt);
  27563. each$1(item.seriesDataIndices, function (idxItem) {
  27564. var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
  27565. var dataIndex = idxItem.dataIndexInside;
  27566. var dataParams = series && series.getDataParams(dataIndex);
  27567. dataParams.axisDim = item.axisDim;
  27568. dataParams.axisIndex = item.axisIndex;
  27569. dataParams.axisType = item.axisType;
  27570. dataParams.axisId = item.axisId;
  27571. dataParams.axisValue = getAxisRawValue(axisModel.axis, axisValue);
  27572. dataParams.axisValueLabel = valueLabel;
  27573. if (dataParams) {
  27574. singleParamsList.push(dataParams);
  27575. seriesDefaultHTML.push(series.formatTooltip(dataIndex, true));
  27576. }
  27577. }); // Default tooltip content
  27578. // FIXME
  27579. // (1) shold be the first data which has name?
  27580. // (2) themeRiver, firstDataIndex is array, and first line is unnecessary.
  27581. var firstLine = valueLabel;
  27582. singleDefaultHTML.push((firstLine ? encodeHTML(firstLine) + '<br />' : '') + seriesDefaultHTML.join('<br />'));
  27583. });
  27584. }, this); // In most case, the second axis is shown upper than the first one.
  27585. singleDefaultHTML.reverse();
  27586. singleDefaultHTML = singleDefaultHTML.join('<br /><br />');
  27587. var positionExpr = e.position;
  27588. this._showOrMove(singleTooltipModel, function () {
  27589. if (this._updateContentNotChangedOnAxis(dataByCoordSys)) {
  27590. this._updatePosition(singleTooltipModel, positionExpr, point[0], point[1], this._tooltipContent, singleParamsList);
  27591. } else {
  27592. this._showTooltipContent(singleTooltipModel, singleDefaultHTML, singleParamsList, Math.random(), point[0], point[1], positionExpr);
  27593. }
  27594. }); // Do not trigger events here, because this branch only be entered
  27595. // from dispatchAction.
  27596. },
  27597. _showSeriesItemTooltip: function (e, el, dispatchAction) {
  27598. var ecModel = this._ecModel; // Use dataModel in element if possible
  27599. // Used when mouseover on a element like markPoint or edge
  27600. // In which case, the data is not main data in series.
  27601. var seriesIndex = el.seriesIndex;
  27602. var seriesModel = ecModel.getSeriesByIndex(seriesIndex); // For example, graph link.
  27603. var dataModel = el.dataModel || seriesModel;
  27604. var dataIndex = el.dataIndex;
  27605. var dataType = el.dataType;
  27606. var data = dataModel.getData();
  27607. var tooltipModel = buildTooltipModel([data.getItemModel(dataIndex), dataModel, seriesModel && (seriesModel.coordinateSystem || {}).model, this._tooltipModel]);
  27608. var tooltipTrigger = tooltipModel.get('trigger');
  27609. if (tooltipTrigger != null && tooltipTrigger !== 'item') {
  27610. return;
  27611. }
  27612. var params = dataModel.getDataParams(dataIndex, dataType);
  27613. var defaultHtml = dataModel.formatTooltip(dataIndex, false, dataType);
  27614. var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;
  27615. this._showOrMove(tooltipModel, function () {
  27616. this._showTooltipContent(tooltipModel, defaultHtml, params, asyncTicket, e.offsetX, e.offsetY, e.position, e.target);
  27617. }); // FIXME
  27618. // duplicated showtip if manuallyShowTip is called from dispatchAction.
  27619. dispatchAction({
  27620. type: 'showTip',
  27621. dataIndexInside: dataIndex,
  27622. dataIndex: data.getRawIndex(dataIndex),
  27623. seriesIndex: seriesIndex,
  27624. from: this.uid
  27625. });
  27626. },
  27627. _showComponentItemTooltip: function (e, el, dispatchAction) {
  27628. var tooltipOpt = el.tooltip;
  27629. if (typeof tooltipOpt === 'string') {
  27630. var content = tooltipOpt;
  27631. tooltipOpt = {
  27632. content: content,
  27633. // Fixed formatter
  27634. formatter: content
  27635. };
  27636. }
  27637. var subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel);
  27638. var defaultHtml = subTooltipModel.get('content');
  27639. var asyncTicket = Math.random(); // Do not check whether `trigger` is 'none' here, because `trigger`
  27640. // only works on cooridinate system. In fact, we have not found case
  27641. // that requires setting `trigger` nothing on component yet.
  27642. this._showOrMove(subTooltipModel, function () {
  27643. this._showTooltipContent(subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {}, asyncTicket, e.offsetX, e.offsetY, e.position, el);
  27644. }); // If not dispatch showTip, tip may be hide triggered by axis.
  27645. dispatchAction({
  27646. type: 'showTip',
  27647. from: this.uid
  27648. });
  27649. },
  27650. _showTooltipContent: function (tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el) {
  27651. // Reset ticket
  27652. this._ticket = '';
  27653. if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) {
  27654. return;
  27655. }
  27656. var tooltipContent = this._tooltipContent;
  27657. var formatter = tooltipModel.get('formatter');
  27658. positionExpr = positionExpr || tooltipModel.get('position');
  27659. var html = defaultHtml;
  27660. if (formatter && typeof formatter === 'string') {
  27661. html = formatTpl(formatter, params, true);
  27662. } else if (typeof formatter === 'function') {
  27663. var callback = bind$2(function (cbTicket, html) {
  27664. if (cbTicket === this._ticket) {
  27665. tooltipContent.setContent(html);
  27666. this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);
  27667. }
  27668. }, this);
  27669. this._ticket = asyncTicket;
  27670. html = formatter(params, asyncTicket, callback);
  27671. }
  27672. tooltipContent.setContent(html);
  27673. tooltipContent.show(tooltipModel);
  27674. this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el);
  27675. },
  27676. /**
  27677. * @param {string|Function|Array.<number>|Object} positionExpr
  27678. * @param {number} x Mouse x
  27679. * @param {number} y Mouse y
  27680. * @param {boolean} confine Whether confine tooltip content in view rect.
  27681. * @param {Object|<Array.<Object>} params
  27682. * @param {module:zrender/Element} el target element
  27683. * @param {module:echarts/ExtensionAPI} api
  27684. * @return {Array.<number>}
  27685. */
  27686. _updatePosition: function (tooltipModel, positionExpr, x, y, content, params, el) {
  27687. var viewWidth = this._api.getWidth();
  27688. var viewHeight = this._api.getHeight();
  27689. positionExpr = positionExpr || tooltipModel.get('position');
  27690. var contentSize = content.getSize();
  27691. var align = tooltipModel.get('align');
  27692. var vAlign = tooltipModel.get('verticalAlign');
  27693. var rect = el && el.getBoundingRect().clone();
  27694. el && rect.applyTransform(el.transform);
  27695. if (typeof positionExpr === 'function') {
  27696. // Callback of position can be an array or a string specify the position
  27697. positionExpr = positionExpr([x, y], params, content.el, rect, {
  27698. viewSize: [viewWidth, viewHeight],
  27699. contentSize: contentSize.slice()
  27700. });
  27701. }
  27702. if (isArray(positionExpr)) {
  27703. x = parsePercent$2(positionExpr[0], viewWidth);
  27704. y = parsePercent$2(positionExpr[1], viewHeight);
  27705. } else if (isObject(positionExpr)) {
  27706. positionExpr.width = contentSize[0];
  27707. positionExpr.height = contentSize[1];
  27708. var layoutRect = getLayoutRect(positionExpr, {
  27709. width: viewWidth,
  27710. height: viewHeight
  27711. });
  27712. x = layoutRect.x;
  27713. y = layoutRect.y;
  27714. align = null; // When positionExpr is left/top/right/bottom,
  27715. // align and verticalAlign will not work.
  27716. vAlign = null;
  27717. } // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element
  27718. else if (typeof positionExpr === 'string' && el) {
  27719. var pos = calcTooltipPosition(positionExpr, rect, contentSize);
  27720. x = pos[0];
  27721. y = pos[1];
  27722. } else {
  27723. var pos = refixTooltipPosition(x, y, content.el, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20);
  27724. x = pos[0];
  27725. y = pos[1];
  27726. }
  27727. align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0);
  27728. vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);
  27729. if (tooltipModel.get('confine')) {
  27730. var pos = confineTooltipPosition(x, y, content.el, viewWidth, viewHeight);
  27731. x = pos[0];
  27732. y = pos[1];
  27733. }
  27734. content.moveTo(x, y);
  27735. },
  27736. // FIXME
  27737. // Should we remove this but leave this to user?
  27738. _updateContentNotChangedOnAxis: function (dataByCoordSys) {
  27739. var lastCoordSys = this._lastDataByCoordSys;
  27740. var contentNotChanged = !!lastCoordSys && lastCoordSys.length === dataByCoordSys.length;
  27741. contentNotChanged && each$13(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {
  27742. var lastDataByAxis = lastItemCoordSys.dataByAxis || {};
  27743. var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};
  27744. var thisDataByAxis = thisItemCoordSys.dataByAxis || [];
  27745. contentNotChanged &= lastDataByAxis.length === thisDataByAxis.length;
  27746. contentNotChanged && each$13(lastDataByAxis, function (lastItem, indexAxis) {
  27747. var thisItem = thisDataByAxis[indexAxis] || {};
  27748. var lastIndices = lastItem.seriesDataIndices || [];
  27749. var newIndices = thisItem.seriesDataIndices || [];
  27750. contentNotChanged &= lastItem.value === thisItem.value && lastItem.axisType === thisItem.axisType && lastItem.axisId === thisItem.axisId && lastIndices.length === newIndices.length;
  27751. contentNotChanged && each$13(lastIndices, function (lastIdxItem, j) {
  27752. var newIdxItem = newIndices[j];
  27753. contentNotChanged &= lastIdxItem.seriesIndex === newIdxItem.seriesIndex && lastIdxItem.dataIndex === newIdxItem.dataIndex;
  27754. });
  27755. });
  27756. });
  27757. this._lastDataByCoordSys = dataByCoordSys;
  27758. return !!contentNotChanged;
  27759. },
  27760. _hide: function (dispatchAction) {
  27761. // Do not directly hideLater here, because this behavior may be prevented
  27762. // in dispatchAction when showTip is dispatched.
  27763. // FIXME
  27764. // duplicated hideTip if manuallyHideTip is called from dispatchAction.
  27765. this._lastDataByCoordSys = null;
  27766. dispatchAction({
  27767. type: 'hideTip',
  27768. from: this.uid
  27769. });
  27770. },
  27771. dispose: function (ecModel, api) {
  27772. if (env$1.node) {
  27773. return;
  27774. }
  27775. this._tooltipContent.hide();
  27776. unregister('itemTooltip', api);
  27777. }
  27778. });
  27779. /**
  27780. * @param {Array.<Object|module:echarts/model/Model>} modelCascade
  27781. * From top to bottom. (the last one should be globalTooltipModel);
  27782. */
  27783. function buildTooltipModel(modelCascade) {
  27784. var resultModel = modelCascade.pop();
  27785. while (modelCascade.length) {
  27786. var tooltipOpt = modelCascade.pop();
  27787. if (tooltipOpt) {
  27788. if (tooltipOpt instanceof Model) {
  27789. tooltipOpt = tooltipOpt.get('tooltip', true);
  27790. } // In each data item tooltip can be simply write:
  27791. // {
  27792. // value: 10,
  27793. // tooltip: 'Something you need to know'
  27794. // }
  27795. if (typeof tooltipOpt === 'string') {
  27796. tooltipOpt = {
  27797. formatter: tooltipOpt
  27798. };
  27799. }
  27800. resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel);
  27801. }
  27802. }
  27803. return resultModel;
  27804. }
  27805. function makeDispatchAction$1(payload, api) {
  27806. return payload.dispatchAction || bind(api.dispatchAction, api);
  27807. }
  27808. function refixTooltipPosition(x, y, el, viewWidth, viewHeight, gapH, gapV) {
  27809. var size = getOuterSize(el);
  27810. var width = size.width;
  27811. var height = size.height;
  27812. if (gapH != null) {
  27813. if (x + width + gapH > viewWidth) {
  27814. x -= width + gapH;
  27815. } else {
  27816. x += gapH;
  27817. }
  27818. }
  27819. if (gapV != null) {
  27820. if (y + height + gapV > viewHeight) {
  27821. y -= height + gapV;
  27822. } else {
  27823. y += gapV;
  27824. }
  27825. }
  27826. return [x, y];
  27827. }
  27828. function confineTooltipPosition(x, y, el, viewWidth, viewHeight) {
  27829. var size = getOuterSize(el);
  27830. var width = size.width;
  27831. var height = size.height;
  27832. x = Math.min(x + width, viewWidth) - width;
  27833. y = Math.min(y + height, viewHeight) - height;
  27834. x = Math.max(x, 0);
  27835. y = Math.max(y, 0);
  27836. return [x, y];
  27837. }
  27838. function getOuterSize(el) {
  27839. var width = el.clientWidth;
  27840. var height = el.clientHeight; // Consider browser compatibility.
  27841. // IE8 does not support getComputedStyle.
  27842. if (document.defaultView && document.defaultView.getComputedStyle) {
  27843. var stl = document.defaultView.getComputedStyle(el);
  27844. if (stl) {
  27845. width += parseInt(stl.paddingLeft, 10) + parseInt(stl.paddingRight, 10) + parseInt(stl.borderLeftWidth, 10) + parseInt(stl.borderRightWidth, 10);
  27846. height += parseInt(stl.paddingTop, 10) + parseInt(stl.paddingBottom, 10) + parseInt(stl.borderTopWidth, 10) + parseInt(stl.borderBottomWidth, 10);
  27847. }
  27848. }
  27849. return {
  27850. width: width,
  27851. height: height
  27852. };
  27853. }
  27854. function calcTooltipPosition(position, rect, contentSize) {
  27855. var domWidth = contentSize[0];
  27856. var domHeight = contentSize[1];
  27857. var gap = 5;
  27858. var x = 0;
  27859. var y = 0;
  27860. var rectWidth = rect.width;
  27861. var rectHeight = rect.height;
  27862. switch (position) {
  27863. case 'inside':
  27864. x = rect.x + rectWidth / 2 - domWidth / 2;
  27865. y = rect.y + rectHeight / 2 - domHeight / 2;
  27866. break;
  27867. case 'top':
  27868. x = rect.x + rectWidth / 2 - domWidth / 2;
  27869. y = rect.y - domHeight - gap;
  27870. break;
  27871. case 'bottom':
  27872. x = rect.x + rectWidth / 2 - domWidth / 2;
  27873. y = rect.y + rectHeight + gap;
  27874. break;
  27875. case 'left':
  27876. x = rect.x - domWidth - gap;
  27877. y = rect.y + rectHeight / 2 - domHeight / 2;
  27878. break;
  27879. case 'right':
  27880. x = rect.x + rectWidth + gap;
  27881. y = rect.y + rectHeight / 2 - domHeight / 2;
  27882. }
  27883. return [x, y];
  27884. }
  27885. function isCenterAlign(align) {
  27886. return align === 'center' || align === 'middle';
  27887. } // FIXME Better way to pack data in graphic element
  27888. /**
  27889. * @action
  27890. * @property {string} type
  27891. * @property {number} seriesIndex
  27892. * @property {number} dataIndex
  27893. * @property {number} [x]
  27894. * @property {number} [y]
  27895. */
  27896. registerAction({
  27897. type: 'showTip',
  27898. event: 'showTip',
  27899. update: 'tooltip:manuallyShowTip'
  27900. }, // noop
  27901. function () {});
  27902. registerAction({
  27903. type: 'hideTip',
  27904. event: 'hideTip',
  27905. update: 'tooltip:manuallyHideTip'
  27906. }, // noop
  27907. function () {});
  27908. var urn = 'urn:schemas-microsoft-com:vml';
  27909. var win = typeof window === 'undefined' ? null : window;
  27910. var vmlInited = false;
  27911. var doc = win && win.document;
  27912. function createNode(tagName) {
  27913. return doCreateNode(tagName);
  27914. } // Avoid assign to an exported variable, for transforming to cjs.
  27915. var doCreateNode;
  27916. if (doc && !env$1.canvasSupported) {
  27917. try {
  27918. !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn);
  27919. doCreateNode = function (tagName) {
  27920. return doc.createElement('<zrvml:' + tagName + ' class="zrvml">');
  27921. };
  27922. } catch (e) {
  27923. doCreateNode = function (tagName) {
  27924. return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">');
  27925. };
  27926. }
  27927. } // From raphael
  27928. function initVML() {
  27929. if (vmlInited || !doc) {
  27930. return;
  27931. }
  27932. vmlInited = true;
  27933. var styleSheets = doc.styleSheets;
  27934. if (styleSheets.length < 31) {
  27935. doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)');
  27936. } else {
  27937. // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx
  27938. styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)');
  27939. }
  27940. } // http://www.w3.org/TR/NOTE-VML
  27941. // TODO Use proxy like svg instead of overwrite brush methods
  27942. var CMD$3 = PathProxy.CMD;
  27943. var round$2 = Math.round;
  27944. var sqrt = Math.sqrt;
  27945. var abs$1 = Math.abs;
  27946. var cos = Math.cos;
  27947. var sin = Math.sin;
  27948. var mathMax$4 = Math.max;
  27949. if (!env$1.canvasSupported) {
  27950. var comma = ',';
  27951. var imageTransformPrefix = 'progid:DXImageTransform.Microsoft';
  27952. var Z = 21600;
  27953. var Z2 = Z / 2;
  27954. var ZLEVEL_BASE = 100000;
  27955. var Z_BASE = 1000;
  27956. var initRootElStyle = function (el) {
  27957. el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;';
  27958. el.coordsize = Z + ',' + Z;
  27959. el.coordorigin = '0,0';
  27960. };
  27961. var encodeHtmlAttribute = function (s) {
  27962. return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
  27963. };
  27964. var rgb2Str = function (r, g, b) {
  27965. return 'rgb(' + [r, g, b].join(',') + ')';
  27966. };
  27967. var append = function (parent, child) {
  27968. if (child && parent && child.parentNode !== parent) {
  27969. parent.appendChild(child);
  27970. }
  27971. };
  27972. var remove = function (parent, child) {
  27973. if (child && parent && child.parentNode === parent) {
  27974. parent.removeChild(child);
  27975. }
  27976. };
  27977. var getZIndex = function (zlevel, z, z2) {
  27978. // z 的取值范围为 [0, 1000]
  27979. return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE + z2;
  27980. };
  27981. var parsePercent$3 = function (value, maxValue) {
  27982. if (typeof value === 'string') {
  27983. if (value.lastIndexOf('%') >= 0) {
  27984. return parseFloat(value) / 100 * maxValue;
  27985. }
  27986. return parseFloat(value);
  27987. }
  27988. return value;
  27989. };
  27990. /***************************************************
  27991. * PATH
  27992. **************************************************/
  27993. var setColorAndOpacity = function (el, color, opacity) {
  27994. var colorArr = parse(color);
  27995. opacity = +opacity;
  27996. if (isNaN(opacity)) {
  27997. opacity = 1;
  27998. }
  27999. if (colorArr) {
  28000. el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]);
  28001. el.opacity = opacity * colorArr[3];
  28002. }
  28003. };
  28004. var getColorAndAlpha = function (color) {
  28005. var colorArr = parse(color);
  28006. return [rgb2Str(colorArr[0], colorArr[1], colorArr[2]), colorArr[3]];
  28007. };
  28008. var updateFillNode = function (el, style, zrEl) {
  28009. // TODO pattern
  28010. var fill = style.fill;
  28011. if (fill != null) {
  28012. // Modified from excanvas
  28013. if (fill instanceof Gradient) {
  28014. var gradientType;
  28015. var angle = 0;
  28016. var focus = [0, 0]; // additional offset
  28017. var shift = 0; // scale factor for offset
  28018. var expansion = 1;
  28019. var rect = zrEl.getBoundingRect();
  28020. var rectWidth = rect.width;
  28021. var rectHeight = rect.height;
  28022. if (fill.type === 'linear') {
  28023. gradientType = 'gradient';
  28024. var transform = zrEl.transform;
  28025. var p0 = [fill.x * rectWidth, fill.y * rectHeight];
  28026. var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight];
  28027. if (transform) {
  28028. applyTransform(p0, p0, transform);
  28029. applyTransform(p1, p1, transform);
  28030. }
  28031. var dx = p1[0] - p0[0];
  28032. var dy = p1[1] - p0[1];
  28033. angle = Math.atan2(dx, dy) * 180 / Math.PI; // The angle should be a non-negative number.
  28034. if (angle < 0) {
  28035. angle += 360;
  28036. } // Very small angles produce an unexpected result because they are
  28037. // converted to a scientific notation string.
  28038. if (angle < 1e-6) {
  28039. angle = 0;
  28040. }
  28041. } else {
  28042. gradientType = 'gradientradial';
  28043. var p0 = [fill.x * rectWidth, fill.y * rectHeight];
  28044. var transform = zrEl.transform;
  28045. var scale$$1 = zrEl.scale;
  28046. var width = rectWidth;
  28047. var height = rectHeight;
  28048. focus = [// Percent in bounding rect
  28049. (p0[0] - rect.x) / width, (p0[1] - rect.y) / height];
  28050. if (transform) {
  28051. applyTransform(p0, p0, transform);
  28052. }
  28053. width /= scale$$1[0] * Z;
  28054. height /= scale$$1[1] * Z;
  28055. var dimension = mathMax$4(width, height);
  28056. shift = 2 * 0 / dimension;
  28057. expansion = 2 * fill.r / dimension - shift;
  28058. } // We need to sort the color stops in ascending order by offset,
  28059. // otherwise IE won't interpret it correctly.
  28060. var stops = fill.colorStops.slice();
  28061. stops.sort(function (cs1, cs2) {
  28062. return cs1.offset - cs2.offset;
  28063. });
  28064. var length$$1 = stops.length; // Color and alpha list of first and last stop
  28065. var colorAndAlphaList = [];
  28066. var colors = [];
  28067. for (var i = 0; i < length$$1; i++) {
  28068. var stop = stops[i];
  28069. var colorAndAlpha = getColorAndAlpha(stop.color);
  28070. colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]);
  28071. if (i === 0 || i === length$$1 - 1) {
  28072. colorAndAlphaList.push(colorAndAlpha);
  28073. }
  28074. }
  28075. if (length$$1 >= 2) {
  28076. var color1 = colorAndAlphaList[0][0];
  28077. var color2 = colorAndAlphaList[1][0];
  28078. var opacity1 = colorAndAlphaList[0][1] * style.opacity;
  28079. var opacity2 = colorAndAlphaList[1][1] * style.opacity;
  28080. el.type = gradientType;
  28081. el.method = 'none';
  28082. el.focus = '100%';
  28083. el.angle = angle;
  28084. el.color = color1;
  28085. el.color2 = color2;
  28086. el.colors = colors.join(','); // When colors attribute is used, the meanings of opacity and o:opacity2
  28087. // are reversed.
  28088. el.opacity = opacity2; // FIXME g_o_:opacity ?
  28089. el.opacity2 = opacity1;
  28090. }
  28091. if (gradientType === 'radial') {
  28092. el.focusposition = focus.join(',');
  28093. }
  28094. } else {
  28095. // FIXME Change from Gradient fill to color fill
  28096. setColorAndOpacity(el, fill, style.opacity);
  28097. }
  28098. }
  28099. };
  28100. var updateStrokeNode = function (el, style) {
  28101. // if (style.lineJoin != null) {
  28102. // el.joinstyle = style.lineJoin;
  28103. // }
  28104. // if (style.miterLimit != null) {
  28105. // el.miterlimit = style.miterLimit * Z;
  28106. // }
  28107. // if (style.lineCap != null) {
  28108. // el.endcap = style.lineCap;
  28109. // }
  28110. if (style.lineDash != null) {
  28111. el.dashstyle = style.lineDash.join(' ');
  28112. }
  28113. if (style.stroke != null && !(style.stroke instanceof Gradient)) {
  28114. setColorAndOpacity(el, style.stroke, style.opacity);
  28115. }
  28116. };
  28117. var updateFillAndStroke = function (vmlEl, type, style, zrEl) {
  28118. var isFill = type == 'fill';
  28119. var el = vmlEl.getElementsByTagName(type)[0]; // Stroke must have lineWidth
  28120. if (style[type] != null && style[type] !== 'none' && (isFill || !isFill && style.lineWidth)) {
  28121. vmlEl[isFill ? 'filled' : 'stroked'] = 'true'; // FIXME Remove before updating, or set `colors` will throw error
  28122. if (style[type] instanceof Gradient) {
  28123. remove(vmlEl, el);
  28124. }
  28125. if (!el) {
  28126. el = createNode(type);
  28127. }
  28128. isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style);
  28129. append(vmlEl, el);
  28130. } else {
  28131. vmlEl[isFill ? 'filled' : 'stroked'] = 'false';
  28132. remove(vmlEl, el);
  28133. }
  28134. };
  28135. var points$1 = [[], [], []];
  28136. var pathDataToString = function (path, m) {
  28137. var M = CMD$3.M;
  28138. var C = CMD$3.C;
  28139. var L = CMD$3.L;
  28140. var A = CMD$3.A;
  28141. var Q = CMD$3.Q;
  28142. var str = [];
  28143. var nPoint;
  28144. var cmdStr;
  28145. var cmd;
  28146. var i;
  28147. var xi;
  28148. var yi;
  28149. var data = path.data;
  28150. var dataLength = path.len();
  28151. for (i = 0; i < dataLength;) {
  28152. cmd = data[i++];
  28153. cmdStr = '';
  28154. nPoint = 0;
  28155. switch (cmd) {
  28156. case M:
  28157. cmdStr = ' m ';
  28158. nPoint = 1;
  28159. xi = data[i++];
  28160. yi = data[i++];
  28161. points$1[0][0] = xi;
  28162. points$1[0][1] = yi;
  28163. break;
  28164. case L:
  28165. cmdStr = ' l ';
  28166. nPoint = 1;
  28167. xi = data[i++];
  28168. yi = data[i++];
  28169. points$1[0][0] = xi;
  28170. points$1[0][1] = yi;
  28171. break;
  28172. case Q:
  28173. case C:
  28174. cmdStr = ' c ';
  28175. nPoint = 3;
  28176. var x1 = data[i++];
  28177. var y1 = data[i++];
  28178. var x2 = data[i++];
  28179. var y2 = data[i++];
  28180. var x3;
  28181. var y3;
  28182. if (cmd === Q) {
  28183. // Convert quadratic to cubic using degree elevation
  28184. x3 = x2;
  28185. y3 = y2;
  28186. x2 = (x2 + 2 * x1) / 3;
  28187. y2 = (y2 + 2 * y1) / 3;
  28188. x1 = (xi + 2 * x1) / 3;
  28189. y1 = (yi + 2 * y1) / 3;
  28190. } else {
  28191. x3 = data[i++];
  28192. y3 = data[i++];
  28193. }
  28194. points$1[0][0] = x1;
  28195. points$1[0][1] = y1;
  28196. points$1[1][0] = x2;
  28197. points$1[1][1] = y2;
  28198. points$1[2][0] = x3;
  28199. points$1[2][1] = y3;
  28200. xi = x3;
  28201. yi = y3;
  28202. break;
  28203. case A:
  28204. var x = 0;
  28205. var y = 0;
  28206. var sx = 1;
  28207. var sy = 1;
  28208. var angle = 0;
  28209. if (m) {
  28210. // Extract SRT from matrix
  28211. x = m[4];
  28212. y = m[5];
  28213. sx = sqrt(m[0] * m[0] + m[1] * m[1]);
  28214. sy = sqrt(m[2] * m[2] + m[3] * m[3]);
  28215. angle = Math.atan2(-m[1] / sy, m[0] / sx);
  28216. }
  28217. var cx = data[i++];
  28218. var cy = data[i++];
  28219. var rx = data[i++];
  28220. var ry = data[i++];
  28221. var startAngle = data[i++] + angle;
  28222. var endAngle = data[i++] + startAngle + angle; // FIXME
  28223. // var psi = data[i++];
  28224. i++;
  28225. var clockwise = data[i++];
  28226. var x0 = cx + cos(startAngle) * rx;
  28227. var y0 = cy + sin(startAngle) * ry;
  28228. var x1 = cx + cos(endAngle) * rx;
  28229. var y1 = cy + sin(endAngle) * ry;
  28230. var type = clockwise ? ' wa ' : ' at ';
  28231. if (Math.abs(x0 - x1) < 1e-4) {
  28232. // IE won't render arches drawn counter clockwise if x0 == x1.
  28233. if (Math.abs(endAngle - startAngle) > 1e-2) {
  28234. // Offset x0 by 1/80 of a pixel. Use something
  28235. // that can be represented in binary
  28236. if (clockwise) {
  28237. x0 += 270 / Z;
  28238. }
  28239. } else {
  28240. // Avoid case draw full circle
  28241. if (Math.abs(y0 - cy) < 1e-4) {
  28242. if (clockwise && x0 < cx || !clockwise && x0 > cx) {
  28243. y1 -= 270 / Z;
  28244. } else {
  28245. y1 += 270 / Z;
  28246. }
  28247. } else if (clockwise && y0 < cy || !clockwise && y0 > cy) {
  28248. x1 += 270 / Z;
  28249. } else {
  28250. x1 -= 270 / Z;
  28251. }
  28252. }
  28253. }
  28254. str.push(type, round$2(((cx - rx) * sx + x) * Z - Z2), comma, round$2(((cy - ry) * sy + y) * Z - Z2), comma, round$2(((cx + rx) * sx + x) * Z - Z2), comma, round$2(((cy + ry) * sy + y) * Z - Z2), comma, round$2((x0 * sx + x) * Z - Z2), comma, round$2((y0 * sy + y) * Z - Z2), comma, round$2((x1 * sx + x) * Z - Z2), comma, round$2((y1 * sy + y) * Z - Z2));
  28255. xi = x1;
  28256. yi = y1;
  28257. break;
  28258. case CMD$3.R:
  28259. var p0 = points$1[0];
  28260. var p1 = points$1[1]; // x0, y0
  28261. p0[0] = data[i++];
  28262. p0[1] = data[i++]; // x1, y1
  28263. p1[0] = p0[0] + data[i++];
  28264. p1[1] = p0[1] + data[i++];
  28265. if (m) {
  28266. applyTransform(p0, p0, m);
  28267. applyTransform(p1, p1, m);
  28268. }
  28269. p0[0] = round$2(p0[0] * Z - Z2);
  28270. p1[0] = round$2(p1[0] * Z - Z2);
  28271. p0[1] = round$2(p0[1] * Z - Z2);
  28272. p1[1] = round$2(p1[1] * Z - Z2);
  28273. str.push( // x0, y0
  28274. ' m ', p0[0], comma, p0[1], // x1, y0
  28275. ' l ', p1[0], comma, p0[1], // x1, y1
  28276. ' l ', p1[0], comma, p1[1], // x0, y1
  28277. ' l ', p0[0], comma, p1[1]);
  28278. break;
  28279. case CMD$3.Z:
  28280. // FIXME Update xi, yi
  28281. str.push(' x ');
  28282. }
  28283. if (nPoint > 0) {
  28284. str.push(cmdStr);
  28285. for (var k = 0; k < nPoint; k++) {
  28286. var p = points$1[k];
  28287. m && applyTransform(p, p, m); // 不 round 会非常慢
  28288. str.push(round$2(p[0] * Z - Z2), comma, round$2(p[1] * Z - Z2), k < nPoint - 1 ? comma : '');
  28289. }
  28290. }
  28291. }
  28292. return str.join('');
  28293. }; // Rewrite the original path method
  28294. Path.prototype.brushVML = function (vmlRoot) {
  28295. var style = this.style;
  28296. var vmlEl = this._vmlEl;
  28297. if (!vmlEl) {
  28298. vmlEl = createNode('shape');
  28299. initRootElStyle(vmlEl);
  28300. this._vmlEl = vmlEl;
  28301. }
  28302. updateFillAndStroke(vmlEl, 'fill', style, this);
  28303. updateFillAndStroke(vmlEl, 'stroke', style, this);
  28304. var m = this.transform;
  28305. var needTransform = m != null;
  28306. var strokeEl = vmlEl.getElementsByTagName('stroke')[0];
  28307. if (strokeEl) {
  28308. var lineWidth = style.lineWidth; // Get the line scale.
  28309. // Determinant of this.m_ means how much the area is enlarged by the
  28310. // transformation. So its square root can be used as a scale factor
  28311. // for width.
  28312. if (needTransform && !style.strokeNoScale) {
  28313. var det = m[0] * m[3] - m[1] * m[2];
  28314. lineWidth *= sqrt(abs$1(det));
  28315. }
  28316. strokeEl.weight = lineWidth + 'px';
  28317. }
  28318. var path = this.path || (this.path = new PathProxy());
  28319. if (this.__dirtyPath) {
  28320. path.beginPath();
  28321. this.buildPath(path, this.shape);
  28322. path.toStatic();
  28323. this.__dirtyPath = false;
  28324. }
  28325. vmlEl.path = pathDataToString(path, this.transform);
  28326. vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); // Append to root
  28327. append(vmlRoot, vmlEl); // Text
  28328. if (style.text != null) {
  28329. this.drawRectText(vmlRoot, this.getBoundingRect());
  28330. } else {
  28331. this.removeRectText(vmlRoot);
  28332. }
  28333. };
  28334. Path.prototype.onRemove = function (vmlRoot) {
  28335. remove(vmlRoot, this._vmlEl);
  28336. this.removeRectText(vmlRoot);
  28337. };
  28338. Path.prototype.onAdd = function (vmlRoot) {
  28339. append(vmlRoot, this._vmlEl);
  28340. this.appendRectText(vmlRoot);
  28341. };
  28342. /***************************************************
  28343. * IMAGE
  28344. **************************************************/
  28345. var isImage = function (img) {
  28346. // FIXME img instanceof Image 如果 img 是一个字符串的时候,IE8 下会报错
  28347. return typeof img === 'object' && img.tagName && img.tagName.toUpperCase() === 'IMG'; // return img instanceof Image;
  28348. }; // Rewrite the original path method
  28349. ZImage.prototype.brushVML = function (vmlRoot) {
  28350. var style = this.style;
  28351. var image = style.image; // Image original width, height
  28352. var ow;
  28353. var oh;
  28354. if (isImage(image)) {
  28355. var src = image.src;
  28356. if (src === this._imageSrc) {
  28357. ow = this._imageWidth;
  28358. oh = this._imageHeight;
  28359. } else {
  28360. var imageRuntimeStyle = image.runtimeStyle;
  28361. var oldRuntimeWidth = imageRuntimeStyle.width;
  28362. var oldRuntimeHeight = imageRuntimeStyle.height;
  28363. imageRuntimeStyle.width = 'auto';
  28364. imageRuntimeStyle.height = 'auto'; // get the original size
  28365. ow = image.width;
  28366. oh = image.height; // and remove overides
  28367. imageRuntimeStyle.width = oldRuntimeWidth;
  28368. imageRuntimeStyle.height = oldRuntimeHeight; // Caching image original width, height and src
  28369. this._imageSrc = src;
  28370. this._imageWidth = ow;
  28371. this._imageHeight = oh;
  28372. }
  28373. image = src;
  28374. } else {
  28375. if (image === this._imageSrc) {
  28376. ow = this._imageWidth;
  28377. oh = this._imageHeight;
  28378. }
  28379. }
  28380. if (!image) {
  28381. return;
  28382. }
  28383. var x = style.x || 0;
  28384. var y = style.y || 0;
  28385. var dw = style.width;
  28386. var dh = style.height;
  28387. var sw = style.sWidth;
  28388. var sh = style.sHeight;
  28389. var sx = style.sx || 0;
  28390. var sy = style.sy || 0;
  28391. var hasCrop = sw && sh;
  28392. var vmlEl = this._vmlEl;
  28393. if (!vmlEl) {
  28394. // FIXME 使用 group 在 left, top 都不是 0 的时候就无法显示了。
  28395. // vmlEl = vmlCore.createNode('group');
  28396. vmlEl = doc.createElement('div');
  28397. initRootElStyle(vmlEl);
  28398. this._vmlEl = vmlEl;
  28399. }
  28400. var vmlElStyle = vmlEl.style;
  28401. var hasRotation = false;
  28402. var m;
  28403. var scaleX = 1;
  28404. var scaleY = 1;
  28405. if (this.transform) {
  28406. m = this.transform;
  28407. scaleX = sqrt(m[0] * m[0] + m[1] * m[1]);
  28408. scaleY = sqrt(m[2] * m[2] + m[3] * m[3]);
  28409. hasRotation = m[1] || m[2];
  28410. }
  28411. if (hasRotation) {
  28412. // If filters are necessary (rotation exists), create them
  28413. // filters are bog-slow, so only create them if abbsolutely necessary
  28414. // The following check doesn't account for skews (which don't exist
  28415. // in the canvas spec (yet) anyway.
  28416. // From excanvas
  28417. var p0 = [x, y];
  28418. var p1 = [x + dw, y];
  28419. var p2 = [x, y + dh];
  28420. var p3 = [x + dw, y + dh];
  28421. applyTransform(p0, p0, m);
  28422. applyTransform(p1, p1, m);
  28423. applyTransform(p2, p2, m);
  28424. applyTransform(p3, p3, m);
  28425. var maxX = mathMax$4(p0[0], p1[0], p2[0], p3[0]);
  28426. var maxY = mathMax$4(p0[1], p1[1], p2[1], p3[1]);
  28427. var transformFilter = [];
  28428. transformFilter.push('M11=', m[0] / scaleX, comma, 'M12=', m[2] / scaleY, comma, 'M21=', m[1] / scaleX, comma, 'M22=', m[3] / scaleY, comma, 'Dx=', round$2(x * scaleX + m[4]), comma, 'Dy=', round$2(y * scaleY + m[5]));
  28429. vmlElStyle.padding = '0 ' + round$2(maxX) + 'px ' + round$2(maxY) + 'px 0'; // FIXME DXImageTransform 在 IE11 的兼容模式下不起作用
  28430. vmlElStyle.filter = imageTransformPrefix + '.Matrix(' + transformFilter.join('') + ', SizingMethod=clip)';
  28431. } else {
  28432. if (m) {
  28433. x = x * scaleX + m[4];
  28434. y = y * scaleY + m[5];
  28435. }
  28436. vmlElStyle.filter = '';
  28437. vmlElStyle.left = round$2(x) + 'px';
  28438. vmlElStyle.top = round$2(y) + 'px';
  28439. }
  28440. var imageEl = this._imageEl;
  28441. var cropEl = this._cropEl;
  28442. if (!imageEl) {
  28443. imageEl = doc.createElement('div');
  28444. this._imageEl = imageEl;
  28445. }
  28446. var imageELStyle = imageEl.style;
  28447. if (hasCrop) {
  28448. // Needs know image original width and height
  28449. if (!(ow && oh)) {
  28450. var tmpImage = new Image();
  28451. var self = this;
  28452. tmpImage.onload = function () {
  28453. tmpImage.onload = null;
  28454. ow = tmpImage.width;
  28455. oh = tmpImage.height; // Adjust image width and height to fit the ratio destinationSize / sourceSize
  28456. imageELStyle.width = round$2(scaleX * ow * dw / sw) + 'px';
  28457. imageELStyle.height = round$2(scaleY * oh * dh / sh) + 'px'; // Caching image original width, height and src
  28458. self._imageWidth = ow;
  28459. self._imageHeight = oh;
  28460. self._imageSrc = image;
  28461. };
  28462. tmpImage.src = image;
  28463. } else {
  28464. imageELStyle.width = round$2(scaleX * ow * dw / sw) + 'px';
  28465. imageELStyle.height = round$2(scaleY * oh * dh / sh) + 'px';
  28466. }
  28467. if (!cropEl) {
  28468. cropEl = doc.createElement('div');
  28469. cropEl.style.overflow = 'hidden';
  28470. this._cropEl = cropEl;
  28471. }
  28472. var cropElStyle = cropEl.style;
  28473. cropElStyle.width = round$2((dw + sx * dw / sw) * scaleX);
  28474. cropElStyle.height = round$2((dh + sy * dh / sh) * scaleY);
  28475. cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx=' + -sx * dw / sw * scaleX + ',Dy=' + -sy * dh / sh * scaleY + ')';
  28476. if (!cropEl.parentNode) {
  28477. vmlEl.appendChild(cropEl);
  28478. }
  28479. if (imageEl.parentNode != cropEl) {
  28480. cropEl.appendChild(imageEl);
  28481. }
  28482. } else {
  28483. imageELStyle.width = round$2(scaleX * dw) + 'px';
  28484. imageELStyle.height = round$2(scaleY * dh) + 'px';
  28485. vmlEl.appendChild(imageEl);
  28486. if (cropEl && cropEl.parentNode) {
  28487. vmlEl.removeChild(cropEl);
  28488. this._cropEl = null;
  28489. }
  28490. }
  28491. var filterStr = '';
  28492. var alpha = style.opacity;
  28493. if (alpha < 1) {
  28494. filterStr += '.Alpha(opacity=' + round$2(alpha * 100) + ') ';
  28495. }
  28496. filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)';
  28497. imageELStyle.filter = filterStr;
  28498. vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); // Append to root
  28499. append(vmlRoot, vmlEl); // Text
  28500. if (style.text != null) {
  28501. this.drawRectText(vmlRoot, this.getBoundingRect());
  28502. }
  28503. };
  28504. ZImage.prototype.onRemove = function (vmlRoot) {
  28505. remove(vmlRoot, this._vmlEl);
  28506. this._vmlEl = null;
  28507. this._cropEl = null;
  28508. this._imageEl = null;
  28509. this.removeRectText(vmlRoot);
  28510. };
  28511. ZImage.prototype.onAdd = function (vmlRoot) {
  28512. append(vmlRoot, this._vmlEl);
  28513. this.appendRectText(vmlRoot);
  28514. };
  28515. /***************************************************
  28516. * TEXT
  28517. **************************************************/
  28518. var DEFAULT_STYLE_NORMAL = 'normal';
  28519. var fontStyleCache = {};
  28520. var fontStyleCacheCount = 0;
  28521. var MAX_FONT_CACHE_SIZE = 100;
  28522. var fontEl = document.createElement('div');
  28523. var getFontStyle = function (fontString) {
  28524. var fontStyle = fontStyleCache[fontString];
  28525. if (!fontStyle) {
  28526. // Clear cache
  28527. if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) {
  28528. fontStyleCacheCount = 0;
  28529. fontStyleCache = {};
  28530. }
  28531. var style = fontEl.style;
  28532. var fontFamily;
  28533. try {
  28534. style.font = fontString;
  28535. fontFamily = style.fontFamily.split(',')[0];
  28536. } catch (e) {}
  28537. fontStyle = {
  28538. style: style.fontStyle || DEFAULT_STYLE_NORMAL,
  28539. variant: style.fontVariant || DEFAULT_STYLE_NORMAL,
  28540. weight: style.fontWeight || DEFAULT_STYLE_NORMAL,
  28541. size: parseFloat(style.fontSize || 12) | 0,
  28542. family: fontFamily || 'Microsoft YaHei'
  28543. };
  28544. fontStyleCache[fontString] = fontStyle;
  28545. fontStyleCacheCount++;
  28546. }
  28547. return fontStyle;
  28548. };
  28549. var textMeasureEl; // Overwrite measure text method
  28550. $override$1('measureText', function (text, textFont) {
  28551. var doc$$1 = doc;
  28552. if (!textMeasureEl) {
  28553. textMeasureEl = doc$$1.createElement('div');
  28554. textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;' + 'padding:0;margin:0;border:none;white-space:pre;';
  28555. doc.body.appendChild(textMeasureEl);
  28556. }
  28557. try {
  28558. textMeasureEl.style.font = textFont;
  28559. } catch (ex) {// Ignore failures to set to invalid font.
  28560. }
  28561. textMeasureEl.innerHTML = ''; // Don't use innerHTML or innerText because they allow markup/whitespace.
  28562. textMeasureEl.appendChild(doc$$1.createTextNode(text));
  28563. return {
  28564. width: textMeasureEl.offsetWidth
  28565. };
  28566. });
  28567. var tmpRect$2 = new BoundingRect();
  28568. var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) {
  28569. var style = this.style; // Optimize, avoid normalize every time.
  28570. this.__dirty && normalizeTextStyle(style, true);
  28571. var text = style.text; // Convert to string
  28572. text != null && (text += '');
  28573. if (!text) {
  28574. return;
  28575. } // Convert rich text to plain text. Rich text is not supported in
  28576. // IE8-, but tags in rich text template will be removed.
  28577. if (style.rich) {
  28578. var contentBlock = parseRichText(text, style);
  28579. text = [];
  28580. for (var i = 0; i < contentBlock.lines.length; i++) {
  28581. var tokens = contentBlock.lines[i].tokens;
  28582. var textLine = [];
  28583. for (var j = 0; j < tokens.length; j++) {
  28584. textLine.push(tokens[j].text);
  28585. }
  28586. text.push(textLine.join(''));
  28587. }
  28588. text = text.join('\n');
  28589. }
  28590. var x;
  28591. var y;
  28592. var align = style.textAlign;
  28593. var verticalAlign = style.textVerticalAlign;
  28594. var fontStyle = getFontStyle(style.font); // FIXME encodeHtmlAttribute ?
  28595. var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' ' + fontStyle.size + 'px "' + fontStyle.family + '"';
  28596. textRect = textRect || getBoundingRect(text, font, align, verticalAlign); // Transform rect to view space
  28597. var m = this.transform; // Ignore transform for text in other element
  28598. if (m && !fromTextEl) {
  28599. tmpRect$2.copy(rect);
  28600. tmpRect$2.applyTransform(m);
  28601. rect = tmpRect$2;
  28602. }
  28603. if (!fromTextEl) {
  28604. var textPosition = style.textPosition;
  28605. var distance$$1 = style.textDistance; // Text position represented by coord
  28606. if (textPosition instanceof Array) {
  28607. x = rect.x + parsePercent$3(textPosition[0], rect.width);
  28608. y = rect.y + parsePercent$3(textPosition[1], rect.height);
  28609. align = align || 'left';
  28610. } else {
  28611. var res = adjustTextPositionOnRect(textPosition, rect, distance$$1);
  28612. x = res.x;
  28613. y = res.y; // Default align and baseline when has textPosition
  28614. align = align || res.textAlign;
  28615. verticalAlign = verticalAlign || res.textVerticalAlign;
  28616. }
  28617. } else {
  28618. x = rect.x;
  28619. y = rect.y;
  28620. }
  28621. x = adjustTextX(x, textRect.width, align);
  28622. y = adjustTextY(y, textRect.height, verticalAlign); // Force baseline 'middle'
  28623. y += textRect.height / 2; // var fontSize = fontStyle.size;
  28624. // 1.75 is an arbitrary number, as there is no info about the text baseline
  28625. // switch (baseline) {
  28626. // case 'hanging':
  28627. // case 'top':
  28628. // y += fontSize / 1.75;
  28629. // break;
  28630. // case 'middle':
  28631. // break;
  28632. // default:
  28633. // // case null:
  28634. // // case 'alphabetic':
  28635. // // case 'ideographic':
  28636. // // case 'bottom':
  28637. // y -= fontSize / 2.25;
  28638. // break;
  28639. // }
  28640. // switch (align) {
  28641. // case 'left':
  28642. // break;
  28643. // case 'center':
  28644. // x -= textRect.width / 2;
  28645. // break;
  28646. // case 'right':
  28647. // x -= textRect.width;
  28648. // break;
  28649. // case 'end':
  28650. // align = elementStyle.direction == 'ltr' ? 'right' : 'left';
  28651. // break;
  28652. // case 'start':
  28653. // align = elementStyle.direction == 'rtl' ? 'right' : 'left';
  28654. // break;
  28655. // default:
  28656. // align = 'left';
  28657. // }
  28658. var createNode$$1 = createNode;
  28659. var textVmlEl = this._textVmlEl;
  28660. var pathEl;
  28661. var textPathEl;
  28662. var skewEl;
  28663. if (!textVmlEl) {
  28664. textVmlEl = createNode$$1('line');
  28665. pathEl = createNode$$1('path');
  28666. textPathEl = createNode$$1('textpath');
  28667. skewEl = createNode$$1('skew'); // FIXME Why here is not cammel case
  28668. // Align 'center' seems wrong
  28669. textPathEl.style['v-text-align'] = 'left';
  28670. initRootElStyle(textVmlEl);
  28671. pathEl.textpathok = true;
  28672. textPathEl.on = true;
  28673. textVmlEl.from = '0 0';
  28674. textVmlEl.to = '1000 0.05';
  28675. append(textVmlEl, skewEl);
  28676. append(textVmlEl, pathEl);
  28677. append(textVmlEl, textPathEl);
  28678. this._textVmlEl = textVmlEl;
  28679. } else {
  28680. // 这里是在前面 appendChild 保证顺序的前提下
  28681. skewEl = textVmlEl.firstChild;
  28682. pathEl = skewEl.nextSibling;
  28683. textPathEl = pathEl.nextSibling;
  28684. }
  28685. var coords = [x, y];
  28686. var textVmlElStyle = textVmlEl.style; // Ignore transform for text in other element
  28687. if (m && fromTextEl) {
  28688. applyTransform(coords, coords, m);
  28689. skewEl.on = true;
  28690. skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma + m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0'; // Text position
  28691. skewEl.offset = (round$2(coords[0]) || 0) + ',' + (round$2(coords[1]) || 0); // Left top point as origin
  28692. skewEl.origin = '0 0';
  28693. textVmlElStyle.left = '0px';
  28694. textVmlElStyle.top = '0px';
  28695. } else {
  28696. skewEl.on = false;
  28697. textVmlElStyle.left = round$2(x) + 'px';
  28698. textVmlElStyle.top = round$2(y) + 'px';
  28699. }
  28700. textPathEl.string = encodeHtmlAttribute(text); // TODO
  28701. try {
  28702. textPathEl.style.font = font;
  28703. } // Error font format
  28704. catch (e) {}
  28705. updateFillAndStroke(textVmlEl, 'fill', {
  28706. fill: style.textFill,
  28707. opacity: style.opacity
  28708. }, this);
  28709. updateFillAndStroke(textVmlEl, 'stroke', {
  28710. stroke: style.textStroke,
  28711. opacity: style.opacity,
  28712. lineDash: style.lineDash
  28713. }, this);
  28714. textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); // Attached to root
  28715. append(vmlRoot, textVmlEl);
  28716. };
  28717. var removeRectText = function (vmlRoot) {
  28718. remove(vmlRoot, this._textVmlEl);
  28719. this._textVmlEl = null;
  28720. };
  28721. var appendRectText = function (vmlRoot) {
  28722. append(vmlRoot, this._textVmlEl);
  28723. };
  28724. var list = [RectText, Displayable, ZImage, Path, Text]; // In case Displayable has been mixed in RectText
  28725. for (var i$1 = 0; i$1 < list.length; i$1++) {
  28726. var proto = list[i$1].prototype;
  28727. proto.drawRectText = drawRectText;
  28728. proto.removeRectText = removeRectText;
  28729. proto.appendRectText = appendRectText;
  28730. }
  28731. Text.prototype.brushVML = function (vmlRoot) {
  28732. var style = this.style;
  28733. if (style.text != null) {
  28734. this.drawRectText(vmlRoot, {
  28735. x: style.x || 0,
  28736. y: style.y || 0,
  28737. width: 0,
  28738. height: 0
  28739. }, this.getBoundingRect(), true);
  28740. } else {
  28741. this.removeRectText(vmlRoot);
  28742. }
  28743. };
  28744. Text.prototype.onRemove = function (vmlRoot) {
  28745. this.removeRectText(vmlRoot);
  28746. };
  28747. Text.prototype.onAdd = function (vmlRoot) {
  28748. this.appendRectText(vmlRoot);
  28749. };
  28750. }
  28751. /**
  28752. * VML Painter.
  28753. *
  28754. * @module zrender/vml/Painter
  28755. */
  28756. function parseInt10$1(val) {
  28757. return parseInt(val, 10);
  28758. }
  28759. /**
  28760. * @alias module:zrender/vml/Painter
  28761. */
  28762. function VMLPainter(root, storage) {
  28763. initVML();
  28764. this.root = root;
  28765. this.storage = storage;
  28766. var vmlViewport = document.createElement('div');
  28767. var vmlRoot = document.createElement('div');
  28768. vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;';
  28769. vmlRoot.style.cssText = 'position:absolute;left:0;top:0;';
  28770. root.appendChild(vmlViewport);
  28771. this._vmlRoot = vmlRoot;
  28772. this._vmlViewport = vmlViewport;
  28773. this.resize(); // Modify storage
  28774. var oldDelFromStorage = storage.delFromStorage;
  28775. var oldAddToStorage = storage.addToStorage;
  28776. storage.delFromStorage = function (el) {
  28777. oldDelFromStorage.call(storage, el);
  28778. if (el) {
  28779. el.onRemove && el.onRemove(vmlRoot);
  28780. }
  28781. };
  28782. storage.addToStorage = function (el) {
  28783. // Displayable already has a vml node
  28784. el.onAdd && el.onAdd(vmlRoot);
  28785. oldAddToStorage.call(storage, el);
  28786. };
  28787. this._firstPaint = true;
  28788. }
  28789. VMLPainter.prototype = {
  28790. constructor: VMLPainter,
  28791. getType: function () {
  28792. return 'vml';
  28793. },
  28794. /**
  28795. * @return {HTMLDivElement}
  28796. */
  28797. getViewportRoot: function () {
  28798. return this._vmlViewport;
  28799. },
  28800. getViewportRootOffset: function () {
  28801. var viewportRoot = this.getViewportRoot();
  28802. if (viewportRoot) {
  28803. return {
  28804. offsetLeft: viewportRoot.offsetLeft || 0,
  28805. offsetTop: viewportRoot.offsetTop || 0
  28806. };
  28807. }
  28808. },
  28809. /**
  28810. * 刷新
  28811. */
  28812. refresh: function () {
  28813. var list = this.storage.getDisplayList(true, true);
  28814. this._paintList(list);
  28815. },
  28816. _paintList: function (list) {
  28817. var vmlRoot = this._vmlRoot;
  28818. for (var i = 0; i < list.length; i++) {
  28819. var el = list[i];
  28820. if (el.invisible || el.ignore) {
  28821. if (!el.__alreadyNotVisible) {
  28822. el.onRemove(vmlRoot);
  28823. } // Set as already invisible
  28824. el.__alreadyNotVisible = true;
  28825. } else {
  28826. if (el.__alreadyNotVisible) {
  28827. el.onAdd(vmlRoot);
  28828. }
  28829. el.__alreadyNotVisible = false;
  28830. if (el.__dirty) {
  28831. el.beforeBrush && el.beforeBrush();
  28832. (el.brushVML || el.brush).call(el, vmlRoot);
  28833. el.afterBrush && el.afterBrush();
  28834. }
  28835. }
  28836. el.__dirty = false;
  28837. }
  28838. if (this._firstPaint) {
  28839. // Detached from document at first time
  28840. // to avoid page refreshing too many times
  28841. // FIXME 如果每次都先 removeChild 可能会导致一些填充和描边的效果改变
  28842. this._vmlViewport.appendChild(vmlRoot);
  28843. this._firstPaint = false;
  28844. }
  28845. },
  28846. resize: function (width, height) {
  28847. var width = width == null ? this._getWidth() : width;
  28848. var height = height == null ? this._getHeight() : height;
  28849. if (this._width != width || this._height != height) {
  28850. this._width = width;
  28851. this._height = height;
  28852. var vmlViewportStyle = this._vmlViewport.style;
  28853. vmlViewportStyle.width = width + 'px';
  28854. vmlViewportStyle.height = height + 'px';
  28855. }
  28856. },
  28857. dispose: function () {
  28858. this.root.innerHTML = '';
  28859. this._vmlRoot = this._vmlViewport = this.storage = null;
  28860. },
  28861. getWidth: function () {
  28862. return this._width;
  28863. },
  28864. getHeight: function () {
  28865. return this._height;
  28866. },
  28867. clear: function () {
  28868. if (this._vmlViewport) {
  28869. this.root.removeChild(this._vmlViewport);
  28870. }
  28871. },
  28872. _getWidth: function () {
  28873. var root = this.root;
  28874. var stl = root.currentStyle;
  28875. return (root.clientWidth || parseInt10$1(stl.width)) - parseInt10$1(stl.paddingLeft) - parseInt10$1(stl.paddingRight) | 0;
  28876. },
  28877. _getHeight: function () {
  28878. var root = this.root;
  28879. var stl = root.currentStyle;
  28880. return (root.clientHeight || parseInt10$1(stl.height)) - parseInt10$1(stl.paddingTop) - parseInt10$1(stl.paddingBottom) | 0;
  28881. }
  28882. }; // Not supported methods
  28883. function createMethodNotSupport(method) {
  28884. return function () {
  28885. zrLog('In IE8.0 VML mode painter not support method "' + method + '"');
  28886. };
  28887. } // Unsupported methods
  28888. each$1(['getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers', 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'], function (name) {
  28889. VMLPainter.prototype[name] = createMethodNotSupport(name);
  28890. });
  28891. registerPainter('vml', VMLPainter);
  28892. exports.version = version;
  28893. exports.dependencies = dependencies;
  28894. exports.PRIORITY = PRIORITY;
  28895. exports.init = init;
  28896. exports.connect = connect;
  28897. exports.disConnect = disConnect;
  28898. exports.disconnect = disconnect;
  28899. exports.dispose = dispose;
  28900. exports.getInstanceByDom = getInstanceByDom;
  28901. exports.getInstanceById = getInstanceById;
  28902. exports.registerTheme = registerTheme;
  28903. exports.registerPreprocessor = registerPreprocessor;
  28904. exports.registerProcessor = registerProcessor;
  28905. exports.registerPostUpdate = registerPostUpdate;
  28906. exports.registerAction = registerAction;
  28907. exports.registerCoordinateSystem = registerCoordinateSystem;
  28908. exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;
  28909. exports.registerLayout = registerLayout;
  28910. exports.registerVisual = registerVisual;
  28911. exports.registerLoading = registerLoading;
  28912. exports.extendComponentModel = extendComponentModel;
  28913. exports.extendComponentView = extendComponentView;
  28914. exports.extendSeriesModel = extendSeriesModel;
  28915. exports.extendChartView = extendChartView;
  28916. exports.setCanvasCreator = setCanvasCreator;
  28917. exports.$inject = $inject;
  28918. exports.zrender = zrender;
  28919. exports.graphic = graphic;
  28920. exports.number = number;
  28921. exports.format = format;
  28922. exports.throttle = throttle;
  28923. exports.helper = helper;
  28924. exports.matrix = matrix;
  28925. exports.vector = vector;
  28926. exports.color = color;
  28927. exports.util = ecUtil;
  28928. exports.List = List;
  28929. exports.Model = Model;
  28930. exports.Axis = Axis;
  28931. exports.env = env$1;
  28932. });