echarts-en.simple.js 994 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (factory((global.echarts = {})));
  5. }(this, (function (exports) { 'use strict';
  6. // (1) The code `if (__DEV__) ...` can be removed by build tool.
  7. // (2) If intend to use `__DEV__`, this module should be imported. Use a global
  8. // variable `__DEV__` may cause that miss the declaration (see #6535), or the
  9. // declaration is behind of the using position (for example in `Model.extent`,
  10. // And tools like rollup can not analysis the dependency if not import).
  11. var dev;
  12. // In browser
  13. if (typeof window !== 'undefined') {
  14. dev = window.__DEV__;
  15. }
  16. // In node
  17. else if (typeof global !== 'undefined') {
  18. dev = global.__DEV__;
  19. }
  20. if (typeof dev === 'undefined') {
  21. dev = true;
  22. }
  23. var __DEV__ = dev;
  24. /**
  25. * zrender: 生成唯一id
  26. *
  27. * @author errorrik (errorrik@gmail.com)
  28. */
  29. var idStart = 0x0907;
  30. var guid = function () {
  31. return idStart++;
  32. };
  33. /**
  34. * echarts设备环境识别
  35. *
  36. * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
  37. * @author firede[firede@firede.us]
  38. * @desc thanks zepto.
  39. */
  40. var env = {};
  41. if (typeof wx !== 'undefined') {
  42. // In Weixin Application
  43. env = {
  44. browser: {},
  45. os: {},
  46. node: false,
  47. wxa: true, // Weixin Application
  48. canvasSupported: true,
  49. svgSupported: false,
  50. touchEventsSupported: true
  51. };
  52. }
  53. else if (typeof document === 'undefined' && typeof self !== 'undefined') {
  54. // In worker
  55. env = {
  56. browser: {},
  57. os: {},
  58. node: false,
  59. worker: true,
  60. canvasSupported: true
  61. };
  62. }
  63. else if (typeof navigator === 'undefined') {
  64. // In node
  65. env = {
  66. browser: {},
  67. os: {},
  68. node: true,
  69. worker: false,
  70. // Assume canvas is supported
  71. canvasSupported: true,
  72. svgSupported: true
  73. };
  74. }
  75. else {
  76. env = detect(navigator.userAgent);
  77. }
  78. var env$1 = env;
  79. // Zepto.js
  80. // (c) 2010-2013 Thomas Fuchs
  81. // Zepto.js may be freely distributed under the MIT license.
  82. function detect(ua) {
  83. var os = {};
  84. var browser = {};
  85. // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/);
  86. // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  87. // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  88. // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  89. // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  90. // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/);
  91. // var touchpad = webos && ua.match(/TouchPad/);
  92. // var kindle = ua.match(/Kindle\/([\d.]+)/);
  93. // var silk = ua.match(/Silk\/([\d._]+)/);
  94. // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
  95. // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/);
  96. // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/);
  97. // var playbook = ua.match(/PlayBook/);
  98. // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/);
  99. var firefox = ua.match(/Firefox\/([\d.]+)/);
  100. // var safari = webkit && ua.match(/Mobile\//) && !chrome;
  101. // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;
  102. var ie = ua.match(/MSIE\s([\d.]+)/)
  103. // IE 11 Trident/7.0; rv:11.0
  104. || ua.match(/Trident\/.+?rv:(([\d.]+))/);
  105. var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+
  106. var weChat = (/micromessenger/i).test(ua);
  107. // Todo: clean this up with a better OS/browser seperation:
  108. // - discern (more) between multiple browsers on android
  109. // - decide if kindle fire in silk mode is android or not
  110. // - Firefox on Android doesn't specify the Android version
  111. // - possibly devide in os, device and browser hashes
  112. // if (browser.webkit = !!webkit) browser.version = webkit[1];
  113. // if (android) os.android = true, os.version = android[2];
  114. // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
  115. // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
  116. // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  117. // if (webos) os.webos = true, os.version = webos[2];
  118. // if (touchpad) os.touchpad = true;
  119. // if (blackberry) os.blackberry = true, os.version = blackberry[2];
  120. // if (bb10) os.bb10 = true, os.version = bb10[2];
  121. // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
  122. // if (playbook) browser.playbook = true;
  123. // if (kindle) os.kindle = true, os.version = kindle[1];
  124. // if (silk) browser.silk = true, browser.version = silk[1];
  125. // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
  126. // if (chrome) browser.chrome = true, browser.version = chrome[1];
  127. if (firefox) {
  128. browser.firefox = true;
  129. browser.version = firefox[1];
  130. }
  131. // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;
  132. // if (webview) browser.webview = true;
  133. if (ie) {
  134. browser.ie = true;
  135. browser.version = ie[1];
  136. }
  137. if (edge) {
  138. browser.edge = true;
  139. browser.version = edge[1];
  140. }
  141. // It is difficult to detect WeChat in Win Phone precisely, because ua can
  142. // not be set on win phone. So we do not consider Win Phone.
  143. if (weChat) {
  144. browser.weChat = true;
  145. }
  146. // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
  147. // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
  148. // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||
  149. // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
  150. // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
  151. return {
  152. browser: browser,
  153. os: os,
  154. node: false,
  155. // 原生canvas支持,改极端点了
  156. // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)
  157. canvasSupported: !!document.createElement('canvas').getContext,
  158. svgSupported: typeof SVGRect !== 'undefined',
  159. // works on most browsers
  160. // IE10/11 does not support touch event, and MS Edge supports them but not by
  161. // default, so we dont check navigator.maxTouchPoints for them here.
  162. touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,
  163. // <http://caniuse.com/#search=pointer%20event>.
  164. pointerEventsSupported: 'onpointerdown' in window
  165. // Firefox supports pointer but not by default, only MS browsers are reliable on pointer
  166. // events currently. So we dont use that on other browsers unless tested sufficiently.
  167. // Although IE 10 supports pointer event, it use old style and is different from the
  168. // standard. So we exclude that. (IE 10 is hardly used on touch device)
  169. && (browser.edge || (browser.ie && browser.version >= 11))
  170. };
  171. }
  172. /**
  173. * @module zrender/core/util
  174. */
  175. // 用于处理merge时无法遍历Date等对象的问题
  176. var BUILTIN_OBJECT = {
  177. '[object Function]': 1,
  178. '[object RegExp]': 1,
  179. '[object Date]': 1,
  180. '[object Error]': 1,
  181. '[object CanvasGradient]': 1,
  182. '[object CanvasPattern]': 1,
  183. // For node-canvas
  184. '[object Image]': 1,
  185. '[object Canvas]': 1
  186. };
  187. var TYPED_ARRAY = {
  188. '[object Int8Array]': 1,
  189. '[object Uint8Array]': 1,
  190. '[object Uint8ClampedArray]': 1,
  191. '[object Int16Array]': 1,
  192. '[object Uint16Array]': 1,
  193. '[object Int32Array]': 1,
  194. '[object Uint32Array]': 1,
  195. '[object Float32Array]': 1,
  196. '[object Float64Array]': 1
  197. };
  198. var objToString = Object.prototype.toString;
  199. var arrayProto = Array.prototype;
  200. var nativeForEach = arrayProto.forEach;
  201. var nativeFilter = arrayProto.filter;
  202. var nativeSlice = arrayProto.slice;
  203. var nativeMap = arrayProto.map;
  204. var nativeReduce = arrayProto.reduce;
  205. // Avoid assign to an exported variable, for transforming to cjs.
  206. var methods = {};
  207. function $override(name, fn) {
  208. // Clear ctx instance for different environment
  209. if (name === 'createCanvas') {
  210. _ctx = null;
  211. }
  212. methods[name] = fn;
  213. }
  214. /**
  215. * Those data types can be cloned:
  216. * Plain object, Array, TypedArray, number, string, null, undefined.
  217. * Those data types will be assgined using the orginal data:
  218. * BUILTIN_OBJECT
  219. * Instance of user defined class will be cloned to a plain object, without
  220. * properties in prototype.
  221. * Other data types is not supported (not sure what will happen).
  222. *
  223. * Caution: do not support clone Date, for performance consideration.
  224. * (There might be a large number of date in `series.data`).
  225. * So date should not be modified in and out of echarts.
  226. *
  227. * @param {*} source
  228. * @return {*} new
  229. */
  230. function clone(source) {
  231. if (source == null || typeof source != 'object') {
  232. return source;
  233. }
  234. var result = source;
  235. var typeStr = objToString.call(source);
  236. if (typeStr === '[object Array]') {
  237. if (!isPrimitive(source)) {
  238. result = [];
  239. for (var i = 0, len = source.length; i < len; i++) {
  240. result[i] = clone(source[i]);
  241. }
  242. }
  243. }
  244. else if (TYPED_ARRAY[typeStr]) {
  245. if (!isPrimitive(source)) {
  246. var Ctor = source.constructor;
  247. if (source.constructor.from) {
  248. result = Ctor.from(source);
  249. }
  250. else {
  251. result = new Ctor(source.length);
  252. for (var i = 0, len = source.length; i < len; i++) {
  253. result[i] = clone(source[i]);
  254. }
  255. }
  256. }
  257. }
  258. else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
  259. result = {};
  260. for (var key in source) {
  261. if (source.hasOwnProperty(key)) {
  262. result[key] = clone(source[key]);
  263. }
  264. }
  265. }
  266. return result;
  267. }
  268. /**
  269. * @memberOf module:zrender/core/util
  270. * @param {*} target
  271. * @param {*} source
  272. * @param {boolean} [overwrite=false]
  273. */
  274. function merge(target, source, overwrite) {
  275. // We should escapse that source is string
  276. // and enter for ... in ...
  277. if (!isObject$1(source) || !isObject$1(target)) {
  278. return overwrite ? clone(source) : target;
  279. }
  280. for (var key in source) {
  281. if (source.hasOwnProperty(key)) {
  282. var targetProp = target[key];
  283. var sourceProp = source[key];
  284. if (isObject$1(sourceProp)
  285. && isObject$1(targetProp)
  286. && !isArray(sourceProp)
  287. && !isArray(targetProp)
  288. && !isDom(sourceProp)
  289. && !isDom(targetProp)
  290. && !isBuiltInObject(sourceProp)
  291. && !isBuiltInObject(targetProp)
  292. && !isPrimitive(sourceProp)
  293. && !isPrimitive(targetProp)
  294. ) {
  295. // 如果需要递归覆盖,就递归调用merge
  296. merge(targetProp, sourceProp, overwrite);
  297. }
  298. else if (overwrite || !(key in target)) {
  299. // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
  300. // NOTE,在 target[key] 不存在的时候也是直接覆盖
  301. target[key] = clone(source[key], true);
  302. }
  303. }
  304. }
  305. return target;
  306. }
  307. /**
  308. * @param {Array} targetAndSources The first item is target, and the rests are source.
  309. * @param {boolean} [overwrite=false]
  310. * @return {*} target
  311. */
  312. function mergeAll(targetAndSources, overwrite) {
  313. var result = targetAndSources[0];
  314. for (var i = 1, len = targetAndSources.length; i < len; i++) {
  315. result = merge(result, targetAndSources[i], overwrite);
  316. }
  317. return result;
  318. }
  319. /**
  320. * @param {*} target
  321. * @param {*} source
  322. * @memberOf module:zrender/core/util
  323. */
  324. function extend(target, source) {
  325. for (var key in source) {
  326. if (source.hasOwnProperty(key)) {
  327. target[key] = source[key];
  328. }
  329. }
  330. return target;
  331. }
  332. /**
  333. * @param {*} target
  334. * @param {*} source
  335. * @param {boolean} [overlay=false]
  336. * @memberOf module:zrender/core/util
  337. */
  338. function defaults(target, source, overlay) {
  339. for (var key in source) {
  340. if (source.hasOwnProperty(key)
  341. && (overlay ? source[key] != null : target[key] == null)
  342. ) {
  343. target[key] = source[key];
  344. }
  345. }
  346. return target;
  347. }
  348. var createCanvas = function () {
  349. return methods.createCanvas();
  350. };
  351. methods.createCanvas = function () {
  352. return document.createElement('canvas');
  353. };
  354. // FIXME
  355. var _ctx;
  356. function getContext() {
  357. if (!_ctx) {
  358. // Use util.createCanvas instead of createCanvas
  359. // because createCanvas may be overwritten in different environment
  360. _ctx = createCanvas().getContext('2d');
  361. }
  362. return _ctx;
  363. }
  364. /**
  365. * 查询数组中元素的index
  366. * @memberOf module:zrender/core/util
  367. */
  368. function indexOf(array, value) {
  369. if (array) {
  370. if (array.indexOf) {
  371. return array.indexOf(value);
  372. }
  373. for (var i = 0, len = array.length; i < len; i++) {
  374. if (array[i] === value) {
  375. return i;
  376. }
  377. }
  378. }
  379. return -1;
  380. }
  381. /**
  382. * 构造类继承关系
  383. *
  384. * @memberOf module:zrender/core/util
  385. * @param {Function} clazz 源类
  386. * @param {Function} baseClazz 基类
  387. */
  388. function inherits(clazz, baseClazz) {
  389. var clazzPrototype = clazz.prototype;
  390. function F() {}
  391. F.prototype = baseClazz.prototype;
  392. clazz.prototype = new F();
  393. for (var prop in clazzPrototype) {
  394. clazz.prototype[prop] = clazzPrototype[prop];
  395. }
  396. clazz.prototype.constructor = clazz;
  397. clazz.superClass = baseClazz;
  398. }
  399. /**
  400. * @memberOf module:zrender/core/util
  401. * @param {Object|Function} target
  402. * @param {Object|Function} sorce
  403. * @param {boolean} overlay
  404. */
  405. function mixin(target, source, overlay) {
  406. target = 'prototype' in target ? target.prototype : target;
  407. source = 'prototype' in source ? source.prototype : source;
  408. defaults(target, source, overlay);
  409. }
  410. /**
  411. * Consider typed array.
  412. * @param {Array|TypedArray} data
  413. */
  414. function isArrayLike(data) {
  415. if (! data) {
  416. return;
  417. }
  418. if (typeof data == 'string') {
  419. return false;
  420. }
  421. return typeof data.length == 'number';
  422. }
  423. /**
  424. * 数组或对象遍历
  425. * @memberOf module:zrender/core/util
  426. * @param {Object|Array} obj
  427. * @param {Function} cb
  428. * @param {*} [context]
  429. */
  430. function each$1(obj, cb, context) {
  431. if (!(obj && cb)) {
  432. return;
  433. }
  434. if (obj.forEach && obj.forEach === nativeForEach) {
  435. obj.forEach(cb, context);
  436. }
  437. else if (obj.length === +obj.length) {
  438. for (var i = 0, len = obj.length; i < len; i++) {
  439. cb.call(context, obj[i], i, obj);
  440. }
  441. }
  442. else {
  443. for (var key in obj) {
  444. if (obj.hasOwnProperty(key)) {
  445. cb.call(context, obj[key], key, obj);
  446. }
  447. }
  448. }
  449. }
  450. /**
  451. * 数组映射
  452. * @memberOf module:zrender/core/util
  453. * @param {Array} obj
  454. * @param {Function} cb
  455. * @param {*} [context]
  456. * @return {Array}
  457. */
  458. function map(obj, cb, context) {
  459. if (!(obj && cb)) {
  460. return;
  461. }
  462. if (obj.map && obj.map === nativeMap) {
  463. return obj.map(cb, context);
  464. }
  465. else {
  466. var result = [];
  467. for (var i = 0, len = obj.length; i < len; i++) {
  468. result.push(cb.call(context, obj[i], i, obj));
  469. }
  470. return result;
  471. }
  472. }
  473. /**
  474. * @memberOf module:zrender/core/util
  475. * @param {Array} obj
  476. * @param {Function} cb
  477. * @param {Object} [memo]
  478. * @param {*} [context]
  479. * @return {Array}
  480. */
  481. function reduce(obj, cb, memo, context) {
  482. if (!(obj && cb)) {
  483. return;
  484. }
  485. if (obj.reduce && obj.reduce === nativeReduce) {
  486. return obj.reduce(cb, memo, context);
  487. }
  488. else {
  489. for (var i = 0, len = obj.length; i < len; i++) {
  490. memo = cb.call(context, memo, obj[i], i, obj);
  491. }
  492. return memo;
  493. }
  494. }
  495. /**
  496. * 数组过滤
  497. * @memberOf module:zrender/core/util
  498. * @param {Array} obj
  499. * @param {Function} cb
  500. * @param {*} [context]
  501. * @return {Array}
  502. */
  503. function filter(obj, cb, context) {
  504. if (!(obj && cb)) {
  505. return;
  506. }
  507. if (obj.filter && obj.filter === nativeFilter) {
  508. return obj.filter(cb, context);
  509. }
  510. else {
  511. var result = [];
  512. for (var i = 0, len = obj.length; i < len; i++) {
  513. if (cb.call(context, obj[i], i, obj)) {
  514. result.push(obj[i]);
  515. }
  516. }
  517. return result;
  518. }
  519. }
  520. /**
  521. * 数组项查找
  522. * @memberOf module:zrender/core/util
  523. * @param {Array} obj
  524. * @param {Function} cb
  525. * @param {*} [context]
  526. * @return {*}
  527. */
  528. /**
  529. * @memberOf module:zrender/core/util
  530. * @param {Function} func
  531. * @param {*} context
  532. * @return {Function}
  533. */
  534. function bind(func, context) {
  535. var args = nativeSlice.call(arguments, 2);
  536. return function () {
  537. return func.apply(context, args.concat(nativeSlice.call(arguments)));
  538. };
  539. }
  540. /**
  541. * @memberOf module:zrender/core/util
  542. * @param {Function} func
  543. * @return {Function}
  544. */
  545. function curry(func) {
  546. var args = nativeSlice.call(arguments, 1);
  547. return function () {
  548. return func.apply(this, args.concat(nativeSlice.call(arguments)));
  549. };
  550. }
  551. /**
  552. * @memberOf module:zrender/core/util
  553. * @param {*} value
  554. * @return {boolean}
  555. */
  556. function isArray(value) {
  557. return objToString.call(value) === '[object Array]';
  558. }
  559. /**
  560. * @memberOf module:zrender/core/util
  561. * @param {*} value
  562. * @return {boolean}
  563. */
  564. function isFunction$1(value) {
  565. return typeof value === 'function';
  566. }
  567. /**
  568. * @memberOf module:zrender/core/util
  569. * @param {*} value
  570. * @return {boolean}
  571. */
  572. function isString(value) {
  573. return objToString.call(value) === '[object String]';
  574. }
  575. /**
  576. * @memberOf module:zrender/core/util
  577. * @param {*} value
  578. * @return {boolean}
  579. */
  580. function isObject$1(value) {
  581. // Avoid a V8 JIT bug in Chrome 19-20.
  582. // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
  583. var type = typeof value;
  584. return type === 'function' || (!!value && type == 'object');
  585. }
  586. /**
  587. * @memberOf module:zrender/core/util
  588. * @param {*} value
  589. * @return {boolean}
  590. */
  591. function isBuiltInObject(value) {
  592. return !!BUILTIN_OBJECT[objToString.call(value)];
  593. }
  594. /**
  595. * @memberOf module:zrender/core/util
  596. * @param {*} value
  597. * @return {boolean}
  598. */
  599. function isTypedArray(value) {
  600. return !!TYPED_ARRAY[objToString.call(value)];
  601. }
  602. /**
  603. * @memberOf module:zrender/core/util
  604. * @param {*} value
  605. * @return {boolean}
  606. */
  607. function isDom(value) {
  608. return typeof value === 'object'
  609. && typeof value.nodeType === 'number'
  610. && typeof value.ownerDocument === 'object';
  611. }
  612. /**
  613. * Whether is exactly NaN. Notice isNaN('a') returns true.
  614. * @param {*} value
  615. * @return {boolean}
  616. */
  617. function eqNaN(value) {
  618. return value !== value;
  619. }
  620. /**
  621. * If value1 is not null, then return value1, otherwise judget rest of values.
  622. * Low performance.
  623. * @memberOf module:zrender/core/util
  624. * @return {*} Final value
  625. */
  626. function retrieve(values) {
  627. for (var i = 0, len = arguments.length; i < len; i++) {
  628. if (arguments[i] != null) {
  629. return arguments[i];
  630. }
  631. }
  632. }
  633. function retrieve2(value0, value1) {
  634. return value0 != null
  635. ? value0
  636. : value1;
  637. }
  638. function retrieve3(value0, value1, value2) {
  639. return value0 != null
  640. ? value0
  641. : value1 != null
  642. ? value1
  643. : value2;
  644. }
  645. /**
  646. * @memberOf module:zrender/core/util
  647. * @param {Array} arr
  648. * @param {number} startIndex
  649. * @param {number} endIndex
  650. * @return {Array}
  651. */
  652. function slice() {
  653. return Function.call.apply(nativeSlice, arguments);
  654. }
  655. /**
  656. * Normalize css liked array configuration
  657. * e.g.
  658. * 3 => [3, 3, 3, 3]
  659. * [4, 2] => [4, 2, 4, 2]
  660. * [4, 3, 2] => [4, 3, 2, 3]
  661. * @param {number|Array.<number>} val
  662. * @return {Array.<number>}
  663. */
  664. function normalizeCssArray(val) {
  665. if (typeof (val) === 'number') {
  666. return [val, val, val, val];
  667. }
  668. var len = val.length;
  669. if (len === 2) {
  670. // vertical | horizontal
  671. return [val[0], val[1], val[0], val[1]];
  672. }
  673. else if (len === 3) {
  674. // top | horizontal | bottom
  675. return [val[0], val[1], val[2], val[1]];
  676. }
  677. return val;
  678. }
  679. /**
  680. * @memberOf module:zrender/core/util
  681. * @param {boolean} condition
  682. * @param {string} message
  683. */
  684. function assert$1(condition, message) {
  685. if (!condition) {
  686. throw new Error(message);
  687. }
  688. }
  689. /**
  690. * @memberOf module:zrender/core/util
  691. * @param {string} str string to be trimed
  692. * @return {string} trimed string
  693. */
  694. function trim(str) {
  695. if (str == null) {
  696. return null;
  697. }
  698. else if (typeof str.trim === 'function') {
  699. return str.trim();
  700. }
  701. else {
  702. return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
  703. }
  704. }
  705. var primitiveKey = '__ec_primitive__';
  706. /**
  707. * Set an object as primitive to be ignored traversing children in clone or merge
  708. */
  709. function setAsPrimitive(obj) {
  710. obj[primitiveKey] = true;
  711. }
  712. function isPrimitive(obj) {
  713. return obj[primitiveKey];
  714. }
  715. /**
  716. * @constructor
  717. * @param {Object} obj Only apply `ownProperty`.
  718. */
  719. function HashMap(obj) {
  720. var isArr = isArray(obj);
  721. var thisMap = this;
  722. (obj instanceof HashMap)
  723. ? obj.each(visit)
  724. : (obj && each$1(obj, visit));
  725. function visit(value, key) {
  726. isArr ? thisMap.set(value, key) : thisMap.set(key, value);
  727. }
  728. }
  729. // Add prefix to avoid conflict with Object.prototype.
  730. HashMap.prototype = {
  731. constructor: HashMap,
  732. // Do not provide `has` method to avoid defining what is `has`.
  733. // (We usually treat `null` and `undefined` as the same, different
  734. // from ES6 Map).
  735. get: function (key) {
  736. return this.hasOwnProperty(key) ? this[key] : null;
  737. },
  738. set: function (key, value) {
  739. // Comparing with invocation chaining, `return value` is more commonly
  740. // used in this case: `var someVal = map.set('a', genVal());`
  741. return (this[key] = value);
  742. },
  743. // Although util.each can be performed on this hashMap directly, user
  744. // should not use the exposed keys, who are prefixed.
  745. each: function (cb, context) {
  746. context !== void 0 && (cb = bind(cb, context));
  747. for (var key in this) {
  748. this.hasOwnProperty(key) && cb(this[key], key);
  749. }
  750. },
  751. // Do not use this method if performance sensitive.
  752. removeKey: function (key) {
  753. delete this[key];
  754. }
  755. };
  756. function createHashMap(obj) {
  757. return new HashMap(obj);
  758. }
  759. function noop() {}
  760. var ArrayCtor = typeof Float32Array === 'undefined'
  761. ? Array
  762. : Float32Array;
  763. /**
  764. * 创建一个向量
  765. * @param {number} [x=0]
  766. * @param {number} [y=0]
  767. * @return {Vector2}
  768. */
  769. function create(x, y) {
  770. var out = new ArrayCtor(2);
  771. if (x == null) {
  772. x = 0;
  773. }
  774. if (y == null) {
  775. y = 0;
  776. }
  777. out[0] = x;
  778. out[1] = y;
  779. return out;
  780. }
  781. /**
  782. * 复制向量数据
  783. * @param {Vector2} out
  784. * @param {Vector2} v
  785. * @return {Vector2}
  786. */
  787. function copy(out, v) {
  788. out[0] = v[0];
  789. out[1] = v[1];
  790. return out;
  791. }
  792. /**
  793. * 克隆一个向量
  794. * @param {Vector2} v
  795. * @return {Vector2}
  796. */
  797. function clone$1(v) {
  798. var out = new ArrayCtor(2);
  799. out[0] = v[0];
  800. out[1] = v[1];
  801. return out;
  802. }
  803. /**
  804. * 设置向量的两个项
  805. * @param {Vector2} out
  806. * @param {number} a
  807. * @param {number} b
  808. * @return {Vector2} 结果
  809. */
  810. /**
  811. * 向量相加
  812. * @param {Vector2} out
  813. * @param {Vector2} v1
  814. * @param {Vector2} v2
  815. */
  816. function add(out, v1, v2) {
  817. out[0] = v1[0] + v2[0];
  818. out[1] = v1[1] + v2[1];
  819. return out;
  820. }
  821. /**
  822. * 向量缩放后相加
  823. * @param {Vector2} out
  824. * @param {Vector2} v1
  825. * @param {Vector2} v2
  826. * @param {number} a
  827. */
  828. function scaleAndAdd(out, v1, v2, a) {
  829. out[0] = v1[0] + v2[0] * a;
  830. out[1] = v1[1] + v2[1] * a;
  831. return out;
  832. }
  833. /**
  834. * 向量相减
  835. * @param {Vector2} out
  836. * @param {Vector2} v1
  837. * @param {Vector2} v2
  838. */
  839. function sub(out, v1, v2) {
  840. out[0] = v1[0] - v2[0];
  841. out[1] = v1[1] - v2[1];
  842. return out;
  843. }
  844. /**
  845. * 向量长度
  846. * @param {Vector2} v
  847. * @return {number}
  848. */
  849. function len(v) {
  850. return Math.sqrt(lenSquare(v));
  851. }
  852. // jshint ignore:line
  853. /**
  854. * 向量长度平方
  855. * @param {Vector2} v
  856. * @return {number}
  857. */
  858. function lenSquare(v) {
  859. return v[0] * v[0] + v[1] * v[1];
  860. }
  861. /**
  862. * 向量乘法
  863. * @param {Vector2} out
  864. * @param {Vector2} v1
  865. * @param {Vector2} v2
  866. */
  867. /**
  868. * 向量除法
  869. * @param {Vector2} out
  870. * @param {Vector2} v1
  871. * @param {Vector2} v2
  872. */
  873. /**
  874. * 向量点乘
  875. * @param {Vector2} v1
  876. * @param {Vector2} v2
  877. * @return {number}
  878. */
  879. /**
  880. * 向量缩放
  881. * @param {Vector2} out
  882. * @param {Vector2} v
  883. * @param {number} s
  884. */
  885. function scale(out, v, s) {
  886. out[0] = v[0] * s;
  887. out[1] = v[1] * s;
  888. return out;
  889. }
  890. /**
  891. * 向量归一化
  892. * @param {Vector2} out
  893. * @param {Vector2} v
  894. */
  895. function normalize(out, v) {
  896. var d = len(v);
  897. if (d === 0) {
  898. out[0] = 0;
  899. out[1] = 0;
  900. }
  901. else {
  902. out[0] = v[0] / d;
  903. out[1] = v[1] / d;
  904. }
  905. return out;
  906. }
  907. /**
  908. * 计算向量间距离
  909. * @param {Vector2} v1
  910. * @param {Vector2} v2
  911. * @return {number}
  912. */
  913. function distance(v1, v2) {
  914. return Math.sqrt(
  915. (v1[0] - v2[0]) * (v1[0] - v2[0])
  916. + (v1[1] - v2[1]) * (v1[1] - v2[1])
  917. );
  918. }
  919. var dist = distance;
  920. /**
  921. * 向量距离平方
  922. * @param {Vector2} v1
  923. * @param {Vector2} v2
  924. * @return {number}
  925. */
  926. function distanceSquare(v1, v2) {
  927. return (v1[0] - v2[0]) * (v1[0] - v2[0])
  928. + (v1[1] - v2[1]) * (v1[1] - v2[1]);
  929. }
  930. var distSquare = distanceSquare;
  931. /**
  932. * 求负向量
  933. * @param {Vector2} out
  934. * @param {Vector2} v
  935. */
  936. /**
  937. * 插值两个点
  938. * @param {Vector2} out
  939. * @param {Vector2} v1
  940. * @param {Vector2} v2
  941. * @param {number} t
  942. */
  943. /**
  944. * 矩阵左乘向量
  945. * @param {Vector2} out
  946. * @param {Vector2} v
  947. * @param {Vector2} m
  948. */
  949. function applyTransform(out, v, m) {
  950. var x = v[0];
  951. var y = v[1];
  952. out[0] = m[0] * x + m[2] * y + m[4];
  953. out[1] = m[1] * x + m[3] * y + m[5];
  954. return out;
  955. }
  956. /**
  957. * 求两个向量最小值
  958. * @param {Vector2} out
  959. * @param {Vector2} v1
  960. * @param {Vector2} v2
  961. */
  962. function min(out, v1, v2) {
  963. out[0] = Math.min(v1[0], v2[0]);
  964. out[1] = Math.min(v1[1], v2[1]);
  965. return out;
  966. }
  967. /**
  968. * 求两个向量最大值
  969. * @param {Vector2} out
  970. * @param {Vector2} v1
  971. * @param {Vector2} v2
  972. */
  973. function max(out, v1, v2) {
  974. out[0] = Math.max(v1[0], v2[0]);
  975. out[1] = Math.max(v1[1], v2[1]);
  976. return out;
  977. }
  978. // TODO Draggable for group
  979. // FIXME Draggable on element which has parent rotation or scale
  980. function Draggable() {
  981. this.on('mousedown', this._dragStart, this);
  982. this.on('mousemove', this._drag, this);
  983. this.on('mouseup', this._dragEnd, this);
  984. this.on('globalout', this._dragEnd, this);
  985. // this._dropTarget = null;
  986. // this._draggingTarget = null;
  987. // this._x = 0;
  988. // this._y = 0;
  989. }
  990. Draggable.prototype = {
  991. constructor: Draggable,
  992. _dragStart: function (e) {
  993. var draggingTarget = e.target;
  994. if (draggingTarget && draggingTarget.draggable) {
  995. this._draggingTarget = draggingTarget;
  996. draggingTarget.dragging = true;
  997. this._x = e.offsetX;
  998. this._y = e.offsetY;
  999. this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);
  1000. }
  1001. },
  1002. _drag: function (e) {
  1003. var draggingTarget = this._draggingTarget;
  1004. if (draggingTarget) {
  1005. var x = e.offsetX;
  1006. var y = e.offsetY;
  1007. var dx = x - this._x;
  1008. var dy = y - this._y;
  1009. this._x = x;
  1010. this._y = y;
  1011. draggingTarget.drift(dx, dy, e);
  1012. this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);
  1013. var dropTarget = this.findHover(x, y, draggingTarget).target;
  1014. var lastDropTarget = this._dropTarget;
  1015. this._dropTarget = dropTarget;
  1016. if (draggingTarget !== dropTarget) {
  1017. if (lastDropTarget && dropTarget !== lastDropTarget) {
  1018. this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);
  1019. }
  1020. if (dropTarget && dropTarget !== lastDropTarget) {
  1021. this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);
  1022. }
  1023. }
  1024. }
  1025. },
  1026. _dragEnd: function (e) {
  1027. var draggingTarget = this._draggingTarget;
  1028. if (draggingTarget) {
  1029. draggingTarget.dragging = false;
  1030. }
  1031. this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);
  1032. if (this._dropTarget) {
  1033. this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);
  1034. }
  1035. this._draggingTarget = null;
  1036. this._dropTarget = null;
  1037. }
  1038. };
  1039. function param(target, e) {
  1040. return {target: target, topTarget: e && e.topTarget};
  1041. }
  1042. /**
  1043. * 事件扩展
  1044. * @module zrender/mixin/Eventful
  1045. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  1046. * pissang (https://www.github.com/pissang)
  1047. */
  1048. var arrySlice = Array.prototype.slice;
  1049. /**
  1050. * 事件分发器
  1051. * @alias module:zrender/mixin/Eventful
  1052. * @constructor
  1053. */
  1054. var Eventful = function () {
  1055. this._$handlers = {};
  1056. };
  1057. Eventful.prototype = {
  1058. constructor: Eventful,
  1059. /**
  1060. * 单次触发绑定,trigger后销毁
  1061. *
  1062. * @param {string} event 事件名
  1063. * @param {Function} handler 响应函数
  1064. * @param {Object} context
  1065. */
  1066. one: function (event, handler, context) {
  1067. var _h = this._$handlers;
  1068. if (!handler || !event) {
  1069. return this;
  1070. }
  1071. if (!_h[event]) {
  1072. _h[event] = [];
  1073. }
  1074. for (var i = 0; i < _h[event].length; i++) {
  1075. if (_h[event][i].h === handler) {
  1076. return this;
  1077. }
  1078. }
  1079. _h[event].push({
  1080. h: handler,
  1081. one: true,
  1082. ctx: context || this
  1083. });
  1084. return this;
  1085. },
  1086. /**
  1087. * 绑定事件
  1088. * @param {string} event 事件名
  1089. * @param {Function} handler 事件处理函数
  1090. * @param {Object} [context]
  1091. */
  1092. on: function (event, handler, context) {
  1093. var _h = this._$handlers;
  1094. if (!handler || !event) {
  1095. return this;
  1096. }
  1097. if (!_h[event]) {
  1098. _h[event] = [];
  1099. }
  1100. for (var i = 0; i < _h[event].length; i++) {
  1101. if (_h[event][i].h === handler) {
  1102. return this;
  1103. }
  1104. }
  1105. _h[event].push({
  1106. h: handler,
  1107. one: false,
  1108. ctx: context || this
  1109. });
  1110. return this;
  1111. },
  1112. /**
  1113. * 是否绑定了事件
  1114. * @param {string} event
  1115. * @return {boolean}
  1116. */
  1117. isSilent: function (event) {
  1118. var _h = this._$handlers;
  1119. return _h[event] && _h[event].length;
  1120. },
  1121. /**
  1122. * 解绑事件
  1123. * @param {string} event 事件名
  1124. * @param {Function} [handler] 事件处理函数
  1125. */
  1126. off: function (event, handler) {
  1127. var _h = this._$handlers;
  1128. if (!event) {
  1129. this._$handlers = {};
  1130. return this;
  1131. }
  1132. if (handler) {
  1133. if (_h[event]) {
  1134. var newList = [];
  1135. for (var i = 0, l = _h[event].length; i < l; i++) {
  1136. if (_h[event][i]['h'] != handler) {
  1137. newList.push(_h[event][i]);
  1138. }
  1139. }
  1140. _h[event] = newList;
  1141. }
  1142. if (_h[event] && _h[event].length === 0) {
  1143. delete _h[event];
  1144. }
  1145. }
  1146. else {
  1147. delete _h[event];
  1148. }
  1149. return this;
  1150. },
  1151. /**
  1152. * 事件分发
  1153. *
  1154. * @param {string} type 事件类型
  1155. */
  1156. trigger: function (type) {
  1157. if (this._$handlers[type]) {
  1158. var args = arguments;
  1159. var argLen = args.length;
  1160. if (argLen > 3) {
  1161. args = arrySlice.call(args, 1);
  1162. }
  1163. var _h = this._$handlers[type];
  1164. var len = _h.length;
  1165. for (var i = 0; i < len;) {
  1166. // Optimize advise from backbone
  1167. switch (argLen) {
  1168. case 1:
  1169. _h[i]['h'].call(_h[i]['ctx']);
  1170. break;
  1171. case 2:
  1172. _h[i]['h'].call(_h[i]['ctx'], args[1]);
  1173. break;
  1174. case 3:
  1175. _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]);
  1176. break;
  1177. default:
  1178. // have more than 2 given arguments
  1179. _h[i]['h'].apply(_h[i]['ctx'], args);
  1180. break;
  1181. }
  1182. if (_h[i]['one']) {
  1183. _h.splice(i, 1);
  1184. len--;
  1185. }
  1186. else {
  1187. i++;
  1188. }
  1189. }
  1190. }
  1191. return this;
  1192. },
  1193. /**
  1194. * 带有context的事件分发, 最后一个参数是事件回调的context
  1195. * @param {string} type 事件类型
  1196. */
  1197. triggerWithContext: function (type) {
  1198. if (this._$handlers[type]) {
  1199. var args = arguments;
  1200. var argLen = args.length;
  1201. if (argLen > 4) {
  1202. args = arrySlice.call(args, 1, args.length - 1);
  1203. }
  1204. var ctx = args[args.length - 1];
  1205. var _h = this._$handlers[type];
  1206. var len = _h.length;
  1207. for (var i = 0; i < len;) {
  1208. // Optimize advise from backbone
  1209. switch (argLen) {
  1210. case 1:
  1211. _h[i]['h'].call(ctx);
  1212. break;
  1213. case 2:
  1214. _h[i]['h'].call(ctx, args[1]);
  1215. break;
  1216. case 3:
  1217. _h[i]['h'].call(ctx, args[1], args[2]);
  1218. break;
  1219. default:
  1220. // have more than 2 given arguments
  1221. _h[i]['h'].apply(ctx, args);
  1222. break;
  1223. }
  1224. if (_h[i]['one']) {
  1225. _h.splice(i, 1);
  1226. len--;
  1227. }
  1228. else {
  1229. i++;
  1230. }
  1231. }
  1232. }
  1233. return this;
  1234. }
  1235. };
  1236. var SILENT = 'silent';
  1237. function makeEventPacket(eveType, targetInfo, event) {
  1238. return {
  1239. type: eveType,
  1240. event: event,
  1241. // target can only be an element that is not silent.
  1242. target: targetInfo.target,
  1243. // topTarget can be a silent element.
  1244. topTarget: targetInfo.topTarget,
  1245. cancelBubble: false,
  1246. offsetX: event.zrX,
  1247. offsetY: event.zrY,
  1248. gestureEvent: event.gestureEvent,
  1249. pinchX: event.pinchX,
  1250. pinchY: event.pinchY,
  1251. pinchScale: event.pinchScale,
  1252. wheelDelta: event.zrDelta,
  1253. zrByTouch: event.zrByTouch,
  1254. which: event.which
  1255. };
  1256. }
  1257. function EmptyProxy () {}
  1258. EmptyProxy.prototype.dispose = function () {};
  1259. var handlerNames = [
  1260. 'click', 'dblclick', 'mousewheel', 'mouseout',
  1261. 'mouseup', 'mousedown', 'mousemove', 'contextmenu'
  1262. ];
  1263. /**
  1264. * @alias module:zrender/Handler
  1265. * @constructor
  1266. * @extends module:zrender/mixin/Eventful
  1267. * @param {module:zrender/Storage} storage Storage instance.
  1268. * @param {module:zrender/Painter} painter Painter instance.
  1269. * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.
  1270. * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).
  1271. */
  1272. var Handler = function(storage, painter, proxy, painterRoot) {
  1273. Eventful.call(this);
  1274. this.storage = storage;
  1275. this.painter = painter;
  1276. this.painterRoot = painterRoot;
  1277. proxy = proxy || new EmptyProxy();
  1278. /**
  1279. * Proxy of event. can be Dom, WebGLSurface, etc.
  1280. */
  1281. this.proxy = null;
  1282. /**
  1283. * {target, topTarget, x, y}
  1284. * @private
  1285. * @type {Object}
  1286. */
  1287. this._hovered = {};
  1288. /**
  1289. * @private
  1290. * @type {Date}
  1291. */
  1292. this._lastTouchMoment;
  1293. /**
  1294. * @private
  1295. * @type {number}
  1296. */
  1297. this._lastX;
  1298. /**
  1299. * @private
  1300. * @type {number}
  1301. */
  1302. this._lastY;
  1303. Draggable.call(this);
  1304. this.setHandlerProxy(proxy);
  1305. };
  1306. Handler.prototype = {
  1307. constructor: Handler,
  1308. setHandlerProxy: function (proxy) {
  1309. if (this.proxy) {
  1310. this.proxy.dispose();
  1311. }
  1312. if (proxy) {
  1313. each$1(handlerNames, function (name) {
  1314. proxy.on && proxy.on(name, this[name], this);
  1315. }, this);
  1316. // Attach handler
  1317. proxy.handler = this;
  1318. }
  1319. this.proxy = proxy;
  1320. },
  1321. mousemove: function (event) {
  1322. var x = event.zrX;
  1323. var y = event.zrY;
  1324. var lastHovered = this._hovered;
  1325. var lastHoveredTarget = lastHovered.target;
  1326. // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call
  1327. // (like 'setOption' or 'dispatchAction') in event handlers, we should find
  1328. // lastHovered again here. Otherwise 'mouseout' can not be triggered normally.
  1329. // See #6198.
  1330. if (lastHoveredTarget && !lastHoveredTarget.__zr) {
  1331. lastHovered = this.findHover(lastHovered.x, lastHovered.y);
  1332. lastHoveredTarget = lastHovered.target;
  1333. }
  1334. var hovered = this._hovered = this.findHover(x, y);
  1335. var hoveredTarget = hovered.target;
  1336. var proxy = this.proxy;
  1337. proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');
  1338. // Mouse out on previous hovered element
  1339. if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1340. this.dispatchToElement(lastHovered, 'mouseout', event);
  1341. }
  1342. // Mouse moving on one element
  1343. this.dispatchToElement(hovered, 'mousemove', event);
  1344. // Mouse over on a new element
  1345. if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1346. this.dispatchToElement(hovered, 'mouseover', event);
  1347. }
  1348. },
  1349. mouseout: function (event) {
  1350. this.dispatchToElement(this._hovered, 'mouseout', event);
  1351. // There might be some doms created by upper layer application
  1352. // at the same level of painter.getViewportRoot() (e.g., tooltip
  1353. // dom created by echarts), where 'globalout' event should not
  1354. // be triggered when mouse enters these doms. (But 'mouseout'
  1355. // should be triggered at the original hovered element as usual).
  1356. var element = event.toElement || event.relatedTarget;
  1357. var innerDom;
  1358. do {
  1359. element = element && element.parentNode;
  1360. }
  1361. while (element && element.nodeType != 9 && !(
  1362. innerDom = element === this.painterRoot
  1363. ));
  1364. !innerDom && this.trigger('globalout', {event: event});
  1365. },
  1366. /**
  1367. * Resize
  1368. */
  1369. resize: function (event) {
  1370. this._hovered = {};
  1371. },
  1372. /**
  1373. * Dispatch event
  1374. * @param {string} eventName
  1375. * @param {event=} eventArgs
  1376. */
  1377. dispatch: function (eventName, eventArgs) {
  1378. var handler = this[eventName];
  1379. handler && handler.call(this, eventArgs);
  1380. },
  1381. /**
  1382. * Dispose
  1383. */
  1384. dispose: function () {
  1385. this.proxy.dispose();
  1386. this.storage =
  1387. this.proxy =
  1388. this.painter = null;
  1389. },
  1390. /**
  1391. * 设置默认的cursor style
  1392. * @param {string} [cursorStyle='default'] 例如 crosshair
  1393. */
  1394. setCursorStyle: function (cursorStyle) {
  1395. var proxy = this.proxy;
  1396. proxy.setCursor && proxy.setCursor(cursorStyle);
  1397. },
  1398. /**
  1399. * 事件分发代理
  1400. *
  1401. * @private
  1402. * @param {Object} targetInfo {target, topTarget} 目标图形元素
  1403. * @param {string} eventName 事件名称
  1404. * @param {Object} event 事件对象
  1405. */
  1406. dispatchToElement: function (targetInfo, eventName, event) {
  1407. targetInfo = targetInfo || {};
  1408. var el = targetInfo.target;
  1409. if (el && el.silent) {
  1410. return;
  1411. }
  1412. var eventHandler = 'on' + eventName;
  1413. var eventPacket = makeEventPacket(eventName, targetInfo, event);
  1414. while (el) {
  1415. el[eventHandler]
  1416. && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket));
  1417. el.trigger(eventName, eventPacket);
  1418. el = el.parent;
  1419. if (eventPacket.cancelBubble) {
  1420. break;
  1421. }
  1422. }
  1423. if (!eventPacket.cancelBubble) {
  1424. // 冒泡到顶级 zrender 对象
  1425. this.trigger(eventName, eventPacket);
  1426. // 分发事件到用户自定义层
  1427. // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在
  1428. this.painter && this.painter.eachOtherLayer(function (layer) {
  1429. if (typeof(layer[eventHandler]) == 'function') {
  1430. layer[eventHandler].call(layer, eventPacket);
  1431. }
  1432. if (layer.trigger) {
  1433. layer.trigger(eventName, eventPacket);
  1434. }
  1435. });
  1436. }
  1437. },
  1438. /**
  1439. * @private
  1440. * @param {number} x
  1441. * @param {number} y
  1442. * @param {module:zrender/graphic/Displayable} exclude
  1443. * @return {model:zrender/Element}
  1444. * @method
  1445. */
  1446. findHover: function(x, y, exclude) {
  1447. var list = this.storage.getDisplayList();
  1448. var out = {x: x, y: y};
  1449. for (var i = list.length - 1; i >= 0 ; i--) {
  1450. var hoverCheckResult;
  1451. if (list[i] !== exclude
  1452. // getDisplayList may include ignored item in VML mode
  1453. && !list[i].ignore
  1454. && (hoverCheckResult = isHover(list[i], x, y))
  1455. ) {
  1456. !out.topTarget && (out.topTarget = list[i]);
  1457. if (hoverCheckResult !== SILENT) {
  1458. out.target = list[i];
  1459. break;
  1460. }
  1461. }
  1462. }
  1463. return out;
  1464. }
  1465. };
  1466. // Common handlers
  1467. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  1468. Handler.prototype[name] = function (event) {
  1469. // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover
  1470. var hovered = this.findHover(event.zrX, event.zrY);
  1471. var hoveredTarget = hovered.target;
  1472. if (name === 'mousedown') {
  1473. this._downEl = hoveredTarget;
  1474. this._downPoint = [event.zrX, event.zrY];
  1475. // In case click triggered before mouseup
  1476. this._upEl = hoveredTarget;
  1477. }
  1478. else if (name === 'mouseup') {
  1479. this._upEl = hoveredTarget;
  1480. }
  1481. else if (name === 'click') {
  1482. if (this._downEl !== this._upEl
  1483. // Original click event is triggered on the whole canvas element,
  1484. // including the case that `mousedown` - `mousemove` - `mouseup`,
  1485. // which should be filtered, otherwise it will bring trouble to
  1486. // pan and zoom.
  1487. || !this._downPoint
  1488. // Arbitrary value
  1489. || dist(this._downPoint, [event.zrX, event.zrY]) > 4
  1490. ) {
  1491. return;
  1492. }
  1493. this._downPoint = null;
  1494. }
  1495. this.dispatchToElement(hovered, name, event);
  1496. };
  1497. });
  1498. function isHover(displayable, x, y) {
  1499. if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {
  1500. var el = displayable;
  1501. var isSilent;
  1502. while (el) {
  1503. // If clipped by ancestor.
  1504. // FIXME: If clipPath has neither stroke nor fill,
  1505. // el.clipPath.contain(x, y) will always return false.
  1506. if (el.clipPath && !el.clipPath.contain(x, y)) {
  1507. return false;
  1508. }
  1509. if (el.silent) {
  1510. isSilent = true;
  1511. }
  1512. el = el.parent;
  1513. }
  1514. return isSilent ? SILENT : true;
  1515. }
  1516. return false;
  1517. }
  1518. mixin(Handler, Eventful);
  1519. mixin(Handler, Draggable);
  1520. /**
  1521. * 3x2矩阵操作类
  1522. * @exports zrender/tool/matrix
  1523. */
  1524. var ArrayCtor$1 = typeof Float32Array === 'undefined'
  1525. ? Array
  1526. : Float32Array;
  1527. /**
  1528. * Create a identity matrix.
  1529. * @return {Float32Array|Array.<number>}
  1530. */
  1531. function create$1() {
  1532. var out = new ArrayCtor$1(6);
  1533. identity(out);
  1534. return out;
  1535. }
  1536. /**
  1537. * 设置矩阵为单位矩阵
  1538. * @param {Float32Array|Array.<number>} out
  1539. */
  1540. function identity(out) {
  1541. out[0] = 1;
  1542. out[1] = 0;
  1543. out[2] = 0;
  1544. out[3] = 1;
  1545. out[4] = 0;
  1546. out[5] = 0;
  1547. return out;
  1548. }
  1549. /**
  1550. * 复制矩阵
  1551. * @param {Float32Array|Array.<number>} out
  1552. * @param {Float32Array|Array.<number>} m
  1553. */
  1554. function copy$1(out, m) {
  1555. out[0] = m[0];
  1556. out[1] = m[1];
  1557. out[2] = m[2];
  1558. out[3] = m[3];
  1559. out[4] = m[4];
  1560. out[5] = m[5];
  1561. return out;
  1562. }
  1563. /**
  1564. * 矩阵相乘
  1565. * @param {Float32Array|Array.<number>} out
  1566. * @param {Float32Array|Array.<number>} m1
  1567. * @param {Float32Array|Array.<number>} m2
  1568. */
  1569. function mul$1(out, m1, m2) {
  1570. // Consider matrix.mul(m, m2, m);
  1571. // where out is the same as m2.
  1572. // So use temp variable to escape error.
  1573. var out0 = m1[0] * m2[0] + m1[2] * m2[1];
  1574. var out1 = m1[1] * m2[0] + m1[3] * m2[1];
  1575. var out2 = m1[0] * m2[2] + m1[2] * m2[3];
  1576. var out3 = m1[1] * m2[2] + m1[3] * m2[3];
  1577. var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];
  1578. var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];
  1579. out[0] = out0;
  1580. out[1] = out1;
  1581. out[2] = out2;
  1582. out[3] = out3;
  1583. out[4] = out4;
  1584. out[5] = out5;
  1585. return out;
  1586. }
  1587. /**
  1588. * 平移变换
  1589. * @param {Float32Array|Array.<number>} out
  1590. * @param {Float32Array|Array.<number>} a
  1591. * @param {Float32Array|Array.<number>} v
  1592. */
  1593. function translate(out, a, v) {
  1594. out[0] = a[0];
  1595. out[1] = a[1];
  1596. out[2] = a[2];
  1597. out[3] = a[3];
  1598. out[4] = a[4] + v[0];
  1599. out[5] = a[5] + v[1];
  1600. return out;
  1601. }
  1602. /**
  1603. * 旋转变换
  1604. * @param {Float32Array|Array.<number>} out
  1605. * @param {Float32Array|Array.<number>} a
  1606. * @param {number} rad
  1607. */
  1608. function rotate(out, a, rad) {
  1609. var aa = a[0];
  1610. var ac = a[2];
  1611. var atx = a[4];
  1612. var ab = a[1];
  1613. var ad = a[3];
  1614. var aty = a[5];
  1615. var st = Math.sin(rad);
  1616. var ct = Math.cos(rad);
  1617. out[0] = aa * ct + ab * st;
  1618. out[1] = -aa * st + ab * ct;
  1619. out[2] = ac * ct + ad * st;
  1620. out[3] = -ac * st + ct * ad;
  1621. out[4] = ct * atx + st * aty;
  1622. out[5] = ct * aty - st * atx;
  1623. return out;
  1624. }
  1625. /**
  1626. * 缩放变换
  1627. * @param {Float32Array|Array.<number>} out
  1628. * @param {Float32Array|Array.<number>} a
  1629. * @param {Float32Array|Array.<number>} v
  1630. */
  1631. function scale$1(out, a, v) {
  1632. var vx = v[0];
  1633. var vy = v[1];
  1634. out[0] = a[0] * vx;
  1635. out[1] = a[1] * vy;
  1636. out[2] = a[2] * vx;
  1637. out[3] = a[3] * vy;
  1638. out[4] = a[4] * vx;
  1639. out[5] = a[5] * vy;
  1640. return out;
  1641. }
  1642. /**
  1643. * 求逆矩阵
  1644. * @param {Float32Array|Array.<number>} out
  1645. * @param {Float32Array|Array.<number>} a
  1646. */
  1647. function invert(out, a) {
  1648. var aa = a[0];
  1649. var ac = a[2];
  1650. var atx = a[4];
  1651. var ab = a[1];
  1652. var ad = a[3];
  1653. var aty = a[5];
  1654. var det = aa * ad - ab * ac;
  1655. if (!det) {
  1656. return null;
  1657. }
  1658. det = 1.0 / det;
  1659. out[0] = ad * det;
  1660. out[1] = -ab * det;
  1661. out[2] = -ac * det;
  1662. out[3] = aa * det;
  1663. out[4] = (ac * aty - ad * atx) * det;
  1664. out[5] = (ab * atx - aa * aty) * det;
  1665. return out;
  1666. }
  1667. /**
  1668. * Clone a new matrix.
  1669. * @param {Float32Array|Array.<number>} a
  1670. */
  1671. /**
  1672. * 提供变换扩展
  1673. * @module zrender/mixin/Transformable
  1674. * @author pissang (https://www.github.com/pissang)
  1675. */
  1676. var mIdentity = identity;
  1677. var EPSILON = 5e-5;
  1678. function isNotAroundZero(val) {
  1679. return val > EPSILON || val < -EPSILON;
  1680. }
  1681. /**
  1682. * @alias module:zrender/mixin/Transformable
  1683. * @constructor
  1684. */
  1685. var Transformable = function (opts) {
  1686. opts = opts || {};
  1687. // If there are no given position, rotation, scale
  1688. if (!opts.position) {
  1689. /**
  1690. * 平移
  1691. * @type {Array.<number>}
  1692. * @default [0, 0]
  1693. */
  1694. this.position = [0, 0];
  1695. }
  1696. if (opts.rotation == null) {
  1697. /**
  1698. * 旋转
  1699. * @type {Array.<number>}
  1700. * @default 0
  1701. */
  1702. this.rotation = 0;
  1703. }
  1704. if (!opts.scale) {
  1705. /**
  1706. * 缩放
  1707. * @type {Array.<number>}
  1708. * @default [1, 1]
  1709. */
  1710. this.scale = [1, 1];
  1711. }
  1712. /**
  1713. * 旋转和缩放的原点
  1714. * @type {Array.<number>}
  1715. * @default null
  1716. */
  1717. this.origin = this.origin || null;
  1718. };
  1719. var transformableProto = Transformable.prototype;
  1720. transformableProto.transform = null;
  1721. /**
  1722. * 判断是否需要有坐标变换
  1723. * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵
  1724. */
  1725. transformableProto.needLocalTransform = function () {
  1726. return isNotAroundZero(this.rotation)
  1727. || isNotAroundZero(this.position[0])
  1728. || isNotAroundZero(this.position[1])
  1729. || isNotAroundZero(this.scale[0] - 1)
  1730. || isNotAroundZero(this.scale[1] - 1);
  1731. };
  1732. transformableProto.updateTransform = function () {
  1733. var parent = this.parent;
  1734. var parentHasTransform = parent && parent.transform;
  1735. var needLocalTransform = this.needLocalTransform();
  1736. var m = this.transform;
  1737. if (!(needLocalTransform || parentHasTransform)) {
  1738. m && mIdentity(m);
  1739. return;
  1740. }
  1741. m = m || create$1();
  1742. if (needLocalTransform) {
  1743. this.getLocalTransform(m);
  1744. }
  1745. else {
  1746. mIdentity(m);
  1747. }
  1748. // 应用父节点变换
  1749. if (parentHasTransform) {
  1750. if (needLocalTransform) {
  1751. mul$1(m, parent.transform, m);
  1752. }
  1753. else {
  1754. copy$1(m, parent.transform);
  1755. }
  1756. }
  1757. // 保存这个变换矩阵
  1758. this.transform = m;
  1759. this.invTransform = this.invTransform || create$1();
  1760. invert(this.invTransform, m);
  1761. };
  1762. transformableProto.getLocalTransform = function (m) {
  1763. return Transformable.getLocalTransform(this, m);
  1764. };
  1765. /**
  1766. * 将自己的transform应用到context上
  1767. * @param {CanvasRenderingContext2D} ctx
  1768. */
  1769. transformableProto.setTransform = function (ctx) {
  1770. var m = this.transform;
  1771. var dpr = ctx.dpr || 1;
  1772. if (m) {
  1773. ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
  1774. }
  1775. else {
  1776. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  1777. }
  1778. };
  1779. transformableProto.restoreTransform = function (ctx) {
  1780. var dpr = ctx.dpr || 1;
  1781. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  1782. };
  1783. var tmpTransform = [];
  1784. /**
  1785. * 分解`transform`矩阵到`position`, `rotation`, `scale`
  1786. */
  1787. transformableProto.decomposeTransform = function () {
  1788. if (!this.transform) {
  1789. return;
  1790. }
  1791. var parent = this.parent;
  1792. var m = this.transform;
  1793. if (parent && parent.transform) {
  1794. // Get local transform and decompose them to position, scale, rotation
  1795. mul$1(tmpTransform, parent.invTransform, m);
  1796. m = tmpTransform;
  1797. }
  1798. var sx = m[0] * m[0] + m[1] * m[1];
  1799. var sy = m[2] * m[2] + m[3] * m[3];
  1800. var position = this.position;
  1801. var scale$$1 = this.scale;
  1802. if (isNotAroundZero(sx - 1)) {
  1803. sx = Math.sqrt(sx);
  1804. }
  1805. if (isNotAroundZero(sy - 1)) {
  1806. sy = Math.sqrt(sy);
  1807. }
  1808. if (m[0] < 0) {
  1809. sx = -sx;
  1810. }
  1811. if (m[3] < 0) {
  1812. sy = -sy;
  1813. }
  1814. position[0] = m[4];
  1815. position[1] = m[5];
  1816. scale$$1[0] = sx;
  1817. scale$$1[1] = sy;
  1818. this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
  1819. };
  1820. /**
  1821. * Get global scale
  1822. * @return {Array.<number>}
  1823. */
  1824. transformableProto.getGlobalScale = function () {
  1825. var m = this.transform;
  1826. if (!m) {
  1827. return [1, 1];
  1828. }
  1829. var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
  1830. var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
  1831. if (m[0] < 0) {
  1832. sx = -sx;
  1833. }
  1834. if (m[3] < 0) {
  1835. sy = -sy;
  1836. }
  1837. return [sx, sy];
  1838. };
  1839. /**
  1840. * 变换坐标位置到 shape 的局部坐标空间
  1841. * @method
  1842. * @param {number} x
  1843. * @param {number} y
  1844. * @return {Array.<number>}
  1845. */
  1846. transformableProto.transformCoordToLocal = function (x, y) {
  1847. var v2 = [x, y];
  1848. var invTransform = this.invTransform;
  1849. if (invTransform) {
  1850. applyTransform(v2, v2, invTransform);
  1851. }
  1852. return v2;
  1853. };
  1854. /**
  1855. * 变换局部坐标位置到全局坐标空间
  1856. * @method
  1857. * @param {number} x
  1858. * @param {number} y
  1859. * @return {Array.<number>}
  1860. */
  1861. transformableProto.transformCoordToGlobal = function (x, y) {
  1862. var v2 = [x, y];
  1863. var transform = this.transform;
  1864. if (transform) {
  1865. applyTransform(v2, v2, transform);
  1866. }
  1867. return v2;
  1868. };
  1869. /**
  1870. * @static
  1871. * @param {Object} target
  1872. * @param {Array.<number>} target.origin
  1873. * @param {number} target.rotation
  1874. * @param {Array.<number>} target.position
  1875. * @param {Array.<number>} [m]
  1876. */
  1877. Transformable.getLocalTransform = function (target, m) {
  1878. m = m || [];
  1879. mIdentity(m);
  1880. var origin = target.origin;
  1881. var scale$$1 = target.scale || [1, 1];
  1882. var rotation = target.rotation || 0;
  1883. var position = target.position || [0, 0];
  1884. if (origin) {
  1885. // Translate to origin
  1886. m[4] -= origin[0];
  1887. m[5] -= origin[1];
  1888. }
  1889. scale$1(m, m, scale$$1);
  1890. if (rotation) {
  1891. rotate(m, m, rotation);
  1892. }
  1893. if (origin) {
  1894. // Translate back from origin
  1895. m[4] += origin[0];
  1896. m[5] += origin[1];
  1897. }
  1898. m[4] += position[0];
  1899. m[5] += position[1];
  1900. return m;
  1901. };
  1902. /**
  1903. * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js
  1904. * @see http://sole.github.io/tween.js/examples/03_graphs.html
  1905. * @exports zrender/animation/easing
  1906. */
  1907. var easing = {
  1908. /**
  1909. * @param {number} k
  1910. * @return {number}
  1911. */
  1912. linear: function (k) {
  1913. return k;
  1914. },
  1915. /**
  1916. * @param {number} k
  1917. * @return {number}
  1918. */
  1919. quadraticIn: function (k) {
  1920. return k * k;
  1921. },
  1922. /**
  1923. * @param {number} k
  1924. * @return {number}
  1925. */
  1926. quadraticOut: function (k) {
  1927. return k * (2 - k);
  1928. },
  1929. /**
  1930. * @param {number} k
  1931. * @return {number}
  1932. */
  1933. quadraticInOut: function (k) {
  1934. if ((k *= 2) < 1) {
  1935. return 0.5 * k * k;
  1936. }
  1937. return -0.5 * (--k * (k - 2) - 1);
  1938. },
  1939. // 三次方的缓动(t^3)
  1940. /**
  1941. * @param {number} k
  1942. * @return {number}
  1943. */
  1944. cubicIn: function (k) {
  1945. return k * k * k;
  1946. },
  1947. /**
  1948. * @param {number} k
  1949. * @return {number}
  1950. */
  1951. cubicOut: function (k) {
  1952. return --k * k * k + 1;
  1953. },
  1954. /**
  1955. * @param {number} k
  1956. * @return {number}
  1957. */
  1958. cubicInOut: function (k) {
  1959. if ((k *= 2) < 1) {
  1960. return 0.5 * k * k * k;
  1961. }
  1962. return 0.5 * ((k -= 2) * k * k + 2);
  1963. },
  1964. // 四次方的缓动(t^4)
  1965. /**
  1966. * @param {number} k
  1967. * @return {number}
  1968. */
  1969. quarticIn: function (k) {
  1970. return k * k * k * k;
  1971. },
  1972. /**
  1973. * @param {number} k
  1974. * @return {number}
  1975. */
  1976. quarticOut: function (k) {
  1977. return 1 - (--k * k * k * k);
  1978. },
  1979. /**
  1980. * @param {number} k
  1981. * @return {number}
  1982. */
  1983. quarticInOut: function (k) {
  1984. if ((k *= 2) < 1) {
  1985. return 0.5 * k * k * k * k;
  1986. }
  1987. return -0.5 * ((k -= 2) * k * k * k - 2);
  1988. },
  1989. // 五次方的缓动(t^5)
  1990. /**
  1991. * @param {number} k
  1992. * @return {number}
  1993. */
  1994. quinticIn: function (k) {
  1995. return k * k * k * k * k;
  1996. },
  1997. /**
  1998. * @param {number} k
  1999. * @return {number}
  2000. */
  2001. quinticOut: function (k) {
  2002. return --k * k * k * k * k + 1;
  2003. },
  2004. /**
  2005. * @param {number} k
  2006. * @return {number}
  2007. */
  2008. quinticInOut: function (k) {
  2009. if ((k *= 2) < 1) {
  2010. return 0.5 * k * k * k * k * k;
  2011. }
  2012. return 0.5 * ((k -= 2) * k * k * k * k + 2);
  2013. },
  2014. // 正弦曲线的缓动(sin(t))
  2015. /**
  2016. * @param {number} k
  2017. * @return {number}
  2018. */
  2019. sinusoidalIn: function (k) {
  2020. return 1 - Math.cos(k * Math.PI / 2);
  2021. },
  2022. /**
  2023. * @param {number} k
  2024. * @return {number}
  2025. */
  2026. sinusoidalOut: function (k) {
  2027. return Math.sin(k * Math.PI / 2);
  2028. },
  2029. /**
  2030. * @param {number} k
  2031. * @return {number}
  2032. */
  2033. sinusoidalInOut: function (k) {
  2034. return 0.5 * (1 - Math.cos(Math.PI * k));
  2035. },
  2036. // 指数曲线的缓动(2^t)
  2037. /**
  2038. * @param {number} k
  2039. * @return {number}
  2040. */
  2041. exponentialIn: function (k) {
  2042. return k === 0 ? 0 : Math.pow(1024, k - 1);
  2043. },
  2044. /**
  2045. * @param {number} k
  2046. * @return {number}
  2047. */
  2048. exponentialOut: function (k) {
  2049. return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
  2050. },
  2051. /**
  2052. * @param {number} k
  2053. * @return {number}
  2054. */
  2055. exponentialInOut: function (k) {
  2056. if (k === 0) {
  2057. return 0;
  2058. }
  2059. if (k === 1) {
  2060. return 1;
  2061. }
  2062. if ((k *= 2) < 1) {
  2063. return 0.5 * Math.pow(1024, k - 1);
  2064. }
  2065. return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
  2066. },
  2067. // 圆形曲线的缓动(sqrt(1-t^2))
  2068. /**
  2069. * @param {number} k
  2070. * @return {number}
  2071. */
  2072. circularIn: function (k) {
  2073. return 1 - Math.sqrt(1 - k * k);
  2074. },
  2075. /**
  2076. * @param {number} k
  2077. * @return {number}
  2078. */
  2079. circularOut: function (k) {
  2080. return Math.sqrt(1 - (--k * k));
  2081. },
  2082. /**
  2083. * @param {number} k
  2084. * @return {number}
  2085. */
  2086. circularInOut: function (k) {
  2087. if ((k *= 2) < 1) {
  2088. return -0.5 * (Math.sqrt(1 - k * k) - 1);
  2089. }
  2090. return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
  2091. },
  2092. // 创建类似于弹簧在停止前来回振荡的动画
  2093. /**
  2094. * @param {number} k
  2095. * @return {number}
  2096. */
  2097. elasticIn: function (k) {
  2098. var s;
  2099. var a = 0.1;
  2100. var p = 0.4;
  2101. if (k === 0) {
  2102. return 0;
  2103. }
  2104. if (k === 1) {
  2105. return 1;
  2106. }
  2107. if (!a || a < 1) {
  2108. a = 1; s = p / 4;
  2109. }
  2110. else {
  2111. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2112. }
  2113. return -(a * Math.pow(2, 10 * (k -= 1)) *
  2114. Math.sin((k - s) * (2 * Math.PI) / p));
  2115. },
  2116. /**
  2117. * @param {number} k
  2118. * @return {number}
  2119. */
  2120. elasticOut: function (k) {
  2121. var s;
  2122. var a = 0.1;
  2123. var p = 0.4;
  2124. if (k === 0) {
  2125. return 0;
  2126. }
  2127. if (k === 1) {
  2128. return 1;
  2129. }
  2130. if (!a || a < 1) {
  2131. a = 1; s = p / 4;
  2132. }
  2133. else {
  2134. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2135. }
  2136. return (a * Math.pow(2, -10 * k) *
  2137. Math.sin((k - s) * (2 * Math.PI) / p) + 1);
  2138. },
  2139. /**
  2140. * @param {number} k
  2141. * @return {number}
  2142. */
  2143. elasticInOut: function (k) {
  2144. var s;
  2145. var a = 0.1;
  2146. var p = 0.4;
  2147. if (k === 0) {
  2148. return 0;
  2149. }
  2150. if (k === 1) {
  2151. return 1;
  2152. }
  2153. if (!a || a < 1) {
  2154. a = 1; s = p / 4;
  2155. }
  2156. else {
  2157. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2158. }
  2159. if ((k *= 2) < 1) {
  2160. return -0.5 * (a * Math.pow(2, 10 * (k -= 1))
  2161. * Math.sin((k - s) * (2 * Math.PI) / p));
  2162. }
  2163. return a * Math.pow(2, -10 * (k -= 1))
  2164. * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;
  2165. },
  2166. // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动
  2167. /**
  2168. * @param {number} k
  2169. * @return {number}
  2170. */
  2171. backIn: function (k) {
  2172. var s = 1.70158;
  2173. return k * k * ((s + 1) * k - s);
  2174. },
  2175. /**
  2176. * @param {number} k
  2177. * @return {number}
  2178. */
  2179. backOut: function (k) {
  2180. var s = 1.70158;
  2181. return --k * k * ((s + 1) * k + s) + 1;
  2182. },
  2183. /**
  2184. * @param {number} k
  2185. * @return {number}
  2186. */
  2187. backInOut: function (k) {
  2188. var s = 1.70158 * 1.525;
  2189. if ((k *= 2) < 1) {
  2190. return 0.5 * (k * k * ((s + 1) * k - s));
  2191. }
  2192. return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
  2193. },
  2194. // 创建弹跳效果
  2195. /**
  2196. * @param {number} k
  2197. * @return {number}
  2198. */
  2199. bounceIn: function (k) {
  2200. return 1 - easing.bounceOut(1 - k);
  2201. },
  2202. /**
  2203. * @param {number} k
  2204. * @return {number}
  2205. */
  2206. bounceOut: function (k) {
  2207. if (k < (1 / 2.75)) {
  2208. return 7.5625 * k * k;
  2209. }
  2210. else if (k < (2 / 2.75)) {
  2211. return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
  2212. }
  2213. else if (k < (2.5 / 2.75)) {
  2214. return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
  2215. }
  2216. else {
  2217. return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
  2218. }
  2219. },
  2220. /**
  2221. * @param {number} k
  2222. * @return {number}
  2223. */
  2224. bounceInOut: function (k) {
  2225. if (k < 0.5) {
  2226. return easing.bounceIn(k * 2) * 0.5;
  2227. }
  2228. return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
  2229. }
  2230. };
  2231. /**
  2232. * 动画主控制器
  2233. * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件
  2234. * @config life(1000) 动画时长
  2235. * @config delay(0) 动画延迟时间
  2236. * @config loop(true)
  2237. * @config gap(0) 循环的间隔时间
  2238. * @config onframe
  2239. * @config easing(optional)
  2240. * @config ondestroy(optional)
  2241. * @config onrestart(optional)
  2242. *
  2243. * TODO pause
  2244. */
  2245. function Clip(options) {
  2246. this._target = options.target;
  2247. // 生命周期
  2248. this._life = options.life || 1000;
  2249. // 延时
  2250. this._delay = options.delay || 0;
  2251. // 开始时间
  2252. // this._startTime = new Date().getTime() + this._delay;// 单位毫秒
  2253. this._initialized = false;
  2254. // 是否循环
  2255. this.loop = options.loop == null ? false : options.loop;
  2256. this.gap = options.gap || 0;
  2257. this.easing = options.easing || 'Linear';
  2258. this.onframe = options.onframe;
  2259. this.ondestroy = options.ondestroy;
  2260. this.onrestart = options.onrestart;
  2261. this._pausedTime = 0;
  2262. this._paused = false;
  2263. }
  2264. Clip.prototype = {
  2265. constructor: Clip,
  2266. step: function (globalTime, deltaTime) {
  2267. // Set startTime on first step, or _startTime may has milleseconds different between clips
  2268. // PENDING
  2269. if (!this._initialized) {
  2270. this._startTime = globalTime + this._delay;
  2271. this._initialized = true;
  2272. }
  2273. if (this._paused) {
  2274. this._pausedTime += deltaTime;
  2275. return;
  2276. }
  2277. var percent = (globalTime - this._startTime - this._pausedTime) / this._life;
  2278. // 还没开始
  2279. if (percent < 0) {
  2280. return;
  2281. }
  2282. percent = Math.min(percent, 1);
  2283. var easing$$1 = this.easing;
  2284. var easingFunc = typeof easing$$1 == 'string' ? easing[easing$$1] : easing$$1;
  2285. var schedule = typeof easingFunc === 'function'
  2286. ? easingFunc(percent)
  2287. : percent;
  2288. this.fire('frame', schedule);
  2289. // 结束
  2290. if (percent == 1) {
  2291. if (this.loop) {
  2292. this.restart (globalTime);
  2293. // 重新开始周期
  2294. // 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件
  2295. return 'restart';
  2296. }
  2297. // 动画完成将这个控制器标识为待删除
  2298. // 在Animation.update中进行批量删除
  2299. this._needsRemove = true;
  2300. return 'destroy';
  2301. }
  2302. return null;
  2303. },
  2304. restart: function (globalTime) {
  2305. var remainder = (globalTime - this._startTime - this._pausedTime) % this._life;
  2306. this._startTime = globalTime - remainder + this.gap;
  2307. this._pausedTime = 0;
  2308. this._needsRemove = false;
  2309. },
  2310. fire: function (eventType, arg) {
  2311. eventType = 'on' + eventType;
  2312. if (this[eventType]) {
  2313. this[eventType](this._target, arg);
  2314. }
  2315. },
  2316. pause: function () {
  2317. this._paused = true;
  2318. },
  2319. resume: function () {
  2320. this._paused = false;
  2321. }
  2322. };
  2323. // Simple LRU cache use doubly linked list
  2324. // @module zrender/core/LRU
  2325. /**
  2326. * Simple double linked list. Compared with array, it has O(1) remove operation.
  2327. * @constructor
  2328. */
  2329. var LinkedList = function () {
  2330. /**
  2331. * @type {module:zrender/core/LRU~Entry}
  2332. */
  2333. this.head = null;
  2334. /**
  2335. * @type {module:zrender/core/LRU~Entry}
  2336. */
  2337. this.tail = null;
  2338. this._len = 0;
  2339. };
  2340. var linkedListProto = LinkedList.prototype;
  2341. /**
  2342. * Insert a new value at the tail
  2343. * @param {} val
  2344. * @return {module:zrender/core/LRU~Entry}
  2345. */
  2346. linkedListProto.insert = function (val) {
  2347. var entry = new Entry(val);
  2348. this.insertEntry(entry);
  2349. return entry;
  2350. };
  2351. /**
  2352. * Insert an entry at the tail
  2353. * @param {module:zrender/core/LRU~Entry} entry
  2354. */
  2355. linkedListProto.insertEntry = function (entry) {
  2356. if (!this.head) {
  2357. this.head = this.tail = entry;
  2358. }
  2359. else {
  2360. this.tail.next = entry;
  2361. entry.prev = this.tail;
  2362. entry.next = null;
  2363. this.tail = entry;
  2364. }
  2365. this._len++;
  2366. };
  2367. /**
  2368. * Remove entry.
  2369. * @param {module:zrender/core/LRU~Entry} entry
  2370. */
  2371. linkedListProto.remove = function (entry) {
  2372. var prev = entry.prev;
  2373. var next = entry.next;
  2374. if (prev) {
  2375. prev.next = next;
  2376. }
  2377. else {
  2378. // Is head
  2379. this.head = next;
  2380. }
  2381. if (next) {
  2382. next.prev = prev;
  2383. }
  2384. else {
  2385. // Is tail
  2386. this.tail = prev;
  2387. }
  2388. entry.next = entry.prev = null;
  2389. this._len--;
  2390. };
  2391. /**
  2392. * @return {number}
  2393. */
  2394. linkedListProto.len = function () {
  2395. return this._len;
  2396. };
  2397. /**
  2398. * Clear list
  2399. */
  2400. linkedListProto.clear = function () {
  2401. this.head = this.tail = null;
  2402. this._len = 0;
  2403. };
  2404. /**
  2405. * @constructor
  2406. * @param {} val
  2407. */
  2408. var Entry = function (val) {
  2409. /**
  2410. * @type {}
  2411. */
  2412. this.value = val;
  2413. /**
  2414. * @type {module:zrender/core/LRU~Entry}
  2415. */
  2416. this.next;
  2417. /**
  2418. * @type {module:zrender/core/LRU~Entry}
  2419. */
  2420. this.prev;
  2421. };
  2422. /**
  2423. * LRU Cache
  2424. * @constructor
  2425. * @alias module:zrender/core/LRU
  2426. */
  2427. var LRU = function (maxSize) {
  2428. this._list = new LinkedList();
  2429. this._map = {};
  2430. this._maxSize = maxSize || 10;
  2431. this._lastRemovedEntry = null;
  2432. };
  2433. var LRUProto = LRU.prototype;
  2434. /**
  2435. * @param {string} key
  2436. * @param {} value
  2437. * @return {} Removed value
  2438. */
  2439. LRUProto.put = function (key, value) {
  2440. var list = this._list;
  2441. var map = this._map;
  2442. var removed = null;
  2443. if (map[key] == null) {
  2444. var len = list.len();
  2445. // Reuse last removed entry
  2446. var entry = this._lastRemovedEntry;
  2447. if (len >= this._maxSize && len > 0) {
  2448. // Remove the least recently used
  2449. var leastUsedEntry = list.head;
  2450. list.remove(leastUsedEntry);
  2451. delete map[leastUsedEntry.key];
  2452. removed = leastUsedEntry.value;
  2453. this._lastRemovedEntry = leastUsedEntry;
  2454. }
  2455. if (entry) {
  2456. entry.value = value;
  2457. }
  2458. else {
  2459. entry = new Entry(value);
  2460. }
  2461. entry.key = key;
  2462. list.insertEntry(entry);
  2463. map[key] = entry;
  2464. }
  2465. return removed;
  2466. };
  2467. /**
  2468. * @param {string} key
  2469. * @return {}
  2470. */
  2471. LRUProto.get = function (key) {
  2472. var entry = this._map[key];
  2473. var list = this._list;
  2474. if (entry != null) {
  2475. // Put the latest used entry in the tail
  2476. if (entry !== list.tail) {
  2477. list.remove(entry);
  2478. list.insertEntry(entry);
  2479. }
  2480. return entry.value;
  2481. }
  2482. };
  2483. /**
  2484. * Clear the cache
  2485. */
  2486. LRUProto.clear = function () {
  2487. this._list.clear();
  2488. this._map = {};
  2489. };
  2490. var kCSSColorTable = {
  2491. 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1],
  2492. 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1],
  2493. 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1],
  2494. 'beige': [245,245,220,1], 'bisque': [255,228,196,1],
  2495. 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1],
  2496. 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1],
  2497. 'brown': [165,42,42,1], 'burlywood': [222,184,135,1],
  2498. 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1],
  2499. 'chocolate': [210,105,30,1], 'coral': [255,127,80,1],
  2500. 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1],
  2501. 'crimson': [220,20,60,1], 'cyan': [0,255,255,1],
  2502. 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1],
  2503. 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1],
  2504. 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1],
  2505. 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1],
  2506. 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1],
  2507. 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1],
  2508. 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1],
  2509. 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1],
  2510. 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1],
  2511. 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1],
  2512. 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1],
  2513. 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1],
  2514. 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1],
  2515. 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1],
  2516. 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1],
  2517. 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1],
  2518. 'gray': [128,128,128,1], 'green': [0,128,0,1],
  2519. 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1],
  2520. 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1],
  2521. 'indianred': [205,92,92,1], 'indigo': [75,0,130,1],
  2522. 'ivory': [255,255,240,1], 'khaki': [240,230,140,1],
  2523. 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1],
  2524. 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1],
  2525. 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1],
  2526. 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1],
  2527. 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1],
  2528. 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1],
  2529. 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1],
  2530. 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1],
  2531. 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1],
  2532. 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1],
  2533. 'limegreen': [50,205,50,1], 'linen': [250,240,230,1],
  2534. 'magenta': [255,0,255,1], 'maroon': [128,0,0,1],
  2535. 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1],
  2536. 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1],
  2537. 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1],
  2538. 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1],
  2539. 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1],
  2540. 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1],
  2541. 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1],
  2542. 'navy': [0,0,128,1], 'oldlace': [253,245,230,1],
  2543. 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1],
  2544. 'orange': [255,165,0,1], 'orangered': [255,69,0,1],
  2545. 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1],
  2546. 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1],
  2547. 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1],
  2548. 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1],
  2549. 'pink': [255,192,203,1], 'plum': [221,160,221,1],
  2550. 'powderblue': [176,224,230,1], 'purple': [128,0,128,1],
  2551. 'red': [255,0,0,1], 'rosybrown': [188,143,143,1],
  2552. 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1],
  2553. 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1],
  2554. 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1],
  2555. 'sienna': [160,82,45,1], 'silver': [192,192,192,1],
  2556. 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1],
  2557. 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1],
  2558. 'snow': [255,250,250,1], 'springgreen': [0,255,127,1],
  2559. 'steelblue': [70,130,180,1], 'tan': [210,180,140,1],
  2560. 'teal': [0,128,128,1], 'thistle': [216,191,216,1],
  2561. 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1],
  2562. 'violet': [238,130,238,1], 'wheat': [245,222,179,1],
  2563. 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1],
  2564. 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1]
  2565. };
  2566. function clampCssByte(i) { // Clamp to integer 0 .. 255.
  2567. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  2568. return i < 0 ? 0 : i > 255 ? 255 : i;
  2569. }
  2570. function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0.
  2571. return f < 0 ? 0 : f > 1 ? 1 : f;
  2572. }
  2573. function parseCssInt(str) { // int or percentage.
  2574. if (str.length && str.charAt(str.length - 1) === '%') {
  2575. return clampCssByte(parseFloat(str) / 100 * 255);
  2576. }
  2577. return clampCssByte(parseInt(str, 10));
  2578. }
  2579. function parseCssFloat(str) { // float or percentage.
  2580. if (str.length && str.charAt(str.length - 1) === '%') {
  2581. return clampCssFloat(parseFloat(str) / 100);
  2582. }
  2583. return clampCssFloat(parseFloat(str));
  2584. }
  2585. function cssHueToRgb(m1, m2, h) {
  2586. if (h < 0) {
  2587. h += 1;
  2588. }
  2589. else if (h > 1) {
  2590. h -= 1;
  2591. }
  2592. if (h * 6 < 1) {
  2593. return m1 + (m2 - m1) * h * 6;
  2594. }
  2595. if (h * 2 < 1) {
  2596. return m2;
  2597. }
  2598. if (h * 3 < 2) {
  2599. return m1 + (m2 - m1) * (2/3 - h) * 6;
  2600. }
  2601. return m1;
  2602. }
  2603. function setRgba(out, r, g, b, a) {
  2604. out[0] = r; out[1] = g; out[2] = b; out[3] = a;
  2605. return out;
  2606. }
  2607. function copyRgba(out, a) {
  2608. out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3];
  2609. return out;
  2610. }
  2611. var colorCache = new LRU(20);
  2612. var lastRemovedArr = null;
  2613. function putToCache(colorStr, rgbaArr) {
  2614. // Reuse removed array
  2615. if (lastRemovedArr) {
  2616. copyRgba(lastRemovedArr, rgbaArr);
  2617. }
  2618. lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));
  2619. }
  2620. /**
  2621. * @param {string} colorStr
  2622. * @param {Array.<number>} out
  2623. * @return {Array.<number>}
  2624. * @memberOf module:zrender/util/color
  2625. */
  2626. function parse(colorStr, rgbaArr) {
  2627. if (!colorStr) {
  2628. return;
  2629. }
  2630. rgbaArr = rgbaArr || [];
  2631. var cached = colorCache.get(colorStr);
  2632. if (cached) {
  2633. return copyRgba(rgbaArr, cached);
  2634. }
  2635. // colorStr may be not string
  2636. colorStr = colorStr + '';
  2637. // Remove all whitespace, not compliant, but should just be more accepting.
  2638. var str = colorStr.replace(/ /g, '').toLowerCase();
  2639. // Color keywords (and transparent) lookup.
  2640. if (str in kCSSColorTable) {
  2641. copyRgba(rgbaArr, kCSSColorTable[str]);
  2642. putToCache(colorStr, rgbaArr);
  2643. return rgbaArr;
  2644. }
  2645. // #abc and #abc123 syntax.
  2646. if (str.charAt(0) === '#') {
  2647. if (str.length === 4) {
  2648. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  2649. if (!(iv >= 0 && iv <= 0xfff)) {
  2650. setRgba(rgbaArr, 0, 0, 0, 1);
  2651. return; // Covers NaN.
  2652. }
  2653. setRgba(rgbaArr,
  2654. ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
  2655. (iv & 0xf0) | ((iv & 0xf0) >> 4),
  2656. (iv & 0xf) | ((iv & 0xf) << 4),
  2657. 1
  2658. );
  2659. putToCache(colorStr, rgbaArr);
  2660. return rgbaArr;
  2661. }
  2662. else if (str.length === 7) {
  2663. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  2664. if (!(iv >= 0 && iv <= 0xffffff)) {
  2665. setRgba(rgbaArr, 0, 0, 0, 1);
  2666. return; // Covers NaN.
  2667. }
  2668. setRgba(rgbaArr,
  2669. (iv & 0xff0000) >> 16,
  2670. (iv & 0xff00) >> 8,
  2671. iv & 0xff,
  2672. 1
  2673. );
  2674. putToCache(colorStr, rgbaArr);
  2675. return rgbaArr;
  2676. }
  2677. return;
  2678. }
  2679. var op = str.indexOf('('), ep = str.indexOf(')');
  2680. if (op !== -1 && ep + 1 === str.length) {
  2681. var fname = str.substr(0, op);
  2682. var params = str.substr(op + 1, ep - (op + 1)).split(',');
  2683. var alpha = 1; // To allow case fallthrough.
  2684. switch (fname) {
  2685. case 'rgba':
  2686. if (params.length !== 4) {
  2687. setRgba(rgbaArr, 0, 0, 0, 1);
  2688. return;
  2689. }
  2690. alpha = parseCssFloat(params.pop()); // jshint ignore:line
  2691. // Fall through.
  2692. case 'rgb':
  2693. if (params.length !== 3) {
  2694. setRgba(rgbaArr, 0, 0, 0, 1);
  2695. return;
  2696. }
  2697. setRgba(rgbaArr,
  2698. parseCssInt(params[0]),
  2699. parseCssInt(params[1]),
  2700. parseCssInt(params[2]),
  2701. alpha
  2702. );
  2703. putToCache(colorStr, rgbaArr);
  2704. return rgbaArr;
  2705. case 'hsla':
  2706. if (params.length !== 4) {
  2707. setRgba(rgbaArr, 0, 0, 0, 1);
  2708. return;
  2709. }
  2710. params[3] = parseCssFloat(params[3]);
  2711. hsla2rgba(params, rgbaArr);
  2712. putToCache(colorStr, rgbaArr);
  2713. return rgbaArr;
  2714. case 'hsl':
  2715. if (params.length !== 3) {
  2716. setRgba(rgbaArr, 0, 0, 0, 1);
  2717. return;
  2718. }
  2719. hsla2rgba(params, rgbaArr);
  2720. putToCache(colorStr, rgbaArr);
  2721. return rgbaArr;
  2722. default:
  2723. return;
  2724. }
  2725. }
  2726. setRgba(rgbaArr, 0, 0, 0, 1);
  2727. return;
  2728. }
  2729. /**
  2730. * @param {Array.<number>} hsla
  2731. * @param {Array.<number>} rgba
  2732. * @return {Array.<number>} rgba
  2733. */
  2734. function hsla2rgba(hsla, rgba) {
  2735. var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1
  2736. // NOTE(deanm): According to the CSS spec s/l should only be
  2737. // percentages, but we don't bother and let float or percentage.
  2738. var s = parseCssFloat(hsla[1]);
  2739. var l = parseCssFloat(hsla[2]);
  2740. var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
  2741. var m1 = l * 2 - m2;
  2742. rgba = rgba || [];
  2743. setRgba(rgba,
  2744. clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),
  2745. clampCssByte(cssHueToRgb(m1, m2, h) * 255),
  2746. clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),
  2747. 1
  2748. );
  2749. if (hsla.length === 4) {
  2750. rgba[3] = hsla[3];
  2751. }
  2752. return rgba;
  2753. }
  2754. /**
  2755. * @param {string} color
  2756. * @param {number} level
  2757. * @return {string}
  2758. * @memberOf module:zrender/util/color
  2759. */
  2760. function lift(color, level) {
  2761. var colorArr = parse(color);
  2762. if (colorArr) {
  2763. for (var i = 0; i < 3; i++) {
  2764. if (level < 0) {
  2765. colorArr[i] = colorArr[i] * (1 - level) | 0;
  2766. }
  2767. else {
  2768. colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;
  2769. }
  2770. }
  2771. return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
  2772. }
  2773. }
  2774. /**
  2775. * @param {string} color
  2776. * @return {string}
  2777. * @memberOf module:zrender/util/color
  2778. */
  2779. /**
  2780. * Map value to color. Faster than lerp methods because color is represented by rgba array.
  2781. * @param {number} normalizedValue A float between 0 and 1.
  2782. * @param {Array.<Array.<number>>} colors List of rgba color array
  2783. * @param {Array.<number>} [out] Mapped gba color array
  2784. * @return {Array.<number>} will be null/undefined if input illegal.
  2785. */
  2786. /**
  2787. * @deprecated
  2788. */
  2789. /**
  2790. * @param {number} normalizedValue A float between 0 and 1.
  2791. * @param {Array.<string>} colors Color list.
  2792. * @param {boolean=} fullOutput Default false.
  2793. * @return {(string|Object)} Result color. If fullOutput,
  2794. * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},
  2795. * @memberOf module:zrender/util/color
  2796. */
  2797. /**
  2798. * @deprecated
  2799. */
  2800. /**
  2801. * @param {string} color
  2802. * @param {number=} h 0 ~ 360, ignore when null.
  2803. * @param {number=} s 0 ~ 1, ignore when null.
  2804. * @param {number=} l 0 ~ 1, ignore when null.
  2805. * @return {string} Color string in rgba format.
  2806. * @memberOf module:zrender/util/color
  2807. */
  2808. /**
  2809. * @param {string} color
  2810. * @param {number=} alpha 0 ~ 1
  2811. * @return {string} Color string in rgba format.
  2812. * @memberOf module:zrender/util/color
  2813. */
  2814. /**
  2815. * @param {Array.<number>} arrColor like [12,33,44,0.4]
  2816. * @param {string} type 'rgba', 'hsva', ...
  2817. * @return {string} Result color. (If input illegal, return undefined).
  2818. */
  2819. function stringify(arrColor, type) {
  2820. if (!arrColor || !arrColor.length) {
  2821. return;
  2822. }
  2823. var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];
  2824. if (type === 'rgba' || type === 'hsva' || type === 'hsla') {
  2825. colorStr += ',' + arrColor[3];
  2826. }
  2827. return type + '(' + colorStr + ')';
  2828. }
  2829. /**
  2830. * @module echarts/animation/Animator
  2831. */
  2832. var arraySlice = Array.prototype.slice;
  2833. function defaultGetter(target, key) {
  2834. return target[key];
  2835. }
  2836. function defaultSetter(target, key, value) {
  2837. target[key] = value;
  2838. }
  2839. /**
  2840. * @param {number} p0
  2841. * @param {number} p1
  2842. * @param {number} percent
  2843. * @return {number}
  2844. */
  2845. function interpolateNumber(p0, p1, percent) {
  2846. return (p1 - p0) * percent + p0;
  2847. }
  2848. /**
  2849. * @param {string} p0
  2850. * @param {string} p1
  2851. * @param {number} percent
  2852. * @return {string}
  2853. */
  2854. function interpolateString(p0, p1, percent) {
  2855. return percent > 0.5 ? p1 : p0;
  2856. }
  2857. /**
  2858. * @param {Array} p0
  2859. * @param {Array} p1
  2860. * @param {number} percent
  2861. * @param {Array} out
  2862. * @param {number} arrDim
  2863. */
  2864. function interpolateArray(p0, p1, percent, out, arrDim) {
  2865. var len = p0.length;
  2866. if (arrDim == 1) {
  2867. for (var i = 0; i < len; i++) {
  2868. out[i] = interpolateNumber(p0[i], p1[i], percent);
  2869. }
  2870. }
  2871. else {
  2872. var len2 = len && p0[0].length;
  2873. for (var i = 0; i < len; i++) {
  2874. for (var j = 0; j < len2; j++) {
  2875. out[i][j] = interpolateNumber(
  2876. p0[i][j], p1[i][j], percent
  2877. );
  2878. }
  2879. }
  2880. }
  2881. }
  2882. // arr0 is source array, arr1 is target array.
  2883. // Do some preprocess to avoid error happened when interpolating from arr0 to arr1
  2884. function fillArr(arr0, arr1, arrDim) {
  2885. var arr0Len = arr0.length;
  2886. var arr1Len = arr1.length;
  2887. if (arr0Len !== arr1Len) {
  2888. // FIXME Not work for TypedArray
  2889. var isPreviousLarger = arr0Len > arr1Len;
  2890. if (isPreviousLarger) {
  2891. // Cut the previous
  2892. arr0.length = arr1Len;
  2893. }
  2894. else {
  2895. // Fill the previous
  2896. for (var i = arr0Len; i < arr1Len; i++) {
  2897. arr0.push(
  2898. arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])
  2899. );
  2900. }
  2901. }
  2902. }
  2903. // Handling NaN value
  2904. var len2 = arr0[0] && arr0[0].length;
  2905. for (var i = 0; i < arr0.length; i++) {
  2906. if (arrDim === 1) {
  2907. if (isNaN(arr0[i])) {
  2908. arr0[i] = arr1[i];
  2909. }
  2910. }
  2911. else {
  2912. for (var j = 0; j < len2; j++) {
  2913. if (isNaN(arr0[i][j])) {
  2914. arr0[i][j] = arr1[i][j];
  2915. }
  2916. }
  2917. }
  2918. }
  2919. }
  2920. /**
  2921. * @param {Array} arr0
  2922. * @param {Array} arr1
  2923. * @param {number} arrDim
  2924. * @return {boolean}
  2925. */
  2926. function isArraySame(arr0, arr1, arrDim) {
  2927. if (arr0 === arr1) {
  2928. return true;
  2929. }
  2930. var len = arr0.length;
  2931. if (len !== arr1.length) {
  2932. return false;
  2933. }
  2934. if (arrDim === 1) {
  2935. for (var i = 0; i < len; i++) {
  2936. if (arr0[i] !== arr1[i]) {
  2937. return false;
  2938. }
  2939. }
  2940. }
  2941. else {
  2942. var len2 = arr0[0].length;
  2943. for (var i = 0; i < len; i++) {
  2944. for (var j = 0; j < len2; j++) {
  2945. if (arr0[i][j] !== arr1[i][j]) {
  2946. return false;
  2947. }
  2948. }
  2949. }
  2950. }
  2951. return true;
  2952. }
  2953. /**
  2954. * Catmull Rom interpolate array
  2955. * @param {Array} p0
  2956. * @param {Array} p1
  2957. * @param {Array} p2
  2958. * @param {Array} p3
  2959. * @param {number} t
  2960. * @param {number} t2
  2961. * @param {number} t3
  2962. * @param {Array} out
  2963. * @param {number} arrDim
  2964. */
  2965. function catmullRomInterpolateArray(
  2966. p0, p1, p2, p3, t, t2, t3, out, arrDim
  2967. ) {
  2968. var len = p0.length;
  2969. if (arrDim == 1) {
  2970. for (var i = 0; i < len; i++) {
  2971. out[i] = catmullRomInterpolate(
  2972. p0[i], p1[i], p2[i], p3[i], t, t2, t3
  2973. );
  2974. }
  2975. }
  2976. else {
  2977. var len2 = p0[0].length;
  2978. for (var i = 0; i < len; i++) {
  2979. for (var j = 0; j < len2; j++) {
  2980. out[i][j] = catmullRomInterpolate(
  2981. p0[i][j], p1[i][j], p2[i][j], p3[i][j],
  2982. t, t2, t3
  2983. );
  2984. }
  2985. }
  2986. }
  2987. }
  2988. /**
  2989. * Catmull Rom interpolate number
  2990. * @param {number} p0
  2991. * @param {number} p1
  2992. * @param {number} p2
  2993. * @param {number} p3
  2994. * @param {number} t
  2995. * @param {number} t2
  2996. * @param {number} t3
  2997. * @return {number}
  2998. */
  2999. function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {
  3000. var v0 = (p2 - p0) * 0.5;
  3001. var v1 = (p3 - p1) * 0.5;
  3002. return (2 * (p1 - p2) + v0 + v1) * t3
  3003. + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
  3004. + v0 * t + p1;
  3005. }
  3006. function cloneValue(value) {
  3007. if (isArrayLike(value)) {
  3008. var len = value.length;
  3009. if (isArrayLike(value[0])) {
  3010. var ret = [];
  3011. for (var i = 0; i < len; i++) {
  3012. ret.push(arraySlice.call(value[i]));
  3013. }
  3014. return ret;
  3015. }
  3016. return arraySlice.call(value);
  3017. }
  3018. return value;
  3019. }
  3020. function rgba2String(rgba) {
  3021. rgba[0] = Math.floor(rgba[0]);
  3022. rgba[1] = Math.floor(rgba[1]);
  3023. rgba[2] = Math.floor(rgba[2]);
  3024. return 'rgba(' + rgba.join(',') + ')';
  3025. }
  3026. function getArrayDim(keyframes) {
  3027. var lastValue = keyframes[keyframes.length - 1].value;
  3028. return isArrayLike(lastValue && lastValue[0]) ? 2 : 1;
  3029. }
  3030. function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) {
  3031. var getter = animator._getter;
  3032. var setter = animator._setter;
  3033. var useSpline = easing === 'spline';
  3034. var trackLen = keyframes.length;
  3035. if (!trackLen) {
  3036. return;
  3037. }
  3038. // Guess data type
  3039. var firstVal = keyframes[0].value;
  3040. var isValueArray = isArrayLike(firstVal);
  3041. var isValueColor = false;
  3042. var isValueString = false;
  3043. // For vertices morphing
  3044. var arrDim = isValueArray ? getArrayDim(keyframes) : 0;
  3045. var trackMaxTime;
  3046. // Sort keyframe as ascending
  3047. keyframes.sort(function(a, b) {
  3048. return a.time - b.time;
  3049. });
  3050. trackMaxTime = keyframes[trackLen - 1].time;
  3051. // Percents of each keyframe
  3052. var kfPercents = [];
  3053. // Value of each keyframe
  3054. var kfValues = [];
  3055. var prevValue = keyframes[0].value;
  3056. var isAllValueEqual = true;
  3057. for (var i = 0; i < trackLen; i++) {
  3058. kfPercents.push(keyframes[i].time / trackMaxTime);
  3059. // Assume value is a color when it is a string
  3060. var value = keyframes[i].value;
  3061. // Check if value is equal, deep check if value is array
  3062. if (!((isValueArray && isArraySame(value, prevValue, arrDim))
  3063. || (!isValueArray && value === prevValue))) {
  3064. isAllValueEqual = false;
  3065. }
  3066. prevValue = value;
  3067. // Try converting a string to a color array
  3068. if (typeof value == 'string') {
  3069. var colorArray = parse(value);
  3070. if (colorArray) {
  3071. value = colorArray;
  3072. isValueColor = true;
  3073. }
  3074. else {
  3075. isValueString = true;
  3076. }
  3077. }
  3078. kfValues.push(value);
  3079. }
  3080. if (!forceAnimate && isAllValueEqual) {
  3081. return;
  3082. }
  3083. var lastValue = kfValues[trackLen - 1];
  3084. // Polyfill array and NaN value
  3085. for (var i = 0; i < trackLen - 1; i++) {
  3086. if (isValueArray) {
  3087. fillArr(kfValues[i], lastValue, arrDim);
  3088. }
  3089. else {
  3090. if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) {
  3091. kfValues[i] = lastValue;
  3092. }
  3093. }
  3094. }
  3095. isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim);
  3096. // Cache the key of last frame to speed up when
  3097. // animation playback is sequency
  3098. var lastFrame = 0;
  3099. var lastFramePercent = 0;
  3100. var start;
  3101. var w;
  3102. var p0;
  3103. var p1;
  3104. var p2;
  3105. var p3;
  3106. if (isValueColor) {
  3107. var rgba = [0, 0, 0, 0];
  3108. }
  3109. var onframe = function (target, percent) {
  3110. // Find the range keyframes
  3111. // kf1-----kf2---------current--------kf3
  3112. // find kf2 and kf3 and do interpolation
  3113. var frame;
  3114. // In the easing function like elasticOut, percent may less than 0
  3115. if (percent < 0) {
  3116. frame = 0;
  3117. }
  3118. else if (percent < lastFramePercent) {
  3119. // Start from next key
  3120. // PENDING start from lastFrame ?
  3121. start = Math.min(lastFrame + 1, trackLen - 1);
  3122. for (frame = start; frame >= 0; frame--) {
  3123. if (kfPercents[frame] <= percent) {
  3124. break;
  3125. }
  3126. }
  3127. // PENDING really need to do this ?
  3128. frame = Math.min(frame, trackLen - 2);
  3129. }
  3130. else {
  3131. for (frame = lastFrame; frame < trackLen; frame++) {
  3132. if (kfPercents[frame] > percent) {
  3133. break;
  3134. }
  3135. }
  3136. frame = Math.min(frame - 1, trackLen - 2);
  3137. }
  3138. lastFrame = frame;
  3139. lastFramePercent = percent;
  3140. var range = (kfPercents[frame + 1] - kfPercents[frame]);
  3141. if (range === 0) {
  3142. return;
  3143. }
  3144. else {
  3145. w = (percent - kfPercents[frame]) / range;
  3146. }
  3147. if (useSpline) {
  3148. p1 = kfValues[frame];
  3149. p0 = kfValues[frame === 0 ? frame : frame - 1];
  3150. p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1];
  3151. p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2];
  3152. if (isValueArray) {
  3153. catmullRomInterpolateArray(
  3154. p0, p1, p2, p3, w, w * w, w * w * w,
  3155. getter(target, propName),
  3156. arrDim
  3157. );
  3158. }
  3159. else {
  3160. var value;
  3161. if (isValueColor) {
  3162. value = catmullRomInterpolateArray(
  3163. p0, p1, p2, p3, w, w * w, w * w * w,
  3164. rgba, 1
  3165. );
  3166. value = rgba2String(rgba);
  3167. }
  3168. else if (isValueString) {
  3169. // String is step(0.5)
  3170. return interpolateString(p1, p2, w);
  3171. }
  3172. else {
  3173. value = catmullRomInterpolate(
  3174. p0, p1, p2, p3, w, w * w, w * w * w
  3175. );
  3176. }
  3177. setter(
  3178. target,
  3179. propName,
  3180. value
  3181. );
  3182. }
  3183. }
  3184. else {
  3185. if (isValueArray) {
  3186. interpolateArray(
  3187. kfValues[frame], kfValues[frame + 1], w,
  3188. getter(target, propName),
  3189. arrDim
  3190. );
  3191. }
  3192. else {
  3193. var value;
  3194. if (isValueColor) {
  3195. interpolateArray(
  3196. kfValues[frame], kfValues[frame + 1], w,
  3197. rgba, 1
  3198. );
  3199. value = rgba2String(rgba);
  3200. }
  3201. else if (isValueString) {
  3202. // String is step(0.5)
  3203. return interpolateString(kfValues[frame], kfValues[frame + 1], w);
  3204. }
  3205. else {
  3206. value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w);
  3207. }
  3208. setter(
  3209. target,
  3210. propName,
  3211. value
  3212. );
  3213. }
  3214. }
  3215. };
  3216. var clip = new Clip({
  3217. target: animator._target,
  3218. life: trackMaxTime,
  3219. loop: animator._loop,
  3220. delay: animator._delay,
  3221. onframe: onframe,
  3222. ondestroy: oneTrackDone
  3223. });
  3224. if (easing && easing !== 'spline') {
  3225. clip.easing = easing;
  3226. }
  3227. return clip;
  3228. }
  3229. /**
  3230. * @alias module:zrender/animation/Animator
  3231. * @constructor
  3232. * @param {Object} target
  3233. * @param {boolean} loop
  3234. * @param {Function} getter
  3235. * @param {Function} setter
  3236. */
  3237. var Animator = function(target, loop, getter, setter) {
  3238. this._tracks = {};
  3239. this._target = target;
  3240. this._loop = loop || false;
  3241. this._getter = getter || defaultGetter;
  3242. this._setter = setter || defaultSetter;
  3243. this._clipCount = 0;
  3244. this._delay = 0;
  3245. this._doneList = [];
  3246. this._onframeList = [];
  3247. this._clipList = [];
  3248. };
  3249. Animator.prototype = {
  3250. /**
  3251. * 设置动画关键帧
  3252. * @param {number} time 关键帧时间,单位是ms
  3253. * @param {Object} props 关键帧的属性值,key-value表示
  3254. * @return {module:zrender/animation/Animator}
  3255. */
  3256. when: function(time /* ms */, props) {
  3257. var tracks = this._tracks;
  3258. for (var propName in props) {
  3259. if (!props.hasOwnProperty(propName)) {
  3260. continue;
  3261. }
  3262. if (!tracks[propName]) {
  3263. tracks[propName] = [];
  3264. // Invalid value
  3265. var value = this._getter(this._target, propName);
  3266. if (value == null) {
  3267. // zrLog('Invalid property ' + propName);
  3268. continue;
  3269. }
  3270. // If time is 0
  3271. // Then props is given initialize value
  3272. // Else
  3273. // Initialize value from current prop value
  3274. if (time !== 0) {
  3275. tracks[propName].push({
  3276. time: 0,
  3277. value: cloneValue(value)
  3278. });
  3279. }
  3280. }
  3281. tracks[propName].push({
  3282. time: time,
  3283. value: props[propName]
  3284. });
  3285. }
  3286. return this;
  3287. },
  3288. /**
  3289. * 添加动画每一帧的回调函数
  3290. * @param {Function} callback
  3291. * @return {module:zrender/animation/Animator}
  3292. */
  3293. during: function (callback) {
  3294. this._onframeList.push(callback);
  3295. return this;
  3296. },
  3297. pause: function () {
  3298. for (var i = 0; i < this._clipList.length; i++) {
  3299. this._clipList[i].pause();
  3300. }
  3301. this._paused = true;
  3302. },
  3303. resume: function () {
  3304. for (var i = 0; i < this._clipList.length; i++) {
  3305. this._clipList[i].resume();
  3306. }
  3307. this._paused = false;
  3308. },
  3309. isPaused: function () {
  3310. return !!this._paused;
  3311. },
  3312. _doneCallback: function () {
  3313. // Clear all tracks
  3314. this._tracks = {};
  3315. // Clear all clips
  3316. this._clipList.length = 0;
  3317. var doneList = this._doneList;
  3318. var len = doneList.length;
  3319. for (var i = 0; i < len; i++) {
  3320. doneList[i].call(this);
  3321. }
  3322. },
  3323. /**
  3324. * 开始执行动画
  3325. * @param {string|Function} [easing]
  3326. * 动画缓动函数,详见{@link module:zrender/animation/easing}
  3327. * @param {boolean} forceAnimate
  3328. * @return {module:zrender/animation/Animator}
  3329. */
  3330. start: function (easing, forceAnimate) {
  3331. var self = this;
  3332. var clipCount = 0;
  3333. var oneTrackDone = function() {
  3334. clipCount--;
  3335. if (!clipCount) {
  3336. self._doneCallback();
  3337. }
  3338. };
  3339. var lastClip;
  3340. for (var propName in this._tracks) {
  3341. if (!this._tracks.hasOwnProperty(propName)) {
  3342. continue;
  3343. }
  3344. var clip = createTrackClip(
  3345. this, easing, oneTrackDone,
  3346. this._tracks[propName], propName, forceAnimate
  3347. );
  3348. if (clip) {
  3349. this._clipList.push(clip);
  3350. clipCount++;
  3351. // If start after added to animation
  3352. if (this.animation) {
  3353. this.animation.addClip(clip);
  3354. }
  3355. lastClip = clip;
  3356. }
  3357. }
  3358. // Add during callback on the last clip
  3359. if (lastClip) {
  3360. var oldOnFrame = lastClip.onframe;
  3361. lastClip.onframe = function (target, percent) {
  3362. oldOnFrame(target, percent);
  3363. for (var i = 0; i < self._onframeList.length; i++) {
  3364. self._onframeList[i](target, percent);
  3365. }
  3366. };
  3367. }
  3368. // This optimization will help the case that in the upper application
  3369. // the view may be refreshed frequently, where animation will be
  3370. // called repeatly but nothing changed.
  3371. if (!clipCount) {
  3372. this._doneCallback();
  3373. }
  3374. return this;
  3375. },
  3376. /**
  3377. * 停止动画
  3378. * @param {boolean} forwardToLast If move to last frame before stop
  3379. */
  3380. stop: function (forwardToLast) {
  3381. var clipList = this._clipList;
  3382. var animation = this.animation;
  3383. for (var i = 0; i < clipList.length; i++) {
  3384. var clip = clipList[i];
  3385. if (forwardToLast) {
  3386. // Move to last frame before stop
  3387. clip.onframe(this._target, 1);
  3388. }
  3389. animation && animation.removeClip(clip);
  3390. }
  3391. clipList.length = 0;
  3392. },
  3393. /**
  3394. * 设置动画延迟开始的时间
  3395. * @param {number} time 单位ms
  3396. * @return {module:zrender/animation/Animator}
  3397. */
  3398. delay: function (time) {
  3399. this._delay = time;
  3400. return this;
  3401. },
  3402. /**
  3403. * 添加动画结束的回调
  3404. * @param {Function} cb
  3405. * @return {module:zrender/animation/Animator}
  3406. */
  3407. done: function(cb) {
  3408. if (cb) {
  3409. this._doneList.push(cb);
  3410. }
  3411. return this;
  3412. },
  3413. /**
  3414. * @return {Array.<module:zrender/animation/Clip>}
  3415. */
  3416. getClips: function () {
  3417. return this._clipList;
  3418. }
  3419. };
  3420. var dpr = 1;
  3421. // If in browser environment
  3422. if (typeof window !== 'undefined') {
  3423. dpr = Math.max(window.devicePixelRatio || 1, 1);
  3424. }
  3425. /**
  3426. * config默认配置项
  3427. * @exports zrender/config
  3428. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  3429. */
  3430. /**
  3431. * debug日志选项:catchBrushException为true下有效
  3432. * 0 : 不生成debug数据,发布用
  3433. * 1 : 异常抛出,调试用
  3434. * 2 : 控制台输出,调试用
  3435. */
  3436. var debugMode = 0;
  3437. // retina 屏幕优化
  3438. var devicePixelRatio = dpr;
  3439. var log = function () {
  3440. };
  3441. if (debugMode === 1) {
  3442. log = function () {
  3443. for (var k in arguments) {
  3444. throw new Error(arguments[k]);
  3445. }
  3446. };
  3447. }
  3448. else if (debugMode > 1) {
  3449. log = function () {
  3450. for (var k in arguments) {
  3451. console.log(arguments[k]);
  3452. }
  3453. };
  3454. }
  3455. var log$1 = log;
  3456. /**
  3457. * @alias modue:zrender/mixin/Animatable
  3458. * @constructor
  3459. */
  3460. var Animatable = function () {
  3461. /**
  3462. * @type {Array.<module:zrender/animation/Animator>}
  3463. * @readOnly
  3464. */
  3465. this.animators = [];
  3466. };
  3467. Animatable.prototype = {
  3468. constructor: Animatable,
  3469. /**
  3470. * 动画
  3471. *
  3472. * @param {string} path The path to fetch value from object, like 'a.b.c'.
  3473. * @param {boolean} [loop] Whether to loop animation.
  3474. * @return {module:zrender/animation/Animator}
  3475. * @example:
  3476. * el.animate('style', false)
  3477. * .when(1000, {x: 10} )
  3478. * .done(function(){ // Animation done })
  3479. * .start()
  3480. */
  3481. animate: function (path, loop) {
  3482. var target;
  3483. var animatingShape = false;
  3484. var el = this;
  3485. var zr = this.__zr;
  3486. if (path) {
  3487. var pathSplitted = path.split('.');
  3488. var prop = el;
  3489. // If animating shape
  3490. animatingShape = pathSplitted[0] === 'shape';
  3491. for (var i = 0, l = pathSplitted.length; i < l; i++) {
  3492. if (!prop) {
  3493. continue;
  3494. }
  3495. prop = prop[pathSplitted[i]];
  3496. }
  3497. if (prop) {
  3498. target = prop;
  3499. }
  3500. }
  3501. else {
  3502. target = el;
  3503. }
  3504. if (!target) {
  3505. log$1(
  3506. 'Property "'
  3507. + path
  3508. + '" is not existed in element '
  3509. + el.id
  3510. );
  3511. return;
  3512. }
  3513. var animators = el.animators;
  3514. var animator = new Animator(target, loop);
  3515. animator.during(function (target) {
  3516. el.dirty(animatingShape);
  3517. })
  3518. .done(function () {
  3519. // FIXME Animator will not be removed if use `Animator#stop` to stop animation
  3520. animators.splice(indexOf(animators, animator), 1);
  3521. });
  3522. animators.push(animator);
  3523. // If animate after added to the zrender
  3524. if (zr) {
  3525. zr.animation.addAnimator(animator);
  3526. }
  3527. return animator;
  3528. },
  3529. /**
  3530. * 停止动画
  3531. * @param {boolean} forwardToLast If move to last frame before stop
  3532. */
  3533. stopAnimation: function (forwardToLast) {
  3534. var animators = this.animators;
  3535. var len = animators.length;
  3536. for (var i = 0; i < len; i++) {
  3537. animators[i].stop(forwardToLast);
  3538. }
  3539. animators.length = 0;
  3540. return this;
  3541. },
  3542. /**
  3543. * Caution: this method will stop previous animation.
  3544. * So do not use this method to one element twice before
  3545. * animation starts, unless you know what you are doing.
  3546. * @param {Object} target
  3547. * @param {number} [time=500] Time in ms
  3548. * @param {string} [easing='linear']
  3549. * @param {number} [delay=0]
  3550. * @param {Function} [callback]
  3551. * @param {Function} [forceAnimate] Prevent stop animation and callback
  3552. * immediently when target values are the same as current values.
  3553. *
  3554. * @example
  3555. * // Animate position
  3556. * el.animateTo({
  3557. * position: [10, 10]
  3558. * }, function () { // done })
  3559. *
  3560. * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing
  3561. * el.animateTo({
  3562. * shape: {
  3563. * width: 500
  3564. * },
  3565. * style: {
  3566. * fill: 'red'
  3567. * }
  3568. * position: [10, 10]
  3569. * }, 100, 100, 'cubicOut', function () { // done })
  3570. */
  3571. // TODO Return animation key
  3572. animateTo: function (target, time, delay, easing, callback, forceAnimate) {
  3573. // animateTo(target, time, easing, callback);
  3574. if (isString(delay)) {
  3575. callback = easing;
  3576. easing = delay;
  3577. delay = 0;
  3578. }
  3579. // animateTo(target, time, delay, callback);
  3580. else if (isFunction$1(easing)) {
  3581. callback = easing;
  3582. easing = 'linear';
  3583. delay = 0;
  3584. }
  3585. // animateTo(target, time, callback);
  3586. else if (isFunction$1(delay)) {
  3587. callback = delay;
  3588. delay = 0;
  3589. }
  3590. // animateTo(target, callback)
  3591. else if (isFunction$1(time)) {
  3592. callback = time;
  3593. time = 500;
  3594. }
  3595. // animateTo(target)
  3596. else if (!time) {
  3597. time = 500;
  3598. }
  3599. // Stop all previous animations
  3600. this.stopAnimation();
  3601. this._animateToShallow('', this, target, time, delay);
  3602. // Animators may be removed immediately after start
  3603. // if there is nothing to animate
  3604. var animators = this.animators.slice();
  3605. var count = animators.length;
  3606. function done() {
  3607. count--;
  3608. if (!count) {
  3609. callback && callback();
  3610. }
  3611. }
  3612. // No animators. This should be checked before animators[i].start(),
  3613. // because 'done' may be executed immediately if no need to animate.
  3614. if (!count) {
  3615. callback && callback();
  3616. }
  3617. // Start after all animators created
  3618. // Incase any animator is done immediately when all animation properties are not changed
  3619. for (var i = 0; i < animators.length; i++) {
  3620. animators[i]
  3621. .done(done)
  3622. .start(easing, forceAnimate);
  3623. }
  3624. },
  3625. /**
  3626. * @private
  3627. * @param {string} path=''
  3628. * @param {Object} source=this
  3629. * @param {Object} target
  3630. * @param {number} [time=500]
  3631. * @param {number} [delay=0]
  3632. *
  3633. * @example
  3634. * // Animate position
  3635. * el._animateToShallow({
  3636. * position: [10, 10]
  3637. * })
  3638. *
  3639. * // Animate shape, style and position in 100ms, delayed 100ms
  3640. * el._animateToShallow({
  3641. * shape: {
  3642. * width: 500
  3643. * },
  3644. * style: {
  3645. * fill: 'red'
  3646. * }
  3647. * position: [10, 10]
  3648. * }, 100, 100)
  3649. */
  3650. _animateToShallow: function (path, source, target, time, delay) {
  3651. var objShallow = {};
  3652. var propertyCount = 0;
  3653. for (var name in target) {
  3654. if (!target.hasOwnProperty(name)) {
  3655. continue;
  3656. }
  3657. if (source[name] != null) {
  3658. if (isObject$1(target[name]) && !isArrayLike(target[name])) {
  3659. this._animateToShallow(
  3660. path ? path + '.' + name : name,
  3661. source[name],
  3662. target[name],
  3663. time,
  3664. delay
  3665. );
  3666. }
  3667. else {
  3668. objShallow[name] = target[name];
  3669. propertyCount++;
  3670. }
  3671. }
  3672. else if (target[name] != null) {
  3673. // Attr directly if not has property
  3674. // FIXME, if some property not needed for element ?
  3675. if (!path) {
  3676. this.attr(name, target[name]);
  3677. }
  3678. else { // Shape or style
  3679. var props = {};
  3680. props[path] = {};
  3681. props[path][name] = target[name];
  3682. this.attr(props);
  3683. }
  3684. }
  3685. }
  3686. if (propertyCount > 0) {
  3687. this.animate(path, false)
  3688. .when(time == null ? 500 : time, objShallow)
  3689. .delay(delay || 0);
  3690. }
  3691. return this;
  3692. }
  3693. };
  3694. /**
  3695. * @alias module:zrender/Element
  3696. * @constructor
  3697. * @extends {module:zrender/mixin/Animatable}
  3698. * @extends {module:zrender/mixin/Transformable}
  3699. * @extends {module:zrender/mixin/Eventful}
  3700. */
  3701. var Element = function (opts) { // jshint ignore:line
  3702. Transformable.call(this, opts);
  3703. Eventful.call(this, opts);
  3704. Animatable.call(this, opts);
  3705. /**
  3706. * 画布元素ID
  3707. * @type {string}
  3708. */
  3709. this.id = opts.id || guid();
  3710. };
  3711. Element.prototype = {
  3712. /**
  3713. * 元素类型
  3714. * Element type
  3715. * @type {string}
  3716. */
  3717. type: 'element',
  3718. /**
  3719. * 元素名字
  3720. * Element name
  3721. * @type {string}
  3722. */
  3723. name: '',
  3724. /**
  3725. * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值
  3726. * ZRender instance will be assigned when element is associated with zrender
  3727. * @name module:/zrender/Element#__zr
  3728. * @type {module:zrender/ZRender}
  3729. */
  3730. __zr: null,
  3731. /**
  3732. * 图形是否忽略,为true时忽略图形的绘制以及事件触发
  3733. * If ignore drawing and events of the element object
  3734. * @name module:/zrender/Element#ignore
  3735. * @type {boolean}
  3736. * @default false
  3737. */
  3738. ignore: false,
  3739. /**
  3740. * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪
  3741. * 该路径会继承被裁减对象的变换
  3742. * @type {module:zrender/graphic/Path}
  3743. * @see http://www.w3.org/TR/2dcontext/#clipping-region
  3744. * @readOnly
  3745. */
  3746. clipPath: null,
  3747. /**
  3748. * 是否是 Group
  3749. * @type {boolean}
  3750. */
  3751. isGroup: false,
  3752. /**
  3753. * Drift element
  3754. * @param {number} dx dx on the global space
  3755. * @param {number} dy dy on the global space
  3756. */
  3757. drift: function (dx, dy) {
  3758. switch (this.draggable) {
  3759. case 'horizontal':
  3760. dy = 0;
  3761. break;
  3762. case 'vertical':
  3763. dx = 0;
  3764. break;
  3765. }
  3766. var m = this.transform;
  3767. if (!m) {
  3768. m = this.transform = [1, 0, 0, 1, 0, 0];
  3769. }
  3770. m[4] += dx;
  3771. m[5] += dy;
  3772. this.decomposeTransform();
  3773. this.dirty(false);
  3774. },
  3775. /**
  3776. * Hook before update
  3777. */
  3778. beforeUpdate: function () {},
  3779. /**
  3780. * Hook after update
  3781. */
  3782. afterUpdate: function () {},
  3783. /**
  3784. * Update each frame
  3785. */
  3786. update: function () {
  3787. this.updateTransform();
  3788. },
  3789. /**
  3790. * @param {Function} cb
  3791. * @param {} context
  3792. */
  3793. traverse: function (cb, context) {},
  3794. /**
  3795. * @protected
  3796. */
  3797. attrKV: function (key, value) {
  3798. if (key === 'position' || key === 'scale' || key === 'origin') {
  3799. // Copy the array
  3800. if (value) {
  3801. var target = this[key];
  3802. if (!target) {
  3803. target = this[key] = [];
  3804. }
  3805. target[0] = value[0];
  3806. target[1] = value[1];
  3807. }
  3808. }
  3809. else {
  3810. this[key] = value;
  3811. }
  3812. },
  3813. /**
  3814. * Hide the element
  3815. */
  3816. hide: function () {
  3817. this.ignore = true;
  3818. this.__zr && this.__zr.refresh();
  3819. },
  3820. /**
  3821. * Show the element
  3822. */
  3823. show: function () {
  3824. this.ignore = false;
  3825. this.__zr && this.__zr.refresh();
  3826. },
  3827. /**
  3828. * @param {string|Object} key
  3829. * @param {*} value
  3830. */
  3831. attr: function (key, value) {
  3832. if (typeof key === 'string') {
  3833. this.attrKV(key, value);
  3834. }
  3835. else if (isObject$1(key)) {
  3836. for (var name in key) {
  3837. if (key.hasOwnProperty(name)) {
  3838. this.attrKV(name, key[name]);
  3839. }
  3840. }
  3841. }
  3842. this.dirty(false);
  3843. return this;
  3844. },
  3845. /**
  3846. * @param {module:zrender/graphic/Path} clipPath
  3847. */
  3848. setClipPath: function (clipPath) {
  3849. var zr = this.__zr;
  3850. if (zr) {
  3851. clipPath.addSelfToZr(zr);
  3852. }
  3853. // Remove previous clip path
  3854. if (this.clipPath && this.clipPath !== clipPath) {
  3855. this.removeClipPath();
  3856. }
  3857. this.clipPath = clipPath;
  3858. clipPath.__zr = zr;
  3859. clipPath.__clipTarget = this;
  3860. this.dirty(false);
  3861. },
  3862. /**
  3863. */
  3864. removeClipPath: function () {
  3865. var clipPath = this.clipPath;
  3866. if (clipPath) {
  3867. if (clipPath.__zr) {
  3868. clipPath.removeSelfFromZr(clipPath.__zr);
  3869. }
  3870. clipPath.__zr = null;
  3871. clipPath.__clipTarget = null;
  3872. this.clipPath = null;
  3873. this.dirty(false);
  3874. }
  3875. },
  3876. /**
  3877. * Add self from zrender instance.
  3878. * Not recursively because it will be invoked when element added to storage.
  3879. * @param {module:zrender/ZRender} zr
  3880. */
  3881. addSelfToZr: function (zr) {
  3882. this.__zr = zr;
  3883. // 添加动画
  3884. var animators = this.animators;
  3885. if (animators) {
  3886. for (var i = 0; i < animators.length; i++) {
  3887. zr.animation.addAnimator(animators[i]);
  3888. }
  3889. }
  3890. if (this.clipPath) {
  3891. this.clipPath.addSelfToZr(zr);
  3892. }
  3893. },
  3894. /**
  3895. * Remove self from zrender instance.
  3896. * Not recursively because it will be invoked when element added to storage.
  3897. * @param {module:zrender/ZRender} zr
  3898. */
  3899. removeSelfFromZr: function (zr) {
  3900. this.__zr = null;
  3901. // 移除动画
  3902. var animators = this.animators;
  3903. if (animators) {
  3904. for (var i = 0; i < animators.length; i++) {
  3905. zr.animation.removeAnimator(animators[i]);
  3906. }
  3907. }
  3908. if (this.clipPath) {
  3909. this.clipPath.removeSelfFromZr(zr);
  3910. }
  3911. }
  3912. };
  3913. mixin(Element, Animatable);
  3914. mixin(Element, Transformable);
  3915. mixin(Element, Eventful);
  3916. /**
  3917. * @module echarts/core/BoundingRect
  3918. */
  3919. var v2ApplyTransform = applyTransform;
  3920. var mathMin = Math.min;
  3921. var mathMax = Math.max;
  3922. /**
  3923. * @alias module:echarts/core/BoundingRect
  3924. */
  3925. function BoundingRect(x, y, width, height) {
  3926. if (width < 0) {
  3927. x = x + width;
  3928. width = -width;
  3929. }
  3930. if (height < 0) {
  3931. y = y + height;
  3932. height = -height;
  3933. }
  3934. /**
  3935. * @type {number}
  3936. */
  3937. this.x = x;
  3938. /**
  3939. * @type {number}
  3940. */
  3941. this.y = y;
  3942. /**
  3943. * @type {number}
  3944. */
  3945. this.width = width;
  3946. /**
  3947. * @type {number}
  3948. */
  3949. this.height = height;
  3950. }
  3951. BoundingRect.prototype = {
  3952. constructor: BoundingRect,
  3953. /**
  3954. * @param {module:echarts/core/BoundingRect} other
  3955. */
  3956. union: function (other) {
  3957. var x = mathMin(other.x, this.x);
  3958. var y = mathMin(other.y, this.y);
  3959. this.width = mathMax(
  3960. other.x + other.width,
  3961. this.x + this.width
  3962. ) - x;
  3963. this.height = mathMax(
  3964. other.y + other.height,
  3965. this.y + this.height
  3966. ) - y;
  3967. this.x = x;
  3968. this.y = y;
  3969. },
  3970. /**
  3971. * @param {Array.<number>} m
  3972. * @methods
  3973. */
  3974. applyTransform: (function () {
  3975. var lt = [];
  3976. var rb = [];
  3977. var lb = [];
  3978. var rt = [];
  3979. return function (m) {
  3980. // In case usage like this
  3981. // el.getBoundingRect().applyTransform(el.transform)
  3982. // And element has no transform
  3983. if (!m) {
  3984. return;
  3985. }
  3986. lt[0] = lb[0] = this.x;
  3987. lt[1] = rt[1] = this.y;
  3988. rb[0] = rt[0] = this.x + this.width;
  3989. rb[1] = lb[1] = this.y + this.height;
  3990. v2ApplyTransform(lt, lt, m);
  3991. v2ApplyTransform(rb, rb, m);
  3992. v2ApplyTransform(lb, lb, m);
  3993. v2ApplyTransform(rt, rt, m);
  3994. this.x = mathMin(lt[0], rb[0], lb[0], rt[0]);
  3995. this.y = mathMin(lt[1], rb[1], lb[1], rt[1]);
  3996. var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]);
  3997. var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]);
  3998. this.width = maxX - this.x;
  3999. this.height = maxY - this.y;
  4000. };
  4001. })(),
  4002. /**
  4003. * Calculate matrix of transforming from self to target rect
  4004. * @param {module:zrender/core/BoundingRect} b
  4005. * @return {Array.<number>}
  4006. */
  4007. calculateTransform: function (b) {
  4008. var a = this;
  4009. var sx = b.width / a.width;
  4010. var sy = b.height / a.height;
  4011. var m = create$1();
  4012. // 矩阵右乘
  4013. translate(m, m, [-a.x, -a.y]);
  4014. scale$1(m, m, [sx, sy]);
  4015. translate(m, m, [b.x, b.y]);
  4016. return m;
  4017. },
  4018. /**
  4019. * @param {(module:echarts/core/BoundingRect|Object)} b
  4020. * @return {boolean}
  4021. */
  4022. intersect: function (b) {
  4023. if (!b) {
  4024. return false;
  4025. }
  4026. if (!(b instanceof BoundingRect)) {
  4027. // Normalize negative width/height.
  4028. b = BoundingRect.create(b);
  4029. }
  4030. var a = this;
  4031. var ax0 = a.x;
  4032. var ax1 = a.x + a.width;
  4033. var ay0 = a.y;
  4034. var ay1 = a.y + a.height;
  4035. var bx0 = b.x;
  4036. var bx1 = b.x + b.width;
  4037. var by0 = b.y;
  4038. var by1 = b.y + b.height;
  4039. return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
  4040. },
  4041. contain: function (x, y) {
  4042. var rect = this;
  4043. return x >= rect.x
  4044. && x <= (rect.x + rect.width)
  4045. && y >= rect.y
  4046. && y <= (rect.y + rect.height);
  4047. },
  4048. /**
  4049. * @return {module:echarts/core/BoundingRect}
  4050. */
  4051. clone: function () {
  4052. return new BoundingRect(this.x, this.y, this.width, this.height);
  4053. },
  4054. /**
  4055. * Copy from another rect
  4056. */
  4057. copy: function (other) {
  4058. this.x = other.x;
  4059. this.y = other.y;
  4060. this.width = other.width;
  4061. this.height = other.height;
  4062. },
  4063. plain: function () {
  4064. return {
  4065. x: this.x,
  4066. y: this.y,
  4067. width: this.width,
  4068. height: this.height
  4069. };
  4070. }
  4071. };
  4072. /**
  4073. * @param {Object|module:zrender/core/BoundingRect} rect
  4074. * @param {number} rect.x
  4075. * @param {number} rect.y
  4076. * @param {number} rect.width
  4077. * @param {number} rect.height
  4078. * @return {module:zrender/core/BoundingRect}
  4079. */
  4080. BoundingRect.create = function (rect) {
  4081. return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
  4082. };
  4083. /**
  4084. * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
  4085. * @module zrender/graphic/Group
  4086. * @example
  4087. * var Group = require('zrender/container/Group');
  4088. * var Circle = require('zrender/graphic/shape/Circle');
  4089. * var g = new Group();
  4090. * g.position[0] = 100;
  4091. * g.position[1] = 100;
  4092. * g.add(new Circle({
  4093. * style: {
  4094. * x: 100,
  4095. * y: 100,
  4096. * r: 20,
  4097. * }
  4098. * }));
  4099. * zr.add(g);
  4100. */
  4101. /**
  4102. * @alias module:zrender/graphic/Group
  4103. * @constructor
  4104. * @extends module:zrender/mixin/Transformable
  4105. * @extends module:zrender/mixin/Eventful
  4106. */
  4107. var Group = function (opts) {
  4108. opts = opts || {};
  4109. Element.call(this, opts);
  4110. for (var key in opts) {
  4111. if (opts.hasOwnProperty(key)) {
  4112. this[key] = opts[key];
  4113. }
  4114. }
  4115. this._children = [];
  4116. this.__storage = null;
  4117. this.__dirty = true;
  4118. };
  4119. Group.prototype = {
  4120. constructor: Group,
  4121. isGroup: true,
  4122. /**
  4123. * @type {string}
  4124. */
  4125. type: 'group',
  4126. /**
  4127. * 所有子孙元素是否响应鼠标事件
  4128. * @name module:/zrender/container/Group#silent
  4129. * @type {boolean}
  4130. * @default false
  4131. */
  4132. silent: false,
  4133. /**
  4134. * @return {Array.<module:zrender/Element>}
  4135. */
  4136. children: function () {
  4137. return this._children.slice();
  4138. },
  4139. /**
  4140. * 获取指定 index 的儿子节点
  4141. * @param {number} idx
  4142. * @return {module:zrender/Element}
  4143. */
  4144. childAt: function (idx) {
  4145. return this._children[idx];
  4146. },
  4147. /**
  4148. * 获取指定名字的儿子节点
  4149. * @param {string} name
  4150. * @return {module:zrender/Element}
  4151. */
  4152. childOfName: function (name) {
  4153. var children = this._children;
  4154. for (var i = 0; i < children.length; i++) {
  4155. if (children[i].name === name) {
  4156. return children[i];
  4157. }
  4158. }
  4159. },
  4160. /**
  4161. * @return {number}
  4162. */
  4163. childCount: function () {
  4164. return this._children.length;
  4165. },
  4166. /**
  4167. * 添加子节点到最后
  4168. * @param {module:zrender/Element} child
  4169. */
  4170. add: function (child) {
  4171. if (child && child !== this && child.parent !== this) {
  4172. this._children.push(child);
  4173. this._doAdd(child);
  4174. }
  4175. return this;
  4176. },
  4177. /**
  4178. * 添加子节点在 nextSibling 之前
  4179. * @param {module:zrender/Element} child
  4180. * @param {module:zrender/Element} nextSibling
  4181. */
  4182. addBefore: function (child, nextSibling) {
  4183. if (child && child !== this && child.parent !== this
  4184. && nextSibling && nextSibling.parent === this) {
  4185. var children = this._children;
  4186. var idx = children.indexOf(nextSibling);
  4187. if (idx >= 0) {
  4188. children.splice(idx, 0, child);
  4189. this._doAdd(child);
  4190. }
  4191. }
  4192. return this;
  4193. },
  4194. _doAdd: function (child) {
  4195. if (child.parent) {
  4196. child.parent.remove(child);
  4197. }
  4198. child.parent = this;
  4199. var storage = this.__storage;
  4200. var zr = this.__zr;
  4201. if (storage && storage !== child.__storage) {
  4202. storage.addToStorage(child);
  4203. if (child instanceof Group) {
  4204. child.addChildrenToStorage(storage);
  4205. }
  4206. }
  4207. zr && zr.refresh();
  4208. },
  4209. /**
  4210. * 移除子节点
  4211. * @param {module:zrender/Element} child
  4212. */
  4213. remove: function (child) {
  4214. var zr = this.__zr;
  4215. var storage = this.__storage;
  4216. var children = this._children;
  4217. var idx = indexOf(children, child);
  4218. if (idx < 0) {
  4219. return this;
  4220. }
  4221. children.splice(idx, 1);
  4222. child.parent = null;
  4223. if (storage) {
  4224. storage.delFromStorage(child);
  4225. if (child instanceof Group) {
  4226. child.delChildrenFromStorage(storage);
  4227. }
  4228. }
  4229. zr && zr.refresh();
  4230. return this;
  4231. },
  4232. /**
  4233. * 移除所有子节点
  4234. */
  4235. removeAll: function () {
  4236. var children = this._children;
  4237. var storage = this.__storage;
  4238. var child;
  4239. var i;
  4240. for (i = 0; i < children.length; i++) {
  4241. child = children[i];
  4242. if (storage) {
  4243. storage.delFromStorage(child);
  4244. if (child instanceof Group) {
  4245. child.delChildrenFromStorage(storage);
  4246. }
  4247. }
  4248. child.parent = null;
  4249. }
  4250. children.length = 0;
  4251. return this;
  4252. },
  4253. /**
  4254. * 遍历所有子节点
  4255. * @param {Function} cb
  4256. * @param {} context
  4257. */
  4258. eachChild: function (cb, context) {
  4259. var children = this._children;
  4260. for (var i = 0; i < children.length; i++) {
  4261. var child = children[i];
  4262. cb.call(context, child, i);
  4263. }
  4264. return this;
  4265. },
  4266. /**
  4267. * 深度优先遍历所有子孙节点
  4268. * @param {Function} cb
  4269. * @param {} context
  4270. */
  4271. traverse: function (cb, context) {
  4272. for (var i = 0; i < this._children.length; i++) {
  4273. var child = this._children[i];
  4274. cb.call(context, child);
  4275. if (child.type === 'group') {
  4276. child.traverse(cb, context);
  4277. }
  4278. }
  4279. return this;
  4280. },
  4281. addChildrenToStorage: function (storage) {
  4282. for (var i = 0; i < this._children.length; i++) {
  4283. var child = this._children[i];
  4284. storage.addToStorage(child);
  4285. if (child instanceof Group) {
  4286. child.addChildrenToStorage(storage);
  4287. }
  4288. }
  4289. },
  4290. delChildrenFromStorage: function (storage) {
  4291. for (var i = 0; i < this._children.length; i++) {
  4292. var child = this._children[i];
  4293. storage.delFromStorage(child);
  4294. if (child instanceof Group) {
  4295. child.delChildrenFromStorage(storage);
  4296. }
  4297. }
  4298. },
  4299. dirty: function () {
  4300. this.__dirty = true;
  4301. this.__zr && this.__zr.refresh();
  4302. return this;
  4303. },
  4304. /**
  4305. * @return {module:zrender/core/BoundingRect}
  4306. */
  4307. getBoundingRect: function (includeChildren) {
  4308. // TODO Caching
  4309. var rect = null;
  4310. var tmpRect = new BoundingRect(0, 0, 0, 0);
  4311. var children = includeChildren || this._children;
  4312. var tmpMat = [];
  4313. for (var i = 0; i < children.length; i++) {
  4314. var child = children[i];
  4315. if (child.ignore || child.invisible) {
  4316. continue;
  4317. }
  4318. var childRect = child.getBoundingRect();
  4319. var transform = child.getLocalTransform(tmpMat);
  4320. // TODO
  4321. // The boundingRect cacluated by transforming original
  4322. // rect may be bigger than the actual bundingRect when rotation
  4323. // is used. (Consider a circle rotated aginst its center, where
  4324. // the actual boundingRect should be the same as that not be
  4325. // rotated.) But we can not find better approach to calculate
  4326. // actual boundingRect yet, considering performance.
  4327. if (transform) {
  4328. tmpRect.copy(childRect);
  4329. tmpRect.applyTransform(transform);
  4330. rect = rect || tmpRect.clone();
  4331. rect.union(tmpRect);
  4332. }
  4333. else {
  4334. rect = rect || childRect.clone();
  4335. rect.union(childRect);
  4336. }
  4337. }
  4338. return rect || tmpRect;
  4339. }
  4340. };
  4341. inherits(Group, Element);
  4342. // https://github.com/mziccard/node-timsort
  4343. var DEFAULT_MIN_MERGE = 32;
  4344. var DEFAULT_MIN_GALLOPING = 7;
  4345. function minRunLength(n) {
  4346. var r = 0;
  4347. while (n >= DEFAULT_MIN_MERGE) {
  4348. r |= n & 1;
  4349. n >>= 1;
  4350. }
  4351. return n + r;
  4352. }
  4353. function makeAscendingRun(array, lo, hi, compare) {
  4354. var runHi = lo + 1;
  4355. if (runHi === hi) {
  4356. return 1;
  4357. }
  4358. if (compare(array[runHi++], array[lo]) < 0) {
  4359. while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
  4360. runHi++;
  4361. }
  4362. reverseRun(array, lo, runHi);
  4363. }
  4364. else {
  4365. while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
  4366. runHi++;
  4367. }
  4368. }
  4369. return runHi - lo;
  4370. }
  4371. function reverseRun(array, lo, hi) {
  4372. hi--;
  4373. while (lo < hi) {
  4374. var t = array[lo];
  4375. array[lo++] = array[hi];
  4376. array[hi--] = t;
  4377. }
  4378. }
  4379. function binaryInsertionSort(array, lo, hi, start, compare) {
  4380. if (start === lo) {
  4381. start++;
  4382. }
  4383. for (; start < hi; start++) {
  4384. var pivot = array[start];
  4385. var left = lo;
  4386. var right = start;
  4387. var mid;
  4388. while (left < right) {
  4389. mid = left + right >>> 1;
  4390. if (compare(pivot, array[mid]) < 0) {
  4391. right = mid;
  4392. }
  4393. else {
  4394. left = mid + 1;
  4395. }
  4396. }
  4397. var n = start - left;
  4398. switch (n) {
  4399. case 3:
  4400. array[left + 3] = array[left + 2];
  4401. case 2:
  4402. array[left + 2] = array[left + 1];
  4403. case 1:
  4404. array[left + 1] = array[left];
  4405. break;
  4406. default:
  4407. while (n > 0) {
  4408. array[left + n] = array[left + n - 1];
  4409. n--;
  4410. }
  4411. }
  4412. array[left] = pivot;
  4413. }
  4414. }
  4415. function gallopLeft(value, array, start, length, hint, compare) {
  4416. var lastOffset = 0;
  4417. var maxOffset = 0;
  4418. var offset = 1;
  4419. if (compare(value, array[start + hint]) > 0) {
  4420. maxOffset = length - hint;
  4421. while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
  4422. lastOffset = offset;
  4423. offset = (offset << 1) + 1;
  4424. if (offset <= 0) {
  4425. offset = maxOffset;
  4426. }
  4427. }
  4428. if (offset > maxOffset) {
  4429. offset = maxOffset;
  4430. }
  4431. lastOffset += hint;
  4432. offset += hint;
  4433. }
  4434. else {
  4435. maxOffset = hint + 1;
  4436. while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
  4437. lastOffset = offset;
  4438. offset = (offset << 1) + 1;
  4439. if (offset <= 0) {
  4440. offset = maxOffset;
  4441. }
  4442. }
  4443. if (offset > maxOffset) {
  4444. offset = maxOffset;
  4445. }
  4446. var tmp = lastOffset;
  4447. lastOffset = hint - offset;
  4448. offset = hint - tmp;
  4449. }
  4450. lastOffset++;
  4451. while (lastOffset < offset) {
  4452. var m = lastOffset + (offset - lastOffset >>> 1);
  4453. if (compare(value, array[start + m]) > 0) {
  4454. lastOffset = m + 1;
  4455. }
  4456. else {
  4457. offset = m;
  4458. }
  4459. }
  4460. return offset;
  4461. }
  4462. function gallopRight(value, array, start, length, hint, compare) {
  4463. var lastOffset = 0;
  4464. var maxOffset = 0;
  4465. var offset = 1;
  4466. if (compare(value, array[start + hint]) < 0) {
  4467. maxOffset = hint + 1;
  4468. while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
  4469. lastOffset = offset;
  4470. offset = (offset << 1) + 1;
  4471. if (offset <= 0) {
  4472. offset = maxOffset;
  4473. }
  4474. }
  4475. if (offset > maxOffset) {
  4476. offset = maxOffset;
  4477. }
  4478. var tmp = lastOffset;
  4479. lastOffset = hint - offset;
  4480. offset = hint - tmp;
  4481. }
  4482. else {
  4483. maxOffset = length - hint;
  4484. while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
  4485. lastOffset = offset;
  4486. offset = (offset << 1) + 1;
  4487. if (offset <= 0) {
  4488. offset = maxOffset;
  4489. }
  4490. }
  4491. if (offset > maxOffset) {
  4492. offset = maxOffset;
  4493. }
  4494. lastOffset += hint;
  4495. offset += hint;
  4496. }
  4497. lastOffset++;
  4498. while (lastOffset < offset) {
  4499. var m = lastOffset + (offset - lastOffset >>> 1);
  4500. if (compare(value, array[start + m]) < 0) {
  4501. offset = m;
  4502. }
  4503. else {
  4504. lastOffset = m + 1;
  4505. }
  4506. }
  4507. return offset;
  4508. }
  4509. function TimSort(array, compare) {
  4510. var minGallop = DEFAULT_MIN_GALLOPING;
  4511. var runStart;
  4512. var runLength;
  4513. var stackSize = 0;
  4514. var tmp = [];
  4515. runStart = [];
  4516. runLength = [];
  4517. function pushRun(_runStart, _runLength) {
  4518. runStart[stackSize] = _runStart;
  4519. runLength[stackSize] = _runLength;
  4520. stackSize += 1;
  4521. }
  4522. function mergeRuns() {
  4523. while (stackSize > 1) {
  4524. var n = stackSize - 2;
  4525. if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) {
  4526. if (runLength[n - 1] < runLength[n + 1]) {
  4527. n--;
  4528. }
  4529. }
  4530. else if (runLength[n] > runLength[n + 1]) {
  4531. break;
  4532. }
  4533. mergeAt(n);
  4534. }
  4535. }
  4536. function forceMergeRuns() {
  4537. while (stackSize > 1) {
  4538. var n = stackSize - 2;
  4539. if (n > 0 && runLength[n - 1] < runLength[n + 1]) {
  4540. n--;
  4541. }
  4542. mergeAt(n);
  4543. }
  4544. }
  4545. function mergeAt(i) {
  4546. var start1 = runStart[i];
  4547. var length1 = runLength[i];
  4548. var start2 = runStart[i + 1];
  4549. var length2 = runLength[i + 1];
  4550. runLength[i] = length1 + length2;
  4551. if (i === stackSize - 3) {
  4552. runStart[i + 1] = runStart[i + 2];
  4553. runLength[i + 1] = runLength[i + 2];
  4554. }
  4555. stackSize--;
  4556. var k = gallopRight(array[start2], array, start1, length1, 0, compare);
  4557. start1 += k;
  4558. length1 -= k;
  4559. if (length1 === 0) {
  4560. return;
  4561. }
  4562. length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
  4563. if (length2 === 0) {
  4564. return;
  4565. }
  4566. if (length1 <= length2) {
  4567. mergeLow(start1, length1, start2, length2);
  4568. }
  4569. else {
  4570. mergeHigh(start1, length1, start2, length2);
  4571. }
  4572. }
  4573. function mergeLow(start1, length1, start2, length2) {
  4574. var i = 0;
  4575. for (i = 0; i < length1; i++) {
  4576. tmp[i] = array[start1 + i];
  4577. }
  4578. var cursor1 = 0;
  4579. var cursor2 = start2;
  4580. var dest = start1;
  4581. array[dest++] = array[cursor2++];
  4582. if (--length2 === 0) {
  4583. for (i = 0; i < length1; i++) {
  4584. array[dest + i] = tmp[cursor1 + i];
  4585. }
  4586. return;
  4587. }
  4588. if (length1 === 1) {
  4589. for (i = 0; i < length2; i++) {
  4590. array[dest + i] = array[cursor2 + i];
  4591. }
  4592. array[dest + length2] = tmp[cursor1];
  4593. return;
  4594. }
  4595. var _minGallop = minGallop;
  4596. var count1, count2, exit;
  4597. while (1) {
  4598. count1 = 0;
  4599. count2 = 0;
  4600. exit = false;
  4601. do {
  4602. if (compare(array[cursor2], tmp[cursor1]) < 0) {
  4603. array[dest++] = array[cursor2++];
  4604. count2++;
  4605. count1 = 0;
  4606. if (--length2 === 0) {
  4607. exit = true;
  4608. break;
  4609. }
  4610. }
  4611. else {
  4612. array[dest++] = tmp[cursor1++];
  4613. count1++;
  4614. count2 = 0;
  4615. if (--length1 === 1) {
  4616. exit = true;
  4617. break;
  4618. }
  4619. }
  4620. } while ((count1 | count2) < _minGallop);
  4621. if (exit) {
  4622. break;
  4623. }
  4624. do {
  4625. count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
  4626. if (count1 !== 0) {
  4627. for (i = 0; i < count1; i++) {
  4628. array[dest + i] = tmp[cursor1 + i];
  4629. }
  4630. dest += count1;
  4631. cursor1 += count1;
  4632. length1 -= count1;
  4633. if (length1 <= 1) {
  4634. exit = true;
  4635. break;
  4636. }
  4637. }
  4638. array[dest++] = array[cursor2++];
  4639. if (--length2 === 0) {
  4640. exit = true;
  4641. break;
  4642. }
  4643. count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
  4644. if (count2 !== 0) {
  4645. for (i = 0; i < count2; i++) {
  4646. array[dest + i] = array[cursor2 + i];
  4647. }
  4648. dest += count2;
  4649. cursor2 += count2;
  4650. length2 -= count2;
  4651. if (length2 === 0) {
  4652. exit = true;
  4653. break;
  4654. }
  4655. }
  4656. array[dest++] = tmp[cursor1++];
  4657. if (--length1 === 1) {
  4658. exit = true;
  4659. break;
  4660. }
  4661. _minGallop--;
  4662. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  4663. if (exit) {
  4664. break;
  4665. }
  4666. if (_minGallop < 0) {
  4667. _minGallop = 0;
  4668. }
  4669. _minGallop += 2;
  4670. }
  4671. minGallop = _minGallop;
  4672. minGallop < 1 && (minGallop = 1);
  4673. if (length1 === 1) {
  4674. for (i = 0; i < length2; i++) {
  4675. array[dest + i] = array[cursor2 + i];
  4676. }
  4677. array[dest + length2] = tmp[cursor1];
  4678. }
  4679. else if (length1 === 0) {
  4680. throw new Error();
  4681. // throw new Error('mergeLow preconditions were not respected');
  4682. }
  4683. else {
  4684. for (i = 0; i < length1; i++) {
  4685. array[dest + i] = tmp[cursor1 + i];
  4686. }
  4687. }
  4688. }
  4689. function mergeHigh (start1, length1, start2, length2) {
  4690. var i = 0;
  4691. for (i = 0; i < length2; i++) {
  4692. tmp[i] = array[start2 + i];
  4693. }
  4694. var cursor1 = start1 + length1 - 1;
  4695. var cursor2 = length2 - 1;
  4696. var dest = start2 + length2 - 1;
  4697. var customCursor = 0;
  4698. var customDest = 0;
  4699. array[dest--] = array[cursor1--];
  4700. if (--length1 === 0) {
  4701. customCursor = dest - (length2 - 1);
  4702. for (i = 0; i < length2; i++) {
  4703. array[customCursor + i] = tmp[i];
  4704. }
  4705. return;
  4706. }
  4707. if (length2 === 1) {
  4708. dest -= length1;
  4709. cursor1 -= length1;
  4710. customDest = dest + 1;
  4711. customCursor = cursor1 + 1;
  4712. for (i = length1 - 1; i >= 0; i--) {
  4713. array[customDest + i] = array[customCursor + i];
  4714. }
  4715. array[dest] = tmp[cursor2];
  4716. return;
  4717. }
  4718. var _minGallop = minGallop;
  4719. while (true) {
  4720. var count1 = 0;
  4721. var count2 = 0;
  4722. var exit = false;
  4723. do {
  4724. if (compare(tmp[cursor2], array[cursor1]) < 0) {
  4725. array[dest--] = array[cursor1--];
  4726. count1++;
  4727. count2 = 0;
  4728. if (--length1 === 0) {
  4729. exit = true;
  4730. break;
  4731. }
  4732. }
  4733. else {
  4734. array[dest--] = tmp[cursor2--];
  4735. count2++;
  4736. count1 = 0;
  4737. if (--length2 === 1) {
  4738. exit = true;
  4739. break;
  4740. }
  4741. }
  4742. } while ((count1 | count2) < _minGallop);
  4743. if (exit) {
  4744. break;
  4745. }
  4746. do {
  4747. count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
  4748. if (count1 !== 0) {
  4749. dest -= count1;
  4750. cursor1 -= count1;
  4751. length1 -= count1;
  4752. customDest = dest + 1;
  4753. customCursor = cursor1 + 1;
  4754. for (i = count1 - 1; i >= 0; i--) {
  4755. array[customDest + i] = array[customCursor + i];
  4756. }
  4757. if (length1 === 0) {
  4758. exit = true;
  4759. break;
  4760. }
  4761. }
  4762. array[dest--] = tmp[cursor2--];
  4763. if (--length2 === 1) {
  4764. exit = true;
  4765. break;
  4766. }
  4767. count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
  4768. if (count2 !== 0) {
  4769. dest -= count2;
  4770. cursor2 -= count2;
  4771. length2 -= count2;
  4772. customDest = dest + 1;
  4773. customCursor = cursor2 + 1;
  4774. for (i = 0; i < count2; i++) {
  4775. array[customDest + i] = tmp[customCursor + i];
  4776. }
  4777. if (length2 <= 1) {
  4778. exit = true;
  4779. break;
  4780. }
  4781. }
  4782. array[dest--] = array[cursor1--];
  4783. if (--length1 === 0) {
  4784. exit = true;
  4785. break;
  4786. }
  4787. _minGallop--;
  4788. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  4789. if (exit) {
  4790. break;
  4791. }
  4792. if (_minGallop < 0) {
  4793. _minGallop = 0;
  4794. }
  4795. _minGallop += 2;
  4796. }
  4797. minGallop = _minGallop;
  4798. if (minGallop < 1) {
  4799. minGallop = 1;
  4800. }
  4801. if (length2 === 1) {
  4802. dest -= length1;
  4803. cursor1 -= length1;
  4804. customDest = dest + 1;
  4805. customCursor = cursor1 + 1;
  4806. for (i = length1 - 1; i >= 0; i--) {
  4807. array[customDest + i] = array[customCursor + i];
  4808. }
  4809. array[dest] = tmp[cursor2];
  4810. }
  4811. else if (length2 === 0) {
  4812. throw new Error();
  4813. // throw new Error('mergeHigh preconditions were not respected');
  4814. }
  4815. else {
  4816. customCursor = dest - (length2 - 1);
  4817. for (i = 0; i < length2; i++) {
  4818. array[customCursor + i] = tmp[i];
  4819. }
  4820. }
  4821. }
  4822. this.mergeRuns = mergeRuns;
  4823. this.forceMergeRuns = forceMergeRuns;
  4824. this.pushRun = pushRun;
  4825. }
  4826. function sort(array, compare, lo, hi) {
  4827. if (!lo) {
  4828. lo = 0;
  4829. }
  4830. if (!hi) {
  4831. hi = array.length;
  4832. }
  4833. var remaining = hi - lo;
  4834. if (remaining < 2) {
  4835. return;
  4836. }
  4837. var runLength = 0;
  4838. if (remaining < DEFAULT_MIN_MERGE) {
  4839. runLength = makeAscendingRun(array, lo, hi, compare);
  4840. binaryInsertionSort(array, lo, hi, lo + runLength, compare);
  4841. return;
  4842. }
  4843. var ts = new TimSort(array, compare);
  4844. var minRun = minRunLength(remaining);
  4845. do {
  4846. runLength = makeAscendingRun(array, lo, hi, compare);
  4847. if (runLength < minRun) {
  4848. var force = remaining;
  4849. if (force > minRun) {
  4850. force = minRun;
  4851. }
  4852. binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
  4853. runLength = force;
  4854. }
  4855. ts.pushRun(lo, runLength);
  4856. ts.mergeRuns();
  4857. remaining -= runLength;
  4858. lo += runLength;
  4859. } while (remaining !== 0);
  4860. ts.forceMergeRuns();
  4861. }
  4862. // Use timsort because in most case elements are partially sorted
  4863. // https://jsfiddle.net/pissang/jr4x7mdm/8/
  4864. function shapeCompareFunc(a, b) {
  4865. if (a.zlevel === b.zlevel) {
  4866. if (a.z === b.z) {
  4867. // if (a.z2 === b.z2) {
  4868. // // FIXME Slow has renderidx compare
  4869. // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
  4870. // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
  4871. // return a.__renderidx - b.__renderidx;
  4872. // }
  4873. return a.z2 - b.z2;
  4874. }
  4875. return a.z - b.z;
  4876. }
  4877. return a.zlevel - b.zlevel;
  4878. }
  4879. /**
  4880. * 内容仓库 (M)
  4881. * @alias module:zrender/Storage
  4882. * @constructor
  4883. */
  4884. var Storage = function () { // jshint ignore:line
  4885. this._roots = [];
  4886. this._displayList = [];
  4887. this._displayListLen = 0;
  4888. };
  4889. Storage.prototype = {
  4890. constructor: Storage,
  4891. /**
  4892. * @param {Function} cb
  4893. *
  4894. */
  4895. traverse: function (cb, context) {
  4896. for (var i = 0; i < this._roots.length; i++) {
  4897. this._roots[i].traverse(cb, context);
  4898. }
  4899. },
  4900. /**
  4901. * 返回所有图形的绘制队列
  4902. * @param {boolean} [update=false] 是否在返回前更新该数组
  4903. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效
  4904. *
  4905. * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
  4906. * @return {Array.<module:zrender/graphic/Displayable>}
  4907. */
  4908. getDisplayList: function (update, includeIgnore) {
  4909. includeIgnore = includeIgnore || false;
  4910. if (update) {
  4911. this.updateDisplayList(includeIgnore);
  4912. }
  4913. return this._displayList;
  4914. },
  4915. /**
  4916. * 更新图形的绘制队列。
  4917. * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
  4918. * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
  4919. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组
  4920. */
  4921. updateDisplayList: function (includeIgnore) {
  4922. this._displayListLen = 0;
  4923. var roots = this._roots;
  4924. var displayList = this._displayList;
  4925. for (var i = 0, len = roots.length; i < len; i++) {
  4926. this._updateAndAddDisplayable(roots[i], null, includeIgnore);
  4927. }
  4928. displayList.length = this._displayListLen;
  4929. env$1.canvasSupported && sort(displayList, shapeCompareFunc);
  4930. },
  4931. _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
  4932. if (el.ignore && !includeIgnore) {
  4933. return;
  4934. }
  4935. el.beforeUpdate();
  4936. if (el.__dirty) {
  4937. el.update();
  4938. }
  4939. el.afterUpdate();
  4940. var userSetClipPath = el.clipPath;
  4941. if (userSetClipPath) {
  4942. // FIXME 效率影响
  4943. if (clipPaths) {
  4944. clipPaths = clipPaths.slice();
  4945. }
  4946. else {
  4947. clipPaths = [];
  4948. }
  4949. var currentClipPath = userSetClipPath;
  4950. var parentClipPath = el;
  4951. // Recursively add clip path
  4952. while (currentClipPath) {
  4953. // clipPath 的变换是基于使用这个 clipPath 的元素
  4954. currentClipPath.parent = parentClipPath;
  4955. currentClipPath.updateTransform();
  4956. clipPaths.push(currentClipPath);
  4957. parentClipPath = currentClipPath;
  4958. currentClipPath = currentClipPath.clipPath;
  4959. }
  4960. }
  4961. if (el.isGroup) {
  4962. var children = el._children;
  4963. for (var i = 0; i < children.length; i++) {
  4964. var child = children[i];
  4965. // Force to mark as dirty if group is dirty
  4966. // FIXME __dirtyPath ?
  4967. if (el.__dirty) {
  4968. child.__dirty = true;
  4969. }
  4970. this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
  4971. }
  4972. // Mark group clean here
  4973. el.__dirty = false;
  4974. }
  4975. else {
  4976. el.__clipPaths = clipPaths;
  4977. this._displayList[this._displayListLen++] = el;
  4978. }
  4979. },
  4980. /**
  4981. * 添加图形(Shape)或者组(Group)到根节点
  4982. * @param {module:zrender/Element} el
  4983. */
  4984. addRoot: function (el) {
  4985. if (el.__storage === this) {
  4986. return;
  4987. }
  4988. if (el instanceof Group) {
  4989. el.addChildrenToStorage(this);
  4990. }
  4991. this.addToStorage(el);
  4992. this._roots.push(el);
  4993. },
  4994. /**
  4995. * 删除指定的图形(Shape)或者组(Group)
  4996. * @param {string|Array.<string>} [el] 如果为空清空整个Storage
  4997. */
  4998. delRoot: function (el) {
  4999. if (el == null) {
  5000. // 不指定el清空
  5001. for (var i = 0; i < this._roots.length; i++) {
  5002. var root = this._roots[i];
  5003. if (root instanceof Group) {
  5004. root.delChildrenFromStorage(this);
  5005. }
  5006. }
  5007. this._roots = [];
  5008. this._displayList = [];
  5009. this._displayListLen = 0;
  5010. return;
  5011. }
  5012. if (el instanceof Array) {
  5013. for (var i = 0, l = el.length; i < l; i++) {
  5014. this.delRoot(el[i]);
  5015. }
  5016. return;
  5017. }
  5018. var idx = indexOf(this._roots, el);
  5019. if (idx >= 0) {
  5020. this.delFromStorage(el);
  5021. this._roots.splice(idx, 1);
  5022. if (el instanceof Group) {
  5023. el.delChildrenFromStorage(this);
  5024. }
  5025. }
  5026. },
  5027. addToStorage: function (el) {
  5028. if (el) {
  5029. el.__storage = this;
  5030. el.dirty(false);
  5031. }
  5032. return this;
  5033. },
  5034. delFromStorage: function (el) {
  5035. if (el) {
  5036. el.__storage = null;
  5037. }
  5038. return this;
  5039. },
  5040. /**
  5041. * 清空并且释放Storage
  5042. */
  5043. dispose: function () {
  5044. this._renderList =
  5045. this._roots = null;
  5046. },
  5047. displayableSortFunc: shapeCompareFunc
  5048. };
  5049. var SHADOW_PROPS = {
  5050. 'shadowBlur': 1,
  5051. 'shadowOffsetX': 1,
  5052. 'shadowOffsetY': 1,
  5053. 'textShadowBlur': 1,
  5054. 'textShadowOffsetX': 1,
  5055. 'textShadowOffsetY': 1,
  5056. 'textBoxShadowBlur': 1,
  5057. 'textBoxShadowOffsetX': 1,
  5058. 'textBoxShadowOffsetY': 1
  5059. };
  5060. var fixShadow = function (ctx, propName, value) {
  5061. if (SHADOW_PROPS.hasOwnProperty(propName)) {
  5062. return value *= ctx.dpr;
  5063. }
  5064. return value;
  5065. };
  5066. var STYLE_COMMON_PROPS = [
  5067. ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],
  5068. ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
  5069. ];
  5070. // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
  5071. // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);
  5072. var Style = function (opts, host) {
  5073. this.extendFrom(opts, false);
  5074. this.host = host;
  5075. };
  5076. function createLinearGradient(ctx, obj, rect) {
  5077. var x = obj.x == null ? 0 : obj.x;
  5078. var x2 = obj.x2 == null ? 1 : obj.x2;
  5079. var y = obj.y == null ? 0 : obj.y;
  5080. var y2 = obj.y2 == null ? 0 : obj.y2;
  5081. if (!obj.global) {
  5082. x = x * rect.width + rect.x;
  5083. x2 = x2 * rect.width + rect.x;
  5084. y = y * rect.height + rect.y;
  5085. y2 = y2 * rect.height + rect.y;
  5086. }
  5087. var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
  5088. return canvasGradient;
  5089. }
  5090. function createRadialGradient(ctx, obj, rect) {
  5091. var width = rect.width;
  5092. var height = rect.height;
  5093. var min = Math.min(width, height);
  5094. var x = obj.x == null ? 0.5 : obj.x;
  5095. var y = obj.y == null ? 0.5 : obj.y;
  5096. var r = obj.r == null ? 0.5 : obj.r;
  5097. if (!obj.global) {
  5098. x = x * width + rect.x;
  5099. y = y * height + rect.y;
  5100. r = r * min;
  5101. }
  5102. var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
  5103. return canvasGradient;
  5104. }
  5105. Style.prototype = {
  5106. constructor: Style,
  5107. /**
  5108. * @type {module:zrender/graphic/Displayable}
  5109. */
  5110. host: null,
  5111. /**
  5112. * @type {string}
  5113. */
  5114. fill: '#000',
  5115. /**
  5116. * @type {string}
  5117. */
  5118. stroke: null,
  5119. /**
  5120. * @type {number}
  5121. */
  5122. opacity: 1,
  5123. /**
  5124. * @type {Array.<number>}
  5125. */
  5126. lineDash: null,
  5127. /**
  5128. * @type {number}
  5129. */
  5130. lineDashOffset: 0,
  5131. /**
  5132. * @type {number}
  5133. */
  5134. shadowBlur: 0,
  5135. /**
  5136. * @type {number}
  5137. */
  5138. shadowOffsetX: 0,
  5139. /**
  5140. * @type {number}
  5141. */
  5142. shadowOffsetY: 0,
  5143. /**
  5144. * @type {number}
  5145. */
  5146. lineWidth: 1,
  5147. /**
  5148. * If stroke ignore scale
  5149. * @type {Boolean}
  5150. */
  5151. strokeNoScale: false,
  5152. // Bounding rect text configuration
  5153. // Not affected by element transform
  5154. /**
  5155. * @type {string}
  5156. */
  5157. text: null,
  5158. /**
  5159. * If `fontSize` or `fontFamily` exists, `font` will be reset by
  5160. * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
  5161. * So do not visit it directly in upper application (like echarts),
  5162. * but use `contain/text#makeFont` instead.
  5163. * @type {string}
  5164. */
  5165. font: null,
  5166. /**
  5167. * The same as font. Use font please.
  5168. * @deprecated
  5169. * @type {string}
  5170. */
  5171. textFont: null,
  5172. /**
  5173. * It helps merging respectively, rather than parsing an entire font string.
  5174. * @type {string}
  5175. */
  5176. fontStyle: null,
  5177. /**
  5178. * It helps merging respectively, rather than parsing an entire font string.
  5179. * @type {string}
  5180. */
  5181. fontWeight: null,
  5182. /**
  5183. * It helps merging respectively, rather than parsing an entire font string.
  5184. * Should be 12 but not '12px'.
  5185. * @type {number}
  5186. */
  5187. fontSize: null,
  5188. /**
  5189. * It helps merging respectively, rather than parsing an entire font string.
  5190. * @type {string}
  5191. */
  5192. fontFamily: null,
  5193. /**
  5194. * Reserved for special functinality, like 'hr'.
  5195. * @type {string}
  5196. */
  5197. textTag: null,
  5198. /**
  5199. * @type {string}
  5200. */
  5201. textFill: '#000',
  5202. /**
  5203. * @type {string}
  5204. */
  5205. textStroke: null,
  5206. /**
  5207. * @type {number}
  5208. */
  5209. textWidth: null,
  5210. /**
  5211. * Only for textBackground.
  5212. * @type {number}
  5213. */
  5214. textHeight: null,
  5215. /**
  5216. * textStroke may be set as some color as a default
  5217. * value in upper applicaion, where the default value
  5218. * of textStrokeWidth should be 0 to make sure that
  5219. * user can choose to do not use text stroke.
  5220. * @type {number}
  5221. */
  5222. textStrokeWidth: 0,
  5223. /**
  5224. * @type {number}
  5225. */
  5226. textLineHeight: null,
  5227. /**
  5228. * 'inside', 'left', 'right', 'top', 'bottom'
  5229. * [x, y]
  5230. * Based on x, y of rect.
  5231. * @type {string|Array.<number>}
  5232. * @default 'inside'
  5233. */
  5234. textPosition: 'inside',
  5235. /**
  5236. * If not specified, use the boundingRect of a `displayable`.
  5237. * @type {Object}
  5238. */
  5239. textRect: null,
  5240. /**
  5241. * [x, y]
  5242. * @type {Array.<number>}
  5243. */
  5244. textOffset: null,
  5245. /**
  5246. * @type {string}
  5247. */
  5248. textAlign: null,
  5249. /**
  5250. * @type {string}
  5251. */
  5252. textVerticalAlign: null,
  5253. /**
  5254. * @type {number}
  5255. */
  5256. textDistance: 5,
  5257. /**
  5258. * @type {string}
  5259. */
  5260. textShadowColor: 'transparent',
  5261. /**
  5262. * @type {number}
  5263. */
  5264. textShadowBlur: 0,
  5265. /**
  5266. * @type {number}
  5267. */
  5268. textShadowOffsetX: 0,
  5269. /**
  5270. * @type {number}
  5271. */
  5272. textShadowOffsetY: 0,
  5273. /**
  5274. * @type {string}
  5275. */
  5276. textBoxShadowColor: 'transparent',
  5277. /**
  5278. * @type {number}
  5279. */
  5280. textBoxShadowBlur: 0,
  5281. /**
  5282. * @type {number}
  5283. */
  5284. textBoxShadowOffsetX: 0,
  5285. /**
  5286. * @type {number}
  5287. */
  5288. textBoxShadowOffsetY: 0,
  5289. /**
  5290. * Whether transform text.
  5291. * Only useful in Path and Image element
  5292. * @type {boolean}
  5293. */
  5294. transformText: false,
  5295. /**
  5296. * Text rotate around position of Path or Image
  5297. * Only useful in Path and Image element and transformText is false.
  5298. */
  5299. textRotation: 0,
  5300. /**
  5301. * Text origin of text rotation, like [10, 40].
  5302. * Based on x, y of rect.
  5303. * Useful in label rotation of circular symbol.
  5304. * By default, this origin is textPosition.
  5305. * Can be 'center'.
  5306. * @type {string|Array.<number>}
  5307. */
  5308. textOrigin: null,
  5309. /**
  5310. * @type {string}
  5311. */
  5312. textBackgroundColor: null,
  5313. /**
  5314. * @type {string}
  5315. */
  5316. textBorderColor: null,
  5317. /**
  5318. * @type {number}
  5319. */
  5320. textBorderWidth: 0,
  5321. /**
  5322. * @type {number}
  5323. */
  5324. textBorderRadius: 0,
  5325. /**
  5326. * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
  5327. * @type {number|Array.<number>}
  5328. */
  5329. textPadding: null,
  5330. /**
  5331. * Text styles for rich text.
  5332. * @type {Object}
  5333. */
  5334. rich: null,
  5335. /**
  5336. * {outerWidth, outerHeight, ellipsis, placeholder}
  5337. * @type {Object}
  5338. */
  5339. truncate: null,
  5340. /**
  5341. * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  5342. * @type {string}
  5343. */
  5344. blend: null,
  5345. /**
  5346. * @param {CanvasRenderingContext2D} ctx
  5347. */
  5348. bind: function (ctx, el, prevEl) {
  5349. var style = this;
  5350. var prevStyle = prevEl && prevEl.style;
  5351. var firstDraw = !prevStyle;
  5352. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  5353. var prop = STYLE_COMMON_PROPS[i];
  5354. var styleName = prop[0];
  5355. if (firstDraw || style[styleName] !== prevStyle[styleName]) {
  5356. // FIXME Invalid property value will cause style leak from previous element.
  5357. ctx[styleName] =
  5358. fixShadow(ctx, styleName, style[styleName] || prop[1]);
  5359. }
  5360. }
  5361. if ((firstDraw || style.fill !== prevStyle.fill)) {
  5362. ctx.fillStyle = style.fill;
  5363. }
  5364. if ((firstDraw || style.stroke !== prevStyle.stroke)) {
  5365. ctx.strokeStyle = style.stroke;
  5366. }
  5367. if ((firstDraw || style.opacity !== prevStyle.opacity)) {
  5368. ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
  5369. }
  5370. if ((firstDraw || style.blend !== prevStyle.blend)) {
  5371. ctx.globalCompositeOperation = style.blend || 'source-over';
  5372. }
  5373. if (this.hasStroke()) {
  5374. var lineWidth = style.lineWidth;
  5375. ctx.lineWidth = lineWidth / (
  5376. (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1
  5377. );
  5378. }
  5379. },
  5380. hasFill: function () {
  5381. var fill = this.fill;
  5382. return fill != null && fill !== 'none';
  5383. },
  5384. hasStroke: function () {
  5385. var stroke = this.stroke;
  5386. return stroke != null && stroke !== 'none' && this.lineWidth > 0;
  5387. },
  5388. /**
  5389. * Extend from other style
  5390. * @param {zrender/graphic/Style} otherStyle
  5391. * @param {boolean} overwrite true: overwrirte any way.
  5392. * false: overwrite only when !target.hasOwnProperty
  5393. * others: overwrite when property is not null/undefined.
  5394. */
  5395. extendFrom: function (otherStyle, overwrite) {
  5396. if (otherStyle) {
  5397. for (var name in otherStyle) {
  5398. if (otherStyle.hasOwnProperty(name)
  5399. && (overwrite === true
  5400. || (
  5401. overwrite === false
  5402. ? !this.hasOwnProperty(name)
  5403. : otherStyle[name] != null
  5404. )
  5405. )
  5406. ) {
  5407. this[name] = otherStyle[name];
  5408. }
  5409. }
  5410. }
  5411. },
  5412. /**
  5413. * Batch setting style with a given object
  5414. * @param {Object|string} obj
  5415. * @param {*} [obj]
  5416. */
  5417. set: function (obj, value) {
  5418. if (typeof obj === 'string') {
  5419. this[obj] = value;
  5420. }
  5421. else {
  5422. this.extendFrom(obj, true);
  5423. }
  5424. },
  5425. /**
  5426. * Clone
  5427. * @return {zrender/graphic/Style} [description]
  5428. */
  5429. clone: function () {
  5430. var newStyle = new this.constructor();
  5431. newStyle.extendFrom(this, true);
  5432. return newStyle;
  5433. },
  5434. getGradient: function (ctx, obj, rect) {
  5435. var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
  5436. var canvasGradient = method(ctx, obj, rect);
  5437. var colorStops = obj.colorStops;
  5438. for (var i = 0; i < colorStops.length; i++) {
  5439. canvasGradient.addColorStop(
  5440. colorStops[i].offset, colorStops[i].color
  5441. );
  5442. }
  5443. return canvasGradient;
  5444. }
  5445. };
  5446. var styleProto = Style.prototype;
  5447. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  5448. var prop = STYLE_COMMON_PROPS[i];
  5449. if (!(prop[0] in styleProto)) {
  5450. styleProto[prop[0]] = prop[1];
  5451. }
  5452. }
  5453. // Provide for others
  5454. Style.getGradient = styleProto.getGradient;
  5455. var Pattern = function (image, repeat) {
  5456. // Should do nothing more in this constructor. Because gradient can be
  5457. // declard by `color: {image: ...}`, where this constructor will not be called.
  5458. this.image = image;
  5459. this.repeat = repeat;
  5460. // Can be cloned
  5461. this.type = 'pattern';
  5462. };
  5463. Pattern.prototype.getCanvasPattern = function (ctx) {
  5464. return ctx.createPattern(this.image, this.repeat || 'repeat');
  5465. };
  5466. /**
  5467. * @module zrender/Layer
  5468. * @author pissang(https://www.github.com/pissang)
  5469. */
  5470. function returnFalse() {
  5471. return false;
  5472. }
  5473. /**
  5474. * 创建dom
  5475. *
  5476. * @inner
  5477. * @param {string} id dom id 待用
  5478. * @param {Painter} painter painter instance
  5479. * @param {number} number
  5480. */
  5481. function createDom(id, painter, dpr) {
  5482. var newDom = createCanvas();
  5483. var width = painter.getWidth();
  5484. var height = painter.getHeight();
  5485. var newDomStyle = newDom.style;
  5486. if (newDomStyle) { // In node or some other non-browser environment
  5487. newDomStyle.position = 'absolute';
  5488. newDomStyle.left = 0;
  5489. newDomStyle.top = 0;
  5490. newDomStyle.width = width + 'px';
  5491. newDomStyle.height = height + 'px';
  5492. newDom.setAttribute('data-zr-dom-id', id);
  5493. }
  5494. newDom.width = width * dpr;
  5495. newDom.height = height * dpr;
  5496. return newDom;
  5497. }
  5498. /**
  5499. * @alias module:zrender/Layer
  5500. * @constructor
  5501. * @extends module:zrender/mixin/Transformable
  5502. * @param {string} id
  5503. * @param {module:zrender/Painter} painter
  5504. * @param {number} [dpr]
  5505. */
  5506. var Layer = function(id, painter, dpr) {
  5507. var dom;
  5508. dpr = dpr || devicePixelRatio;
  5509. if (typeof id === 'string') {
  5510. dom = createDom(id, painter, dpr);
  5511. }
  5512. // Not using isDom because in node it will return false
  5513. else if (isObject$1(id)) {
  5514. dom = id;
  5515. id = dom.id;
  5516. }
  5517. this.id = id;
  5518. this.dom = dom;
  5519. var domStyle = dom.style;
  5520. if (domStyle) { // Not in node
  5521. dom.onselectstart = returnFalse; // 避免页面选中的尴尬
  5522. domStyle['-webkit-user-select'] = 'none';
  5523. domStyle['user-select'] = 'none';
  5524. domStyle['-webkit-touch-callout'] = 'none';
  5525. domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';
  5526. domStyle['padding'] = 0;
  5527. domStyle['margin'] = 0;
  5528. domStyle['border-width'] = 0;
  5529. }
  5530. this.domBack = null;
  5531. this.ctxBack = null;
  5532. this.painter = painter;
  5533. this.config = null;
  5534. // Configs
  5535. /**
  5536. * 每次清空画布的颜色
  5537. * @type {string}
  5538. * @default 0
  5539. */
  5540. this.clearColor = 0;
  5541. /**
  5542. * 是否开启动态模糊
  5543. * @type {boolean}
  5544. * @default false
  5545. */
  5546. this.motionBlur = false;
  5547. /**
  5548. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  5549. * @type {number}
  5550. * @default 0.7
  5551. */
  5552. this.lastFrameAlpha = 0.7;
  5553. /**
  5554. * Layer dpr
  5555. * @type {number}
  5556. */
  5557. this.dpr = dpr;
  5558. };
  5559. Layer.prototype = {
  5560. constructor: Layer,
  5561. __dirty: true,
  5562. __used: false,
  5563. __drawIndex: 0,
  5564. __startIndex: 0,
  5565. __endIndex: 0,
  5566. incremental: false,
  5567. getElementCount: function () {
  5568. return this.__endIndex - this.__startIndex;
  5569. },
  5570. initContext: function () {
  5571. this.ctx = this.dom.getContext('2d');
  5572. this.ctx.dpr = this.dpr;
  5573. },
  5574. createBackBuffer: function () {
  5575. var dpr = this.dpr;
  5576. this.domBack = createDom('back-' + this.id, this.painter, dpr);
  5577. this.ctxBack = this.domBack.getContext('2d');
  5578. if (dpr != 1) {
  5579. this.ctxBack.scale(dpr, dpr);
  5580. }
  5581. },
  5582. /**
  5583. * @param {number} width
  5584. * @param {number} height
  5585. */
  5586. resize: function (width, height) {
  5587. var dpr = this.dpr;
  5588. var dom = this.dom;
  5589. var domStyle = dom.style;
  5590. var domBack = this.domBack;
  5591. domStyle.width = width + 'px';
  5592. domStyle.height = height + 'px';
  5593. dom.width = width * dpr;
  5594. dom.height = height * dpr;
  5595. if (domBack) {
  5596. domBack.width = width * dpr;
  5597. domBack.height = height * dpr;
  5598. if (dpr != 1) {
  5599. this.ctxBack.scale(dpr, dpr);
  5600. }
  5601. }
  5602. },
  5603. /**
  5604. * 清空该层画布
  5605. * @param {boolean} clearAll Clear all with out motion blur
  5606. */
  5607. clear: function (clearAll) {
  5608. var dom = this.dom;
  5609. var ctx = this.ctx;
  5610. var width = dom.width;
  5611. var height = dom.height;
  5612. var clearColor = this.clearColor;
  5613. var haveMotionBLur = this.motionBlur && !clearAll;
  5614. var lastFrameAlpha = this.lastFrameAlpha;
  5615. var dpr = this.dpr;
  5616. if (haveMotionBLur) {
  5617. if (!this.domBack) {
  5618. this.createBackBuffer();
  5619. }
  5620. this.ctxBack.globalCompositeOperation = 'copy';
  5621. this.ctxBack.drawImage(
  5622. dom, 0, 0,
  5623. width / dpr,
  5624. height / dpr
  5625. );
  5626. }
  5627. ctx.clearRect(0, 0, width, height);
  5628. if (clearColor) {
  5629. var clearColorGradientOrPattern;
  5630. // Gradient
  5631. if (clearColor.colorStops) {
  5632. // Cache canvas gradient
  5633. clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, {
  5634. x: 0,
  5635. y: 0,
  5636. width: width,
  5637. height: height
  5638. });
  5639. clearColor.__canvasGradient = clearColorGradientOrPattern;
  5640. }
  5641. // Pattern
  5642. else if (clearColor.image) {
  5643. clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx);
  5644. }
  5645. ctx.save();
  5646. ctx.fillStyle = clearColorGradientOrPattern || clearColor;
  5647. ctx.fillRect(0, 0, width, height);
  5648. ctx.restore();
  5649. }
  5650. if (haveMotionBLur) {
  5651. var domBack = this.domBack;
  5652. ctx.save();
  5653. ctx.globalAlpha = lastFrameAlpha;
  5654. ctx.drawImage(domBack, 0, 0, width, height);
  5655. ctx.restore();
  5656. }
  5657. }
  5658. };
  5659. var requestAnimationFrame = (
  5660. typeof window !== 'undefined'
  5661. && (
  5662. (window.requestAnimationFrame && window.requestAnimationFrame.bind(window))
  5663. // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809
  5664. || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))
  5665. || window.mozRequestAnimationFrame
  5666. || window.webkitRequestAnimationFrame
  5667. )
  5668. ) || function (func) {
  5669. setTimeout(func, 16);
  5670. };
  5671. var globalImageCache = new LRU(50);
  5672. /**
  5673. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  5674. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  5675. */
  5676. function findExistImage(newImageOrSrc) {
  5677. if (typeof newImageOrSrc === 'string') {
  5678. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  5679. return cachedImgObj && cachedImgObj.image;
  5680. }
  5681. else {
  5682. return newImageOrSrc;
  5683. }
  5684. }
  5685. /**
  5686. * Caution: User should cache loaded images, but not just count on LRU.
  5687. * Consider if required images more than LRU size, will dead loop occur?
  5688. *
  5689. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  5690. * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image.
  5691. * @param {module:zrender/Element} [hostEl] For calling `dirty`.
  5692. * @param {Function} [cb] params: (image, cbPayload)
  5693. * @param {Object} [cbPayload] Payload on cb calling.
  5694. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  5695. */
  5696. function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) {
  5697. if (!newImageOrSrc) {
  5698. return image;
  5699. }
  5700. else if (typeof newImageOrSrc === 'string') {
  5701. // Image should not be loaded repeatly.
  5702. if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {
  5703. return image;
  5704. }
  5705. // Only when there is no existent image or existent image src
  5706. // is different, this method is responsible for load.
  5707. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  5708. var pendingWrap = {hostEl: hostEl, cb: cb, cbPayload: cbPayload};
  5709. if (cachedImgObj) {
  5710. image = cachedImgObj.image;
  5711. !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);
  5712. }
  5713. else {
  5714. !image && (image = new Image());
  5715. image.onload = imageOnLoad;
  5716. globalImageCache.put(
  5717. newImageOrSrc,
  5718. image.__cachedImgObj = {
  5719. image: image,
  5720. pending: [pendingWrap]
  5721. }
  5722. );
  5723. image.src = image.__zrImageSrc = newImageOrSrc;
  5724. }
  5725. return image;
  5726. }
  5727. // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas
  5728. else {
  5729. return newImageOrSrc;
  5730. }
  5731. }
  5732. function imageOnLoad() {
  5733. var cachedImgObj = this.__cachedImgObj;
  5734. this.onload = this.__cachedImgObj = null;
  5735. for (var i = 0; i < cachedImgObj.pending.length; i++) {
  5736. var pendingWrap = cachedImgObj.pending[i];
  5737. var cb = pendingWrap.cb;
  5738. cb && cb(this, pendingWrap.cbPayload);
  5739. pendingWrap.hostEl.dirty();
  5740. }
  5741. cachedImgObj.pending.length = 0;
  5742. }
  5743. function isImageReady(image) {
  5744. return image && image.width && image.height;
  5745. }
  5746. var textWidthCache = {};
  5747. var textWidthCacheCounter = 0;
  5748. var TEXT_CACHE_MAX = 5000;
  5749. var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g;
  5750. var DEFAULT_FONT = '12px sans-serif';
  5751. // Avoid assign to an exported variable, for transforming to cjs.
  5752. var methods$1 = {};
  5753. /**
  5754. * @public
  5755. * @param {string} text
  5756. * @param {string} font
  5757. * @return {number} width
  5758. */
  5759. function getWidth(text, font) {
  5760. font = font || DEFAULT_FONT;
  5761. var key = text + ':' + font;
  5762. if (textWidthCache[key]) {
  5763. return textWidthCache[key];
  5764. }
  5765. var textLines = (text + '').split('\n');
  5766. var width = 0;
  5767. for (var i = 0, l = textLines.length; i < l; i++) {
  5768. // textContain.measureText may be overrided in SVG or VML
  5769. width = Math.max(measureText(textLines[i], font).width, width);
  5770. }
  5771. if (textWidthCacheCounter > TEXT_CACHE_MAX) {
  5772. textWidthCacheCounter = 0;
  5773. textWidthCache = {};
  5774. }
  5775. textWidthCacheCounter++;
  5776. textWidthCache[key] = width;
  5777. return width;
  5778. }
  5779. /**
  5780. * @public
  5781. * @param {string} text
  5782. * @param {string} font
  5783. * @param {string} [textAlign='left']
  5784. * @param {string} [textVerticalAlign='top']
  5785. * @param {Array.<number>} [textPadding]
  5786. * @param {Object} [rich]
  5787. * @param {Object} [truncate]
  5788. * @return {Object} {x, y, width, height, lineHeight}
  5789. */
  5790. function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
  5791. return rich
  5792. ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate)
  5793. : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate);
  5794. }
  5795. function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate) {
  5796. var contentBlock = parsePlainText(text, font, textPadding, truncate);
  5797. var outerWidth = getWidth(text, font);
  5798. if (textPadding) {
  5799. outerWidth += textPadding[1] + textPadding[3];
  5800. }
  5801. var outerHeight = contentBlock.outerHeight;
  5802. var x = adjustTextX(0, outerWidth, textAlign);
  5803. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  5804. var rect = new BoundingRect(x, y, outerWidth, outerHeight);
  5805. rect.lineHeight = contentBlock.lineHeight;
  5806. return rect;
  5807. }
  5808. function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
  5809. var contentBlock = parseRichText(text, {
  5810. rich: rich,
  5811. truncate: truncate,
  5812. font: font,
  5813. textAlign: textAlign,
  5814. textPadding: textPadding
  5815. });
  5816. var outerWidth = contentBlock.outerWidth;
  5817. var outerHeight = contentBlock.outerHeight;
  5818. var x = adjustTextX(0, outerWidth, textAlign);
  5819. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  5820. return new BoundingRect(x, y, outerWidth, outerHeight);
  5821. }
  5822. /**
  5823. * @public
  5824. * @param {number} x
  5825. * @param {number} width
  5826. * @param {string} [textAlign='left']
  5827. * @return {number} Adjusted x.
  5828. */
  5829. function adjustTextX(x, width, textAlign) {
  5830. // FIXME Right to left language
  5831. if (textAlign === 'right') {
  5832. x -= width;
  5833. }
  5834. else if (textAlign === 'center') {
  5835. x -= width / 2;
  5836. }
  5837. return x;
  5838. }
  5839. /**
  5840. * @public
  5841. * @param {number} y
  5842. * @param {number} height
  5843. * @param {string} [textVerticalAlign='top']
  5844. * @return {number} Adjusted y.
  5845. */
  5846. function adjustTextY(y, height, textVerticalAlign) {
  5847. if (textVerticalAlign === 'middle') {
  5848. y -= height / 2;
  5849. }
  5850. else if (textVerticalAlign === 'bottom') {
  5851. y -= height;
  5852. }
  5853. return y;
  5854. }
  5855. /**
  5856. * @public
  5857. * @param {stirng} textPosition
  5858. * @param {Object} rect {x, y, width, height}
  5859. * @param {number} distance
  5860. * @return {Object} {x, y, textAlign, textVerticalAlign}
  5861. */
  5862. function adjustTextPositionOnRect(textPosition, rect, distance) {
  5863. var x = rect.x;
  5864. var y = rect.y;
  5865. var height = rect.height;
  5866. var width = rect.width;
  5867. var halfHeight = height / 2;
  5868. var textAlign = 'left';
  5869. var textVerticalAlign = 'top';
  5870. switch (textPosition) {
  5871. case 'left':
  5872. x -= distance;
  5873. y += halfHeight;
  5874. textAlign = 'right';
  5875. textVerticalAlign = 'middle';
  5876. break;
  5877. case 'right':
  5878. x += distance + width;
  5879. y += halfHeight;
  5880. textVerticalAlign = 'middle';
  5881. break;
  5882. case 'top':
  5883. x += width / 2;
  5884. y -= distance;
  5885. textAlign = 'center';
  5886. textVerticalAlign = 'bottom';
  5887. break;
  5888. case 'bottom':
  5889. x += width / 2;
  5890. y += height + distance;
  5891. textAlign = 'center';
  5892. break;
  5893. case 'inside':
  5894. x += width / 2;
  5895. y += halfHeight;
  5896. textAlign = 'center';
  5897. textVerticalAlign = 'middle';
  5898. break;
  5899. case 'insideLeft':
  5900. x += distance;
  5901. y += halfHeight;
  5902. textVerticalAlign = 'middle';
  5903. break;
  5904. case 'insideRight':
  5905. x += width - distance;
  5906. y += halfHeight;
  5907. textAlign = 'right';
  5908. textVerticalAlign = 'middle';
  5909. break;
  5910. case 'insideTop':
  5911. x += width / 2;
  5912. y += distance;
  5913. textAlign = 'center';
  5914. break;
  5915. case 'insideBottom':
  5916. x += width / 2;
  5917. y += height - distance;
  5918. textAlign = 'center';
  5919. textVerticalAlign = 'bottom';
  5920. break;
  5921. case 'insideTopLeft':
  5922. x += distance;
  5923. y += distance;
  5924. break;
  5925. case 'insideTopRight':
  5926. x += width - distance;
  5927. y += distance;
  5928. textAlign = 'right';
  5929. break;
  5930. case 'insideBottomLeft':
  5931. x += distance;
  5932. y += height - distance;
  5933. textVerticalAlign = 'bottom';
  5934. break;
  5935. case 'insideBottomRight':
  5936. x += width - distance;
  5937. y += height - distance;
  5938. textAlign = 'right';
  5939. textVerticalAlign = 'bottom';
  5940. break;
  5941. }
  5942. return {
  5943. x: x,
  5944. y: y,
  5945. textAlign: textAlign,
  5946. textVerticalAlign: textVerticalAlign
  5947. };
  5948. }
  5949. /**
  5950. * Show ellipsis if overflow.
  5951. *
  5952. * @public
  5953. * @param {string} text
  5954. * @param {string} containerWidth
  5955. * @param {string} font
  5956. * @param {number} [ellipsis='...']
  5957. * @param {Object} [options]
  5958. * @param {number} [options.maxIterations=3]
  5959. * @param {number} [options.minChar=0] If truncate result are less
  5960. * then minChar, ellipsis will not show, which is
  5961. * better for user hint in some cases.
  5962. * @param {number} [options.placeholder=''] When all truncated, use the placeholder.
  5963. * @return {string}
  5964. */
  5965. function truncateText(text, containerWidth, font, ellipsis, options) {
  5966. if (!containerWidth) {
  5967. return '';
  5968. }
  5969. var textLines = (text + '').split('\n');
  5970. options = prepareTruncateOptions(containerWidth, font, ellipsis, options);
  5971. // FIXME
  5972. // It is not appropriate that every line has '...' when truncate multiple lines.
  5973. for (var i = 0, len = textLines.length; i < len; i++) {
  5974. textLines[i] = truncateSingleLine(textLines[i], options);
  5975. }
  5976. return textLines.join('\n');
  5977. }
  5978. function prepareTruncateOptions(containerWidth, font, ellipsis, options) {
  5979. options = extend({}, options);
  5980. options.font = font;
  5981. var ellipsis = retrieve2(ellipsis, '...');
  5982. options.maxIterations = retrieve2(options.maxIterations, 2);
  5983. var minChar = options.minChar = retrieve2(options.minChar, 0);
  5984. // FIXME
  5985. // Other languages?
  5986. options.cnCharWidth = getWidth('国', font);
  5987. // FIXME
  5988. // Consider proportional font?
  5989. var ascCharWidth = options.ascCharWidth = getWidth('a', font);
  5990. options.placeholder = retrieve2(options.placeholder, '');
  5991. // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'.
  5992. // Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'.
  5993. var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap.
  5994. for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {
  5995. contentWidth -= ascCharWidth;
  5996. }
  5997. var ellipsisWidth = getWidth(ellipsis);
  5998. if (ellipsisWidth > contentWidth) {
  5999. ellipsis = '';
  6000. ellipsisWidth = 0;
  6001. }
  6002. contentWidth = containerWidth - ellipsisWidth;
  6003. options.ellipsis = ellipsis;
  6004. options.ellipsisWidth = ellipsisWidth;
  6005. options.contentWidth = contentWidth;
  6006. options.containerWidth = containerWidth;
  6007. return options;
  6008. }
  6009. function truncateSingleLine(textLine, options) {
  6010. var containerWidth = options.containerWidth;
  6011. var font = options.font;
  6012. var contentWidth = options.contentWidth;
  6013. if (!containerWidth) {
  6014. return '';
  6015. }
  6016. var lineWidth = getWidth(textLine, font);
  6017. if (lineWidth <= containerWidth) {
  6018. return textLine;
  6019. }
  6020. for (var j = 0;; j++) {
  6021. if (lineWidth <= contentWidth || j >= options.maxIterations) {
  6022. textLine += options.ellipsis;
  6023. break;
  6024. }
  6025. var subLength = j === 0
  6026. ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)
  6027. : lineWidth > 0
  6028. ? Math.floor(textLine.length * contentWidth / lineWidth)
  6029. : 0;
  6030. textLine = textLine.substr(0, subLength);
  6031. lineWidth = getWidth(textLine, font);
  6032. }
  6033. if (textLine === '') {
  6034. textLine = options.placeholder;
  6035. }
  6036. return textLine;
  6037. }
  6038. function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {
  6039. var width = 0;
  6040. var i = 0;
  6041. for (var len = text.length; i < len && width < contentWidth; i++) {
  6042. var charCode = text.charCodeAt(i);
  6043. width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;
  6044. }
  6045. return i;
  6046. }
  6047. /**
  6048. * @public
  6049. * @param {string} font
  6050. * @return {number} line height
  6051. */
  6052. function getLineHeight(font) {
  6053. // FIXME A rough approach.
  6054. return getWidth('国', font);
  6055. }
  6056. /**
  6057. * @public
  6058. * @param {string} text
  6059. * @param {string} font
  6060. * @return {Object} width
  6061. */
  6062. function measureText(text, font) {
  6063. return methods$1.measureText(text, font);
  6064. }
  6065. // Avoid assign to an exported variable, for transforming to cjs.
  6066. methods$1.measureText = function (text, font) {
  6067. var ctx = getContext();
  6068. ctx.font = font || DEFAULT_FONT;
  6069. return ctx.measureText(text);
  6070. };
  6071. /**
  6072. * @public
  6073. * @param {string} text
  6074. * @param {string} font
  6075. * @param {Object} [truncate]
  6076. * @return {Object} block: {lineHeight, lines, height, outerHeight}
  6077. * Notice: for performance, do not calculate outerWidth util needed.
  6078. */
  6079. function parsePlainText(text, font, padding, truncate) {
  6080. text != null && (text += '');
  6081. var lineHeight = getLineHeight(font);
  6082. var lines = text ? text.split('\n') : [];
  6083. var height = lines.length * lineHeight;
  6084. var outerHeight = height;
  6085. if (padding) {
  6086. outerHeight += padding[0] + padding[2];
  6087. }
  6088. if (text && truncate) {
  6089. var truncOuterHeight = truncate.outerHeight;
  6090. var truncOuterWidth = truncate.outerWidth;
  6091. if (truncOuterHeight != null && outerHeight > truncOuterHeight) {
  6092. text = '';
  6093. lines = [];
  6094. }
  6095. else if (truncOuterWidth != null) {
  6096. var options = prepareTruncateOptions(
  6097. truncOuterWidth - (padding ? padding[1] + padding[3] : 0),
  6098. font,
  6099. truncate.ellipsis,
  6100. {minChar: truncate.minChar, placeholder: truncate.placeholder}
  6101. );
  6102. // FIXME
  6103. // It is not appropriate that every line has '...' when truncate multiple lines.
  6104. for (var i = 0, len = lines.length; i < len; i++) {
  6105. lines[i] = truncateSingleLine(lines[i], options);
  6106. }
  6107. }
  6108. }
  6109. return {
  6110. lines: lines,
  6111. height: height,
  6112. outerHeight: outerHeight,
  6113. lineHeight: lineHeight
  6114. };
  6115. }
  6116. /**
  6117. * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx'
  6118. * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'.
  6119. *
  6120. * @public
  6121. * @param {string} text
  6122. * @param {Object} style
  6123. * @return {Object} block
  6124. * {
  6125. * width,
  6126. * height,
  6127. * lines: [{
  6128. * lineHeight,
  6129. * width,
  6130. * tokens: [[{
  6131. * styleName,
  6132. * text,
  6133. * width, // include textPadding
  6134. * height, // include textPadding
  6135. * textWidth, // pure text width
  6136. * textHeight, // pure text height
  6137. * lineHeihgt,
  6138. * font,
  6139. * textAlign,
  6140. * textVerticalAlign
  6141. * }], [...], ...]
  6142. * }, ...]
  6143. * }
  6144. * If styleName is undefined, it is plain text.
  6145. */
  6146. function parseRichText(text, style) {
  6147. var contentBlock = {lines: [], width: 0, height: 0};
  6148. text != null && (text += '');
  6149. if (!text) {
  6150. return contentBlock;
  6151. }
  6152. var lastIndex = STYLE_REG.lastIndex = 0;
  6153. var result;
  6154. while ((result = STYLE_REG.exec(text)) != null) {
  6155. var matchedIndex = result.index;
  6156. if (matchedIndex > lastIndex) {
  6157. pushTokens(contentBlock, text.substring(lastIndex, matchedIndex));
  6158. }
  6159. pushTokens(contentBlock, result[2], result[1]);
  6160. lastIndex = STYLE_REG.lastIndex;
  6161. }
  6162. if (lastIndex < text.length) {
  6163. pushTokens(contentBlock, text.substring(lastIndex, text.length));
  6164. }
  6165. var lines = contentBlock.lines;
  6166. var contentHeight = 0;
  6167. var contentWidth = 0;
  6168. // For `textWidth: 100%`
  6169. var pendingList = [];
  6170. var stlPadding = style.textPadding;
  6171. var truncate = style.truncate;
  6172. var truncateWidth = truncate && truncate.outerWidth;
  6173. var truncateHeight = truncate && truncate.outerHeight;
  6174. if (stlPadding) {
  6175. truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]);
  6176. truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]);
  6177. }
  6178. // Calculate layout info of tokens.
  6179. for (var i = 0; i < lines.length; i++) {
  6180. var line = lines[i];
  6181. var lineHeight = 0;
  6182. var lineWidth = 0;
  6183. for (var j = 0; j < line.tokens.length; j++) {
  6184. var token = line.tokens[j];
  6185. var tokenStyle = token.styleName && style.rich[token.styleName] || {};
  6186. // textPadding should not inherit from style.
  6187. var textPadding = token.textPadding = tokenStyle.textPadding;
  6188. // textFont has been asigned to font by `normalizeStyle`.
  6189. var font = token.font = tokenStyle.font || style.font;
  6190. // textHeight can be used when textVerticalAlign is specified in token.
  6191. var tokenHeight = token.textHeight = retrieve2(
  6192. // textHeight should not be inherited, consider it can be specified
  6193. // as box height of the block.
  6194. tokenStyle.textHeight, getLineHeight(font)
  6195. );
  6196. textPadding && (tokenHeight += textPadding[0] + textPadding[2]);
  6197. token.height = tokenHeight;
  6198. token.lineHeight = retrieve3(
  6199. tokenStyle.textLineHeight, style.textLineHeight, tokenHeight
  6200. );
  6201. token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign;
  6202. token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle';
  6203. if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) {
  6204. return {lines: [], width: 0, height: 0};
  6205. }
  6206. token.textWidth = getWidth(token.text, font);
  6207. var tokenWidth = tokenStyle.textWidth;
  6208. var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto';
  6209. // Percent width, can be `100%`, can be used in drawing separate
  6210. // line when box width is needed to be auto.
  6211. if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') {
  6212. token.percentWidth = tokenWidth;
  6213. pendingList.push(token);
  6214. tokenWidth = 0;
  6215. // Do not truncate in this case, because there is no user case
  6216. // and it is too complicated.
  6217. }
  6218. else {
  6219. if (tokenWidthNotSpecified) {
  6220. tokenWidth = token.textWidth;
  6221. // FIXME: If image is not loaded and textWidth is not specified, calling
  6222. // `getBoundingRect()` will not get correct result.
  6223. var textBackgroundColor = tokenStyle.textBackgroundColor;
  6224. var bgImg = textBackgroundColor && textBackgroundColor.image;
  6225. // Use cases:
  6226. // (1) If image is not loaded, it will be loaded at render phase and call
  6227. // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded
  6228. // image, and then the right size will be calculated here at the next tick.
  6229. // See `graphic/helper/text.js`.
  6230. // (2) If image loaded, and `textBackgroundColor.image` is image src string,
  6231. // use `imageHelper.findExistImage` to find cached image.
  6232. // `imageHelper.findExistImage` will always be called here before
  6233. // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`
  6234. // which ensures that image will not be rendered before correct size calcualted.
  6235. if (bgImg) {
  6236. bgImg = findExistImage(bgImg);
  6237. if (isImageReady(bgImg)) {
  6238. tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height);
  6239. }
  6240. }
  6241. }
  6242. var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0;
  6243. tokenWidth += paddingW;
  6244. var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null;
  6245. if (remianTruncWidth != null && remianTruncWidth < tokenWidth) {
  6246. if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) {
  6247. token.text = '';
  6248. token.textWidth = tokenWidth = 0;
  6249. }
  6250. else {
  6251. token.text = truncateText(
  6252. token.text, remianTruncWidth - paddingW, font, truncate.ellipsis,
  6253. {minChar: truncate.minChar}
  6254. );
  6255. token.textWidth = getWidth(token.text, font);
  6256. tokenWidth = token.textWidth + paddingW;
  6257. }
  6258. }
  6259. }
  6260. lineWidth += (token.width = tokenWidth);
  6261. tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));
  6262. }
  6263. line.width = lineWidth;
  6264. line.lineHeight = lineHeight;
  6265. contentHeight += lineHeight;
  6266. contentWidth = Math.max(contentWidth, lineWidth);
  6267. }
  6268. contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth);
  6269. contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight);
  6270. if (stlPadding) {
  6271. contentBlock.outerWidth += stlPadding[1] + stlPadding[3];
  6272. contentBlock.outerHeight += stlPadding[0] + stlPadding[2];
  6273. }
  6274. for (var i = 0; i < pendingList.length; i++) {
  6275. var token = pendingList[i];
  6276. var percentWidth = token.percentWidth;
  6277. // Should not base on outerWidth, because token can not be placed out of padding.
  6278. token.width = parseInt(percentWidth, 10) / 100 * contentWidth;
  6279. }
  6280. return contentBlock;
  6281. }
  6282. function pushTokens(block, str, styleName) {
  6283. var isEmptyStr = str === '';
  6284. var strs = str.split('\n');
  6285. var lines = block.lines;
  6286. for (var i = 0; i < strs.length; i++) {
  6287. var text = strs[i];
  6288. var token = {
  6289. styleName: styleName,
  6290. text: text,
  6291. isLineHolder: !text && !isEmptyStr
  6292. };
  6293. // The first token should be appended to the last line.
  6294. if (!i) {
  6295. var tokens = (lines[lines.length - 1] || (lines[0] = {tokens: []})).tokens;
  6296. // Consider cases:
  6297. // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item
  6298. // (which is a placeholder) should be replaced by new token.
  6299. // (2) A image backage, where token likes {a|}.
  6300. // (3) A redundant '' will affect textAlign in line.
  6301. // (4) tokens with the same tplName should not be merged, because
  6302. // they should be displayed in different box (with border and padding).
  6303. var tokensLen = tokens.length;
  6304. (tokensLen === 1 && tokens[0].isLineHolder)
  6305. ? (tokens[0] = token)
  6306. // Consider text is '', only insert when it is the "lineHolder" or
  6307. // "emptyStr". Otherwise a redundant '' will affect textAlign in line.
  6308. : ((text || !tokensLen || isEmptyStr) && tokens.push(token));
  6309. }
  6310. // Other tokens always start a new line.
  6311. else {
  6312. // If there is '', insert it as a placeholder.
  6313. lines.push({tokens: [token]});
  6314. }
  6315. }
  6316. }
  6317. function makeFont(style) {
  6318. // FIXME in node-canvas fontWeight is before fontStyle
  6319. // Use `fontSize` `fontFamily` to check whether font properties are defined.
  6320. var font = (style.fontSize || style.fontFamily) && [
  6321. style.fontStyle,
  6322. style.fontWeight,
  6323. (style.fontSize || 12) + 'px',
  6324. // If font properties are defined, `fontFamily` should not be ignored.
  6325. style.fontFamily || 'sans-serif'
  6326. ].join(' ');
  6327. return font && trim(font) || style.textFont || style.font;
  6328. }
  6329. function buildPath(ctx, shape) {
  6330. var x = shape.x;
  6331. var y = shape.y;
  6332. var width = shape.width;
  6333. var height = shape.height;
  6334. var r = shape.r;
  6335. var r1;
  6336. var r2;
  6337. var r3;
  6338. var r4;
  6339. // Convert width and height to positive for better borderRadius
  6340. if (width < 0) {
  6341. x = x + width;
  6342. width = -width;
  6343. }
  6344. if (height < 0) {
  6345. y = y + height;
  6346. height = -height;
  6347. }
  6348. if (typeof r === 'number') {
  6349. r1 = r2 = r3 = r4 = r;
  6350. }
  6351. else if (r instanceof Array) {
  6352. if (r.length === 1) {
  6353. r1 = r2 = r3 = r4 = r[0];
  6354. }
  6355. else if (r.length === 2) {
  6356. r1 = r3 = r[0];
  6357. r2 = r4 = r[1];
  6358. }
  6359. else if (r.length === 3) {
  6360. r1 = r[0];
  6361. r2 = r4 = r[1];
  6362. r3 = r[2];
  6363. }
  6364. else {
  6365. r1 = r[0];
  6366. r2 = r[1];
  6367. r3 = r[2];
  6368. r4 = r[3];
  6369. }
  6370. }
  6371. else {
  6372. r1 = r2 = r3 = r4 = 0;
  6373. }
  6374. var total;
  6375. if (r1 + r2 > width) {
  6376. total = r1 + r2;
  6377. r1 *= width / total;
  6378. r2 *= width / total;
  6379. }
  6380. if (r3 + r4 > width) {
  6381. total = r3 + r4;
  6382. r3 *= width / total;
  6383. r4 *= width / total;
  6384. }
  6385. if (r2 + r3 > height) {
  6386. total = r2 + r3;
  6387. r2 *= height / total;
  6388. r3 *= height / total;
  6389. }
  6390. if (r1 + r4 > height) {
  6391. total = r1 + r4;
  6392. r1 *= height / total;
  6393. r4 *= height / total;
  6394. }
  6395. ctx.moveTo(x + r1, y);
  6396. ctx.lineTo(x + width - r2, y);
  6397. r2 !== 0 && ctx.quadraticCurveTo(
  6398. x + width, y, x + width, y + r2
  6399. );
  6400. ctx.lineTo(x + width, y + height - r3);
  6401. r3 !== 0 && ctx.quadraticCurveTo(
  6402. x + width, y + height, x + width - r3, y + height
  6403. );
  6404. ctx.lineTo(x + r4, y + height);
  6405. r4 !== 0 && ctx.quadraticCurveTo(
  6406. x, y + height, x, y + height - r4
  6407. );
  6408. ctx.lineTo(x, y + r1);
  6409. r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y);
  6410. }
  6411. // TODO: Have not support 'start', 'end' yet.
  6412. var VALID_TEXT_ALIGN = {left: 1, right: 1, center: 1};
  6413. var VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1};
  6414. /**
  6415. * @param {module:zrender/graphic/Style} style
  6416. * @return {module:zrender/graphic/Style} The input style.
  6417. */
  6418. function normalizeTextStyle(style) {
  6419. normalizeStyle(style);
  6420. each$1(style.rich, normalizeStyle);
  6421. return style;
  6422. }
  6423. function normalizeStyle(style) {
  6424. if (style) {
  6425. style.font = makeFont(style);
  6426. var textAlign = style.textAlign;
  6427. textAlign === 'middle' && (textAlign = 'center');
  6428. style.textAlign = (
  6429. textAlign == null || VALID_TEXT_ALIGN[textAlign]
  6430. ) ? textAlign : 'left';
  6431. // Compatible with textBaseline.
  6432. var textVerticalAlign = style.textVerticalAlign || style.textBaseline;
  6433. textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
  6434. style.textVerticalAlign = (
  6435. textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]
  6436. ) ? textVerticalAlign : 'top';
  6437. var textPadding = style.textPadding;
  6438. if (textPadding) {
  6439. style.textPadding = normalizeCssArray(style.textPadding);
  6440. }
  6441. }
  6442. }
  6443. /**
  6444. * @param {CanvasRenderingContext2D} ctx
  6445. * @param {string} text
  6446. * @param {module:zrender/graphic/Style} style
  6447. * @param {Object|boolean} [rect] {x, y, width, height}
  6448. * If set false, rect text is not used.
  6449. */
  6450. function renderText(hostEl, ctx, text, style, rect) {
  6451. style.rich
  6452. ? renderRichText(hostEl, ctx, text, style, rect)
  6453. : renderPlainText(hostEl, ctx, text, style, rect);
  6454. }
  6455. function renderPlainText(hostEl, ctx, text, style, rect) {
  6456. var font = setCtx(ctx, 'font', style.font || DEFAULT_FONT);
  6457. var textPadding = style.textPadding;
  6458. var contentBlock = hostEl.__textCotentBlock;
  6459. if (!contentBlock || hostEl.__dirty) {
  6460. contentBlock = hostEl.__textCotentBlock = parsePlainText(
  6461. text, font, textPadding, style.truncate
  6462. );
  6463. }
  6464. var outerHeight = contentBlock.outerHeight;
  6465. var textLines = contentBlock.lines;
  6466. var lineHeight = contentBlock.lineHeight;
  6467. var boxPos = getBoxPosition(outerHeight, style, rect);
  6468. var baseX = boxPos.baseX;
  6469. var baseY = boxPos.baseY;
  6470. var textAlign = boxPos.textAlign;
  6471. var textVerticalAlign = boxPos.textVerticalAlign;
  6472. // Origin of textRotation should be the base point of text drawing.
  6473. applyTextRotation(ctx, style, rect, baseX, baseY);
  6474. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  6475. var textX = baseX;
  6476. var textY = boxY;
  6477. var needDrawBg = needDrawBackground(style);
  6478. if (needDrawBg || textPadding) {
  6479. // Consider performance, do not call getTextWidth util necessary.
  6480. var textWidth = getWidth(text, font);
  6481. var outerWidth = textWidth;
  6482. textPadding && (outerWidth += textPadding[1] + textPadding[3]);
  6483. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  6484. needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
  6485. if (textPadding) {
  6486. textX = getTextXForPadding(baseX, textAlign, textPadding);
  6487. textY += textPadding[0];
  6488. }
  6489. }
  6490. setCtx(ctx, 'textAlign', textAlign || 'left');
  6491. // Force baseline to be "middle". Otherwise, if using "top", the
  6492. // text will offset downward a little bit in font "Microsoft YaHei".
  6493. setCtx(ctx, 'textBaseline', 'middle');
  6494. // Always set shadowBlur and shadowOffset to avoid leak from displayable.
  6495. setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0);
  6496. setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent');
  6497. setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0);
  6498. setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0);
  6499. // `textBaseline` is set as 'middle'.
  6500. textY += lineHeight / 2;
  6501. var textStrokeWidth = style.textStrokeWidth;
  6502. var textStroke = getStroke(style.textStroke, textStrokeWidth);
  6503. var textFill = getFill(style.textFill);
  6504. if (textStroke) {
  6505. setCtx(ctx, 'lineWidth', textStrokeWidth);
  6506. setCtx(ctx, 'strokeStyle', textStroke);
  6507. }
  6508. if (textFill) {
  6509. setCtx(ctx, 'fillStyle', textFill);
  6510. }
  6511. for (var i = 0; i < textLines.length; i++) {
  6512. // Fill after stroke so the outline will not cover the main part.
  6513. textStroke && ctx.strokeText(textLines[i], textX, textY);
  6514. textFill && ctx.fillText(textLines[i], textX, textY);
  6515. textY += lineHeight;
  6516. }
  6517. }
  6518. function renderRichText(hostEl, ctx, text, style, rect) {
  6519. var contentBlock = hostEl.__textCotentBlock;
  6520. if (!contentBlock || hostEl.__dirty) {
  6521. contentBlock = hostEl.__textCotentBlock = parseRichText(text, style);
  6522. }
  6523. drawRichText(hostEl, ctx, contentBlock, style, rect);
  6524. }
  6525. function drawRichText(hostEl, ctx, contentBlock, style, rect) {
  6526. var contentWidth = contentBlock.width;
  6527. var outerWidth = contentBlock.outerWidth;
  6528. var outerHeight = contentBlock.outerHeight;
  6529. var textPadding = style.textPadding;
  6530. var boxPos = getBoxPosition(outerHeight, style, rect);
  6531. var baseX = boxPos.baseX;
  6532. var baseY = boxPos.baseY;
  6533. var textAlign = boxPos.textAlign;
  6534. var textVerticalAlign = boxPos.textVerticalAlign;
  6535. // Origin of textRotation should be the base point of text drawing.
  6536. applyTextRotation(ctx, style, rect, baseX, baseY);
  6537. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  6538. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  6539. var xLeft = boxX;
  6540. var lineTop = boxY;
  6541. if (textPadding) {
  6542. xLeft += textPadding[3];
  6543. lineTop += textPadding[0];
  6544. }
  6545. var xRight = xLeft + contentWidth;
  6546. needDrawBackground(style) && drawBackground(
  6547. hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight
  6548. );
  6549. for (var i = 0; i < contentBlock.lines.length; i++) {
  6550. var line = contentBlock.lines[i];
  6551. var tokens = line.tokens;
  6552. var tokenCount = tokens.length;
  6553. var lineHeight = line.lineHeight;
  6554. var usedWidth = line.width;
  6555. var leftIndex = 0;
  6556. var lineXLeft = xLeft;
  6557. var lineXRight = xRight;
  6558. var rightIndex = tokenCount - 1;
  6559. var token;
  6560. while (
  6561. leftIndex < tokenCount
  6562. && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')
  6563. ) {
  6564. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');
  6565. usedWidth -= token.width;
  6566. lineXLeft += token.width;
  6567. leftIndex++;
  6568. }
  6569. while (
  6570. rightIndex >= 0
  6571. && (token = tokens[rightIndex], token.textAlign === 'right')
  6572. ) {
  6573. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');
  6574. usedWidth -= token.width;
  6575. lineXRight -= token.width;
  6576. rightIndex--;
  6577. }
  6578. // The other tokens are placed as textAlign 'center' if there is enough space.
  6579. lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
  6580. while (leftIndex <= rightIndex) {
  6581. token = tokens[leftIndex];
  6582. // Consider width specified by user, use 'center' rather than 'left'.
  6583. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
  6584. lineXLeft += token.width;
  6585. leftIndex++;
  6586. }
  6587. lineTop += lineHeight;
  6588. }
  6589. }
  6590. function applyTextRotation(ctx, style, rect, x, y) {
  6591. // textRotation only apply in RectText.
  6592. if (rect && style.textRotation) {
  6593. var origin = style.textOrigin;
  6594. if (origin === 'center') {
  6595. x = rect.width / 2 + rect.x;
  6596. y = rect.height / 2 + rect.y;
  6597. }
  6598. else if (origin) {
  6599. x = origin[0] + rect.x;
  6600. y = origin[1] + rect.y;
  6601. }
  6602. ctx.translate(x, y);
  6603. // Positive: anticlockwise
  6604. ctx.rotate(-style.textRotation);
  6605. ctx.translate(-x, -y);
  6606. }
  6607. }
  6608. function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {
  6609. var tokenStyle = style.rich[token.styleName] || {};
  6610. // 'ctx.textBaseline' is always set as 'middle', for sake of
  6611. // the bias of "Microsoft YaHei".
  6612. var textVerticalAlign = token.textVerticalAlign;
  6613. var y = lineTop + lineHeight / 2;
  6614. if (textVerticalAlign === 'top') {
  6615. y = lineTop + token.height / 2;
  6616. }
  6617. else if (textVerticalAlign === 'bottom') {
  6618. y = lineTop + lineHeight - token.height / 2;
  6619. }
  6620. !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(
  6621. hostEl,
  6622. ctx,
  6623. tokenStyle,
  6624. textAlign === 'right'
  6625. ? x - token.width
  6626. : textAlign === 'center'
  6627. ? x - token.width / 2
  6628. : x,
  6629. y - token.height / 2,
  6630. token.width,
  6631. token.height
  6632. );
  6633. var textPadding = token.textPadding;
  6634. if (textPadding) {
  6635. x = getTextXForPadding(x, textAlign, textPadding);
  6636. y -= token.height / 2 - textPadding[2] - token.textHeight / 2;
  6637. }
  6638. setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));
  6639. setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');
  6640. setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));
  6641. setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));
  6642. setCtx(ctx, 'textAlign', textAlign);
  6643. // Force baseline to be "middle". Otherwise, if using "top", the
  6644. // text will offset downward a little bit in font "Microsoft YaHei".
  6645. setCtx(ctx, 'textBaseline', 'middle');
  6646. setCtx(ctx, 'font', token.font || DEFAULT_FONT);
  6647. var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);
  6648. var textFill = getFill(tokenStyle.textFill || style.textFill);
  6649. var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth);
  6650. // Fill after stroke so the outline will not cover the main part.
  6651. if (textStroke) {
  6652. setCtx(ctx, 'lineWidth', textStrokeWidth);
  6653. setCtx(ctx, 'strokeStyle', textStroke);
  6654. ctx.strokeText(token.text, x, y);
  6655. }
  6656. if (textFill) {
  6657. setCtx(ctx, 'fillStyle', textFill);
  6658. ctx.fillText(token.text, x, y);
  6659. }
  6660. }
  6661. function needDrawBackground(style) {
  6662. return style.textBackgroundColor
  6663. || (style.textBorderWidth && style.textBorderColor);
  6664. }
  6665. // style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius}
  6666. // shape: {x, y, width, height}
  6667. function drawBackground(hostEl, ctx, style, x, y, width, height) {
  6668. var textBackgroundColor = style.textBackgroundColor;
  6669. var textBorderWidth = style.textBorderWidth;
  6670. var textBorderColor = style.textBorderColor;
  6671. var isPlainBg = isString(textBackgroundColor);
  6672. setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);
  6673. setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');
  6674. setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);
  6675. setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);
  6676. if (isPlainBg || (textBorderWidth && textBorderColor)) {
  6677. ctx.beginPath();
  6678. var textBorderRadius = style.textBorderRadius;
  6679. if (!textBorderRadius) {
  6680. ctx.rect(x, y, width, height);
  6681. }
  6682. else {
  6683. buildPath(ctx, {
  6684. x: x, y: y, width: width, height: height, r: textBorderRadius
  6685. });
  6686. }
  6687. ctx.closePath();
  6688. }
  6689. if (isPlainBg) {
  6690. setCtx(ctx, 'fillStyle', textBackgroundColor);
  6691. ctx.fill();
  6692. }
  6693. else if (isObject$1(textBackgroundColor)) {
  6694. var image = textBackgroundColor.image;
  6695. image = createOrUpdateImage(
  6696. image, null, hostEl, onBgImageLoaded, textBackgroundColor
  6697. );
  6698. if (image && isImageReady(image)) {
  6699. ctx.drawImage(image, x, y, width, height);
  6700. }
  6701. }
  6702. if (textBorderWidth && textBorderColor) {
  6703. setCtx(ctx, 'lineWidth', textBorderWidth);
  6704. setCtx(ctx, 'strokeStyle', textBorderColor);
  6705. ctx.stroke();
  6706. }
  6707. }
  6708. function onBgImageLoaded(image, textBackgroundColor) {
  6709. // Replace image, so that `contain/text.js#parseRichText`
  6710. // will get correct result in next tick.
  6711. textBackgroundColor.image = image;
  6712. }
  6713. function getBoxPosition(blockHeiht, style, rect) {
  6714. var baseX = style.x || 0;
  6715. var baseY = style.y || 0;
  6716. var textAlign = style.textAlign;
  6717. var textVerticalAlign = style.textVerticalAlign;
  6718. // Text position represented by coord
  6719. if (rect) {
  6720. var textPosition = style.textPosition;
  6721. if (textPosition instanceof Array) {
  6722. // Percent
  6723. baseX = rect.x + parsePercent(textPosition[0], rect.width);
  6724. baseY = rect.y + parsePercent(textPosition[1], rect.height);
  6725. }
  6726. else {
  6727. var res = adjustTextPositionOnRect(
  6728. textPosition, rect, style.textDistance
  6729. );
  6730. baseX = res.x;
  6731. baseY = res.y;
  6732. // Default align and baseline when has textPosition
  6733. textAlign = textAlign || res.textAlign;
  6734. textVerticalAlign = textVerticalAlign || res.textVerticalAlign;
  6735. }
  6736. // textOffset is only support in RectText, otherwise
  6737. // we have to adjust boundingRect for textOffset.
  6738. var textOffset = style.textOffset;
  6739. if (textOffset) {
  6740. baseX += textOffset[0];
  6741. baseY += textOffset[1];
  6742. }
  6743. }
  6744. return {
  6745. baseX: baseX,
  6746. baseY: baseY,
  6747. textAlign: textAlign,
  6748. textVerticalAlign: textVerticalAlign
  6749. };
  6750. }
  6751. function setCtx(ctx, prop, value) {
  6752. ctx[prop] = fixShadow(ctx, prop, value);
  6753. return ctx[prop];
  6754. }
  6755. /**
  6756. * @param {string} [stroke] If specified, do not check style.textStroke.
  6757. * @param {string} [lineWidth] If specified, do not check style.textStroke.
  6758. * @param {number} style
  6759. */
  6760. function getStroke(stroke, lineWidth) {
  6761. return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')
  6762. ? null
  6763. // TODO pattern and gradient?
  6764. : (stroke.image || stroke.colorStops)
  6765. ? '#000'
  6766. : stroke;
  6767. }
  6768. function getFill(fill) {
  6769. return (fill == null || fill === 'none')
  6770. ? null
  6771. // TODO pattern and gradient?
  6772. : (fill.image || fill.colorStops)
  6773. ? '#000'
  6774. : fill;
  6775. }
  6776. function parsePercent(value, maxValue) {
  6777. if (typeof value === 'string') {
  6778. if (value.lastIndexOf('%') >= 0) {
  6779. return parseFloat(value) / 100 * maxValue;
  6780. }
  6781. return parseFloat(value);
  6782. }
  6783. return value;
  6784. }
  6785. function getTextXForPadding(x, textAlign, textPadding) {
  6786. return textAlign === 'right'
  6787. ? (x - textPadding[1])
  6788. : textAlign === 'center'
  6789. ? (x + textPadding[3] / 2 - textPadding[1] / 2)
  6790. : (x + textPadding[3]);
  6791. }
  6792. /**
  6793. * @param {string} text
  6794. * @param {module:zrender/Style} style
  6795. * @return {boolean}
  6796. */
  6797. function needDrawText(text, style) {
  6798. return text != null
  6799. && (text
  6800. || style.textBackgroundColor
  6801. || (style.textBorderWidth && style.textBorderColor)
  6802. || style.textPadding
  6803. );
  6804. }
  6805. /**
  6806. * Mixin for drawing text in a element bounding rect
  6807. * @module zrender/mixin/RectText
  6808. */
  6809. var tmpRect$1 = new BoundingRect();
  6810. var RectText = function () {};
  6811. RectText.prototype = {
  6812. constructor: RectText,
  6813. /**
  6814. * Draw text in a rect with specified position.
  6815. * @param {CanvasRenderingContext2D} ctx
  6816. * @param {Object} rect Displayable rect
  6817. */
  6818. drawRectText: function (ctx, rect) {
  6819. var style = this.style;
  6820. rect = style.textRect || rect;
  6821. // Optimize, avoid normalize every time.
  6822. this.__dirty && normalizeTextStyle(style, true);
  6823. var text = style.text;
  6824. // Convert to string
  6825. text != null && (text += '');
  6826. if (!needDrawText(text, style)) {
  6827. return;
  6828. }
  6829. // FIXME
  6830. ctx.save();
  6831. // Transform rect to view space
  6832. var transform = this.transform;
  6833. if (!style.transformText) {
  6834. if (transform) {
  6835. tmpRect$1.copy(rect);
  6836. tmpRect$1.applyTransform(transform);
  6837. rect = tmpRect$1;
  6838. }
  6839. }
  6840. else {
  6841. this.setTransform(ctx);
  6842. }
  6843. // transformText and textRotation can not be used at the same time.
  6844. renderText(this, ctx, text, style, rect);
  6845. ctx.restore();
  6846. }
  6847. };
  6848. /**
  6849. * 可绘制的图形基类
  6850. * Base class of all displayable graphic objects
  6851. * @module zrender/graphic/Displayable
  6852. */
  6853. /**
  6854. * @alias module:zrender/graphic/Displayable
  6855. * @extends module:zrender/Element
  6856. * @extends module:zrender/graphic/mixin/RectText
  6857. */
  6858. function Displayable(opts) {
  6859. opts = opts || {};
  6860. Element.call(this, opts);
  6861. // Extend properties
  6862. for (var name in opts) {
  6863. if (
  6864. opts.hasOwnProperty(name) &&
  6865. name !== 'style'
  6866. ) {
  6867. this[name] = opts[name];
  6868. }
  6869. }
  6870. /**
  6871. * @type {module:zrender/graphic/Style}
  6872. */
  6873. this.style = new Style(opts.style, this);
  6874. this._rect = null;
  6875. // Shapes for cascade clipping.
  6876. this.__clipPaths = [];
  6877. // FIXME Stateful must be mixined after style is setted
  6878. // Stateful.call(this, opts);
  6879. }
  6880. Displayable.prototype = {
  6881. constructor: Displayable,
  6882. type: 'displayable',
  6883. /**
  6884. * Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制
  6885. * Dirty flag. From which painter will determine if this displayable object needs brush
  6886. * @name module:zrender/graphic/Displayable#__dirty
  6887. * @type {boolean}
  6888. */
  6889. __dirty: true,
  6890. /**
  6891. * 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件
  6892. * If ignore drawing of the displayable object. Mouse event will still be triggered
  6893. * @name module:/zrender/graphic/Displayable#invisible
  6894. * @type {boolean}
  6895. * @default false
  6896. */
  6897. invisible: false,
  6898. /**
  6899. * @name module:/zrender/graphic/Displayable#z
  6900. * @type {number}
  6901. * @default 0
  6902. */
  6903. z: 0,
  6904. /**
  6905. * @name module:/zrender/graphic/Displayable#z
  6906. * @type {number}
  6907. * @default 0
  6908. */
  6909. z2: 0,
  6910. /**
  6911. * z层level,决定绘画在哪层canvas中
  6912. * @name module:/zrender/graphic/Displayable#zlevel
  6913. * @type {number}
  6914. * @default 0
  6915. */
  6916. zlevel: 0,
  6917. /**
  6918. * 是否可拖拽
  6919. * @name module:/zrender/graphic/Displayable#draggable
  6920. * @type {boolean}
  6921. * @default false
  6922. */
  6923. draggable: false,
  6924. /**
  6925. * 是否正在拖拽
  6926. * @name module:/zrender/graphic/Displayable#draggable
  6927. * @type {boolean}
  6928. * @default false
  6929. */
  6930. dragging: false,
  6931. /**
  6932. * 是否相应鼠标事件
  6933. * @name module:/zrender/graphic/Displayable#silent
  6934. * @type {boolean}
  6935. * @default false
  6936. */
  6937. silent: false,
  6938. /**
  6939. * If enable culling
  6940. * @type {boolean}
  6941. * @default false
  6942. */
  6943. culling: false,
  6944. /**
  6945. * Mouse cursor when hovered
  6946. * @name module:/zrender/graphic/Displayable#cursor
  6947. * @type {string}
  6948. */
  6949. cursor: 'pointer',
  6950. /**
  6951. * If hover area is bounding rect
  6952. * @name module:/zrender/graphic/Displayable#rectHover
  6953. * @type {string}
  6954. */
  6955. rectHover: false,
  6956. /**
  6957. * Render the element progressively when the value >= 0,
  6958. * usefull for large data.
  6959. * @type {boolean}
  6960. */
  6961. progressive: false,
  6962. /**
  6963. * @type {boolean}
  6964. */
  6965. incremental: false,
  6966. // inplace is used with incremental
  6967. inplace: false,
  6968. beforeBrush: function (ctx) {},
  6969. afterBrush: function (ctx) {},
  6970. /**
  6971. * 图形绘制方法
  6972. * @param {CanvasRenderingContext2D} ctx
  6973. */
  6974. // Interface
  6975. brush: function (ctx, prevEl) {},
  6976. /**
  6977. * 获取最小包围盒
  6978. * @return {module:zrender/core/BoundingRect}
  6979. */
  6980. // Interface
  6981. getBoundingRect: function () {},
  6982. /**
  6983. * 判断坐标 x, y 是否在图形上
  6984. * If displayable element contain coord x, y
  6985. * @param {number} x
  6986. * @param {number} y
  6987. * @return {boolean}
  6988. */
  6989. contain: function (x, y) {
  6990. return this.rectContain(x, y);
  6991. },
  6992. /**
  6993. * @param {Function} cb
  6994. * @param {} context
  6995. */
  6996. traverse: function (cb, context) {
  6997. cb.call(context, this);
  6998. },
  6999. /**
  7000. * 判断坐标 x, y 是否在图形的包围盒上
  7001. * If bounding rect of element contain coord x, y
  7002. * @param {number} x
  7003. * @param {number} y
  7004. * @return {boolean}
  7005. */
  7006. rectContain: function (x, y) {
  7007. var coord = this.transformCoordToLocal(x, y);
  7008. var rect = this.getBoundingRect();
  7009. return rect.contain(coord[0], coord[1]);
  7010. },
  7011. /**
  7012. * 标记图形元素为脏,并且在下一帧重绘
  7013. * Mark displayable element dirty and refresh next frame
  7014. */
  7015. dirty: function () {
  7016. this.__dirty = true;
  7017. this._rect = null;
  7018. this.__zr && this.__zr.refresh();
  7019. },
  7020. /**
  7021. * 图形是否会触发事件
  7022. * If displayable object binded any event
  7023. * @return {boolean}
  7024. */
  7025. // TODO, 通过 bind 绑定的事件
  7026. // isSilent: function () {
  7027. // return !(
  7028. // this.hoverable || this.draggable
  7029. // || this.onmousemove || this.onmouseover || this.onmouseout
  7030. // || this.onmousedown || this.onmouseup || this.onclick
  7031. // || this.ondragenter || this.ondragover || this.ondragleave
  7032. // || this.ondrop
  7033. // );
  7034. // },
  7035. /**
  7036. * Alias for animate('style')
  7037. * @param {boolean} loop
  7038. */
  7039. animateStyle: function (loop) {
  7040. return this.animate('style', loop);
  7041. },
  7042. attrKV: function (key, value) {
  7043. if (key !== 'style') {
  7044. Element.prototype.attrKV.call(this, key, value);
  7045. }
  7046. else {
  7047. this.style.set(value);
  7048. }
  7049. },
  7050. /**
  7051. * @param {Object|string} key
  7052. * @param {*} value
  7053. */
  7054. setStyle: function (key, value) {
  7055. this.style.set(key, value);
  7056. this.dirty(false);
  7057. return this;
  7058. },
  7059. /**
  7060. * Use given style object
  7061. * @param {Object} obj
  7062. */
  7063. useStyle: function (obj) {
  7064. this.style = new Style(obj, this);
  7065. this.dirty(false);
  7066. return this;
  7067. }
  7068. };
  7069. inherits(Displayable, Element);
  7070. mixin(Displayable, RectText);
  7071. /**
  7072. * @alias zrender/graphic/Image
  7073. * @extends module:zrender/graphic/Displayable
  7074. * @constructor
  7075. * @param {Object} opts
  7076. */
  7077. function ZImage(opts) {
  7078. Displayable.call(this, opts);
  7079. }
  7080. ZImage.prototype = {
  7081. constructor: ZImage,
  7082. type: 'image',
  7083. brush: function (ctx, prevEl) {
  7084. var style = this.style;
  7085. var src = style.image;
  7086. // Must bind each time
  7087. style.bind(ctx, this, prevEl);
  7088. var image = this._image = createOrUpdateImage(
  7089. src,
  7090. this._image,
  7091. this,
  7092. this.onload
  7093. );
  7094. if (!image || !isImageReady(image)) {
  7095. return;
  7096. }
  7097. // 图片已经加载完成
  7098. // if (image.nodeName.toUpperCase() == 'IMG') {
  7099. // if (!image.complete) {
  7100. // return;
  7101. // }
  7102. // }
  7103. // Else is canvas
  7104. var x = style.x || 0;
  7105. var y = style.y || 0;
  7106. var width = style.width;
  7107. var height = style.height;
  7108. var aspect = image.width / image.height;
  7109. if (width == null && height != null) {
  7110. // Keep image/height ratio
  7111. width = height * aspect;
  7112. }
  7113. else if (height == null && width != null) {
  7114. height = width / aspect;
  7115. }
  7116. else if (width == null && height == null) {
  7117. width = image.width;
  7118. height = image.height;
  7119. }
  7120. // 设置transform
  7121. this.setTransform(ctx);
  7122. if (style.sWidth && style.sHeight) {
  7123. var sx = style.sx || 0;
  7124. var sy = style.sy || 0;
  7125. ctx.drawImage(
  7126. image,
  7127. sx, sy, style.sWidth, style.sHeight,
  7128. x, y, width, height
  7129. );
  7130. }
  7131. else if (style.sx && style.sy) {
  7132. var sx = style.sx;
  7133. var sy = style.sy;
  7134. var sWidth = width - sx;
  7135. var sHeight = height - sy;
  7136. ctx.drawImage(
  7137. image,
  7138. sx, sy, sWidth, sHeight,
  7139. x, y, width, height
  7140. );
  7141. }
  7142. else {
  7143. ctx.drawImage(image, x, y, width, height);
  7144. }
  7145. // Draw rect text
  7146. if (style.text != null) {
  7147. // Only restore transform when needs draw text.
  7148. this.restoreTransform(ctx);
  7149. this.drawRectText(ctx, this.getBoundingRect());
  7150. }
  7151. },
  7152. getBoundingRect: function () {
  7153. var style = this.style;
  7154. if (! this._rect) {
  7155. this._rect = new BoundingRect(
  7156. style.x || 0, style.y || 0, style.width || 0, style.height || 0
  7157. );
  7158. }
  7159. return this._rect;
  7160. }
  7161. };
  7162. inherits(ZImage, Displayable);
  7163. var HOVER_LAYER_ZLEVEL = 1e5;
  7164. var CANVAS_ZLEVEL = 314159;
  7165. var EL_AFTER_INCREMENTAL_INC = 0.01;
  7166. var INCREMENTAL_INC = 0.001;
  7167. function parseInt10(val) {
  7168. return parseInt(val, 10);
  7169. }
  7170. function isLayerValid(layer) {
  7171. if (!layer) {
  7172. return false;
  7173. }
  7174. if (layer.__builtin__) {
  7175. return true;
  7176. }
  7177. if (typeof(layer.resize) !== 'function'
  7178. || typeof(layer.refresh) !== 'function'
  7179. ) {
  7180. return false;
  7181. }
  7182. return true;
  7183. }
  7184. var tmpRect = new BoundingRect(0, 0, 0, 0);
  7185. var viewRect = new BoundingRect(0, 0, 0, 0);
  7186. function isDisplayableCulled(el, width, height) {
  7187. tmpRect.copy(el.getBoundingRect());
  7188. if (el.transform) {
  7189. tmpRect.applyTransform(el.transform);
  7190. }
  7191. viewRect.width = width;
  7192. viewRect.height = height;
  7193. return !tmpRect.intersect(viewRect);
  7194. }
  7195. function isClipPathChanged(clipPaths, prevClipPaths) {
  7196. if (clipPaths == prevClipPaths) { // Can both be null or undefined
  7197. return false;
  7198. }
  7199. if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {
  7200. return true;
  7201. }
  7202. for (var i = 0; i < clipPaths.length; i++) {
  7203. if (clipPaths[i] !== prevClipPaths[i]) {
  7204. return true;
  7205. }
  7206. }
  7207. }
  7208. function doClip(clipPaths, ctx) {
  7209. for (var i = 0; i < clipPaths.length; i++) {
  7210. var clipPath = clipPaths[i];
  7211. clipPath.setTransform(ctx);
  7212. ctx.beginPath();
  7213. clipPath.buildPath(ctx, clipPath.shape);
  7214. ctx.clip();
  7215. // Transform back
  7216. clipPath.restoreTransform(ctx);
  7217. }
  7218. }
  7219. function createRoot(width, height) {
  7220. var domRoot = document.createElement('div');
  7221. // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬
  7222. domRoot.style.cssText = [
  7223. 'position:relative',
  7224. 'overflow:hidden',
  7225. 'width:' + width + 'px',
  7226. 'height:' + height + 'px',
  7227. 'padding:0',
  7228. 'margin:0',
  7229. 'border-width:0'
  7230. ].join(';') + ';';
  7231. return domRoot;
  7232. }
  7233. /**
  7234. * @alias module:zrender/Painter
  7235. * @constructor
  7236. * @param {HTMLElement} root 绘图容器
  7237. * @param {module:zrender/Storage} storage
  7238. * @param {Object} opts
  7239. */
  7240. var Painter = function (root, storage, opts) {
  7241. this.type = 'canvas';
  7242. // In node environment using node-canvas
  7243. var singleCanvas = !root.nodeName // In node ?
  7244. || root.nodeName.toUpperCase() === 'CANVAS';
  7245. this._opts = opts = extend({}, opts || {});
  7246. /**
  7247. * @type {number}
  7248. */
  7249. this.dpr = opts.devicePixelRatio || devicePixelRatio;
  7250. /**
  7251. * @type {boolean}
  7252. * @private
  7253. */
  7254. this._singleCanvas = singleCanvas;
  7255. /**
  7256. * 绘图容器
  7257. * @type {HTMLElement}
  7258. */
  7259. this.root = root;
  7260. var rootStyle = root.style;
  7261. if (rootStyle) {
  7262. rootStyle['-webkit-tap-highlight-color'] = 'transparent';
  7263. rootStyle['-webkit-user-select'] =
  7264. rootStyle['user-select'] =
  7265. rootStyle['-webkit-touch-callout'] = 'none';
  7266. root.innerHTML = '';
  7267. }
  7268. /**
  7269. * @type {module:zrender/Storage}
  7270. */
  7271. this.storage = storage;
  7272. /**
  7273. * @type {Array.<number>}
  7274. * @private
  7275. */
  7276. var zlevelList = this._zlevelList = [];
  7277. /**
  7278. * @type {Object.<string, module:zrender/Layer>}
  7279. * @private
  7280. */
  7281. var layers = this._layers = {};
  7282. /**
  7283. * @type {Object.<string, Object>}
  7284. * @private
  7285. */
  7286. this._layerConfig = {};
  7287. /**
  7288. * zrender will do compositing when root is a canvas and have multiple zlevels.
  7289. */
  7290. this._needsManuallyCompositing = false;
  7291. if (!singleCanvas) {
  7292. this._width = this._getSize(0);
  7293. this._height = this._getSize(1);
  7294. var domRoot = this._domRoot = createRoot(
  7295. this._width, this._height
  7296. );
  7297. root.appendChild(domRoot);
  7298. }
  7299. else {
  7300. if (opts.width != null) {
  7301. root.width = opts.width;
  7302. }
  7303. if (opts.height != null) {
  7304. root.height = opts.height;
  7305. }
  7306. // Use canvas width and height directly
  7307. var width = root.width;
  7308. var height = root.height;
  7309. this._width = width;
  7310. this._height = height;
  7311. // Create layer if only one given canvas
  7312. // Device pixel ratio is fixed to 1 because given canvas has its specified width and height
  7313. var mainLayer = new Layer(root, this, 1);
  7314. mainLayer.__builtin__ = true;
  7315. mainLayer.initContext();
  7316. // FIXME Use canvas width and height
  7317. // mainLayer.resize(width, height);
  7318. layers[CANVAS_ZLEVEL] = mainLayer;
  7319. // Not use common zlevel.
  7320. zlevelList.push(CANVAS_ZLEVEL);
  7321. this._domRoot = root;
  7322. }
  7323. /**
  7324. * @type {module:zrender/Layer}
  7325. * @private
  7326. */
  7327. this._hoverlayer = null;
  7328. this._hoverElements = [];
  7329. };
  7330. Painter.prototype = {
  7331. constructor: Painter,
  7332. getType: function () {
  7333. return 'canvas';
  7334. },
  7335. /**
  7336. * If painter use a single canvas
  7337. * @return {boolean}
  7338. */
  7339. isSingleCanvas: function () {
  7340. return this._singleCanvas;
  7341. },
  7342. /**
  7343. * @return {HTMLDivElement}
  7344. */
  7345. getViewportRoot: function () {
  7346. return this._domRoot;
  7347. },
  7348. getViewportRootOffset: function () {
  7349. var viewportRoot = this.getViewportRoot();
  7350. if (viewportRoot) {
  7351. return {
  7352. offsetLeft: viewportRoot.offsetLeft || 0,
  7353. offsetTop: viewportRoot.offsetTop || 0
  7354. };
  7355. }
  7356. },
  7357. /**
  7358. * 刷新
  7359. * @param {boolean} [paintAll=false] 强制绘制所有displayable
  7360. */
  7361. refresh: function (paintAll) {
  7362. var list = this.storage.getDisplayList(true);
  7363. var zlevelList = this._zlevelList;
  7364. this._redrawId = Math.random();
  7365. this._paintList(list, paintAll, this._redrawId);
  7366. // Paint custum layers
  7367. for (var i = 0; i < zlevelList.length; i++) {
  7368. var z = zlevelList[i];
  7369. var layer = this._layers[z];
  7370. if (!layer.__builtin__ && layer.refresh) {
  7371. layer.refresh();
  7372. }
  7373. }
  7374. this.refreshHover();
  7375. return this;
  7376. },
  7377. addHover: function (el, hoverStyle) {
  7378. if (el.__hoverMir) {
  7379. return;
  7380. }
  7381. var elMirror = new el.constructor({
  7382. style: el.style,
  7383. shape: el.shape
  7384. });
  7385. elMirror.__from = el;
  7386. el.__hoverMir = elMirror;
  7387. elMirror.setStyle(hoverStyle);
  7388. this._hoverElements.push(elMirror);
  7389. },
  7390. removeHover: function (el) {
  7391. var elMirror = el.__hoverMir;
  7392. var hoverElements = this._hoverElements;
  7393. var idx = indexOf(hoverElements, elMirror);
  7394. if (idx >= 0) {
  7395. hoverElements.splice(idx, 1);
  7396. }
  7397. el.__hoverMir = null;
  7398. },
  7399. clearHover: function (el) {
  7400. var hoverElements = this._hoverElements;
  7401. for (var i = 0; i < hoverElements.length; i++) {
  7402. var from = hoverElements[i].__from;
  7403. if (from) {
  7404. from.__hoverMir = null;
  7405. }
  7406. }
  7407. hoverElements.length = 0;
  7408. },
  7409. refreshHover: function () {
  7410. var hoverElements = this._hoverElements;
  7411. var len = hoverElements.length;
  7412. var hoverLayer = this._hoverlayer;
  7413. hoverLayer && hoverLayer.clear();
  7414. if (!len) {
  7415. return;
  7416. }
  7417. sort(hoverElements, this.storage.displayableSortFunc);
  7418. // Use a extream large zlevel
  7419. // FIXME?
  7420. if (!hoverLayer) {
  7421. hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL);
  7422. }
  7423. var scope = {};
  7424. hoverLayer.ctx.save();
  7425. for (var i = 0; i < len;) {
  7426. var el = hoverElements[i];
  7427. var originalEl = el.__from;
  7428. // Original el is removed
  7429. // PENDING
  7430. if (!(originalEl && originalEl.__zr)) {
  7431. hoverElements.splice(i, 1);
  7432. originalEl.__hoverMir = null;
  7433. len--;
  7434. continue;
  7435. }
  7436. i++;
  7437. // Use transform
  7438. // FIXME style and shape ?
  7439. if (!originalEl.invisible) {
  7440. el.transform = originalEl.transform;
  7441. el.invTransform = originalEl.invTransform;
  7442. el.__clipPaths = originalEl.__clipPaths;
  7443. // el.
  7444. this._doPaintEl(el, hoverLayer, true, scope);
  7445. }
  7446. }
  7447. hoverLayer.ctx.restore();
  7448. },
  7449. getHoverLayer: function () {
  7450. return this.getLayer(HOVER_LAYER_ZLEVEL);
  7451. },
  7452. _paintList: function (list, paintAll, redrawId) {
  7453. if (this._redrawId !== redrawId) {
  7454. return;
  7455. }
  7456. paintAll = paintAll || false;
  7457. this._updateLayerStatus(list);
  7458. var finished = this._doPaintList(list, paintAll);
  7459. if (this._needsManuallyCompositing) {
  7460. this._compositeManually();
  7461. }
  7462. if (!finished) {
  7463. var self = this;
  7464. requestAnimationFrame(function () {
  7465. self._paintList(list, paintAll, redrawId);
  7466. });
  7467. }
  7468. },
  7469. _compositeManually: function () {
  7470. var ctx = this.getLayer(CANVAS_ZLEVEL).ctx;
  7471. var width = this._domRoot.width;
  7472. var height = this._domRoot.height;
  7473. ctx.clearRect(0, 0, width, height);
  7474. // PENDING, If only builtin layer?
  7475. this.eachBuiltinLayer(function (layer) {
  7476. if (layer.virtual) {
  7477. ctx.drawImage(layer.dom, 0, 0, width, height);
  7478. }
  7479. });
  7480. },
  7481. _doPaintList: function (list, paintAll) {
  7482. var layerList = [];
  7483. for (var zi = 0; zi < this._zlevelList.length; zi++) {
  7484. var zlevel = this._zlevelList[zi];
  7485. var layer = this._layers[zlevel];
  7486. if (layer.__builtin__
  7487. && layer !== this._hoverlayer
  7488. && (layer.__dirty || paintAll)
  7489. ) {
  7490. layerList.push(layer);
  7491. }
  7492. }
  7493. var finished = true;
  7494. for (var k = 0; k < layerList.length; k++) {
  7495. var layer = layerList[k];
  7496. var ctx = layer.ctx;
  7497. var scope = {};
  7498. ctx.save();
  7499. var start = paintAll ? layer.__startIndex : layer.__drawIndex;
  7500. var useTimer = !paintAll && layer.incremental && Date.now;
  7501. var startTime = useTimer && Date.now();
  7502. // All elements in this layer are cleared.
  7503. if (layer.__startIndex === layer.__endIndex) {
  7504. layer.clear();
  7505. }
  7506. else if (start === layer.__startIndex) {
  7507. var firstEl = list[start];
  7508. if (!firstEl.incremental || !firstEl.notClear || paintAll) {
  7509. layer.clear();
  7510. }
  7511. }
  7512. if (start === -1) {
  7513. console.error('For some unknown reason. drawIndex is -1');
  7514. start = layer.__startIndex;
  7515. }
  7516. for (var i = start; i < layer.__endIndex; i++) {
  7517. var el = list[i];
  7518. this._doPaintEl(el, layer, paintAll, scope);
  7519. el.__dirty = false;
  7520. if (useTimer) {
  7521. // Date.now can be executed in 13,025,305 ops/second.
  7522. var dTime = Date.now() - startTime;
  7523. // Give 15 millisecond to draw.
  7524. // The rest elements will be drawn in the next frame.
  7525. if (dTime > 15) {
  7526. break;
  7527. }
  7528. }
  7529. }
  7530. layer.__drawIndex = i;
  7531. if (layer.__drawIndex < layer.__endIndex) {
  7532. finished = false;
  7533. }
  7534. if (scope.prevElClipPaths) {
  7535. // Needs restore the state. If last drawn element is in the clipping area.
  7536. ctx.restore();
  7537. }
  7538. ctx.restore();
  7539. }
  7540. if (env$1.wxa) {
  7541. // Flush for weixin application
  7542. each$1(this._layers, function (layer) {
  7543. if (layer && layer.ctx && layer.ctx.draw) {
  7544. layer.ctx.draw();
  7545. }
  7546. });
  7547. }
  7548. return finished;
  7549. },
  7550. _doPaintEl: function (el, currentLayer, forcePaint, scope) {
  7551. var ctx = currentLayer.ctx;
  7552. var m = el.transform;
  7553. if (
  7554. (currentLayer.__dirty || forcePaint)
  7555. // Ignore invisible element
  7556. && !el.invisible
  7557. // Ignore transparent element
  7558. && el.style.opacity !== 0
  7559. // Ignore scale 0 element, in some environment like node-canvas
  7560. // Draw a scale 0 element can cause all following draw wrong
  7561. // And setTransform with scale 0 will cause set back transform failed.
  7562. && !(m && !m[0] && !m[3])
  7563. // Ignore culled element
  7564. && !(el.culling && isDisplayableCulled(el, this._width, this._height))
  7565. ) {
  7566. var clipPaths = el.__clipPaths;
  7567. // Optimize when clipping on group with several elements
  7568. if (!scope.prevElClipPaths
  7569. || isClipPathChanged(clipPaths, scope.prevElClipPaths)
  7570. ) {
  7571. // If has previous clipping state, restore from it
  7572. if (scope.prevElClipPaths) {
  7573. currentLayer.ctx.restore();
  7574. scope.prevElClipPaths = null;
  7575. // Reset prevEl since context has been restored
  7576. scope.prevEl = null;
  7577. }
  7578. // New clipping state
  7579. if (clipPaths) {
  7580. ctx.save();
  7581. doClip(clipPaths, ctx);
  7582. scope.prevElClipPaths = clipPaths;
  7583. }
  7584. }
  7585. el.beforeBrush && el.beforeBrush(ctx);
  7586. el.brush(ctx, scope.prevEl || null);
  7587. scope.prevEl = el;
  7588. el.afterBrush && el.afterBrush(ctx);
  7589. }
  7590. },
  7591. /**
  7592. * 获取 zlevel 所在层,如果不存在则会创建一个新的层
  7593. * @param {number} zlevel
  7594. * @param {boolean} virtual Virtual layer will not be inserted into dom.
  7595. * @return {module:zrender/Layer}
  7596. */
  7597. getLayer: function (zlevel, virtual) {
  7598. if (this._singleCanvas && !this._needsManuallyCompositing) {
  7599. zlevel = CANVAS_ZLEVEL;
  7600. }
  7601. var layer = this._layers[zlevel];
  7602. if (!layer) {
  7603. // Create a new layer
  7604. layer = new Layer('zr_' + zlevel, this, this.dpr);
  7605. layer.zlevel = zlevel;
  7606. layer.__builtin__ = true;
  7607. if (this._layerConfig[zlevel]) {
  7608. merge(layer, this._layerConfig[zlevel], true);
  7609. }
  7610. if (virtual) {
  7611. layer.virtual = virtual;
  7612. }
  7613. this.insertLayer(zlevel, layer);
  7614. // Context is created after dom inserted to document
  7615. // Or excanvas will get 0px clientWidth and clientHeight
  7616. layer.initContext();
  7617. }
  7618. return layer;
  7619. },
  7620. insertLayer: function (zlevel, layer) {
  7621. var layersMap = this._layers;
  7622. var zlevelList = this._zlevelList;
  7623. var len = zlevelList.length;
  7624. var prevLayer = null;
  7625. var i = -1;
  7626. var domRoot = this._domRoot;
  7627. if (layersMap[zlevel]) {
  7628. log$1('ZLevel ' + zlevel + ' has been used already');
  7629. return;
  7630. }
  7631. // Check if is a valid layer
  7632. if (!isLayerValid(layer)) {
  7633. log$1('Layer of zlevel ' + zlevel + ' is not valid');
  7634. return;
  7635. }
  7636. if (len > 0 && zlevel > zlevelList[0]) {
  7637. for (i = 0; i < len - 1; i++) {
  7638. if (
  7639. zlevelList[i] < zlevel
  7640. && zlevelList[i + 1] > zlevel
  7641. ) {
  7642. break;
  7643. }
  7644. }
  7645. prevLayer = layersMap[zlevelList[i]];
  7646. }
  7647. zlevelList.splice(i + 1, 0, zlevel);
  7648. layersMap[zlevel] = layer;
  7649. // Vitual layer will not directly show on the screen.
  7650. // (It can be a WebGL layer and assigned to a ZImage element)
  7651. // But it still under management of zrender.
  7652. if (!layer.virtual) {
  7653. if (prevLayer) {
  7654. var prevDom = prevLayer.dom;
  7655. if (prevDom.nextSibling) {
  7656. domRoot.insertBefore(
  7657. layer.dom,
  7658. prevDom.nextSibling
  7659. );
  7660. }
  7661. else {
  7662. domRoot.appendChild(layer.dom);
  7663. }
  7664. }
  7665. else {
  7666. if (domRoot.firstChild) {
  7667. domRoot.insertBefore(layer.dom, domRoot.firstChild);
  7668. }
  7669. else {
  7670. domRoot.appendChild(layer.dom);
  7671. }
  7672. }
  7673. }
  7674. },
  7675. // Iterate each layer
  7676. eachLayer: function (cb, context) {
  7677. var zlevelList = this._zlevelList;
  7678. var z;
  7679. var i;
  7680. for (i = 0; i < zlevelList.length; i++) {
  7681. z = zlevelList[i];
  7682. cb.call(context, this._layers[z], z);
  7683. }
  7684. },
  7685. // Iterate each buildin layer
  7686. eachBuiltinLayer: function (cb, context) {
  7687. var zlevelList = this._zlevelList;
  7688. var layer;
  7689. var z;
  7690. var i;
  7691. for (i = 0; i < zlevelList.length; i++) {
  7692. z = zlevelList[i];
  7693. layer = this._layers[z];
  7694. if (layer.__builtin__) {
  7695. cb.call(context, layer, z);
  7696. }
  7697. }
  7698. },
  7699. // Iterate each other layer except buildin layer
  7700. eachOtherLayer: function (cb, context) {
  7701. var zlevelList = this._zlevelList;
  7702. var layer;
  7703. var z;
  7704. var i;
  7705. for (i = 0; i < zlevelList.length; i++) {
  7706. z = zlevelList[i];
  7707. layer = this._layers[z];
  7708. if (!layer.__builtin__) {
  7709. cb.call(context, layer, z);
  7710. }
  7711. }
  7712. },
  7713. /**
  7714. * 获取所有已创建的层
  7715. * @param {Array.<module:zrender/Layer>} [prevLayer]
  7716. */
  7717. getLayers: function () {
  7718. return this._layers;
  7719. },
  7720. _updateLayerStatus: function (list) {
  7721. this.eachBuiltinLayer(function (layer, z) {
  7722. layer.__dirty = layer.__used = false;
  7723. });
  7724. function updatePrevLayer(idx) {
  7725. if (prevLayer) {
  7726. if (prevLayer.__endIndex !== idx) {
  7727. prevLayer.__dirty = true;
  7728. }
  7729. prevLayer.__endIndex = idx;
  7730. }
  7731. }
  7732. if (this._singleCanvas) {
  7733. for (var i = 1; i < list.length; i++) {
  7734. var el = list[i];
  7735. if (el.zlevel !== list[i - 1].zlevel || el.incremental) {
  7736. this._needsManuallyCompositing = true;
  7737. break;
  7738. }
  7739. }
  7740. }
  7741. var prevLayer = null;
  7742. var incrementalLayerCount = 0;
  7743. for (var i = 0; i < list.length; i++) {
  7744. var el = list[i];
  7745. var zlevel = el.zlevel;
  7746. var layer;
  7747. // PENDING If change one incremental element style ?
  7748. // TODO Where there are non-incremental elements between incremental elements.
  7749. if (el.incremental) {
  7750. layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing);
  7751. layer.incremental = true;
  7752. incrementalLayerCount = 1;
  7753. }
  7754. else {
  7755. layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing);
  7756. }
  7757. if (!layer.__builtin__) {
  7758. log$1('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id);
  7759. }
  7760. if (layer !== prevLayer) {
  7761. layer.__used = true;
  7762. if (layer.__startIndex !== i) {
  7763. layer.__dirty = true;
  7764. }
  7765. layer.__startIndex = i;
  7766. if (!layer.incremental) {
  7767. layer.__drawIndex = i;
  7768. }
  7769. else {
  7770. // Mark layer draw index needs to update.
  7771. layer.__drawIndex = -1;
  7772. }
  7773. updatePrevLayer(i);
  7774. prevLayer = layer;
  7775. }
  7776. if (el.__dirty) {
  7777. layer.__dirty = true;
  7778. if (layer.incremental && layer.__drawIndex < 0) {
  7779. // Start draw from the first dirty element.
  7780. layer.__drawIndex = i;
  7781. }
  7782. }
  7783. }
  7784. updatePrevLayer(i);
  7785. this.eachBuiltinLayer(function (layer, z) {
  7786. // Used in last frame but not in this frame. Needs clear
  7787. if (!layer.__used && layer.getElementCount() > 0) {
  7788. layer.__dirty = true;
  7789. layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0;
  7790. }
  7791. // For incremental layer. In case start index changed and no elements are dirty.
  7792. if (layer.__dirty && layer.__drawIndex < 0) {
  7793. layer.__drawIndex = layer.__startIndex;
  7794. }
  7795. });
  7796. },
  7797. /**
  7798. * 清除hover层外所有内容
  7799. */
  7800. clear: function () {
  7801. this.eachBuiltinLayer(this._clearLayer);
  7802. return this;
  7803. },
  7804. _clearLayer: function (layer) {
  7805. layer.clear();
  7806. },
  7807. /**
  7808. * 修改指定zlevel的绘制参数
  7809. *
  7810. * @param {string} zlevel
  7811. * @param {Object} config 配置对象
  7812. * @param {string} [config.clearColor=0] 每次清空画布的颜色
  7813. * @param {string} [config.motionBlur=false] 是否开启动态模糊
  7814. * @param {number} [config.lastFrameAlpha=0.7]
  7815. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  7816. */
  7817. configLayer: function (zlevel, config) {
  7818. if (config) {
  7819. var layerConfig = this._layerConfig;
  7820. if (!layerConfig[zlevel]) {
  7821. layerConfig[zlevel] = config;
  7822. }
  7823. else {
  7824. merge(layerConfig[zlevel], config, true);
  7825. }
  7826. for (var i = 0; i < this._zlevelList.length; i++) {
  7827. var _zlevel = this._zlevelList[i];
  7828. if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) {
  7829. var layer = this._layers[_zlevel];
  7830. merge(layer, layerConfig[zlevel], true);
  7831. }
  7832. }
  7833. }
  7834. },
  7835. /**
  7836. * 删除指定层
  7837. * @param {number} zlevel 层所在的zlevel
  7838. */
  7839. delLayer: function (zlevel) {
  7840. var layers = this._layers;
  7841. var zlevelList = this._zlevelList;
  7842. var layer = layers[zlevel];
  7843. if (!layer) {
  7844. return;
  7845. }
  7846. layer.dom.parentNode.removeChild(layer.dom);
  7847. delete layers[zlevel];
  7848. zlevelList.splice(indexOf(zlevelList, zlevel), 1);
  7849. },
  7850. /**
  7851. * 区域大小变化后重绘
  7852. */
  7853. resize: function (width, height) {
  7854. if (!this._domRoot.style) { // Maybe in node or worker
  7855. if (width == null || height == null) {
  7856. return;
  7857. }
  7858. this._width = width;
  7859. this._height = height;
  7860. this.getLayer(CANVAS_ZLEVEL).resize(width, height);
  7861. }
  7862. else {
  7863. var domRoot = this._domRoot;
  7864. // FIXME Why ?
  7865. domRoot.style.display = 'none';
  7866. // Save input w/h
  7867. var opts = this._opts;
  7868. width != null && (opts.width = width);
  7869. height != null && (opts.height = height);
  7870. width = this._getSize(0);
  7871. height = this._getSize(1);
  7872. domRoot.style.display = '';
  7873. // 优化没有实际改变的resize
  7874. if (this._width != width || height != this._height) {
  7875. domRoot.style.width = width + 'px';
  7876. domRoot.style.height = height + 'px';
  7877. for (var id in this._layers) {
  7878. if (this._layers.hasOwnProperty(id)) {
  7879. this._layers[id].resize(width, height);
  7880. }
  7881. }
  7882. each$1(this._progressiveLayers, function (layer) {
  7883. layer.resize(width, height);
  7884. });
  7885. this.refresh(true);
  7886. }
  7887. this._width = width;
  7888. this._height = height;
  7889. }
  7890. return this;
  7891. },
  7892. /**
  7893. * 清除单独的一个层
  7894. * @param {number} zlevel
  7895. */
  7896. clearLayer: function (zlevel) {
  7897. var layer = this._layers[zlevel];
  7898. if (layer) {
  7899. layer.clear();
  7900. }
  7901. },
  7902. /**
  7903. * 释放
  7904. */
  7905. dispose: function () {
  7906. this.root.innerHTML = '';
  7907. this.root =
  7908. this.storage =
  7909. this._domRoot =
  7910. this._layers = null;
  7911. },
  7912. /**
  7913. * Get canvas which has all thing rendered
  7914. * @param {Object} opts
  7915. * @param {string} [opts.backgroundColor]
  7916. * @param {number} [opts.pixelRatio]
  7917. */
  7918. getRenderedCanvas: function (opts) {
  7919. opts = opts || {};
  7920. if (this._singleCanvas && !this._compositeManually) {
  7921. return this._layers[CANVAS_ZLEVEL].dom;
  7922. }
  7923. var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
  7924. imageLayer.initContext();
  7925. imageLayer.clearColor = opts.backgroundColor;
  7926. imageLayer.clear();
  7927. if (opts.pixelRatio <= this.dpr) {
  7928. this.refresh();
  7929. var width = imageLayer.dom.width;
  7930. var height = imageLayer.dom.height;
  7931. var ctx = imageLayer.ctx;
  7932. this.eachLayer(function (layer) {
  7933. if (layer.__builtin__) {
  7934. ctx.drawImage(layer.dom, 0, 0, width, height);
  7935. }
  7936. else if (layer.renderToCanvas) {
  7937. imageLayer.ctx.save();
  7938. layer.renderToCanvas(imageLayer.ctx);
  7939. imageLayer.ctx.restore();
  7940. }
  7941. });
  7942. }
  7943. else {
  7944. // PENDING, echarts-gl and incremental rendering.
  7945. var scope = {};
  7946. var displayList = this.storage.getDisplayList(true);
  7947. for (var i = 0; i < displayList.length; i++) {
  7948. var el = displayList[i];
  7949. this._doPaintEl(el, imageLayer, true, scope);
  7950. }
  7951. }
  7952. return imageLayer.dom;
  7953. },
  7954. /**
  7955. * 获取绘图区域宽度
  7956. */
  7957. getWidth: function () {
  7958. return this._width;
  7959. },
  7960. /**
  7961. * 获取绘图区域高度
  7962. */
  7963. getHeight: function () {
  7964. return this._height;
  7965. },
  7966. _getSize: function (whIdx) {
  7967. var opts = this._opts;
  7968. var wh = ['width', 'height'][whIdx];
  7969. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  7970. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  7971. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  7972. if (opts[wh] != null && opts[wh] !== 'auto') {
  7973. return parseFloat(opts[wh]);
  7974. }
  7975. var root = this.root;
  7976. // IE8 does not support getComputedStyle, but it use VML.
  7977. var stl = document.defaultView.getComputedStyle(root);
  7978. return (
  7979. (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
  7980. - (parseInt10(stl[plt]) || 0)
  7981. - (parseInt10(stl[prb]) || 0)
  7982. ) | 0;
  7983. },
  7984. pathToImage: function (path, dpr) {
  7985. dpr = dpr || this.dpr;
  7986. var canvas = document.createElement('canvas');
  7987. var ctx = canvas.getContext('2d');
  7988. var rect = path.getBoundingRect();
  7989. var style = path.style;
  7990. var shadowBlurSize = style.shadowBlur * dpr;
  7991. var shadowOffsetX = style.shadowOffsetX * dpr;
  7992. var shadowOffsetY = style.shadowOffsetY * dpr;
  7993. var lineWidth = style.hasStroke() ? style.lineWidth : 0;
  7994. var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
  7995. var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
  7996. var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
  7997. var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
  7998. var width = rect.width + leftMargin + rightMargin;
  7999. var height = rect.height + topMargin + bottomMargin;
  8000. canvas.width = width * dpr;
  8001. canvas.height = height * dpr;
  8002. ctx.scale(dpr, dpr);
  8003. ctx.clearRect(0, 0, width, height);
  8004. ctx.dpr = dpr;
  8005. var pathTransform = {
  8006. position: path.position,
  8007. rotation: path.rotation,
  8008. scale: path.scale
  8009. };
  8010. path.position = [leftMargin - rect.x, topMargin - rect.y];
  8011. path.rotation = 0;
  8012. path.scale = [1, 1];
  8013. path.updateTransform();
  8014. if (path) {
  8015. path.brush(ctx);
  8016. }
  8017. var ImageShape = ZImage;
  8018. var imgShape = new ImageShape({
  8019. style: {
  8020. x: 0,
  8021. y: 0,
  8022. image: canvas
  8023. }
  8024. });
  8025. if (pathTransform.position != null) {
  8026. imgShape.position = path.position = pathTransform.position;
  8027. }
  8028. if (pathTransform.rotation != null) {
  8029. imgShape.rotation = path.rotation = pathTransform.rotation;
  8030. }
  8031. if (pathTransform.scale != null) {
  8032. imgShape.scale = path.scale = pathTransform.scale;
  8033. }
  8034. return imgShape;
  8035. }
  8036. };
  8037. /**
  8038. * 事件辅助类
  8039. * @module zrender/core/event
  8040. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  8041. */
  8042. var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;
  8043. var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;
  8044. function getBoundingClientRect(el) {
  8045. // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect
  8046. return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0};
  8047. }
  8048. // `calculate` is optional, default false
  8049. function clientToLocal(el, e, out, calculate) {
  8050. out = out || {};
  8051. // According to the W3C Working Draft, offsetX and offsetY should be relative
  8052. // to the padding edge of the target element. The only browser using this convention
  8053. // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does
  8054. // not support the properties.
  8055. // (see http://www.jacklmoore.com/notes/mouse-position/)
  8056. // In zr painter.dom, padding edge equals to border edge.
  8057. // FIXME
  8058. // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and
  8059. // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y
  8060. // is too complex. So css-transfrom dont support in this case temporarily.
  8061. if (calculate || !env$1.canvasSupported) {
  8062. defaultGetZrXY(el, e, out);
  8063. }
  8064. // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned
  8065. // ancestor element, so we should make sure el is positioned (e.g., not position:static).
  8066. // BTW1, Webkit don't return the same results as FF in non-simple cases (like add
  8067. // zoom-factor, overflow / opacity layers, transforms ...)
  8068. // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.
  8069. // <https://bugs.jquery.com/ticket/8523#comment:14>
  8070. // BTW3, In ff, offsetX/offsetY is always 0.
  8071. else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) {
  8072. out.zrX = e.layerX;
  8073. out.zrY = e.layerY;
  8074. }
  8075. // For IE6+, chrome, safari, opera. (When will ff support offsetX?)
  8076. else if (e.offsetX != null) {
  8077. out.zrX = e.offsetX;
  8078. out.zrY = e.offsetY;
  8079. }
  8080. // For some other device, e.g., IOS safari.
  8081. else {
  8082. defaultGetZrXY(el, e, out);
  8083. }
  8084. return out;
  8085. }
  8086. function defaultGetZrXY(el, e, out) {
  8087. // This well-known method below does not support css transform.
  8088. var box = getBoundingClientRect(el);
  8089. out.zrX = e.clientX - box.left;
  8090. out.zrY = e.clientY - box.top;
  8091. }
  8092. /**
  8093. * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标.
  8094. * `calculate` is optional, default false.
  8095. */
  8096. function normalizeEvent(el, e, calculate) {
  8097. e = e || window.event;
  8098. if (e.zrX != null) {
  8099. return e;
  8100. }
  8101. var eventType = e.type;
  8102. var isTouch = eventType && eventType.indexOf('touch') >= 0;
  8103. if (!isTouch) {
  8104. clientToLocal(el, e, e, calculate);
  8105. e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
  8106. }
  8107. else {
  8108. var touch = eventType != 'touchend'
  8109. ? e.targetTouches[0]
  8110. : e.changedTouches[0];
  8111. touch && clientToLocal(el, touch, e, calculate);
  8112. }
  8113. // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;
  8114. // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js
  8115. // If e.which has been defined, if may be readonly,
  8116. // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
  8117. var button = e.button;
  8118. if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {
  8119. e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));
  8120. }
  8121. return e;
  8122. }
  8123. function addEventListener(el, name, handler) {
  8124. if (isDomLevel2) {
  8125. el.addEventListener(name, handler);
  8126. }
  8127. else {
  8128. el.attachEvent('on' + name, handler);
  8129. }
  8130. }
  8131. function removeEventListener(el, name, handler) {
  8132. if (isDomLevel2) {
  8133. el.removeEventListener(name, handler);
  8134. }
  8135. else {
  8136. el.detachEvent('on' + name, handler);
  8137. }
  8138. }
  8139. /**
  8140. * preventDefault and stopPropagation.
  8141. * Notice: do not do that in zrender. Upper application
  8142. * do that if necessary.
  8143. *
  8144. * @memberOf module:zrender/core/event
  8145. * @method
  8146. * @param {Event} e : event对象
  8147. */
  8148. /**
  8149. * 动画主类, 调度和管理所有动画控制器
  8150. *
  8151. * @module zrender/animation/Animation
  8152. * @author pissang(https://github.com/pissang)
  8153. */
  8154. // TODO Additive animation
  8155. // http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
  8156. // https://developer.apple.com/videos/wwdc2014/#236
  8157. /**
  8158. * @typedef {Object} IZRenderStage
  8159. * @property {Function} update
  8160. */
  8161. /**
  8162. * @alias module:zrender/animation/Animation
  8163. * @constructor
  8164. * @param {Object} [options]
  8165. * @param {Function} [options.onframe]
  8166. * @param {IZRenderStage} [options.stage]
  8167. * @example
  8168. * var animation = new Animation();
  8169. * var obj = {
  8170. * x: 100,
  8171. * y: 100
  8172. * };
  8173. * animation.animate(node.position)
  8174. * .when(1000, {
  8175. * x: 500,
  8176. * y: 500
  8177. * })
  8178. * .when(2000, {
  8179. * x: 100,
  8180. * y: 100
  8181. * })
  8182. * .start('spline');
  8183. */
  8184. var Animation = function (options) {
  8185. options = options || {};
  8186. this.stage = options.stage || {};
  8187. this.onframe = options.onframe || function() {};
  8188. // private properties
  8189. this._clips = [];
  8190. this._running = false;
  8191. this._time;
  8192. this._pausedTime;
  8193. this._pauseStart;
  8194. this._paused = false;
  8195. Eventful.call(this);
  8196. };
  8197. Animation.prototype = {
  8198. constructor: Animation,
  8199. /**
  8200. * 添加 clip
  8201. * @param {module:zrender/animation/Clip} clip
  8202. */
  8203. addClip: function (clip) {
  8204. this._clips.push(clip);
  8205. },
  8206. /**
  8207. * 添加 animator
  8208. * @param {module:zrender/animation/Animator} animator
  8209. */
  8210. addAnimator: function (animator) {
  8211. animator.animation = this;
  8212. var clips = animator.getClips();
  8213. for (var i = 0; i < clips.length; i++) {
  8214. this.addClip(clips[i]);
  8215. }
  8216. },
  8217. /**
  8218. * 删除动画片段
  8219. * @param {module:zrender/animation/Clip} clip
  8220. */
  8221. removeClip: function(clip) {
  8222. var idx = indexOf(this._clips, clip);
  8223. if (idx >= 0) {
  8224. this._clips.splice(idx, 1);
  8225. }
  8226. },
  8227. /**
  8228. * 删除动画片段
  8229. * @param {module:zrender/animation/Animator} animator
  8230. */
  8231. removeAnimator: function (animator) {
  8232. var clips = animator.getClips();
  8233. for (var i = 0; i < clips.length; i++) {
  8234. this.removeClip(clips[i]);
  8235. }
  8236. animator.animation = null;
  8237. },
  8238. _update: function() {
  8239. var time = new Date().getTime() - this._pausedTime;
  8240. var delta = time - this._time;
  8241. var clips = this._clips;
  8242. var len = clips.length;
  8243. var deferredEvents = [];
  8244. var deferredClips = [];
  8245. for (var i = 0; i < len; i++) {
  8246. var clip = clips[i];
  8247. var e = clip.step(time, delta);
  8248. // Throw out the events need to be called after
  8249. // stage.update, like destroy
  8250. if (e) {
  8251. deferredEvents.push(e);
  8252. deferredClips.push(clip);
  8253. }
  8254. }
  8255. // Remove the finished clip
  8256. for (var i = 0; i < len;) {
  8257. if (clips[i]._needsRemove) {
  8258. clips[i] = clips[len - 1];
  8259. clips.pop();
  8260. len--;
  8261. }
  8262. else {
  8263. i++;
  8264. }
  8265. }
  8266. len = deferredEvents.length;
  8267. for (var i = 0; i < len; i++) {
  8268. deferredClips[i].fire(deferredEvents[i]);
  8269. }
  8270. this._time = time;
  8271. this.onframe(delta);
  8272. // Frame should before stage update. Upper application
  8273. // depends on the sequence (e.g., echarts-stream)
  8274. this.trigger('frame', delta);
  8275. if (this.stage.update) {
  8276. this.stage.update();
  8277. }
  8278. },
  8279. _startLoop: function () {
  8280. var self = this;
  8281. this._running = true;
  8282. function step() {
  8283. if (self._running) {
  8284. requestAnimationFrame(step);
  8285. !self._paused && self._update();
  8286. }
  8287. }
  8288. requestAnimationFrame(step);
  8289. },
  8290. /**
  8291. * 开始运行动画
  8292. */
  8293. start: function () {
  8294. this._time = new Date().getTime();
  8295. this._pausedTime = 0;
  8296. this._startLoop();
  8297. },
  8298. /**
  8299. * 停止运行动画
  8300. */
  8301. stop: function () {
  8302. this._running = false;
  8303. },
  8304. /**
  8305. * Pause
  8306. */
  8307. pause: function () {
  8308. if (!this._paused) {
  8309. this._pauseStart = new Date().getTime();
  8310. this._paused = true;
  8311. }
  8312. },
  8313. /**
  8314. * Resume
  8315. */
  8316. resume: function () {
  8317. if (this._paused) {
  8318. this._pausedTime += (new Date().getTime()) - this._pauseStart;
  8319. this._paused = false;
  8320. }
  8321. },
  8322. /**
  8323. * 清除所有动画片段
  8324. */
  8325. clear: function () {
  8326. this._clips = [];
  8327. },
  8328. /**
  8329. * 对一个目标创建一个animator对象,可以指定目标中的属性使用动画
  8330. * @param {Object} target
  8331. * @param {Object} options
  8332. * @param {boolean} [options.loop=false] 是否循环播放动画
  8333. * @param {Function} [options.getter=null]
  8334. * 如果指定getter函数,会通过getter函数取属性值
  8335. * @param {Function} [options.setter=null]
  8336. * 如果指定setter函数,会通过setter函数设置属性值
  8337. * @return {module:zrender/animation/Animation~Animator}
  8338. */
  8339. // TODO Gap
  8340. animate: function (target, options) {
  8341. options = options || {};
  8342. var animator = new Animator(
  8343. target,
  8344. options.loop,
  8345. options.getter,
  8346. options.setter
  8347. );
  8348. this.addAnimator(animator);
  8349. return animator;
  8350. }
  8351. };
  8352. mixin(Animation, Eventful);
  8353. /**
  8354. * Only implements needed gestures for mobile.
  8355. */
  8356. var GestureMgr = function () {
  8357. /**
  8358. * @private
  8359. * @type {Array.<Object>}
  8360. */
  8361. this._track = [];
  8362. };
  8363. GestureMgr.prototype = {
  8364. constructor: GestureMgr,
  8365. recognize: function (event, target, root) {
  8366. this._doTrack(event, target, root);
  8367. return this._recognize(event);
  8368. },
  8369. clear: function () {
  8370. this._track.length = 0;
  8371. return this;
  8372. },
  8373. _doTrack: function (event, target, root) {
  8374. var touches = event.touches;
  8375. if (!touches) {
  8376. return;
  8377. }
  8378. var trackItem = {
  8379. points: [],
  8380. touches: [],
  8381. target: target,
  8382. event: event
  8383. };
  8384. for (var i = 0, len = touches.length; i < len; i++) {
  8385. var touch = touches[i];
  8386. var pos = clientToLocal(root, touch, {});
  8387. trackItem.points.push([pos.zrX, pos.zrY]);
  8388. trackItem.touches.push(touch);
  8389. }
  8390. this._track.push(trackItem);
  8391. },
  8392. _recognize: function (event) {
  8393. for (var eventName in recognizers) {
  8394. if (recognizers.hasOwnProperty(eventName)) {
  8395. var gestureInfo = recognizers[eventName](this._track, event);
  8396. if (gestureInfo) {
  8397. return gestureInfo;
  8398. }
  8399. }
  8400. }
  8401. }
  8402. };
  8403. function dist$1(pointPair) {
  8404. var dx = pointPair[1][0] - pointPair[0][0];
  8405. var dy = pointPair[1][1] - pointPair[0][1];
  8406. return Math.sqrt(dx * dx + dy * dy);
  8407. }
  8408. function center(pointPair) {
  8409. return [
  8410. (pointPair[0][0] + pointPair[1][0]) / 2,
  8411. (pointPair[0][1] + pointPair[1][1]) / 2
  8412. ];
  8413. }
  8414. var recognizers = {
  8415. pinch: function (track, event) {
  8416. var trackLen = track.length;
  8417. if (!trackLen) {
  8418. return;
  8419. }
  8420. var pinchEnd = (track[trackLen - 1] || {}).points;
  8421. var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;
  8422. if (pinchPre
  8423. && pinchPre.length > 1
  8424. && pinchEnd
  8425. && pinchEnd.length > 1
  8426. ) {
  8427. var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);
  8428. !isFinite(pinchScale) && (pinchScale = 1);
  8429. event.pinchScale = pinchScale;
  8430. var pinchCenter = center(pinchEnd);
  8431. event.pinchX = pinchCenter[0];
  8432. event.pinchY = pinchCenter[1];
  8433. return {
  8434. type: 'pinch',
  8435. target: track[0].target,
  8436. event: event
  8437. };
  8438. }
  8439. }
  8440. // Only pinch currently.
  8441. };
  8442. var TOUCH_CLICK_DELAY = 300;
  8443. var mouseHandlerNames = [
  8444. 'click', 'dblclick', 'mousewheel', 'mouseout',
  8445. 'mouseup', 'mousedown', 'mousemove', 'contextmenu'
  8446. ];
  8447. var touchHandlerNames = [
  8448. 'touchstart', 'touchend', 'touchmove'
  8449. ];
  8450. var pointerEventNames = {
  8451. pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1
  8452. };
  8453. var pointerHandlerNames = map(mouseHandlerNames, function (name) {
  8454. var nm = name.replace('mouse', 'pointer');
  8455. return pointerEventNames[nm] ? nm : name;
  8456. });
  8457. function eventNameFix(name) {
  8458. return (name === 'mousewheel' && env$1.browser.firefox) ? 'DOMMouseScroll' : name;
  8459. }
  8460. function processGesture(proxy, event, stage) {
  8461. var gestureMgr = proxy._gestureMgr;
  8462. stage === 'start' && gestureMgr.clear();
  8463. var gestureInfo = gestureMgr.recognize(
  8464. event,
  8465. proxy.handler.findHover(event.zrX, event.zrY, null).target,
  8466. proxy.dom
  8467. );
  8468. stage === 'end' && gestureMgr.clear();
  8469. // Do not do any preventDefault here. Upper application do that if necessary.
  8470. if (gestureInfo) {
  8471. var type = gestureInfo.type;
  8472. event.gestureEvent = type;
  8473. proxy.handler.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event);
  8474. }
  8475. }
  8476. // function onMSGestureChange(proxy, event) {
  8477. // if (event.translationX || event.translationY) {
  8478. // // mousemove is carried by MSGesture to reduce the sensitivity.
  8479. // proxy.handler.dispatchToElement(event.target, 'mousemove', event);
  8480. // }
  8481. // if (event.scale !== 1) {
  8482. // event.pinchX = event.offsetX;
  8483. // event.pinchY = event.offsetY;
  8484. // event.pinchScale = event.scale;
  8485. // proxy.handler.dispatchToElement(event.target, 'pinch', event);
  8486. // }
  8487. // }
  8488. /**
  8489. * Prevent mouse event from being dispatched after Touch Events action
  8490. * @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>
  8491. * 1. Mobile browsers dispatch mouse events 300ms after touchend.
  8492. * 2. Chrome for Android dispatch mousedown for long-touch about 650ms
  8493. * Result: Blocking Mouse Events for 700ms.
  8494. */
  8495. function setTouchTimer(instance) {
  8496. instance._touching = true;
  8497. clearTimeout(instance._touchTimer);
  8498. instance._touchTimer = setTimeout(function () {
  8499. instance._touching = false;
  8500. }, 700);
  8501. }
  8502. var domHandlers = {
  8503. /**
  8504. * Mouse move handler
  8505. * @inner
  8506. * @param {Event} event
  8507. */
  8508. mousemove: function (event) {
  8509. event = normalizeEvent(this.dom, event);
  8510. this.trigger('mousemove', event);
  8511. },
  8512. /**
  8513. * Mouse out handler
  8514. * @inner
  8515. * @param {Event} event
  8516. */
  8517. mouseout: function (event) {
  8518. event = normalizeEvent(this.dom, event);
  8519. var element = event.toElement || event.relatedTarget;
  8520. if (element != this.dom) {
  8521. while (element && element.nodeType != 9) {
  8522. // 忽略包含在root中的dom引起的mouseOut
  8523. if (element === this.dom) {
  8524. return;
  8525. }
  8526. element = element.parentNode;
  8527. }
  8528. }
  8529. this.trigger('mouseout', event);
  8530. },
  8531. /**
  8532. * Touch开始响应函数
  8533. * @inner
  8534. * @param {Event} event
  8535. */
  8536. touchstart: function (event) {
  8537. // Default mouse behaviour should not be disabled here.
  8538. // For example, page may needs to be slided.
  8539. event = normalizeEvent(this.dom, event);
  8540. // Mark touch, which is useful in distinguish touch and
  8541. // mouse event in upper applicatoin.
  8542. event.zrByTouch = true;
  8543. this._lastTouchMoment = new Date();
  8544. processGesture(this, event, 'start');
  8545. // In touch device, trigger `mousemove`(`mouseover`) should
  8546. // be triggered, and must before `mousedown` triggered.
  8547. domHandlers.mousemove.call(this, event);
  8548. domHandlers.mousedown.call(this, event);
  8549. setTouchTimer(this);
  8550. },
  8551. /**
  8552. * Touch移动响应函数
  8553. * @inner
  8554. * @param {Event} event
  8555. */
  8556. touchmove: function (event) {
  8557. event = normalizeEvent(this.dom, event);
  8558. // Mark touch, which is useful in distinguish touch and
  8559. // mouse event in upper applicatoin.
  8560. event.zrByTouch = true;
  8561. processGesture(this, event, 'change');
  8562. // Mouse move should always be triggered no matter whether
  8563. // there is gestrue event, because mouse move and pinch may
  8564. // be used at the same time.
  8565. domHandlers.mousemove.call(this, event);
  8566. setTouchTimer(this);
  8567. },
  8568. /**
  8569. * Touch结束响应函数
  8570. * @inner
  8571. * @param {Event} event
  8572. */
  8573. touchend: function (event) {
  8574. event = normalizeEvent(this.dom, event);
  8575. // Mark touch, which is useful in distinguish touch and
  8576. // mouse event in upper applicatoin.
  8577. event.zrByTouch = true;
  8578. processGesture(this, event, 'end');
  8579. domHandlers.mouseup.call(this, event);
  8580. // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is
  8581. // triggered in `touchstart`. This seems to be illogical, but by this mechanism,
  8582. // we can conveniently implement "hover style" in both PC and touch device just
  8583. // by listening to `mouseover` to add "hover style" and listening to `mouseout`
  8584. // to remove "hover style" on an element, without any additional code for
  8585. // compatibility. (`mouseout` will not be triggered in `touchend`, so "hover
  8586. // style" will remain for user view)
  8587. // click event should always be triggered no matter whether
  8588. // there is gestrue event. System click can not be prevented.
  8589. if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {
  8590. domHandlers.click.call(this, event);
  8591. }
  8592. setTouchTimer(this);
  8593. },
  8594. pointerdown: function (event) {
  8595. domHandlers.mousedown.call(this, event);
  8596. // if (useMSGuesture(this, event)) {
  8597. // this._msGesture.addPointer(event.pointerId);
  8598. // }
  8599. },
  8600. pointermove: function (event) {
  8601. // FIXME
  8602. // pointermove is so sensitive that it always triggered when
  8603. // tap(click) on touch screen, which affect some judgement in
  8604. // upper application. So, we dont support mousemove on MS touch
  8605. // device yet.
  8606. if (!isPointerFromTouch(event)) {
  8607. domHandlers.mousemove.call(this, event);
  8608. }
  8609. },
  8610. pointerup: function (event) {
  8611. domHandlers.mouseup.call(this, event);
  8612. },
  8613. pointerout: function (event) {
  8614. // pointerout will be triggered when tap on touch screen
  8615. // (IE11+/Edge on MS Surface) after click event triggered,
  8616. // which is inconsistent with the mousout behavior we defined
  8617. // in touchend. So we unify them.
  8618. // (check domHandlers.touchend for detailed explanation)
  8619. if (!isPointerFromTouch(event)) {
  8620. domHandlers.mouseout.call(this, event);
  8621. }
  8622. }
  8623. };
  8624. function isPointerFromTouch(event) {
  8625. var pointerType = event.pointerType;
  8626. return pointerType === 'pen' || pointerType === 'touch';
  8627. }
  8628. // function useMSGuesture(handlerProxy, event) {
  8629. // return isPointerFromTouch(event) && !!handlerProxy._msGesture;
  8630. // }
  8631. // Common handlers
  8632. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  8633. domHandlers[name] = function (event) {
  8634. event = normalizeEvent(this.dom, event);
  8635. this.trigger(name, event);
  8636. };
  8637. });
  8638. /**
  8639. * 为控制类实例初始化dom 事件处理函数
  8640. *
  8641. * @inner
  8642. * @param {module:zrender/Handler} instance 控制类实例
  8643. */
  8644. function initDomHandler(instance) {
  8645. each$1(touchHandlerNames, function (name) {
  8646. instance._handlers[name] = bind(domHandlers[name], instance);
  8647. });
  8648. each$1(pointerHandlerNames, function (name) {
  8649. instance._handlers[name] = bind(domHandlers[name], instance);
  8650. });
  8651. each$1(mouseHandlerNames, function (name) {
  8652. instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);
  8653. });
  8654. function makeMouseHandler(fn, instance) {
  8655. return function () {
  8656. if (instance._touching) {
  8657. return;
  8658. }
  8659. return fn.apply(instance, arguments);
  8660. };
  8661. }
  8662. }
  8663. function HandlerDomProxy(dom) {
  8664. Eventful.call(this);
  8665. this.dom = dom;
  8666. /**
  8667. * @private
  8668. * @type {boolean}
  8669. */
  8670. this._touching = false;
  8671. /**
  8672. * @private
  8673. * @type {number}
  8674. */
  8675. this._touchTimer;
  8676. /**
  8677. * @private
  8678. * @type {module:zrender/core/GestureMgr}
  8679. */
  8680. this._gestureMgr = new GestureMgr();
  8681. this._handlers = {};
  8682. initDomHandler(this);
  8683. if (env$1.pointerEventsSupported) { // Only IE11+/Edge
  8684. // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),
  8685. // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event
  8686. // at the same time.
  8687. // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on
  8688. // screen, which do not occurs in pointer event.
  8689. // So we use pointer event to both detect touch gesture and mouse behavior.
  8690. mountHandlers(pointerHandlerNames, this);
  8691. // FIXME
  8692. // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,
  8693. // which does not prevent defuault behavior occasionally (which may cause view port
  8694. // zoomed in but use can not zoom it back). And event.preventDefault() does not work.
  8695. // So we have to not to use MSGesture and not to support touchmove and pinch on MS
  8696. // touch screen. And we only support click behavior on MS touch screen now.
  8697. // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.
  8698. // We dont support touch on IE on win7.
  8699. // See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>
  8700. // if (typeof MSGesture === 'function') {
  8701. // (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line
  8702. // dom.addEventListener('MSGestureChange', onMSGestureChange);
  8703. // }
  8704. }
  8705. else {
  8706. if (env$1.touchEventsSupported) {
  8707. mountHandlers(touchHandlerNames, this);
  8708. // Handler of 'mouseout' event is needed in touch mode, which will be mounted below.
  8709. // addEventListener(root, 'mouseout', this._mouseoutHandler);
  8710. }
  8711. // 1. Considering some devices that both enable touch and mouse event (like on MS Surface
  8712. // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise
  8713. // mouse event can not be handle in those devices.
  8714. // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent
  8715. // mouseevent after touch event triggered, see `setTouchTimer`.
  8716. mountHandlers(mouseHandlerNames, this);
  8717. }
  8718. function mountHandlers(handlerNames, instance) {
  8719. each$1(handlerNames, function (name) {
  8720. addEventListener(dom, eventNameFix(name), instance._handlers[name]);
  8721. }, instance);
  8722. }
  8723. }
  8724. var handlerDomProxyProto = HandlerDomProxy.prototype;
  8725. handlerDomProxyProto.dispose = function () {
  8726. var handlerNames = mouseHandlerNames.concat(touchHandlerNames);
  8727. for (var i = 0; i < handlerNames.length; i++) {
  8728. var name = handlerNames[i];
  8729. removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);
  8730. }
  8731. };
  8732. handlerDomProxyProto.setCursor = function (cursorStyle) {
  8733. this.dom.style && (this.dom.style.cursor = cursorStyle || 'default');
  8734. };
  8735. mixin(HandlerDomProxy, Eventful);
  8736. /*!
  8737. * ZRender, a high performance 2d drawing library.
  8738. *
  8739. * Copyright (c) 2013, Baidu Inc.
  8740. * All rights reserved.
  8741. *
  8742. * LICENSE
  8743. * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt
  8744. */
  8745. var useVML = !env$1.canvasSupported;
  8746. var painterCtors = {
  8747. canvas: Painter
  8748. };
  8749. /**
  8750. * @type {string}
  8751. */
  8752. var version$1 = '4.0.1';
  8753. /**
  8754. * Initializing a zrender instance
  8755. * @param {HTMLElement} dom
  8756. * @param {Object} opts
  8757. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  8758. * @param {number} [opts.devicePixelRatio]
  8759. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  8760. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  8761. * @return {module:zrender/ZRender}
  8762. */
  8763. function init$1(dom, opts) {
  8764. var zr = new ZRender(guid(), dom, opts);
  8765. return zr;
  8766. }
  8767. /**
  8768. * Dispose zrender instance
  8769. * @param {module:zrender/ZRender} zr
  8770. */
  8771. /**
  8772. * Get zrender instance by id
  8773. * @param {string} id zrender instance id
  8774. * @return {module:zrender/ZRender}
  8775. */
  8776. /**
  8777. * @module zrender/ZRender
  8778. */
  8779. /**
  8780. * @constructor
  8781. * @alias module:zrender/ZRender
  8782. * @param {string} id
  8783. * @param {HTMLElement} dom
  8784. * @param {Object} opts
  8785. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  8786. * @param {number} [opts.devicePixelRatio]
  8787. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  8788. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  8789. */
  8790. var ZRender = function (id, dom, opts) {
  8791. opts = opts || {};
  8792. /**
  8793. * @type {HTMLDomElement}
  8794. */
  8795. this.dom = dom;
  8796. /**
  8797. * @type {string}
  8798. */
  8799. this.id = id;
  8800. var self = this;
  8801. var storage = new Storage();
  8802. var rendererType = opts.renderer;
  8803. // TODO WebGL
  8804. if (useVML) {
  8805. if (!painterCtors.vml) {
  8806. throw new Error('You need to require \'zrender/vml/vml\' to support IE8');
  8807. }
  8808. rendererType = 'vml';
  8809. }
  8810. else if (!rendererType || !painterCtors[rendererType]) {
  8811. rendererType = 'canvas';
  8812. }
  8813. var painter = new painterCtors[rendererType](dom, storage, opts, id);
  8814. this.storage = storage;
  8815. this.painter = painter;
  8816. var handerProxy = (!env$1.node && !env$1.worker) ? new HandlerDomProxy(painter.getViewportRoot()) : null;
  8817. this.handler = new Handler(storage, painter, handerProxy, painter.root);
  8818. /**
  8819. * @type {module:zrender/animation/Animation}
  8820. */
  8821. this.animation = new Animation({
  8822. stage: {
  8823. update: bind(this.flush, this)
  8824. }
  8825. });
  8826. this.animation.start();
  8827. /**
  8828. * @type {boolean}
  8829. * @private
  8830. */
  8831. this._needsRefresh;
  8832. // 修改 storage.delFromStorage, 每次删除元素之前删除动画
  8833. // FIXME 有点ugly
  8834. var oldDelFromStorage = storage.delFromStorage;
  8835. var oldAddToStorage = storage.addToStorage;
  8836. storage.delFromStorage = function (el) {
  8837. oldDelFromStorage.call(storage, el);
  8838. el && el.removeSelfFromZr(self);
  8839. };
  8840. storage.addToStorage = function (el) {
  8841. oldAddToStorage.call(storage, el);
  8842. el.addSelfToZr(self);
  8843. };
  8844. };
  8845. ZRender.prototype = {
  8846. constructor: ZRender,
  8847. /**
  8848. * 获取实例唯一标识
  8849. * @return {string}
  8850. */
  8851. getId: function () {
  8852. return this.id;
  8853. },
  8854. /**
  8855. * 添加元素
  8856. * @param {module:zrender/Element} el
  8857. */
  8858. add: function (el) {
  8859. this.storage.addRoot(el);
  8860. this._needsRefresh = true;
  8861. },
  8862. /**
  8863. * 删除元素
  8864. * @param {module:zrender/Element} el
  8865. */
  8866. remove: function (el) {
  8867. this.storage.delRoot(el);
  8868. this._needsRefresh = true;
  8869. },
  8870. /**
  8871. * Change configuration of layer
  8872. * @param {string} zLevel
  8873. * @param {Object} config
  8874. * @param {string} [config.clearColor=0] Clear color
  8875. * @param {string} [config.motionBlur=false] If enable motion blur
  8876. * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer
  8877. */
  8878. configLayer: function (zLevel, config) {
  8879. this.painter.configLayer(zLevel, config);
  8880. this._needsRefresh = true;
  8881. },
  8882. /**
  8883. * Repaint the canvas immediately
  8884. */
  8885. refreshImmediately: function () {
  8886. // var start = new Date();
  8887. // Clear needsRefresh ahead to avoid something wrong happens in refresh
  8888. // Or it will cause zrender refreshes again and again.
  8889. this._needsRefresh = false;
  8890. this.painter.refresh();
  8891. /**
  8892. * Avoid trigger zr.refresh in Element#beforeUpdate hook
  8893. */
  8894. this._needsRefresh = false;
  8895. // var end = new Date();
  8896. // var log = document.getElementById('log');
  8897. // if (log) {
  8898. // log.innerHTML = log.innerHTML + '<br>' + (end - start);
  8899. // }
  8900. },
  8901. /**
  8902. * Mark and repaint the canvas in the next frame of browser
  8903. */
  8904. refresh: function() {
  8905. this._needsRefresh = true;
  8906. },
  8907. /**
  8908. * Perform all refresh
  8909. */
  8910. flush: function () {
  8911. if (this._needsRefresh) {
  8912. this.refreshImmediately();
  8913. }
  8914. if (this._needsRefreshHover) {
  8915. this.refreshHoverImmediately();
  8916. }
  8917. },
  8918. /**
  8919. * Add element to hover layer
  8920. * @param {module:zrender/Element} el
  8921. * @param {Object} style
  8922. */
  8923. addHover: function (el, style) {
  8924. if (this.painter.addHover) {
  8925. this.painter.addHover(el, style);
  8926. this.refreshHover();
  8927. }
  8928. },
  8929. /**
  8930. * Add element from hover layer
  8931. * @param {module:zrender/Element} el
  8932. */
  8933. removeHover: function (el) {
  8934. if (this.painter.removeHover) {
  8935. this.painter.removeHover(el);
  8936. this.refreshHover();
  8937. }
  8938. },
  8939. /**
  8940. * Clear all hover elements in hover layer
  8941. * @param {module:zrender/Element} el
  8942. */
  8943. clearHover: function () {
  8944. if (this.painter.clearHover) {
  8945. this.painter.clearHover();
  8946. this.refreshHover();
  8947. }
  8948. },
  8949. /**
  8950. * Refresh hover in next frame
  8951. */
  8952. refreshHover: function () {
  8953. this._needsRefreshHover = true;
  8954. },
  8955. /**
  8956. * Refresh hover immediately
  8957. */
  8958. refreshHoverImmediately: function () {
  8959. this._needsRefreshHover = false;
  8960. this.painter.refreshHover && this.painter.refreshHover();
  8961. },
  8962. /**
  8963. * Resize the canvas.
  8964. * Should be invoked when container size is changed
  8965. * @param {Object} [opts]
  8966. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  8967. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  8968. */
  8969. resize: function(opts) {
  8970. opts = opts || {};
  8971. this.painter.resize(opts.width, opts.height);
  8972. this.handler.resize();
  8973. },
  8974. /**
  8975. * Stop and clear all animation immediately
  8976. */
  8977. clearAnimation: function () {
  8978. this.animation.clear();
  8979. },
  8980. /**
  8981. * Get container width
  8982. */
  8983. getWidth: function() {
  8984. return this.painter.getWidth();
  8985. },
  8986. /**
  8987. * Get container height
  8988. */
  8989. getHeight: function() {
  8990. return this.painter.getHeight();
  8991. },
  8992. /**
  8993. * Export the canvas as Base64 URL
  8994. * @param {string} type
  8995. * @param {string} [backgroundColor='#fff']
  8996. * @return {string} Base64 URL
  8997. */
  8998. // toDataURL: function(type, backgroundColor) {
  8999. // return this.painter.getRenderedCanvas({
  9000. // backgroundColor: backgroundColor
  9001. // }).toDataURL(type);
  9002. // },
  9003. /**
  9004. * Converting a path to image.
  9005. * It has much better performance of drawing image rather than drawing a vector path.
  9006. * @param {module:zrender/graphic/Path} e
  9007. * @param {number} width
  9008. * @param {number} height
  9009. */
  9010. pathToImage: function(e, dpr) {
  9011. return this.painter.pathToImage(e, dpr);
  9012. },
  9013. /**
  9014. * Set default cursor
  9015. * @param {string} [cursorStyle='default'] 例如 crosshair
  9016. */
  9017. setCursorStyle: function (cursorStyle) {
  9018. this.handler.setCursorStyle(cursorStyle);
  9019. },
  9020. /**
  9021. * Find hovered element
  9022. * @param {number} x
  9023. * @param {number} y
  9024. * @return {Object} {target, topTarget}
  9025. */
  9026. findHover: function (x, y) {
  9027. return this.handler.findHover(x, y);
  9028. },
  9029. /**
  9030. * Bind event
  9031. *
  9032. * @param {string} eventName Event name
  9033. * @param {Function} eventHandler Handler function
  9034. * @param {Object} [context] Context object
  9035. */
  9036. on: function(eventName, eventHandler, context) {
  9037. this.handler.on(eventName, eventHandler, context);
  9038. },
  9039. /**
  9040. * Unbind event
  9041. * @param {string} eventName Event name
  9042. * @param {Function} [eventHandler] Handler function
  9043. */
  9044. off: function(eventName, eventHandler) {
  9045. this.handler.off(eventName, eventHandler);
  9046. },
  9047. /**
  9048. * Trigger event manually
  9049. *
  9050. * @param {string} eventName Event name
  9051. * @param {event=} event Event object
  9052. */
  9053. trigger: function (eventName, event) {
  9054. this.handler.trigger(eventName, event);
  9055. },
  9056. /**
  9057. * Clear all objects and the canvas.
  9058. */
  9059. clear: function () {
  9060. this.storage.delRoot();
  9061. this.painter.clear();
  9062. },
  9063. /**
  9064. * Dispose self.
  9065. */
  9066. dispose: function () {
  9067. this.animation.stop();
  9068. this.clear();
  9069. this.storage.dispose();
  9070. this.painter.dispose();
  9071. this.handler.dispose();
  9072. this.animation =
  9073. this.storage =
  9074. this.painter =
  9075. this.handler = null;
  9076. }
  9077. };
  9078. var each$2 = each$1;
  9079. var isObject$2 = isObject$1;
  9080. var isArray$1 = isArray;
  9081. /**
  9082. * name may be displayed on screen, so use '-'.
  9083. * But we should make sure it is not duplicated
  9084. * with user specified name, so use '\0';
  9085. */
  9086. var DEFAULT_COMPONENT_NAME = '\0-';
  9087. /**
  9088. * If value is not array, then translate it to array.
  9089. * @param {*} value
  9090. * @return {Array} [value] or value
  9091. */
  9092. function normalizeToArray(value) {
  9093. return value instanceof Array
  9094. ? value
  9095. : value == null
  9096. ? []
  9097. : [value];
  9098. }
  9099. /**
  9100. * Sync default option between normal and emphasis like `position` and `show`
  9101. * In case some one will write code like
  9102. * label: {
  9103. * show: false,
  9104. * position: 'outside',
  9105. * fontSize: 18
  9106. * },
  9107. * emphasis: {
  9108. * label: { show: true }
  9109. * }
  9110. * @param {Object} opt
  9111. * @param {string} key
  9112. * @param {Array.<string>} subOpts
  9113. */
  9114. function defaultEmphasis(opt, key, subOpts) {
  9115. if (opt) {
  9116. opt[key] = opt[key] || {};
  9117. opt.emphasis = opt.emphasis || {};
  9118. opt.emphasis[key] = opt.emphasis[key] || {};
  9119. // Default emphasis option from normal
  9120. for (var i = 0, len = subOpts.length; i < len; i++) {
  9121. var subOptName = subOpts[i];
  9122. if (!opt.emphasis[key].hasOwnProperty(subOptName)
  9123. && opt[key].hasOwnProperty(subOptName)
  9124. ) {
  9125. opt.emphasis[key][subOptName] = opt[key][subOptName];
  9126. }
  9127. }
  9128. }
  9129. }
  9130. var TEXT_STYLE_OPTIONS = [
  9131. 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  9132. 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth',
  9133. 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline',
  9134. 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY',
  9135. 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY',
  9136. 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding'
  9137. ];
  9138. // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
  9139. // 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
  9140. // 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  9141. // // FIXME: deprecated, check and remove it.
  9142. // 'textStyle'
  9143. // ]);
  9144. /**
  9145. * The method do not ensure performance.
  9146. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  9147. * This helper method retieves value from data.
  9148. * @param {string|number|Date|Array|Object} dataItem
  9149. * @return {number|string|Date|Array.<number|string|Date>}
  9150. */
  9151. function getDataItemValue(dataItem) {
  9152. return (isObject$2(dataItem) && !isArray$1(dataItem) && !(dataItem instanceof Date))
  9153. ? dataItem.value : dataItem;
  9154. }
  9155. /**
  9156. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  9157. * This helper method determine if dataItem has extra option besides value
  9158. * @param {string|number|Date|Array|Object} dataItem
  9159. */
  9160. function isDataItemOption(dataItem) {
  9161. return isObject$2(dataItem)
  9162. && !(dataItem instanceof Array);
  9163. // // markLine data can be array
  9164. // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
  9165. }
  9166. /**
  9167. * Mapping to exists for merge.
  9168. *
  9169. * @public
  9170. * @param {Array.<Object>|Array.<module:echarts/model/Component>} exists
  9171. * @param {Object|Array.<Object>} newCptOptions
  9172. * @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  9173. * index of which is the same as exists.
  9174. */
  9175. function mappingToExists(exists, newCptOptions) {
  9176. // Mapping by the order by original option (but not order of
  9177. // new option) in merge mode. Because we should ensure
  9178. // some specified index (like xAxisIndex) is consistent with
  9179. // original option, which is easy to understand, espatially in
  9180. // media query. And in most case, merge option is used to
  9181. // update partial option but not be expected to change order.
  9182. newCptOptions = (newCptOptions || []).slice();
  9183. var result = map(exists || [], function (obj, index) {
  9184. return {exist: obj};
  9185. });
  9186. // Mapping by id or name if specified.
  9187. each$2(newCptOptions, function (cptOption, index) {
  9188. if (!isObject$2(cptOption)) {
  9189. return;
  9190. }
  9191. // id has highest priority.
  9192. for (var i = 0; i < result.length; i++) {
  9193. if (!result[i].option // Consider name: two map to one.
  9194. && cptOption.id != null
  9195. && result[i].exist.id === cptOption.id + ''
  9196. ) {
  9197. result[i].option = cptOption;
  9198. newCptOptions[index] = null;
  9199. return;
  9200. }
  9201. }
  9202. for (var i = 0; i < result.length; i++) {
  9203. var exist = result[i].exist;
  9204. if (!result[i].option // Consider name: two map to one.
  9205. // Can not match when both ids exist but different.
  9206. && (exist.id == null || cptOption.id == null)
  9207. && cptOption.name != null
  9208. && !isIdInner(cptOption)
  9209. && !isIdInner(exist)
  9210. && exist.name === cptOption.name + ''
  9211. ) {
  9212. result[i].option = cptOption;
  9213. newCptOptions[index] = null;
  9214. return;
  9215. }
  9216. }
  9217. });
  9218. // Otherwise mapping by index.
  9219. each$2(newCptOptions, function (cptOption, index) {
  9220. if (!isObject$2(cptOption)) {
  9221. return;
  9222. }
  9223. var i = 0;
  9224. for (; i < result.length; i++) {
  9225. var exist = result[i].exist;
  9226. if (!result[i].option
  9227. // Existing model that already has id should be able to
  9228. // mapped to (because after mapping performed model may
  9229. // be assigned with a id, whish should not affect next
  9230. // mapping), except those has inner id.
  9231. && !isIdInner(exist)
  9232. // Caution:
  9233. // Do not overwrite id. But name can be overwritten,
  9234. // because axis use name as 'show label text'.
  9235. // 'exist' always has id and name and we dont
  9236. // need to check it.
  9237. && cptOption.id == null
  9238. ) {
  9239. result[i].option = cptOption;
  9240. break;
  9241. }
  9242. }
  9243. if (i >= result.length) {
  9244. result.push({option: cptOption});
  9245. }
  9246. });
  9247. return result;
  9248. }
  9249. /**
  9250. * Make id and name for mapping result (result of mappingToExists)
  9251. * into `keyInfo` field.
  9252. *
  9253. * @public
  9254. * @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  9255. * which order is the same as exists.
  9256. * @return {Array.<Object>} The input.
  9257. */
  9258. function makeIdAndName(mapResult) {
  9259. // We use this id to hash component models and view instances
  9260. // in echarts. id can be specified by user, or auto generated.
  9261. // The id generation rule ensures new view instance are able
  9262. // to mapped to old instance when setOption are called in
  9263. // no-merge mode. So we generate model id by name and plus
  9264. // type in view id.
  9265. // name can be duplicated among components, which is convenient
  9266. // to specify multi components (like series) by one name.
  9267. // Ensure that each id is distinct.
  9268. var idMap = createHashMap();
  9269. each$2(mapResult, function (item, index) {
  9270. var existCpt = item.exist;
  9271. existCpt && idMap.set(existCpt.id, item);
  9272. });
  9273. each$2(mapResult, function (item, index) {
  9274. var opt = item.option;
  9275. assert$1(
  9276. !opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item,
  9277. 'id duplicates: ' + (opt && opt.id)
  9278. );
  9279. opt && opt.id != null && idMap.set(opt.id, item);
  9280. !item.keyInfo && (item.keyInfo = {});
  9281. });
  9282. // Make name and id.
  9283. each$2(mapResult, function (item, index) {
  9284. var existCpt = item.exist;
  9285. var opt = item.option;
  9286. var keyInfo = item.keyInfo;
  9287. if (!isObject$2(opt)) {
  9288. return;
  9289. }
  9290. // name can be overwitten. Consider case: axis.name = '20km'.
  9291. // But id generated by name will not be changed, which affect
  9292. // only in that case: setOption with 'not merge mode' and view
  9293. // instance will be recreated, which can be accepted.
  9294. keyInfo.name = opt.name != null
  9295. ? opt.name + ''
  9296. : existCpt
  9297. ? existCpt.name
  9298. : DEFAULT_COMPONENT_NAME;
  9299. if (existCpt) {
  9300. keyInfo.id = existCpt.id;
  9301. }
  9302. else if (opt.id != null) {
  9303. keyInfo.id = opt.id + '';
  9304. }
  9305. else {
  9306. // Consider this situatoin:
  9307. // optionA: [{name: 'a'}, {name: 'a'}, {..}]
  9308. // optionB [{..}, {name: 'a'}, {name: 'a'}]
  9309. // Series with the same name between optionA and optionB
  9310. // should be mapped.
  9311. var idNum = 0;
  9312. do {
  9313. keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
  9314. }
  9315. while (idMap.get(keyInfo.id));
  9316. }
  9317. idMap.set(keyInfo.id, item);
  9318. });
  9319. }
  9320. /**
  9321. * @public
  9322. * @param {Object} cptOption
  9323. * @return {boolean}
  9324. */
  9325. function isIdInner(cptOption) {
  9326. return isObject$2(cptOption)
  9327. && cptOption.id
  9328. && (cptOption.id + '').indexOf('\0_ec_\0') === 0;
  9329. }
  9330. /**
  9331. * A helper for removing duplicate items between batchA and batchB,
  9332. * and in themselves, and categorize by series.
  9333. *
  9334. * @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  9335. * @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  9336. * @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB]
  9337. */
  9338. /**
  9339. * @param {module:echarts/data/List} data
  9340. * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name
  9341. * each of which can be Array or primary type.
  9342. * @return {number|Array.<number>} dataIndex If not found, return undefined/null.
  9343. */
  9344. function queryDataIndex(data, payload) {
  9345. if (payload.dataIndexInside != null) {
  9346. return payload.dataIndexInside;
  9347. }
  9348. else if (payload.dataIndex != null) {
  9349. return isArray(payload.dataIndex)
  9350. ? map(payload.dataIndex, function (value) {
  9351. return data.indexOfRawIndex(value);
  9352. })
  9353. : data.indexOfRawIndex(payload.dataIndex);
  9354. }
  9355. else if (payload.name != null) {
  9356. return isArray(payload.name)
  9357. ? map(payload.name, function (value) {
  9358. return data.indexOfName(value);
  9359. })
  9360. : data.indexOfName(payload.name);
  9361. }
  9362. }
  9363. /**
  9364. * Enable property storage to any host object.
  9365. * Notice: Serialization is not supported.
  9366. *
  9367. * For example:
  9368. * var inner = zrUitl.makeInner();
  9369. *
  9370. * function some1(hostObj) {
  9371. * inner(hostObj).someProperty = 1212;
  9372. * ...
  9373. * }
  9374. * function some2() {
  9375. * var fields = inner(this);
  9376. * fields.someProperty1 = 1212;
  9377. * fields.someProperty2 = 'xx';
  9378. * ...
  9379. * }
  9380. *
  9381. * @return {Function}
  9382. */
  9383. function makeInner() {
  9384. // Consider different scope by es module import.
  9385. var key = '__\0ec_inner_' + innerUniqueIndex++ + '_' + Math.random().toFixed(5);
  9386. return function (hostObj) {
  9387. return hostObj[key] || (hostObj[key] = {});
  9388. };
  9389. }
  9390. var innerUniqueIndex = 0;
  9391. /**
  9392. * @param {module:echarts/model/Global} ecModel
  9393. * @param {string|Object} finder
  9394. * If string, e.g., 'geo', means {geoIndex: 0}.
  9395. * If Object, could contain some of these properties below:
  9396. * {
  9397. * seriesIndex, seriesId, seriesName,
  9398. * geoIndex, geoId, geoName,
  9399. * bmapIndex, bmapId, bmapName,
  9400. * xAxisIndex, xAxisId, xAxisName,
  9401. * yAxisIndex, yAxisId, yAxisName,
  9402. * gridIndex, gridId, gridName,
  9403. * ... (can be extended)
  9404. * }
  9405. * Each properties can be number|string|Array.<number>|Array.<string>
  9406. * For example, a finder could be
  9407. * {
  9408. * seriesIndex: 3,
  9409. * geoId: ['aa', 'cc'],
  9410. * gridName: ['xx', 'rr']
  9411. * }
  9412. * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
  9413. * If nothing or null/undefined specified, return nothing.
  9414. * @param {Object} [opt]
  9415. * @param {string} [opt.defaultMainType]
  9416. * @param {Array.<string>} [opt.includeMainTypes]
  9417. * @return {Object} result like:
  9418. * {
  9419. * seriesModels: [seriesModel1, seriesModel2],
  9420. * seriesModel: seriesModel1, // The first model
  9421. * geoModels: [geoModel1, geoModel2],
  9422. * geoModel: geoModel1, // The first model
  9423. * ...
  9424. * }
  9425. */
  9426. function parseFinder(ecModel, finder, opt) {
  9427. if (isString(finder)) {
  9428. var obj = {};
  9429. obj[finder + 'Index'] = 0;
  9430. finder = obj;
  9431. }
  9432. var defaultMainType = opt && opt.defaultMainType;
  9433. if (defaultMainType
  9434. && !has(finder, defaultMainType + 'Index')
  9435. && !has(finder, defaultMainType + 'Id')
  9436. && !has(finder, defaultMainType + 'Name')
  9437. ) {
  9438. finder[defaultMainType + 'Index'] = 0;
  9439. }
  9440. var result = {};
  9441. each$2(finder, function (value, key) {
  9442. var value = finder[key];
  9443. // Exclude 'dataIndex' and other illgal keys.
  9444. if (key === 'dataIndex' || key === 'dataIndexInside') {
  9445. result[key] = value;
  9446. return;
  9447. }
  9448. var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
  9449. var mainType = parsedKey[1];
  9450. var queryType = (parsedKey[2] || '').toLowerCase();
  9451. if (!mainType
  9452. || !queryType
  9453. || value == null
  9454. || (queryType === 'index' && value === 'none')
  9455. || (opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0)
  9456. ) {
  9457. return;
  9458. }
  9459. var queryParam = {mainType: mainType};
  9460. if (queryType !== 'index' || value !== 'all') {
  9461. queryParam[queryType] = value;
  9462. }
  9463. var models = ecModel.queryComponents(queryParam);
  9464. result[mainType + 'Models'] = models;
  9465. result[mainType + 'Model'] = models[0];
  9466. });
  9467. return result;
  9468. }
  9469. function has(obj, prop) {
  9470. return obj && obj.hasOwnProperty(prop);
  9471. }
  9472. function setAttribute(dom, key, value) {
  9473. dom.setAttribute
  9474. ? dom.setAttribute(key, value)
  9475. : (dom[key] = value);
  9476. }
  9477. function getAttribute(dom, key) {
  9478. return dom.getAttribute
  9479. ? dom.getAttribute(key)
  9480. : dom[key];
  9481. }
  9482. var TYPE_DELIMITER = '.';
  9483. var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
  9484. /**
  9485. * Notice, parseClassType('') should returns {main: '', sub: ''}
  9486. * @public
  9487. */
  9488. function parseClassType$1(componentType) {
  9489. var ret = {main: '', sub: ''};
  9490. if (componentType) {
  9491. componentType = componentType.split(TYPE_DELIMITER);
  9492. ret.main = componentType[0] || '';
  9493. ret.sub = componentType[1] || '';
  9494. }
  9495. return ret;
  9496. }
  9497. /**
  9498. * @public
  9499. */
  9500. function checkClassType(componentType) {
  9501. assert$1(
  9502. /^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType),
  9503. 'componentType "' + componentType + '" illegal'
  9504. );
  9505. }
  9506. /**
  9507. * @public
  9508. */
  9509. function enableClassExtend(RootClass, mandatoryMethods) {
  9510. RootClass.$constructor = RootClass;
  9511. RootClass.extend = function (proto) {
  9512. if (__DEV__) {
  9513. each$1(mandatoryMethods, function (method) {
  9514. if (!proto[method]) {
  9515. console.warn(
  9516. 'Method `' + method + '` should be implemented'
  9517. + (proto.type ? ' in ' + proto.type : '') + '.'
  9518. );
  9519. }
  9520. });
  9521. }
  9522. var superClass = this;
  9523. var ExtendedClass = function () {
  9524. if (!proto.$constructor) {
  9525. superClass.apply(this, arguments);
  9526. }
  9527. else {
  9528. proto.$constructor.apply(this, arguments);
  9529. }
  9530. };
  9531. extend(ExtendedClass.prototype, proto);
  9532. ExtendedClass.extend = this.extend;
  9533. ExtendedClass.superCall = superCall;
  9534. ExtendedClass.superApply = superApply;
  9535. inherits(ExtendedClass, this);
  9536. ExtendedClass.superClass = superClass;
  9537. return ExtendedClass;
  9538. };
  9539. }
  9540. var classBase = 0;
  9541. /**
  9542. * Can not use instanceof, consider different scope by
  9543. * cross domain or es module import in ec extensions.
  9544. * Mount a method "isInstance()" to Clz.
  9545. */
  9546. function enableClassCheck(Clz) {
  9547. var classAttr = ['__\0is_clz', classBase++, Math.random().toFixed(3)].join('_');
  9548. Clz.prototype[classAttr] = true;
  9549. if (__DEV__) {
  9550. assert$1(!Clz.isInstance, 'The method "is" can not be defined.');
  9551. }
  9552. Clz.isInstance = function (obj) {
  9553. return !!(obj && obj[classAttr]);
  9554. };
  9555. }
  9556. // superCall should have class info, which can not be fetch from 'this'.
  9557. // Consider this case:
  9558. // class A has method f,
  9559. // class B inherits class A, overrides method f, f call superApply('f'),
  9560. // class C inherits class B, do not overrides method f,
  9561. // then when method of class C is called, dead loop occured.
  9562. function superCall(context, methodName) {
  9563. var args = slice(arguments, 2);
  9564. return this.superClass.prototype[methodName].apply(context, args);
  9565. }
  9566. function superApply(context, methodName, args) {
  9567. return this.superClass.prototype[methodName].apply(context, args);
  9568. }
  9569. /**
  9570. * @param {Object} entity
  9571. * @param {Object} options
  9572. * @param {boolean} [options.registerWhenExtend]
  9573. * @public
  9574. */
  9575. function enableClassManagement(entity, options) {
  9576. options = options || {};
  9577. /**
  9578. * Component model classes
  9579. * key: componentType,
  9580. * value:
  9581. * componentClass, when componentType is 'xxx'
  9582. * or Object.<subKey, componentClass>, when componentType is 'xxx.yy'
  9583. * @type {Object}
  9584. */
  9585. var storage = {};
  9586. entity.registerClass = function (Clazz, componentType) {
  9587. if (componentType) {
  9588. checkClassType(componentType);
  9589. componentType = parseClassType$1(componentType);
  9590. if (!componentType.sub) {
  9591. if (__DEV__) {
  9592. if (storage[componentType.main]) {
  9593. console.warn(componentType.main + ' exists.');
  9594. }
  9595. }
  9596. storage[componentType.main] = Clazz;
  9597. }
  9598. else if (componentType.sub !== IS_CONTAINER) {
  9599. var container = makeContainer(componentType);
  9600. container[componentType.sub] = Clazz;
  9601. }
  9602. }
  9603. return Clazz;
  9604. };
  9605. entity.getClass = function (componentMainType, subType, throwWhenNotFound) {
  9606. var Clazz = storage[componentMainType];
  9607. if (Clazz && Clazz[IS_CONTAINER]) {
  9608. Clazz = subType ? Clazz[subType] : null;
  9609. }
  9610. if (throwWhenNotFound && !Clazz) {
  9611. throw new Error(
  9612. !subType
  9613. ? componentMainType + '.' + 'type should be specified.'
  9614. : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.'
  9615. );
  9616. }
  9617. return Clazz;
  9618. };
  9619. entity.getClassesByMainType = function (componentType) {
  9620. componentType = parseClassType$1(componentType);
  9621. var result = [];
  9622. var obj = storage[componentType.main];
  9623. if (obj && obj[IS_CONTAINER]) {
  9624. each$1(obj, function (o, type) {
  9625. type !== IS_CONTAINER && result.push(o);
  9626. });
  9627. }
  9628. else {
  9629. result.push(obj);
  9630. }
  9631. return result;
  9632. };
  9633. entity.hasClass = function (componentType) {
  9634. // Just consider componentType.main.
  9635. componentType = parseClassType$1(componentType);
  9636. return !!storage[componentType.main];
  9637. };
  9638. /**
  9639. * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx']
  9640. */
  9641. entity.getAllClassMainTypes = function () {
  9642. var types = [];
  9643. each$1(storage, function (obj, type) {
  9644. types.push(type);
  9645. });
  9646. return types;
  9647. };
  9648. /**
  9649. * If a main type is container and has sub types
  9650. * @param {string} mainType
  9651. * @return {boolean}
  9652. */
  9653. entity.hasSubTypes = function (componentType) {
  9654. componentType = parseClassType$1(componentType);
  9655. var obj = storage[componentType.main];
  9656. return obj && obj[IS_CONTAINER];
  9657. };
  9658. entity.parseClassType = parseClassType$1;
  9659. function makeContainer(componentType) {
  9660. var container = storage[componentType.main];
  9661. if (!container || !container[IS_CONTAINER]) {
  9662. container = storage[componentType.main] = {};
  9663. container[IS_CONTAINER] = true;
  9664. }
  9665. return container;
  9666. }
  9667. if (options.registerWhenExtend) {
  9668. var originalExtend = entity.extend;
  9669. if (originalExtend) {
  9670. entity.extend = function (proto) {
  9671. var ExtendedClass = originalExtend.call(this, proto);
  9672. return entity.registerClass(ExtendedClass, proto.type);
  9673. };
  9674. }
  9675. }
  9676. return entity;
  9677. }
  9678. /**
  9679. * @param {string|Array.<string>} properties
  9680. */
  9681. // TODO Parse shadow style
  9682. // TODO Only shallow path support
  9683. var makeStyleMapper = function (properties) {
  9684. // Normalize
  9685. for (var i = 0; i < properties.length; i++) {
  9686. if (!properties[i][1]) {
  9687. properties[i][1] = properties[i][0];
  9688. }
  9689. }
  9690. return function (model, excludes, includes) {
  9691. var style = {};
  9692. for (var i = 0; i < properties.length; i++) {
  9693. var propName = properties[i][1];
  9694. if ((excludes && indexOf(excludes, propName) >= 0)
  9695. || (includes && indexOf(includes, propName) < 0)
  9696. ) {
  9697. continue;
  9698. }
  9699. var val = model.getShallow(propName);
  9700. if (val != null) {
  9701. style[properties[i][0]] = val;
  9702. }
  9703. }
  9704. return style;
  9705. };
  9706. };
  9707. var getLineStyle = makeStyleMapper(
  9708. [
  9709. ['lineWidth', 'width'],
  9710. ['stroke', 'color'],
  9711. ['opacity'],
  9712. ['shadowBlur'],
  9713. ['shadowOffsetX'],
  9714. ['shadowOffsetY'],
  9715. ['shadowColor']
  9716. ]
  9717. );
  9718. var lineStyleMixin = {
  9719. getLineStyle: function (excludes) {
  9720. var style = getLineStyle(this, excludes);
  9721. var lineDash = this.getLineDash(style.lineWidth);
  9722. lineDash && (style.lineDash = lineDash);
  9723. return style;
  9724. },
  9725. getLineDash: function (lineWidth) {
  9726. if (lineWidth == null) {
  9727. lineWidth = 1;
  9728. }
  9729. var lineType = this.get('type');
  9730. var dotSize = Math.max(lineWidth, 2);
  9731. var dashSize = lineWidth * 4;
  9732. return (lineType === 'solid' || lineType == null) ? null
  9733. : (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]);
  9734. }
  9735. };
  9736. var getAreaStyle = makeStyleMapper(
  9737. [
  9738. ['fill', 'color'],
  9739. ['shadowBlur'],
  9740. ['shadowOffsetX'],
  9741. ['shadowOffsetY'],
  9742. ['opacity'],
  9743. ['shadowColor']
  9744. ]
  9745. );
  9746. var areaStyleMixin = {
  9747. getAreaStyle: function (excludes, includes) {
  9748. return getAreaStyle(this, excludes, includes);
  9749. }
  9750. };
  9751. /**
  9752. * 曲线辅助模块
  9753. * @module zrender/core/curve
  9754. * @author pissang(https://www.github.com/pissang)
  9755. */
  9756. var mathPow = Math.pow;
  9757. var mathSqrt$2 = Math.sqrt;
  9758. var EPSILON$1 = 1e-8;
  9759. var EPSILON_NUMERIC = 1e-4;
  9760. var THREE_SQRT = mathSqrt$2(3);
  9761. var ONE_THIRD = 1 / 3;
  9762. // 临时变量
  9763. var _v0 = create();
  9764. var _v1 = create();
  9765. var _v2 = create();
  9766. function isAroundZero(val) {
  9767. return val > -EPSILON$1 && val < EPSILON$1;
  9768. }
  9769. function isNotAroundZero$1(val) {
  9770. return val > EPSILON$1 || val < -EPSILON$1;
  9771. }
  9772. /**
  9773. * 计算三次贝塞尔值
  9774. * @memberOf module:zrender/core/curve
  9775. * @param {number} p0
  9776. * @param {number} p1
  9777. * @param {number} p2
  9778. * @param {number} p3
  9779. * @param {number} t
  9780. * @return {number}
  9781. */
  9782. function cubicAt(p0, p1, p2, p3, t) {
  9783. var onet = 1 - t;
  9784. return onet * onet * (onet * p0 + 3 * t * p1)
  9785. + t * t * (t * p3 + 3 * onet * p2);
  9786. }
  9787. /**
  9788. * 计算三次贝塞尔导数值
  9789. * @memberOf module:zrender/core/curve
  9790. * @param {number} p0
  9791. * @param {number} p1
  9792. * @param {number} p2
  9793. * @param {number} p3
  9794. * @param {number} t
  9795. * @return {number}
  9796. */
  9797. function cubicDerivativeAt(p0, p1, p2, p3, t) {
  9798. var onet = 1 - t;
  9799. return 3 * (
  9800. ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet
  9801. + (p3 - p2) * t * t
  9802. );
  9803. }
  9804. /**
  9805. * 计算三次贝塞尔方程根,使用盛金公式
  9806. * @memberOf module:zrender/core/curve
  9807. * @param {number} p0
  9808. * @param {number} p1
  9809. * @param {number} p2
  9810. * @param {number} p3
  9811. * @param {number} val
  9812. * @param {Array.<number>} roots
  9813. * @return {number} 有效根数目
  9814. */
  9815. function cubicRootAt(p0, p1, p2, p3, val, roots) {
  9816. // Evaluate roots of cubic functions
  9817. var a = p3 + 3 * (p1 - p2) - p0;
  9818. var b = 3 * (p2 - p1 * 2 + p0);
  9819. var c = 3 * (p1 - p0);
  9820. var d = p0 - val;
  9821. var A = b * b - 3 * a * c;
  9822. var B = b * c - 9 * a * d;
  9823. var C = c * c - 3 * b * d;
  9824. var n = 0;
  9825. if (isAroundZero(A) && isAroundZero(B)) {
  9826. if (isAroundZero(b)) {
  9827. roots[0] = 0;
  9828. }
  9829. else {
  9830. var t1 = -c / b; //t1, t2, t3, b is not zero
  9831. if (t1 >= 0 && t1 <= 1) {
  9832. roots[n++] = t1;
  9833. }
  9834. }
  9835. }
  9836. else {
  9837. var disc = B * B - 4 * A * C;
  9838. if (isAroundZero(disc)) {
  9839. var K = B / A;
  9840. var t1 = -b / a + K; // t1, a is not zero
  9841. var t2 = -K / 2; // t2, t3
  9842. if (t1 >= 0 && t1 <= 1) {
  9843. roots[n++] = t1;
  9844. }
  9845. if (t2 >= 0 && t2 <= 1) {
  9846. roots[n++] = t2;
  9847. }
  9848. }
  9849. else if (disc > 0) {
  9850. var discSqrt = mathSqrt$2(disc);
  9851. var Y1 = A * b + 1.5 * a * (-B + discSqrt);
  9852. var Y2 = A * b + 1.5 * a * (-B - discSqrt);
  9853. if (Y1 < 0) {
  9854. Y1 = -mathPow(-Y1, ONE_THIRD);
  9855. }
  9856. else {
  9857. Y1 = mathPow(Y1, ONE_THIRD);
  9858. }
  9859. if (Y2 < 0) {
  9860. Y2 = -mathPow(-Y2, ONE_THIRD);
  9861. }
  9862. else {
  9863. Y2 = mathPow(Y2, ONE_THIRD);
  9864. }
  9865. var t1 = (-b - (Y1 + Y2)) / (3 * a);
  9866. if (t1 >= 0 && t1 <= 1) {
  9867. roots[n++] = t1;
  9868. }
  9869. }
  9870. else {
  9871. var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt$2(A * A * A));
  9872. var theta = Math.acos(T) / 3;
  9873. var ASqrt = mathSqrt$2(A);
  9874. var tmp = Math.cos(theta);
  9875. var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);
  9876. var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);
  9877. var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
  9878. if (t1 >= 0 && t1 <= 1) {
  9879. roots[n++] = t1;
  9880. }
  9881. if (t2 >= 0 && t2 <= 1) {
  9882. roots[n++] = t2;
  9883. }
  9884. if (t3 >= 0 && t3 <= 1) {
  9885. roots[n++] = t3;
  9886. }
  9887. }
  9888. }
  9889. return n;
  9890. }
  9891. /**
  9892. * 计算三次贝塞尔方程极限值的位置
  9893. * @memberOf module:zrender/core/curve
  9894. * @param {number} p0
  9895. * @param {number} p1
  9896. * @param {number} p2
  9897. * @param {number} p3
  9898. * @param {Array.<number>} extrema
  9899. * @return {number} 有效数目
  9900. */
  9901. function cubicExtrema(p0, p1, p2, p3, extrema) {
  9902. var b = 6 * p2 - 12 * p1 + 6 * p0;
  9903. var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;
  9904. var c = 3 * p1 - 3 * p0;
  9905. var n = 0;
  9906. if (isAroundZero(a)) {
  9907. if (isNotAroundZero$1(b)) {
  9908. var t1 = -c / b;
  9909. if (t1 >= 0 && t1 <=1) {
  9910. extrema[n++] = t1;
  9911. }
  9912. }
  9913. }
  9914. else {
  9915. var disc = b * b - 4 * a * c;
  9916. if (isAroundZero(disc)) {
  9917. extrema[0] = -b / (2 * a);
  9918. }
  9919. else if (disc > 0) {
  9920. var discSqrt = mathSqrt$2(disc);
  9921. var t1 = (-b + discSqrt) / (2 * a);
  9922. var t2 = (-b - discSqrt) / (2 * a);
  9923. if (t1 >= 0 && t1 <= 1) {
  9924. extrema[n++] = t1;
  9925. }
  9926. if (t2 >= 0 && t2 <= 1) {
  9927. extrema[n++] = t2;
  9928. }
  9929. }
  9930. }
  9931. return n;
  9932. }
  9933. /**
  9934. * 细分三次贝塞尔曲线
  9935. * @memberOf module:zrender/core/curve
  9936. * @param {number} p0
  9937. * @param {number} p1
  9938. * @param {number} p2
  9939. * @param {number} p3
  9940. * @param {number} t
  9941. * @param {Array.<number>} out
  9942. */
  9943. function cubicSubdivide(p0, p1, p2, p3, t, out) {
  9944. var p01 = (p1 - p0) * t + p0;
  9945. var p12 = (p2 - p1) * t + p1;
  9946. var p23 = (p3 - p2) * t + p2;
  9947. var p012 = (p12 - p01) * t + p01;
  9948. var p123 = (p23 - p12) * t + p12;
  9949. var p0123 = (p123 - p012) * t + p012;
  9950. // Seg0
  9951. out[0] = p0;
  9952. out[1] = p01;
  9953. out[2] = p012;
  9954. out[3] = p0123;
  9955. // Seg1
  9956. out[4] = p0123;
  9957. out[5] = p123;
  9958. out[6] = p23;
  9959. out[7] = p3;
  9960. }
  9961. /**
  9962. * 投射点到三次贝塞尔曲线上,返回投射距离。
  9963. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  9964. * @param {number} x0
  9965. * @param {number} y0
  9966. * @param {number} x1
  9967. * @param {number} y1
  9968. * @param {number} x2
  9969. * @param {number} y2
  9970. * @param {number} x3
  9971. * @param {number} y3
  9972. * @param {number} x
  9973. * @param {number} y
  9974. * @param {Array.<number>} [out] 投射点
  9975. * @return {number}
  9976. */
  9977. function cubicProjectPoint(
  9978. x0, y0, x1, y1, x2, y2, x3, y3,
  9979. x, y, out
  9980. ) {
  9981. // http://pomax.github.io/bezierinfo/#projections
  9982. var t;
  9983. var interval = 0.005;
  9984. var d = Infinity;
  9985. var prev;
  9986. var next;
  9987. var d1;
  9988. var d2;
  9989. _v0[0] = x;
  9990. _v0[1] = y;
  9991. // 先粗略估计一下可能的最小距离的 t 值
  9992. // PENDING
  9993. for (var _t = 0; _t < 1; _t += 0.05) {
  9994. _v1[0] = cubicAt(x0, x1, x2, x3, _t);
  9995. _v1[1] = cubicAt(y0, y1, y2, y3, _t);
  9996. d1 = distSquare(_v0, _v1);
  9997. if (d1 < d) {
  9998. t = _t;
  9999. d = d1;
  10000. }
  10001. }
  10002. d = Infinity;
  10003. // At most 32 iteration
  10004. for (var i = 0; i < 32; i++) {
  10005. if (interval < EPSILON_NUMERIC) {
  10006. break;
  10007. }
  10008. prev = t - interval;
  10009. next = t + interval;
  10010. // t - interval
  10011. _v1[0] = cubicAt(x0, x1, x2, x3, prev);
  10012. _v1[1] = cubicAt(y0, y1, y2, y3, prev);
  10013. d1 = distSquare(_v1, _v0);
  10014. if (prev >= 0 && d1 < d) {
  10015. t = prev;
  10016. d = d1;
  10017. }
  10018. else {
  10019. // t + interval
  10020. _v2[0] = cubicAt(x0, x1, x2, x3, next);
  10021. _v2[1] = cubicAt(y0, y1, y2, y3, next);
  10022. d2 = distSquare(_v2, _v0);
  10023. if (next <= 1 && d2 < d) {
  10024. t = next;
  10025. d = d2;
  10026. }
  10027. else {
  10028. interval *= 0.5;
  10029. }
  10030. }
  10031. }
  10032. // t
  10033. if (out) {
  10034. out[0] = cubicAt(x0, x1, x2, x3, t);
  10035. out[1] = cubicAt(y0, y1, y2, y3, t);
  10036. }
  10037. // console.log(interval, i);
  10038. return mathSqrt$2(d);
  10039. }
  10040. /**
  10041. * 计算二次方贝塞尔值
  10042. * @param {number} p0
  10043. * @param {number} p1
  10044. * @param {number} p2
  10045. * @param {number} t
  10046. * @return {number}
  10047. */
  10048. function quadraticAt(p0, p1, p2, t) {
  10049. var onet = 1 - t;
  10050. return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
  10051. }
  10052. /**
  10053. * 计算二次方贝塞尔导数值
  10054. * @param {number} p0
  10055. * @param {number} p1
  10056. * @param {number} p2
  10057. * @param {number} t
  10058. * @return {number}
  10059. */
  10060. function quadraticDerivativeAt(p0, p1, p2, t) {
  10061. return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));
  10062. }
  10063. /**
  10064. * 计算二次方贝塞尔方程根
  10065. * @param {number} p0
  10066. * @param {number} p1
  10067. * @param {number} p2
  10068. * @param {number} t
  10069. * @param {Array.<number>} roots
  10070. * @return {number} 有效根数目
  10071. */
  10072. function quadraticRootAt(p0, p1, p2, val, roots) {
  10073. var a = p0 - 2 * p1 + p2;
  10074. var b = 2 * (p1 - p0);
  10075. var c = p0 - val;
  10076. var n = 0;
  10077. if (isAroundZero(a)) {
  10078. if (isNotAroundZero$1(b)) {
  10079. var t1 = -c / b;
  10080. if (t1 >= 0 && t1 <= 1) {
  10081. roots[n++] = t1;
  10082. }
  10083. }
  10084. }
  10085. else {
  10086. var disc = b * b - 4 * a * c;
  10087. if (isAroundZero(disc)) {
  10088. var t1 = -b / (2 * a);
  10089. if (t1 >= 0 && t1 <= 1) {
  10090. roots[n++] = t1;
  10091. }
  10092. }
  10093. else if (disc > 0) {
  10094. var discSqrt = mathSqrt$2(disc);
  10095. var t1 = (-b + discSqrt) / (2 * a);
  10096. var t2 = (-b - discSqrt) / (2 * a);
  10097. if (t1 >= 0 && t1 <= 1) {
  10098. roots[n++] = t1;
  10099. }
  10100. if (t2 >= 0 && t2 <= 1) {
  10101. roots[n++] = t2;
  10102. }
  10103. }
  10104. }
  10105. return n;
  10106. }
  10107. /**
  10108. * 计算二次贝塞尔方程极限值
  10109. * @memberOf module:zrender/core/curve
  10110. * @param {number} p0
  10111. * @param {number} p1
  10112. * @param {number} p2
  10113. * @return {number}
  10114. */
  10115. function quadraticExtremum(p0, p1, p2) {
  10116. var divider = p0 + p2 - 2 * p1;
  10117. if (divider === 0) {
  10118. // p1 is center of p0 and p2
  10119. return 0.5;
  10120. }
  10121. else {
  10122. return (p0 - p1) / divider;
  10123. }
  10124. }
  10125. /**
  10126. * 细分二次贝塞尔曲线
  10127. * @memberOf module:zrender/core/curve
  10128. * @param {number} p0
  10129. * @param {number} p1
  10130. * @param {number} p2
  10131. * @param {number} t
  10132. * @param {Array.<number>} out
  10133. */
  10134. function quadraticSubdivide(p0, p1, p2, t, out) {
  10135. var p01 = (p1 - p0) * t + p0;
  10136. var p12 = (p2 - p1) * t + p1;
  10137. var p012 = (p12 - p01) * t + p01;
  10138. // Seg0
  10139. out[0] = p0;
  10140. out[1] = p01;
  10141. out[2] = p012;
  10142. // Seg1
  10143. out[3] = p012;
  10144. out[4] = p12;
  10145. out[5] = p2;
  10146. }
  10147. /**
  10148. * 投射点到二次贝塞尔曲线上,返回投射距离。
  10149. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  10150. * @param {number} x0
  10151. * @param {number} y0
  10152. * @param {number} x1
  10153. * @param {number} y1
  10154. * @param {number} x2
  10155. * @param {number} y2
  10156. * @param {number} x
  10157. * @param {number} y
  10158. * @param {Array.<number>} out 投射点
  10159. * @return {number}
  10160. */
  10161. function quadraticProjectPoint(
  10162. x0, y0, x1, y1, x2, y2,
  10163. x, y, out
  10164. ) {
  10165. // http://pomax.github.io/bezierinfo/#projections
  10166. var t;
  10167. var interval = 0.005;
  10168. var d = Infinity;
  10169. _v0[0] = x;
  10170. _v0[1] = y;
  10171. // 先粗略估计一下可能的最小距离的 t 值
  10172. // PENDING
  10173. for (var _t = 0; _t < 1; _t += 0.05) {
  10174. _v1[0] = quadraticAt(x0, x1, x2, _t);
  10175. _v1[1] = quadraticAt(y0, y1, y2, _t);
  10176. var d1 = distSquare(_v0, _v1);
  10177. if (d1 < d) {
  10178. t = _t;
  10179. d = d1;
  10180. }
  10181. }
  10182. d = Infinity;
  10183. // At most 32 iteration
  10184. for (var i = 0; i < 32; i++) {
  10185. if (interval < EPSILON_NUMERIC) {
  10186. break;
  10187. }
  10188. var prev = t - interval;
  10189. var next = t + interval;
  10190. // t - interval
  10191. _v1[0] = quadraticAt(x0, x1, x2, prev);
  10192. _v1[1] = quadraticAt(y0, y1, y2, prev);
  10193. var d1 = distSquare(_v1, _v0);
  10194. if (prev >= 0 && d1 < d) {
  10195. t = prev;
  10196. d = d1;
  10197. }
  10198. else {
  10199. // t + interval
  10200. _v2[0] = quadraticAt(x0, x1, x2, next);
  10201. _v2[1] = quadraticAt(y0, y1, y2, next);
  10202. var d2 = distSquare(_v2, _v0);
  10203. if (next <= 1 && d2 < d) {
  10204. t = next;
  10205. d = d2;
  10206. }
  10207. else {
  10208. interval *= 0.5;
  10209. }
  10210. }
  10211. }
  10212. // t
  10213. if (out) {
  10214. out[0] = quadraticAt(x0, x1, x2, t);
  10215. out[1] = quadraticAt(y0, y1, y2, t);
  10216. }
  10217. // console.log(interval, i);
  10218. return mathSqrt$2(d);
  10219. }
  10220. /**
  10221. * @author Yi Shen(https://github.com/pissang)
  10222. */
  10223. var mathMin$3 = Math.min;
  10224. var mathMax$3 = Math.max;
  10225. var mathSin$2 = Math.sin;
  10226. var mathCos$2 = Math.cos;
  10227. var PI2 = Math.PI * 2;
  10228. var start = create();
  10229. var end = create();
  10230. var extremity = create();
  10231. /**
  10232. * 从顶点数组中计算出最小包围盒,写入`min`和`max`中
  10233. * @module zrender/core/bbox
  10234. * @param {Array<Object>} points 顶点数组
  10235. * @param {number} min
  10236. * @param {number} max
  10237. */
  10238. /**
  10239. * @memberOf module:zrender/core/bbox
  10240. * @param {number} x0
  10241. * @param {number} y0
  10242. * @param {number} x1
  10243. * @param {number} y1
  10244. * @param {Array.<number>} min
  10245. * @param {Array.<number>} max
  10246. */
  10247. function fromLine(x0, y0, x1, y1, min$$1, max$$1) {
  10248. min$$1[0] = mathMin$3(x0, x1);
  10249. min$$1[1] = mathMin$3(y0, y1);
  10250. max$$1[0] = mathMax$3(x0, x1);
  10251. max$$1[1] = mathMax$3(y0, y1);
  10252. }
  10253. var xDim = [];
  10254. var yDim = [];
  10255. /**
  10256. * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中
  10257. * @memberOf module:zrender/core/bbox
  10258. * @param {number} x0
  10259. * @param {number} y0
  10260. * @param {number} x1
  10261. * @param {number} y1
  10262. * @param {number} x2
  10263. * @param {number} y2
  10264. * @param {number} x3
  10265. * @param {number} y3
  10266. * @param {Array.<number>} min
  10267. * @param {Array.<number>} max
  10268. */
  10269. function fromCubic(
  10270. x0, y0, x1, y1, x2, y2, x3, y3, min$$1, max$$1
  10271. ) {
  10272. var cubicExtrema$$1 = cubicExtrema;
  10273. var cubicAt$$1 = cubicAt;
  10274. var i;
  10275. var n = cubicExtrema$$1(x0, x1, x2, x3, xDim);
  10276. min$$1[0] = Infinity;
  10277. min$$1[1] = Infinity;
  10278. max$$1[0] = -Infinity;
  10279. max$$1[1] = -Infinity;
  10280. for (i = 0; i < n; i++) {
  10281. var x = cubicAt$$1(x0, x1, x2, x3, xDim[i]);
  10282. min$$1[0] = mathMin$3(x, min$$1[0]);
  10283. max$$1[0] = mathMax$3(x, max$$1[0]);
  10284. }
  10285. n = cubicExtrema$$1(y0, y1, y2, y3, yDim);
  10286. for (i = 0; i < n; i++) {
  10287. var y = cubicAt$$1(y0, y1, y2, y3, yDim[i]);
  10288. min$$1[1] = mathMin$3(y, min$$1[1]);
  10289. max$$1[1] = mathMax$3(y, max$$1[1]);
  10290. }
  10291. min$$1[0] = mathMin$3(x0, min$$1[0]);
  10292. max$$1[0] = mathMax$3(x0, max$$1[0]);
  10293. min$$1[0] = mathMin$3(x3, min$$1[0]);
  10294. max$$1[0] = mathMax$3(x3, max$$1[0]);
  10295. min$$1[1] = mathMin$3(y0, min$$1[1]);
  10296. max$$1[1] = mathMax$3(y0, max$$1[1]);
  10297. min$$1[1] = mathMin$3(y3, min$$1[1]);
  10298. max$$1[1] = mathMax$3(y3, max$$1[1]);
  10299. }
  10300. /**
  10301. * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中
  10302. * @memberOf module:zrender/core/bbox
  10303. * @param {number} x0
  10304. * @param {number} y0
  10305. * @param {number} x1
  10306. * @param {number} y1
  10307. * @param {number} x2
  10308. * @param {number} y2
  10309. * @param {Array.<number>} min
  10310. * @param {Array.<number>} max
  10311. */
  10312. function fromQuadratic(x0, y0, x1, y1, x2, y2, min$$1, max$$1) {
  10313. var quadraticExtremum$$1 = quadraticExtremum;
  10314. var quadraticAt$$1 = quadraticAt;
  10315. // Find extremities, where derivative in x dim or y dim is zero
  10316. var tx =
  10317. mathMax$3(
  10318. mathMin$3(quadraticExtremum$$1(x0, x1, x2), 1), 0
  10319. );
  10320. var ty =
  10321. mathMax$3(
  10322. mathMin$3(quadraticExtremum$$1(y0, y1, y2), 1), 0
  10323. );
  10324. var x = quadraticAt$$1(x0, x1, x2, tx);
  10325. var y = quadraticAt$$1(y0, y1, y2, ty);
  10326. min$$1[0] = mathMin$3(x0, x2, x);
  10327. min$$1[1] = mathMin$3(y0, y2, y);
  10328. max$$1[0] = mathMax$3(x0, x2, x);
  10329. max$$1[1] = mathMax$3(y0, y2, y);
  10330. }
  10331. /**
  10332. * 从圆弧中计算出最小包围盒,写入`min`和`max`中
  10333. * @method
  10334. * @memberOf module:zrender/core/bbox
  10335. * @param {number} x
  10336. * @param {number} y
  10337. * @param {number} rx
  10338. * @param {number} ry
  10339. * @param {number} startAngle
  10340. * @param {number} endAngle
  10341. * @param {number} anticlockwise
  10342. * @param {Array.<number>} min
  10343. * @param {Array.<number>} max
  10344. */
  10345. function fromArc(
  10346. x, y, rx, ry, startAngle, endAngle, anticlockwise, min$$1, max$$1
  10347. ) {
  10348. var vec2Min = min;
  10349. var vec2Max = max;
  10350. var diff = Math.abs(startAngle - endAngle);
  10351. if (diff % PI2 < 1e-4 && diff > 1e-4) {
  10352. // Is a circle
  10353. min$$1[0] = x - rx;
  10354. min$$1[1] = y - ry;
  10355. max$$1[0] = x + rx;
  10356. max$$1[1] = y + ry;
  10357. return;
  10358. }
  10359. start[0] = mathCos$2(startAngle) * rx + x;
  10360. start[1] = mathSin$2(startAngle) * ry + y;
  10361. end[0] = mathCos$2(endAngle) * rx + x;
  10362. end[1] = mathSin$2(endAngle) * ry + y;
  10363. vec2Min(min$$1, start, end);
  10364. vec2Max(max$$1, start, end);
  10365. // Thresh to [0, Math.PI * 2]
  10366. startAngle = startAngle % (PI2);
  10367. if (startAngle < 0) {
  10368. startAngle = startAngle + PI2;
  10369. }
  10370. endAngle = endAngle % (PI2);
  10371. if (endAngle < 0) {
  10372. endAngle = endAngle + PI2;
  10373. }
  10374. if (startAngle > endAngle && !anticlockwise) {
  10375. endAngle += PI2;
  10376. }
  10377. else if (startAngle < endAngle && anticlockwise) {
  10378. startAngle += PI2;
  10379. }
  10380. if (anticlockwise) {
  10381. var tmp = endAngle;
  10382. endAngle = startAngle;
  10383. startAngle = tmp;
  10384. }
  10385. // var number = 0;
  10386. // var step = (anticlockwise ? -Math.PI : Math.PI) / 2;
  10387. for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
  10388. if (angle > startAngle) {
  10389. extremity[0] = mathCos$2(angle) * rx + x;
  10390. extremity[1] = mathSin$2(angle) * ry + y;
  10391. vec2Min(min$$1, extremity, min$$1);
  10392. vec2Max(max$$1, extremity, max$$1);
  10393. }
  10394. }
  10395. }
  10396. /**
  10397. * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
  10398. * 可以用于 isInsidePath 判断以及获取boundingRect
  10399. *
  10400. * @module zrender/core/PathProxy
  10401. * @author Yi Shen (http://www.github.com/pissang)
  10402. */
  10403. // TODO getTotalLength, getPointAtLength
  10404. var CMD = {
  10405. M: 1,
  10406. L: 2,
  10407. C: 3,
  10408. Q: 4,
  10409. A: 5,
  10410. Z: 6,
  10411. // Rect
  10412. R: 7
  10413. };
  10414. // var CMD_MEM_SIZE = {
  10415. // M: 3,
  10416. // L: 3,
  10417. // C: 7,
  10418. // Q: 5,
  10419. // A: 9,
  10420. // R: 5,
  10421. // Z: 1
  10422. // };
  10423. var min$1 = [];
  10424. var max$1 = [];
  10425. var min2 = [];
  10426. var max2 = [];
  10427. var mathMin$2 = Math.min;
  10428. var mathMax$2 = Math.max;
  10429. var mathCos$1 = Math.cos;
  10430. var mathSin$1 = Math.sin;
  10431. var mathSqrt$1 = Math.sqrt;
  10432. var mathAbs = Math.abs;
  10433. var hasTypedArray = typeof Float32Array != 'undefined';
  10434. /**
  10435. * @alias module:zrender/core/PathProxy
  10436. * @constructor
  10437. */
  10438. var PathProxy = function (notSaveData) {
  10439. this._saveData = !(notSaveData || false);
  10440. if (this._saveData) {
  10441. /**
  10442. * Path data. Stored as flat array
  10443. * @type {Array.<Object>}
  10444. */
  10445. this.data = [];
  10446. }
  10447. this._ctx = null;
  10448. };
  10449. /**
  10450. * 快速计算Path包围盒(并不是最小包围盒)
  10451. * @return {Object}
  10452. */
  10453. PathProxy.prototype = {
  10454. constructor: PathProxy,
  10455. _xi: 0,
  10456. _yi: 0,
  10457. _x0: 0,
  10458. _y0: 0,
  10459. // Unit x, Unit y. Provide for avoiding drawing that too short line segment
  10460. _ux: 0,
  10461. _uy: 0,
  10462. _len: 0,
  10463. _lineDash: null,
  10464. _dashOffset: 0,
  10465. _dashIdx: 0,
  10466. _dashSum: 0,
  10467. /**
  10468. * @readOnly
  10469. */
  10470. setScale: function (sx, sy) {
  10471. this._ux = mathAbs(1 / devicePixelRatio / sx) || 0;
  10472. this._uy = mathAbs(1 / devicePixelRatio / sy) || 0;
  10473. },
  10474. getContext: function () {
  10475. return this._ctx;
  10476. },
  10477. /**
  10478. * @param {CanvasRenderingContext2D} ctx
  10479. * @return {module:zrender/core/PathProxy}
  10480. */
  10481. beginPath: function (ctx) {
  10482. this._ctx = ctx;
  10483. ctx && ctx.beginPath();
  10484. ctx && (this.dpr = ctx.dpr);
  10485. // Reset
  10486. if (this._saveData) {
  10487. this._len = 0;
  10488. }
  10489. if (this._lineDash) {
  10490. this._lineDash = null;
  10491. this._dashOffset = 0;
  10492. }
  10493. return this;
  10494. },
  10495. /**
  10496. * @param {number} x
  10497. * @param {number} y
  10498. * @return {module:zrender/core/PathProxy}
  10499. */
  10500. moveTo: function (x, y) {
  10501. this.addData(CMD.M, x, y);
  10502. this._ctx && this._ctx.moveTo(x, y);
  10503. // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
  10504. // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
  10505. // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要
  10506. // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持
  10507. this._x0 = x;
  10508. this._y0 = y;
  10509. this._xi = x;
  10510. this._yi = y;
  10511. return this;
  10512. },
  10513. /**
  10514. * @param {number} x
  10515. * @param {number} y
  10516. * @return {module:zrender/core/PathProxy}
  10517. */
  10518. lineTo: function (x, y) {
  10519. var exceedUnit = mathAbs(x - this._xi) > this._ux
  10520. || mathAbs(y - this._yi) > this._uy
  10521. // Force draw the first segment
  10522. || this._len < 5;
  10523. this.addData(CMD.L, x, y);
  10524. if (this._ctx && exceedUnit) {
  10525. this._needsDash() ? this._dashedLineTo(x, y)
  10526. : this._ctx.lineTo(x, y);
  10527. }
  10528. if (exceedUnit) {
  10529. this._xi = x;
  10530. this._yi = y;
  10531. }
  10532. return this;
  10533. },
  10534. /**
  10535. * @param {number} x1
  10536. * @param {number} y1
  10537. * @param {number} x2
  10538. * @param {number} y2
  10539. * @param {number} x3
  10540. * @param {number} y3
  10541. * @return {module:zrender/core/PathProxy}
  10542. */
  10543. bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
  10544. this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
  10545. if (this._ctx) {
  10546. this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)
  10547. : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  10548. }
  10549. this._xi = x3;
  10550. this._yi = y3;
  10551. return this;
  10552. },
  10553. /**
  10554. * @param {number} x1
  10555. * @param {number} y1
  10556. * @param {number} x2
  10557. * @param {number} y2
  10558. * @return {module:zrender/core/PathProxy}
  10559. */
  10560. quadraticCurveTo: function (x1, y1, x2, y2) {
  10561. this.addData(CMD.Q, x1, y1, x2, y2);
  10562. if (this._ctx) {
  10563. this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2)
  10564. : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
  10565. }
  10566. this._xi = x2;
  10567. this._yi = y2;
  10568. return this;
  10569. },
  10570. /**
  10571. * @param {number} cx
  10572. * @param {number} cy
  10573. * @param {number} r
  10574. * @param {number} startAngle
  10575. * @param {number} endAngle
  10576. * @param {boolean} anticlockwise
  10577. * @return {module:zrender/core/PathProxy}
  10578. */
  10579. arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
  10580. this.addData(
  10581. CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1
  10582. );
  10583. this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
  10584. this._xi = mathCos$1(endAngle) * r + cx;
  10585. this._yi = mathSin$1(endAngle) * r + cx;
  10586. return this;
  10587. },
  10588. // TODO
  10589. arcTo: function (x1, y1, x2, y2, radius) {
  10590. if (this._ctx) {
  10591. this._ctx.arcTo(x1, y1, x2, y2, radius);
  10592. }
  10593. return this;
  10594. },
  10595. // TODO
  10596. rect: function (x, y, w, h) {
  10597. this._ctx && this._ctx.rect(x, y, w, h);
  10598. this.addData(CMD.R, x, y, w, h);
  10599. return this;
  10600. },
  10601. /**
  10602. * @return {module:zrender/core/PathProxy}
  10603. */
  10604. closePath: function () {
  10605. this.addData(CMD.Z);
  10606. var ctx = this._ctx;
  10607. var x0 = this._x0;
  10608. var y0 = this._y0;
  10609. if (ctx) {
  10610. this._needsDash() && this._dashedLineTo(x0, y0);
  10611. ctx.closePath();
  10612. }
  10613. this._xi = x0;
  10614. this._yi = y0;
  10615. return this;
  10616. },
  10617. /**
  10618. * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。
  10619. * stroke 同样
  10620. * @param {CanvasRenderingContext2D} ctx
  10621. * @return {module:zrender/core/PathProxy}
  10622. */
  10623. fill: function (ctx) {
  10624. ctx && ctx.fill();
  10625. this.toStatic();
  10626. },
  10627. /**
  10628. * @param {CanvasRenderingContext2D} ctx
  10629. * @return {module:zrender/core/PathProxy}
  10630. */
  10631. stroke: function (ctx) {
  10632. ctx && ctx.stroke();
  10633. this.toStatic();
  10634. },
  10635. /**
  10636. * 必须在其它绘制命令前调用
  10637. * Must be invoked before all other path drawing methods
  10638. * @return {module:zrender/core/PathProxy}
  10639. */
  10640. setLineDash: function (lineDash) {
  10641. if (lineDash instanceof Array) {
  10642. this._lineDash = lineDash;
  10643. this._dashIdx = 0;
  10644. var lineDashSum = 0;
  10645. for (var i = 0; i < lineDash.length; i++) {
  10646. lineDashSum += lineDash[i];
  10647. }
  10648. this._dashSum = lineDashSum;
  10649. }
  10650. return this;
  10651. },
  10652. /**
  10653. * 必须在其它绘制命令前调用
  10654. * Must be invoked before all other path drawing methods
  10655. * @return {module:zrender/core/PathProxy}
  10656. */
  10657. setLineDashOffset: function (offset) {
  10658. this._dashOffset = offset;
  10659. return this;
  10660. },
  10661. /**
  10662. *
  10663. * @return {boolean}
  10664. */
  10665. len: function () {
  10666. return this._len;
  10667. },
  10668. /**
  10669. * 直接设置 Path 数据
  10670. */
  10671. setData: function (data) {
  10672. var len$$1 = data.length;
  10673. if (! (this.data && this.data.length == len$$1) && hasTypedArray) {
  10674. this.data = new Float32Array(len$$1);
  10675. }
  10676. for (var i = 0; i < len$$1; i++) {
  10677. this.data[i] = data[i];
  10678. }
  10679. this._len = len$$1;
  10680. },
  10681. /**
  10682. * 添加子路径
  10683. * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
  10684. */
  10685. appendPath: function (path) {
  10686. if (!(path instanceof Array)) {
  10687. path = [path];
  10688. }
  10689. var len$$1 = path.length;
  10690. var appendSize = 0;
  10691. var offset = this._len;
  10692. for (var i = 0; i < len$$1; i++) {
  10693. appendSize += path[i].len();
  10694. }
  10695. if (hasTypedArray && (this.data instanceof Float32Array)) {
  10696. this.data = new Float32Array(offset + appendSize);
  10697. }
  10698. for (var i = 0; i < len$$1; i++) {
  10699. var appendPathData = path[i].data;
  10700. for (var k = 0; k < appendPathData.length; k++) {
  10701. this.data[offset++] = appendPathData[k];
  10702. }
  10703. }
  10704. this._len = offset;
  10705. },
  10706. /**
  10707. * 填充 Path 数据。
  10708. * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
  10709. */
  10710. addData: function (cmd) {
  10711. if (!this._saveData) {
  10712. return;
  10713. }
  10714. var data = this.data;
  10715. if (this._len + arguments.length > data.length) {
  10716. // 因为之前的数组已经转换成静态的 Float32Array
  10717. // 所以不够用时需要扩展一个新的动态数组
  10718. this._expandData();
  10719. data = this.data;
  10720. }
  10721. for (var i = 0; i < arguments.length; i++) {
  10722. data[this._len++] = arguments[i];
  10723. }
  10724. this._prevCmd = cmd;
  10725. },
  10726. _expandData: function () {
  10727. // Only if data is Float32Array
  10728. if (!(this.data instanceof Array)) {
  10729. var newData = [];
  10730. for (var i = 0; i < this._len; i++) {
  10731. newData[i] = this.data[i];
  10732. }
  10733. this.data = newData;
  10734. }
  10735. },
  10736. /**
  10737. * If needs js implemented dashed line
  10738. * @return {boolean}
  10739. * @private
  10740. */
  10741. _needsDash: function () {
  10742. return this._lineDash;
  10743. },
  10744. _dashedLineTo: function (x1, y1) {
  10745. var dashSum = this._dashSum;
  10746. var offset = this._dashOffset;
  10747. var lineDash = this._lineDash;
  10748. var ctx = this._ctx;
  10749. var x0 = this._xi;
  10750. var y0 = this._yi;
  10751. var dx = x1 - x0;
  10752. var dy = y1 - y0;
  10753. var dist$$1 = mathSqrt$1(dx * dx + dy * dy);
  10754. var x = x0;
  10755. var y = y0;
  10756. var dash;
  10757. var nDash = lineDash.length;
  10758. var idx;
  10759. dx /= dist$$1;
  10760. dy /= dist$$1;
  10761. if (offset < 0) {
  10762. // Convert to positive offset
  10763. offset = dashSum + offset;
  10764. }
  10765. offset %= dashSum;
  10766. x -= offset * dx;
  10767. y -= offset * dy;
  10768. while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1)
  10769. || (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) {
  10770. idx = this._dashIdx;
  10771. dash = lineDash[idx];
  10772. x += dx * dash;
  10773. y += dy * dash;
  10774. this._dashIdx = (idx + 1) % nDash;
  10775. // Skip positive offset
  10776. if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) {
  10777. continue;
  10778. }
  10779. ctx[idx % 2 ? 'moveTo' : 'lineTo'](
  10780. dx >= 0 ? mathMin$2(x, x1) : mathMax$2(x, x1),
  10781. dy >= 0 ? mathMin$2(y, y1) : mathMax$2(y, y1)
  10782. );
  10783. }
  10784. // Offset for next lineTo
  10785. dx = x - x1;
  10786. dy = y - y1;
  10787. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  10788. },
  10789. // Not accurate dashed line to
  10790. _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
  10791. var dashSum = this._dashSum;
  10792. var offset = this._dashOffset;
  10793. var lineDash = this._lineDash;
  10794. var ctx = this._ctx;
  10795. var x0 = this._xi;
  10796. var y0 = this._yi;
  10797. var t;
  10798. var dx;
  10799. var dy;
  10800. var cubicAt$$1 = cubicAt;
  10801. var bezierLen = 0;
  10802. var idx = this._dashIdx;
  10803. var nDash = lineDash.length;
  10804. var x;
  10805. var y;
  10806. var tmpLen = 0;
  10807. if (offset < 0) {
  10808. // Convert to positive offset
  10809. offset = dashSum + offset;
  10810. }
  10811. offset %= dashSum;
  10812. // Bezier approx length
  10813. for (t = 0; t < 1; t += 0.1) {
  10814. dx = cubicAt$$1(x0, x1, x2, x3, t + 0.1)
  10815. - cubicAt$$1(x0, x1, x2, x3, t);
  10816. dy = cubicAt$$1(y0, y1, y2, y3, t + 0.1)
  10817. - cubicAt$$1(y0, y1, y2, y3, t);
  10818. bezierLen += mathSqrt$1(dx * dx + dy * dy);
  10819. }
  10820. // Find idx after add offset
  10821. for (; idx < nDash; idx++) {
  10822. tmpLen += lineDash[idx];
  10823. if (tmpLen > offset) {
  10824. break;
  10825. }
  10826. }
  10827. t = (tmpLen - offset) / bezierLen;
  10828. while (t <= 1) {
  10829. x = cubicAt$$1(x0, x1, x2, x3, t);
  10830. y = cubicAt$$1(y0, y1, y2, y3, t);
  10831. // Use line to approximate dashed bezier
  10832. // Bad result if dash is long
  10833. idx % 2 ? ctx.moveTo(x, y)
  10834. : ctx.lineTo(x, y);
  10835. t += lineDash[idx] / bezierLen;
  10836. idx = (idx + 1) % nDash;
  10837. }
  10838. // Finish the last segment and calculate the new offset
  10839. (idx % 2 !== 0) && ctx.lineTo(x3, y3);
  10840. dx = x3 - x;
  10841. dy = y3 - y;
  10842. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  10843. },
  10844. _dashedQuadraticTo: function (x1, y1, x2, y2) {
  10845. // Convert quadratic to cubic using degree elevation
  10846. var x3 = x2;
  10847. var y3 = y2;
  10848. x2 = (x2 + 2 * x1) / 3;
  10849. y2 = (y2 + 2 * y1) / 3;
  10850. x1 = (this._xi + 2 * x1) / 3;
  10851. y1 = (this._yi + 2 * y1) / 3;
  10852. this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
  10853. },
  10854. /**
  10855. * 转成静态的 Float32Array 减少堆内存占用
  10856. * Convert dynamic array to static Float32Array
  10857. */
  10858. toStatic: function () {
  10859. var data = this.data;
  10860. if (data instanceof Array) {
  10861. data.length = this._len;
  10862. if (hasTypedArray) {
  10863. this.data = new Float32Array(data);
  10864. }
  10865. }
  10866. },
  10867. /**
  10868. * @return {module:zrender/core/BoundingRect}
  10869. */
  10870. getBoundingRect: function () {
  10871. min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;
  10872. max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
  10873. var data = this.data;
  10874. var xi = 0;
  10875. var yi = 0;
  10876. var x0 = 0;
  10877. var y0 = 0;
  10878. for (var i = 0; i < data.length;) {
  10879. var cmd = data[i++];
  10880. if (i == 1) {
  10881. // 如果第一个命令是 L, C, Q
  10882. // 则 previous point 同绘制命令的第一个 point
  10883. //
  10884. // 第一个命令为 Arc 的情况下会在后面特殊处理
  10885. xi = data[i];
  10886. yi = data[i + 1];
  10887. x0 = xi;
  10888. y0 = yi;
  10889. }
  10890. switch (cmd) {
  10891. case CMD.M:
  10892. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  10893. // 在 closePath 的时候使用
  10894. x0 = data[i++];
  10895. y0 = data[i++];
  10896. xi = x0;
  10897. yi = y0;
  10898. min2[0] = x0;
  10899. min2[1] = y0;
  10900. max2[0] = x0;
  10901. max2[1] = y0;
  10902. break;
  10903. case CMD.L:
  10904. fromLine(xi, yi, data[i], data[i + 1], min2, max2);
  10905. xi = data[i++];
  10906. yi = data[i++];
  10907. break;
  10908. case CMD.C:
  10909. fromCubic(
  10910. xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  10911. min2, max2
  10912. );
  10913. xi = data[i++];
  10914. yi = data[i++];
  10915. break;
  10916. case CMD.Q:
  10917. fromQuadratic(
  10918. xi, yi, data[i++], data[i++], data[i], data[i + 1],
  10919. min2, max2
  10920. );
  10921. xi = data[i++];
  10922. yi = data[i++];
  10923. break;
  10924. case CMD.A:
  10925. // TODO Arc 判断的开销比较大
  10926. var cx = data[i++];
  10927. var cy = data[i++];
  10928. var rx = data[i++];
  10929. var ry = data[i++];
  10930. var startAngle = data[i++];
  10931. var endAngle = data[i++] + startAngle;
  10932. // TODO Arc 旋转
  10933. var psi = data[i++];
  10934. var anticlockwise = 1 - data[i++];
  10935. if (i == 1) {
  10936. // 直接使用 arc 命令
  10937. // 第一个命令起点还未定义
  10938. x0 = mathCos$1(startAngle) * rx + cx;
  10939. y0 = mathSin$1(startAngle) * ry + cy;
  10940. }
  10941. fromArc(
  10942. cx, cy, rx, ry, startAngle, endAngle,
  10943. anticlockwise, min2, max2
  10944. );
  10945. xi = mathCos$1(endAngle) * rx + cx;
  10946. yi = mathSin$1(endAngle) * ry + cy;
  10947. break;
  10948. case CMD.R:
  10949. x0 = xi = data[i++];
  10950. y0 = yi = data[i++];
  10951. var width = data[i++];
  10952. var height = data[i++];
  10953. // Use fromLine
  10954. fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
  10955. break;
  10956. case CMD.Z:
  10957. xi = x0;
  10958. yi = y0;
  10959. break;
  10960. }
  10961. // Union
  10962. min(min$1, min$1, min2);
  10963. max(max$1, max$1, max2);
  10964. }
  10965. // No data
  10966. if (i === 0) {
  10967. min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;
  10968. }
  10969. return new BoundingRect(
  10970. min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]
  10971. );
  10972. },
  10973. /**
  10974. * Rebuild path from current data
  10975. * Rebuild path will not consider javascript implemented line dash.
  10976. * @param {CanvasRenderingContext2D} ctx
  10977. */
  10978. rebuildPath: function (ctx) {
  10979. var d = this.data;
  10980. var x0, y0;
  10981. var xi, yi;
  10982. var x, y;
  10983. var ux = this._ux;
  10984. var uy = this._uy;
  10985. var len$$1 = this._len;
  10986. for (var i = 0; i < len$$1;) {
  10987. var cmd = d[i++];
  10988. if (i == 1) {
  10989. // 如果第一个命令是 L, C, Q
  10990. // 则 previous point 同绘制命令的第一个 point
  10991. //
  10992. // 第一个命令为 Arc 的情况下会在后面特殊处理
  10993. xi = d[i];
  10994. yi = d[i + 1];
  10995. x0 = xi;
  10996. y0 = yi;
  10997. }
  10998. switch (cmd) {
  10999. case CMD.M:
  11000. x0 = xi = d[i++];
  11001. y0 = yi = d[i++];
  11002. ctx.moveTo(xi, yi);
  11003. break;
  11004. case CMD.L:
  11005. x = d[i++];
  11006. y = d[i++];
  11007. // Not draw too small seg between
  11008. if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len$$1 - 1) {
  11009. ctx.lineTo(x, y);
  11010. xi = x;
  11011. yi = y;
  11012. }
  11013. break;
  11014. case CMD.C:
  11015. ctx.bezierCurveTo(
  11016. d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]
  11017. );
  11018. xi = d[i - 2];
  11019. yi = d[i - 1];
  11020. break;
  11021. case CMD.Q:
  11022. ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
  11023. xi = d[i - 2];
  11024. yi = d[i - 1];
  11025. break;
  11026. case CMD.A:
  11027. var cx = d[i++];
  11028. var cy = d[i++];
  11029. var rx = d[i++];
  11030. var ry = d[i++];
  11031. var theta = d[i++];
  11032. var dTheta = d[i++];
  11033. var psi = d[i++];
  11034. var fs = d[i++];
  11035. var r = (rx > ry) ? rx : ry;
  11036. var scaleX = (rx > ry) ? 1 : rx / ry;
  11037. var scaleY = (rx > ry) ? ry / rx : 1;
  11038. var isEllipse = Math.abs(rx - ry) > 1e-3;
  11039. var endAngle = theta + dTheta;
  11040. if (isEllipse) {
  11041. ctx.translate(cx, cy);
  11042. ctx.rotate(psi);
  11043. ctx.scale(scaleX, scaleY);
  11044. ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
  11045. ctx.scale(1 / scaleX, 1 / scaleY);
  11046. ctx.rotate(-psi);
  11047. ctx.translate(-cx, -cy);
  11048. }
  11049. else {
  11050. ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
  11051. }
  11052. if (i == 1) {
  11053. // 直接使用 arc 命令
  11054. // 第一个命令起点还未定义
  11055. x0 = mathCos$1(theta) * rx + cx;
  11056. y0 = mathSin$1(theta) * ry + cy;
  11057. }
  11058. xi = mathCos$1(endAngle) * rx + cx;
  11059. yi = mathSin$1(endAngle) * ry + cy;
  11060. break;
  11061. case CMD.R:
  11062. x0 = xi = d[i];
  11063. y0 = yi = d[i + 1];
  11064. ctx.rect(d[i++], d[i++], d[i++], d[i++]);
  11065. break;
  11066. case CMD.Z:
  11067. ctx.closePath();
  11068. xi = x0;
  11069. yi = y0;
  11070. }
  11071. }
  11072. }
  11073. };
  11074. PathProxy.CMD = CMD;
  11075. /**
  11076. * 线段包含判断
  11077. * @param {number} x0
  11078. * @param {number} y0
  11079. * @param {number} x1
  11080. * @param {number} y1
  11081. * @param {number} lineWidth
  11082. * @param {number} x
  11083. * @param {number} y
  11084. * @return {boolean}
  11085. */
  11086. function containStroke$1(x0, y0, x1, y1, lineWidth, x, y) {
  11087. if (lineWidth === 0) {
  11088. return false;
  11089. }
  11090. var _l = lineWidth;
  11091. var _a = 0;
  11092. var _b = x0;
  11093. // Quick reject
  11094. if (
  11095. (y > y0 + _l && y > y1 + _l)
  11096. || (y < y0 - _l && y < y1 - _l)
  11097. || (x > x0 + _l && x > x1 + _l)
  11098. || (x < x0 - _l && x < x1 - _l)
  11099. ) {
  11100. return false;
  11101. }
  11102. if (x0 !== x1) {
  11103. _a = (y0 - y1) / (x0 - x1);
  11104. _b = (x0 * y1 - x1 * y0) / (x0 - x1) ;
  11105. }
  11106. else {
  11107. return Math.abs(x - x0) <= _l / 2;
  11108. }
  11109. var tmp = _a * x - y + _b;
  11110. var _s = tmp * tmp / (_a * _a + 1);
  11111. return _s <= _l / 2 * _l / 2;
  11112. }
  11113. /**
  11114. * 三次贝塞尔曲线描边包含判断
  11115. * @param {number} x0
  11116. * @param {number} y0
  11117. * @param {number} x1
  11118. * @param {number} y1
  11119. * @param {number} x2
  11120. * @param {number} y2
  11121. * @param {number} x3
  11122. * @param {number} y3
  11123. * @param {number} lineWidth
  11124. * @param {number} x
  11125. * @param {number} y
  11126. * @return {boolean}
  11127. */
  11128. function containStroke$2(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
  11129. if (lineWidth === 0) {
  11130. return false;
  11131. }
  11132. var _l = lineWidth;
  11133. // Quick reject
  11134. if (
  11135. (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)
  11136. || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)
  11137. || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)
  11138. || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)
  11139. ) {
  11140. return false;
  11141. }
  11142. var d = cubicProjectPoint(
  11143. x0, y0, x1, y1, x2, y2, x3, y3,
  11144. x, y, null
  11145. );
  11146. return d <= _l / 2;
  11147. }
  11148. /**
  11149. * 二次贝塞尔曲线描边包含判断
  11150. * @param {number} x0
  11151. * @param {number} y0
  11152. * @param {number} x1
  11153. * @param {number} y1
  11154. * @param {number} x2
  11155. * @param {number} y2
  11156. * @param {number} lineWidth
  11157. * @param {number} x
  11158. * @param {number} y
  11159. * @return {boolean}
  11160. */
  11161. function containStroke$3(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
  11162. if (lineWidth === 0) {
  11163. return false;
  11164. }
  11165. var _l = lineWidth;
  11166. // Quick reject
  11167. if (
  11168. (y > y0 + _l && y > y1 + _l && y > y2 + _l)
  11169. || (y < y0 - _l && y < y1 - _l && y < y2 - _l)
  11170. || (x > x0 + _l && x > x1 + _l && x > x2 + _l)
  11171. || (x < x0 - _l && x < x1 - _l && x < x2 - _l)
  11172. ) {
  11173. return false;
  11174. }
  11175. var d = quadraticProjectPoint(
  11176. x0, y0, x1, y1, x2, y2,
  11177. x, y, null
  11178. );
  11179. return d <= _l / 2;
  11180. }
  11181. var PI2$3 = Math.PI * 2;
  11182. function normalizeRadian(angle) {
  11183. angle %= PI2$3;
  11184. if (angle < 0) {
  11185. angle += PI2$3;
  11186. }
  11187. return angle;
  11188. }
  11189. var PI2$2 = Math.PI * 2;
  11190. /**
  11191. * 圆弧描边包含判断
  11192. * @param {number} cx
  11193. * @param {number} cy
  11194. * @param {number} r
  11195. * @param {number} startAngle
  11196. * @param {number} endAngle
  11197. * @param {boolean} anticlockwise
  11198. * @param {number} lineWidth
  11199. * @param {number} x
  11200. * @param {number} y
  11201. * @return {Boolean}
  11202. */
  11203. function containStroke$4(
  11204. cx, cy, r, startAngle, endAngle, anticlockwise,
  11205. lineWidth, x, y
  11206. ) {
  11207. if (lineWidth === 0) {
  11208. return false;
  11209. }
  11210. var _l = lineWidth;
  11211. x -= cx;
  11212. y -= cy;
  11213. var d = Math.sqrt(x * x + y * y);
  11214. if ((d - _l > r) || (d + _l < r)) {
  11215. return false;
  11216. }
  11217. if (Math.abs(startAngle - endAngle) % PI2$2 < 1e-4) {
  11218. // Is a circle
  11219. return true;
  11220. }
  11221. if (anticlockwise) {
  11222. var tmp = startAngle;
  11223. startAngle = normalizeRadian(endAngle);
  11224. endAngle = normalizeRadian(tmp);
  11225. } else {
  11226. startAngle = normalizeRadian(startAngle);
  11227. endAngle = normalizeRadian(endAngle);
  11228. }
  11229. if (startAngle > endAngle) {
  11230. endAngle += PI2$2;
  11231. }
  11232. var angle = Math.atan2(y, x);
  11233. if (angle < 0) {
  11234. angle += PI2$2;
  11235. }
  11236. return (angle >= startAngle && angle <= endAngle)
  11237. || (angle + PI2$2 >= startAngle && angle + PI2$2 <= endAngle);
  11238. }
  11239. function windingLine(x0, y0, x1, y1, x, y) {
  11240. if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
  11241. return 0;
  11242. }
  11243. // Ignore horizontal line
  11244. if (y1 === y0) {
  11245. return 0;
  11246. }
  11247. var dir = y1 < y0 ? 1 : -1;
  11248. var t = (y - y0) / (y1 - y0);
  11249. // Avoid winding error when intersection point is the connect point of two line of polygon
  11250. if (t === 1 || t === 0) {
  11251. dir = y1 < y0 ? 0.5 : -0.5;
  11252. }
  11253. var x_ = t * (x1 - x0) + x0;
  11254. return x_ > x ? dir : 0;
  11255. }
  11256. var CMD$1 = PathProxy.CMD;
  11257. var PI2$1 = Math.PI * 2;
  11258. var EPSILON$2 = 1e-4;
  11259. function isAroundEqual(a, b) {
  11260. return Math.abs(a - b) < EPSILON$2;
  11261. }
  11262. // 临时数组
  11263. var roots = [-1, -1, -1];
  11264. var extrema = [-1, -1];
  11265. function swapExtrema() {
  11266. var tmp = extrema[0];
  11267. extrema[0] = extrema[1];
  11268. extrema[1] = tmp;
  11269. }
  11270. function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
  11271. // Quick reject
  11272. if (
  11273. (y > y0 && y > y1 && y > y2 && y > y3)
  11274. || (y < y0 && y < y1 && y < y2 && y < y3)
  11275. ) {
  11276. return 0;
  11277. }
  11278. var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);
  11279. if (nRoots === 0) {
  11280. return 0;
  11281. }
  11282. else {
  11283. var w = 0;
  11284. var nExtrema = -1;
  11285. var y0_, y1_;
  11286. for (var i = 0; i < nRoots; i++) {
  11287. var t = roots[i];
  11288. // Avoid winding error when intersection point is the connect point of two line of polygon
  11289. var unit = (t === 0 || t === 1) ? 0.5 : 1;
  11290. var x_ = cubicAt(x0, x1, x2, x3, t);
  11291. if (x_ < x) { // Quick reject
  11292. continue;
  11293. }
  11294. if (nExtrema < 0) {
  11295. nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);
  11296. if (extrema[1] < extrema[0] && nExtrema > 1) {
  11297. swapExtrema();
  11298. }
  11299. y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);
  11300. if (nExtrema > 1) {
  11301. y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);
  11302. }
  11303. }
  11304. if (nExtrema == 2) {
  11305. // 分成三段单调函数
  11306. if (t < extrema[0]) {
  11307. w += y0_ < y0 ? unit : -unit;
  11308. }
  11309. else if (t < extrema[1]) {
  11310. w += y1_ < y0_ ? unit : -unit;
  11311. }
  11312. else {
  11313. w += y3 < y1_ ? unit : -unit;
  11314. }
  11315. }
  11316. else {
  11317. // 分成两段单调函数
  11318. if (t < extrema[0]) {
  11319. w += y0_ < y0 ? unit : -unit;
  11320. }
  11321. else {
  11322. w += y3 < y0_ ? unit : -unit;
  11323. }
  11324. }
  11325. }
  11326. return w;
  11327. }
  11328. }
  11329. function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
  11330. // Quick reject
  11331. if (
  11332. (y > y0 && y > y1 && y > y2)
  11333. || (y < y0 && y < y1 && y < y2)
  11334. ) {
  11335. return 0;
  11336. }
  11337. var nRoots = quadraticRootAt(y0, y1, y2, y, roots);
  11338. if (nRoots === 0) {
  11339. return 0;
  11340. }
  11341. else {
  11342. var t = quadraticExtremum(y0, y1, y2);
  11343. if (t >= 0 && t <= 1) {
  11344. var w = 0;
  11345. var y_ = quadraticAt(y0, y1, y2, t);
  11346. for (var i = 0; i < nRoots; i++) {
  11347. // Remove one endpoint.
  11348. var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
  11349. var x_ = quadraticAt(x0, x1, x2, roots[i]);
  11350. if (x_ < x) { // Quick reject
  11351. continue;
  11352. }
  11353. if (roots[i] < t) {
  11354. w += y_ < y0 ? unit : -unit;
  11355. }
  11356. else {
  11357. w += y2 < y_ ? unit : -unit;
  11358. }
  11359. }
  11360. return w;
  11361. }
  11362. else {
  11363. // Remove one endpoint.
  11364. var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
  11365. var x_ = quadraticAt(x0, x1, x2, roots[0]);
  11366. if (x_ < x) { // Quick reject
  11367. return 0;
  11368. }
  11369. return y2 < y0 ? unit : -unit;
  11370. }
  11371. }
  11372. }
  11373. // TODO
  11374. // Arc 旋转
  11375. function windingArc(
  11376. cx, cy, r, startAngle, endAngle, anticlockwise, x, y
  11377. ) {
  11378. y -= cy;
  11379. if (y > r || y < -r) {
  11380. return 0;
  11381. }
  11382. var tmp = Math.sqrt(r * r - y * y);
  11383. roots[0] = -tmp;
  11384. roots[1] = tmp;
  11385. var diff = Math.abs(startAngle - endAngle);
  11386. if (diff < 1e-4) {
  11387. return 0;
  11388. }
  11389. if (diff % PI2$1 < 1e-4) {
  11390. // Is a circle
  11391. startAngle = 0;
  11392. endAngle = PI2$1;
  11393. var dir = anticlockwise ? 1 : -1;
  11394. if (x >= roots[0] + cx && x <= roots[1] + cx) {
  11395. return dir;
  11396. } else {
  11397. return 0;
  11398. }
  11399. }
  11400. if (anticlockwise) {
  11401. var tmp = startAngle;
  11402. startAngle = normalizeRadian(endAngle);
  11403. endAngle = normalizeRadian(tmp);
  11404. }
  11405. else {
  11406. startAngle = normalizeRadian(startAngle);
  11407. endAngle = normalizeRadian(endAngle);
  11408. }
  11409. if (startAngle > endAngle) {
  11410. endAngle += PI2$1;
  11411. }
  11412. var w = 0;
  11413. for (var i = 0; i < 2; i++) {
  11414. var x_ = roots[i];
  11415. if (x_ + cx > x) {
  11416. var angle = Math.atan2(y, x_);
  11417. var dir = anticlockwise ? 1 : -1;
  11418. if (angle < 0) {
  11419. angle = PI2$1 + angle;
  11420. }
  11421. if (
  11422. (angle >= startAngle && angle <= endAngle)
  11423. || (angle + PI2$1 >= startAngle && angle + PI2$1 <= endAngle)
  11424. ) {
  11425. if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
  11426. dir = -dir;
  11427. }
  11428. w += dir;
  11429. }
  11430. }
  11431. }
  11432. return w;
  11433. }
  11434. function containPath(data, lineWidth, isStroke, x, y) {
  11435. var w = 0;
  11436. var xi = 0;
  11437. var yi = 0;
  11438. var x0 = 0;
  11439. var y0 = 0;
  11440. for (var i = 0; i < data.length;) {
  11441. var cmd = data[i++];
  11442. // Begin a new subpath
  11443. if (cmd === CMD$1.M && i > 1) {
  11444. // Close previous subpath
  11445. if (!isStroke) {
  11446. w += windingLine(xi, yi, x0, y0, x, y);
  11447. }
  11448. // 如果被任何一个 subpath 包含
  11449. // if (w !== 0) {
  11450. // return true;
  11451. // }
  11452. }
  11453. if (i == 1) {
  11454. // 如果第一个命令是 L, C, Q
  11455. // 则 previous point 同绘制命令的第一个 point
  11456. //
  11457. // 第一个命令为 Arc 的情况下会在后面特殊处理
  11458. xi = data[i];
  11459. yi = data[i + 1];
  11460. x0 = xi;
  11461. y0 = yi;
  11462. }
  11463. switch (cmd) {
  11464. case CMD$1.M:
  11465. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  11466. // 在 closePath 的时候使用
  11467. x0 = data[i++];
  11468. y0 = data[i++];
  11469. xi = x0;
  11470. yi = y0;
  11471. break;
  11472. case CMD$1.L:
  11473. if (isStroke) {
  11474. if (containStroke$1(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
  11475. return true;
  11476. }
  11477. }
  11478. else {
  11479. // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN
  11480. w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
  11481. }
  11482. xi = data[i++];
  11483. yi = data[i++];
  11484. break;
  11485. case CMD$1.C:
  11486. if (isStroke) {
  11487. if (containStroke$2(xi, yi,
  11488. data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  11489. lineWidth, x, y
  11490. )) {
  11491. return true;
  11492. }
  11493. }
  11494. else {
  11495. w += windingCubic(
  11496. xi, yi,
  11497. data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  11498. x, y
  11499. ) || 0;
  11500. }
  11501. xi = data[i++];
  11502. yi = data[i++];
  11503. break;
  11504. case CMD$1.Q:
  11505. if (isStroke) {
  11506. if (containStroke$3(xi, yi,
  11507. data[i++], data[i++], data[i], data[i + 1],
  11508. lineWidth, x, y
  11509. )) {
  11510. return true;
  11511. }
  11512. }
  11513. else {
  11514. w += windingQuadratic(
  11515. xi, yi,
  11516. data[i++], data[i++], data[i], data[i + 1],
  11517. x, y
  11518. ) || 0;
  11519. }
  11520. xi = data[i++];
  11521. yi = data[i++];
  11522. break;
  11523. case CMD$1.A:
  11524. // TODO Arc 判断的开销比较大
  11525. var cx = data[i++];
  11526. var cy = data[i++];
  11527. var rx = data[i++];
  11528. var ry = data[i++];
  11529. var theta = data[i++];
  11530. var dTheta = data[i++];
  11531. // TODO Arc 旋转
  11532. var psi = data[i++];
  11533. var anticlockwise = 1 - data[i++];
  11534. var x1 = Math.cos(theta) * rx + cx;
  11535. var y1 = Math.sin(theta) * ry + cy;
  11536. // 不是直接使用 arc 命令
  11537. if (i > 1) {
  11538. w += windingLine(xi, yi, x1, y1, x, y);
  11539. }
  11540. else {
  11541. // 第一个命令起点还未定义
  11542. x0 = x1;
  11543. y0 = y1;
  11544. }
  11545. // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
  11546. var _x = (x - cx) * ry / rx + cx;
  11547. if (isStroke) {
  11548. if (containStroke$4(
  11549. cx, cy, ry, theta, theta + dTheta, anticlockwise,
  11550. lineWidth, _x, y
  11551. )) {
  11552. return true;
  11553. }
  11554. }
  11555. else {
  11556. w += windingArc(
  11557. cx, cy, ry, theta, theta + dTheta, anticlockwise,
  11558. _x, y
  11559. );
  11560. }
  11561. xi = Math.cos(theta + dTheta) * rx + cx;
  11562. yi = Math.sin(theta + dTheta) * ry + cy;
  11563. break;
  11564. case CMD$1.R:
  11565. x0 = xi = data[i++];
  11566. y0 = yi = data[i++];
  11567. var width = data[i++];
  11568. var height = data[i++];
  11569. var x1 = x0 + width;
  11570. var y1 = y0 + height;
  11571. if (isStroke) {
  11572. if (containStroke$1(x0, y0, x1, y0, lineWidth, x, y)
  11573. || containStroke$1(x1, y0, x1, y1, lineWidth, x, y)
  11574. || containStroke$1(x1, y1, x0, y1, lineWidth, x, y)
  11575. || containStroke$1(x0, y1, x0, y0, lineWidth, x, y)
  11576. ) {
  11577. return true;
  11578. }
  11579. }
  11580. else {
  11581. // FIXME Clockwise ?
  11582. w += windingLine(x1, y0, x1, y1, x, y);
  11583. w += windingLine(x0, y1, x0, y0, x, y);
  11584. }
  11585. break;
  11586. case CMD$1.Z:
  11587. if (isStroke) {
  11588. if (containStroke$1(
  11589. xi, yi, x0, y0, lineWidth, x, y
  11590. )) {
  11591. return true;
  11592. }
  11593. }
  11594. else {
  11595. // Close a subpath
  11596. w += windingLine(xi, yi, x0, y0, x, y);
  11597. // 如果被任何一个 subpath 包含
  11598. // FIXME subpaths may overlap
  11599. // if (w !== 0) {
  11600. // return true;
  11601. // }
  11602. }
  11603. xi = x0;
  11604. yi = y0;
  11605. break;
  11606. }
  11607. }
  11608. if (!isStroke && !isAroundEqual(yi, y0)) {
  11609. w += windingLine(xi, yi, x0, y0, x, y) || 0;
  11610. }
  11611. return w !== 0;
  11612. }
  11613. function contain(pathData, x, y) {
  11614. return containPath(pathData, 0, false, x, y);
  11615. }
  11616. function containStroke(pathData, lineWidth, x, y) {
  11617. return containPath(pathData, lineWidth, true, x, y);
  11618. }
  11619. var getCanvasPattern = Pattern.prototype.getCanvasPattern;
  11620. var abs = Math.abs;
  11621. var pathProxyForDraw = new PathProxy(true);
  11622. /**
  11623. * @alias module:zrender/graphic/Path
  11624. * @extends module:zrender/graphic/Displayable
  11625. * @constructor
  11626. * @param {Object} opts
  11627. */
  11628. function Path(opts) {
  11629. Displayable.call(this, opts);
  11630. /**
  11631. * @type {module:zrender/core/PathProxy}
  11632. * @readOnly
  11633. */
  11634. this.path = null;
  11635. }
  11636. Path.prototype = {
  11637. constructor: Path,
  11638. type: 'path',
  11639. __dirtyPath: true,
  11640. strokeContainThreshold: 5,
  11641. brush: function (ctx, prevEl) {
  11642. var style = this.style;
  11643. var path = this.path || pathProxyForDraw;
  11644. var hasStroke = style.hasStroke();
  11645. var hasFill = style.hasFill();
  11646. var fill = style.fill;
  11647. var stroke = style.stroke;
  11648. var hasFillGradient = hasFill && !!(fill.colorStops);
  11649. var hasStrokeGradient = hasStroke && !!(stroke.colorStops);
  11650. var hasFillPattern = hasFill && !!(fill.image);
  11651. var hasStrokePattern = hasStroke && !!(stroke.image);
  11652. style.bind(ctx, this, prevEl);
  11653. this.setTransform(ctx);
  11654. if (this.__dirty) {
  11655. var rect;
  11656. // Update gradient because bounding rect may changed
  11657. if (hasFillGradient) {
  11658. rect = rect || this.getBoundingRect();
  11659. this._fillGradient = style.getGradient(ctx, fill, rect);
  11660. }
  11661. if (hasStrokeGradient) {
  11662. rect = rect || this.getBoundingRect();
  11663. this._strokeGradient = style.getGradient(ctx, stroke, rect);
  11664. }
  11665. }
  11666. // Use the gradient or pattern
  11667. if (hasFillGradient) {
  11668. // PENDING If may have affect the state
  11669. ctx.fillStyle = this._fillGradient;
  11670. }
  11671. else if (hasFillPattern) {
  11672. ctx.fillStyle = getCanvasPattern.call(fill, ctx);
  11673. }
  11674. if (hasStrokeGradient) {
  11675. ctx.strokeStyle = this._strokeGradient;
  11676. }
  11677. else if (hasStrokePattern) {
  11678. ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);
  11679. }
  11680. var lineDash = style.lineDash;
  11681. var lineDashOffset = style.lineDashOffset;
  11682. var ctxLineDash = !!ctx.setLineDash;
  11683. // Update path sx, sy
  11684. var scale = this.getGlobalScale();
  11685. path.setScale(scale[0], scale[1]);
  11686. // Proxy context
  11687. // Rebuild path in following 2 cases
  11688. // 1. Path is dirty
  11689. // 2. Path needs javascript implemented lineDash stroking.
  11690. // In this case, lineDash information will not be saved in PathProxy
  11691. if (this.__dirtyPath
  11692. || (lineDash && !ctxLineDash && hasStroke)
  11693. ) {
  11694. path.beginPath(ctx);
  11695. // Setting line dash before build path
  11696. if (lineDash && !ctxLineDash) {
  11697. path.setLineDash(lineDash);
  11698. path.setLineDashOffset(lineDashOffset);
  11699. }
  11700. this.buildPath(path, this.shape, false);
  11701. // Clear path dirty flag
  11702. if (this.path) {
  11703. this.__dirtyPath = false;
  11704. }
  11705. }
  11706. else {
  11707. // Replay path building
  11708. ctx.beginPath();
  11709. this.path.rebuildPath(ctx);
  11710. }
  11711. hasFill && path.fill(ctx);
  11712. if (lineDash && ctxLineDash) {
  11713. ctx.setLineDash(lineDash);
  11714. ctx.lineDashOffset = lineDashOffset;
  11715. }
  11716. hasStroke && path.stroke(ctx);
  11717. if (lineDash && ctxLineDash) {
  11718. // PENDING
  11719. // Remove lineDash
  11720. ctx.setLineDash([]);
  11721. }
  11722. // Draw rect text
  11723. if (style.text != null) {
  11724. // Only restore transform when needs draw text.
  11725. this.restoreTransform(ctx);
  11726. this.drawRectText(ctx, this.getBoundingRect());
  11727. }
  11728. },
  11729. // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath
  11730. // Like in circle
  11731. buildPath: function (ctx, shapeCfg, inBundle) {},
  11732. createPathProxy: function () {
  11733. this.path = new PathProxy();
  11734. },
  11735. getBoundingRect: function () {
  11736. var rect = this._rect;
  11737. var style = this.style;
  11738. var needsUpdateRect = !rect;
  11739. if (needsUpdateRect) {
  11740. var path = this.path;
  11741. if (!path) {
  11742. // Create path on demand.
  11743. path = this.path = new PathProxy();
  11744. }
  11745. if (this.__dirtyPath) {
  11746. path.beginPath();
  11747. this.buildPath(path, this.shape, false);
  11748. }
  11749. rect = path.getBoundingRect();
  11750. }
  11751. this._rect = rect;
  11752. if (style.hasStroke()) {
  11753. // Needs update rect with stroke lineWidth when
  11754. // 1. Element changes scale or lineWidth
  11755. // 2. Shape is changed
  11756. var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());
  11757. if (this.__dirty || needsUpdateRect) {
  11758. rectWithStroke.copy(rect);
  11759. // FIXME Must after updateTransform
  11760. var w = style.lineWidth;
  11761. // PENDING, Min line width is needed when line is horizontal or vertical
  11762. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  11763. // Only add extra hover lineWidth when there are no fill
  11764. if (!style.hasFill()) {
  11765. w = Math.max(w, this.strokeContainThreshold || 4);
  11766. }
  11767. // Consider line width
  11768. // Line scale can't be 0;
  11769. if (lineScale > 1e-10) {
  11770. rectWithStroke.width += w / lineScale;
  11771. rectWithStroke.height += w / lineScale;
  11772. rectWithStroke.x -= w / lineScale / 2;
  11773. rectWithStroke.y -= w / lineScale / 2;
  11774. }
  11775. }
  11776. // Return rect with stroke
  11777. return rectWithStroke;
  11778. }
  11779. return rect;
  11780. },
  11781. contain: function (x, y) {
  11782. var localPos = this.transformCoordToLocal(x, y);
  11783. var rect = this.getBoundingRect();
  11784. var style = this.style;
  11785. x = localPos[0];
  11786. y = localPos[1];
  11787. if (rect.contain(x, y)) {
  11788. var pathData = this.path.data;
  11789. if (style.hasStroke()) {
  11790. var lineWidth = style.lineWidth;
  11791. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  11792. // Line scale can't be 0;
  11793. if (lineScale > 1e-10) {
  11794. // Only add extra hover lineWidth when there are no fill
  11795. if (!style.hasFill()) {
  11796. lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
  11797. }
  11798. if (containStroke(
  11799. pathData, lineWidth / lineScale, x, y
  11800. )) {
  11801. return true;
  11802. }
  11803. }
  11804. }
  11805. if (style.hasFill()) {
  11806. return contain(pathData, x, y);
  11807. }
  11808. }
  11809. return false;
  11810. },
  11811. /**
  11812. * @param {boolean} dirtyPath
  11813. */
  11814. dirty: function (dirtyPath) {
  11815. if (dirtyPath == null) {
  11816. dirtyPath = true;
  11817. }
  11818. // Only mark dirty, not mark clean
  11819. if (dirtyPath) {
  11820. this.__dirtyPath = dirtyPath;
  11821. this._rect = null;
  11822. }
  11823. this.__dirty = true;
  11824. this.__zr && this.__zr.refresh();
  11825. // Used as a clipping path
  11826. if (this.__clipTarget) {
  11827. this.__clipTarget.dirty();
  11828. }
  11829. },
  11830. /**
  11831. * Alias for animate('shape')
  11832. * @param {boolean} loop
  11833. */
  11834. animateShape: function (loop) {
  11835. return this.animate('shape', loop);
  11836. },
  11837. // Overwrite attrKV
  11838. attrKV: function (key, value) {
  11839. // FIXME
  11840. if (key === 'shape') {
  11841. this.setShape(value);
  11842. this.__dirtyPath = true;
  11843. this._rect = null;
  11844. }
  11845. else {
  11846. Displayable.prototype.attrKV.call(this, key, value);
  11847. }
  11848. },
  11849. /**
  11850. * @param {Object|string} key
  11851. * @param {*} value
  11852. */
  11853. setShape: function (key, value) {
  11854. var shape = this.shape;
  11855. // Path from string may not have shape
  11856. if (shape) {
  11857. if (isObject$1(key)) {
  11858. for (var name in key) {
  11859. if (key.hasOwnProperty(name)) {
  11860. shape[name] = key[name];
  11861. }
  11862. }
  11863. }
  11864. else {
  11865. shape[key] = value;
  11866. }
  11867. this.dirty(true);
  11868. }
  11869. return this;
  11870. },
  11871. getLineScale: function () {
  11872. var m = this.transform;
  11873. // Get the line scale.
  11874. // Determinant of `m` means how much the area is enlarged by the
  11875. // transformation. So its square root can be used as a scale factor
  11876. // for width.
  11877. return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10
  11878. ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))
  11879. : 1;
  11880. }
  11881. };
  11882. /**
  11883. * 扩展一个 Path element, 比如星形,圆等。
  11884. * Extend a path element
  11885. * @param {Object} props
  11886. * @param {string} props.type Path type
  11887. * @param {Function} props.init Initialize
  11888. * @param {Function} props.buildPath Overwrite buildPath method
  11889. * @param {Object} [props.style] Extended default style config
  11890. * @param {Object} [props.shape] Extended default shape config
  11891. */
  11892. Path.extend = function (defaults$$1) {
  11893. var Sub = function (opts) {
  11894. Path.call(this, opts);
  11895. if (defaults$$1.style) {
  11896. // Extend default style
  11897. this.style.extendFrom(defaults$$1.style, false);
  11898. }
  11899. // Extend default shape
  11900. var defaultShape = defaults$$1.shape;
  11901. if (defaultShape) {
  11902. this.shape = this.shape || {};
  11903. var thisShape = this.shape;
  11904. for (var name in defaultShape) {
  11905. if (
  11906. ! thisShape.hasOwnProperty(name)
  11907. && defaultShape.hasOwnProperty(name)
  11908. ) {
  11909. thisShape[name] = defaultShape[name];
  11910. }
  11911. }
  11912. }
  11913. defaults$$1.init && defaults$$1.init.call(this, opts);
  11914. };
  11915. inherits(Sub, Path);
  11916. // FIXME 不能 extend position, rotation 等引用对象
  11917. for (var name in defaults$$1) {
  11918. // Extending prototype values and methods
  11919. if (name !== 'style' && name !== 'shape') {
  11920. Sub.prototype[name] = defaults$$1[name];
  11921. }
  11922. }
  11923. return Sub;
  11924. };
  11925. inherits(Path, Displayable);
  11926. var CMD$2 = PathProxy.CMD;
  11927. var points = [[], [], []];
  11928. var mathSqrt$3 = Math.sqrt;
  11929. var mathAtan2 = Math.atan2;
  11930. var transformPath = function (path, m) {
  11931. var data = path.data;
  11932. var cmd;
  11933. var nPoint;
  11934. var i;
  11935. var j;
  11936. var k;
  11937. var p;
  11938. var M = CMD$2.M;
  11939. var C = CMD$2.C;
  11940. var L = CMD$2.L;
  11941. var R = CMD$2.R;
  11942. var A = CMD$2.A;
  11943. var Q = CMD$2.Q;
  11944. for (i = 0, j = 0; i < data.length;) {
  11945. cmd = data[i++];
  11946. j = i;
  11947. nPoint = 0;
  11948. switch (cmd) {
  11949. case M:
  11950. nPoint = 1;
  11951. break;
  11952. case L:
  11953. nPoint = 1;
  11954. break;
  11955. case C:
  11956. nPoint = 3;
  11957. break;
  11958. case Q:
  11959. nPoint = 2;
  11960. break;
  11961. case A:
  11962. var x = m[4];
  11963. var y = m[5];
  11964. var sx = mathSqrt$3(m[0] * m[0] + m[1] * m[1]);
  11965. var sy = mathSqrt$3(m[2] * m[2] + m[3] * m[3]);
  11966. var angle = mathAtan2(-m[1] / sy, m[0] / sx);
  11967. // cx
  11968. data[i] *= sx;
  11969. data[i++] += x;
  11970. // cy
  11971. data[i] *= sy;
  11972. data[i++] += y;
  11973. // Scale rx and ry
  11974. // FIXME Assume psi is 0 here
  11975. data[i++] *= sx;
  11976. data[i++] *= sy;
  11977. // Start angle
  11978. data[i++] += angle;
  11979. // end angle
  11980. data[i++] += angle;
  11981. // FIXME psi
  11982. i += 2;
  11983. j = i;
  11984. break;
  11985. case R:
  11986. // x0, y0
  11987. p[0] = data[i++];
  11988. p[1] = data[i++];
  11989. applyTransform(p, p, m);
  11990. data[j++] = p[0];
  11991. data[j++] = p[1];
  11992. // x1, y1
  11993. p[0] += data[i++];
  11994. p[1] += data[i++];
  11995. applyTransform(p, p, m);
  11996. data[j++] = p[0];
  11997. data[j++] = p[1];
  11998. }
  11999. for (k = 0; k < nPoint; k++) {
  12000. var p = points[k];
  12001. p[0] = data[i++];
  12002. p[1] = data[i++];
  12003. applyTransform(p, p, m);
  12004. // Write back
  12005. data[j++] = p[0];
  12006. data[j++] = p[1];
  12007. }
  12008. }
  12009. };
  12010. // command chars
  12011. var cc = [
  12012. 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z',
  12013. 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'
  12014. ];
  12015. var mathSqrt = Math.sqrt;
  12016. var mathSin = Math.sin;
  12017. var mathCos = Math.cos;
  12018. var PI = Math.PI;
  12019. var vMag = function(v) {
  12020. return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
  12021. };
  12022. var vRatio = function(u, v) {
  12023. return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
  12024. };
  12025. var vAngle = function(u, v) {
  12026. return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)
  12027. * Math.acos(vRatio(u, v));
  12028. };
  12029. function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {
  12030. var psi = psiDeg * (PI / 180.0);
  12031. var xp = mathCos(psi) * (x1 - x2) / 2.0
  12032. + mathSin(psi) * (y1 - y2) / 2.0;
  12033. var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0
  12034. + mathCos(psi) * (y1 - y2) / 2.0;
  12035. var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
  12036. if (lambda > 1) {
  12037. rx *= mathSqrt(lambda);
  12038. ry *= mathSqrt(lambda);
  12039. }
  12040. var f = (fa === fs ? -1 : 1)
  12041. * mathSqrt((((rx * rx) * (ry * ry))
  12042. - ((rx * rx) * (yp * yp))
  12043. - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)
  12044. + (ry * ry) * (xp * xp))
  12045. ) || 0;
  12046. var cxp = f * rx * yp / ry;
  12047. var cyp = f * -ry * xp / rx;
  12048. var cx = (x1 + x2) / 2.0
  12049. + mathCos(psi) * cxp
  12050. - mathSin(psi) * cyp;
  12051. var cy = (y1 + y2) / 2.0
  12052. + mathSin(psi) * cxp
  12053. + mathCos(psi) * cyp;
  12054. var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]);
  12055. var u = [ (xp - cxp) / rx, (yp - cyp) / ry ];
  12056. var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ];
  12057. var dTheta = vAngle(u, v);
  12058. if (vRatio(u, v) <= -1) {
  12059. dTheta = PI;
  12060. }
  12061. if (vRatio(u, v) >= 1) {
  12062. dTheta = 0;
  12063. }
  12064. if (fs === 0 && dTheta > 0) {
  12065. dTheta = dTheta - 2 * PI;
  12066. }
  12067. if (fs === 1 && dTheta < 0) {
  12068. dTheta = dTheta + 2 * PI;
  12069. }
  12070. path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);
  12071. }
  12072. function createPathProxyFromString(data) {
  12073. if (!data) {
  12074. return [];
  12075. }
  12076. // command string
  12077. var cs = data.replace(/-/g, ' -')
  12078. .replace(/ /g, ' ')
  12079. .replace(/ /g, ',')
  12080. .replace(/,,/g, ',');
  12081. var n;
  12082. // create pipes so that we can split the data
  12083. for (n = 0; n < cc.length; n++) {
  12084. cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
  12085. }
  12086. // create array
  12087. var arr = cs.split('|');
  12088. // init context point
  12089. var cpx = 0;
  12090. var cpy = 0;
  12091. var path = new PathProxy();
  12092. var CMD = PathProxy.CMD;
  12093. var prevCmd;
  12094. for (n = 1; n < arr.length; n++) {
  12095. var str = arr[n];
  12096. var c = str.charAt(0);
  12097. var off = 0;
  12098. var p = str.slice(1).replace(/e,-/g, 'e-').split(',');
  12099. var cmd;
  12100. if (p.length > 0 && p[0] === '') {
  12101. p.shift();
  12102. }
  12103. for (var i = 0; i < p.length; i++) {
  12104. p[i] = parseFloat(p[i]);
  12105. }
  12106. while (off < p.length && !isNaN(p[off])) {
  12107. if (isNaN(p[0])) {
  12108. break;
  12109. }
  12110. var ctlPtx;
  12111. var ctlPty;
  12112. var rx;
  12113. var ry;
  12114. var psi;
  12115. var fa;
  12116. var fs;
  12117. var x1 = cpx;
  12118. var y1 = cpy;
  12119. // convert l, H, h, V, and v to L
  12120. switch (c) {
  12121. case 'l':
  12122. cpx += p[off++];
  12123. cpy += p[off++];
  12124. cmd = CMD.L;
  12125. path.addData(cmd, cpx, cpy);
  12126. break;
  12127. case 'L':
  12128. cpx = p[off++];
  12129. cpy = p[off++];
  12130. cmd = CMD.L;
  12131. path.addData(cmd, cpx, cpy);
  12132. break;
  12133. case 'm':
  12134. cpx += p[off++];
  12135. cpy += p[off++];
  12136. cmd = CMD.M;
  12137. path.addData(cmd, cpx, cpy);
  12138. c = 'l';
  12139. break;
  12140. case 'M':
  12141. cpx = p[off++];
  12142. cpy = p[off++];
  12143. cmd = CMD.M;
  12144. path.addData(cmd, cpx, cpy);
  12145. c = 'L';
  12146. break;
  12147. case 'h':
  12148. cpx += p[off++];
  12149. cmd = CMD.L;
  12150. path.addData(cmd, cpx, cpy);
  12151. break;
  12152. case 'H':
  12153. cpx = p[off++];
  12154. cmd = CMD.L;
  12155. path.addData(cmd, cpx, cpy);
  12156. break;
  12157. case 'v':
  12158. cpy += p[off++];
  12159. cmd = CMD.L;
  12160. path.addData(cmd, cpx, cpy);
  12161. break;
  12162. case 'V':
  12163. cpy = p[off++];
  12164. cmd = CMD.L;
  12165. path.addData(cmd, cpx, cpy);
  12166. break;
  12167. case 'C':
  12168. cmd = CMD.C;
  12169. path.addData(
  12170. cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]
  12171. );
  12172. cpx = p[off - 2];
  12173. cpy = p[off - 1];
  12174. break;
  12175. case 'c':
  12176. cmd = CMD.C;
  12177. path.addData(
  12178. cmd,
  12179. p[off++] + cpx, p[off++] + cpy,
  12180. p[off++] + cpx, p[off++] + cpy,
  12181. p[off++] + cpx, p[off++] + cpy
  12182. );
  12183. cpx += p[off - 2];
  12184. cpy += p[off - 1];
  12185. break;
  12186. case 'S':
  12187. ctlPtx = cpx;
  12188. ctlPty = cpy;
  12189. var len = path.len();
  12190. var pathData = path.data;
  12191. if (prevCmd === CMD.C) {
  12192. ctlPtx += cpx - pathData[len - 4];
  12193. ctlPty += cpy - pathData[len - 3];
  12194. }
  12195. cmd = CMD.C;
  12196. x1 = p[off++];
  12197. y1 = p[off++];
  12198. cpx = p[off++];
  12199. cpy = p[off++];
  12200. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  12201. break;
  12202. case 's':
  12203. ctlPtx = cpx;
  12204. ctlPty = cpy;
  12205. var len = path.len();
  12206. var pathData = path.data;
  12207. if (prevCmd === CMD.C) {
  12208. ctlPtx += cpx - pathData[len - 4];
  12209. ctlPty += cpy - pathData[len - 3];
  12210. }
  12211. cmd = CMD.C;
  12212. x1 = cpx + p[off++];
  12213. y1 = cpy + p[off++];
  12214. cpx += p[off++];
  12215. cpy += p[off++];
  12216. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  12217. break;
  12218. case 'Q':
  12219. x1 = p[off++];
  12220. y1 = p[off++];
  12221. cpx = p[off++];
  12222. cpy = p[off++];
  12223. cmd = CMD.Q;
  12224. path.addData(cmd, x1, y1, cpx, cpy);
  12225. break;
  12226. case 'q':
  12227. x1 = p[off++] + cpx;
  12228. y1 = p[off++] + cpy;
  12229. cpx += p[off++];
  12230. cpy += p[off++];
  12231. cmd = CMD.Q;
  12232. path.addData(cmd, x1, y1, cpx, cpy);
  12233. break;
  12234. case 'T':
  12235. ctlPtx = cpx;
  12236. ctlPty = cpy;
  12237. var len = path.len();
  12238. var pathData = path.data;
  12239. if (prevCmd === CMD.Q) {
  12240. ctlPtx += cpx - pathData[len - 4];
  12241. ctlPty += cpy - pathData[len - 3];
  12242. }
  12243. cpx = p[off++];
  12244. cpy = p[off++];
  12245. cmd = CMD.Q;
  12246. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  12247. break;
  12248. case 't':
  12249. ctlPtx = cpx;
  12250. ctlPty = cpy;
  12251. var len = path.len();
  12252. var pathData = path.data;
  12253. if (prevCmd === CMD.Q) {
  12254. ctlPtx += cpx - pathData[len - 4];
  12255. ctlPty += cpy - pathData[len - 3];
  12256. }
  12257. cpx += p[off++];
  12258. cpy += p[off++];
  12259. cmd = CMD.Q;
  12260. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  12261. break;
  12262. case 'A':
  12263. rx = p[off++];
  12264. ry = p[off++];
  12265. psi = p[off++];
  12266. fa = p[off++];
  12267. fs = p[off++];
  12268. x1 = cpx, y1 = cpy;
  12269. cpx = p[off++];
  12270. cpy = p[off++];
  12271. cmd = CMD.A;
  12272. processArc(
  12273. x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
  12274. );
  12275. break;
  12276. case 'a':
  12277. rx = p[off++];
  12278. ry = p[off++];
  12279. psi = p[off++];
  12280. fa = p[off++];
  12281. fs = p[off++];
  12282. x1 = cpx, y1 = cpy;
  12283. cpx += p[off++];
  12284. cpy += p[off++];
  12285. cmd = CMD.A;
  12286. processArc(
  12287. x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
  12288. );
  12289. break;
  12290. }
  12291. }
  12292. if (c === 'z' || c === 'Z') {
  12293. cmd = CMD.Z;
  12294. path.addData(cmd);
  12295. }
  12296. prevCmd = cmd;
  12297. }
  12298. path.toStatic();
  12299. return path;
  12300. }
  12301. // TODO Optimize double memory cost problem
  12302. function createPathOptions(str, opts) {
  12303. var pathProxy = createPathProxyFromString(str);
  12304. opts = opts || {};
  12305. opts.buildPath = function (path) {
  12306. if (path.setData) {
  12307. path.setData(pathProxy.data);
  12308. // Svg and vml renderer don't have context
  12309. var ctx = path.getContext();
  12310. if (ctx) {
  12311. path.rebuildPath(ctx);
  12312. }
  12313. }
  12314. else {
  12315. var ctx = path;
  12316. pathProxy.rebuildPath(ctx);
  12317. }
  12318. };
  12319. opts.applyTransform = function (m) {
  12320. transformPath(pathProxy, m);
  12321. this.dirty(true);
  12322. };
  12323. return opts;
  12324. }
  12325. /**
  12326. * Create a Path object from path string data
  12327. * http://www.w3.org/TR/SVG/paths.html#PathData
  12328. * @param {Object} opts Other options
  12329. */
  12330. function createFromString(str, opts) {
  12331. return new Path(createPathOptions(str, opts));
  12332. }
  12333. /**
  12334. * Create a Path class from path string data
  12335. * @param {string} str
  12336. * @param {Object} opts Other options
  12337. */
  12338. function extendFromString(str, opts) {
  12339. return Path.extend(createPathOptions(str, opts));
  12340. }
  12341. /**
  12342. * Merge multiple paths
  12343. */
  12344. // TODO Apply transform
  12345. // TODO stroke dash
  12346. // TODO Optimize double memory cost problem
  12347. function mergePath$1(pathEls, opts) {
  12348. var pathList = [];
  12349. var len = pathEls.length;
  12350. for (var i = 0; i < len; i++) {
  12351. var pathEl = pathEls[i];
  12352. if (!pathEl.path) {
  12353. pathEl.createPathProxy();
  12354. }
  12355. if (pathEl.__dirtyPath) {
  12356. pathEl.buildPath(pathEl.path, pathEl.shape, true);
  12357. }
  12358. pathList.push(pathEl.path);
  12359. }
  12360. var pathBundle = new Path(opts);
  12361. // Need path proxy.
  12362. pathBundle.createPathProxy();
  12363. pathBundle.buildPath = function (path) {
  12364. path.appendPath(pathList);
  12365. // Svg and vml renderer don't have context
  12366. var ctx = path.getContext();
  12367. if (ctx) {
  12368. path.rebuildPath(ctx);
  12369. }
  12370. };
  12371. return pathBundle;
  12372. }
  12373. /**
  12374. * @alias zrender/graphic/Text
  12375. * @extends module:zrender/graphic/Displayable
  12376. * @constructor
  12377. * @param {Object} opts
  12378. */
  12379. var Text = function (opts) { // jshint ignore:line
  12380. Displayable.call(this, opts);
  12381. };
  12382. Text.prototype = {
  12383. constructor: Text,
  12384. type: 'text',
  12385. brush: function (ctx, prevEl) {
  12386. var style = this.style;
  12387. // Optimize, avoid normalize every time.
  12388. this.__dirty && normalizeTextStyle(style, true);
  12389. // Use props with prefix 'text'.
  12390. style.fill = style.stroke = style.shadowBlur = style.shadowColor =
  12391. style.shadowOffsetX = style.shadowOffsetY = null;
  12392. var text = style.text;
  12393. // Convert to string
  12394. text != null && (text += '');
  12395. // Always bind style
  12396. style.bind(ctx, this, prevEl);
  12397. if (!needDrawText(text, style)) {
  12398. return;
  12399. }
  12400. this.setTransform(ctx);
  12401. renderText(this, ctx, text, style);
  12402. this.restoreTransform(ctx);
  12403. },
  12404. getBoundingRect: function () {
  12405. var style = this.style;
  12406. // Optimize, avoid normalize every time.
  12407. this.__dirty && normalizeTextStyle(style, true);
  12408. if (!this._rect) {
  12409. var text = style.text;
  12410. text != null ? (text += '') : (text = '');
  12411. var rect = getBoundingRect(
  12412. style.text + '',
  12413. style.font,
  12414. style.textAlign,
  12415. style.textVerticalAlign,
  12416. style.textPadding,
  12417. style.rich
  12418. );
  12419. rect.x += style.x || 0;
  12420. rect.y += style.y || 0;
  12421. if (getStroke(style.textStroke, style.textStrokeWidth)) {
  12422. var w = style.textStrokeWidth;
  12423. rect.x -= w / 2;
  12424. rect.y -= w / 2;
  12425. rect.width += w;
  12426. rect.height += w;
  12427. }
  12428. this._rect = rect;
  12429. }
  12430. return this._rect;
  12431. }
  12432. };
  12433. inherits(Text, Displayable);
  12434. /**
  12435. * 圆形
  12436. * @module zrender/shape/Circle
  12437. */
  12438. var Circle = Path.extend({
  12439. type: 'circle',
  12440. shape: {
  12441. cx: 0,
  12442. cy: 0,
  12443. r: 0
  12444. },
  12445. buildPath : function (ctx, shape, inBundle) {
  12446. // Better stroking in ShapeBundle
  12447. // Always do it may have performence issue ( fill may be 2x more cost)
  12448. if (inBundle) {
  12449. ctx.moveTo(shape.cx + shape.r, shape.cy);
  12450. }
  12451. // else {
  12452. // if (ctx.allocate && !ctx.data.length) {
  12453. // ctx.allocate(ctx.CMD_MEM_SIZE.A);
  12454. // }
  12455. // }
  12456. // Better stroking in ShapeBundle
  12457. // ctx.moveTo(shape.cx + shape.r, shape.cy);
  12458. ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true);
  12459. }
  12460. });
  12461. // Fix weird bug in some version of IE11 (like 11.0.9600.178**),
  12462. // where exception "unexpected call to method or property access"
  12463. // might be thrown when calling ctx.fill or ctx.stroke after a path
  12464. // whose area size is zero is drawn and ctx.clip() is called and
  12465. // shadowBlur is set. See #4572, #3112, #5777.
  12466. // (e.g.,
  12467. // ctx.moveTo(10, 10);
  12468. // ctx.lineTo(20, 10);
  12469. // ctx.closePath();
  12470. // ctx.clip();
  12471. // ctx.shadowBlur = 10;
  12472. // ...
  12473. // ctx.fill();
  12474. // )
  12475. var shadowTemp = [
  12476. ['shadowBlur', 0],
  12477. ['shadowColor', '#000'],
  12478. ['shadowOffsetX', 0],
  12479. ['shadowOffsetY', 0]
  12480. ];
  12481. var fixClipWithShadow = function (orignalBrush) {
  12482. // version string can be: '11.0'
  12483. return (env$1.browser.ie && env$1.browser.version >= 11)
  12484. ? function () {
  12485. var clipPaths = this.__clipPaths;
  12486. var style = this.style;
  12487. var modified;
  12488. if (clipPaths) {
  12489. for (var i = 0; i < clipPaths.length; i++) {
  12490. var clipPath = clipPaths[i];
  12491. var shape = clipPath && clipPath.shape;
  12492. var type = clipPath && clipPath.type;
  12493. if (shape && (
  12494. (type === 'sector' && shape.startAngle === shape.endAngle)
  12495. || (type === 'rect' && (!shape.width || !shape.height))
  12496. )) {
  12497. for (var j = 0; j < shadowTemp.length; j++) {
  12498. // It is save to put shadowTemp static, because shadowTemp
  12499. // will be all modified each item brush called.
  12500. shadowTemp[j][2] = style[shadowTemp[j][0]];
  12501. style[shadowTemp[j][0]] = shadowTemp[j][1];
  12502. }
  12503. modified = true;
  12504. break;
  12505. }
  12506. }
  12507. }
  12508. orignalBrush.apply(this, arguments);
  12509. if (modified) {
  12510. for (var j = 0; j < shadowTemp.length; j++) {
  12511. style[shadowTemp[j][0]] = shadowTemp[j][2];
  12512. }
  12513. }
  12514. }
  12515. : orignalBrush;
  12516. };
  12517. /**
  12518. * 扇形
  12519. * @module zrender/graphic/shape/Sector
  12520. */
  12521. var Sector = Path.extend({
  12522. type: 'sector',
  12523. shape: {
  12524. cx: 0,
  12525. cy: 0,
  12526. r0: 0,
  12527. r: 0,
  12528. startAngle: 0,
  12529. endAngle: Math.PI * 2,
  12530. clockwise: true
  12531. },
  12532. brush: fixClipWithShadow(Path.prototype.brush),
  12533. buildPath: function (ctx, shape) {
  12534. var x = shape.cx;
  12535. var y = shape.cy;
  12536. var r0 = Math.max(shape.r0 || 0, 0);
  12537. var r = Math.max(shape.r, 0);
  12538. var startAngle = shape.startAngle;
  12539. var endAngle = shape.endAngle;
  12540. var clockwise = shape.clockwise;
  12541. var unitX = Math.cos(startAngle);
  12542. var unitY = Math.sin(startAngle);
  12543. ctx.moveTo(unitX * r0 + x, unitY * r0 + y);
  12544. ctx.lineTo(unitX * r + x, unitY * r + y);
  12545. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  12546. ctx.lineTo(
  12547. Math.cos(endAngle) * r0 + x,
  12548. Math.sin(endAngle) * r0 + y
  12549. );
  12550. if (r0 !== 0) {
  12551. ctx.arc(x, y, r0, endAngle, startAngle, clockwise);
  12552. }
  12553. ctx.closePath();
  12554. }
  12555. });
  12556. /**
  12557. * 圆环
  12558. * @module zrender/graphic/shape/Ring
  12559. */
  12560. var Ring = Path.extend({
  12561. type: 'ring',
  12562. shape: {
  12563. cx: 0,
  12564. cy: 0,
  12565. r: 0,
  12566. r0: 0
  12567. },
  12568. buildPath: function (ctx, shape) {
  12569. var x = shape.cx;
  12570. var y = shape.cy;
  12571. var PI2 = Math.PI * 2;
  12572. ctx.moveTo(x + shape.r, y);
  12573. ctx.arc(x, y, shape.r, 0, PI2, false);
  12574. ctx.moveTo(x + shape.r0, y);
  12575. ctx.arc(x, y, shape.r0, 0, PI2, true);
  12576. }
  12577. });
  12578. /**
  12579. * Catmull-Rom spline 插值折线
  12580. * @module zrender/shape/util/smoothSpline
  12581. * @author pissang (https://www.github.com/pissang)
  12582. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  12583. * errorrik (errorrik@gmail.com)
  12584. */
  12585. /**
  12586. * @inner
  12587. */
  12588. function interpolate(p0, p1, p2, p3, t, t2, t3) {
  12589. var v0 = (p2 - p0) * 0.5;
  12590. var v1 = (p3 - p1) * 0.5;
  12591. return (2 * (p1 - p2) + v0 + v1) * t3
  12592. + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
  12593. + v0 * t + p1;
  12594. }
  12595. /**
  12596. * @alias module:zrender/shape/util/smoothSpline
  12597. * @param {Array} points 线段顶点数组
  12598. * @param {boolean} isLoop
  12599. * @return {Array}
  12600. */
  12601. var smoothSpline = function (points, isLoop) {
  12602. var len$$1 = points.length;
  12603. var ret = [];
  12604. var distance$$1 = 0;
  12605. for (var i = 1; i < len$$1; i++) {
  12606. distance$$1 += distance(points[i - 1], points[i]);
  12607. }
  12608. var segs = distance$$1 / 2;
  12609. segs = segs < len$$1 ? len$$1 : segs;
  12610. for (var i = 0; i < segs; i++) {
  12611. var pos = i / (segs - 1) * (isLoop ? len$$1 : len$$1 - 1);
  12612. var idx = Math.floor(pos);
  12613. var w = pos - idx;
  12614. var p0;
  12615. var p1 = points[idx % len$$1];
  12616. var p2;
  12617. var p3;
  12618. if (!isLoop) {
  12619. p0 = points[idx === 0 ? idx : idx - 1];
  12620. p2 = points[idx > len$$1 - 2 ? len$$1 - 1 : idx + 1];
  12621. p3 = points[idx > len$$1 - 3 ? len$$1 - 1 : idx + 2];
  12622. }
  12623. else {
  12624. p0 = points[(idx - 1 + len$$1) % len$$1];
  12625. p2 = points[(idx + 1) % len$$1];
  12626. p3 = points[(idx + 2) % len$$1];
  12627. }
  12628. var w2 = w * w;
  12629. var w3 = w * w2;
  12630. ret.push([
  12631. interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3),
  12632. interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)
  12633. ]);
  12634. }
  12635. return ret;
  12636. };
  12637. /**
  12638. * 贝塞尔平滑曲线
  12639. * @module zrender/shape/util/smoothBezier
  12640. * @author pissang (https://www.github.com/pissang)
  12641. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  12642. * errorrik (errorrik@gmail.com)
  12643. */
  12644. /**
  12645. * 贝塞尔平滑曲线
  12646. * @alias module:zrender/shape/util/smoothBezier
  12647. * @param {Array} points 线段顶点数组
  12648. * @param {number} smooth 平滑等级, 0-1
  12649. * @param {boolean} isLoop
  12650. * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内
  12651. * 比如 [[0, 0], [100, 100]], 这个包围盒会与
  12652. * 整个折线的包围盒做一个并集用来约束控制点。
  12653. * @param {Array} 计算出来的控制点数组
  12654. */
  12655. var smoothBezier = function (points, smooth, isLoop, constraint) {
  12656. var cps = [];
  12657. var v = [];
  12658. var v1 = [];
  12659. var v2 = [];
  12660. var prevPoint;
  12661. var nextPoint;
  12662. var min$$1, max$$1;
  12663. if (constraint) {
  12664. min$$1 = [Infinity, Infinity];
  12665. max$$1 = [-Infinity, -Infinity];
  12666. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  12667. min(min$$1, min$$1, points[i]);
  12668. max(max$$1, max$$1, points[i]);
  12669. }
  12670. // 与指定的包围盒做并集
  12671. min(min$$1, min$$1, constraint[0]);
  12672. max(max$$1, max$$1, constraint[1]);
  12673. }
  12674. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  12675. var point = points[i];
  12676. if (isLoop) {
  12677. prevPoint = points[i ? i - 1 : len$$1 - 1];
  12678. nextPoint = points[(i + 1) % len$$1];
  12679. }
  12680. else {
  12681. if (i === 0 || i === len$$1 - 1) {
  12682. cps.push(clone$1(points[i]));
  12683. continue;
  12684. }
  12685. else {
  12686. prevPoint = points[i - 1];
  12687. nextPoint = points[i + 1];
  12688. }
  12689. }
  12690. sub(v, nextPoint, prevPoint);
  12691. // use degree to scale the handle length
  12692. scale(v, v, smooth);
  12693. var d0 = distance(point, prevPoint);
  12694. var d1 = distance(point, nextPoint);
  12695. var sum = d0 + d1;
  12696. if (sum !== 0) {
  12697. d0 /= sum;
  12698. d1 /= sum;
  12699. }
  12700. scale(v1, v, -d0);
  12701. scale(v2, v, d1);
  12702. var cp0 = add([], point, v1);
  12703. var cp1 = add([], point, v2);
  12704. if (constraint) {
  12705. max(cp0, cp0, min$$1);
  12706. min(cp0, cp0, max$$1);
  12707. max(cp1, cp1, min$$1);
  12708. min(cp1, cp1, max$$1);
  12709. }
  12710. cps.push(cp0);
  12711. cps.push(cp1);
  12712. }
  12713. if (isLoop) {
  12714. cps.push(cps.shift());
  12715. }
  12716. return cps;
  12717. };
  12718. function buildPath$1(ctx, shape, closePath) {
  12719. var points = shape.points;
  12720. var smooth = shape.smooth;
  12721. if (points && points.length >= 2) {
  12722. if (smooth && smooth !== 'spline') {
  12723. var controlPoints = smoothBezier(
  12724. points, smooth, closePath, shape.smoothConstraint
  12725. );
  12726. ctx.moveTo(points[0][0], points[0][1]);
  12727. var len = points.length;
  12728. for (var i = 0; i < (closePath ? len : len - 1); i++) {
  12729. var cp1 = controlPoints[i * 2];
  12730. var cp2 = controlPoints[i * 2 + 1];
  12731. var p = points[(i + 1) % len];
  12732. ctx.bezierCurveTo(
  12733. cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]
  12734. );
  12735. }
  12736. }
  12737. else {
  12738. if (smooth === 'spline') {
  12739. points = smoothSpline(points, closePath);
  12740. }
  12741. ctx.moveTo(points[0][0], points[0][1]);
  12742. for (var i = 1, l = points.length; i < l; i++) {
  12743. ctx.lineTo(points[i][0], points[i][1]);
  12744. }
  12745. }
  12746. closePath && ctx.closePath();
  12747. }
  12748. }
  12749. /**
  12750. * 多边形
  12751. * @module zrender/shape/Polygon
  12752. */
  12753. var Polygon = Path.extend({
  12754. type: 'polygon',
  12755. shape: {
  12756. points: null,
  12757. smooth: false,
  12758. smoothConstraint: null
  12759. },
  12760. buildPath: function (ctx, shape) {
  12761. buildPath$1(ctx, shape, true);
  12762. }
  12763. });
  12764. /**
  12765. * @module zrender/graphic/shape/Polyline
  12766. */
  12767. var Polyline = Path.extend({
  12768. type: 'polyline',
  12769. shape: {
  12770. points: null,
  12771. smooth: false,
  12772. smoothConstraint: null
  12773. },
  12774. style: {
  12775. stroke: '#000',
  12776. fill: null
  12777. },
  12778. buildPath: function (ctx, shape) {
  12779. buildPath$1(ctx, shape, false);
  12780. }
  12781. });
  12782. /**
  12783. * 矩形
  12784. * @module zrender/graphic/shape/Rect
  12785. */
  12786. var Rect = Path.extend({
  12787. type: 'rect',
  12788. shape: {
  12789. // 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4
  12790. // r缩写为1 相当于 [1, 1, 1, 1]
  12791. // r缩写为[1] 相当于 [1, 1, 1, 1]
  12792. // r缩写为[1, 2] 相当于 [1, 2, 1, 2]
  12793. // r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]
  12794. r: 0,
  12795. x: 0,
  12796. y: 0,
  12797. width: 0,
  12798. height: 0
  12799. },
  12800. buildPath: function (ctx, shape) {
  12801. var x = shape.x;
  12802. var y = shape.y;
  12803. var width = shape.width;
  12804. var height = shape.height;
  12805. if (!shape.r) {
  12806. ctx.rect(x, y, width, height);
  12807. }
  12808. else {
  12809. buildPath(ctx, shape);
  12810. }
  12811. ctx.closePath();
  12812. return;
  12813. }
  12814. });
  12815. /**
  12816. * 直线
  12817. * @module zrender/graphic/shape/Line
  12818. */
  12819. var Line = Path.extend({
  12820. type: 'line',
  12821. shape: {
  12822. // Start point
  12823. x1: 0,
  12824. y1: 0,
  12825. // End point
  12826. x2: 0,
  12827. y2: 0,
  12828. percent: 1
  12829. },
  12830. style: {
  12831. stroke: '#000',
  12832. fill: null
  12833. },
  12834. buildPath: function (ctx, shape) {
  12835. var x1 = shape.x1;
  12836. var y1 = shape.y1;
  12837. var x2 = shape.x2;
  12838. var y2 = shape.y2;
  12839. var percent = shape.percent;
  12840. if (percent === 0) {
  12841. return;
  12842. }
  12843. ctx.moveTo(x1, y1);
  12844. if (percent < 1) {
  12845. x2 = x1 * (1 - percent) + x2 * percent;
  12846. y2 = y1 * (1 - percent) + y2 * percent;
  12847. }
  12848. ctx.lineTo(x2, y2);
  12849. },
  12850. /**
  12851. * Get point at percent
  12852. * @param {number} percent
  12853. * @return {Array.<number>}
  12854. */
  12855. pointAt: function (p) {
  12856. var shape = this.shape;
  12857. return [
  12858. shape.x1 * (1 - p) + shape.x2 * p,
  12859. shape.y1 * (1 - p) + shape.y2 * p
  12860. ];
  12861. }
  12862. });
  12863. /**
  12864. * 贝塞尔曲线
  12865. * @module zrender/shape/BezierCurve
  12866. */
  12867. var out = [];
  12868. function someVectorAt(shape, t, isTangent) {
  12869. var cpx2 = shape.cpx2;
  12870. var cpy2 = shape.cpy2;
  12871. if (cpx2 === null || cpy2 === null) {
  12872. return [
  12873. (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),
  12874. (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)
  12875. ];
  12876. }
  12877. else {
  12878. return [
  12879. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),
  12880. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)
  12881. ];
  12882. }
  12883. }
  12884. var BezierCurve = Path.extend({
  12885. type: 'bezier-curve',
  12886. shape: {
  12887. x1: 0,
  12888. y1: 0,
  12889. x2: 0,
  12890. y2: 0,
  12891. cpx1: 0,
  12892. cpy1: 0,
  12893. // cpx2: 0,
  12894. // cpy2: 0
  12895. // Curve show percent, for animating
  12896. percent: 1
  12897. },
  12898. style: {
  12899. stroke: '#000',
  12900. fill: null
  12901. },
  12902. buildPath: function (ctx, shape) {
  12903. var x1 = shape.x1;
  12904. var y1 = shape.y1;
  12905. var x2 = shape.x2;
  12906. var y2 = shape.y2;
  12907. var cpx1 = shape.cpx1;
  12908. var cpy1 = shape.cpy1;
  12909. var cpx2 = shape.cpx2;
  12910. var cpy2 = shape.cpy2;
  12911. var percent = shape.percent;
  12912. if (percent === 0) {
  12913. return;
  12914. }
  12915. ctx.moveTo(x1, y1);
  12916. if (cpx2 == null || cpy2 == null) {
  12917. if (percent < 1) {
  12918. quadraticSubdivide(
  12919. x1, cpx1, x2, percent, out
  12920. );
  12921. cpx1 = out[1];
  12922. x2 = out[2];
  12923. quadraticSubdivide(
  12924. y1, cpy1, y2, percent, out
  12925. );
  12926. cpy1 = out[1];
  12927. y2 = out[2];
  12928. }
  12929. ctx.quadraticCurveTo(
  12930. cpx1, cpy1,
  12931. x2, y2
  12932. );
  12933. }
  12934. else {
  12935. if (percent < 1) {
  12936. cubicSubdivide(
  12937. x1, cpx1, cpx2, x2, percent, out
  12938. );
  12939. cpx1 = out[1];
  12940. cpx2 = out[2];
  12941. x2 = out[3];
  12942. cubicSubdivide(
  12943. y1, cpy1, cpy2, y2, percent, out
  12944. );
  12945. cpy1 = out[1];
  12946. cpy2 = out[2];
  12947. y2 = out[3];
  12948. }
  12949. ctx.bezierCurveTo(
  12950. cpx1, cpy1,
  12951. cpx2, cpy2,
  12952. x2, y2
  12953. );
  12954. }
  12955. },
  12956. /**
  12957. * Get point at percent
  12958. * @param {number} t
  12959. * @return {Array.<number>}
  12960. */
  12961. pointAt: function (t) {
  12962. return someVectorAt(this.shape, t, false);
  12963. },
  12964. /**
  12965. * Get tangent at percent
  12966. * @param {number} t
  12967. * @return {Array.<number>}
  12968. */
  12969. tangentAt: function (t) {
  12970. var p = someVectorAt(this.shape, t, true);
  12971. return normalize(p, p);
  12972. }
  12973. });
  12974. /**
  12975. * 圆弧
  12976. * @module zrender/graphic/shape/Arc
  12977. */
  12978. var Arc = Path.extend({
  12979. type: 'arc',
  12980. shape: {
  12981. cx: 0,
  12982. cy: 0,
  12983. r: 0,
  12984. startAngle: 0,
  12985. endAngle: Math.PI * 2,
  12986. clockwise: true
  12987. },
  12988. style: {
  12989. stroke: '#000',
  12990. fill: null
  12991. },
  12992. buildPath: function (ctx, shape) {
  12993. var x = shape.cx;
  12994. var y = shape.cy;
  12995. var r = Math.max(shape.r, 0);
  12996. var startAngle = shape.startAngle;
  12997. var endAngle = shape.endAngle;
  12998. var clockwise = shape.clockwise;
  12999. var unitX = Math.cos(startAngle);
  13000. var unitY = Math.sin(startAngle);
  13001. ctx.moveTo(unitX * r + x, unitY * r + y);
  13002. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  13003. }
  13004. });
  13005. // CompoundPath to improve performance
  13006. var CompoundPath = Path.extend({
  13007. type: 'compound',
  13008. shape: {
  13009. paths: null
  13010. },
  13011. _updatePathDirty: function () {
  13012. var dirtyPath = this.__dirtyPath;
  13013. var paths = this.shape.paths;
  13014. for (var i = 0; i < paths.length; i++) {
  13015. // Mark as dirty if any subpath is dirty
  13016. dirtyPath = dirtyPath || paths[i].__dirtyPath;
  13017. }
  13018. this.__dirtyPath = dirtyPath;
  13019. this.__dirty = this.__dirty || dirtyPath;
  13020. },
  13021. beforeBrush: function () {
  13022. this._updatePathDirty();
  13023. var paths = this.shape.paths || [];
  13024. var scale = this.getGlobalScale();
  13025. // Update path scale
  13026. for (var i = 0; i < paths.length; i++) {
  13027. if (!paths[i].path) {
  13028. paths[i].createPathProxy();
  13029. }
  13030. paths[i].path.setScale(scale[0], scale[1]);
  13031. }
  13032. },
  13033. buildPath: function (ctx, shape) {
  13034. var paths = shape.paths || [];
  13035. for (var i = 0; i < paths.length; i++) {
  13036. paths[i].buildPath(ctx, paths[i].shape, true);
  13037. }
  13038. },
  13039. afterBrush: function () {
  13040. var paths = this.shape.paths || [];
  13041. for (var i = 0; i < paths.length; i++) {
  13042. paths[i].__dirtyPath = false;
  13043. }
  13044. },
  13045. getBoundingRect: function () {
  13046. this._updatePathDirty();
  13047. return Path.prototype.getBoundingRect.call(this);
  13048. }
  13049. });
  13050. /**
  13051. * @param {Array.<Object>} colorStops
  13052. */
  13053. var Gradient = function (colorStops) {
  13054. this.colorStops = colorStops || [];
  13055. };
  13056. Gradient.prototype = {
  13057. constructor: Gradient,
  13058. addColorStop: function (offset, color) {
  13059. this.colorStops.push({
  13060. offset: offset,
  13061. color: color
  13062. });
  13063. }
  13064. };
  13065. /**
  13066. * x, y, x2, y2 are all percent from 0 to 1
  13067. * @param {number} [x=0]
  13068. * @param {number} [y=0]
  13069. * @param {number} [x2=1]
  13070. * @param {number} [y2=0]
  13071. * @param {Array.<Object>} colorStops
  13072. * @param {boolean} [globalCoord=false]
  13073. */
  13074. var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) {
  13075. // Should do nothing more in this constructor. Because gradient can be
  13076. // declard by `color: {type: 'linear', colorStops: ...}`, where
  13077. // this constructor will not be called.
  13078. this.x = x == null ? 0 : x;
  13079. this.y = y == null ? 0 : y;
  13080. this.x2 = x2 == null ? 1 : x2;
  13081. this.y2 = y2 == null ? 0 : y2;
  13082. // Can be cloned
  13083. this.type = 'linear';
  13084. // If use global coord
  13085. this.global = globalCoord || false;
  13086. Gradient.call(this, colorStops);
  13087. };
  13088. LinearGradient.prototype = {
  13089. constructor: LinearGradient
  13090. };
  13091. inherits(LinearGradient, Gradient);
  13092. /**
  13093. * x, y, r are all percent from 0 to 1
  13094. * @param {number} [x=0.5]
  13095. * @param {number} [y=0.5]
  13096. * @param {number} [r=0.5]
  13097. * @param {Array.<Object>} [colorStops]
  13098. * @param {boolean} [globalCoord=false]
  13099. */
  13100. var RadialGradient = function (x, y, r, colorStops, globalCoord) {
  13101. // Should do nothing more in this constructor. Because gradient can be
  13102. // declard by `color: {type: 'radial', colorStops: ...}`, where
  13103. // this constructor will not be called.
  13104. this.x = x == null ? 0.5 : x;
  13105. this.y = y == null ? 0.5 : y;
  13106. this.r = r == null ? 0.5 : r;
  13107. // Can be cloned
  13108. this.type = 'radial';
  13109. // If use global coord
  13110. this.global = globalCoord || false;
  13111. Gradient.call(this, colorStops);
  13112. };
  13113. RadialGradient.prototype = {
  13114. constructor: RadialGradient
  13115. };
  13116. inherits(RadialGradient, Gradient);
  13117. /**
  13118. * Displayable for incremental rendering. It will be rendered in a separate layer
  13119. * IncrementalDisplay have too main methods. `clearDisplayables` and `addDisplayables`
  13120. * addDisplayables will render the added displayables incremetally.
  13121. *
  13122. * It use a not clearFlag to tell the painter don't clear the layer if it's the first element.
  13123. */
  13124. // TODO Style override ?
  13125. function IncrementalDisplayble(opts) {
  13126. Displayable.call(this, opts);
  13127. this._displayables = [];
  13128. this._temporaryDisplayables = [];
  13129. this._cursor = 0;
  13130. this.notClear = true;
  13131. }
  13132. IncrementalDisplayble.prototype.incremental = true;
  13133. IncrementalDisplayble.prototype.clearDisplaybles = function () {
  13134. this._displayables = [];
  13135. this._temporaryDisplayables = [];
  13136. this._cursor = 0;
  13137. this.dirty();
  13138. this.notClear = false;
  13139. };
  13140. IncrementalDisplayble.prototype.addDisplayable = function (displayable, notPersistent) {
  13141. if (notPersistent) {
  13142. this._temporaryDisplayables.push(displayable);
  13143. }
  13144. else {
  13145. this._displayables.push(displayable);
  13146. }
  13147. this.dirty();
  13148. };
  13149. IncrementalDisplayble.prototype.addDisplayables = function (displayables, notPersistent) {
  13150. notPersistent = notPersistent || false;
  13151. for (var i = 0; i < displayables.length; i++) {
  13152. this.addDisplayable(displayables[i], notPersistent);
  13153. }
  13154. };
  13155. IncrementalDisplayble.prototype.eachPendingDisplayable = function (cb) {
  13156. for (var i = this._cursor; i < this._displayables.length; i++) {
  13157. cb && cb(this._displayables[i]);
  13158. }
  13159. for (var i = 0; i < this._temporaryDisplayables.length; i++) {
  13160. cb && cb(this._temporaryDisplayables[i]);
  13161. }
  13162. };
  13163. IncrementalDisplayble.prototype.update = function () {
  13164. this.updateTransform();
  13165. for (var i = this._cursor; i < this._displayables.length; i++) {
  13166. var displayable = this._displayables[i];
  13167. // PENDING
  13168. displayable.parent = this;
  13169. displayable.update();
  13170. displayable.parent = null;
  13171. }
  13172. for (var i = 0; i < this._temporaryDisplayables.length; i++) {
  13173. var displayable = this._temporaryDisplayables[i];
  13174. // PENDING
  13175. displayable.parent = this;
  13176. displayable.update();
  13177. displayable.parent = null;
  13178. }
  13179. };
  13180. IncrementalDisplayble.prototype.brush = function (ctx, prevEl) {
  13181. // Render persistant displayables.
  13182. for (var i = this._cursor; i < this._displayables.length; i++) {
  13183. var displayable = this._temporaryDisplayables[i];
  13184. displayable.beforeBrush && displayable.beforeBrush(ctx);
  13185. displayable.brush(ctx, i === this._cursor ? null : this._displayables[i - 1]);
  13186. displayable.afterBrush && displayable.afterBrush(ctx);
  13187. }
  13188. this._cursor = i;
  13189. // Render temporary displayables.
  13190. for (var i = 0; i < this._temporaryDisplayables.length; i++) {
  13191. var displayable = this._temporaryDisplayables[i];
  13192. displayable.beforeBrush && displayable.beforeBrush(ctx);
  13193. displayable.brush(ctx, i === 0 ? null : this._temporaryDisplayables[i - 1]);
  13194. displayable.afterBrush && displayable.afterBrush(ctx);
  13195. }
  13196. this._temporaryDisplayables = [];
  13197. this.notClear = true;
  13198. };
  13199. var m = [];
  13200. IncrementalDisplayble.prototype.getBoundingRect = function () {
  13201. if (!this._rect) {
  13202. var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);
  13203. for (var i = 0; i < this._displayables.length; i++) {
  13204. var displayable = this._displayables[i];
  13205. var childRect = displayable.getBoundingRect().clone();
  13206. if (displayable.needLocalTransform()) {
  13207. childRect.applyTransform(displayable.getLocalTransform(m));
  13208. }
  13209. rect.union(childRect);
  13210. }
  13211. this._rect = rect;
  13212. }
  13213. return this._rect;
  13214. };
  13215. IncrementalDisplayble.prototype.contain = function (x, y) {
  13216. var localPos = this.transformCoordToLocal(x, y);
  13217. var rect = this.getBoundingRect();
  13218. if (rect.contain(localPos[0], localPos[1])) {
  13219. for (var i = 0; i < this._displayables.length; i++) {
  13220. var displayable = this._displayables[i];
  13221. if (displayable.contain(x, y)) {
  13222. return true;
  13223. }
  13224. }
  13225. }
  13226. return false;
  13227. };
  13228. inherits(IncrementalDisplayble, Displayable);
  13229. var round = Math.round;
  13230. var mathMax$1 = Math.max;
  13231. var mathMin$1 = Math.min;
  13232. var EMPTY_OBJ = {};
  13233. /**
  13234. * Extend shape with parameters
  13235. */
  13236. function extendShape(opts) {
  13237. return Path.extend(opts);
  13238. }
  13239. /**
  13240. * Extend path
  13241. */
  13242. function extendPath(pathData, opts) {
  13243. return extendFromString(pathData, opts);
  13244. }
  13245. /**
  13246. * Create a path element from path data string
  13247. * @param {string} pathData
  13248. * @param {Object} opts
  13249. * @param {module:zrender/core/BoundingRect} rect
  13250. * @param {string} [layout=cover] 'center' or 'cover'
  13251. */
  13252. function makePath(pathData, opts, rect, layout) {
  13253. var path = createFromString(pathData, opts);
  13254. var boundingRect = path.getBoundingRect();
  13255. if (rect) {
  13256. if (layout === 'center') {
  13257. rect = centerGraphic(rect, boundingRect);
  13258. }
  13259. resizePath(path, rect);
  13260. }
  13261. return path;
  13262. }
  13263. /**
  13264. * Create a image element from image url
  13265. * @param {string} imageUrl image url
  13266. * @param {Object} opts options
  13267. * @param {module:zrender/core/BoundingRect} rect constrain rect
  13268. * @param {string} [layout=cover] 'center' or 'cover'
  13269. */
  13270. function makeImage(imageUrl, rect, layout) {
  13271. var path = new ZImage({
  13272. style: {
  13273. image: imageUrl,
  13274. x: rect.x,
  13275. y: rect.y,
  13276. width: rect.width,
  13277. height: rect.height
  13278. },
  13279. onload: function (img) {
  13280. if (layout === 'center') {
  13281. var boundingRect = {
  13282. width: img.width,
  13283. height: img.height
  13284. };
  13285. path.setStyle(centerGraphic(rect, boundingRect));
  13286. }
  13287. }
  13288. });
  13289. return path;
  13290. }
  13291. /**
  13292. * Get position of centered element in bounding box.
  13293. *
  13294. * @param {Object} rect element local bounding box
  13295. * @param {Object} boundingRect constraint bounding box
  13296. * @return {Object} element position containing x, y, width, and height
  13297. */
  13298. function centerGraphic(rect, boundingRect) {
  13299. // Set rect to center, keep width / height ratio.
  13300. var aspect = boundingRect.width / boundingRect.height;
  13301. var width = rect.height * aspect;
  13302. var height;
  13303. if (width <= rect.width) {
  13304. height = rect.height;
  13305. }
  13306. else {
  13307. width = rect.width;
  13308. height = width / aspect;
  13309. }
  13310. var cx = rect.x + rect.width / 2;
  13311. var cy = rect.y + rect.height / 2;
  13312. return {
  13313. x: cx - width / 2,
  13314. y: cy - height / 2,
  13315. width: width,
  13316. height: height
  13317. };
  13318. }
  13319. var mergePath = mergePath$1;
  13320. /**
  13321. * Resize a path to fit the rect
  13322. * @param {module:zrender/graphic/Path} path
  13323. * @param {Object} rect
  13324. */
  13325. function resizePath(path, rect) {
  13326. if (!path.applyTransform) {
  13327. return;
  13328. }
  13329. var pathRect = path.getBoundingRect();
  13330. var m = pathRect.calculateTransform(rect);
  13331. path.applyTransform(m);
  13332. }
  13333. /**
  13334. * Sub pixel optimize line for canvas
  13335. *
  13336. * @param {Object} param
  13337. * @param {Object} [param.shape]
  13338. * @param {number} [param.shape.x1]
  13339. * @param {number} [param.shape.y1]
  13340. * @param {number} [param.shape.x2]
  13341. * @param {number} [param.shape.y2]
  13342. * @param {Object} [param.style]
  13343. * @param {number} [param.style.lineWidth]
  13344. * @return {Object} Modified param
  13345. */
  13346. function subPixelOptimizeLine(param) {
  13347. var shape = param.shape;
  13348. var lineWidth = param.style.lineWidth;
  13349. if (round(shape.x1 * 2) === round(shape.x2 * 2)) {
  13350. shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true);
  13351. }
  13352. if (round(shape.y1 * 2) === round(shape.y2 * 2)) {
  13353. shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true);
  13354. }
  13355. return param;
  13356. }
  13357. /**
  13358. * Sub pixel optimize rect for canvas
  13359. *
  13360. * @param {Object} param
  13361. * @param {Object} [param.shape]
  13362. * @param {number} [param.shape.x]
  13363. * @param {number} [param.shape.y]
  13364. * @param {number} [param.shape.width]
  13365. * @param {number} [param.shape.height]
  13366. * @param {Object} [param.style]
  13367. * @param {number} [param.style.lineWidth]
  13368. * @return {Object} Modified param
  13369. */
  13370. function subPixelOptimizeRect(param) {
  13371. var shape = param.shape;
  13372. var lineWidth = param.style.lineWidth;
  13373. var originX = shape.x;
  13374. var originY = shape.y;
  13375. var originWidth = shape.width;
  13376. var originHeight = shape.height;
  13377. shape.x = subPixelOptimize(shape.x, lineWidth, true);
  13378. shape.y = subPixelOptimize(shape.y, lineWidth, true);
  13379. shape.width = Math.max(
  13380. subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x,
  13381. originWidth === 0 ? 0 : 1
  13382. );
  13383. shape.height = Math.max(
  13384. subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y,
  13385. originHeight === 0 ? 0 : 1
  13386. );
  13387. return param;
  13388. }
  13389. /**
  13390. * Sub pixel optimize for canvas
  13391. *
  13392. * @param {number} position Coordinate, such as x, y
  13393. * @param {number} lineWidth Should be nonnegative integer.
  13394. * @param {boolean=} positiveOrNegative Default false (negative).
  13395. * @return {number} Optimized position.
  13396. */
  13397. function subPixelOptimize(position, lineWidth, positiveOrNegative) {
  13398. // Assure that (position + lineWidth / 2) is near integer edge,
  13399. // otherwise line will be fuzzy in canvas.
  13400. var doubledPosition = round(position * 2);
  13401. return (doubledPosition + round(lineWidth)) % 2 === 0
  13402. ? doubledPosition / 2
  13403. : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
  13404. }
  13405. function hasFillOrStroke(fillOrStroke) {
  13406. return fillOrStroke != null && fillOrStroke != 'none';
  13407. }
  13408. function liftColor(color) {
  13409. return typeof color === 'string' ? lift(color, -0.1) : color;
  13410. }
  13411. /**
  13412. * @private
  13413. */
  13414. function cacheElementStl(el) {
  13415. if (el.__hoverStlDirty) {
  13416. var stroke = el.style.stroke;
  13417. var fill = el.style.fill;
  13418. // Create hoverStyle on mouseover
  13419. var hoverStyle = el.__hoverStl;
  13420. hoverStyle.fill = hoverStyle.fill
  13421. || (hasFillOrStroke(fill) ? liftColor(fill) : null);
  13422. hoverStyle.stroke = hoverStyle.stroke
  13423. || (hasFillOrStroke(stroke) ? liftColor(stroke) : null);
  13424. var normalStyle = {};
  13425. for (var name in hoverStyle) {
  13426. // See comment in `doSingleEnterHover`.
  13427. if (hoverStyle[name] != null) {
  13428. normalStyle[name] = el.style[name];
  13429. }
  13430. }
  13431. el.__normalStl = normalStyle;
  13432. el.__hoverStlDirty = false;
  13433. }
  13434. }
  13435. /**
  13436. * @private
  13437. */
  13438. function doSingleEnterHover(el) {
  13439. if (el.__isHover) {
  13440. return;
  13441. }
  13442. cacheElementStl(el);
  13443. if (el.useHoverLayer) {
  13444. el.__zr && el.__zr.addHover(el, el.__hoverStl);
  13445. }
  13446. else {
  13447. var style = el.style;
  13448. var insideRollbackOpt = style.insideRollbackOpt;
  13449. // Consider case: only `position: 'top'` is set on emphasis, then text
  13450. // color should be returned to `autoColor`, rather than remain '#fff'.
  13451. // So we should rollback then apply again after style merging.
  13452. insideRollbackOpt && rollbackInsideStyle(style);
  13453. // styles can be:
  13454. // {
  13455. // label: {
  13456. // show: false,
  13457. // position: 'outside',
  13458. // fontSize: 18
  13459. // },
  13460. // emphasis: {
  13461. // label: {
  13462. // show: true
  13463. // }
  13464. // }
  13465. // },
  13466. // where properties of `emphasis` may not appear in `normal`. We previously use
  13467. // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.
  13468. // But consider rich text and setOption in merge mode, it is impossible to cover
  13469. // all properties in merge. So we use merge mode when setting style here, where
  13470. // only properties that is not `null/undefined` can be set. The disadventage:
  13471. // null/undefined can not be used to remove style any more in `emphasis`.
  13472. style.extendFrom(el.__hoverStl);
  13473. // Do not save `insideRollback`.
  13474. if (insideRollbackOpt) {
  13475. applyInsideStyle(style, style.insideOriginalTextPosition, insideRollbackOpt);
  13476. // textFill may be rollbacked to null.
  13477. if (style.textFill == null) {
  13478. style.textFill = insideRollbackOpt.autoColor;
  13479. }
  13480. }
  13481. el.dirty(false);
  13482. el.z2 += 1;
  13483. }
  13484. el.__isHover = true;
  13485. }
  13486. /**
  13487. * @inner
  13488. */
  13489. function doSingleLeaveHover(el) {
  13490. if (!el.__isHover) {
  13491. return;
  13492. }
  13493. var normalStl = el.__normalStl;
  13494. if (el.useHoverLayer) {
  13495. el.__zr && el.__zr.removeHover(el);
  13496. }
  13497. else {
  13498. // Consider null/undefined value, should use
  13499. // `setStyle` but not `extendFrom(stl, true)`.
  13500. normalStl && el.setStyle(normalStl);
  13501. el.z2 -= 1;
  13502. }
  13503. el.__isHover = false;
  13504. }
  13505. /**
  13506. * @inner
  13507. */
  13508. function doEnterHover(el) {
  13509. el.type === 'group'
  13510. ? el.traverse(function (child) {
  13511. if (child.type !== 'group') {
  13512. doSingleEnterHover(child);
  13513. }
  13514. })
  13515. : doSingleEnterHover(el);
  13516. }
  13517. function doLeaveHover(el) {
  13518. el.type === 'group'
  13519. ? el.traverse(function (child) {
  13520. if (child.type !== 'group') {
  13521. doSingleLeaveHover(child);
  13522. }
  13523. })
  13524. : doSingleLeaveHover(el);
  13525. }
  13526. /**
  13527. * @inner
  13528. */
  13529. function setElementHoverStl(el, hoverStl) {
  13530. // If element has sepcified hoverStyle, then use it instead of given hoverStyle
  13531. // Often used when item group has a label element and it's hoverStyle is different
  13532. el.__hoverStl = el.hoverStyle || hoverStl || {};
  13533. el.__hoverStlDirty = true;
  13534. if (el.__isHover) {
  13535. cacheElementStl(el);
  13536. }
  13537. }
  13538. /**
  13539. * @inner
  13540. */
  13541. function onElementMouseOver(e) {
  13542. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  13543. return;
  13544. }
  13545. // Only if element is not in emphasis status
  13546. !this.__isEmphasis && doEnterHover(this);
  13547. }
  13548. /**
  13549. * @inner
  13550. */
  13551. function onElementMouseOut(e) {
  13552. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  13553. return;
  13554. }
  13555. // Only if element is not in emphasis status
  13556. !this.__isEmphasis && doLeaveHover(this);
  13557. }
  13558. /**
  13559. * @inner
  13560. */
  13561. function enterEmphasis() {
  13562. this.__isEmphasis = true;
  13563. doEnterHover(this);
  13564. }
  13565. /**
  13566. * @inner
  13567. */
  13568. function leaveEmphasis() {
  13569. this.__isEmphasis = false;
  13570. doLeaveHover(this);
  13571. }
  13572. /**
  13573. * Set hover style of element.
  13574. * This method can be called repeatly without side-effects.
  13575. * @param {module:zrender/Element} el
  13576. * @param {Object} [hoverStyle]
  13577. * @param {Object} [opt]
  13578. * @param {boolean} [opt.hoverSilentOnTouch=false]
  13579. * In touch device, mouseover event will be trigger on touchstart event
  13580. * (see module:zrender/dom/HandlerProxy). By this mechanism, we can
  13581. * conviniently use hoverStyle when tap on touch screen without additional
  13582. * code for compatibility.
  13583. * But if the chart/component has select feature, which usually also use
  13584. * hoverStyle, there might be conflict between 'select-highlight' and
  13585. * 'hover-highlight' especially when roam is enabled (see geo for example).
  13586. * In this case, hoverSilentOnTouch should be used to disable hover-highlight
  13587. * on touch device.
  13588. */
  13589. function setHoverStyle(el, hoverStyle, opt) {
  13590. el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch;
  13591. el.type === 'group'
  13592. ? el.traverse(function (child) {
  13593. if (child.type !== 'group') {
  13594. setElementHoverStl(child, hoverStyle);
  13595. }
  13596. })
  13597. : setElementHoverStl(el, hoverStyle);
  13598. // Duplicated function will be auto-ignored, see Eventful.js.
  13599. el.on('mouseover', onElementMouseOver)
  13600. .on('mouseout', onElementMouseOut);
  13601. // Emphasis, normal can be triggered manually
  13602. el.on('emphasis', enterEmphasis)
  13603. .on('normal', leaveEmphasis);
  13604. }
  13605. /**
  13606. * @param {Object|module:zrender/graphic/Style} normalStyle
  13607. * @param {Object} emphasisStyle
  13608. * @param {module:echarts/model/Model} normalModel
  13609. * @param {module:echarts/model/Model} emphasisModel
  13610. * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.
  13611. * @param {string|Function} [opt.defaultText]
  13612. * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by
  13613. * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  13614. * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by
  13615. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  13616. * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by
  13617. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  13618. * @param {Object} [normalSpecified]
  13619. * @param {Object} [emphasisSpecified]
  13620. */
  13621. function setLabelStyle(
  13622. normalStyle, emphasisStyle,
  13623. normalModel, emphasisModel,
  13624. opt,
  13625. normalSpecified, emphasisSpecified
  13626. ) {
  13627. opt = opt || EMPTY_OBJ;
  13628. var labelFetcher = opt.labelFetcher;
  13629. var labelDataIndex = opt.labelDataIndex;
  13630. var labelDimIndex = opt.labelDimIndex;
  13631. // This scenario, `label.normal.show = true; label.emphasis.show = false`,
  13632. // is not supported util someone requests.
  13633. var showNormal = normalModel.getShallow('show');
  13634. var showEmphasis = emphasisModel.getShallow('show');
  13635. // Consider performance, only fetch label when necessary.
  13636. // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,
  13637. // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.
  13638. var baseText;
  13639. if (showNormal || showEmphasis) {
  13640. if (labelFetcher) {
  13641. baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex);
  13642. }
  13643. if (baseText == null) {
  13644. baseText = isFunction$1(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText;
  13645. }
  13646. }
  13647. var normalStyleText = showNormal ? baseText : null;
  13648. var emphasisStyleText = showEmphasis
  13649. ? retrieve2(
  13650. labelFetcher
  13651. ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex)
  13652. : null,
  13653. baseText
  13654. )
  13655. : null;
  13656. // Optimize: If style.text is null, text will not be drawn.
  13657. if (normalStyleText != null || emphasisStyleText != null) {
  13658. // Always set `textStyle` even if `normalStyle.text` is null, because default
  13659. // values have to be set on `normalStyle`.
  13660. // If we set default values on `emphasisStyle`, consider case:
  13661. // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`
  13662. // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`
  13663. // Then the 'red' will not work on emphasis.
  13664. setTextStyle(normalStyle, normalModel, normalSpecified, opt);
  13665. setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);
  13666. }
  13667. normalStyle.text = normalStyleText;
  13668. emphasisStyle.text = emphasisStyleText;
  13669. }
  13670. /**
  13671. * Set basic textStyle properties.
  13672. * @param {Object|module:zrender/graphic/Style} textStyle
  13673. * @param {module:echarts/model/Model} model
  13674. * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.
  13675. * @param {Object} [opt] See `opt` of `setTextStyleCommon`.
  13676. * @param {boolean} [isEmphasis]
  13677. */
  13678. function setTextStyle(
  13679. textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis
  13680. ) {
  13681. setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);
  13682. specifiedTextStyle && extend(textStyle, specifiedTextStyle);
  13683. textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  13684. return textStyle;
  13685. }
  13686. /**
  13687. * Set text option in the style.
  13688. * @deprecated
  13689. * @param {Object} textStyle
  13690. * @param {module:echarts/model/Model} labelModel
  13691. * @param {string|boolean} defaultColor Default text color.
  13692. * If set as false, it will be processed as a emphasis style.
  13693. */
  13694. function setText(textStyle, labelModel, defaultColor) {
  13695. var opt = {isRectText: true};
  13696. var isEmphasis;
  13697. if (defaultColor === false) {
  13698. isEmphasis = true;
  13699. }
  13700. else {
  13701. // Support setting color as 'auto' to get visual color.
  13702. opt.autoColor = defaultColor;
  13703. }
  13704. setTextStyleCommon(textStyle, labelModel, opt, isEmphasis);
  13705. textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  13706. }
  13707. /**
  13708. * {
  13709. * disableBox: boolean, Whether diable drawing box of block (outer most).
  13710. * isRectText: boolean,
  13711. * autoColor: string, specify a color when color is 'auto',
  13712. * for textFill, textStroke, textBackgroundColor, and textBorderColor.
  13713. * If autoColor specified, it is used as default textFill.
  13714. * useInsideStyle:
  13715. * `true`: Use inside style (textFill, textStroke, textStrokeWidth)
  13716. * if `textFill` is not specified.
  13717. * `false`: Do not use inside style.
  13718. * `null/undefined`: use inside style if `isRectText` is true and
  13719. * `textFill` is not specified and textPosition contains `'inside'`.
  13720. * forceRich: boolean
  13721. * }
  13722. */
  13723. function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {
  13724. // Consider there will be abnormal when merge hover style to normal style if given default value.
  13725. opt = opt || EMPTY_OBJ;
  13726. if (opt.isRectText) {
  13727. var textPosition = textStyleModel.getShallow('position')
  13728. || (isEmphasis ? null : 'inside');
  13729. // 'outside' is not a valid zr textPostion value, but used
  13730. // in bar series, and magric type should be considered.
  13731. textPosition === 'outside' && (textPosition = 'top');
  13732. textStyle.textPosition = textPosition;
  13733. textStyle.textOffset = textStyleModel.getShallow('offset');
  13734. var labelRotate = textStyleModel.getShallow('rotate');
  13735. labelRotate != null && (labelRotate *= Math.PI / 180);
  13736. textStyle.textRotation = labelRotate;
  13737. textStyle.textDistance = retrieve2(
  13738. textStyleModel.getShallow('distance'), isEmphasis ? null : 5
  13739. );
  13740. }
  13741. var ecModel = textStyleModel.ecModel;
  13742. var globalTextStyle = ecModel && ecModel.option.textStyle;
  13743. // Consider case:
  13744. // {
  13745. // data: [{
  13746. // value: 12,
  13747. // label: {
  13748. // rich: {
  13749. // // no 'a' here but using parent 'a'.
  13750. // }
  13751. // }
  13752. // }],
  13753. // rich: {
  13754. // a: { ... }
  13755. // }
  13756. // }
  13757. var richItemNames = getRichItemNames(textStyleModel);
  13758. var richResult;
  13759. if (richItemNames) {
  13760. richResult = {};
  13761. for (var name in richItemNames) {
  13762. if (richItemNames.hasOwnProperty(name)) {
  13763. // Cascade is supported in rich.
  13764. var richTextStyle = textStyleModel.getModel(['rich', name]);
  13765. // In rich, never `disableBox`.
  13766. setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);
  13767. }
  13768. }
  13769. }
  13770. textStyle.rich = richResult;
  13771. setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);
  13772. if (opt.forceRich && !opt.textStyle) {
  13773. opt.textStyle = {};
  13774. }
  13775. return textStyle;
  13776. }
  13777. // Consider case:
  13778. // {
  13779. // data: [{
  13780. // value: 12,
  13781. // label: {
  13782. // rich: {
  13783. // // no 'a' here but using parent 'a'.
  13784. // }
  13785. // }
  13786. // }],
  13787. // rich: {
  13788. // a: { ... }
  13789. // }
  13790. // }
  13791. function getRichItemNames(textStyleModel) {
  13792. // Use object to remove duplicated names.
  13793. var richItemNameMap;
  13794. while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
  13795. var rich = (textStyleModel.option || EMPTY_OBJ).rich;
  13796. if (rich) {
  13797. richItemNameMap = richItemNameMap || {};
  13798. for (var name in rich) {
  13799. if (rich.hasOwnProperty(name)) {
  13800. richItemNameMap[name] = 1;
  13801. }
  13802. }
  13803. }
  13804. textStyleModel = textStyleModel.parentModel;
  13805. }
  13806. return richItemNameMap;
  13807. }
  13808. function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {
  13809. // In merge mode, default value should not be given.
  13810. globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
  13811. textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt)
  13812. || globalTextStyle.color;
  13813. textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt)
  13814. || globalTextStyle.textBorderColor;
  13815. textStyle.textStrokeWidth = retrieve2(
  13816. textStyleModel.getShallow('textBorderWidth'),
  13817. globalTextStyle.textBorderWidth
  13818. );
  13819. if (!isEmphasis) {
  13820. if (isBlock) {
  13821. // Always set `insideRollback`, for clearing previous.
  13822. var originalTextPosition = textStyle.textPosition;
  13823. textStyle.insideRollback = applyInsideStyle(textStyle, originalTextPosition, opt);
  13824. // Save original textPosition, because style.textPosition will be repalced by
  13825. // real location (like [10, 30]) in zrender.
  13826. textStyle.insideOriginalTextPosition = originalTextPosition;
  13827. textStyle.insideRollbackOpt = opt;
  13828. }
  13829. // Set default finally.
  13830. if (textStyle.textFill == null) {
  13831. textStyle.textFill = opt.autoColor;
  13832. }
  13833. }
  13834. // Do not use `getFont` here, because merge should be supported, where
  13835. // part of these properties may be changed in emphasis style, and the
  13836. // others should remain their original value got from normal style.
  13837. textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;
  13838. textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;
  13839. textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;
  13840. textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;
  13841. textStyle.textAlign = textStyleModel.getShallow('align');
  13842. textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign')
  13843. || textStyleModel.getShallow('baseline');
  13844. textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');
  13845. textStyle.textWidth = textStyleModel.getShallow('width');
  13846. textStyle.textHeight = textStyleModel.getShallow('height');
  13847. textStyle.textTag = textStyleModel.getShallow('tag');
  13848. if (!isBlock || !opt.disableBox) {
  13849. textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);
  13850. textStyle.textPadding = textStyleModel.getShallow('padding');
  13851. textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);
  13852. textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');
  13853. textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');
  13854. textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');
  13855. textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');
  13856. textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');
  13857. textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');
  13858. }
  13859. textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor')
  13860. || globalTextStyle.textShadowColor;
  13861. textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur')
  13862. || globalTextStyle.textShadowBlur;
  13863. textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX')
  13864. || globalTextStyle.textShadowOffsetX;
  13865. textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY')
  13866. || globalTextStyle.textShadowOffsetY;
  13867. }
  13868. function getAutoColor(color, opt) {
  13869. return color !== 'auto' ? color : (opt && opt.autoColor) ? opt.autoColor : null;
  13870. }
  13871. function applyInsideStyle(textStyle, textPosition, opt) {
  13872. var useInsideStyle = opt.useInsideStyle;
  13873. var insideRollback;
  13874. if (textStyle.textFill == null
  13875. && useInsideStyle !== false
  13876. && (useInsideStyle === true
  13877. || (opt.isRectText
  13878. && textPosition
  13879. // textPosition can be [10, 30]
  13880. && typeof textPosition === 'string'
  13881. && textPosition.indexOf('inside') >= 0
  13882. )
  13883. )
  13884. ) {
  13885. insideRollback = {
  13886. textFill: null,
  13887. textStroke: textStyle.textStroke,
  13888. textStrokeWidth: textStyle.textStrokeWidth
  13889. };
  13890. textStyle.textFill = '#fff';
  13891. // Consider text with #fff overflow its container.
  13892. if (textStyle.textStroke == null) {
  13893. textStyle.textStroke = opt.autoColor;
  13894. textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);
  13895. }
  13896. }
  13897. return insideRollback;
  13898. }
  13899. function rollbackInsideStyle(style) {
  13900. var insideRollback = style.insideRollback;
  13901. if (insideRollback) {
  13902. style.textFill = insideRollback.textFill;
  13903. style.textStroke = insideRollback.textStroke;
  13904. style.textStrokeWidth = insideRollback.textStrokeWidth;
  13905. }
  13906. }
  13907. function getFont(opt, ecModel) {
  13908. // ecModel or default text style model.
  13909. var gTextStyleModel = ecModel || ecModel.getModel('textStyle');
  13910. return trim([
  13911. // FIXME in node-canvas fontWeight is before fontStyle
  13912. opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '',
  13913. opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '',
  13914. (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px',
  13915. opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'
  13916. ].join(' '));
  13917. }
  13918. function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {
  13919. if (typeof dataIndex === 'function') {
  13920. cb = dataIndex;
  13921. dataIndex = null;
  13922. }
  13923. // Do not check 'animation' property directly here. Consider this case:
  13924. // animation model is an `itemModel`, whose does not have `isAnimationEnabled`
  13925. // but its parent model (`seriesModel`) does.
  13926. var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
  13927. if (animationEnabled) {
  13928. var postfix = isUpdate ? 'Update' : '';
  13929. var duration = animatableModel.getShallow('animationDuration' + postfix);
  13930. var animationEasing = animatableModel.getShallow('animationEasing' + postfix);
  13931. var animationDelay = animatableModel.getShallow('animationDelay' + postfix);
  13932. if (typeof animationDelay === 'function') {
  13933. animationDelay = animationDelay(
  13934. dataIndex,
  13935. animatableModel.getAnimationDelayParams
  13936. ? animatableModel.getAnimationDelayParams(el, dataIndex)
  13937. : null
  13938. );
  13939. }
  13940. if (typeof duration === 'function') {
  13941. duration = duration(dataIndex);
  13942. }
  13943. duration > 0
  13944. ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb)
  13945. : (el.stopAnimation(), el.attr(props), cb && cb());
  13946. }
  13947. else {
  13948. el.stopAnimation();
  13949. el.attr(props);
  13950. cb && cb();
  13951. }
  13952. }
  13953. /**
  13954. * Update graphic element properties with or without animation according to the
  13955. * configuration in series.
  13956. *
  13957. * Caution: this method will stop previous animation.
  13958. * So if do not use this method to one element twice before
  13959. * animation starts, unless you know what you are doing.
  13960. *
  13961. * @param {module:zrender/Element} el
  13962. * @param {Object} props
  13963. * @param {module:echarts/model/Model} [animatableModel]
  13964. * @param {number} [dataIndex]
  13965. * @param {Function} [cb]
  13966. * @example
  13967. * graphic.updateProps(el, {
  13968. * position: [100, 100]
  13969. * }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
  13970. * // Or
  13971. * graphic.updateProps(el, {
  13972. * position: [100, 100]
  13973. * }, seriesModel, function () { console.log('Animation done!'); });
  13974. */
  13975. function updateProps(el, props, animatableModel, dataIndex, cb) {
  13976. animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
  13977. }
  13978. /**
  13979. * Init graphic element properties with or without animation according to the
  13980. * configuration in series.
  13981. *
  13982. * Caution: this method will stop previous animation.
  13983. * So if do not use this method to one element twice before
  13984. * animation starts, unless you know what you are doing.
  13985. *
  13986. * @param {module:zrender/Element} el
  13987. * @param {Object} props
  13988. * @param {module:echarts/model/Model} [animatableModel]
  13989. * @param {number} [dataIndex]
  13990. * @param {Function} cb
  13991. */
  13992. function initProps(el, props, animatableModel, dataIndex, cb) {
  13993. animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
  13994. }
  13995. /**
  13996. * Get transform matrix of target (param target),
  13997. * in coordinate of its ancestor (param ancestor)
  13998. *
  13999. * @param {module:zrender/mixin/Transformable} target
  14000. * @param {module:zrender/mixin/Transformable} [ancestor]
  14001. */
  14002. function getTransform(target, ancestor) {
  14003. var mat = identity([]);
  14004. while (target && target !== ancestor) {
  14005. mul$1(mat, target.getLocalTransform(), mat);
  14006. target = target.parent;
  14007. }
  14008. return mat;
  14009. }
  14010. /**
  14011. * Apply transform to an vertex.
  14012. * @param {Array.<number>} target [x, y]
  14013. * @param {Array.<number>|TypedArray.<number>|Object} transform Can be:
  14014. * + Transform matrix: like [1, 0, 0, 1, 0, 0]
  14015. * + {position, rotation, scale}, the same as `zrender/Transformable`.
  14016. * @param {boolean=} invert Whether use invert matrix.
  14017. * @return {Array.<number>} [x, y]
  14018. */
  14019. function applyTransform$1(target, transform, invert$$1) {
  14020. if (transform && !isArrayLike(transform)) {
  14021. transform = Transformable.getLocalTransform(transform);
  14022. }
  14023. if (invert$$1) {
  14024. transform = invert([], transform);
  14025. }
  14026. return applyTransform([], target, transform);
  14027. }
  14028. /**
  14029. * @param {string} direction 'left' 'right' 'top' 'bottom'
  14030. * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]
  14031. * @param {boolean=} invert Whether use invert matrix.
  14032. * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'
  14033. */
  14034. function transformDirection(direction, transform, invert$$1) {
  14035. // Pick a base, ensure that transform result will not be (0, 0).
  14036. var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0)
  14037. ? 1 : Math.abs(2 * transform[4] / transform[0]);
  14038. var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0)
  14039. ? 1 : Math.abs(2 * transform[4] / transform[2]);
  14040. var vertex = [
  14041. direction === 'left' ? -hBase : direction === 'right' ? hBase : 0,
  14042. direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0
  14043. ];
  14044. vertex = applyTransform$1(vertex, transform, invert$$1);
  14045. return Math.abs(vertex[0]) > Math.abs(vertex[1])
  14046. ? (vertex[0] > 0 ? 'right' : 'left')
  14047. : (vertex[1] > 0 ? 'bottom' : 'top');
  14048. }
  14049. /**
  14050. * Apply group transition animation from g1 to g2.
  14051. * If no animatableModel, no animation.
  14052. */
  14053. function groupTransition(g1, g2, animatableModel, cb) {
  14054. if (!g1 || !g2) {
  14055. return;
  14056. }
  14057. function getElMap(g) {
  14058. var elMap = {};
  14059. g.traverse(function (el) {
  14060. if (!el.isGroup && el.anid) {
  14061. elMap[el.anid] = el;
  14062. }
  14063. });
  14064. return elMap;
  14065. }
  14066. function getAnimatableProps(el) {
  14067. var obj = {
  14068. position: clone$1(el.position),
  14069. rotation: el.rotation
  14070. };
  14071. if (el.shape) {
  14072. obj.shape = extend({}, el.shape);
  14073. }
  14074. return obj;
  14075. }
  14076. var elMap1 = getElMap(g1);
  14077. g2.traverse(function (el) {
  14078. if (!el.isGroup && el.anid) {
  14079. var oldEl = elMap1[el.anid];
  14080. if (oldEl) {
  14081. var newProp = getAnimatableProps(el);
  14082. el.attr(getAnimatableProps(oldEl));
  14083. updateProps(el, newProp, animatableModel, el.dataIndex);
  14084. }
  14085. // else {
  14086. // if (el.previousProps) {
  14087. // graphic.updateProps
  14088. // }
  14089. // }
  14090. }
  14091. });
  14092. }
  14093. /**
  14094. * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]
  14095. * @param {Object} rect {x, y, width, height}
  14096. * @return {Array.<Array.<number>>} A new clipped points.
  14097. */
  14098. function clipPointsByRect(points, rect) {
  14099. return map(points, function (point) {
  14100. var x = point[0];
  14101. x = mathMax$1(x, rect.x);
  14102. x = mathMin$1(x, rect.x + rect.width);
  14103. var y = point[1];
  14104. y = mathMax$1(y, rect.y);
  14105. y = mathMin$1(y, rect.y + rect.height);
  14106. return [x, y];
  14107. });
  14108. }
  14109. /**
  14110. * @param {Object} targetRect {x, y, width, height}
  14111. * @param {Object} rect {x, y, width, height}
  14112. * @return {Object} A new clipped rect. If rect size are negative, return undefined.
  14113. */
  14114. function clipRectByRect(targetRect, rect) {
  14115. var x = mathMax$1(targetRect.x, rect.x);
  14116. var x2 = mathMin$1(targetRect.x + targetRect.width, rect.x + rect.width);
  14117. var y = mathMax$1(targetRect.y, rect.y);
  14118. var y2 = mathMin$1(targetRect.y + targetRect.height, rect.y + rect.height);
  14119. if (x2 >= x && y2 >= y) {
  14120. return {
  14121. x: x,
  14122. y: y,
  14123. width: x2 - x,
  14124. height: y2 - y
  14125. };
  14126. }
  14127. }
  14128. /**
  14129. * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.
  14130. * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.
  14131. * @param {Object} [rect] {x, y, width, height}
  14132. * @return {module:zrender/Element} Icon path or image element.
  14133. */
  14134. function createIcon(iconStr, opt, rect) {
  14135. opt = extend({rectHover: true}, opt);
  14136. var style = opt.style = {strokeNoScale: true};
  14137. rect = rect || {x: -1, y: -1, width: 2, height: 2};
  14138. if (iconStr) {
  14139. return iconStr.indexOf('image://') === 0
  14140. ? (
  14141. style.image = iconStr.slice(8),
  14142. defaults(style, rect),
  14143. new ZImage(opt)
  14144. )
  14145. : (
  14146. makePath(
  14147. iconStr.replace('path://', ''),
  14148. opt,
  14149. rect,
  14150. 'center'
  14151. )
  14152. );
  14153. }
  14154. }
  14155. var graphic = (Object.freeze || Object)({
  14156. extendShape: extendShape,
  14157. extendPath: extendPath,
  14158. makePath: makePath,
  14159. makeImage: makeImage,
  14160. mergePath: mergePath,
  14161. resizePath: resizePath,
  14162. subPixelOptimizeLine: subPixelOptimizeLine,
  14163. subPixelOptimizeRect: subPixelOptimizeRect,
  14164. subPixelOptimize: subPixelOptimize,
  14165. setHoverStyle: setHoverStyle,
  14166. setLabelStyle: setLabelStyle,
  14167. setTextStyle: setTextStyle,
  14168. setText: setText,
  14169. getFont: getFont,
  14170. updateProps: updateProps,
  14171. initProps: initProps,
  14172. getTransform: getTransform,
  14173. applyTransform: applyTransform$1,
  14174. transformDirection: transformDirection,
  14175. groupTransition: groupTransition,
  14176. clipPointsByRect: clipPointsByRect,
  14177. clipRectByRect: clipRectByRect,
  14178. createIcon: createIcon,
  14179. Group: Group,
  14180. Image: ZImage,
  14181. Text: Text,
  14182. Circle: Circle,
  14183. Sector: Sector,
  14184. Ring: Ring,
  14185. Polygon: Polygon,
  14186. Polyline: Polyline,
  14187. Rect: Rect,
  14188. Line: Line,
  14189. BezierCurve: BezierCurve,
  14190. Arc: Arc,
  14191. IncrementalDisplayable: IncrementalDisplayble,
  14192. CompoundPath: CompoundPath,
  14193. LinearGradient: LinearGradient,
  14194. RadialGradient: RadialGradient,
  14195. BoundingRect: BoundingRect
  14196. });
  14197. var PATH_COLOR = ['textStyle', 'color'];
  14198. var textStyleMixin = {
  14199. /**
  14200. * Get color property or get color from option.textStyle.color
  14201. * @param {boolean} [isEmphasis]
  14202. * @return {string}
  14203. */
  14204. getTextColor: function (isEmphasis) {
  14205. var ecModel = this.ecModel;
  14206. return this.getShallow('color')
  14207. || (
  14208. (!isEmphasis && ecModel) ? ecModel.get(PATH_COLOR) : null
  14209. );
  14210. },
  14211. /**
  14212. * Create font string from fontStyle, fontWeight, fontSize, fontFamily
  14213. * @return {string}
  14214. */
  14215. getFont: function () {
  14216. return getFont({
  14217. fontStyle: this.getShallow('fontStyle'),
  14218. fontWeight: this.getShallow('fontWeight'),
  14219. fontSize: this.getShallow('fontSize'),
  14220. fontFamily: this.getShallow('fontFamily')
  14221. }, this.ecModel);
  14222. },
  14223. getTextRect: function (text) {
  14224. return getBoundingRect(
  14225. text,
  14226. this.getFont(),
  14227. this.getShallow('align'),
  14228. this.getShallow('verticalAlign') || this.getShallow('baseline'),
  14229. this.getShallow('padding'),
  14230. this.getShallow('rich'),
  14231. this.getShallow('truncateText')
  14232. );
  14233. }
  14234. };
  14235. var getItemStyle = makeStyleMapper(
  14236. [
  14237. ['fill', 'color'],
  14238. ['stroke', 'borderColor'],
  14239. ['lineWidth', 'borderWidth'],
  14240. ['opacity'],
  14241. ['shadowBlur'],
  14242. ['shadowOffsetX'],
  14243. ['shadowOffsetY'],
  14244. ['shadowColor'],
  14245. ['textPosition'],
  14246. ['textAlign']
  14247. ]
  14248. );
  14249. var itemStyleMixin = {
  14250. getItemStyle: function (excludes, includes) {
  14251. var style = getItemStyle(this, excludes, includes);
  14252. var lineDash = this.getBorderLineDash();
  14253. lineDash && (style.lineDash = lineDash);
  14254. return style;
  14255. },
  14256. getBorderLineDash: function () {
  14257. var lineType = this.get('borderType');
  14258. return (lineType === 'solid' || lineType == null) ? null
  14259. : (lineType === 'dashed' ? [5, 5] : [1, 1]);
  14260. }
  14261. };
  14262. /**
  14263. * @module echarts/model/Model
  14264. */
  14265. var mixin$1 = mixin;
  14266. var inner = makeInner();
  14267. /**
  14268. * @alias module:echarts/model/Model
  14269. * @constructor
  14270. * @param {Object} option
  14271. * @param {module:echarts/model/Model} [parentModel]
  14272. * @param {module:echarts/model/Global} [ecModel]
  14273. */
  14274. function Model(option, parentModel, ecModel) {
  14275. /**
  14276. * @type {module:echarts/model/Model}
  14277. * @readOnly
  14278. */
  14279. this.parentModel = parentModel;
  14280. /**
  14281. * @type {module:echarts/model/Global}
  14282. * @readOnly
  14283. */
  14284. this.ecModel = ecModel;
  14285. /**
  14286. * @type {Object}
  14287. * @protected
  14288. */
  14289. this.option = option;
  14290. // Simple optimization
  14291. // if (this.init) {
  14292. // if (arguments.length <= 4) {
  14293. // this.init(option, parentModel, ecModel, extraOpt);
  14294. // }
  14295. // else {
  14296. // this.init.apply(this, arguments);
  14297. // }
  14298. // }
  14299. }
  14300. Model.prototype = {
  14301. constructor: Model,
  14302. /**
  14303. * Model 的初始化函数
  14304. * @param {Object} option
  14305. */
  14306. init: null,
  14307. /**
  14308. * 从新的 Option merge
  14309. */
  14310. mergeOption: function (option) {
  14311. merge(this.option, option, true);
  14312. },
  14313. /**
  14314. * @param {string|Array.<string>} path
  14315. * @param {boolean} [ignoreParent=false]
  14316. * @return {*}
  14317. */
  14318. get: function (path, ignoreParent) {
  14319. if (path == null) {
  14320. return this.option;
  14321. }
  14322. return doGet(
  14323. this.option,
  14324. this.parsePath(path),
  14325. !ignoreParent && getParent(this, path)
  14326. );
  14327. },
  14328. /**
  14329. * @param {string} key
  14330. * @param {boolean} [ignoreParent=false]
  14331. * @return {*}
  14332. */
  14333. getShallow: function (key, ignoreParent) {
  14334. var option = this.option;
  14335. var val = option == null ? option : option[key];
  14336. var parentModel = !ignoreParent && getParent(this, key);
  14337. if (val == null && parentModel) {
  14338. val = parentModel.getShallow(key);
  14339. }
  14340. return val;
  14341. },
  14342. /**
  14343. * @param {string|Array.<string>} [path]
  14344. * @param {module:echarts/model/Model} [parentModel]
  14345. * @return {module:echarts/model/Model}
  14346. */
  14347. getModel: function (path, parentModel) {
  14348. var obj = path == null
  14349. ? this.option
  14350. : doGet(this.option, path = this.parsePath(path));
  14351. var thisParentModel;
  14352. parentModel = parentModel || (
  14353. (thisParentModel = getParent(this, path))
  14354. && thisParentModel.getModel(path)
  14355. );
  14356. return new Model(obj, parentModel, this.ecModel);
  14357. },
  14358. /**
  14359. * If model has option
  14360. */
  14361. isEmpty: function () {
  14362. return this.option == null;
  14363. },
  14364. restoreData: function () {},
  14365. // Pending
  14366. clone: function () {
  14367. var Ctor = this.constructor;
  14368. return new Ctor(clone(this.option));
  14369. },
  14370. setReadOnly: function (properties) {
  14371. // clazzUtil.setReadOnly(this, properties);
  14372. },
  14373. // If path is null/undefined, return null/undefined.
  14374. parsePath: function(path) {
  14375. if (typeof path === 'string') {
  14376. path = path.split('.');
  14377. }
  14378. return path;
  14379. },
  14380. /**
  14381. * @param {Function} getParentMethod
  14382. * param {Array.<string>|string} path
  14383. * return {module:echarts/model/Model}
  14384. */
  14385. customizeGetParent: function (getParentMethod) {
  14386. inner(this).getParent = getParentMethod;
  14387. },
  14388. isAnimationEnabled: function () {
  14389. if (!env$1.node) {
  14390. if (this.option.animation != null) {
  14391. return !!this.option.animation;
  14392. }
  14393. else if (this.parentModel) {
  14394. return this.parentModel.isAnimationEnabled();
  14395. }
  14396. }
  14397. }
  14398. };
  14399. function doGet(obj, pathArr, parentModel) {
  14400. for (var i = 0; i < pathArr.length; i++) {
  14401. // Ignore empty
  14402. if (!pathArr[i]) {
  14403. continue;
  14404. }
  14405. // obj could be number/string/... (like 0)
  14406. obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null;
  14407. if (obj == null) {
  14408. break;
  14409. }
  14410. }
  14411. if (obj == null && parentModel) {
  14412. obj = parentModel.get(pathArr);
  14413. }
  14414. return obj;
  14415. }
  14416. // `path` can be null/undefined
  14417. function getParent(model, path) {
  14418. var getParentMethod = inner(model).getParent;
  14419. return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;
  14420. }
  14421. // Enable Model.extend.
  14422. enableClassExtend(Model);
  14423. enableClassCheck(Model);
  14424. mixin$1(Model, lineStyleMixin);
  14425. mixin$1(Model, areaStyleMixin);
  14426. mixin$1(Model, textStyleMixin);
  14427. mixin$1(Model, itemStyleMixin);
  14428. var base = 0;
  14429. /**
  14430. * @public
  14431. * @param {string} type
  14432. * @return {string}
  14433. */
  14434. function getUID(type) {
  14435. // Considering the case of crossing js context,
  14436. // use Math.random to make id as unique as possible.
  14437. return [(type || ''), base++, Math.random().toFixed(5)].join('_');
  14438. }
  14439. /**
  14440. * @inner
  14441. */
  14442. function enableSubTypeDefaulter(entity) {
  14443. var subTypeDefaulters = {};
  14444. entity.registerSubTypeDefaulter = function (componentType, defaulter) {
  14445. componentType = parseClassType$1(componentType);
  14446. subTypeDefaulters[componentType.main] = defaulter;
  14447. };
  14448. entity.determineSubType = function (componentType, option) {
  14449. var type = option.type;
  14450. if (!type) {
  14451. var componentTypeMain = parseClassType$1(componentType).main;
  14452. if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {
  14453. type = subTypeDefaulters[componentTypeMain](option);
  14454. }
  14455. }
  14456. return type;
  14457. };
  14458. return entity;
  14459. }
  14460. /**
  14461. * Topological travel on Activity Network (Activity On Vertices).
  14462. * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
  14463. *
  14464. * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
  14465. *
  14466. * If there is circle dependencey, Error will be thrown.
  14467. *
  14468. */
  14469. function enableTopologicalTravel(entity, dependencyGetter) {
  14470. /**
  14471. * @public
  14472. * @param {Array.<string>} targetNameList Target Component type list.
  14473. * Can be ['aa', 'bb', 'aa.xx']
  14474. * @param {Array.<string>} fullNameList By which we can build dependency graph.
  14475. * @param {Function} callback Params: componentType, dependencies.
  14476. * @param {Object} context Scope of callback.
  14477. */
  14478. entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
  14479. if (!targetNameList.length) {
  14480. return;
  14481. }
  14482. var result = makeDepndencyGraph(fullNameList);
  14483. var graph = result.graph;
  14484. var stack = result.noEntryList;
  14485. var targetNameSet = {};
  14486. each$1(targetNameList, function (name) {
  14487. targetNameSet[name] = true;
  14488. });
  14489. while (stack.length) {
  14490. var currComponentType = stack.pop();
  14491. var currVertex = graph[currComponentType];
  14492. var isInTargetNameSet = !!targetNameSet[currComponentType];
  14493. if (isInTargetNameSet) {
  14494. callback.call(context, currComponentType, currVertex.originalDeps.slice());
  14495. delete targetNameSet[currComponentType];
  14496. }
  14497. each$1(
  14498. currVertex.successor,
  14499. isInTargetNameSet ? removeEdgeAndAdd : removeEdge
  14500. );
  14501. }
  14502. each$1(targetNameSet, function () {
  14503. throw new Error('Circle dependency may exists');
  14504. });
  14505. function removeEdge(succComponentType) {
  14506. graph[succComponentType].entryCount--;
  14507. if (graph[succComponentType].entryCount === 0) {
  14508. stack.push(succComponentType);
  14509. }
  14510. }
  14511. // Consider this case: legend depends on series, and we call
  14512. // chart.setOption({series: [...]}), where only series is in option.
  14513. // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
  14514. // not be called, but only sereis.mergeOption is called. Thus legend
  14515. // have no chance to update its local record about series (like which
  14516. // name of series is available in legend).
  14517. function removeEdgeAndAdd(succComponentType) {
  14518. targetNameSet[succComponentType] = true;
  14519. removeEdge(succComponentType);
  14520. }
  14521. };
  14522. /**
  14523. * DepndencyGraph: {Object}
  14524. * key: conponentType,
  14525. * value: {
  14526. * successor: [conponentTypes...],
  14527. * originalDeps: [conponentTypes...],
  14528. * entryCount: {number}
  14529. * }
  14530. */
  14531. function makeDepndencyGraph(fullNameList) {
  14532. var graph = {};
  14533. var noEntryList = [];
  14534. each$1(fullNameList, function (name) {
  14535. var thisItem = createDependencyGraphItem(graph, name);
  14536. var originalDeps = thisItem.originalDeps = dependencyGetter(name);
  14537. var availableDeps = getAvailableDependencies(originalDeps, fullNameList);
  14538. thisItem.entryCount = availableDeps.length;
  14539. if (thisItem.entryCount === 0) {
  14540. noEntryList.push(name);
  14541. }
  14542. each$1(availableDeps, function (dependentName) {
  14543. if (indexOf(thisItem.predecessor, dependentName) < 0) {
  14544. thisItem.predecessor.push(dependentName);
  14545. }
  14546. var thatItem = createDependencyGraphItem(graph, dependentName);
  14547. if (indexOf(thatItem.successor, dependentName) < 0) {
  14548. thatItem.successor.push(name);
  14549. }
  14550. });
  14551. });
  14552. return {graph: graph, noEntryList: noEntryList};
  14553. }
  14554. function createDependencyGraphItem(graph, name) {
  14555. if (!graph[name]) {
  14556. graph[name] = {predecessor: [], successor: []};
  14557. }
  14558. return graph[name];
  14559. }
  14560. function getAvailableDependencies(originalDeps, fullNameList) {
  14561. var availableDeps = [];
  14562. each$1(originalDeps, function (dep) {
  14563. indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);
  14564. });
  14565. return availableDeps;
  14566. }
  14567. }
  14568. var RADIAN_EPSILON = 1e-4;
  14569. function _trim(str) {
  14570. return str.replace(/^\s+/, '').replace(/\s+$/, '');
  14571. }
  14572. /**
  14573. * Linear mapping a value from domain to range
  14574. * @memberOf module:echarts/util/number
  14575. * @param {(number|Array.<number>)} val
  14576. * @param {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1]
  14577. * @param {Array.<number>} range Range extent range[0] can be bigger than range[1]
  14578. * @param {boolean} clamp
  14579. * @return {(number|Array.<number>}
  14580. */
  14581. function linearMap(val, domain, range, clamp) {
  14582. var subDomain = domain[1] - domain[0];
  14583. var subRange = range[1] - range[0];
  14584. if (subDomain === 0) {
  14585. return subRange === 0
  14586. ? range[0]
  14587. : (range[0] + range[1]) / 2;
  14588. }
  14589. // Avoid accuracy problem in edge, such as
  14590. // 146.39 - 62.83 === 83.55999999999999.
  14591. // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
  14592. // It is a little verbose for efficiency considering this method
  14593. // is a hotspot.
  14594. if (clamp) {
  14595. if (subDomain > 0) {
  14596. if (val <= domain[0]) {
  14597. return range[0];
  14598. }
  14599. else if (val >= domain[1]) {
  14600. return range[1];
  14601. }
  14602. }
  14603. else {
  14604. if (val >= domain[0]) {
  14605. return range[0];
  14606. }
  14607. else if (val <= domain[1]) {
  14608. return range[1];
  14609. }
  14610. }
  14611. }
  14612. else {
  14613. if (val === domain[0]) {
  14614. return range[0];
  14615. }
  14616. if (val === domain[1]) {
  14617. return range[1];
  14618. }
  14619. }
  14620. return (val - domain[0]) / subDomain * subRange + range[0];
  14621. }
  14622. /**
  14623. * Convert a percent string to absolute number.
  14624. * Returns NaN if percent is not a valid string or number
  14625. * @memberOf module:echarts/util/number
  14626. * @param {string|number} percent
  14627. * @param {number} all
  14628. * @return {number}
  14629. */
  14630. function parsePercent$1(percent, all) {
  14631. switch (percent) {
  14632. case 'center':
  14633. case 'middle':
  14634. percent = '50%';
  14635. break;
  14636. case 'left':
  14637. case 'top':
  14638. percent = '0%';
  14639. break;
  14640. case 'right':
  14641. case 'bottom':
  14642. percent = '100%';
  14643. break;
  14644. }
  14645. if (typeof percent === 'string') {
  14646. if (_trim(percent).match(/%$/)) {
  14647. return parseFloat(percent) / 100 * all;
  14648. }
  14649. return parseFloat(percent);
  14650. }
  14651. return percent == null ? NaN : +percent;
  14652. }
  14653. /**
  14654. * (1) Fix rounding error of float numbers.
  14655. * (2) Support return string to avoid scientific notation like '3.5e-7'.
  14656. *
  14657. * @param {number} x
  14658. * @param {number} [precision]
  14659. * @param {boolean} [returnStr]
  14660. * @return {number|string}
  14661. */
  14662. function round$1(x, precision, returnStr) {
  14663. if (precision == null) {
  14664. precision = 10;
  14665. }
  14666. // Avoid range error
  14667. precision = Math.min(Math.max(0, precision), 20);
  14668. x = (+x).toFixed(precision);
  14669. return returnStr ? x : +x;
  14670. }
  14671. /**
  14672. * Get precision
  14673. * @param {number} val
  14674. */
  14675. /**
  14676. * @param {string|number} val
  14677. * @return {number}
  14678. */
  14679. function getPrecisionSafe(val) {
  14680. var str = val.toString();
  14681. // Consider scientific notation: '3.4e-12' '3.4e+12'
  14682. var eIndex = str.indexOf('e');
  14683. if (eIndex > 0) {
  14684. var precision = +str.slice(eIndex + 1);
  14685. return precision < 0 ? -precision : 0;
  14686. }
  14687. else {
  14688. var dotIndex = str.indexOf('.');
  14689. return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;
  14690. }
  14691. }
  14692. /**
  14693. * Minimal dicernible data precisioin according to a single pixel.
  14694. *
  14695. * @param {Array.<number>} dataExtent
  14696. * @param {Array.<number>} pixelExtent
  14697. * @return {number} precision
  14698. */
  14699. function getPixelPrecision(dataExtent, pixelExtent) {
  14700. var log = Math.log;
  14701. var LN10 = Math.LN10;
  14702. var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);
  14703. var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10);
  14704. // toFixed() digits argument must be between 0 and 20.
  14705. var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);
  14706. return !isFinite(precision) ? 20 : precision;
  14707. }
  14708. /**
  14709. * Get a data of given precision, assuring the sum of percentages
  14710. * in valueList is 1.
  14711. * The largest remainer method is used.
  14712. * https://en.wikipedia.org/wiki/Largest_remainder_method
  14713. *
  14714. * @param {Array.<number>} valueList a list of all data
  14715. * @param {number} idx index of the data to be processed in valueList
  14716. * @param {number} precision integer number showing digits of precision
  14717. * @return {number} percent ranging from 0 to 100
  14718. */
  14719. function getPercentWithPrecision(valueList, idx, precision) {
  14720. if (!valueList[idx]) {
  14721. return 0;
  14722. }
  14723. var sum = reduce(valueList, function (acc, val) {
  14724. return acc + (isNaN(val) ? 0 : val);
  14725. }, 0);
  14726. if (sum === 0) {
  14727. return 0;
  14728. }
  14729. var digits = Math.pow(10, precision);
  14730. var votesPerQuota = map(valueList, function (val) {
  14731. return (isNaN(val) ? 0 : val) / sum * digits * 100;
  14732. });
  14733. var targetSeats = digits * 100;
  14734. var seats = map(votesPerQuota, function (votes) {
  14735. // Assign automatic seats.
  14736. return Math.floor(votes);
  14737. });
  14738. var currentSum = reduce(seats, function (acc, val) {
  14739. return acc + val;
  14740. }, 0);
  14741. var remainder = map(votesPerQuota, function (votes, idx) {
  14742. return votes - seats[idx];
  14743. });
  14744. // Has remainding votes.
  14745. while (currentSum < targetSeats) {
  14746. // Find next largest remainder.
  14747. var max = Number.NEGATIVE_INFINITY;
  14748. var maxId = null;
  14749. for (var i = 0, len = remainder.length; i < len; ++i) {
  14750. if (remainder[i] > max) {
  14751. max = remainder[i];
  14752. maxId = i;
  14753. }
  14754. }
  14755. // Add a vote to max remainder.
  14756. ++seats[maxId];
  14757. remainder[maxId] = 0;
  14758. ++currentSum;
  14759. }
  14760. return seats[idx] / digits;
  14761. }
  14762. // Number.MAX_SAFE_INTEGER, ie do not support.
  14763. /**
  14764. * To 0 - 2 * PI, considering negative radian.
  14765. * @param {number} radian
  14766. * @return {number}
  14767. */
  14768. function remRadian(radian) {
  14769. var pi2 = Math.PI * 2;
  14770. return (radian % pi2 + pi2) % pi2;
  14771. }
  14772. /**
  14773. * @param {type} radian
  14774. * @return {boolean}
  14775. */
  14776. function isRadianAroundZero(val) {
  14777. return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
  14778. }
  14779. 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
  14780. /**
  14781. * @param {string|Date|number} value These values can be accepted:
  14782. * + An instance of Date, represent a time in its own time zone.
  14783. * + Or string in a subset of ISO 8601, only including:
  14784. * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
  14785. * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
  14786. * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
  14787. * all of which will be treated as local time if time zone is not specified
  14788. * (see <https://momentjs.com/>).
  14789. * + Or other string format, including (all of which will be treated as loacal time):
  14790. * '2012', '2012-3-1', '2012/3/1', '2012/03/01',
  14791. * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
  14792. * + a timestamp, which represent a time in UTC.
  14793. * @return {Date} date
  14794. */
  14795. function parseDate(value) {
  14796. if (value instanceof Date) {
  14797. return value;
  14798. }
  14799. else if (typeof value === 'string') {
  14800. // Different browsers parse date in different way, so we parse it manually.
  14801. // Some other issues:
  14802. // new Date('1970-01-01') is UTC,
  14803. // new Date('1970/01/01') and new Date('1970-1-01') is local.
  14804. // See issue #3623
  14805. var match = TIME_REG.exec(value);
  14806. if (!match) {
  14807. // return Invalid Date.
  14808. return new Date(NaN);
  14809. }
  14810. // Use local time when no timezone offset specifed.
  14811. if (!match[8]) {
  14812. // match[n] can only be string or undefined.
  14813. // But take care of '12' + 1 => '121'.
  14814. return new Date(
  14815. +match[1],
  14816. +(match[2] || 1) - 1,
  14817. +match[3] || 1,
  14818. +match[4] || 0,
  14819. +(match[5] || 0),
  14820. +match[6] || 0,
  14821. +match[7] || 0
  14822. );
  14823. }
  14824. // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
  14825. // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
  14826. // For example, system timezone is set as "Time Zone: America/Toronto",
  14827. // then these code will get different result:
  14828. // `new Date(1478411999999).getTimezoneOffset(); // get 240`
  14829. // `new Date(1478412000000).getTimezoneOffset(); // get 300`
  14830. // So we should not use `new Date`, but use `Date.UTC`.
  14831. else {
  14832. var hour = +match[4] || 0;
  14833. if (match[8].toUpperCase() !== 'Z') {
  14834. hour -= match[8].slice(0, 3);
  14835. }
  14836. return new Date(Date.UTC(
  14837. +match[1],
  14838. +(match[2] || 1) - 1,
  14839. +match[3] || 1,
  14840. hour,
  14841. +(match[5] || 0),
  14842. +match[6] || 0,
  14843. +match[7] || 0
  14844. ));
  14845. }
  14846. }
  14847. else if (value == null) {
  14848. return new Date(NaN);
  14849. }
  14850. return new Date(Math.round(value));
  14851. }
  14852. /**
  14853. * Quantity of a number. e.g. 0.1, 1, 10, 100
  14854. *
  14855. * @param {number} val
  14856. * @return {number}
  14857. */
  14858. function quantity(val) {
  14859. return Math.pow(10, quantityExponent(val));
  14860. }
  14861. function quantityExponent(val) {
  14862. return Math.floor(Math.log(val) / Math.LN10);
  14863. }
  14864. /**
  14865. * find a “nice” number approximately equal to x. Round the number if round = true,
  14866. * take ceiling if round = false. The primary observation is that the “nicest”
  14867. * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
  14868. *
  14869. * See "Nice Numbers for Graph Labels" of Graphic Gems.
  14870. *
  14871. * @param {number} val Non-negative value.
  14872. * @param {boolean} round
  14873. * @return {number}
  14874. */
  14875. function nice(val, round) {
  14876. var exponent = quantityExponent(val);
  14877. var exp10 = Math.pow(10, exponent);
  14878. var f = val / exp10; // 1 <= f < 10
  14879. var nf;
  14880. if (round) {
  14881. if (f < 1.5) { nf = 1; }
  14882. else if (f < 2.5) { nf = 2; }
  14883. else if (f < 4) { nf = 3; }
  14884. else if (f < 7) { nf = 5; }
  14885. else { nf = 10; }
  14886. }
  14887. else {
  14888. if (f < 1) { nf = 1; }
  14889. else if (f < 2) { nf = 2; }
  14890. else if (f < 3) { nf = 3; }
  14891. else if (f < 5) { nf = 5; }
  14892. else { nf = 10; }
  14893. }
  14894. val = nf * exp10;
  14895. // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).
  14896. // 20 is the uppper bound of toFixed.
  14897. return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
  14898. }
  14899. /**
  14900. * Order intervals asc, and split them when overlap.
  14901. * expect(numberUtil.reformIntervals([
  14902. * {interval: [18, 62], close: [1, 1]},
  14903. * {interval: [-Infinity, -70], close: [0, 0]},
  14904. * {interval: [-70, -26], close: [1, 1]},
  14905. * {interval: [-26, 18], close: [1, 1]},
  14906. * {interval: [62, 150], close: [1, 1]},
  14907. * {interval: [106, 150], close: [1, 1]},
  14908. * {interval: [150, Infinity], close: [0, 0]}
  14909. * ])).toEqual([
  14910. * {interval: [-Infinity, -70], close: [0, 0]},
  14911. * {interval: [-70, -26], close: [1, 1]},
  14912. * {interval: [-26, 18], close: [0, 1]},
  14913. * {interval: [18, 62], close: [0, 1]},
  14914. * {interval: [62, 150], close: [0, 1]},
  14915. * {interval: [150, Infinity], close: [0, 0]}
  14916. * ]);
  14917. * @param {Array.<Object>} list, where `close` mean open or close
  14918. * of the interval, and Infinity can be used.
  14919. * @return {Array.<Object>} The origin list, which has been reformed.
  14920. */
  14921. /**
  14922. * parseFloat NaNs numeric-cast false positives (null|true|false|"")
  14923. * ...but misinterprets leading-number strings, particularly hex literals ("0x...")
  14924. * subtraction forces infinities to NaN
  14925. *
  14926. * @param {*} v
  14927. * @return {boolean}
  14928. */
  14929. /**
  14930. * 每三位默认加,格式化
  14931. * @param {string|number} x
  14932. * @return {string}
  14933. */
  14934. function addCommas(x) {
  14935. if (isNaN(x)) {
  14936. return '-';
  14937. }
  14938. x = (x + '').split('.');
  14939. return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,'$1,')
  14940. + (x.length > 1 ? ('.' + x[1]) : '');
  14941. }
  14942. /**
  14943. * @param {string} str
  14944. * @param {boolean} [upperCaseFirst=false]
  14945. * @return {string} str
  14946. */
  14947. var normalizeCssArray$1 = normalizeCssArray;
  14948. function encodeHTML(source) {
  14949. return String(source)
  14950. .replace(/&/g, '&amp;')
  14951. .replace(/</g, '&lt;')
  14952. .replace(/>/g, '&gt;')
  14953. .replace(/"/g, '&quot;')
  14954. .replace(/'/g, '&#39;');
  14955. }
  14956. var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
  14957. var wrapVar = function (varName, seriesIdx) {
  14958. return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';
  14959. };
  14960. /**
  14961. * Template formatter
  14962. * @param {string} tpl
  14963. * @param {Array.<Object>|Object} paramsList
  14964. * @param {boolean} [encode=false]
  14965. * @return {string}
  14966. */
  14967. function formatTpl(tpl, paramsList, encode) {
  14968. if (!isArray(paramsList)) {
  14969. paramsList = [paramsList];
  14970. }
  14971. var seriesLen = paramsList.length;
  14972. if (!seriesLen) {
  14973. return '';
  14974. }
  14975. var $vars = paramsList[0].$vars || [];
  14976. for (var i = 0; i < $vars.length; i++) {
  14977. var alias = TPL_VAR_ALIAS[i];
  14978. tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0));
  14979. }
  14980. for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {
  14981. for (var k = 0; k < $vars.length; k++) {
  14982. var val = paramsList[seriesIdx][$vars[k]];
  14983. tpl = tpl.replace(
  14984. wrapVar(TPL_VAR_ALIAS[k], seriesIdx),
  14985. encode ? encodeHTML(val) : val
  14986. );
  14987. }
  14988. }
  14989. return tpl;
  14990. }
  14991. /**
  14992. * simple Template formatter
  14993. *
  14994. * @param {string} tpl
  14995. * @param {Object} param
  14996. * @param {boolean} [encode=false]
  14997. * @return {string}
  14998. */
  14999. /**
  15000. * @param {Object|string} [opt] If string, means color.
  15001. * @param {string} [opt.color]
  15002. * @param {string} [opt.extraCssText]
  15003. * @param {string} [opt.type='item'] 'item' or 'subItem'
  15004. * @return {string}
  15005. */
  15006. function getTooltipMarker(opt, extraCssText) {
  15007. opt = isString(opt) ? {color: opt, extraCssText: extraCssText} : (opt || {});
  15008. var color = opt.color;
  15009. var type = opt.type;
  15010. var extraCssText = opt.extraCssText;
  15011. if (!color) {
  15012. return '';
  15013. }
  15014. return type === 'subItem'
  15015. ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;'
  15016. + 'border-radius:4px;width:4px;height:4px;background-color:'
  15017. + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'
  15018. : '<span style="display:inline-block;margin-right:5px;'
  15019. + 'border-radius:10px;width:10px;height:10px;background-color:'
  15020. + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>';
  15021. }
  15022. /**
  15023. * @param {string} str
  15024. * @return {string}
  15025. * @inner
  15026. */
  15027. var s2d = function (str) {
  15028. return str < 10 ? ('0' + str) : str;
  15029. };
  15030. /**
  15031. * ISO Date format
  15032. * @param {string} tpl
  15033. * @param {number} value
  15034. * @param {boolean} [isUTC=false] Default in local time.
  15035. * see `module:echarts/scale/Time`
  15036. * and `module:echarts/util/number#parseDate`.
  15037. * @inner
  15038. */
  15039. function formatTime(tpl, value, isUTC) {
  15040. if (tpl === 'week'
  15041. || tpl === 'month'
  15042. || tpl === 'quarter'
  15043. || tpl === 'half-year'
  15044. || tpl === 'year'
  15045. ) {
  15046. tpl = 'MM-dd\nyyyy';
  15047. }
  15048. var date = parseDate(value);
  15049. var utc = isUTC ? 'UTC' : '';
  15050. var y = date['get' + utc + 'FullYear']();
  15051. var M = date['get' + utc + 'Month']() + 1;
  15052. var d = date['get' + utc + 'Date']();
  15053. var h = date['get' + utc + 'Hours']();
  15054. var m = date['get' + utc + 'Minutes']();
  15055. var s = date['get' + utc + 'Seconds']();
  15056. tpl = tpl.replace('MM', s2d(M))
  15057. .replace('M', M)
  15058. .replace('yyyy', y)
  15059. .replace('yy', y % 100)
  15060. .replace('dd', s2d(d))
  15061. .replace('d', d)
  15062. .replace('hh', s2d(h))
  15063. .replace('h', h)
  15064. .replace('mm', s2d(m))
  15065. .replace('m', m)
  15066. .replace('ss', s2d(s))
  15067. .replace('s', s);
  15068. return tpl;
  15069. }
  15070. /**
  15071. * Capital first
  15072. * @param {string} str
  15073. * @return {string}
  15074. */
  15075. var truncateText$1 = truncateText;
  15076. // Layout helpers for each component positioning
  15077. var each$3 = each$1;
  15078. /**
  15079. * @public
  15080. */
  15081. var LOCATION_PARAMS = [
  15082. 'left', 'right', 'top', 'bottom', 'width', 'height'
  15083. ];
  15084. /**
  15085. * @public
  15086. */
  15087. var HV_NAMES = [
  15088. ['width', 'left', 'right'],
  15089. ['height', 'top', 'bottom']
  15090. ];
  15091. function boxLayout(orient, group, gap, maxWidth, maxHeight) {
  15092. var x = 0;
  15093. var y = 0;
  15094. if (maxWidth == null) {
  15095. maxWidth = Infinity;
  15096. }
  15097. if (maxHeight == null) {
  15098. maxHeight = Infinity;
  15099. }
  15100. var currentLineMaxSize = 0;
  15101. group.eachChild(function (child, idx) {
  15102. var position = child.position;
  15103. var rect = child.getBoundingRect();
  15104. var nextChild = group.childAt(idx + 1);
  15105. var nextChildRect = nextChild && nextChild.getBoundingRect();
  15106. var nextX;
  15107. var nextY;
  15108. if (orient === 'horizontal') {
  15109. var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0);
  15110. nextX = x + moveX;
  15111. // Wrap when width exceeds maxWidth or meet a `newline` group
  15112. // FIXME compare before adding gap?
  15113. if (nextX > maxWidth || child.newline) {
  15114. x = 0;
  15115. nextX = moveX;
  15116. y += currentLineMaxSize + gap;
  15117. currentLineMaxSize = rect.height;
  15118. }
  15119. else {
  15120. // FIXME: consider rect.y is not `0`?
  15121. currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
  15122. }
  15123. }
  15124. else {
  15125. var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0);
  15126. nextY = y + moveY;
  15127. // Wrap when width exceeds maxHeight or meet a `newline` group
  15128. if (nextY > maxHeight || child.newline) {
  15129. x += currentLineMaxSize + gap;
  15130. y = 0;
  15131. nextY = moveY;
  15132. currentLineMaxSize = rect.width;
  15133. }
  15134. else {
  15135. currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
  15136. }
  15137. }
  15138. if (child.newline) {
  15139. return;
  15140. }
  15141. position[0] = x;
  15142. position[1] = y;
  15143. orient === 'horizontal'
  15144. ? (x = nextX + gap)
  15145. : (y = nextY + gap);
  15146. });
  15147. }
  15148. /**
  15149. * VBox or HBox layouting
  15150. * @param {string} orient
  15151. * @param {module:zrender/container/Group} group
  15152. * @param {number} gap
  15153. * @param {number} [width=Infinity]
  15154. * @param {number} [height=Infinity]
  15155. */
  15156. /**
  15157. * VBox layouting
  15158. * @param {module:zrender/container/Group} group
  15159. * @param {number} gap
  15160. * @param {number} [width=Infinity]
  15161. * @param {number} [height=Infinity]
  15162. */
  15163. var vbox = curry(boxLayout, 'vertical');
  15164. /**
  15165. * HBox layouting
  15166. * @param {module:zrender/container/Group} group
  15167. * @param {number} gap
  15168. * @param {number} [width=Infinity]
  15169. * @param {number} [height=Infinity]
  15170. */
  15171. var hbox = curry(boxLayout, 'horizontal');
  15172. /**
  15173. * If x or x2 is not specified or 'center' 'left' 'right',
  15174. * the width would be as long as possible.
  15175. * If y or y2 is not specified or 'middle' 'top' 'bottom',
  15176. * the height would be as long as possible.
  15177. *
  15178. * @param {Object} positionInfo
  15179. * @param {number|string} [positionInfo.x]
  15180. * @param {number|string} [positionInfo.y]
  15181. * @param {number|string} [positionInfo.x2]
  15182. * @param {number|string} [positionInfo.y2]
  15183. * @param {Object} containerRect {width, height}
  15184. * @param {string|number} margin
  15185. * @return {Object} {width, height}
  15186. */
  15187. /**
  15188. * Parse position info.
  15189. *
  15190. * @param {Object} positionInfo
  15191. * @param {number|string} [positionInfo.left]
  15192. * @param {number|string} [positionInfo.top]
  15193. * @param {number|string} [positionInfo.right]
  15194. * @param {number|string} [positionInfo.bottom]
  15195. * @param {number|string} [positionInfo.width]
  15196. * @param {number|string} [positionInfo.height]
  15197. * @param {number|string} [positionInfo.aspect] Aspect is width / height
  15198. * @param {Object} containerRect
  15199. * @param {string|number} [margin]
  15200. *
  15201. * @return {module:zrender/core/BoundingRect}
  15202. */
  15203. function getLayoutRect(
  15204. positionInfo, containerRect, margin
  15205. ) {
  15206. margin = normalizeCssArray$1(margin || 0);
  15207. var containerWidth = containerRect.width;
  15208. var containerHeight = containerRect.height;
  15209. var left = parsePercent$1(positionInfo.left, containerWidth);
  15210. var top = parsePercent$1(positionInfo.top, containerHeight);
  15211. var right = parsePercent$1(positionInfo.right, containerWidth);
  15212. var bottom = parsePercent$1(positionInfo.bottom, containerHeight);
  15213. var width = parsePercent$1(positionInfo.width, containerWidth);
  15214. var height = parsePercent$1(positionInfo.height, containerHeight);
  15215. var verticalMargin = margin[2] + margin[0];
  15216. var horizontalMargin = margin[1] + margin[3];
  15217. var aspect = positionInfo.aspect;
  15218. // If width is not specified, calculate width from left and right
  15219. if (isNaN(width)) {
  15220. width = containerWidth - right - horizontalMargin - left;
  15221. }
  15222. if (isNaN(height)) {
  15223. height = containerHeight - bottom - verticalMargin - top;
  15224. }
  15225. if (aspect != null) {
  15226. // If width and height are not given
  15227. // 1. Graph should not exceeds the container
  15228. // 2. Aspect must be keeped
  15229. // 3. Graph should take the space as more as possible
  15230. // FIXME
  15231. // Margin is not considered, because there is no case that both
  15232. // using margin and aspect so far.
  15233. if (isNaN(width) && isNaN(height)) {
  15234. if (aspect > containerWidth / containerHeight) {
  15235. width = containerWidth * 0.8;
  15236. }
  15237. else {
  15238. height = containerHeight * 0.8;
  15239. }
  15240. }
  15241. // Calculate width or height with given aspect
  15242. if (isNaN(width)) {
  15243. width = aspect * height;
  15244. }
  15245. if (isNaN(height)) {
  15246. height = width / aspect;
  15247. }
  15248. }
  15249. // If left is not specified, calculate left from right and width
  15250. if (isNaN(left)) {
  15251. left = containerWidth - right - width - horizontalMargin;
  15252. }
  15253. if (isNaN(top)) {
  15254. top = containerHeight - bottom - height - verticalMargin;
  15255. }
  15256. // Align left and top
  15257. switch (positionInfo.left || positionInfo.right) {
  15258. case 'center':
  15259. left = containerWidth / 2 - width / 2 - margin[3];
  15260. break;
  15261. case 'right':
  15262. left = containerWidth - width - horizontalMargin;
  15263. break;
  15264. }
  15265. switch (positionInfo.top || positionInfo.bottom) {
  15266. case 'middle':
  15267. case 'center':
  15268. top = containerHeight / 2 - height / 2 - margin[0];
  15269. break;
  15270. case 'bottom':
  15271. top = containerHeight - height - verticalMargin;
  15272. break;
  15273. }
  15274. // If something is wrong and left, top, width, height are calculated as NaN
  15275. left = left || 0;
  15276. top = top || 0;
  15277. if (isNaN(width)) {
  15278. // Width may be NaN if only one value is given except width
  15279. width = containerWidth - horizontalMargin - left - (right || 0);
  15280. }
  15281. if (isNaN(height)) {
  15282. // Height may be NaN if only one value is given except height
  15283. height = containerHeight - verticalMargin - top - (bottom || 0);
  15284. }
  15285. var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);
  15286. rect.margin = margin;
  15287. return rect;
  15288. }
  15289. /**
  15290. * Position a zr element in viewport
  15291. * Group position is specified by either
  15292. * {left, top}, {right, bottom}
  15293. * If all properties exists, right and bottom will be igonred.
  15294. *
  15295. * Logic:
  15296. * 1. Scale (against origin point in parent coord)
  15297. * 2. Rotate (against origin point in parent coord)
  15298. * 3. Traslate (with el.position by this method)
  15299. * So this method only fixes the last step 'Traslate', which does not affect
  15300. * scaling and rotating.
  15301. *
  15302. * If be called repeatly with the same input el, the same result will be gotten.
  15303. *
  15304. * @param {module:zrender/Element} el Should have `getBoundingRect` method.
  15305. * @param {Object} positionInfo
  15306. * @param {number|string} [positionInfo.left]
  15307. * @param {number|string} [positionInfo.top]
  15308. * @param {number|string} [positionInfo.right]
  15309. * @param {number|string} [positionInfo.bottom]
  15310. * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw'
  15311. * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw'
  15312. * @param {Object} containerRect
  15313. * @param {string|number} margin
  15314. * @param {Object} [opt]
  15315. * @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical.
  15316. * @param {Array.<number>} [opt.boundingMode='all']
  15317. * Specify how to calculate boundingRect when locating.
  15318. * 'all': Position the boundingRect that is transformed and uioned
  15319. * both itself and its descendants.
  15320. * This mode simplies confine the elements in the bounding
  15321. * of their container (e.g., using 'right: 0').
  15322. * 'raw': Position the boundingRect that is not transformed and only itself.
  15323. * This mode is useful when you want a element can overflow its
  15324. * container. (Consider a rotated circle needs to be located in a corner.)
  15325. * In this mode positionInfo.width/height can only be number.
  15326. */
  15327. /**
  15328. * @param {Object} option Contains some of the properties in HV_NAMES.
  15329. * @param {number} hvIdx 0: horizontal; 1: vertical.
  15330. */
  15331. /**
  15332. * Consider Case:
  15333. * When defulat option has {left: 0, width: 100}, and we set {right: 0}
  15334. * through setOption or media query, using normal zrUtil.merge will cause
  15335. * {right: 0} does not take effect.
  15336. *
  15337. * @example
  15338. * ComponentModel.extend({
  15339. * init: function () {
  15340. * ...
  15341. * var inputPositionParams = layout.getLayoutParams(option);
  15342. * this.mergeOption(inputPositionParams);
  15343. * },
  15344. * mergeOption: function (newOption) {
  15345. * newOption && zrUtil.merge(thisOption, newOption, true);
  15346. * layout.mergeLayoutParam(thisOption, newOption);
  15347. * }
  15348. * });
  15349. *
  15350. * @param {Object} targetOption
  15351. * @param {Object} newOption
  15352. * @param {Object|string} [opt]
  15353. * @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Used for the components
  15354. * that width (or height) should not be calculated by left and right (or top and bottom).
  15355. */
  15356. function mergeLayoutParam(targetOption, newOption, opt) {
  15357. !isObject$1(opt) && (opt = {});
  15358. var ignoreSize = opt.ignoreSize;
  15359. !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
  15360. var hResult = merge$$1(HV_NAMES[0], 0);
  15361. var vResult = merge$$1(HV_NAMES[1], 1);
  15362. copy(HV_NAMES[0], targetOption, hResult);
  15363. copy(HV_NAMES[1], targetOption, vResult);
  15364. function merge$$1(names, hvIdx) {
  15365. var newParams = {};
  15366. var newValueCount = 0;
  15367. var merged = {};
  15368. var mergedValueCount = 0;
  15369. var enoughParamNumber = 2;
  15370. each$3(names, function (name) {
  15371. merged[name] = targetOption[name];
  15372. });
  15373. each$3(names, function (name) {
  15374. // Consider case: newOption.width is null, which is
  15375. // set by user for removing width setting.
  15376. hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
  15377. hasValue(newParams, name) && newValueCount++;
  15378. hasValue(merged, name) && mergedValueCount++;
  15379. });
  15380. if (ignoreSize[hvIdx]) {
  15381. // Only one of left/right is premitted to exist.
  15382. if (hasValue(newOption, names[1])) {
  15383. merged[names[2]] = null;
  15384. }
  15385. else if (hasValue(newOption, names[2])) {
  15386. merged[names[1]] = null;
  15387. }
  15388. return merged;
  15389. }
  15390. // Case: newOption: {width: ..., right: ...},
  15391. // or targetOption: {right: ...} and newOption: {width: ...},
  15392. // There is no conflict when merged only has params count
  15393. // little than enoughParamNumber.
  15394. if (mergedValueCount === enoughParamNumber || !newValueCount) {
  15395. return merged;
  15396. }
  15397. // Case: newOption: {width: ..., right: ...},
  15398. // Than we can make sure user only want those two, and ignore
  15399. // all origin params in targetOption.
  15400. else if (newValueCount >= enoughParamNumber) {
  15401. return newParams;
  15402. }
  15403. else {
  15404. // Chose another param from targetOption by priority.
  15405. for (var i = 0; i < names.length; i++) {
  15406. var name = names[i];
  15407. if (!hasProp(newParams, name) && hasProp(targetOption, name)) {
  15408. newParams[name] = targetOption[name];
  15409. break;
  15410. }
  15411. }
  15412. return newParams;
  15413. }
  15414. }
  15415. function hasProp(obj, name) {
  15416. return obj.hasOwnProperty(name);
  15417. }
  15418. function hasValue(obj, name) {
  15419. return obj[name] != null && obj[name] !== 'auto';
  15420. }
  15421. function copy(names, target, source) {
  15422. each$3(names, function (name) {
  15423. target[name] = source[name];
  15424. });
  15425. }
  15426. }
  15427. /**
  15428. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  15429. * @param {Object} source
  15430. * @return {Object} Result contains those props.
  15431. */
  15432. function getLayoutParams(source) {
  15433. return copyLayoutParams({}, source);
  15434. }
  15435. /**
  15436. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  15437. * @param {Object} source
  15438. * @return {Object} Result contains those props.
  15439. */
  15440. function copyLayoutParams(target, source) {
  15441. source && target && each$3(LOCATION_PARAMS, function (name) {
  15442. source.hasOwnProperty(name) && (target[name] = source[name]);
  15443. });
  15444. return target;
  15445. }
  15446. var boxLayoutMixin = {
  15447. getBoxLayoutParams: function () {
  15448. return {
  15449. left: this.get('left'),
  15450. top: this.get('top'),
  15451. right: this.get('right'),
  15452. bottom: this.get('bottom'),
  15453. width: this.get('width'),
  15454. height: this.get('height')
  15455. };
  15456. }
  15457. };
  15458. /**
  15459. * Component model
  15460. *
  15461. * @module echarts/model/Component
  15462. */
  15463. var inner$1 = makeInner();
  15464. /**
  15465. * @alias module:echarts/model/Component
  15466. * @constructor
  15467. * @param {Object} option
  15468. * @param {module:echarts/model/Model} parentModel
  15469. * @param {module:echarts/model/Model} ecModel
  15470. */
  15471. var ComponentModel = Model.extend({
  15472. type: 'component',
  15473. /**
  15474. * @readOnly
  15475. * @type {string}
  15476. */
  15477. id: '',
  15478. /**
  15479. * Because simplified concept is probably better, series.name (or component.name)
  15480. * has been having too many resposibilities:
  15481. * (1) Generating id (which requires name in option should not be modified).
  15482. * (2) As an index to mapping series when merging option or calling API (a name
  15483. * can refer to more then one components, which is convinient is some case).
  15484. * (3) Display.
  15485. * @readOnly
  15486. */
  15487. name: '',
  15488. /**
  15489. * @readOnly
  15490. * @type {string}
  15491. */
  15492. mainType: '',
  15493. /**
  15494. * @readOnly
  15495. * @type {string}
  15496. */
  15497. subType: '',
  15498. /**
  15499. * @readOnly
  15500. * @type {number}
  15501. */
  15502. componentIndex: 0,
  15503. /**
  15504. * @type {Object}
  15505. * @protected
  15506. */
  15507. defaultOption: null,
  15508. /**
  15509. * @type {module:echarts/model/Global}
  15510. * @readOnly
  15511. */
  15512. ecModel: null,
  15513. /**
  15514. * key: componentType
  15515. * value: Component model list, can not be null.
  15516. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  15517. * @readOnly
  15518. */
  15519. dependentModels: [],
  15520. /**
  15521. * @type {string}
  15522. * @readOnly
  15523. */
  15524. uid: null,
  15525. /**
  15526. * Support merge layout params.
  15527. * Only support 'box' now (left/right/top/bottom/width/height).
  15528. * @type {string|Object} Object can be {ignoreSize: true}
  15529. * @readOnly
  15530. */
  15531. layoutMode: null,
  15532. $constructor: function (option, parentModel, ecModel, extraOpt) {
  15533. Model.call(this, option, parentModel, ecModel, extraOpt);
  15534. this.uid = getUID('ec_cpt_model');
  15535. },
  15536. init: function (option, parentModel, ecModel, extraOpt) {
  15537. this.mergeDefaultAndTheme(option, ecModel);
  15538. },
  15539. mergeDefaultAndTheme: function (option, ecModel) {
  15540. var layoutMode = this.layoutMode;
  15541. var inputPositionParams = layoutMode
  15542. ? getLayoutParams(option) : {};
  15543. var themeModel = ecModel.getTheme();
  15544. merge(option, themeModel.get(this.mainType));
  15545. merge(option, this.getDefaultOption());
  15546. if (layoutMode) {
  15547. mergeLayoutParam(option, inputPositionParams, layoutMode);
  15548. }
  15549. },
  15550. mergeOption: function (option, extraOpt) {
  15551. merge(this.option, option, true);
  15552. var layoutMode = this.layoutMode;
  15553. if (layoutMode) {
  15554. mergeLayoutParam(this.option, option, layoutMode);
  15555. }
  15556. },
  15557. // Hooker after init or mergeOption
  15558. optionUpdated: function (newCptOption, isInit) {},
  15559. getDefaultOption: function () {
  15560. var fields = inner$1(this);
  15561. if (!fields.defaultOption) {
  15562. var optList = [];
  15563. var Class = this.constructor;
  15564. while (Class) {
  15565. var opt = Class.prototype.defaultOption;
  15566. opt && optList.push(opt);
  15567. Class = Class.superClass;
  15568. }
  15569. var defaultOption = {};
  15570. for (var i = optList.length - 1; i >= 0; i--) {
  15571. defaultOption = merge(defaultOption, optList[i], true);
  15572. }
  15573. fields.defaultOption = defaultOption;
  15574. }
  15575. return fields.defaultOption;
  15576. },
  15577. getReferringComponents: function (mainType) {
  15578. return this.ecModel.queryComponents({
  15579. mainType: mainType,
  15580. index: this.get(mainType + 'Index', true),
  15581. id: this.get(mainType + 'Id', true)
  15582. });
  15583. }
  15584. });
  15585. // Reset ComponentModel.extend, add preConstruct.
  15586. // clazzUtil.enableClassExtend(
  15587. // ComponentModel,
  15588. // function (option, parentModel, ecModel, extraOpt) {
  15589. // // Set dependentModels, componentIndex, name, id, mainType, subType.
  15590. // zrUtil.extend(this, extraOpt);
  15591. // this.uid = componentUtil.getUID('componentModel');
  15592. // // this.setReadOnly([
  15593. // // 'type', 'id', 'uid', 'name', 'mainType', 'subType',
  15594. // // 'dependentModels', 'componentIndex'
  15595. // // ]);
  15596. // }
  15597. // );
  15598. // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  15599. enableClassManagement(
  15600. ComponentModel, {registerWhenExtend: true}
  15601. );
  15602. enableSubTypeDefaulter(ComponentModel);
  15603. // Add capability of ComponentModel.topologicalTravel.
  15604. enableTopologicalTravel(ComponentModel, getDependencies);
  15605. function getDependencies(componentType) {
  15606. var deps = [];
  15607. each$1(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
  15608. deps = deps.concat(Clazz.prototype.dependencies || []);
  15609. });
  15610. // Ensure main type.
  15611. deps = map(deps, function (type) {
  15612. return parseClassType$1(type).main;
  15613. });
  15614. // Hack dataset for convenience.
  15615. if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) {
  15616. deps.unshift('dataset');
  15617. }
  15618. return deps;
  15619. }
  15620. mixin(ComponentModel, boxLayoutMixin);
  15621. var platform = '';
  15622. // Navigator not exists in node
  15623. if (typeof navigator !== 'undefined') {
  15624. platform = navigator.platform || '';
  15625. }
  15626. var globalDefault = {
  15627. // 全图默认背景
  15628. // backgroundColor: 'rgba(0,0,0,0)',
  15629. // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization
  15630. // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],
  15631. // 浅色
  15632. // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],
  15633. // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],
  15634. // 深色
  15635. color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],
  15636. gradientColor: ['#f6efa6', '#d88273', '#bf444c'],
  15637. // 默认需要 Grid 配置项
  15638. // grid: {},
  15639. // 主题,主题
  15640. textStyle: {
  15641. // color: '#000',
  15642. // decoration: 'none',
  15643. // PENDING
  15644. fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',
  15645. // fontFamily: 'Arial, Verdana, sans-serif',
  15646. fontSize: 12,
  15647. fontStyle: 'normal',
  15648. fontWeight: 'normal'
  15649. },
  15650. // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/
  15651. // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  15652. // Default is source-over
  15653. blendMode: null,
  15654. animation: 'auto',
  15655. animationDuration: 1000,
  15656. animationDurationUpdate: 300,
  15657. animationEasing: 'exponentialOut',
  15658. animationEasingUpdate: 'cubicOut',
  15659. animationThreshold: 2000,
  15660. // Configuration for progressive/incremental rendering
  15661. progressiveThreshold: 3000,
  15662. progressive: 400,
  15663. // Threshold of if use single hover layer to optimize.
  15664. // It is recommended that `hoverLayerThreshold` is equivalent to or less than
  15665. // `progressiveThreshold`, otherwise hover will cause restart of progressive,
  15666. // which is unexpected.
  15667. // see example <echarts/test/heatmap-large.html>.
  15668. hoverLayerThreshold: 3000,
  15669. // See: module:echarts/scale/Time
  15670. useUTC: false
  15671. };
  15672. var inner$2 = makeInner();
  15673. function getNearestColorPalette(colors, requestColorNum) {
  15674. var paletteNum = colors.length;
  15675. // TODO colors must be in order
  15676. for (var i = 0; i < paletteNum; i++) {
  15677. if (colors[i].length > requestColorNum) {
  15678. return colors[i];
  15679. }
  15680. }
  15681. return colors[paletteNum - 1];
  15682. }
  15683. var colorPaletteMixin = {
  15684. clearColorPalette: function () {
  15685. inner$2(this).colorIdx = 0;
  15686. inner$2(this).colorNameMap = {};
  15687. },
  15688. getColorFromPalette: function (name, scope, requestColorNum) {
  15689. scope = scope || this;
  15690. var scopeFields = inner$2(scope);
  15691. var colorIdx = scopeFields.colorIdx || 0;
  15692. var colorNameMap = scopeFields.colorNameMap = scopeFields.colorNameMap || {};
  15693. // Use `hasOwnProperty` to avoid conflict with Object.prototype.
  15694. if (colorNameMap.hasOwnProperty(name)) {
  15695. return colorNameMap[name];
  15696. }
  15697. var defaultColorPalette = normalizeToArray(this.get('color', true));
  15698. var layeredColorPalette = this.get('colorLayer', true);
  15699. var colorPalette = ((requestColorNum == null || !layeredColorPalette)
  15700. ? defaultColorPalette : getNearestColorPalette(layeredColorPalette, requestColorNum));
  15701. // In case can't find in layered color palette.
  15702. colorPalette = colorPalette || defaultColorPalette;
  15703. if (!colorPalette || !colorPalette.length) {
  15704. return;
  15705. }
  15706. var color = colorPalette[colorIdx];
  15707. if (name) {
  15708. colorNameMap[name] = color;
  15709. }
  15710. scopeFields.colorIdx = (colorIdx + 1) % colorPalette.length;
  15711. return color;
  15712. }
  15713. };
  15714. /**
  15715. * Helper for model references.
  15716. * There are many manners to refer axis/coordSys.
  15717. */
  15718. // TODO
  15719. // merge relevant logic to this file?
  15720. // check: "modelHelper" of tooltip and "BrushTargetManager".
  15721. /**
  15722. * @return {Object} For example:
  15723. * {
  15724. * coordSysName: 'cartesian2d',
  15725. * coordSysDims: ['x', 'y', ...],
  15726. * axisMap: HashMap({
  15727. * x: xAxisModel,
  15728. * y: yAxisModel
  15729. * }),
  15730. * categoryAxisMap: HashMap({
  15731. * x: xAxisModel,
  15732. * y: undefined
  15733. * }),
  15734. * // It also indicate that whether there is category axis.
  15735. * firstCategoryDimIndex: 1,
  15736. * // To replace user specified encode.
  15737. * }
  15738. */
  15739. function getCoordSysDefineBySeries(seriesModel) {
  15740. var coordSysName = seriesModel.get('coordinateSystem');
  15741. var result = {
  15742. coordSysName: coordSysName,
  15743. coordSysDims: [],
  15744. axisMap: createHashMap(),
  15745. categoryAxisMap: createHashMap()
  15746. };
  15747. var fetch = fetchers[coordSysName];
  15748. if (fetch) {
  15749. fetch(seriesModel, result, result.axisMap, result.categoryAxisMap);
  15750. return result;
  15751. }
  15752. }
  15753. var fetchers = {
  15754. cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) {
  15755. var xAxisModel = seriesModel.getReferringComponents('xAxis')[0];
  15756. var yAxisModel = seriesModel.getReferringComponents('yAxis')[0];
  15757. if (__DEV__) {
  15758. if (!xAxisModel) {
  15759. throw new Error('xAxis "' + retrieve(
  15760. seriesModel.get('xAxisIndex'),
  15761. seriesModel.get('xAxisId'),
  15762. 0
  15763. ) + '" not found');
  15764. }
  15765. if (!yAxisModel) {
  15766. throw new Error('yAxis "' + retrieve(
  15767. seriesModel.get('xAxisIndex'),
  15768. seriesModel.get('yAxisId'),
  15769. 0
  15770. ) + '" not found');
  15771. }
  15772. }
  15773. result.coordSysDims = ['x', 'y'];
  15774. axisMap.set('x', xAxisModel);
  15775. axisMap.set('y', yAxisModel);
  15776. if (isCategory(xAxisModel)) {
  15777. categoryAxisMap.set('x', xAxisModel);
  15778. result.firstCategoryDimIndex = 0;
  15779. }
  15780. if (isCategory(yAxisModel)) {
  15781. categoryAxisMap.set('y', yAxisModel);
  15782. result.firstCategoryDimIndex = 1;
  15783. }
  15784. },
  15785. singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) {
  15786. var singleAxisModel = seriesModel.getReferringComponents('singleAxis')[0];
  15787. if (__DEV__) {
  15788. if (!singleAxisModel) {
  15789. throw new Error('singleAxis should be specified.');
  15790. }
  15791. }
  15792. result.coordSysDims = ['single'];
  15793. axisMap.set('single', singleAxisModel);
  15794. if (isCategory(singleAxisModel)) {
  15795. categoryAxisMap.set('single', singleAxisModel);
  15796. result.firstCategoryDimIndex = 0;
  15797. }
  15798. },
  15799. polar: function (seriesModel, result, axisMap, categoryAxisMap) {
  15800. var polarModel = seriesModel.getReferringComponents('polar')[0];
  15801. var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
  15802. var angleAxisModel = polarModel.findAxisModel('angleAxis');
  15803. if (__DEV__) {
  15804. if (!angleAxisModel) {
  15805. throw new Error('angleAxis option not found');
  15806. }
  15807. if (!radiusAxisModel) {
  15808. throw new Error('radiusAxis option not found');
  15809. }
  15810. }
  15811. result.coordSysDims = ['radius', 'angle'];
  15812. axisMap.set('radius', radiusAxisModel);
  15813. axisMap.set('angle', angleAxisModel);
  15814. if (isCategory(radiusAxisModel)) {
  15815. categoryAxisMap.set('radius', radiusAxisModel);
  15816. result.firstCategoryDimIndex = 0;
  15817. }
  15818. if (isCategory(angleAxisModel)) {
  15819. categoryAxisMap.set('angle', angleAxisModel);
  15820. result.firstCategoryDimIndex = 1;
  15821. }
  15822. },
  15823. geo: function (seriesModel, result, axisMap, categoryAxisMap) {
  15824. result.coordSysDims = ['lng', 'lat'];
  15825. },
  15826. parallel: function (seriesModel, result, axisMap, categoryAxisMap) {
  15827. var ecModel = seriesModel.ecModel;
  15828. var parallelModel = ecModel.getComponent(
  15829. 'parallel', seriesModel.get('parallelIndex')
  15830. );
  15831. var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice();
  15832. each$1(parallelModel.parallelAxisIndex, function (axisIndex, index) {
  15833. var axisModel = ecModel.getComponent('parallelAxis', axisIndex);
  15834. var axisDim = coordSysDims[index];
  15835. axisMap.set(axisDim, axisModel);
  15836. if (isCategory(axisModel) && result.firstCategoryDimIndex == null) {
  15837. categoryAxisMap.set(axisDim, axisModel);
  15838. result.firstCategoryDimIndex = index;
  15839. }
  15840. });
  15841. }
  15842. };
  15843. function isCategory(axisModel) {
  15844. return axisModel.get('type') === 'category';
  15845. }
  15846. // Avoid typo.
  15847. var SOURCE_FORMAT_ORIGINAL = 'original';
  15848. var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';
  15849. var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';
  15850. var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';
  15851. var SOURCE_FORMAT_UNKNOWN = 'unknown';
  15852. // ??? CHANGE A NAME
  15853. var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';
  15854. var SERIES_LAYOUT_BY_COLUMN = 'column';
  15855. var SERIES_LAYOUT_BY_ROW = 'row';
  15856. /**
  15857. * [sourceFormat]
  15858. *
  15859. * + "original":
  15860. * This format is only used in series.data, where
  15861. * itemStyle can be specified in data item.
  15862. *
  15863. * + "arrayRows":
  15864. * [
  15865. * ['product', 'score', 'amount'],
  15866. * ['Matcha Latte', 89.3, 95.8],
  15867. * ['Milk Tea', 92.1, 89.4],
  15868. * ['Cheese Cocoa', 94.4, 91.2],
  15869. * ['Walnut Brownie', 85.4, 76.9]
  15870. * ]
  15871. *
  15872. * + "objectRows":
  15873. * [
  15874. * {product: 'Matcha Latte', score: 89.3, amount: 95.8},
  15875. * {product: 'Milk Tea', score: 92.1, amount: 89.4},
  15876. * {product: 'Cheese Cocoa', score: 94.4, amount: 91.2},
  15877. * {product: 'Walnut Brownie', score: 85.4, amount: 76.9}
  15878. * ]
  15879. *
  15880. * + "keyedColumns":
  15881. * {
  15882. * 'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'],
  15883. * 'count': [823, 235, 1042, 988],
  15884. * 'score': [95.8, 81.4, 91.2, 76.9]
  15885. * }
  15886. *
  15887. * + "typedArray"
  15888. *
  15889. * + "unknown"
  15890. */
  15891. /**
  15892. * @constructor
  15893. * @param {Object} fields
  15894. * @param {string} fields.sourceFormat
  15895. * @param {Array|Object} fields.fromDataset
  15896. * @param {Array|Object} [fields.data]
  15897. * @param {string} [seriesLayoutBy='column']
  15898. * @param {Array.<Object|string>} [dimensionsDefine]
  15899. * @param {Objet|HashMap} [encodeDefine]
  15900. * @param {number} [startIndex=0]
  15901. * @param {number} [dimensionsDetectCount]
  15902. */
  15903. function Source(fields) {
  15904. /**
  15905. * @type {boolean}
  15906. */
  15907. this.fromDataset = fields.fromDataset;
  15908. /**
  15909. * Not null/undefined.
  15910. * @type {Array|Object}
  15911. */
  15912. this.data = fields.data || (
  15913. fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []
  15914. );
  15915. /**
  15916. * See also "detectSourceFormat".
  15917. * Not null/undefined.
  15918. * @type {string}
  15919. */
  15920. this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN;
  15921. /**
  15922. * 'row' or 'column'
  15923. * Not null/undefined.
  15924. * @type {string} seriesLayoutBy
  15925. */
  15926. this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
  15927. /**
  15928. * dimensions definition in option.
  15929. * can be null/undefined.
  15930. * @type {Array.<Object|string>}
  15931. */
  15932. this.dimensionsDefine = fields.dimensionsDefine;
  15933. /**
  15934. * encode definition in option.
  15935. * can be null/undefined.
  15936. * @type {Objet|HashMap}
  15937. */
  15938. this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine);
  15939. /**
  15940. * Not null/undefined, uint.
  15941. * @type {number}
  15942. */
  15943. this.startIndex = fields.startIndex || 0;
  15944. /**
  15945. * Can be null/undefined (when unknown), uint.
  15946. * @type {number}
  15947. */
  15948. this.dimensionsDetectCount = fields.dimensionsDetectCount;
  15949. }
  15950. /**
  15951. * Wrap original series data for some compatibility cases.
  15952. */
  15953. Source.seriesDataToSource = function (data) {
  15954. return new Source({
  15955. data: data,
  15956. sourceFormat: isTypedArray(data)
  15957. ? SOURCE_FORMAT_TYPED_ARRAY
  15958. : SOURCE_FORMAT_ORIGINAL,
  15959. fromDataset: false
  15960. });
  15961. };
  15962. enableClassCheck(Source);
  15963. var inner$3 = makeInner();
  15964. /**
  15965. * @see {module:echarts/data/Source}
  15966. * @param {module:echarts/component/dataset/DatasetModel} datasetModel
  15967. * @return {string} sourceFormat
  15968. */
  15969. function detectSourceFormat(datasetModel) {
  15970. var data = datasetModel.option.source;
  15971. var sourceFormat = SOURCE_FORMAT_UNKNOWN;
  15972. if (isTypedArray(data)) {
  15973. sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;
  15974. }
  15975. else if (isArray(data)) {
  15976. // FIXME Whether tolerate null in top level array?
  15977. for (var i = 0, len = data.length; i < len; i++) {
  15978. var item = data[i];
  15979. if (item == null) {
  15980. continue;
  15981. }
  15982. else if (isArray(item)) {
  15983. sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;
  15984. break;
  15985. }
  15986. else if (isObject$1(item)) {
  15987. sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;
  15988. break;
  15989. }
  15990. }
  15991. }
  15992. else if (isObject$1(data)) {
  15993. for (var key in data) {
  15994. if (data.hasOwnProperty(key) && isArrayLike(data[key])) {
  15995. sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;
  15996. break;
  15997. }
  15998. }
  15999. }
  16000. else if (data != null) {
  16001. throw new Error('Invalid data');
  16002. }
  16003. inner$3(datasetModel).sourceFormat = sourceFormat;
  16004. }
  16005. /**
  16006. * [Scenarios]:
  16007. * (1) Provide source data directly:
  16008. * series: {
  16009. * encode: {...},
  16010. * dimensions: [...]
  16011. * seriesLayoutBy: 'row',
  16012. * data: [[...]]
  16013. * }
  16014. * (2) Refer to datasetModel.
  16015. * series: [{
  16016. * encode: {...}
  16017. * // Ignore datasetIndex means `datasetIndex: 0`
  16018. * // and the dimensions defination in dataset is used
  16019. * }, {
  16020. * encode: {...},
  16021. * seriesLayoutBy: 'column',
  16022. * datasetIndex: 1
  16023. * }]
  16024. *
  16025. * Get data from series itself or datset.
  16026. * @return {module:echarts/data/Source} source
  16027. */
  16028. function getSource(seriesModel) {
  16029. return inner$3(seriesModel).source;
  16030. }
  16031. /**
  16032. * MUST be called before mergeOption of all series.
  16033. * @param {module:echarts/model/Global} ecModel
  16034. */
  16035. function resetSourceDefaulter(ecModel) {
  16036. // `datasetMap` is used to make default encode.
  16037. inner$3(ecModel).datasetMap = createHashMap();
  16038. }
  16039. /**
  16040. * [Caution]:
  16041. * MUST be called after series option merged and
  16042. * before "series.getInitailData()" called.
  16043. *
  16044. * [The rule of making default encode]:
  16045. * Category axis (if exists) alway map to the first dimension.
  16046. * Each other axis occupies a subsequent dimension.
  16047. *
  16048. * [Why make default encode]:
  16049. * Simplify the typing of encode in option, avoiding the case like that:
  16050. * series: [{encode: {x: 0, y: 1}}, {encode: {x: 0, y: 2}}, {encode: {x: 0, y: 3}}],
  16051. * where the "y" have to be manually typed as "1, 2, 3, ...".
  16052. *
  16053. * @param {module:echarts/model/Series} seriesModel
  16054. */
  16055. function prepareSource(seriesModel) {
  16056. var seriesOption = seriesModel.option;
  16057. var data = seriesOption.data;
  16058. var sourceFormat = isTypedArray(data)
  16059. ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;
  16060. var fromDataset = false;
  16061. var seriesLayoutBy = seriesOption.seriesLayoutBy;
  16062. var sourceHeader = seriesOption.sourceHeader;
  16063. var dimensionsDefine = seriesOption.dimensions;
  16064. var datasetModel = getDatasetModel(seriesModel);
  16065. if (datasetModel) {
  16066. var datasetOption = datasetModel.option;
  16067. data = datasetOption.source;
  16068. sourceFormat = inner$3(datasetModel).sourceFormat;
  16069. fromDataset = true;
  16070. // These settings from series has higher priority.
  16071. seriesLayoutBy = seriesLayoutBy || datasetOption.seriesLayoutBy;
  16072. sourceHeader == null && (sourceHeader = datasetOption.sourceHeader);
  16073. dimensionsDefine = dimensionsDefine || datasetOption.dimensions;
  16074. }
  16075. var completeResult = completeBySourceData(
  16076. data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine
  16077. );
  16078. // Note: dataset option does not have `encode`.
  16079. var encodeDefine = seriesOption.encode;
  16080. if (!encodeDefine && datasetModel) {
  16081. encodeDefine = makeDefaultEncode(
  16082. seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult
  16083. );
  16084. }
  16085. inner$3(seriesModel).source = new Source({
  16086. data: data,
  16087. fromDataset: fromDataset,
  16088. seriesLayoutBy: seriesLayoutBy,
  16089. sourceFormat: sourceFormat,
  16090. dimensionsDefine: completeResult.dimensionsDefine,
  16091. startIndex: completeResult.startIndex,
  16092. dimensionsDetectCount: completeResult.dimensionsDetectCount,
  16093. encodeDefine: encodeDefine
  16094. });
  16095. }
  16096. // return {startIndex, dimensionsDefine, dimensionsCount}
  16097. function completeBySourceData(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine) {
  16098. if (!data) {
  16099. return {dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine)};
  16100. }
  16101. var dimensionsDetectCount;
  16102. var startIndex;
  16103. var findPotentialName;
  16104. if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
  16105. // Rule: Most of the first line are string: it is header.
  16106. // Caution: consider a line with 5 string and 1 number,
  16107. // it still can not be sure it is a head, because the
  16108. // 5 string may be 5 values of category columns.
  16109. if (sourceHeader === 'auto' || sourceHeader == null) {
  16110. arrayRowsTravelFirst(function (val) {
  16111. // '-' is regarded as null/undefined.
  16112. if (val != null && val !== '-') {
  16113. if (isString(val)) {
  16114. startIndex == null && (startIndex = 1);
  16115. }
  16116. else {
  16117. startIndex = 0;
  16118. }
  16119. }
  16120. // 10 is an experience number, avoid long loop.
  16121. }, seriesLayoutBy, data, 10);
  16122. }
  16123. else {
  16124. startIndex = sourceHeader ? 1 : 0;
  16125. }
  16126. if (!dimensionsDefine && startIndex === 1) {
  16127. dimensionsDefine = [];
  16128. arrayRowsTravelFirst(function (val, index) {
  16129. dimensionsDefine[index] = val != null ? val : '';
  16130. }, seriesLayoutBy, data);
  16131. }
  16132. dimensionsDetectCount = dimensionsDefine
  16133. ? dimensionsDefine.length
  16134. : seriesLayoutBy === SERIES_LAYOUT_BY_ROW
  16135. ? data.length
  16136. : data[0]
  16137. ? data[0].length
  16138. : null;
  16139. }
  16140. else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
  16141. if (!dimensionsDefine) {
  16142. dimensionsDefine = objectRowsCollectDimensions(data);
  16143. findPotentialName = true;
  16144. }
  16145. }
  16146. else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
  16147. if (!dimensionsDefine) {
  16148. dimensionsDefine = [];
  16149. findPotentialName = true;
  16150. each$1(data, function (colArr, key) {
  16151. dimensionsDefine.push(key);
  16152. });
  16153. }
  16154. }
  16155. else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
  16156. var value0 = getDataItemValue(data[0]);
  16157. dimensionsDetectCount = isArray(value0) && value0.length || 1;
  16158. }
  16159. else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
  16160. if (__DEV__) {
  16161. assert$1(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.');
  16162. }
  16163. }
  16164. var potentialNameDimIndex;
  16165. if (findPotentialName) {
  16166. each$1(dimensionsDefine, function (dim, idx) {
  16167. if ((isObject$1(dim) ? dim.name : dim) === 'name') {
  16168. potentialNameDimIndex = idx;
  16169. }
  16170. });
  16171. }
  16172. return {
  16173. startIndex: startIndex,
  16174. dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine),
  16175. dimensionsDetectCount: dimensionsDetectCount,
  16176. potentialNameDimIndex: potentialNameDimIndex
  16177. // TODO: potentialIdDimIdx
  16178. };
  16179. }
  16180. // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],
  16181. // which is reasonable. But dimension name is duplicated.
  16182. // Returns undefined or an array contains only object without null/undefiend or string.
  16183. function normalizeDimensionsDefine(dimensionsDefine) {
  16184. if (!dimensionsDefine) {
  16185. // The meaning of null/undefined is different from empty array.
  16186. return;
  16187. }
  16188. var nameMap = createHashMap();
  16189. return map(dimensionsDefine, function (item, index) {
  16190. item = extend({}, isObject$1(item) ? item : {name: item});
  16191. // User can set null in dimensions.
  16192. // We dont auto specify name, othewise a given name may
  16193. // cause it be refered unexpectedly.
  16194. if (item.name == null) {
  16195. return item;
  16196. }
  16197. // Also consider number form like 2012.
  16198. item.name += '';
  16199. // User may also specify displayName.
  16200. // displayName will always exists except user not
  16201. // specified or dim name is not specified or detected.
  16202. // (A auto generated dim name will not be used as
  16203. // displayName).
  16204. if (item.displayName == null) {
  16205. item.displayName = item.name;
  16206. }
  16207. var exist = nameMap.get(item.name);
  16208. if (!exist) {
  16209. nameMap.set(item.name, {count: 1});
  16210. }
  16211. else {
  16212. item.name += '-' + exist.count++;
  16213. }
  16214. return item;
  16215. });
  16216. }
  16217. function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {
  16218. maxLoop == null && (maxLoop = Infinity);
  16219. if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
  16220. for (var i = 0; i < data.length && i < maxLoop; i++) {
  16221. cb(data[i] ? data[i][0] : null, i);
  16222. }
  16223. }
  16224. else {
  16225. var value0 = data[0] || [];
  16226. for (var i = 0; i < value0.length && i < maxLoop; i++) {
  16227. cb(value0[i], i);
  16228. }
  16229. }
  16230. }
  16231. function objectRowsCollectDimensions(data) {
  16232. var firstIndex = 0;
  16233. var obj;
  16234. while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line
  16235. if (obj) {
  16236. var dimensions = [];
  16237. each$1(obj, function (value, key) {
  16238. dimensions.push(key);
  16239. });
  16240. return dimensions;
  16241. }
  16242. }
  16243. // ??? TODO merge to completedimensions, where also has
  16244. // default encode making logic. And the default rule
  16245. // should depends on series? consider 'map'.
  16246. function makeDefaultEncode(
  16247. seriesModel, datasetModel, data, sourceFormat, seriesLayoutBy, completeResult
  16248. ) {
  16249. var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
  16250. var encode = {};
  16251. // var encodeTooltip = [];
  16252. // var encodeLabel = [];
  16253. var encodeItemName = [];
  16254. var encodeSeriesName = [];
  16255. var seriesType = seriesModel.subType;
  16256. // ??? TODO refactor: provide by series itself.
  16257. // Consider the case: 'map' series is based on geo coordSys,
  16258. // 'graph', 'heatmap' can be based on cartesian. But can not
  16259. // give default rule simply here.
  16260. var nSeriesMap = createHashMap(['pie', 'map', 'funnel']);
  16261. var cSeriesMap = createHashMap([
  16262. 'line', 'bar', 'pictorialBar', 'scatter', 'effectScatter', 'candlestick', 'boxplot'
  16263. ]);
  16264. // Usually in this case series will use the first data
  16265. // dimension as the "value" dimension, or other default
  16266. // processes respectively.
  16267. if (coordSysDefine && cSeriesMap.get(seriesType) != null) {
  16268. var ecModel = seriesModel.ecModel;
  16269. var datasetMap = inner$3(ecModel).datasetMap;
  16270. var key = datasetModel.uid + '_' + seriesLayoutBy;
  16271. var datasetRecord = datasetMap.get(key)
  16272. || datasetMap.set(key, {categoryWayDim: 1, valueWayDim: 0});
  16273. // TODO
  16274. // Auto detect first time axis and do arrangement.
  16275. each$1(coordSysDefine.coordSysDims, function (coordDim) {
  16276. // In value way.
  16277. if (coordSysDefine.firstCategoryDimIndex == null) {
  16278. var dataDim = datasetRecord.valueWayDim++;
  16279. encode[coordDim] = dataDim;
  16280. // ??? TODO give a better default series name rule?
  16281. // especially when encode x y specified.
  16282. // consider: when mutiple series share one dimension
  16283. // category axis, series name should better use
  16284. // the other dimsion name. On the other hand, use
  16285. // both dimensions name.
  16286. encodeSeriesName.push(dataDim);
  16287. // encodeTooltip.push(dataDim);
  16288. // encodeLabel.push(dataDim);
  16289. }
  16290. // In category way, category axis.
  16291. else if (coordSysDefine.categoryAxisMap.get(coordDim)) {
  16292. encode[coordDim] = 0;
  16293. encodeItemName.push(0);
  16294. }
  16295. // In category way, non-category axis.
  16296. else {
  16297. var dataDim = datasetRecord.categoryWayDim++;
  16298. encode[coordDim] = dataDim;
  16299. // encodeTooltip.push(dataDim);
  16300. // encodeLabel.push(dataDim);
  16301. encodeSeriesName.push(dataDim);
  16302. }
  16303. });
  16304. }
  16305. // Do not make a complex rule! Hard to code maintain and not necessary.
  16306. // ??? TODO refactor: provide by series itself.
  16307. // [{name: ..., value: ...}, ...] like:
  16308. else if (nSeriesMap.get(seriesType) != null) {
  16309. // Find the first not ordinal. (5 is an experience value)
  16310. var firstNotOrdinal;
  16311. for (var i = 0; i < 5 && firstNotOrdinal == null; i++) {
  16312. if (!doGuessOrdinal(
  16313. data, sourceFormat, seriesLayoutBy,
  16314. completeResult.dimensionsDefine, completeResult.startIndex, i
  16315. )) {
  16316. firstNotOrdinal = i;
  16317. }
  16318. }
  16319. if (firstNotOrdinal != null) {
  16320. encode.value = firstNotOrdinal;
  16321. var nameDimIndex = completeResult.potentialNameDimIndex
  16322. || Math.max(firstNotOrdinal - 1, 0);
  16323. // By default, label use itemName in charts.
  16324. // So we dont set encodeLabel here.
  16325. encodeSeriesName.push(nameDimIndex);
  16326. encodeItemName.push(nameDimIndex);
  16327. // encodeTooltip.push(firstNotOrdinal);
  16328. }
  16329. }
  16330. // encodeTooltip.length && (encode.tooltip = encodeTooltip);
  16331. // encodeLabel.length && (encode.label = encodeLabel);
  16332. encodeItemName.length && (encode.itemName = encodeItemName);
  16333. encodeSeriesName.length && (encode.seriesName = encodeSeriesName);
  16334. return encode;
  16335. }
  16336. /**
  16337. * If return null/undefined, indicate that should not use datasetModel.
  16338. */
  16339. function getDatasetModel(seriesModel) {
  16340. var option = seriesModel.option;
  16341. // Caution: consider the scenario:
  16342. // A dataset is declared and a series is not expected to use the dataset,
  16343. // and at the beginning `setOption({series: { noData })` (just prepare other
  16344. // option but no data), then `setOption({series: {data: [...]}); In this case,
  16345. // the user should set an empty array to avoid that dataset is used by default.
  16346. var thisData = option.data;
  16347. if (!thisData) {
  16348. return seriesModel.ecModel.getComponent('dataset', option.datasetIndex || 0);
  16349. }
  16350. }
  16351. /**
  16352. * The rule should not be complex, otherwise user might not
  16353. * be able to known where the data is wrong.
  16354. * The code is ugly, but how to make it neat?
  16355. *
  16356. * @param {module:echars/data/Source} source
  16357. * @param {number} dimIndex
  16358. * @return {boolean} Whether ordinal.
  16359. */
  16360. function guessOrdinal(source, dimIndex) {
  16361. return doGuessOrdinal(
  16362. source.data,
  16363. source.sourceFormat,
  16364. source.seriesLayoutBy,
  16365. source.dimensionsDefine,
  16366. source.startIndex,
  16367. dimIndex
  16368. );
  16369. }
  16370. // dimIndex may be overflow source data.
  16371. function doGuessOrdinal(
  16372. data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex
  16373. ) {
  16374. var result;
  16375. // Experience value.
  16376. var maxLoop = 5;
  16377. if (isTypedArray(data)) {
  16378. return false;
  16379. }
  16380. // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine
  16381. // always exists in source.
  16382. var dimName;
  16383. if (dimensionsDefine) {
  16384. dimName = dimensionsDefine[dimIndex];
  16385. dimName = isObject$1(dimName) ? dimName.name : dimName;
  16386. }
  16387. if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
  16388. if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
  16389. var sample = data[dimIndex];
  16390. for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {
  16391. if ((result = detectValue(sample[startIndex + i])) != null) {
  16392. return result;
  16393. }
  16394. }
  16395. }
  16396. else {
  16397. for (var i = 0; i < data.length && i < maxLoop; i++) {
  16398. var row = data[startIndex + i];
  16399. if (row && (result = detectValue(row[dimIndex])) != null) {
  16400. return result;
  16401. }
  16402. }
  16403. }
  16404. }
  16405. else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
  16406. if (!dimName) {
  16407. return;
  16408. }
  16409. for (var i = 0; i < data.length && i < maxLoop; i++) {
  16410. var item = data[i];
  16411. if (item && (result = detectValue(item[dimName])) != null) {
  16412. return result;
  16413. }
  16414. }
  16415. }
  16416. else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
  16417. if (!dimName) {
  16418. return;
  16419. }
  16420. var sample = data[dimName];
  16421. if (!sample || isTypedArray(sample)) {
  16422. return false;
  16423. }
  16424. for (var i = 0; i < sample.length && i < maxLoop; i++) {
  16425. if ((result = detectValue(sample[i])) != null) {
  16426. return result;
  16427. }
  16428. }
  16429. }
  16430. else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
  16431. for (var i = 0; i < data.length && i < maxLoop; i++) {
  16432. var item = data[i];
  16433. var val = getDataItemValue(item);
  16434. if (!isArray(val)) {
  16435. return false;
  16436. }
  16437. if ((result = detectValue(val[dimIndex])) != null) {
  16438. return result;
  16439. }
  16440. }
  16441. }
  16442. function detectValue(val) {
  16443. // Consider usage convenience, '1', '2' will be treated as "number".
  16444. // `isFinit('')` get `true`.
  16445. if (val != null && isFinite(val) && val !== '') {
  16446. return false;
  16447. }
  16448. else if (isString(val) && val !== '-') {
  16449. return true;
  16450. }
  16451. }
  16452. return false;
  16453. }
  16454. /**
  16455. * ECharts global model
  16456. *
  16457. * @module {echarts/model/Global}
  16458. */
  16459. /**
  16460. * Caution: If the mechanism should be changed some day, these cases
  16461. * should be considered:
  16462. *
  16463. * (1) In `merge option` mode, if using the same option to call `setOption`
  16464. * many times, the result should be the same (try our best to ensure that).
  16465. * (2) In `merge option` mode, if a component has no id/name specified, it
  16466. * will be merged by index, and the result sequence of the components is
  16467. * consistent to the original sequence.
  16468. * (3) `reset` feature (in toolbox). Find detailed info in comments about
  16469. * `mergeOption` in module:echarts/model/OptionManager.
  16470. */
  16471. var OPTION_INNER_KEY = '\0_ec_inner';
  16472. /**
  16473. * @alias module:echarts/model/Global
  16474. *
  16475. * @param {Object} option
  16476. * @param {module:echarts/model/Model} parentModel
  16477. * @param {Object} theme
  16478. */
  16479. var GlobalModel = Model.extend({
  16480. constructor: GlobalModel,
  16481. init: function (option, parentModel, theme, optionManager) {
  16482. theme = theme || {};
  16483. this.option = null; // Mark as not initialized.
  16484. /**
  16485. * @type {module:echarts/model/Model}
  16486. * @private
  16487. */
  16488. this._theme = new Model(theme);
  16489. /**
  16490. * @type {module:echarts/model/OptionManager}
  16491. */
  16492. this._optionManager = optionManager;
  16493. },
  16494. setOption: function (option, optionPreprocessorFuncs) {
  16495. assert$1(
  16496. !(OPTION_INNER_KEY in option),
  16497. 'please use chart.getOption()'
  16498. );
  16499. this._optionManager.setOption(option, optionPreprocessorFuncs);
  16500. this.resetOption(null);
  16501. },
  16502. /**
  16503. * @param {string} type null/undefined: reset all.
  16504. * 'recreate': force recreate all.
  16505. * 'timeline': only reset timeline option
  16506. * 'media': only reset media query option
  16507. * @return {boolean} Whether option changed.
  16508. */
  16509. resetOption: function (type) {
  16510. var optionChanged = false;
  16511. var optionManager = this._optionManager;
  16512. if (!type || type === 'recreate') {
  16513. var baseOption = optionManager.mountOption(type === 'recreate');
  16514. if (!this.option || type === 'recreate') {
  16515. initBase.call(this, baseOption);
  16516. }
  16517. else {
  16518. this.restoreData();
  16519. this.mergeOption(baseOption);
  16520. }
  16521. optionChanged = true;
  16522. }
  16523. if (type === 'timeline' || type === 'media') {
  16524. this.restoreData();
  16525. }
  16526. if (!type || type === 'recreate' || type === 'timeline') {
  16527. var timelineOption = optionManager.getTimelineOption(this);
  16528. timelineOption && (this.mergeOption(timelineOption), optionChanged = true);
  16529. }
  16530. if (!type || type === 'recreate' || type === 'media') {
  16531. var mediaOptions = optionManager.getMediaOption(this, this._api);
  16532. if (mediaOptions.length) {
  16533. each$1(mediaOptions, function (mediaOption) {
  16534. this.mergeOption(mediaOption, optionChanged = true);
  16535. }, this);
  16536. }
  16537. }
  16538. return optionChanged;
  16539. },
  16540. /**
  16541. * @protected
  16542. */
  16543. mergeOption: function (newOption) {
  16544. var option = this.option;
  16545. var componentsMap = this._componentsMap;
  16546. var newCptTypes = [];
  16547. resetSourceDefaulter(this);
  16548. // If no component class, merge directly.
  16549. // For example: color, animaiton options, etc.
  16550. each$1(newOption, function (componentOption, mainType) {
  16551. if (componentOption == null) {
  16552. return;
  16553. }
  16554. if (!ComponentModel.hasClass(mainType)) {
  16555. // globalSettingTask.dirty();
  16556. option[mainType] = option[mainType] == null
  16557. ? clone(componentOption)
  16558. : merge(option[mainType], componentOption, true);
  16559. }
  16560. else if (mainType) {
  16561. newCptTypes.push(mainType);
  16562. }
  16563. });
  16564. ComponentModel.topologicalTravel(
  16565. newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this
  16566. );
  16567. function visitComponent(mainType, dependencies) {
  16568. var newCptOptionList = normalizeToArray(newOption[mainType]);
  16569. var mapResult = mappingToExists(
  16570. componentsMap.get(mainType), newCptOptionList
  16571. );
  16572. makeIdAndName(mapResult);
  16573. // Set mainType and complete subType.
  16574. each$1(mapResult, function (item, index) {
  16575. var opt = item.option;
  16576. if (isObject$1(opt)) {
  16577. item.keyInfo.mainType = mainType;
  16578. item.keyInfo.subType = determineSubType(mainType, opt, item.exist);
  16579. }
  16580. });
  16581. var dependentModels = getComponentsByTypes(
  16582. componentsMap, dependencies
  16583. );
  16584. option[mainType] = [];
  16585. componentsMap.set(mainType, []);
  16586. each$1(mapResult, function (resultItem, index) {
  16587. var componentModel = resultItem.exist;
  16588. var newCptOption = resultItem.option;
  16589. assert$1(
  16590. isObject$1(newCptOption) || componentModel,
  16591. 'Empty component definition'
  16592. );
  16593. // Consider where is no new option and should be merged using {},
  16594. // see removeEdgeAndAdd in topologicalTravel and
  16595. // ComponentModel.getAllClassMainTypes.
  16596. if (!newCptOption) {
  16597. componentModel.mergeOption({}, this);
  16598. componentModel.optionUpdated({}, false);
  16599. }
  16600. else {
  16601. var ComponentModelClass = ComponentModel.getClass(
  16602. mainType, resultItem.keyInfo.subType, true
  16603. );
  16604. if (componentModel && componentModel instanceof ComponentModelClass) {
  16605. componentModel.name = resultItem.keyInfo.name;
  16606. // componentModel.settingTask && componentModel.settingTask.dirty();
  16607. componentModel.mergeOption(newCptOption, this);
  16608. componentModel.optionUpdated(newCptOption, false);
  16609. }
  16610. else {
  16611. // PENDING Global as parent ?
  16612. var extraOpt = extend(
  16613. {
  16614. dependentModels: dependentModels,
  16615. componentIndex: index
  16616. },
  16617. resultItem.keyInfo
  16618. );
  16619. componentModel = new ComponentModelClass(
  16620. newCptOption, this, this, extraOpt
  16621. );
  16622. extend(componentModel, extraOpt);
  16623. componentModel.init(newCptOption, this, this, extraOpt);
  16624. // Call optionUpdated after init.
  16625. // newCptOption has been used as componentModel.option
  16626. // and may be merged with theme and default, so pass null
  16627. // to avoid confusion.
  16628. componentModel.optionUpdated(null, true);
  16629. }
  16630. }
  16631. componentsMap.get(mainType)[index] = componentModel;
  16632. option[mainType][index] = componentModel.option;
  16633. }, this);
  16634. // Backup series for filtering.
  16635. if (mainType === 'series') {
  16636. createSeriesIndices(this, componentsMap.get('series'));
  16637. }
  16638. }
  16639. this._seriesIndicesMap = createHashMap(
  16640. this._seriesIndices = this._seriesIndices || []
  16641. );
  16642. },
  16643. /**
  16644. * Get option for output (cloned option and inner info removed)
  16645. * @public
  16646. * @return {Object}
  16647. */
  16648. getOption: function () {
  16649. var option = clone(this.option);
  16650. each$1(option, function (opts, mainType) {
  16651. if (ComponentModel.hasClass(mainType)) {
  16652. var opts = normalizeToArray(opts);
  16653. for (var i = opts.length - 1; i >= 0; i--) {
  16654. // Remove options with inner id.
  16655. if (isIdInner(opts[i])) {
  16656. opts.splice(i, 1);
  16657. }
  16658. }
  16659. option[mainType] = opts;
  16660. }
  16661. });
  16662. delete option[OPTION_INNER_KEY];
  16663. return option;
  16664. },
  16665. /**
  16666. * @return {module:echarts/model/Model}
  16667. */
  16668. getTheme: function () {
  16669. return this._theme;
  16670. },
  16671. /**
  16672. * @param {string} mainType
  16673. * @param {number} [idx=0]
  16674. * @return {module:echarts/model/Component}
  16675. */
  16676. getComponent: function (mainType, idx) {
  16677. var list = this._componentsMap.get(mainType);
  16678. if (list) {
  16679. return list[idx || 0];
  16680. }
  16681. },
  16682. /**
  16683. * If none of index and id and name used, return all components with mainType.
  16684. * @param {Object} condition
  16685. * @param {string} condition.mainType
  16686. * @param {string} [condition.subType] If ignore, only query by mainType
  16687. * @param {number|Array.<number>} [condition.index] Either input index or id or name.
  16688. * @param {string|Array.<string>} [condition.id] Either input index or id or name.
  16689. * @param {string|Array.<string>} [condition.name] Either input index or id or name.
  16690. * @return {Array.<module:echarts/model/Component>}
  16691. */
  16692. queryComponents: function (condition) {
  16693. var mainType = condition.mainType;
  16694. if (!mainType) {
  16695. return [];
  16696. }
  16697. var index = condition.index;
  16698. var id = condition.id;
  16699. var name = condition.name;
  16700. var cpts = this._componentsMap.get(mainType);
  16701. if (!cpts || !cpts.length) {
  16702. return [];
  16703. }
  16704. var result;
  16705. if (index != null) {
  16706. if (!isArray(index)) {
  16707. index = [index];
  16708. }
  16709. result = filter(map(index, function (idx) {
  16710. return cpts[idx];
  16711. }), function (val) {
  16712. return !!val;
  16713. });
  16714. }
  16715. else if (id != null) {
  16716. var isIdArray = isArray(id);
  16717. result = filter(cpts, function (cpt) {
  16718. return (isIdArray && indexOf(id, cpt.id) >= 0)
  16719. || (!isIdArray && cpt.id === id);
  16720. });
  16721. }
  16722. else if (name != null) {
  16723. var isNameArray = isArray(name);
  16724. result = filter(cpts, function (cpt) {
  16725. return (isNameArray && indexOf(name, cpt.name) >= 0)
  16726. || (!isNameArray && cpt.name === name);
  16727. });
  16728. }
  16729. else {
  16730. // Return all components with mainType
  16731. result = cpts.slice();
  16732. }
  16733. return filterBySubType(result, condition);
  16734. },
  16735. /**
  16736. * The interface is different from queryComponents,
  16737. * which is convenient for inner usage.
  16738. *
  16739. * @usage
  16740. * var result = findComponents(
  16741. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}
  16742. * );
  16743. * var result = findComponents(
  16744. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}
  16745. * );
  16746. * var result = findComponents(
  16747. * {mainType: 'series'},
  16748. * function (model, index) {...}
  16749. * );
  16750. * // result like [component0, componnet1, ...]
  16751. *
  16752. * @param {Object} condition
  16753. * @param {string} condition.mainType Mandatory.
  16754. * @param {string} [condition.subType] Optional.
  16755. * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},
  16756. * where xxx is mainType.
  16757. * If query attribute is null/undefined or has no index/id/name,
  16758. * do not filtering by query conditions, which is convenient for
  16759. * no-payload situations or when target of action is global.
  16760. * @param {Function} [condition.filter] parameter: component, return boolean.
  16761. * @return {Array.<module:echarts/model/Component>}
  16762. */
  16763. findComponents: function (condition) {
  16764. var query = condition.query;
  16765. var mainType = condition.mainType;
  16766. var queryCond = getQueryCond(query);
  16767. var result = queryCond
  16768. ? this.queryComponents(queryCond)
  16769. : this._componentsMap.get(mainType);
  16770. return doFilter(filterBySubType(result, condition));
  16771. function getQueryCond(q) {
  16772. var indexAttr = mainType + 'Index';
  16773. var idAttr = mainType + 'Id';
  16774. var nameAttr = mainType + 'Name';
  16775. return q && (
  16776. q[indexAttr] != null
  16777. || q[idAttr] != null
  16778. || q[nameAttr] != null
  16779. )
  16780. ? {
  16781. mainType: mainType,
  16782. // subType will be filtered finally.
  16783. index: q[indexAttr],
  16784. id: q[idAttr],
  16785. name: q[nameAttr]
  16786. }
  16787. : null;
  16788. }
  16789. function doFilter(res) {
  16790. return condition.filter
  16791. ? filter(res, condition.filter)
  16792. : res;
  16793. }
  16794. },
  16795. /**
  16796. * @usage
  16797. * eachComponent('legend', function (legendModel, index) {
  16798. * ...
  16799. * });
  16800. * eachComponent(function (componentType, model, index) {
  16801. * // componentType does not include subType
  16802. * // (componentType is 'xxx' but not 'xxx.aa')
  16803. * });
  16804. * eachComponent(
  16805. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},
  16806. * function (model, index) {...}
  16807. * );
  16808. * eachComponent(
  16809. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},
  16810. * function (model, index) {...}
  16811. * );
  16812. *
  16813. * @param {string|Object=} mainType When mainType is object, the definition
  16814. * is the same as the method 'findComponents'.
  16815. * @param {Function} cb
  16816. * @param {*} context
  16817. */
  16818. eachComponent: function (mainType, cb, context) {
  16819. var componentsMap = this._componentsMap;
  16820. if (typeof mainType === 'function') {
  16821. context = cb;
  16822. cb = mainType;
  16823. componentsMap.each(function (components, componentType) {
  16824. each$1(components, function (component, index) {
  16825. cb.call(context, componentType, component, index);
  16826. });
  16827. });
  16828. }
  16829. else if (isString(mainType)) {
  16830. each$1(componentsMap.get(mainType), cb, context);
  16831. }
  16832. else if (isObject$1(mainType)) {
  16833. var queryResult = this.findComponents(mainType);
  16834. each$1(queryResult, cb, context);
  16835. }
  16836. },
  16837. /**
  16838. * @param {string} name
  16839. * @return {Array.<module:echarts/model/Series>}
  16840. */
  16841. getSeriesByName: function (name) {
  16842. var series = this._componentsMap.get('series');
  16843. return filter(series, function (oneSeries) {
  16844. return oneSeries.name === name;
  16845. });
  16846. },
  16847. /**
  16848. * @param {number} seriesIndex
  16849. * @return {module:echarts/model/Series}
  16850. */
  16851. getSeriesByIndex: function (seriesIndex) {
  16852. return this._componentsMap.get('series')[seriesIndex];
  16853. },
  16854. /**
  16855. * @param {string} subType
  16856. * @return {Array.<module:echarts/model/Series>}
  16857. */
  16858. getSeriesByType: function (subType) {
  16859. var series = this._componentsMap.get('series');
  16860. return filter(series, function (oneSeries) {
  16861. return oneSeries.subType === subType;
  16862. });
  16863. },
  16864. /**
  16865. * @return {Array.<module:echarts/model/Series>}
  16866. */
  16867. getSeries: function () {
  16868. return this._componentsMap.get('series').slice();
  16869. },
  16870. /**
  16871. * @return {number}
  16872. */
  16873. getSeriesCount: function () {
  16874. return this._componentsMap.get('series').length;
  16875. },
  16876. /**
  16877. * After filtering, series may be different
  16878. * frome raw series.
  16879. *
  16880. * @param {Function} cb
  16881. * @param {*} context
  16882. */
  16883. eachSeries: function (cb, context) {
  16884. assertSeriesInitialized(this);
  16885. each$1(this._seriesIndices, function (rawSeriesIndex) {
  16886. var series = this._componentsMap.get('series')[rawSeriesIndex];
  16887. cb.call(context, series, rawSeriesIndex);
  16888. }, this);
  16889. },
  16890. /**
  16891. * Iterate raw series before filtered.
  16892. *
  16893. * @param {Function} cb
  16894. * @param {*} context
  16895. */
  16896. eachRawSeries: function (cb, context) {
  16897. each$1(this._componentsMap.get('series'), cb, context);
  16898. },
  16899. /**
  16900. * After filtering, series may be different.
  16901. * frome raw series.
  16902. *
  16903. * @parma {string} subType
  16904. * @param {Function} cb
  16905. * @param {*} context
  16906. */
  16907. eachSeriesByType: function (subType, cb, context) {
  16908. assertSeriesInitialized(this);
  16909. each$1(this._seriesIndices, function (rawSeriesIndex) {
  16910. var series = this._componentsMap.get('series')[rawSeriesIndex];
  16911. if (series.subType === subType) {
  16912. cb.call(context, series, rawSeriesIndex);
  16913. }
  16914. }, this);
  16915. },
  16916. /**
  16917. * Iterate raw series before filtered of given type.
  16918. *
  16919. * @parma {string} subType
  16920. * @param {Function} cb
  16921. * @param {*} context
  16922. */
  16923. eachRawSeriesByType: function (subType, cb, context) {
  16924. return each$1(this.getSeriesByType(subType), cb, context);
  16925. },
  16926. /**
  16927. * @param {module:echarts/model/Series} seriesModel
  16928. */
  16929. isSeriesFiltered: function (seriesModel) {
  16930. assertSeriesInitialized(this);
  16931. return this._seriesIndicesMap.get(seriesModel.componentIndex) == null;
  16932. },
  16933. /**
  16934. * @return {Array.<number>}
  16935. */
  16936. getCurrentSeriesIndices: function () {
  16937. return (this._seriesIndices || []).slice();
  16938. },
  16939. /**
  16940. * @param {Function} cb
  16941. * @param {*} context
  16942. */
  16943. filterSeries: function (cb, context) {
  16944. assertSeriesInitialized(this);
  16945. var filteredSeries = filter(
  16946. this._componentsMap.get('series'), cb, context
  16947. );
  16948. createSeriesIndices(this, filteredSeries);
  16949. },
  16950. restoreData: function (payload) {
  16951. var componentsMap = this._componentsMap;
  16952. createSeriesIndices(this, componentsMap.get('series'));
  16953. var componentTypes = [];
  16954. componentsMap.each(function (components, componentType) {
  16955. componentTypes.push(componentType);
  16956. });
  16957. ComponentModel.topologicalTravel(
  16958. componentTypes,
  16959. ComponentModel.getAllClassMainTypes(),
  16960. function (componentType, dependencies) {
  16961. each$1(componentsMap.get(componentType), function (component) {
  16962. (componentType !== 'series' || !isNotTargetSeries(component, payload))
  16963. && component.restoreData();
  16964. });
  16965. }
  16966. );
  16967. }
  16968. });
  16969. function isNotTargetSeries(seriesModel, payload) {
  16970. if (payload) {
  16971. var index = payload.seiresIndex;
  16972. var id = payload.seriesId;
  16973. var name = payload.seriesName;
  16974. return (index != null && seriesModel.componentIndex !== index)
  16975. || (id != null && seriesModel.id !== id)
  16976. || (name != null && seriesModel.name !== name);
  16977. }
  16978. }
  16979. /**
  16980. * @inner
  16981. */
  16982. function mergeTheme(option, theme) {
  16983. each$1(theme, function (themeItem, name) {
  16984. // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理
  16985. if (!ComponentModel.hasClass(name)) {
  16986. if (typeof themeItem === 'object') {
  16987. option[name] = !option[name]
  16988. ? clone(themeItem)
  16989. : merge(option[name], themeItem, false);
  16990. }
  16991. else {
  16992. if (option[name] == null) {
  16993. option[name] = themeItem;
  16994. }
  16995. }
  16996. }
  16997. });
  16998. }
  16999. function initBase(baseOption) {
  17000. baseOption = baseOption;
  17001. // Using OPTION_INNER_KEY to mark that this option can not be used outside,
  17002. // i.e. `chart.setOption(chart.getModel().option);` is forbiden.
  17003. this.option = {};
  17004. this.option[OPTION_INNER_KEY] = 1;
  17005. /**
  17006. * Init with series: [], in case of calling findSeries method
  17007. * before series initialized.
  17008. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  17009. * @private
  17010. */
  17011. this._componentsMap = createHashMap({series: []});
  17012. /**
  17013. * Mapping between filtered series list and raw series list.
  17014. * key: filtered series indices, value: raw series indices.
  17015. * @type {Array.<nubmer>}
  17016. * @private
  17017. */
  17018. this._seriesIndices;
  17019. this._seriesIndicesMap;
  17020. mergeTheme(baseOption, this._theme.option);
  17021. // TODO Needs clone when merging to the unexisted property
  17022. merge(baseOption, globalDefault, false);
  17023. this.mergeOption(baseOption);
  17024. }
  17025. /**
  17026. * @inner
  17027. * @param {Array.<string>|string} types model types
  17028. * @return {Object} key: {string} type, value: {Array.<Object>} models
  17029. */
  17030. function getComponentsByTypes(componentsMap, types) {
  17031. if (!isArray(types)) {
  17032. types = types ? [types] : [];
  17033. }
  17034. var ret = {};
  17035. each$1(types, function (type) {
  17036. ret[type] = (componentsMap.get(type) || []).slice();
  17037. });
  17038. return ret;
  17039. }
  17040. /**
  17041. * @inner
  17042. */
  17043. function determineSubType(mainType, newCptOption, existComponent) {
  17044. var subType = newCptOption.type
  17045. ? newCptOption.type
  17046. : existComponent
  17047. ? existComponent.subType
  17048. // Use determineSubType only when there is no existComponent.
  17049. : ComponentModel.determineSubType(mainType, newCptOption);
  17050. // tooltip, markline, markpoint may always has no subType
  17051. return subType;
  17052. }
  17053. /**
  17054. * @inner
  17055. */
  17056. function createSeriesIndices(ecModel, seriesModels) {
  17057. ecModel._seriesIndicesMap = createHashMap(
  17058. ecModel._seriesIndices = map(seriesModels, function (series) {
  17059. return series.componentIndex;
  17060. }) || []
  17061. );
  17062. }
  17063. /**
  17064. * @inner
  17065. */
  17066. function filterBySubType(components, condition) {
  17067. // Using hasOwnProperty for restrict. Consider
  17068. // subType is undefined in user payload.
  17069. return condition.hasOwnProperty('subType')
  17070. ? filter(components, function (cpt) {
  17071. return cpt.subType === condition.subType;
  17072. })
  17073. : components;
  17074. }
  17075. /**
  17076. * @inner
  17077. */
  17078. function assertSeriesInitialized(ecModel) {
  17079. // Components that use _seriesIndices should depends on series component,
  17080. // which make sure that their initialization is after series.
  17081. if (__DEV__) {
  17082. if (!ecModel._seriesIndices) {
  17083. throw new Error('Option should contains series.');
  17084. }
  17085. }
  17086. }
  17087. mixin(GlobalModel, colorPaletteMixin);
  17088. var echartsAPIList = [
  17089. 'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed',
  17090. 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption',
  17091. 'getViewOfComponentModel', 'getViewOfSeriesModel'
  17092. ];
  17093. // And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js
  17094. function ExtensionAPI(chartInstance) {
  17095. each$1(echartsAPIList, function (name) {
  17096. this[name] = bind(chartInstance[name], chartInstance);
  17097. }, this);
  17098. }
  17099. var coordinateSystemCreators = {};
  17100. function CoordinateSystemManager() {
  17101. this._coordinateSystems = [];
  17102. }
  17103. CoordinateSystemManager.prototype = {
  17104. constructor: CoordinateSystemManager,
  17105. create: function (ecModel, api) {
  17106. var coordinateSystems = [];
  17107. each$1(coordinateSystemCreators, function (creater, type) {
  17108. var list = creater.create(ecModel, api);
  17109. coordinateSystems = coordinateSystems.concat(list || []);
  17110. });
  17111. this._coordinateSystems = coordinateSystems;
  17112. },
  17113. update: function (ecModel, api) {
  17114. each$1(this._coordinateSystems, function (coordSys) {
  17115. coordSys.update && coordSys.update(ecModel, api);
  17116. });
  17117. },
  17118. getCoordinateSystems: function () {
  17119. return this._coordinateSystems.slice();
  17120. }
  17121. };
  17122. CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
  17123. coordinateSystemCreators[type] = coordinateSystemCreator;
  17124. };
  17125. CoordinateSystemManager.get = function (type) {
  17126. return coordinateSystemCreators[type];
  17127. };
  17128. /**
  17129. * ECharts option manager
  17130. *
  17131. * @module {echarts/model/OptionManager}
  17132. */
  17133. var each$4 = each$1;
  17134. var clone$3 = clone;
  17135. var map$1 = map;
  17136. var merge$1 = merge;
  17137. var QUERY_REG = /^(min|max)?(.+)$/;
  17138. /**
  17139. * TERM EXPLANATIONS:
  17140. *
  17141. * [option]:
  17142. *
  17143. * An object that contains definitions of components. For example:
  17144. * var option = {
  17145. * title: {...},
  17146. * legend: {...},
  17147. * visualMap: {...},
  17148. * series: [
  17149. * {data: [...]},
  17150. * {data: [...]},
  17151. * ...
  17152. * ]
  17153. * };
  17154. *
  17155. * [rawOption]:
  17156. *
  17157. * An object input to echarts.setOption. 'rawOption' may be an
  17158. * 'option', or may be an object contains multi-options. For example:
  17159. * var option = {
  17160. * baseOption: {
  17161. * title: {...},
  17162. * legend: {...},
  17163. * series: [
  17164. * {data: [...]},
  17165. * {data: [...]},
  17166. * ...
  17167. * ]
  17168. * },
  17169. * timeline: {...},
  17170. * options: [
  17171. * {title: {...}, series: {data: [...]}},
  17172. * {title: {...}, series: {data: [...]}},
  17173. * ...
  17174. * ],
  17175. * media: [
  17176. * {
  17177. * query: {maxWidth: 320},
  17178. * option: {series: {x: 20}, visualMap: {show: false}}
  17179. * },
  17180. * {
  17181. * query: {minWidth: 320, maxWidth: 720},
  17182. * option: {series: {x: 500}, visualMap: {show: true}}
  17183. * },
  17184. * {
  17185. * option: {series: {x: 1200}, visualMap: {show: true}}
  17186. * }
  17187. * ]
  17188. * };
  17189. *
  17190. * @alias module:echarts/model/OptionManager
  17191. * @param {module:echarts/ExtensionAPI} api
  17192. */
  17193. function OptionManager(api) {
  17194. /**
  17195. * @private
  17196. * @type {module:echarts/ExtensionAPI}
  17197. */
  17198. this._api = api;
  17199. /**
  17200. * @private
  17201. * @type {Array.<number>}
  17202. */
  17203. this._timelineOptions = [];
  17204. /**
  17205. * @private
  17206. * @type {Array.<Object>}
  17207. */
  17208. this._mediaList = [];
  17209. /**
  17210. * @private
  17211. * @type {Object}
  17212. */
  17213. this._mediaDefault;
  17214. /**
  17215. * -1, means default.
  17216. * empty means no media.
  17217. * @private
  17218. * @type {Array.<number>}
  17219. */
  17220. this._currentMediaIndices = [];
  17221. /**
  17222. * @private
  17223. * @type {Object}
  17224. */
  17225. this._optionBackup;
  17226. /**
  17227. * @private
  17228. * @type {Object}
  17229. */
  17230. this._newBaseOption;
  17231. }
  17232. // timeline.notMerge is not supported in ec3. Firstly there is rearly
  17233. // case that notMerge is needed. Secondly supporting 'notMerge' requires
  17234. // rawOption cloned and backuped when timeline changed, which does no
  17235. // good to performance. What's more, that both timeline and setOption
  17236. // method supply 'notMerge' brings complex and some problems.
  17237. // Consider this case:
  17238. // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);
  17239. // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);
  17240. OptionManager.prototype = {
  17241. constructor: OptionManager,
  17242. /**
  17243. * @public
  17244. * @param {Object} rawOption Raw option.
  17245. * @param {module:echarts/model/Global} ecModel
  17246. * @param {Array.<Function>} optionPreprocessorFuncs
  17247. * @return {Object} Init option
  17248. */
  17249. setOption: function (rawOption, optionPreprocessorFuncs) {
  17250. if (rawOption) {
  17251. // That set dat primitive is dangerous if user reuse the data when setOption again.
  17252. each$1(normalizeToArray(rawOption.series), function (series) {
  17253. series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data);
  17254. });
  17255. }
  17256. // Caution: some series modify option data, if do not clone,
  17257. // it should ensure that the repeat modify correctly
  17258. // (create a new object when modify itself).
  17259. rawOption = clone$3(rawOption, true);
  17260. // FIXME
  17261. // 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。
  17262. var oldOptionBackup = this._optionBackup;
  17263. var newParsedOption = parseRawOption.call(
  17264. this, rawOption, optionPreprocessorFuncs, !oldOptionBackup
  17265. );
  17266. this._newBaseOption = newParsedOption.baseOption;
  17267. // For setOption at second time (using merge mode);
  17268. if (oldOptionBackup) {
  17269. // Only baseOption can be merged.
  17270. mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption);
  17271. // For simplicity, timeline options and media options do not support merge,
  17272. // that is, if you `setOption` twice and both has timeline options, the latter
  17273. // timeline opitons will not be merged to the formers, but just substitude them.
  17274. if (newParsedOption.timelineOptions.length) {
  17275. oldOptionBackup.timelineOptions = newParsedOption.timelineOptions;
  17276. }
  17277. if (newParsedOption.mediaList.length) {
  17278. oldOptionBackup.mediaList = newParsedOption.mediaList;
  17279. }
  17280. if (newParsedOption.mediaDefault) {
  17281. oldOptionBackup.mediaDefault = newParsedOption.mediaDefault;
  17282. }
  17283. }
  17284. else {
  17285. this._optionBackup = newParsedOption;
  17286. }
  17287. },
  17288. /**
  17289. * @param {boolean} isRecreate
  17290. * @return {Object}
  17291. */
  17292. mountOption: function (isRecreate) {
  17293. var optionBackup = this._optionBackup;
  17294. // TODO
  17295. // 如果没有reset功能则不clone。
  17296. this._timelineOptions = map$1(optionBackup.timelineOptions, clone$3);
  17297. this._mediaList = map$1(optionBackup.mediaList, clone$3);
  17298. this._mediaDefault = clone$3(optionBackup.mediaDefault);
  17299. this._currentMediaIndices = [];
  17300. return clone$3(isRecreate
  17301. // this._optionBackup.baseOption, which is created at the first `setOption`
  17302. // called, and is merged into every new option by inner method `mergeOption`
  17303. // each time `setOption` called, can be only used in `isRecreate`, because
  17304. // its reliability is under suspicion. In other cases option merge is
  17305. // performed by `model.mergeOption`.
  17306. ? optionBackup.baseOption : this._newBaseOption
  17307. );
  17308. },
  17309. /**
  17310. * @param {module:echarts/model/Global} ecModel
  17311. * @return {Object}
  17312. */
  17313. getTimelineOption: function (ecModel) {
  17314. var option;
  17315. var timelineOptions = this._timelineOptions;
  17316. if (timelineOptions.length) {
  17317. // getTimelineOption can only be called after ecModel inited,
  17318. // so we can get currentIndex from timelineModel.
  17319. var timelineModel = ecModel.getComponent('timeline');
  17320. if (timelineModel) {
  17321. option = clone$3(
  17322. timelineOptions[timelineModel.getCurrentIndex()],
  17323. true
  17324. );
  17325. }
  17326. }
  17327. return option;
  17328. },
  17329. /**
  17330. * @param {module:echarts/model/Global} ecModel
  17331. * @return {Array.<Object>}
  17332. */
  17333. getMediaOption: function (ecModel) {
  17334. var ecWidth = this._api.getWidth();
  17335. var ecHeight = this._api.getHeight();
  17336. var mediaList = this._mediaList;
  17337. var mediaDefault = this._mediaDefault;
  17338. var indices = [];
  17339. var result = [];
  17340. // No media defined.
  17341. if (!mediaList.length && !mediaDefault) {
  17342. return result;
  17343. }
  17344. // Multi media may be applied, the latter defined media has higher priority.
  17345. for (var i = 0, len = mediaList.length; i < len; i++) {
  17346. if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {
  17347. indices.push(i);
  17348. }
  17349. }
  17350. // FIXME
  17351. // 是否mediaDefault应该强制用户设置,否则可能修改不能回归。
  17352. if (!indices.length && mediaDefault) {
  17353. indices = [-1];
  17354. }
  17355. if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {
  17356. result = map$1(indices, function (index) {
  17357. return clone$3(
  17358. index === -1 ? mediaDefault.option : mediaList[index].option
  17359. );
  17360. });
  17361. }
  17362. // Otherwise return nothing.
  17363. this._currentMediaIndices = indices;
  17364. return result;
  17365. }
  17366. };
  17367. function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {
  17368. var timelineOptions = [];
  17369. var mediaList = [];
  17370. var mediaDefault;
  17371. var baseOption;
  17372. // Compatible with ec2.
  17373. var timelineOpt = rawOption.timeline;
  17374. if (rawOption.baseOption) {
  17375. baseOption = rawOption.baseOption;
  17376. }
  17377. // For timeline
  17378. if (timelineOpt || rawOption.options) {
  17379. baseOption = baseOption || {};
  17380. timelineOptions = (rawOption.options || []).slice();
  17381. }
  17382. // For media query
  17383. if (rawOption.media) {
  17384. baseOption = baseOption || {};
  17385. var media = rawOption.media;
  17386. each$4(media, function (singleMedia) {
  17387. if (singleMedia && singleMedia.option) {
  17388. if (singleMedia.query) {
  17389. mediaList.push(singleMedia);
  17390. }
  17391. else if (!mediaDefault) {
  17392. // Use the first media default.
  17393. mediaDefault = singleMedia;
  17394. }
  17395. }
  17396. });
  17397. }
  17398. // For normal option
  17399. if (!baseOption) {
  17400. baseOption = rawOption;
  17401. }
  17402. // Set timelineOpt to baseOption in ec3,
  17403. // which is convenient for merge option.
  17404. if (!baseOption.timeline) {
  17405. baseOption.timeline = timelineOpt;
  17406. }
  17407. // Preprocess.
  17408. each$4([baseOption].concat(timelineOptions)
  17409. .concat(map(mediaList, function (media) {
  17410. return media.option;
  17411. })),
  17412. function (option) {
  17413. each$4(optionPreprocessorFuncs, function (preProcess) {
  17414. preProcess(option, isNew);
  17415. });
  17416. }
  17417. );
  17418. return {
  17419. baseOption: baseOption,
  17420. timelineOptions: timelineOptions,
  17421. mediaDefault: mediaDefault,
  17422. mediaList: mediaList
  17423. };
  17424. }
  17425. /**
  17426. * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>
  17427. * Support: width, height, aspectRatio
  17428. * Can use max or min as prefix.
  17429. */
  17430. function applyMediaQuery(query, ecWidth, ecHeight) {
  17431. var realMap = {
  17432. width: ecWidth,
  17433. height: ecHeight,
  17434. aspectratio: ecWidth / ecHeight // lowser case for convenientce.
  17435. };
  17436. var applicatable = true;
  17437. each$1(query, function (value, attr) {
  17438. var matched = attr.match(QUERY_REG);
  17439. if (!matched || !matched[1] || !matched[2]) {
  17440. return;
  17441. }
  17442. var operator = matched[1];
  17443. var realAttr = matched[2].toLowerCase();
  17444. if (!compare(realMap[realAttr], value, operator)) {
  17445. applicatable = false;
  17446. }
  17447. });
  17448. return applicatable;
  17449. }
  17450. function compare(real, expect, operator) {
  17451. if (operator === 'min') {
  17452. return real >= expect;
  17453. }
  17454. else if (operator === 'max') {
  17455. return real <= expect;
  17456. }
  17457. else { // Equals
  17458. return real === expect;
  17459. }
  17460. }
  17461. function indicesEquals(indices1, indices2) {
  17462. // indices is always order by asc and has only finite number.
  17463. return indices1.join(',') === indices2.join(',');
  17464. }
  17465. /**
  17466. * Consider case:
  17467. * `chart.setOption(opt1);`
  17468. * Then user do some interaction like dataZoom, dataView changing.
  17469. * `chart.setOption(opt2);`
  17470. * Then user press 'reset button' in toolbox.
  17471. *
  17472. * After doing that all of the interaction effects should be reset, the
  17473. * chart should be the same as the result of invoke
  17474. * `chart.setOption(opt1); chart.setOption(opt2);`.
  17475. *
  17476. * Although it is not able ensure that
  17477. * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to
  17478. * `chart.setOption(merge(opt1, opt2));` exactly,
  17479. * this might be the only simple way to implement that feature.
  17480. *
  17481. * MEMO: We've considered some other approaches:
  17482. * 1. Each model handle its self restoration but not uniform treatment.
  17483. * (Too complex in logic and error-prone)
  17484. * 2. Use a shadow ecModel. (Performace expensive)
  17485. */
  17486. function mergeOption(oldOption, newOption) {
  17487. newOption = newOption || {};
  17488. each$4(newOption, function (newCptOpt, mainType) {
  17489. if (newCptOpt == null) {
  17490. return;
  17491. }
  17492. var oldCptOpt = oldOption[mainType];
  17493. if (!ComponentModel.hasClass(mainType)) {
  17494. oldOption[mainType] = merge$1(oldCptOpt, newCptOpt, true);
  17495. }
  17496. else {
  17497. newCptOpt = normalizeToArray(newCptOpt);
  17498. oldCptOpt = normalizeToArray(oldCptOpt);
  17499. var mapResult = mappingToExists(oldCptOpt, newCptOpt);
  17500. oldOption[mainType] = map$1(mapResult, function (item) {
  17501. return (item.option && item.exist)
  17502. ? merge$1(item.exist, item.option, true)
  17503. : (item.exist || item.option);
  17504. });
  17505. }
  17506. });
  17507. }
  17508. var each$5 = each$1;
  17509. var isObject$3 = isObject$1;
  17510. var POSSIBLE_STYLES = [
  17511. 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle',
  17512. 'chordStyle', 'label', 'labelLine'
  17513. ];
  17514. function compatEC2ItemStyle(opt) {
  17515. var itemStyleOpt = opt && opt.itemStyle;
  17516. if (!itemStyleOpt) {
  17517. return;
  17518. }
  17519. for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {
  17520. var styleName = POSSIBLE_STYLES[i];
  17521. var normalItemStyleOpt = itemStyleOpt.normal;
  17522. var emphasisItemStyleOpt = itemStyleOpt.emphasis;
  17523. if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {
  17524. opt[styleName] = opt[styleName] || {};
  17525. if (!opt[styleName].normal) {
  17526. opt[styleName].normal = normalItemStyleOpt[styleName];
  17527. }
  17528. else {
  17529. merge(opt[styleName].normal, normalItemStyleOpt[styleName]);
  17530. }
  17531. normalItemStyleOpt[styleName] = null;
  17532. }
  17533. if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {
  17534. opt[styleName] = opt[styleName] || {};
  17535. if (!opt[styleName].emphasis) {
  17536. opt[styleName].emphasis = emphasisItemStyleOpt[styleName];
  17537. }
  17538. else {
  17539. merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);
  17540. }
  17541. emphasisItemStyleOpt[styleName] = null;
  17542. }
  17543. }
  17544. }
  17545. function convertNormalEmphasis(opt, optType, useExtend) {
  17546. if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) {
  17547. var normalOpt = opt[optType].normal;
  17548. var emphasisOpt = opt[optType].emphasis;
  17549. if (normalOpt) {
  17550. // Timeline controlStyle has other properties besides normal and emphasis
  17551. if (useExtend) {
  17552. opt[optType].normal = opt[optType].emphasis = null;
  17553. defaults(opt[optType], normalOpt);
  17554. }
  17555. else {
  17556. opt[optType] = normalOpt;
  17557. }
  17558. }
  17559. if (emphasisOpt) {
  17560. opt.emphasis = opt.emphasis || {};
  17561. opt.emphasis[optType] = emphasisOpt;
  17562. }
  17563. }
  17564. }
  17565. function removeEC3NormalStatus(opt) {
  17566. convertNormalEmphasis(opt, 'itemStyle');
  17567. convertNormalEmphasis(opt, 'lineStyle');
  17568. convertNormalEmphasis(opt, 'areaStyle');
  17569. convertNormalEmphasis(opt, 'label');
  17570. convertNormalEmphasis(opt, 'labelLine');
  17571. // treemap
  17572. convertNormalEmphasis(opt, 'upperLabel');
  17573. // graph
  17574. convertNormalEmphasis(opt, 'edgeLabel');
  17575. }
  17576. function compatTextStyle(labelOpt) {
  17577. var textStyle = isObject$3(labelOpt) && labelOpt.textStyle;
  17578. if (textStyle) {
  17579. for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {
  17580. var propName = TEXT_STYLE_OPTIONS[i];
  17581. if (textStyle.hasOwnProperty(propName)) {
  17582. labelOpt[propName] = textStyle[propName];
  17583. }
  17584. }
  17585. }
  17586. }
  17587. function compatEC3CommonStyles(opt) {
  17588. if (opt) {
  17589. removeEC3NormalStatus(opt);
  17590. compatTextStyle(opt.label);
  17591. opt.emphasis && compatTextStyle(opt.emphasis.label);
  17592. }
  17593. }
  17594. function processSeries(seriesOpt) {
  17595. if (!isObject$3(seriesOpt)) {
  17596. return;
  17597. }
  17598. compatEC2ItemStyle(seriesOpt);
  17599. removeEC3NormalStatus(seriesOpt);
  17600. compatTextStyle(seriesOpt.label);
  17601. // treemap
  17602. compatTextStyle(seriesOpt.upperLabel);
  17603. // graph
  17604. compatTextStyle(seriesOpt.edgeLabel);
  17605. if (seriesOpt.emphasis) {
  17606. compatTextStyle(seriesOpt.emphasis.label);
  17607. // treemap
  17608. compatTextStyle(seriesOpt.emphasis.upperLabel);
  17609. // graph
  17610. compatTextStyle(seriesOpt.emphasis.edgeLabel);
  17611. }
  17612. var markPoint = seriesOpt.markPoint;
  17613. if (markPoint) {
  17614. compatEC2ItemStyle(markPoint);
  17615. compatEC3CommonStyles(markPoint);
  17616. }
  17617. var markLine = seriesOpt.markLine;
  17618. if (markLine) {
  17619. compatEC2ItemStyle(markLine);
  17620. compatEC3CommonStyles(markLine);
  17621. }
  17622. var markArea = seriesOpt.markArea;
  17623. if (markArea) {
  17624. compatEC3CommonStyles(markArea);
  17625. }
  17626. var data = seriesOpt.data;
  17627. // Break with ec3: if `setOption` again, there may be no `type` in option,
  17628. // then the backward compat based on option type will not be performed.
  17629. if (seriesOpt.type === 'graph') {
  17630. data = data || seriesOpt.nodes;
  17631. var edgeData = seriesOpt.links || seriesOpt.edges;
  17632. if (edgeData && !isTypedArray(edgeData)) {
  17633. for (var i = 0; i < edgeData.length; i++) {
  17634. compatEC3CommonStyles(edgeData[i]);
  17635. }
  17636. }
  17637. each$1(seriesOpt.categories, function (opt) {
  17638. removeEC3NormalStatus(opt);
  17639. });
  17640. }
  17641. if (data && !isTypedArray(data)) {
  17642. for (var i = 0; i < data.length; i++) {
  17643. compatEC3CommonStyles(data[i]);
  17644. }
  17645. }
  17646. // mark point data
  17647. var markPoint = seriesOpt.markPoint;
  17648. if (markPoint && markPoint.data) {
  17649. var mpData = markPoint.data;
  17650. for (var i = 0; i < mpData.length; i++) {
  17651. compatEC3CommonStyles(mpData[i]);
  17652. }
  17653. }
  17654. // mark line data
  17655. var markLine = seriesOpt.markLine;
  17656. if (markLine && markLine.data) {
  17657. var mlData = markLine.data;
  17658. for (var i = 0; i < mlData.length; i++) {
  17659. if (isArray(mlData[i])) {
  17660. compatEC3CommonStyles(mlData[i][0]);
  17661. compatEC3CommonStyles(mlData[i][1]);
  17662. }
  17663. else {
  17664. compatEC3CommonStyles(mlData[i]);
  17665. }
  17666. }
  17667. }
  17668. // Series
  17669. if (seriesOpt.type === 'gauge') {
  17670. compatTextStyle(seriesOpt, 'axisLabel');
  17671. compatTextStyle(seriesOpt, 'title');
  17672. compatTextStyle(seriesOpt, 'detail');
  17673. }
  17674. else if (seriesOpt.type === 'treemap') {
  17675. convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle');
  17676. each$1(seriesOpt.levels, function (opt) {
  17677. removeEC3NormalStatus(opt);
  17678. });
  17679. }
  17680. // sunburst starts from ec4, so it does not need to compat levels.
  17681. }
  17682. function toArr(o) {
  17683. return isArray(o) ? o : o ? [o] : [];
  17684. }
  17685. function toObj(o) {
  17686. return (isArray(o) ? o[0] : o) || {};
  17687. }
  17688. var compatStyle = function (option, isTheme) {
  17689. each$5(toArr(option.series), function (seriesOpt) {
  17690. isObject$3(seriesOpt) && processSeries(seriesOpt);
  17691. });
  17692. var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];
  17693. isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');
  17694. each$5(
  17695. axes,
  17696. function (axisName) {
  17697. each$5(toArr(option[axisName]), function (axisOpt) {
  17698. if (axisOpt) {
  17699. compatTextStyle(axisOpt, 'axisLabel');
  17700. compatTextStyle(axisOpt.axisPointer, 'label');
  17701. }
  17702. });
  17703. }
  17704. );
  17705. each$5(toArr(option.parallel), function (parallelOpt) {
  17706. var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;
  17707. compatTextStyle(parallelAxisDefault, 'axisLabel');
  17708. compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');
  17709. });
  17710. each$5(toArr(option.calendar), function (calendarOpt) {
  17711. convertNormalEmphasis(calendarOpt, 'itemStyle');
  17712. compatTextStyle(calendarOpt, 'dayLabel');
  17713. compatTextStyle(calendarOpt, 'monthLabel');
  17714. compatTextStyle(calendarOpt, 'yearLabel');
  17715. });
  17716. // radar.name.textStyle
  17717. each$5(toArr(option.radar), function (radarOpt) {
  17718. compatTextStyle(radarOpt, 'name');
  17719. });
  17720. each$5(toArr(option.geo), function (geoOpt) {
  17721. if (isObject$3(geoOpt)) {
  17722. compatEC3CommonStyles(geoOpt);
  17723. each$5(toArr(geoOpt.regions), function (regionObj) {
  17724. compatEC3CommonStyles(regionObj);
  17725. });
  17726. }
  17727. });
  17728. each$5(toArr(option.timeline), function (timelineOpt) {
  17729. compatEC3CommonStyles(timelineOpt);
  17730. convertNormalEmphasis(timelineOpt, 'label');
  17731. convertNormalEmphasis(timelineOpt, 'itemStyle');
  17732. convertNormalEmphasis(timelineOpt, 'controlStyle', true);
  17733. convertNormalEmphasis(timelineOpt, 'checkpointStyle');
  17734. var data = timelineOpt.data;
  17735. isArray(data) && each$1(data, function (item) {
  17736. if (isObject$1(item)) {
  17737. convertNormalEmphasis(item, 'label');
  17738. convertNormalEmphasis(item, 'itemStyle');
  17739. }
  17740. });
  17741. });
  17742. each$5(toArr(option.toolbox), function (toolboxOpt) {
  17743. convertNormalEmphasis(toolboxOpt, 'iconStyle');
  17744. each$5(toolboxOpt.feature, function (featureOpt) {
  17745. convertNormalEmphasis(featureOpt, 'iconStyle');
  17746. });
  17747. });
  17748. compatTextStyle(toObj(option.axisPointer), 'label');
  17749. compatTextStyle(toObj(option.tooltip).axisPointer, 'label');
  17750. };
  17751. // Compatitable with 2.0
  17752. function get(opt, path) {
  17753. path = path.split(',');
  17754. var obj = opt;
  17755. for (var i = 0; i < path.length; i++) {
  17756. obj = obj && obj[path[i]];
  17757. if (obj == null) {
  17758. break;
  17759. }
  17760. }
  17761. return obj;
  17762. }
  17763. function set$1(opt, path, val, overwrite) {
  17764. path = path.split(',');
  17765. var obj = opt;
  17766. var key;
  17767. for (var i = 0; i < path.length - 1; i++) {
  17768. key = path[i];
  17769. if (obj[key] == null) {
  17770. obj[key] = {};
  17771. }
  17772. obj = obj[key];
  17773. }
  17774. if (overwrite || obj[path[i]] == null) {
  17775. obj[path[i]] = val;
  17776. }
  17777. }
  17778. function compatLayoutProperties(option) {
  17779. each$1(LAYOUT_PROPERTIES, function (prop) {
  17780. if (prop[0] in option && !(prop[1] in option)) {
  17781. option[prop[1]] = option[prop[0]];
  17782. }
  17783. });
  17784. }
  17785. var LAYOUT_PROPERTIES = [
  17786. ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']
  17787. ];
  17788. var COMPATITABLE_COMPONENTS = [
  17789. 'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'
  17790. ];
  17791. var backwardCompat = function (option, isTheme) {
  17792. compatStyle(option, isTheme);
  17793. // Make sure series array for model initialization.
  17794. option.series = normalizeToArray(option.series);
  17795. each$1(option.series, function (seriesOpt) {
  17796. if (!isObject$1(seriesOpt)) {
  17797. return;
  17798. }
  17799. var seriesType = seriesOpt.type;
  17800. if (seriesType === 'pie' || seriesType === 'gauge') {
  17801. if (seriesOpt.clockWise != null) {
  17802. seriesOpt.clockwise = seriesOpt.clockWise;
  17803. }
  17804. }
  17805. if (seriesType === 'gauge') {
  17806. var pointerColor = get(seriesOpt, 'pointer.color');
  17807. pointerColor != null
  17808. && set$1(seriesOpt, 'itemStyle.normal.color', pointerColor);
  17809. }
  17810. compatLayoutProperties(seriesOpt);
  17811. });
  17812. // dataRange has changed to visualMap
  17813. if (option.dataRange) {
  17814. option.visualMap = option.dataRange;
  17815. }
  17816. each$1(COMPATITABLE_COMPONENTS, function (componentName) {
  17817. var options = option[componentName];
  17818. if (options) {
  17819. if (!isArray(options)) {
  17820. options = [options];
  17821. }
  17822. each$1(options, function (option) {
  17823. compatLayoutProperties(option);
  17824. });
  17825. }
  17826. });
  17827. };
  17828. // TODO
  17829. // ??? refactor? check the outer usage of data provider.
  17830. // merge with defaultDimValueGetter?
  17831. /**
  17832. * If normal array used, mutable chunk size is supported.
  17833. * If typed array used, chunk size must be fixed.
  17834. */
  17835. function DefaultDataProvider(source, dimSize) {
  17836. if (!Source.isInstance(source)) {
  17837. source = Source.seriesDataToSource(source);
  17838. }
  17839. this._source = source;
  17840. var data = this._data = source.data;
  17841. var sourceFormat = source.sourceFormat;
  17842. // Typed array. TODO IE10+?
  17843. if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
  17844. if (__DEV__) {
  17845. if (dimSize == null) {
  17846. throw new Error('Typed array data must specify dimension size');
  17847. }
  17848. }
  17849. this._offset = 0;
  17850. this._dimSize = dimSize;
  17851. this._data = data;
  17852. }
  17853. var methods = providerMethods[
  17854. sourceFormat === SOURCE_FORMAT_ARRAY_ROWS
  17855. ? sourceFormat + '_' + source.seriesLayoutBy
  17856. : sourceFormat
  17857. ];
  17858. if (__DEV__) {
  17859. assert$1(methods, 'Invalide sourceFormat: ' + sourceFormat);
  17860. }
  17861. extend(this, methods);
  17862. }
  17863. var providerProto = DefaultDataProvider.prototype;
  17864. // If data is pure without style configuration
  17865. providerProto.pure = false;
  17866. // If data is persistent and will not be released after use.
  17867. providerProto.persistent = true;
  17868. // ???! FIXME legacy data provider do not has method getSource
  17869. providerProto.getSource = function () {
  17870. return this._source;
  17871. };
  17872. var providerMethods = {
  17873. 'arrayRows_column': {
  17874. pure: true,
  17875. count: function () {
  17876. return Math.max(0, this._data.length - this._source.startIndex);
  17877. },
  17878. getItem: function (idx) {
  17879. return this._data[idx + this._source.startIndex];
  17880. },
  17881. appendData: appendDataSimply
  17882. },
  17883. 'arrayRows_row': {
  17884. pure: true,
  17885. count: function () {
  17886. var row = this._data[0];
  17887. return row ? Math.max(0, row.length - this._source.startIndex) : 0;
  17888. },
  17889. getItem: function (idx) {
  17890. idx += this._source.startIndex;
  17891. var item = [];
  17892. var data = this._data;
  17893. for (var i = 0; i < data.length; i++) {
  17894. var row = data[i];
  17895. item.push(row ? row[idx] : null);
  17896. }
  17897. return item;
  17898. },
  17899. appendData: function () {
  17900. throw new Error('Do not support appendData when set seriesLayoutBy: "row".');
  17901. }
  17902. },
  17903. 'objectRows': {
  17904. pure: true,
  17905. count: countSimply,
  17906. getItem: getItemSimply,
  17907. appendData: appendDataSimply
  17908. },
  17909. 'keyedColumns': {
  17910. pure: true,
  17911. count: function () {
  17912. var dimName = this._source.dimensionsDefine[0].name;
  17913. var col = this._data[dimName];
  17914. return col ? col.length : 0;
  17915. },
  17916. getItem: function (idx) {
  17917. var item = [];
  17918. var dims = this._source.dimensionsDefine;
  17919. for (var i = 0; i < dims.length; i++) {
  17920. var col = this._data[dims[i].name];
  17921. item.push(col ? col[idx] : null);
  17922. }
  17923. return item;
  17924. },
  17925. appendData: function (newData) {
  17926. var data = this._data;
  17927. each$1(newData, function (newCol, key) {
  17928. var oldCol = data[key] || (data[key] = []);
  17929. for (var i = 0; i < (newCol || []).length; i++) {
  17930. oldCol.push(newCol[i]);
  17931. }
  17932. });
  17933. }
  17934. },
  17935. 'original': {
  17936. count: countSimply,
  17937. getItem: getItemSimply,
  17938. appendData: appendDataSimply
  17939. },
  17940. 'typedArray': {
  17941. persistent: false,
  17942. pure: true,
  17943. count: function () {
  17944. return this._data ? (this._data.length / this._dimSize) : 0;
  17945. },
  17946. getItem: function (idx) {
  17947. idx = idx - this._offset;
  17948. var item = [];
  17949. var offset = this._dimSize * idx;
  17950. for (var i = 0; i < this._dimSize; i++) {
  17951. item[i] = this._data[offset + i];
  17952. }
  17953. return item;
  17954. },
  17955. appendData: function (newData) {
  17956. if (__DEV__) {
  17957. assert$1(
  17958. isTypedArray(newData),
  17959. 'Added data must be TypedArray if data in initialization is TypedArray'
  17960. );
  17961. }
  17962. this._data = newData;
  17963. },
  17964. // Clean self if data is already used.
  17965. clean: function () {
  17966. // PENDING
  17967. this._offset += this.count();
  17968. this._data = null;
  17969. }
  17970. }
  17971. };
  17972. function countSimply() {
  17973. return this._data.length;
  17974. }
  17975. function getItemSimply(idx) {
  17976. return this._data[idx];
  17977. }
  17978. function appendDataSimply(newData) {
  17979. for (var i = 0; i < newData.length; i++) {
  17980. this._data.push(newData[i]);
  17981. }
  17982. }
  17983. var rawValueGetters = {
  17984. arrayRows: getRawValueSimply,
  17985. objectRows: function (dataItem, dataIndex, dimIndex, dimName) {
  17986. return dimIndex != null ? dataItem[dimName] : dataItem;
  17987. },
  17988. keyedColumns: getRawValueSimply,
  17989. original: function (dataItem, dataIndex, dimIndex, dimName) {
  17990. // FIXME
  17991. // In some case (markpoint in geo (geo-map.html)), dataItem
  17992. // is {coord: [...]}
  17993. var value = getDataItemValue(dataItem);
  17994. return (dimIndex == null || !(value instanceof Array))
  17995. ? value
  17996. : value[dimIndex];
  17997. },
  17998. typedArray: getRawValueSimply
  17999. };
  18000. function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) {
  18001. return dimIndex != null ? dataItem[dimIndex] : dataItem;
  18002. }
  18003. var defaultDimValueGetters = {
  18004. arrayRows: getDimValueSimply,
  18005. objectRows: function (dataItem, dimName, dataIndex, dimIndex) {
  18006. return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
  18007. },
  18008. keyedColumns: getDimValueSimply,
  18009. original: function (dataItem, dimName, dataIndex, dimIndex) {
  18010. // Performance sensitive, do not use modelUtil.getDataItemValue.
  18011. // If dataItem is an plain object with no value field, the var `value`
  18012. // will be assigned with the object, but it will be tread correctly
  18013. // in the `convertDataValue`.
  18014. var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
  18015. // If any dataItem is like { value: 10 }
  18016. if (!this._rawData.pure && isDataItemOption(dataItem)) {
  18017. this.hasItemOption = true;
  18018. }
  18019. return converDataValue(
  18020. (value instanceof Array)
  18021. ? value[dimIndex]
  18022. // If value is a single number or something else not array.
  18023. : value,
  18024. this._dimensionInfos[dimName]
  18025. );
  18026. },
  18027. typedArray: function (dataItem, dimName, dataIndex, dimIndex) {
  18028. return dataItem[dimIndex];
  18029. }
  18030. };
  18031. function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) {
  18032. return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
  18033. }
  18034. /**
  18035. * This helper method convert value in data.
  18036. * @param {string|number|Date} value
  18037. * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
  18038. * If "dimInfo.ordinalParseAndSave", ordinal value can be parsed.
  18039. */
  18040. function converDataValue(value, dimInfo) {
  18041. // Performance sensitive.
  18042. var dimType = dimInfo && dimInfo.type;
  18043. if (dimType === 'ordinal') {
  18044. // If given value is a category string
  18045. var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
  18046. return ordinalMeta
  18047. ? ordinalMeta.parseAndCollect(value)
  18048. : value;
  18049. }
  18050. if (dimType === 'time'
  18051. // spead up when using timestamp
  18052. && typeof value !== 'number'
  18053. && value != null
  18054. && value !== '-'
  18055. ) {
  18056. value = +parseDate(value);
  18057. }
  18058. // dimType defaults 'number'.
  18059. // If dimType is not ordinal and value is null or undefined or NaN or '-',
  18060. // parse to NaN.
  18061. return (value == null || value === '')
  18062. ? NaN
  18063. // If string (like '-'), using '+' parse to NaN
  18064. // If object, also parse to NaN
  18065. : +value;
  18066. }
  18067. // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,
  18068. // Consider persistent.
  18069. // Caution: why use raw value to display on label or tooltip?
  18070. // A reason is to avoid format. For example time value we do not know
  18071. // how to format is expected. More over, if stack is used, calculated
  18072. // value may be 0.91000000001, which have brings trouble to display.
  18073. // TODO: consider how to treat null/undefined/NaN when display?
  18074. /**
  18075. * @param {module:echarts/data/List} data
  18076. * @param {number} dataIndex
  18077. * @param {string|number} [dim] dimName or dimIndex
  18078. * @return {Array.<number>|string|number} can be null/undefined.
  18079. */
  18080. function retrieveRawValue(data, dataIndex, dim) {
  18081. if (!data) {
  18082. return;
  18083. }
  18084. // Consider data may be not persistent.
  18085. var dataItem = data.getRawDataItem(dataIndex);
  18086. if (dataItem == null) {
  18087. return;
  18088. }
  18089. var sourceFormat = data.getProvider().getSource().sourceFormat;
  18090. var dimName;
  18091. var dimIndex;
  18092. var dimInfo = data.getDimensionInfo(dim);
  18093. if (dimInfo) {
  18094. dimName = dimInfo.name;
  18095. dimIndex = dimInfo.index;
  18096. }
  18097. return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName);
  18098. }
  18099. /**
  18100. * Compatible with some cases (in pie, map) like:
  18101. * data: [{name: 'xx', value: 5, selected: true}, ...]
  18102. * where only sourceFormat is 'original' and 'objectRows' supported.
  18103. *
  18104. * ??? TODO
  18105. * Supported detail options in data item when using 'arrayRows'.
  18106. *
  18107. * @param {module:echarts/data/List} data
  18108. * @param {number} dataIndex
  18109. * @param {string} attr like 'selected'
  18110. */
  18111. function retrieveRawAttr(data, dataIndex, attr) {
  18112. if (!data) {
  18113. return;
  18114. }
  18115. var sourceFormat = data.getProvider().getSource().sourceFormat;
  18116. if (sourceFormat !== SOURCE_FORMAT_ORIGINAL
  18117. && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS
  18118. ) {
  18119. return;
  18120. }
  18121. var dataItem = data.getRawDataItem(dataIndex);
  18122. if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject$1(dataItem)) {
  18123. dataItem = null;
  18124. }
  18125. if (dataItem) {
  18126. return dataItem[attr];
  18127. }
  18128. }
  18129. var DIMENSION_LABEL_REG = /\{@(.+?)\}/g;
  18130. // PENDING A little ugly
  18131. var dataFormatMixin = {
  18132. /**
  18133. * Get params for formatter
  18134. * @param {number} dataIndex
  18135. * @param {string} [dataType]
  18136. * @return {Object}
  18137. */
  18138. getDataParams: function (dataIndex, dataType) {
  18139. var data = this.getData(dataType);
  18140. var rawValue = this.getRawValue(dataIndex, dataType);
  18141. var rawDataIndex = data.getRawIndex(dataIndex);
  18142. var name = data.getName(dataIndex, true);
  18143. var itemOpt = data.getRawDataItem(dataIndex);
  18144. var color = data.getItemVisual(dataIndex, 'color');
  18145. return {
  18146. componentType: this.mainType,
  18147. componentSubType: this.subType,
  18148. seriesType: this.mainType === 'series' ? this.subType : null,
  18149. seriesIndex: this.seriesIndex,
  18150. seriesId: this.id,
  18151. seriesName: this.name,
  18152. name: name,
  18153. dataIndex: rawDataIndex,
  18154. data: itemOpt,
  18155. dataType: dataType,
  18156. value: rawValue,
  18157. color: color,
  18158. marker: getTooltipMarker(color),
  18159. // Param name list for mapping `a`, `b`, `c`, `d`, `e`
  18160. $vars: ['seriesName', 'name', 'value']
  18161. };
  18162. },
  18163. /**
  18164. * Format label
  18165. * @param {number} dataIndex
  18166. * @param {string} [status='normal'] 'normal' or 'emphasis'
  18167. * @param {string} [dataType]
  18168. * @param {number} [dimIndex]
  18169. * @param {string} [labelProp='label']
  18170. * @return {string} If not formatter, return null/undefined
  18171. */
  18172. getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) {
  18173. status = status || 'normal';
  18174. var data = this.getData(dataType);
  18175. var itemModel = data.getItemModel(dataIndex);
  18176. var params = this.getDataParams(dataIndex, dataType);
  18177. if (dimIndex != null && (params.value instanceof Array)) {
  18178. params.value = params.value[dimIndex];
  18179. }
  18180. var formatter = itemModel.get(
  18181. status === 'normal'
  18182. ? [labelProp || 'label', 'formatter']
  18183. : [status, labelProp || 'label', 'formatter']
  18184. );
  18185. if (typeof formatter === 'function') {
  18186. params.status = status;
  18187. return formatter(params);
  18188. }
  18189. else if (typeof formatter === 'string') {
  18190. var str = formatTpl(formatter, params);
  18191. // Support 'aaa{@[3]}bbb{@product}ccc'.
  18192. // Do not support '}' in dim name util have to.
  18193. return str.replace(DIMENSION_LABEL_REG, function (origin, dim) {
  18194. var len = dim.length;
  18195. if (dim.charAt(0) === '[' && dim.charAt(len - 1) === ']') {
  18196. dim = +dim.slice(1, len - 1); // Also: '[]' => 0
  18197. }
  18198. return retrieveRawValue(data, dataIndex, dim);
  18199. });
  18200. }
  18201. },
  18202. /**
  18203. * Get raw value in option
  18204. * @param {number} idx
  18205. * @param {string} [dataType]
  18206. * @return {Array|number|string}
  18207. */
  18208. getRawValue: function (idx, dataType) {
  18209. return retrieveRawValue(this.getData(dataType), idx);
  18210. },
  18211. /**
  18212. * Should be implemented.
  18213. * @param {number} dataIndex
  18214. * @param {boolean} [multipleSeries=false]
  18215. * @param {number} [dataType]
  18216. * @return {string} tooltip string
  18217. */
  18218. formatTooltip: function () {
  18219. // Empty function
  18220. }
  18221. };
  18222. /**
  18223. * @param {Object} define
  18224. * @return See the return of `createTask`.
  18225. */
  18226. function createTask(define) {
  18227. return new Task(define);
  18228. }
  18229. /**
  18230. * @constructor
  18231. * @param {Object} define
  18232. * @param {Function} define.reset Custom reset
  18233. * @param {Function} [define.plan] Returns 'reset' indicate reset immediately.
  18234. * @param {Function} [define.count] count is used to determin data task.
  18235. * @param {Function} [define.onDirty] count is used to determin data task.
  18236. */
  18237. function Task(define) {
  18238. define = define || {};
  18239. this._reset = define.reset;
  18240. this._plan = define.plan;
  18241. this._count = define.count;
  18242. this._onDirty = define.onDirty;
  18243. this._dirty = true;
  18244. // Context must be specified implicitly, to
  18245. // avoid miss update context when model changed.
  18246. this.context;
  18247. }
  18248. var taskProto = Task.prototype;
  18249. /**
  18250. * @param {Object} performArgs
  18251. * @param {number} [performArgs.step] Specified step.
  18252. * @param {number} [performArgs.skip] Skip customer perform call.
  18253. */
  18254. taskProto.perform = function (performArgs) {
  18255. var upTask = this._upstream;
  18256. var skip = performArgs && performArgs.skip;
  18257. // TODO some refactor.
  18258. // Pull data. Must pull data each time, because context.data
  18259. // may be updated by Series.setData.
  18260. if (this._dirty && upTask) {
  18261. var context = this.context;
  18262. context.data = context.outputData = upTask.context.outputData;
  18263. }
  18264. if (this.__pipeline) {
  18265. this.__pipeline.currentTask = this;
  18266. }
  18267. var planResult;
  18268. if (this._plan && !skip) {
  18269. planResult = this._plan(this.context);
  18270. }
  18271. if (this._dirty || planResult === 'reset') {
  18272. this._dirty = false;
  18273. reset(this, skip);
  18274. }
  18275. var step = performArgs && performArgs.step;
  18276. if (upTask) {
  18277. if (__DEV__) {
  18278. assert$1(upTask._outputDueEnd != null);
  18279. }
  18280. // ??? FIXME move to schedueler?
  18281. // this._dueEnd = Math.max(upTask._outputDueEnd, this._dueEnd);
  18282. this._dueEnd = upTask._outputDueEnd;
  18283. }
  18284. // DataTask or overallTask
  18285. else {
  18286. if (__DEV__) {
  18287. assert$1(!this._progress || this._count);
  18288. }
  18289. this._dueEnd = this._count ? this._count(this.context) : Infinity;
  18290. }
  18291. // Note: Stubs, that its host overall task let it has progress, has progress.
  18292. // If no progress, pass index from upstream to downstream each time plan called.
  18293. if (this._progress) {
  18294. var start = this._dueIndex;
  18295. var end = Math.min(
  18296. step != null ? this._dueIndex + step : Infinity,
  18297. this._dueEnd
  18298. );
  18299. !skip && start < end && (
  18300. this._progress({start: start, end: end}, this.context)
  18301. );
  18302. this._dueIndex = end;
  18303. // If no `outputDueEnd`, assume that output data and
  18304. // input data is the same, so use `dueIndex` as `outputDueEnd`.
  18305. var outputDueEnd = this._settedOutputEnd != null
  18306. ? this._settedOutputEnd : end;
  18307. if (__DEV__) {
  18308. // ??? Can not rollback.
  18309. assert$1(outputDueEnd >= this._outputDueEnd);
  18310. }
  18311. this._outputDueEnd = outputDueEnd;
  18312. }
  18313. else {
  18314. // (1) Some overall task has no progress.
  18315. // (2) Stubs, that its host overall task do not let it has progress, has no progress.
  18316. // This should always be performed so it can be passed to downstream.
  18317. this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null
  18318. ? this._settedOutputEnd : this._dueEnd;
  18319. }
  18320. return this.unfinished();
  18321. };
  18322. taskProto.dirty = function () {
  18323. this._dirty = true;
  18324. this._onDirty && this._onDirty(this.context);
  18325. };
  18326. /**
  18327. * @param {Object} [params]
  18328. */
  18329. function reset(taskIns, skip) {
  18330. taskIns._dueIndex = taskIns._outputDueEnd = taskIns._dueEnd = 0;
  18331. taskIns._settedOutputEnd = null;
  18332. taskIns._progress = !skip && taskIns._reset && taskIns._reset(
  18333. taskIns.context
  18334. );
  18335. var downstream = taskIns._downstream;
  18336. downstream && downstream.dirty();
  18337. }
  18338. /**
  18339. * @return {boolean}
  18340. */
  18341. taskProto.unfinished = function () {
  18342. return this._progress && this._dueIndex < this._dueEnd;
  18343. };
  18344. /**
  18345. * @param {Object} downTask The downstream task.
  18346. * @return {Object} The downstream task.
  18347. */
  18348. taskProto.pipe = function (downTask) {
  18349. if (__DEV__) {
  18350. assert$1(downTask && !downTask._disposed && downTask !== this);
  18351. }
  18352. // If already downstream, do not dirty downTask.
  18353. if (this._downstream !== downTask || this._dirty) {
  18354. this._downstream = downTask;
  18355. downTask._upstream = this;
  18356. downTask.dirty();
  18357. }
  18358. };
  18359. taskProto.dispose = function () {
  18360. if (this._disposed) {
  18361. return;
  18362. }
  18363. this._upstream && (this._upstream._downstream = null);
  18364. this._downstream && (this._downstream._upstream = null);
  18365. this._dirty = false;
  18366. this._disposed = true;
  18367. };
  18368. taskProto.getUpstream = function () {
  18369. return this._upstream;
  18370. };
  18371. taskProto.getDownstream = function () {
  18372. return this._downstream;
  18373. };
  18374. taskProto.setOutputEnd = function (end) {
  18375. // ??? FIXME: check
  18376. // This only happend in dataTask, dataZoom, map, currently.
  18377. // where dataZoom do not set end each time, but only set
  18378. // when reset. So we should record the setted end, in case
  18379. // that the stub of dataZoom perform again and earse the
  18380. // setted end by upstream.
  18381. this._outputDueEnd = this._settedOutputEnd = end;
  18382. // this._outputDueEnd = end;
  18383. };
  18384. var inner$4 = makeInner();
  18385. var SeriesModel = ComponentModel.extend({
  18386. type: 'series.__base__',
  18387. /**
  18388. * @readOnly
  18389. */
  18390. seriesIndex: 0,
  18391. // coodinateSystem will be injected in the echarts/CoordinateSystem
  18392. coordinateSystem: null,
  18393. /**
  18394. * @type {Object}
  18395. * @protected
  18396. */
  18397. defaultOption: null,
  18398. /**
  18399. * Data provided for legend
  18400. * @type {Function}
  18401. */
  18402. // PENDING
  18403. legendDataProvider: null,
  18404. /**
  18405. * Access path of color for visual
  18406. */
  18407. visualColorAccessPath: 'itemStyle.color',
  18408. /**
  18409. * Support merge layout params.
  18410. * Only support 'box' now (left/right/top/bottom/width/height).
  18411. * @type {string|Object} Object can be {ignoreSize: true}
  18412. * @readOnly
  18413. */
  18414. layoutMode: null,
  18415. init: function (option, parentModel, ecModel, extraOpt) {
  18416. /**
  18417. * @type {number}
  18418. * @readOnly
  18419. */
  18420. this.seriesIndex = this.componentIndex;
  18421. this.dataTask = createTask({
  18422. count: dataTaskCount,
  18423. reset: dataTaskReset
  18424. });
  18425. this.dataTask.context = {model: this};
  18426. this.mergeDefaultAndTheme(option, ecModel);
  18427. prepareSource(this);
  18428. var data = this.getInitialData(option, ecModel);
  18429. wrapData(data, this);
  18430. this.dataTask.context.data = data;
  18431. if (__DEV__) {
  18432. assert$1(data, 'getInitialData returned invalid data.');
  18433. }
  18434. /**
  18435. * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}
  18436. * @private
  18437. */
  18438. inner$4(this).dataBeforeProcessed = data;
  18439. // If we reverse the order (make data firstly, and then make
  18440. // dataBeforeProcessed by cloneShallow), cloneShallow will
  18441. // cause data.graph.data !== data when using
  18442. // module:echarts/data/Graph or module:echarts/data/Tree.
  18443. // See module:echarts/data/helper/linkList
  18444. // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model
  18445. // init or merge stage, because the data can be restored. So we do not `restoreData`
  18446. // and `setData` here, which forbids calling `seriesModel.getData()` in this stage.
  18447. // Call `seriesModel.getRawData()` instead.
  18448. // this.restoreData();
  18449. autoSeriesName(this);
  18450. },
  18451. /**
  18452. * Util for merge default and theme to option
  18453. * @param {Object} option
  18454. * @param {module:echarts/model/Global} ecModel
  18455. */
  18456. mergeDefaultAndTheme: function (option, ecModel) {
  18457. var layoutMode = this.layoutMode;
  18458. var inputPositionParams = layoutMode
  18459. ? getLayoutParams(option) : {};
  18460. // Backward compat: using subType on theme.
  18461. // But if name duplicate between series subType
  18462. // (for example: parallel) add component mainType,
  18463. // add suffix 'Series'.
  18464. var themeSubType = this.subType;
  18465. if (ComponentModel.hasClass(themeSubType)) {
  18466. themeSubType += 'Series';
  18467. }
  18468. merge(
  18469. option,
  18470. ecModel.getTheme().get(this.subType)
  18471. );
  18472. merge(option, this.getDefaultOption());
  18473. // Default label emphasis `show`
  18474. defaultEmphasis(option, 'label', ['show']);
  18475. this.fillDataTextStyle(option.data);
  18476. if (layoutMode) {
  18477. mergeLayoutParam(option, inputPositionParams, layoutMode);
  18478. }
  18479. },
  18480. mergeOption: function (newSeriesOption, ecModel) {
  18481. // this.settingTask.dirty();
  18482. newSeriesOption = merge(this.option, newSeriesOption, true);
  18483. this.fillDataTextStyle(newSeriesOption.data);
  18484. var layoutMode = this.layoutMode;
  18485. if (layoutMode) {
  18486. mergeLayoutParam(this.option, newSeriesOption, layoutMode);
  18487. }
  18488. prepareSource(this);
  18489. var data = this.getInitialData(newSeriesOption, ecModel);
  18490. wrapData(data, this);
  18491. this.dataTask.dirty();
  18492. this.dataTask.context.data = data;
  18493. inner$4(this).dataBeforeProcessed = data;
  18494. autoSeriesName(this);
  18495. },
  18496. fillDataTextStyle: function (data) {
  18497. // Default data label emphasis `show`
  18498. // FIXME Tree structure data ?
  18499. // FIXME Performance ?
  18500. if (data) {
  18501. var props = ['show'];
  18502. for (var i = 0; i < data.length; i++) {
  18503. if (data[i] && data[i].label) {
  18504. defaultEmphasis(data[i], 'label', props);
  18505. }
  18506. }
  18507. }
  18508. },
  18509. /**
  18510. * Init a data structure from data related option in series
  18511. * Must be overwritten
  18512. */
  18513. getInitialData: function () {},
  18514. /**
  18515. * Append data to list
  18516. * @param {Object} params
  18517. * @param {Array|TypedArray} params.data
  18518. */
  18519. appendData: function (params) {
  18520. // FIXME ???
  18521. // (1) If data from dataset, forbidden append.
  18522. // (2) support append data of dataset.
  18523. var data = this.getRawData();
  18524. data.appendData(params.data);
  18525. },
  18526. /**
  18527. * Consider some method like `filter`, `map` need make new data,
  18528. * We should make sure that `seriesModel.getData()` get correct
  18529. * data in the stream procedure. So we fetch data from upstream
  18530. * each time `task.perform` called.
  18531. * @param {string} [dataType]
  18532. * @return {module:echarts/data/List}
  18533. */
  18534. getData: function (dataType) {
  18535. var task = getCurrentTask(this);
  18536. if (task) {
  18537. var data = task.context.data;
  18538. return dataType == null ? data : data.getLinkedData(dataType);
  18539. }
  18540. else {
  18541. // When series is not alive (that may happen when click toolbox
  18542. // restore or setOption with not merge mode), series data may
  18543. // be still need to judge animation or something when graphic
  18544. // elements want to know whether fade out.
  18545. return inner$4(this).data;
  18546. }
  18547. },
  18548. /**
  18549. * @param {module:echarts/data/List} data
  18550. */
  18551. setData: function (data) {
  18552. var task = getCurrentTask(this);
  18553. if (task) {
  18554. var context = task.context;
  18555. // Consider case: filter, data sample.
  18556. if (context.data !== data && task.isOverallFilter) {
  18557. task.setOutputEnd(data.count());
  18558. }
  18559. context.outputData = data;
  18560. // Caution: setData should update context.data,
  18561. // Because getData may be called multiply in a
  18562. // single stage and expect to get the data just
  18563. // set. (For example, AxisProxy, x y both call
  18564. // getData and setDate sequentially).
  18565. // So the context.data should be fetched from
  18566. // upstream each time when a stage starts to be
  18567. // performed.
  18568. if (task !== this.dataTask) {
  18569. context.data = data;
  18570. }
  18571. }
  18572. inner$4(this).data = data;
  18573. },
  18574. /**
  18575. * @see {module:echarts/data/helper/sourceHelper#getSource}
  18576. * @return {module:echarts/data/Source} source
  18577. */
  18578. getSource: function () {
  18579. return getSource(this);
  18580. },
  18581. /**
  18582. * Get data before processed
  18583. * @return {module:echarts/data/List}
  18584. */
  18585. getRawData: function () {
  18586. return inner$4(this).dataBeforeProcessed;
  18587. },
  18588. /**
  18589. * Get base axis if has coordinate system and has axis.
  18590. * By default use coordSys.getBaseAxis();
  18591. * Can be overrided for some chart.
  18592. * @return {type} description
  18593. */
  18594. getBaseAxis: function () {
  18595. var coordSys = this.coordinateSystem;
  18596. return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();
  18597. },
  18598. // FIXME
  18599. /**
  18600. * Default tooltip formatter
  18601. *
  18602. * @param {number} dataIndex
  18603. * @param {boolean} [multipleSeries=false]
  18604. * @param {number} [dataType]
  18605. */
  18606. formatTooltip: function (dataIndex, multipleSeries, dataType) {
  18607. function formatArrayValue(value) {
  18608. // ??? TODO refactor these logic.
  18609. // check: category-no-encode-has-axis-data in dataset.html
  18610. var vertially = reduce(value, function (vertially, val, idx) {
  18611. var dimItem = data.getDimensionInfo(idx);
  18612. return vertially |= dimItem && dimItem.tooltip !== false && dimItem.displayName != null;
  18613. }, 0);
  18614. var result = [];
  18615. tooltipDims.length
  18616. ? each$1(tooltipDims, function (dim) {
  18617. setEachItem(retrieveRawValue(data, dataIndex, dim), dim);
  18618. })
  18619. // By default, all dims is used on tooltip.
  18620. : each$1(value, setEachItem);
  18621. function setEachItem(val, dim) {
  18622. var dimInfo = data.getDimensionInfo(dim);
  18623. // If `dimInfo.tooltip` is not set, show tooltip.
  18624. if (!dimInfo || dimInfo.otherDims.tooltip === false) {
  18625. return;
  18626. }
  18627. var dimType = dimInfo.type;
  18628. var dimHead = getTooltipMarker({color: color, type: 'subItem'});
  18629. var valStr = (vertially
  18630. ? dimHead + encodeHTML(dimInfo.displayName || '-') + ': '
  18631. : ''
  18632. )
  18633. // FIXME should not format time for raw data?
  18634. + encodeHTML(dimType === 'ordinal'
  18635. ? val + ''
  18636. : dimType === 'time'
  18637. ? (multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val))
  18638. : addCommas(val)
  18639. );
  18640. valStr && result.push(valStr);
  18641. }
  18642. return (vertially ? '<br/>' : '') + result.join(vertially ? '<br/>' : ', ');
  18643. }
  18644. function formatSingleValue(val) {
  18645. return encodeHTML(addCommas(val));
  18646. }
  18647. var data = this.getData();
  18648. var tooltipDims = data.mapDimension('defaultedTooltip', true);
  18649. var tooltipDimLen = tooltipDims.length;
  18650. var value = this.getRawValue(dataIndex);
  18651. var isValueArr = isArray(value);
  18652. var color = data.getItemVisual(dataIndex, 'color');
  18653. if (isObject$1(color) && color.colorStops) {
  18654. color = (color.colorStops[0] || {}).color;
  18655. }
  18656. color = color || 'transparent';
  18657. // Complicated rule for pretty tooltip.
  18658. var formattedValue = (tooltipDimLen > 1 || (isValueArr && !tooltipDimLen))
  18659. ? formatArrayValue(value)
  18660. : tooltipDimLen
  18661. ? formatSingleValue(retrieveRawValue(data, dataIndex, tooltipDims[0]))
  18662. : formatSingleValue(isValueArr ? value[0] : value);
  18663. var colorEl = getTooltipMarker(color);
  18664. var name = data.getName(dataIndex);
  18665. var seriesName = this.name;
  18666. if (seriesName === DEFAULT_COMPONENT_NAME) {
  18667. // Not show '-'
  18668. seriesName = '';
  18669. }
  18670. seriesName = seriesName
  18671. ? encodeHTML(seriesName) + (!multipleSeries ? '<br/>' : ': ')
  18672. : '';
  18673. return !multipleSeries
  18674. ? seriesName + colorEl
  18675. + (name
  18676. ? encodeHTML(name) + ': ' + formattedValue
  18677. : formattedValue
  18678. )
  18679. : colorEl + seriesName + formattedValue;
  18680. },
  18681. /**
  18682. * @return {boolean}
  18683. */
  18684. isAnimationEnabled: function () {
  18685. if (env$1.node) {
  18686. return false;
  18687. }
  18688. var animationEnabled = this.getShallow('animation');
  18689. if (animationEnabled) {
  18690. if (this.getData().count() > this.getShallow('animationThreshold')) {
  18691. animationEnabled = false;
  18692. }
  18693. }
  18694. return animationEnabled;
  18695. },
  18696. restoreData: function () {
  18697. this.dataTask.dirty();
  18698. },
  18699. getColorFromPalette: function (name, scope, requestColorNum) {
  18700. var ecModel = this.ecModel;
  18701. // PENDING
  18702. var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope, requestColorNum);
  18703. if (!color) {
  18704. color = ecModel.getColorFromPalette(name, scope, requestColorNum);
  18705. }
  18706. return color;
  18707. },
  18708. /**
  18709. * Use `data.mapDimension(coordDim, true)` instead.
  18710. * @deprecated
  18711. */
  18712. coordDimToDataDim: function (coordDim) {
  18713. return this.getRawData().mapDimension(coordDim, true);
  18714. },
  18715. /**
  18716. * Get progressive rendering count each step
  18717. * @return {number}
  18718. */
  18719. getProgressive: function () {
  18720. return this.get('progressive');
  18721. },
  18722. /**
  18723. * Get progressive rendering count each step
  18724. * @return {number}
  18725. */
  18726. getProgressiveThreshold: function () {
  18727. return this.get('progressiveThreshold');
  18728. },
  18729. /**
  18730. * Get data indices for show tooltip content. See tooltip.
  18731. * @abstract
  18732. * @param {Array.<string>|string} dim
  18733. * @param {Array.<number>} value
  18734. * @param {module:echarts/coord/single/SingleAxis} baseAxis
  18735. * @return {Object} {dataIndices, nestestValue}.
  18736. */
  18737. getAxisTooltipData: null,
  18738. /**
  18739. * See tooltip.
  18740. * @abstract
  18741. * @param {number} dataIndex
  18742. * @return {Array.<number>} Point of tooltip. null/undefined can be returned.
  18743. */
  18744. getTooltipPosition: null,
  18745. /**
  18746. * @see {module:echarts/stream/Scheduler}
  18747. */
  18748. pipeTask: null,
  18749. /**
  18750. * Convinient for override in extended class.
  18751. * @protected
  18752. * @type {Function}
  18753. */
  18754. preventIncremental: null,
  18755. /**
  18756. * @public
  18757. * @readOnly
  18758. * @type {Object}
  18759. */
  18760. pipelineContext: null
  18761. });
  18762. mixin(SeriesModel, dataFormatMixin);
  18763. mixin(SeriesModel, colorPaletteMixin);
  18764. /**
  18765. * MUST be called after `prepareSource` called
  18766. * Here we need to make auto series, especially for auto legend. But we
  18767. * do not modify series.name in option to avoid side effects.
  18768. */
  18769. function autoSeriesName(seriesModel) {
  18770. // User specified name has higher priority, otherwise it may cause
  18771. // series can not be queried unexpectedly.
  18772. var name = seriesModel.name;
  18773. if (DEFAULT_COMPONENT_NAME === name) {
  18774. seriesModel.name = getSeriesAutoName(seriesModel) || name;
  18775. }
  18776. }
  18777. function getSeriesAutoName(seriesModel) {
  18778. var data = seriesModel.getRawData();
  18779. var dataDims = data.mapDimension('seriesName', true);
  18780. var nameArr = [];
  18781. each$1(dataDims, function (dataDim) {
  18782. var dimInfo = data.getDimensionInfo(dataDim);
  18783. dimInfo.displayName && nameArr.push(dimInfo.displayName);
  18784. });
  18785. return nameArr.join(' ');
  18786. }
  18787. function dataTaskCount(context) {
  18788. return context.model.getRawData().count();
  18789. }
  18790. function dataTaskReset(context) {
  18791. var seriesModel = context.model;
  18792. seriesModel.setData(seriesModel.getRawData().cloneShallow());
  18793. return dataTaskProgress;
  18794. }
  18795. function dataTaskProgress(param, context) {
  18796. // Avoid repead cloneShallow when data just created in reset.
  18797. if (param.end > context.outputData.count()) {
  18798. context.model.getRawData().cloneShallow(context.outputData);
  18799. }
  18800. }
  18801. // TODO refactor
  18802. function wrapData(data, seriesModel) {
  18803. each$1(data.CHANGABLE_METHODS, function (methodName) {
  18804. data.wrapMethod(methodName, curry(onDataSelfChange, seriesModel));
  18805. });
  18806. }
  18807. function onDataSelfChange(seriesModel) {
  18808. var task = getCurrentTask(seriesModel);
  18809. if (task) {
  18810. // Consider case: filter, selectRange
  18811. task.setOutputEnd(this.count());
  18812. }
  18813. }
  18814. function getCurrentTask(seriesModel) {
  18815. var scheduler = (seriesModel.ecModel || {}).scheduler;
  18816. var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid);
  18817. if (pipeline) {
  18818. // When pipline finished, the currrentTask keep the last
  18819. // task (renderTask).
  18820. var task = pipeline.currentTask;
  18821. if (task) {
  18822. var agentStubMap = task.agentStubMap;
  18823. if (agentStubMap) {
  18824. task = agentStubMap.get(seriesModel.uid);
  18825. }
  18826. }
  18827. return task;
  18828. }
  18829. }
  18830. var Component = function () {
  18831. /**
  18832. * @type {module:zrender/container/Group}
  18833. * @readOnly
  18834. */
  18835. this.group = new Group();
  18836. /**
  18837. * @type {string}
  18838. * @readOnly
  18839. */
  18840. this.uid = getUID('viewComponent');
  18841. };
  18842. Component.prototype = {
  18843. constructor: Component,
  18844. init: function (ecModel, api) {},
  18845. render: function (componentModel, ecModel, api, payload) {},
  18846. dispose: function () {}
  18847. };
  18848. var componentProto = Component.prototype;
  18849. componentProto.updateView
  18850. = componentProto.updateLayout
  18851. = componentProto.updateVisual
  18852. = function (seriesModel, ecModel, api, payload) {
  18853. // Do nothing;
  18854. };
  18855. // Enable Component.extend.
  18856. enableClassExtend(Component);
  18857. // Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  18858. enableClassManagement(Component, {registerWhenExtend: true});
  18859. /**
  18860. * @return {string} If large mode changed, return string 'reset';
  18861. */
  18862. var createRenderPlanner = function () {
  18863. var inner = makeInner();
  18864. return function (seriesModel) {
  18865. var fields = inner(seriesModel);
  18866. var pipelineContext = seriesModel.pipelineContext;
  18867. var originalLarge = fields.large;
  18868. var originalIncremental = fields.incrementalRender;
  18869. var large = fields.large = pipelineContext.large;
  18870. var incremental = fields.incrementalRender = pipelineContext.incrementalRender;
  18871. return ((originalLarge ^ large) || (originalIncremental ^ incremental)) && 'reset';
  18872. };
  18873. };
  18874. var inner$5 = makeInner();
  18875. var renderPlanner = createRenderPlanner();
  18876. function Chart() {
  18877. /**
  18878. * @type {module:zrender/container/Group}
  18879. * @readOnly
  18880. */
  18881. this.group = new Group();
  18882. /**
  18883. * @type {string}
  18884. * @readOnly
  18885. */
  18886. this.uid = getUID('viewChart');
  18887. this.renderTask = createTask({
  18888. plan: renderTaskPlan,
  18889. reset: renderTaskReset
  18890. });
  18891. this.renderTask.context = {view: this};
  18892. }
  18893. Chart.prototype = {
  18894. type: 'chart',
  18895. /**
  18896. * Init the chart.
  18897. * @param {module:echarts/model/Global} ecModel
  18898. * @param {module:echarts/ExtensionAPI} api
  18899. */
  18900. init: function (ecModel, api) {},
  18901. /**
  18902. * Render the chart.
  18903. * @param {module:echarts/model/Series} seriesModel
  18904. * @param {module:echarts/model/Global} ecModel
  18905. * @param {module:echarts/ExtensionAPI} api
  18906. * @param {Object} payload
  18907. */
  18908. render: function (seriesModel, ecModel, api, payload) {},
  18909. /**
  18910. * Highlight series or specified data item.
  18911. * @param {module:echarts/model/Series} seriesModel
  18912. * @param {module:echarts/model/Global} ecModel
  18913. * @param {module:echarts/ExtensionAPI} api
  18914. * @param {Object} payload
  18915. */
  18916. highlight: function (seriesModel, ecModel, api, payload) {
  18917. toggleHighlight(seriesModel.getData(), payload, 'emphasis');
  18918. },
  18919. /**
  18920. * Downplay series or specified data item.
  18921. * @param {module:echarts/model/Series} seriesModel
  18922. * @param {module:echarts/model/Global} ecModel
  18923. * @param {module:echarts/ExtensionAPI} api
  18924. * @param {Object} payload
  18925. */
  18926. downplay: function (seriesModel, ecModel, api, payload) {
  18927. toggleHighlight(seriesModel.getData(), payload, 'normal');
  18928. },
  18929. /**
  18930. * Remove self.
  18931. * @param {module:echarts/model/Global} ecModel
  18932. * @param {module:echarts/ExtensionAPI} api
  18933. */
  18934. remove: function (ecModel, api) {
  18935. this.group.removeAll();
  18936. },
  18937. /**
  18938. * Dispose self.
  18939. * @param {module:echarts/model/Global} ecModel
  18940. * @param {module:echarts/ExtensionAPI} api
  18941. */
  18942. dispose: function () {},
  18943. /**
  18944. * Rendering preparation in progressive mode.
  18945. * @param {module:echarts/model/Series} seriesModel
  18946. * @param {module:echarts/model/Global} ecModel
  18947. * @param {module:echarts/ExtensionAPI} api
  18948. * @param {Object} payload
  18949. */
  18950. incrementalPrepareRender: null,
  18951. /**
  18952. * Render in progressive mode.
  18953. * @param {module:echarts/model/Series} seriesModel
  18954. * @param {module:echarts/model/Global} ecModel
  18955. * @param {module:echarts/ExtensionAPI} api
  18956. * @param {Object} payload
  18957. */
  18958. incrementalRender: null,
  18959. /**
  18960. * Update transform directly.
  18961. * @param {module:echarts/model/Series} seriesModel
  18962. * @param {module:echarts/model/Global} ecModel
  18963. * @param {module:echarts/ExtensionAPI} api
  18964. * @param {Object} payload
  18965. * @return {Object} {update: true}
  18966. */
  18967. updateTransform: null
  18968. /**
  18969. * The view contains the given point.
  18970. * @interface
  18971. * @param {Array.<number>} point
  18972. * @return {boolean}
  18973. */
  18974. // containPoint: function () {}
  18975. };
  18976. var chartProto = Chart.prototype;
  18977. chartProto.updateView
  18978. = chartProto.updateLayout
  18979. = chartProto.updateVisual
  18980. = function (seriesModel, ecModel, api, payload) {
  18981. this.render(seriesModel, ecModel, api, payload);
  18982. };
  18983. /**
  18984. * Set state of single element
  18985. * @param {module:zrender/Element} el
  18986. * @param {string} state
  18987. */
  18988. function elSetState(el, state) {
  18989. if (el) {
  18990. el.trigger(state);
  18991. if (el.type === 'group') {
  18992. for (var i = 0; i < el.childCount(); i++) {
  18993. elSetState(el.childAt(i), state);
  18994. }
  18995. }
  18996. }
  18997. }
  18998. /**
  18999. * @param {module:echarts/data/List} data
  19000. * @param {Object} payload
  19001. * @param {string} state 'normal'|'emphasis'
  19002. */
  19003. function toggleHighlight(data, payload, state) {
  19004. var dataIndex = queryDataIndex(data, payload);
  19005. if (dataIndex != null) {
  19006. each$1(normalizeToArray(dataIndex), function (dataIdx) {
  19007. elSetState(data.getItemGraphicEl(dataIdx), state);
  19008. });
  19009. }
  19010. else {
  19011. data.eachItemGraphicEl(function (el) {
  19012. elSetState(el, state);
  19013. });
  19014. }
  19015. }
  19016. // Enable Chart.extend.
  19017. enableClassExtend(Chart, ['dispose']);
  19018. // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  19019. enableClassManagement(Chart, {registerWhenExtend: true});
  19020. Chart.markUpdateMethod = function (payload, methodName) {
  19021. inner$5(payload).updateMethod = methodName;
  19022. };
  19023. function renderTaskPlan(context) {
  19024. return renderPlanner(context.model);
  19025. }
  19026. function renderTaskReset(context) {
  19027. var seriesModel = context.model;
  19028. var ecModel = context.ecModel;
  19029. var api = context.api;
  19030. var payload = context.payload;
  19031. // ???! remove updateView updateVisual
  19032. var incremental = seriesModel.pipelineContext.incrementalRender;
  19033. var view = context.view;
  19034. var updateMethod = payload && inner$5(payload).updateMethod;
  19035. var methodName = (incremental && view.incrementalPrepareRender)
  19036. ? 'incrementalPrepareRender'
  19037. : (updateMethod && view[updateMethod])
  19038. ? updateMethod
  19039. : 'render';
  19040. view[methodName](seriesModel, ecModel, api, payload);
  19041. return incremental ? renderTaskProgress : null;
  19042. }
  19043. function renderTaskProgress(params, context) {
  19044. context.view.incrementalRender(
  19045. params, context.model, context.ecModel, context.api, context.payload
  19046. );
  19047. }
  19048. /**
  19049. * @public
  19050. * @param {(Function)} fn
  19051. * @param {number} [delay=0] Unit: ms.
  19052. * @param {boolean} [debounce=false]
  19053. * true: If call interval less than `delay`, only the last call works.
  19054. * false: If call interval less than `delay, call works on fixed rate.
  19055. * @return {(Function)} throttled fn.
  19056. */
  19057. function throttle(fn, delay, debounce) {
  19058. var currCall;
  19059. var lastCall = 0;
  19060. var lastExec = 0;
  19061. var timer = null;
  19062. var diff;
  19063. var scope;
  19064. var args;
  19065. var debounceNextCall;
  19066. delay = delay || 0;
  19067. function exec() {
  19068. lastExec = (new Date()).getTime();
  19069. timer = null;
  19070. fn.apply(scope, args || []);
  19071. }
  19072. var cb = function () {
  19073. currCall = (new Date()).getTime();
  19074. scope = this;
  19075. args = arguments;
  19076. var thisDelay = debounceNextCall || delay;
  19077. var thisDebounce = debounceNextCall || debounce;
  19078. debounceNextCall = null;
  19079. diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
  19080. clearTimeout(timer);
  19081. if (thisDebounce) {
  19082. timer = setTimeout(exec, thisDelay);
  19083. }
  19084. else {
  19085. if (diff >= 0) {
  19086. exec();
  19087. }
  19088. else {
  19089. timer = setTimeout(exec, -diff);
  19090. }
  19091. }
  19092. lastCall = currCall;
  19093. };
  19094. /**
  19095. * Clear throttle.
  19096. * @public
  19097. */
  19098. cb.clear = function () {
  19099. if (timer) {
  19100. clearTimeout(timer);
  19101. timer = null;
  19102. }
  19103. };
  19104. /**
  19105. * Enable debounce once.
  19106. */
  19107. cb.debounceNextCall = function (debounceDelay) {
  19108. debounceNextCall = debounceDelay;
  19109. };
  19110. return cb;
  19111. }
  19112. /**
  19113. * Create throttle method or update throttle rate.
  19114. *
  19115. * @example
  19116. * ComponentView.prototype.render = function () {
  19117. * ...
  19118. * throttle.createOrUpdate(
  19119. * this,
  19120. * '_dispatchAction',
  19121. * this.model.get('throttle'),
  19122. * 'fixRate'
  19123. * );
  19124. * };
  19125. * ComponentView.prototype.remove = function () {
  19126. * throttle.clear(this, '_dispatchAction');
  19127. * };
  19128. * ComponentView.prototype.dispose = function () {
  19129. * throttle.clear(this, '_dispatchAction');
  19130. * };
  19131. *
  19132. * @public
  19133. * @param {Object} obj
  19134. * @param {string} fnAttr
  19135. * @param {number} [rate]
  19136. * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce'
  19137. * @return {Function} throttled function.
  19138. */
  19139. /**
  19140. * Clear throttle. Example see throttle.createOrUpdate.
  19141. *
  19142. * @public
  19143. * @param {Object} obj
  19144. * @param {string} fnAttr
  19145. */
  19146. var seriesColor = {
  19147. createOnAllSeries: true,
  19148. performRawSeries: true,
  19149. reset: function (seriesModel, ecModel) {
  19150. var data = seriesModel.getData();
  19151. var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.color').split('.');
  19152. var color = seriesModel.get(colorAccessPath) // Set in itemStyle
  19153. || seriesModel.getColorFromPalette(
  19154. // TODO series count changed.
  19155. seriesModel.get('name'), null, ecModel.getSeriesCount()
  19156. ); // Default color
  19157. // FIXME Set color function or use the platte color
  19158. data.setVisual('color', color);
  19159. // Only visible series has each data be visual encoded
  19160. if (!ecModel.isSeriesFiltered(seriesModel)) {
  19161. if (typeof color === 'function' && !(color instanceof Gradient)) {
  19162. data.each(function (idx) {
  19163. data.setItemVisual(
  19164. idx, 'color', color(seriesModel.getDataParams(idx))
  19165. );
  19166. });
  19167. }
  19168. // itemStyle in each data item
  19169. var dataEach = function (data, idx) {
  19170. var itemModel = data.getItemModel(idx);
  19171. var color = itemModel.get(colorAccessPath, true);
  19172. if (color != null) {
  19173. data.setItemVisual(idx, 'color', color);
  19174. }
  19175. };
  19176. return { dataEach: data.hasItemOption ? dataEach : null };
  19177. }
  19178. }
  19179. };
  19180. var lang = {
  19181. toolbox: {
  19182. brush: {
  19183. title: {
  19184. rect: 'Box Select',
  19185. polygon: 'Lasso Select',
  19186. lineX: 'Horizontally Select',
  19187. lineY: 'Vertically Select',
  19188. keep: 'Keep Selections',
  19189. clear: 'Clear Selections'
  19190. }
  19191. },
  19192. dataView: {
  19193. title: 'Data View',
  19194. lang: ['Data View', 'Close', 'Refresh']
  19195. },
  19196. dataZoom: {
  19197. title: {
  19198. zoom: 'Zoom',
  19199. back: 'Zoom Reset'
  19200. }
  19201. },
  19202. magicType: {
  19203. title: {
  19204. line: 'Switch to Line Chart',
  19205. bar: 'Switch to Bar Chart',
  19206. stack: 'Stack',
  19207. tiled: 'Tile'
  19208. }
  19209. },
  19210. restore: {
  19211. title: 'Restore'
  19212. },
  19213. saveAsImage: {
  19214. title: 'Save as Image',
  19215. lang: ['Right Click to Save Image']
  19216. }
  19217. }
  19218. };
  19219. var aria = function (dom, ecModel) {
  19220. var ariaModel = ecModel.getModel('aria');
  19221. if (!ariaModel.get('show')) {
  19222. return;
  19223. }
  19224. else if (ariaModel.get('description')) {
  19225. dom.setAttribute('aria-label', ariaModel.get('description'));
  19226. return;
  19227. }
  19228. var seriesCnt = 0;
  19229. ecModel.eachSeries(function (seriesModel, idx) {
  19230. ++seriesCnt;
  19231. }, this);
  19232. var maxDataCnt = ariaModel.get('data.maxCount') || 10;
  19233. var maxSeriesCnt = ariaModel.get('series.maxCount') || 10;
  19234. var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt);
  19235. var ariaLabel;
  19236. if (seriesCnt < 1) {
  19237. // No series, no aria label
  19238. return;
  19239. }
  19240. else {
  19241. var title = getTitle();
  19242. if (title) {
  19243. ariaLabel = replace(getConfig('general.withTitle'), {
  19244. title: title
  19245. });
  19246. }
  19247. else {
  19248. ariaLabel = getConfig('general.withoutTitle');
  19249. }
  19250. var seriesLabels = [];
  19251. var prefix = seriesCnt > 1
  19252. ? 'series.multiple.prefix'
  19253. : 'series.single.prefix';
  19254. ariaLabel += replace(getConfig(prefix), { seriesCount: seriesCnt });
  19255. ecModel.eachSeries(function (seriesModel, idx) {
  19256. if (idx < displaySeriesCnt) {
  19257. var seriesLabel;
  19258. var seriesName = seriesModel.get('name');
  19259. var seriesTpl = 'series.'
  19260. + (seriesCnt > 1 ? 'multiple' : 'single') + '.';
  19261. seriesLabel = getConfig(seriesName
  19262. ? seriesTpl + 'withName'
  19263. : seriesTpl + 'withoutName');
  19264. seriesLabel = replace(seriesLabel, {
  19265. seriesId: seriesModel.seriesIndex,
  19266. seriesName: seriesModel.get('name'),
  19267. seriesType: getSeriesTypeName(seriesModel.subType)
  19268. });
  19269. var data = seriesModel.getData();
  19270. window.data = data;
  19271. if (data.count() > maxDataCnt) {
  19272. // Show part of data
  19273. seriesLabel += replace(getConfig('data.partialData'), {
  19274. displayCnt: maxDataCnt
  19275. });
  19276. }
  19277. else {
  19278. seriesLabel += getConfig('data.allData');
  19279. }
  19280. var dataLabels = [];
  19281. for (var i = 0; i < data.count(); i++) {
  19282. if (i < maxDataCnt) {
  19283. var name = data.getName(i);
  19284. var value = retrieveRawValue(data, i);
  19285. dataLabels.push(
  19286. replace(
  19287. name
  19288. ? getConfig('data.withName')
  19289. : getConfig('data.withoutName'),
  19290. {
  19291. name: name,
  19292. value: value
  19293. }
  19294. )
  19295. );
  19296. }
  19297. }
  19298. seriesLabel += dataLabels
  19299. .join(getConfig('data.separator.middle'))
  19300. + getConfig('data.separator.end');
  19301. seriesLabels.push(seriesLabel);
  19302. }
  19303. });
  19304. ariaLabel += seriesLabels
  19305. .join(getConfig('series.multiple.separator.middle'))
  19306. + getConfig('series.multiple.separator.end');
  19307. dom.setAttribute('aria-label', ariaLabel);
  19308. }
  19309. function replace(str, keyValues) {
  19310. if (typeof str !== 'string') {
  19311. return str;
  19312. }
  19313. var result = str;
  19314. each$1(keyValues, function (value, key) {
  19315. result = result.replace(
  19316. new RegExp('\\{\\s*' + key + '\\s*\\}', 'g'),
  19317. value
  19318. );
  19319. });
  19320. return result;
  19321. }
  19322. function getConfig(path) {
  19323. var userConfig = ariaModel.get(path);
  19324. if (userConfig == null) {
  19325. var pathArr = path.split('.');
  19326. var result = lang.aria;
  19327. for (var i = 0; i < pathArr.length; ++i) {
  19328. result = result[pathArr[i]];
  19329. }
  19330. return result;
  19331. }
  19332. else {
  19333. return userConfig;
  19334. }
  19335. }
  19336. function getTitle() {
  19337. var title = ecModel.getModel('title').option;
  19338. if (title && title.length) {
  19339. title = title[0];
  19340. }
  19341. return title && title.text;
  19342. }
  19343. function getSeriesTypeName(type) {
  19344. return lang.series.typeNames[type] || '自定义图';
  19345. }
  19346. };
  19347. var PI$1 = Math.PI;
  19348. /**
  19349. * @param {module:echarts/ExtensionAPI} api
  19350. * @param {Object} [opts]
  19351. * @param {string} [opts.text]
  19352. * @param {string} [opts.color]
  19353. * @param {string} [opts.textColor]
  19354. * @return {module:zrender/Element}
  19355. */
  19356. var loadingDefault = function (api, opts) {
  19357. opts = opts || {};
  19358. defaults(opts, {
  19359. text: 'loading',
  19360. color: '#c23531',
  19361. textColor: '#000',
  19362. maskColor: 'rgba(255, 255, 255, 0.8)',
  19363. zlevel: 0
  19364. });
  19365. var mask = new Rect({
  19366. style: {
  19367. fill: opts.maskColor
  19368. },
  19369. zlevel: opts.zlevel,
  19370. z: 10000
  19371. });
  19372. var arc = new Arc({
  19373. shape: {
  19374. startAngle: -PI$1 / 2,
  19375. endAngle: -PI$1 / 2 + 0.1,
  19376. r: 10
  19377. },
  19378. style: {
  19379. stroke: opts.color,
  19380. lineCap: 'round',
  19381. lineWidth: 5
  19382. },
  19383. zlevel: opts.zlevel,
  19384. z: 10001
  19385. });
  19386. var labelRect = new Rect({
  19387. style: {
  19388. fill: 'none',
  19389. text: opts.text,
  19390. textPosition: 'right',
  19391. textDistance: 10,
  19392. textFill: opts.textColor
  19393. },
  19394. zlevel: opts.zlevel,
  19395. z: 10001
  19396. });
  19397. arc.animateShape(true)
  19398. .when(1000, {
  19399. endAngle: PI$1 * 3 / 2
  19400. })
  19401. .start('circularInOut');
  19402. arc.animateShape(true)
  19403. .when(1000, {
  19404. startAngle: PI$1 * 3 / 2
  19405. })
  19406. .delay(300)
  19407. .start('circularInOut');
  19408. var group = new Group();
  19409. group.add(arc);
  19410. group.add(labelRect);
  19411. group.add(mask);
  19412. // Inject resize
  19413. group.resize = function () {
  19414. var cx = api.getWidth() / 2;
  19415. var cy = api.getHeight() / 2;
  19416. arc.setShape({
  19417. cx: cx,
  19418. cy: cy
  19419. });
  19420. var r = arc.shape.r;
  19421. labelRect.setShape({
  19422. x: cx - r,
  19423. y: cy - r,
  19424. width: r * 2,
  19425. height: r * 2
  19426. });
  19427. mask.setShape({
  19428. x: 0,
  19429. y: 0,
  19430. width: api.getWidth(),
  19431. height: api.getHeight()
  19432. });
  19433. };
  19434. group.resize();
  19435. return group;
  19436. };
  19437. /**
  19438. * @module echarts/stream/Scheduler
  19439. */
  19440. /**
  19441. * @constructor
  19442. */
  19443. function Scheduler(ecInstance, api) {
  19444. // this._pipelineMap = createHashMap();
  19445. this.ecInstance = ecInstance;
  19446. this.api = api;
  19447. this.unfinished;
  19448. /**
  19449. * @private
  19450. * @type {
  19451. * [handlerUID: string]: {
  19452. * seriesTaskMap?: {
  19453. * [seriesUID: string]: Task
  19454. * },
  19455. * overallTask?: Task
  19456. * }
  19457. * }
  19458. */
  19459. this._stageTaskMap = createHashMap();
  19460. }
  19461. var proto = Scheduler.prototype;
  19462. // If seriesModel provided, incremental threshold is check by series data.
  19463. proto.getPerformArgs = function (task, isBlock) {
  19464. // For overall task
  19465. if (!task.__pipeline) {
  19466. return;
  19467. }
  19468. var pipeline = this._pipelineMap.get(task.__pipeline.id);
  19469. var pCtx = pipeline.context;
  19470. var incremental = !isBlock
  19471. && pipeline.progressiveEnabled
  19472. && (!pCtx || pCtx.incrementalRender)
  19473. && task.__idxInPipeline > pipeline.bockIndex;
  19474. return {step: incremental ? pipeline.step : null};
  19475. };
  19476. proto.getPipeline = function (pipelineId) {
  19477. return this._pipelineMap.get(pipelineId);
  19478. };
  19479. /**
  19480. * Current, progressive rendering starts from visual and layout.
  19481. * Always detect render mode in the same stage, avoiding that incorrect
  19482. * detection caused by data filtering.
  19483. * Caution:
  19484. * `updateStreamModes` use `seriesModel.getData()`.
  19485. */
  19486. proto.updateStreamModes = function (seriesModel, view) {
  19487. var pipeline = this._pipelineMap.get(seriesModel.uid);
  19488. var data = seriesModel.getData();
  19489. var dataLen = data.count();
  19490. var incrementalRender = pipeline.progressiveEnabled
  19491. && view.incrementalPrepareRender
  19492. && dataLen >= pipeline.threshold;
  19493. var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold');
  19494. seriesModel.pipelineContext = pipeline.context = {
  19495. incrementalRender: incrementalRender,
  19496. large: large
  19497. };
  19498. };
  19499. proto.restorePipelines = function (ecModel) {
  19500. var scheduler = this;
  19501. var pipelineMap = scheduler._pipelineMap = createHashMap();
  19502. ecModel.eachSeries(function (seriesModel) {
  19503. var progressive = seriesModel.getProgressive();
  19504. var pipelineId = seriesModel.uid;
  19505. pipelineMap.set(pipelineId, {
  19506. id: pipelineId,
  19507. head: null,
  19508. tail: null,
  19509. threshold: seriesModel.getProgressiveThreshold(),
  19510. progressiveEnabled: progressive
  19511. && !(seriesModel.preventIncremental && seriesModel.preventIncremental()),
  19512. bockIndex: -1,
  19513. step: progressive || 700, // ??? Temporarily number
  19514. count: 0
  19515. });
  19516. pipe(scheduler, seriesModel, seriesModel.dataTask);
  19517. });
  19518. };
  19519. proto.prepareStageTasks = function (stageHandlers, useClearVisual) {
  19520. var stageTaskMap = this._stageTaskMap;
  19521. var ecModel = this.ecInstance.getModel();
  19522. var api = this.api;
  19523. each$1(stageHandlers, function (handler) {
  19524. var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, []);
  19525. handler.reset && createSeriesStageTask(this, handler, record, ecModel, api);
  19526. handler.overallReset && createOverallStageTask(this, handler, record, ecModel, api);
  19527. }, this);
  19528. };
  19529. proto.prepareView = function (view, model, ecModel, api) {
  19530. var renderTask = view.renderTask;
  19531. var context = renderTask.context;
  19532. context.model = model;
  19533. context.ecModel = ecModel;
  19534. context.api = api;
  19535. renderTask.__block = !view.incrementalPrepareRender;
  19536. pipe(this, model, renderTask);
  19537. };
  19538. proto.performDataProcessorTasks = function (stageHandlers, ecModel, payload) {
  19539. // If we do not use `block` here, it should be considered when to update modes.
  19540. performStageTasks(this, stageHandlers, ecModel, payload, {block: true});
  19541. };
  19542. // opt
  19543. // opt.visualType: 'visual' or 'layout'
  19544. // opt.setDirty
  19545. proto.performVisualTasks = function (stageHandlers, ecModel, payload, opt) {
  19546. performStageTasks(this, stageHandlers, ecModel, payload, opt);
  19547. };
  19548. function performStageTasks(scheduler, stageHandlers, ecModel, payload, opt) {
  19549. opt = opt || {};
  19550. var unfinished;
  19551. each$1(stageHandlers, function (stageHandler, idx) {
  19552. if (opt.visualType && opt.visualType !== stageHandler.visualType) {
  19553. return;
  19554. }
  19555. var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid);
  19556. var seriesTaskMap = stageHandlerRecord.seriesTaskMap;
  19557. var overallTask = stageHandlerRecord.overallTask;
  19558. if (overallTask) {
  19559. var overallNeedDirty;
  19560. var agentStubMap = overallTask.agentStubMap;
  19561. agentStubMap.each(function (stub) {
  19562. if (needSetDirty(opt, stub)) {
  19563. stub.dirty();
  19564. overallNeedDirty = true;
  19565. }
  19566. });
  19567. overallNeedDirty && overallTask.dirty();
  19568. updatePayload(overallTask, payload);
  19569. var performArgs = scheduler.getPerformArgs(overallTask, opt.block);
  19570. // Execute stubs firstly, which may set the overall task dirty,
  19571. // then execute the overall task. And stub will call seriesModel.setData,
  19572. // which ensures that in the overallTask seriesModel.getData() will not
  19573. // return incorrect data.
  19574. agentStubMap.each(function (stub) {
  19575. stub.perform(performArgs);
  19576. });
  19577. unfinished |= overallTask.perform(performArgs);
  19578. }
  19579. else if (seriesTaskMap) {
  19580. seriesTaskMap.each(function (task, pipelineId) {
  19581. if (needSetDirty(opt, task)) {
  19582. task.dirty();
  19583. }
  19584. var performArgs = scheduler.getPerformArgs(task, opt.block);
  19585. performArgs.skip = !stageHandler.performRawSeries
  19586. && ecModel.isSeriesFiltered(task.context.model);
  19587. updatePayload(task, payload);
  19588. unfinished |= task.perform(performArgs);
  19589. });
  19590. }
  19591. });
  19592. function needSetDirty(opt, task) {
  19593. return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id));
  19594. }
  19595. scheduler.unfinished |= unfinished;
  19596. }
  19597. proto.performSeriesTasks = function (ecModel) {
  19598. var unfinished;
  19599. ecModel.eachSeries(function (seriesModel) {
  19600. // Progress to the end for dataInit and dataRestore.
  19601. unfinished |= seriesModel.dataTask.perform();
  19602. });
  19603. this.unfinished |= unfinished;
  19604. };
  19605. proto.plan = function () {
  19606. // Travel pipelines, check block.
  19607. this._pipelineMap.each(function (pipeline) {
  19608. var task = pipeline.tail;
  19609. do {
  19610. if (task.__block) {
  19611. pipeline.bockIndex = task.__idxInPipeline;
  19612. break;
  19613. }
  19614. task = task.getUpstream();
  19615. }
  19616. while (task);
  19617. });
  19618. };
  19619. var updatePayload = proto.updatePayload = function (task, payload) {
  19620. payload !== 'remain' && (task.context.payload = payload);
  19621. };
  19622. function createSeriesStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) {
  19623. var seriesTaskMap = stageHandlerRecord.seriesTaskMap
  19624. || (stageHandlerRecord.seriesTaskMap = createHashMap());
  19625. var seriesType = stageHandler.seriesType;
  19626. var getTargetSeries = stageHandler.getTargetSeries;
  19627. // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily,
  19628. // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`,
  19629. // it works but it may cause other irrelevant charts blocked.
  19630. if (stageHandler.createOnAllSeries) {
  19631. ecModel.eachRawSeries(create);
  19632. }
  19633. else if (seriesType) {
  19634. ecModel.eachRawSeriesByType(seriesType, create);
  19635. }
  19636. else if (getTargetSeries) {
  19637. getTargetSeries(ecModel, api).each(create);
  19638. }
  19639. function create(seriesModel) {
  19640. var pipelineId = seriesModel.uid;
  19641. // Init tasks for each seriesModel only once.
  19642. // Reuse original task instance.
  19643. var task = seriesTaskMap.get(pipelineId)
  19644. || seriesTaskMap.set(pipelineId, createTask({
  19645. plan: seriesTaskPlan,
  19646. reset: seriesTaskReset,
  19647. count: seriesTaskCount
  19648. }));
  19649. task.context = {
  19650. model: seriesModel,
  19651. ecModel: ecModel,
  19652. api: api,
  19653. useClearVisual: stageHandler.isVisual && !stageHandler.isLayout,
  19654. plan: stageHandler.plan,
  19655. reset: stageHandler.reset,
  19656. scheduler: scheduler
  19657. };
  19658. pipe(scheduler, seriesModel, task);
  19659. }
  19660. // Clear unused series tasks.
  19661. var pipelineMap = scheduler._pipelineMap;
  19662. seriesTaskMap.each(function (task, pipelineId) {
  19663. if (!pipelineMap.get(pipelineId)) {
  19664. task.dispose();
  19665. seriesTaskMap.removeKey(pipelineId);
  19666. }
  19667. });
  19668. }
  19669. function createOverallStageTask(scheduler, stageHandler, stageHandlerRecord, ecModel, api) {
  19670. var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask
  19671. // For overall task, the function only be called on reset stage.
  19672. || createTask({reset: overallTaskReset});
  19673. overallTask.context = {
  19674. ecModel: ecModel,
  19675. api: api,
  19676. overallReset: stageHandler.overallReset,
  19677. scheduler: scheduler
  19678. };
  19679. // Reuse orignal stubs.
  19680. var agentStubMap = overallTask.agentStubMap = overallTask.agentStubMap || createHashMap();
  19681. var seriesType = stageHandler.seriesType;
  19682. var getTargetSeries = stageHandler.getTargetSeries;
  19683. var overallProgress = true;
  19684. var isOverallFilter = stageHandler.isOverallFilter;
  19685. // An overall task with seriesType detected or has `getTargetSeries`, we add
  19686. // stub in each pipelines, it will set the overall task dirty when the pipeline
  19687. // progress. Moreover, to avoid call the overall task each frame (too frequent),
  19688. // we set the pipeline block.
  19689. if (seriesType) {
  19690. ecModel.eachRawSeriesByType(seriesType, createStub);
  19691. }
  19692. else if (getTargetSeries) {
  19693. getTargetSeries(ecModel, api).each(createStub);
  19694. }
  19695. // Otherwise, (usually it is legancy case), the overall task will only be
  19696. // executed when upstream dirty. Otherwise the progressive rendering of all
  19697. // pipelines will be disabled unexpectedly. But it still needs stubs to receive
  19698. // dirty info from upsteam.
  19699. else {
  19700. overallProgress = false;
  19701. each$1(ecModel.getSeries(), createStub);
  19702. }
  19703. function createStub(seriesModel) {
  19704. var pipelineId = seriesModel.uid;
  19705. var stub = agentStubMap.get(pipelineId) || agentStubMap.set(pipelineId, createTask(
  19706. {reset: stubReset, onDirty: stubOnDirty}
  19707. ));
  19708. stub.context = {
  19709. model: seriesModel,
  19710. overallProgress: overallProgress,
  19711. isOverallFilter: isOverallFilter
  19712. };
  19713. stub.agent = overallTask;
  19714. stub.__block = overallProgress;
  19715. pipe(scheduler, seriesModel, stub);
  19716. }
  19717. // Clear unused stubs.
  19718. var pipelineMap = scheduler._pipelineMap;
  19719. agentStubMap.each(function (stub, pipelineId) {
  19720. if (!pipelineMap.get(pipelineId)) {
  19721. stub.dispose();
  19722. agentStubMap.removeKey(pipelineId);
  19723. }
  19724. });
  19725. }
  19726. function overallTaskReset(context) {
  19727. context.overallReset(
  19728. context.ecModel, context.api, context.payload
  19729. );
  19730. }
  19731. function stubReset(context, upstreamContext) {
  19732. return context.overallProgress && stubProgress;
  19733. }
  19734. function stubProgress() {
  19735. this.agent.dirty();
  19736. this.getDownstream().dirty();
  19737. }
  19738. function stubOnDirty() {
  19739. this.agent && this.agent.dirty();
  19740. }
  19741. function seriesTaskPlan(context) {
  19742. return context.plan && context.plan(
  19743. context.model, context.ecModel, context.api, context.payload
  19744. );
  19745. }
  19746. function seriesTaskReset(context) {
  19747. if (context.useClearVisual) {
  19748. context.data.clearAllVisual();
  19749. }
  19750. var resetDefines = context.resetDefines = normalizeToArray(context.reset(
  19751. context.model, context.ecModel, context.api, context.payload
  19752. ));
  19753. if (resetDefines.length) {
  19754. return seriesTaskProgress;
  19755. }
  19756. }
  19757. function seriesTaskProgress(params, context) {
  19758. var data = context.data;
  19759. var resetDefines = context.resetDefines;
  19760. for (var k = 0; k < resetDefines.length; k++) {
  19761. var resetDefine = resetDefines[k];
  19762. if (resetDefine && resetDefine.dataEach) {
  19763. for (var i = params.start; i < params.end; i++) {
  19764. resetDefine.dataEach(data, i);
  19765. }
  19766. }
  19767. else if (resetDefine && resetDefine.progress) {
  19768. resetDefine.progress(params, data);
  19769. }
  19770. }
  19771. }
  19772. function seriesTaskCount(context) {
  19773. return context.data.count();
  19774. }
  19775. function pipe(scheduler, seriesModel, task) {
  19776. var pipelineId = seriesModel.uid;
  19777. var pipeline = scheduler._pipelineMap.get(pipelineId);
  19778. !pipeline.head && (pipeline.head = task);
  19779. pipeline.tail && pipeline.tail.pipe(task);
  19780. pipeline.tail = task;
  19781. task.__idxInPipeline = pipeline.count++;
  19782. task.__pipeline = pipeline;
  19783. }
  19784. Scheduler.wrapStageHandler = function (stageHandler, visualType) {
  19785. if (isFunction$1(stageHandler)) {
  19786. stageHandler = {
  19787. overallReset: stageHandler,
  19788. seriesType: detectSeriseType(stageHandler)
  19789. };
  19790. }
  19791. stageHandler.uid = getUID('stageHandler');
  19792. visualType && (stageHandler.visualType = visualType);
  19793. return stageHandler;
  19794. };
  19795. /**
  19796. * Only some legacy stage handlers (usually in echarts extensions) are pure function.
  19797. * To ensure that they can work normally, they should work in block mode, that is,
  19798. * they should not be started util the previous tasks finished. So they cause the
  19799. * progressive rendering disabled. We try to detect the series type, to narrow down
  19800. * the block range to only the series type they concern, but not all series.
  19801. */
  19802. function detectSeriseType(legacyFunc) {
  19803. seriesType = null;
  19804. try {
  19805. // Assume there is no async when calling `eachSeriesByType`.
  19806. legacyFunc(ecModelMock, apiMock);
  19807. }
  19808. catch (e) {
  19809. }
  19810. return seriesType;
  19811. }
  19812. var ecModelMock = {};
  19813. var apiMock = {};
  19814. var seriesType;
  19815. mockMethods(ecModelMock, GlobalModel);
  19816. mockMethods(apiMock, ExtensionAPI);
  19817. ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) {
  19818. seriesType = type;
  19819. };
  19820. ecModelMock.eachComponent = function (cond) {
  19821. if (cond.mainType === 'series' && cond.subType) {
  19822. seriesType = cond.subType;
  19823. }
  19824. };
  19825. function mockMethods(target, Clz) {
  19826. for (var name in Clz.prototype) {
  19827. // Do not use hasOwnProperty
  19828. target[name] = noop;
  19829. }
  19830. }
  19831. var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C','#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];
  19832. var lightTheme = {
  19833. color: colorAll,
  19834. colorLayer: [
  19835. ['#37A2DA', '#ffd85c', '#fd7b5f'],
  19836. ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'],
  19837. ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'],
  19838. colorAll
  19839. ]
  19840. };
  19841. var contrastColor = '#eee';
  19842. var axisCommon = function () {
  19843. return {
  19844. axisLine: {
  19845. lineStyle: {
  19846. color: contrastColor
  19847. }
  19848. },
  19849. axisTick: {
  19850. lineStyle: {
  19851. color: contrastColor
  19852. }
  19853. },
  19854. axisLabel: {
  19855. textStyle: {
  19856. color: contrastColor
  19857. }
  19858. },
  19859. splitLine: {
  19860. lineStyle: {
  19861. type: 'dashed',
  19862. color: '#aaa'
  19863. }
  19864. },
  19865. splitArea: {
  19866. areaStyle: {
  19867. color: contrastColor
  19868. }
  19869. }
  19870. };
  19871. };
  19872. var colorPalette = ['#dd6b66','#759aa0','#e69d87','#8dc1a9','#ea7e53','#eedd78','#73a373','#73b9bc','#7289ab', '#91ca8c','#f49f42'];
  19873. var theme = {
  19874. color: colorPalette,
  19875. backgroundColor: '#333',
  19876. tooltip: {
  19877. axisPointer: {
  19878. lineStyle: {
  19879. color: contrastColor
  19880. },
  19881. crossStyle: {
  19882. color: contrastColor
  19883. }
  19884. }
  19885. },
  19886. legend: {
  19887. textStyle: {
  19888. color: contrastColor
  19889. }
  19890. },
  19891. textStyle: {
  19892. color: contrastColor
  19893. },
  19894. title: {
  19895. textStyle: {
  19896. color: contrastColor
  19897. }
  19898. },
  19899. toolbox: {
  19900. iconStyle: {
  19901. normal: {
  19902. borderColor: contrastColor
  19903. }
  19904. }
  19905. },
  19906. dataZoom: {
  19907. textStyle: {
  19908. color: contrastColor
  19909. }
  19910. },
  19911. visualMap: {
  19912. textStyle: {
  19913. color: contrastColor
  19914. }
  19915. },
  19916. timeline: {
  19917. lineStyle: {
  19918. color: contrastColor
  19919. },
  19920. itemStyle: {
  19921. normal: {
  19922. color: colorPalette[1]
  19923. }
  19924. },
  19925. label: {
  19926. normal: {
  19927. textStyle: {
  19928. color: contrastColor
  19929. }
  19930. }
  19931. },
  19932. controlStyle: {
  19933. normal: {
  19934. color: contrastColor,
  19935. borderColor: contrastColor
  19936. }
  19937. }
  19938. },
  19939. timeAxis: axisCommon(),
  19940. logAxis: axisCommon(),
  19941. valueAxis: axisCommon(),
  19942. categoryAxis: axisCommon(),
  19943. line: {
  19944. symbol: 'circle'
  19945. },
  19946. graph: {
  19947. color: colorPalette
  19948. },
  19949. gauge: {
  19950. title: {
  19951. textStyle: {
  19952. color: contrastColor
  19953. }
  19954. }
  19955. },
  19956. candlestick: {
  19957. itemStyle: {
  19958. normal: {
  19959. color: '#FD1050',
  19960. color0: '#0CF49B',
  19961. borderColor: '#FD1050',
  19962. borderColor0: '#0CF49B'
  19963. }
  19964. }
  19965. }
  19966. };
  19967. theme.categoryAxis.splitLine.show = false;
  19968. /*!
  19969. * ECharts, a free, powerful charting and visualization library.
  19970. *
  19971. * Copyright (c) 2017, Baidu Inc.
  19972. * All rights reserved.
  19973. *
  19974. * LICENSE
  19975. * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
  19976. */
  19977. var assert = assert$1;
  19978. var each = each$1;
  19979. var isFunction = isFunction$1;
  19980. var isObject = isObject$1;
  19981. var parseClassType = ComponentModel.parseClassType;
  19982. var version = '4.0.2';
  19983. var dependencies = {
  19984. zrender: '4.0.1'
  19985. };
  19986. var TEST_FRAME_REMAIN_TIME = 1;
  19987. var PRIORITY_PROCESSOR_FILTER = 1000;
  19988. var PRIORITY_PROCESSOR_STATISTIC = 5000;
  19989. var PRIORITY_VISUAL_LAYOUT = 1000;
  19990. var PRIORITY_VISUAL_GLOBAL = 2000;
  19991. var PRIORITY_VISUAL_CHART = 3000;
  19992. var PRIORITY_VISUAL_COMPONENT = 4000;
  19993. // FIXME
  19994. // necessary?
  19995. var PRIORITY_VISUAL_BRUSH = 5000;
  19996. var PRIORITY = {
  19997. PROCESSOR: {
  19998. FILTER: PRIORITY_PROCESSOR_FILTER,
  19999. STATISTIC: PRIORITY_PROCESSOR_STATISTIC
  20000. },
  20001. VISUAL: {
  20002. LAYOUT: PRIORITY_VISUAL_LAYOUT,
  20003. GLOBAL: PRIORITY_VISUAL_GLOBAL,
  20004. CHART: PRIORITY_VISUAL_CHART,
  20005. COMPONENT: PRIORITY_VISUAL_COMPONENT,
  20006. BRUSH: PRIORITY_VISUAL_BRUSH
  20007. }
  20008. };
  20009. // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
  20010. // where they must not be invoked nestedly, except the only case: invoke
  20011. // dispatchAction with updateMethod "none" in main process.
  20012. // This flag is used to carry out this rule.
  20013. // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
  20014. var IN_MAIN_PROCESS = '__flagInMainProcess';
  20015. var HAS_GRADIENT_OR_PATTERN_BG = '__hasGradientOrPatternBg';
  20016. var OPTION_UPDATED = '__optionUpdated';
  20017. var ACTION_REG = /^[a-zA-Z0-9_]+$/;
  20018. function createRegisterEventWithLowercaseName(method) {
  20019. return function (eventName, handler, context) {
  20020. // Event name is all lowercase
  20021. eventName = eventName && eventName.toLowerCase();
  20022. Eventful.prototype[method].call(this, eventName, handler, context);
  20023. };
  20024. }
  20025. /**
  20026. * @module echarts~MessageCenter
  20027. */
  20028. function MessageCenter() {
  20029. Eventful.call(this);
  20030. }
  20031. MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
  20032. MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
  20033. MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
  20034. mixin(MessageCenter, Eventful);
  20035. /**
  20036. * @module echarts~ECharts
  20037. */
  20038. function ECharts(dom, theme$$1, opts) {
  20039. opts = opts || {};
  20040. // Get theme by name
  20041. if (typeof theme$$1 === 'string') {
  20042. theme$$1 = themeStorage[theme$$1];
  20043. }
  20044. /**
  20045. * @type {string}
  20046. */
  20047. this.id;
  20048. /**
  20049. * Group id
  20050. * @type {string}
  20051. */
  20052. this.group;
  20053. /**
  20054. * @type {HTMLElement}
  20055. * @private
  20056. */
  20057. this._dom = dom;
  20058. var defaultRenderer = 'canvas';
  20059. if (__DEV__) {
  20060. defaultRenderer = (
  20061. typeof window === 'undefined' ? global : window
  20062. ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
  20063. }
  20064. /**
  20065. * @type {module:zrender/ZRender}
  20066. * @private
  20067. */
  20068. var zr = this._zr = init$1(dom, {
  20069. renderer: opts.renderer || defaultRenderer,
  20070. devicePixelRatio: opts.devicePixelRatio,
  20071. width: opts.width,
  20072. height: opts.height
  20073. });
  20074. /**
  20075. * Expect 60 pfs.
  20076. * @type {Function}
  20077. * @private
  20078. */
  20079. this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);
  20080. var theme$$1 = clone(theme$$1);
  20081. theme$$1 && backwardCompat(theme$$1, true);
  20082. /**
  20083. * @type {Object}
  20084. * @private
  20085. */
  20086. this._theme = theme$$1;
  20087. /**
  20088. * @type {Array.<module:echarts/view/Chart>}
  20089. * @private
  20090. */
  20091. this._chartsViews = [];
  20092. /**
  20093. * @type {Object.<string, module:echarts/view/Chart>}
  20094. * @private
  20095. */
  20096. this._chartsMap = {};
  20097. /**
  20098. * @type {Array.<module:echarts/view/Component>}
  20099. * @private
  20100. */
  20101. this._componentsViews = [];
  20102. /**
  20103. * @type {Object.<string, module:echarts/view/Component>}
  20104. * @private
  20105. */
  20106. this._componentsMap = {};
  20107. /**
  20108. * @type {module:echarts/CoordinateSystem}
  20109. * @private
  20110. */
  20111. this._coordSysMgr = new CoordinateSystemManager();
  20112. /**
  20113. * @type {module:echarts/ExtensionAPI}
  20114. * @private
  20115. */
  20116. var api = this._api = createExtensionAPI(this);
  20117. /**
  20118. * @type {module:echarts/stream/Scheduler}
  20119. */
  20120. this._scheduler = new Scheduler(this, api);
  20121. Eventful.call(this);
  20122. /**
  20123. * @type {module:echarts~MessageCenter}
  20124. * @private
  20125. */
  20126. this._messageCenter = new MessageCenter();
  20127. // this._scheduler = new Scheduler();
  20128. // Init mouse events
  20129. this._initEvents();
  20130. // In case some people write `window.onresize = chart.resize`
  20131. this.resize = bind(this.resize, this);
  20132. // Can't dispatch action during rendering procedure
  20133. this._pendingActions = [];
  20134. // Sort on demand
  20135. function prioritySortFunc(a, b) {
  20136. return a.__prio - b.__prio;
  20137. }
  20138. sort(visualFuncs, prioritySortFunc);
  20139. sort(dataProcessorFuncs, prioritySortFunc);
  20140. zr.animation.on('frame', this._onframe, this);
  20141. // ECharts instance can be used as value.
  20142. setAsPrimitive(this);
  20143. }
  20144. var echartsProto = ECharts.prototype;
  20145. echartsProto._onframe = function () {
  20146. if (this._disposed) {
  20147. return;
  20148. }
  20149. var scheduler = this._scheduler;
  20150. // Lazy update
  20151. if (this[OPTION_UPDATED]) {
  20152. var silent = this[OPTION_UPDATED].silent;
  20153. this[IN_MAIN_PROCESS] = true;
  20154. prepare(this);
  20155. updateMethods.update.call(this);
  20156. this[IN_MAIN_PROCESS] = false;
  20157. this[OPTION_UPDATED] = false;
  20158. flushPendingActions.call(this, silent);
  20159. triggerUpdatedEvent.call(this, silent);
  20160. }
  20161. // Avoid do both lazy update and progress in one frame.
  20162. else if (scheduler.unfinished) {
  20163. // Stream progress.
  20164. var remainTime = TEST_FRAME_REMAIN_TIME;
  20165. var ecModel = this._model;
  20166. var api = this._api;
  20167. scheduler.unfinished = false;
  20168. do {
  20169. var startTime = +new Date();
  20170. scheduler.performSeriesTasks(ecModel);
  20171. // Currently dataProcessorFuncs do not check threshold.
  20172. scheduler.performDataProcessorTasks(dataProcessorFuncs, ecModel);
  20173. updateStreamModes(this, ecModel);
  20174. // Do not update coordinate system here. Because that coord system update in
  20175. // each frame is not a good user experience. So we follow the rule that
  20176. // the extent of the coordinate system is determin in the first frame (the
  20177. // frame is executed immedietely after task reset.
  20178. // this._coordSysMgr.update(ecModel, api);
  20179. // console.log('--- ec frame visual ---', remainTime);
  20180. scheduler.performVisualTasks(visualFuncs, ecModel);
  20181. renderSeries(this, this._model, api, 'remain');
  20182. remainTime -= (+new Date() - startTime);
  20183. }
  20184. while (remainTime > 0 && scheduler.unfinished);
  20185. if (!scheduler.unfinished) {
  20186. this._zr && this._zr.flush();
  20187. this.trigger('finished');
  20188. }
  20189. // Else, zr flushing be ensue within the same frame,
  20190. // because zr flushing is after onframe event.
  20191. }
  20192. };
  20193. /**
  20194. * @return {HTMLElement}
  20195. */
  20196. echartsProto.getDom = function () {
  20197. return this._dom;
  20198. };
  20199. /**
  20200. * @return {module:zrender~ZRender}
  20201. */
  20202. echartsProto.getZr = function () {
  20203. return this._zr;
  20204. };
  20205. /**
  20206. * Usage:
  20207. * chart.setOption(option, notMerge, lazyUpdate);
  20208. * chart.setOption(option, {
  20209. * notMerge: ...,
  20210. * lazyUpdate: ...,
  20211. * silent: ...
  20212. * });
  20213. *
  20214. * @param {Object} option
  20215. * @param {Object|boolean} [opts] opts or notMerge.
  20216. * @param {boolean} [opts.notMerge=false]
  20217. * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
  20218. */
  20219. echartsProto.setOption = function (option, notMerge, lazyUpdate) {
  20220. if (__DEV__) {
  20221. assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
  20222. }
  20223. var silent;
  20224. if (isObject(notMerge)) {
  20225. lazyUpdate = notMerge.lazyUpdate;
  20226. silent = notMerge.silent;
  20227. notMerge = notMerge.notMerge;
  20228. }
  20229. this[IN_MAIN_PROCESS] = true;
  20230. if (!this._model || notMerge) {
  20231. var optionManager = new OptionManager(this._api);
  20232. var theme$$1 = this._theme;
  20233. var ecModel = this._model = new GlobalModel(null, null, theme$$1, optionManager);
  20234. ecModel.scheduler = this._scheduler;
  20235. ecModel.init(null, null, theme$$1, optionManager);
  20236. }
  20237. this._model.setOption(option, optionPreprocessorFuncs);
  20238. if (lazyUpdate) {
  20239. this[OPTION_UPDATED] = {silent: silent};
  20240. this[IN_MAIN_PROCESS] = false;
  20241. }
  20242. else {
  20243. prepare(this);
  20244. updateMethods.update.call(this);
  20245. // Ensure zr refresh sychronously, and then pixel in canvas can be
  20246. // fetched after `setOption`.
  20247. this._zr.flush();
  20248. this[OPTION_UPDATED] = false;
  20249. this[IN_MAIN_PROCESS] = false;
  20250. flushPendingActions.call(this, silent);
  20251. triggerUpdatedEvent.call(this, silent);
  20252. }
  20253. };
  20254. /**
  20255. * @DEPRECATED
  20256. */
  20257. echartsProto.setTheme = function () {
  20258. console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
  20259. };
  20260. /**
  20261. * @return {module:echarts/model/Global}
  20262. */
  20263. echartsProto.getModel = function () {
  20264. return this._model;
  20265. };
  20266. /**
  20267. * @return {Object}
  20268. */
  20269. echartsProto.getOption = function () {
  20270. return this._model && this._model.getOption();
  20271. };
  20272. /**
  20273. * @return {number}
  20274. */
  20275. echartsProto.getWidth = function () {
  20276. return this._zr.getWidth();
  20277. };
  20278. /**
  20279. * @return {number}
  20280. */
  20281. echartsProto.getHeight = function () {
  20282. return this._zr.getHeight();
  20283. };
  20284. /**
  20285. * @return {number}
  20286. */
  20287. echartsProto.getDevicePixelRatio = function () {
  20288. return this._zr.painter.dpr || window.devicePixelRatio || 1;
  20289. };
  20290. /**
  20291. * Get canvas which has all thing rendered
  20292. * @param {Object} opts
  20293. * @param {string} [opts.backgroundColor]
  20294. * @return {string}
  20295. */
  20296. echartsProto.getRenderedCanvas = function (opts) {
  20297. if (!env$1.canvasSupported) {
  20298. return;
  20299. }
  20300. opts = opts || {};
  20301. opts.pixelRatio = opts.pixelRatio || 1;
  20302. opts.backgroundColor = opts.backgroundColor
  20303. || this._model.get('backgroundColor');
  20304. var zr = this._zr;
  20305. var list = zr.storage.getDisplayList();
  20306. // Stop animations
  20307. each$1(list, function (el) {
  20308. el.stopAnimation(true);
  20309. });
  20310. return zr.painter.getRenderedCanvas(opts);
  20311. };
  20312. /**
  20313. * Get svg data url
  20314. * @return {string}
  20315. */
  20316. echartsProto.getSvgDataUrl = function () {
  20317. if (!env$1.svgSupported) {
  20318. return;
  20319. }
  20320. var zr = this._zr;
  20321. var list = zr.storage.getDisplayList();
  20322. // Stop animations
  20323. each$1(list, function (el) {
  20324. el.stopAnimation(true);
  20325. });
  20326. return zr.painter.pathToSvg();
  20327. };
  20328. /**
  20329. * @return {string}
  20330. * @param {Object} opts
  20331. * @param {string} [opts.type='png']
  20332. * @param {string} [opts.pixelRatio=1]
  20333. * @param {string} [opts.backgroundColor]
  20334. * @param {string} [opts.excludeComponents]
  20335. */
  20336. echartsProto.getDataURL = function (opts) {
  20337. opts = opts || {};
  20338. var excludeComponents = opts.excludeComponents;
  20339. var ecModel = this._model;
  20340. var excludesComponentViews = [];
  20341. var self = this;
  20342. each(excludeComponents, function (componentType) {
  20343. ecModel.eachComponent({
  20344. mainType: componentType
  20345. }, function (component) {
  20346. var view = self._componentsMap[component.__viewId];
  20347. if (!view.group.ignore) {
  20348. excludesComponentViews.push(view);
  20349. view.group.ignore = true;
  20350. }
  20351. });
  20352. });
  20353. var url = this._zr.painter.getType() === 'svg'
  20354. ? this.getSvgDataUrl()
  20355. : this.getRenderedCanvas(opts).toDataURL(
  20356. 'image/' + (opts && opts.type || 'png')
  20357. );
  20358. each(excludesComponentViews, function (view) {
  20359. view.group.ignore = false;
  20360. });
  20361. return url;
  20362. };
  20363. /**
  20364. * @return {string}
  20365. * @param {Object} opts
  20366. * @param {string} [opts.type='png']
  20367. * @param {string} [opts.pixelRatio=1]
  20368. * @param {string} [opts.backgroundColor]
  20369. */
  20370. echartsProto.getConnectedDataURL = function (opts) {
  20371. if (!env$1.canvasSupported) {
  20372. return;
  20373. }
  20374. var groupId = this.group;
  20375. var mathMin = Math.min;
  20376. var mathMax = Math.max;
  20377. var MAX_NUMBER = Infinity;
  20378. if (connectedGroups[groupId]) {
  20379. var left = MAX_NUMBER;
  20380. var top = MAX_NUMBER;
  20381. var right = -MAX_NUMBER;
  20382. var bottom = -MAX_NUMBER;
  20383. var canvasList = [];
  20384. var dpr = (opts && opts.pixelRatio) || 1;
  20385. each$1(instances, function (chart, id) {
  20386. if (chart.group === groupId) {
  20387. var canvas = chart.getRenderedCanvas(
  20388. clone(opts)
  20389. );
  20390. var boundingRect = chart.getDom().getBoundingClientRect();
  20391. left = mathMin(boundingRect.left, left);
  20392. top = mathMin(boundingRect.top, top);
  20393. right = mathMax(boundingRect.right, right);
  20394. bottom = mathMax(boundingRect.bottom, bottom);
  20395. canvasList.push({
  20396. dom: canvas,
  20397. left: boundingRect.left,
  20398. top: boundingRect.top
  20399. });
  20400. }
  20401. });
  20402. left *= dpr;
  20403. top *= dpr;
  20404. right *= dpr;
  20405. bottom *= dpr;
  20406. var width = right - left;
  20407. var height = bottom - top;
  20408. var targetCanvas = createCanvas();
  20409. targetCanvas.width = width;
  20410. targetCanvas.height = height;
  20411. var zr = init$1(targetCanvas);
  20412. each(canvasList, function (item) {
  20413. var img = new ZImage({
  20414. style: {
  20415. x: item.left * dpr - left,
  20416. y: item.top * dpr - top,
  20417. image: item.dom
  20418. }
  20419. });
  20420. zr.add(img);
  20421. });
  20422. zr.refreshImmediately();
  20423. return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
  20424. }
  20425. else {
  20426. return this.getDataURL(opts);
  20427. }
  20428. };
  20429. /**
  20430. * Convert from logical coordinate system to pixel coordinate system.
  20431. * See CoordinateSystem#convertToPixel.
  20432. * @param {string|Object} finder
  20433. * If string, e.g., 'geo', means {geoIndex: 0}.
  20434. * If Object, could contain some of these properties below:
  20435. * {
  20436. * seriesIndex / seriesId / seriesName,
  20437. * geoIndex / geoId, geoName,
  20438. * bmapIndex / bmapId / bmapName,
  20439. * xAxisIndex / xAxisId / xAxisName,
  20440. * yAxisIndex / yAxisId / yAxisName,
  20441. * gridIndex / gridId / gridName,
  20442. * ... (can be extended)
  20443. * }
  20444. * @param {Array|number} value
  20445. * @return {Array|number} result
  20446. */
  20447. echartsProto.convertToPixel = curry(doConvertPixel, 'convertToPixel');
  20448. /**
  20449. * Convert from pixel coordinate system to logical coordinate system.
  20450. * See CoordinateSystem#convertFromPixel.
  20451. * @param {string|Object} finder
  20452. * If string, e.g., 'geo', means {geoIndex: 0}.
  20453. * If Object, could contain some of these properties below:
  20454. * {
  20455. * seriesIndex / seriesId / seriesName,
  20456. * geoIndex / geoId / geoName,
  20457. * bmapIndex / bmapId / bmapName,
  20458. * xAxisIndex / xAxisId / xAxisName,
  20459. * yAxisIndex / yAxisId / yAxisName
  20460. * gridIndex / gridId / gridName,
  20461. * ... (can be extended)
  20462. * }
  20463. * @param {Array|number} value
  20464. * @return {Array|number} result
  20465. */
  20466. echartsProto.convertFromPixel = curry(doConvertPixel, 'convertFromPixel');
  20467. function doConvertPixel(methodName, finder, value) {
  20468. var ecModel = this._model;
  20469. var coordSysList = this._coordSysMgr.getCoordinateSystems();
  20470. var result;
  20471. finder = parseFinder(ecModel, finder);
  20472. for (var i = 0; i < coordSysList.length; i++) {
  20473. var coordSys = coordSysList[i];
  20474. if (coordSys[methodName]
  20475. && (result = coordSys[methodName](ecModel, finder, value)) != null
  20476. ) {
  20477. return result;
  20478. }
  20479. }
  20480. if (__DEV__) {
  20481. console.warn(
  20482. 'No coordinate system that supports ' + methodName + ' found by the given finder.'
  20483. );
  20484. }
  20485. }
  20486. /**
  20487. * Is the specified coordinate systems or components contain the given pixel point.
  20488. * @param {string|Object} finder
  20489. * If string, e.g., 'geo', means {geoIndex: 0}.
  20490. * If Object, could contain some of these properties below:
  20491. * {
  20492. * seriesIndex / seriesId / seriesName,
  20493. * geoIndex / geoId / geoName,
  20494. * bmapIndex / bmapId / bmapName,
  20495. * xAxisIndex / xAxisId / xAxisName,
  20496. * yAxisIndex / yAxisId / yAxisName,
  20497. * gridIndex / gridId / gridName,
  20498. * ... (can be extended)
  20499. * }
  20500. * @param {Array|number} value
  20501. * @return {boolean} result
  20502. */
  20503. echartsProto.containPixel = function (finder, value) {
  20504. var ecModel = this._model;
  20505. var result;
  20506. finder = parseFinder(ecModel, finder);
  20507. each$1(finder, function (models, key) {
  20508. key.indexOf('Models') >= 0 && each$1(models, function (model) {
  20509. var coordSys = model.coordinateSystem;
  20510. if (coordSys && coordSys.containPoint) {
  20511. result |= !!coordSys.containPoint(value);
  20512. }
  20513. else if (key === 'seriesModels') {
  20514. var view = this._chartsMap[model.__viewId];
  20515. if (view && view.containPoint) {
  20516. result |= view.containPoint(value, model);
  20517. }
  20518. else {
  20519. if (__DEV__) {
  20520. console.warn(key + ': ' + (view
  20521. ? 'The found component do not support containPoint.'
  20522. : 'No view mapping to the found component.'
  20523. ));
  20524. }
  20525. }
  20526. }
  20527. else {
  20528. if (__DEV__) {
  20529. console.warn(key + ': containPoint is not supported');
  20530. }
  20531. }
  20532. }, this);
  20533. }, this);
  20534. return !!result;
  20535. };
  20536. /**
  20537. * Get visual from series or data.
  20538. * @param {string|Object} finder
  20539. * If string, e.g., 'series', means {seriesIndex: 0}.
  20540. * If Object, could contain some of these properties below:
  20541. * {
  20542. * seriesIndex / seriesId / seriesName,
  20543. * dataIndex / dataIndexInside
  20544. * }
  20545. * If dataIndex is not specified, series visual will be fetched,
  20546. * but not data item visual.
  20547. * If all of seriesIndex, seriesId, seriesName are not specified,
  20548. * visual will be fetched from first series.
  20549. * @param {string} visualType 'color', 'symbol', 'symbolSize'
  20550. */
  20551. echartsProto.getVisual = function (finder, visualType) {
  20552. var ecModel = this._model;
  20553. finder = parseFinder(ecModel, finder, {defaultMainType: 'series'});
  20554. var seriesModel = finder.seriesModel;
  20555. if (__DEV__) {
  20556. if (!seriesModel) {
  20557. console.warn('There is no specified seires model');
  20558. }
  20559. }
  20560. var data = seriesModel.getData();
  20561. var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
  20562. ? finder.dataIndexInside
  20563. : finder.hasOwnProperty('dataIndex')
  20564. ? data.indexOfRawIndex(finder.dataIndex)
  20565. : null;
  20566. return dataIndexInside != null
  20567. ? data.getItemVisual(dataIndexInside, visualType)
  20568. : data.getVisual(visualType);
  20569. };
  20570. /**
  20571. * Get view of corresponding component model
  20572. * @param {module:echarts/model/Component} componentModel
  20573. * @return {module:echarts/view/Component}
  20574. */
  20575. echartsProto.getViewOfComponentModel = function (componentModel) {
  20576. return this._componentsMap[componentModel.__viewId];
  20577. };
  20578. /**
  20579. * Get view of corresponding series model
  20580. * @param {module:echarts/model/Series} seriesModel
  20581. * @return {module:echarts/view/Chart}
  20582. */
  20583. echartsProto.getViewOfSeriesModel = function (seriesModel) {
  20584. return this._chartsMap[seriesModel.__viewId];
  20585. };
  20586. var updateMethods = {
  20587. prepareAndUpdate: function (payload) {
  20588. prepare(this);
  20589. updateMethods.update.call(this, payload);
  20590. },
  20591. /**
  20592. * @param {Object} payload
  20593. * @private
  20594. */
  20595. update: function (payload) {
  20596. // console.profile && console.profile('update');
  20597. var ecModel = this._model;
  20598. var api = this._api;
  20599. var zr = this._zr;
  20600. var coordSysMgr = this._coordSysMgr;
  20601. var scheduler = this._scheduler;
  20602. // update before setOption
  20603. if (!ecModel) {
  20604. return;
  20605. }
  20606. ecModel.restoreData(payload);
  20607. scheduler.performSeriesTasks(ecModel);
  20608. // TODO
  20609. // Save total ecModel here for undo/redo (after restoring data and before processing data).
  20610. // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
  20611. // Create new coordinate system each update
  20612. // In LineView may save the old coordinate system and use it to get the orignal point
  20613. coordSysMgr.create(ecModel, api);
  20614. scheduler.performDataProcessorTasks(dataProcessorFuncs, ecModel, payload);
  20615. // Current stream render is not supported in data process. So we can update
  20616. // stream modes after data processing, where the filtered data is used to
  20617. // deteming whether use progressive rendering.
  20618. updateStreamModes(this, ecModel);
  20619. stackSeriesData(ecModel);
  20620. coordSysMgr.update(ecModel, api);
  20621. clearColorPalette(ecModel);
  20622. scheduler.performVisualTasks(visualFuncs, ecModel, payload);
  20623. render(this, ecModel, api, payload);
  20624. // Set background
  20625. var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
  20626. var painter = zr.painter;
  20627. // TODO all use clearColor ?
  20628. if (painter.isSingleCanvas && painter.isSingleCanvas()) {
  20629. zr.configLayer(0, {
  20630. clearColor: backgroundColor
  20631. });
  20632. }
  20633. else {
  20634. // In IE8
  20635. if (!env$1.canvasSupported) {
  20636. var colorArr = parse(backgroundColor);
  20637. backgroundColor = stringify(colorArr, 'rgb');
  20638. if (colorArr[3] === 0) {
  20639. backgroundColor = 'transparent';
  20640. }
  20641. }
  20642. if (backgroundColor.colorStops || backgroundColor.image) {
  20643. // Gradient background
  20644. // FIXME Fixed layer?
  20645. zr.configLayer(0, {
  20646. clearColor: backgroundColor
  20647. });
  20648. this[HAS_GRADIENT_OR_PATTERN_BG] = true;
  20649. this._dom.style.background = 'transparent';
  20650. }
  20651. else {
  20652. if (this[HAS_GRADIENT_OR_PATTERN_BG]) {
  20653. zr.configLayer(0, {
  20654. clearColor: null
  20655. });
  20656. }
  20657. this[HAS_GRADIENT_OR_PATTERN_BG] = false;
  20658. this._dom.style.background = backgroundColor;
  20659. }
  20660. }
  20661. performPostUpdateFuncs(ecModel, api);
  20662. // console.profile && console.profileEnd('update');
  20663. },
  20664. /**
  20665. * @param {Object} payload
  20666. * @private
  20667. */
  20668. updateTransform: function (payload) {
  20669. var ecModel = this._model;
  20670. var ecIns = this;
  20671. var api = this._api;
  20672. // update before setOption
  20673. if (!ecModel) {
  20674. return;
  20675. }
  20676. // ChartView.markUpdateMethod(payload, 'updateTransform');
  20677. var componentDirtyList = [];
  20678. ecModel.eachComponent(function (componentType, componentModel) {
  20679. var componentView = ecIns.getViewOfComponentModel(componentModel);
  20680. if (componentView && componentView.__alive) {
  20681. if (componentView.updateTransform) {
  20682. var result = componentView.updateTransform(componentModel, ecModel, api, payload);
  20683. result && result.update && componentDirtyList.push(componentView);
  20684. }
  20685. else {
  20686. componentDirtyList.push(componentView);
  20687. }
  20688. }
  20689. });
  20690. var seriesDirtyMap = createHashMap();
  20691. ecModel.eachSeries(function (seriesModel) {
  20692. var chartView = ecIns._chartsMap[seriesModel.__viewId];
  20693. if (chartView.updateTransform) {
  20694. var result = chartView.updateTransform(seriesModel, ecModel, api, payload);
  20695. result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);
  20696. }
  20697. else {
  20698. seriesDirtyMap.set(seriesModel.uid, 1);
  20699. }
  20700. });
  20701. clearColorPalette(ecModel);
  20702. // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  20703. // this._scheduler.performVisualTasks(visualFuncs, ecModel, payload, 'layout', true);
  20704. this._scheduler.performVisualTasks(
  20705. visualFuncs, ecModel, payload, {setDirty: true, dirtyMap: seriesDirtyMap}
  20706. );
  20707. // Currently, not call render of components. Geo render cost a lot.
  20708. // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);
  20709. renderSeries(ecIns, ecModel, api, payload, seriesDirtyMap);
  20710. performPostUpdateFuncs(ecModel, this._api);
  20711. },
  20712. /**
  20713. * @param {Object} payload
  20714. * @private
  20715. */
  20716. updateView: function (payload) {
  20717. var ecModel = this._model;
  20718. // update before setOption
  20719. if (!ecModel) {
  20720. return;
  20721. }
  20722. Chart.markUpdateMethod(payload, 'updateView');
  20723. clearColorPalette(ecModel);
  20724. // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  20725. this._scheduler.performVisualTasks(visualFuncs, ecModel, payload, {setDirty: true});
  20726. render(this, this._model, this._api, payload);
  20727. performPostUpdateFuncs(ecModel, this._api);
  20728. },
  20729. /**
  20730. * @param {Object} payload
  20731. * @private
  20732. */
  20733. updateVisual: function (payload) {
  20734. updateMethods.update.call(this, payload);
  20735. // var ecModel = this._model;
  20736. // // update before setOption
  20737. // if (!ecModel) {
  20738. // return;
  20739. // }
  20740. // ChartView.markUpdateMethod(payload, 'updateVisual');
  20741. // clearColorPalette(ecModel);
  20742. // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  20743. // this._scheduler.performVisualTasks(visualFuncs, ecModel, payload, {visualType: 'visual', setDirty: true});
  20744. // render(this, this._model, this._api, payload);
  20745. // performPostUpdateFuncs(ecModel, this._api);
  20746. },
  20747. /**
  20748. * @param {Object} payload
  20749. * @private
  20750. */
  20751. updateLayout: function (payload) {
  20752. updateMethods.update.call(this, payload);
  20753. // var ecModel = this._model;
  20754. // // update before setOption
  20755. // if (!ecModel) {
  20756. // return;
  20757. // }
  20758. // ChartView.markUpdateMethod(payload, 'updateLayout');
  20759. // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  20760. // // this._scheduler.performVisualTasks(visualFuncs, ecModel, payload, 'layout', true);
  20761. // this._scheduler.performVisualTasks(visualFuncs, ecModel, payload, {setDirty: true});
  20762. // render(this, this._model, this._api, payload);
  20763. // performPostUpdateFuncs(ecModel, this._api);
  20764. }
  20765. };
  20766. function prepare(ecIns) {
  20767. var ecModel = ecIns._model;
  20768. var scheduler = ecIns._scheduler;
  20769. scheduler.restorePipelines(ecModel);
  20770. scheduler.prepareStageTasks(dataProcessorFuncs);
  20771. scheduler.prepareStageTasks(visualFuncs);
  20772. prepareView(ecIns, 'component', ecModel, scheduler);
  20773. prepareView(ecIns, 'chart', ecModel, scheduler);
  20774. scheduler.plan();
  20775. }
  20776. /**
  20777. * @private
  20778. */
  20779. function updateDirectly(ecIns, method, payload, mainType, subType) {
  20780. var ecModel = ecIns._model;
  20781. // broadcast
  20782. if (!mainType) {
  20783. // FIXME
  20784. // Chart will not be update directly here, except set dirty.
  20785. // But there is no such scenario now.
  20786. each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
  20787. return;
  20788. }
  20789. var query = {};
  20790. query[mainType + 'Id'] = payload[mainType + 'Id'];
  20791. query[mainType + 'Index'] = payload[mainType + 'Index'];
  20792. query[mainType + 'Name'] = payload[mainType + 'Name'];
  20793. var condition = {mainType: mainType, query: query};
  20794. subType && (condition.subType = subType); // subType may be '' by parseClassType;
  20795. // If dispatchAction before setOption, do nothing.
  20796. ecModel && ecModel.eachComponent(condition, function (model, index) {
  20797. callView(ecIns[
  20798. mainType === 'series' ? '_chartsMap' : '_componentsMap'
  20799. ][model.__viewId]);
  20800. }, ecIns);
  20801. function callView(view) {
  20802. view && view.__alive && view[method] && view[method](
  20803. view.__model, ecModel, ecIns._api, payload
  20804. );
  20805. }
  20806. }
  20807. /**
  20808. * Resize the chart
  20809. * @param {Object} opts
  20810. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  20811. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  20812. * @param {boolean} [opts.silent=false]
  20813. */
  20814. echartsProto.resize = function (opts) {
  20815. if (__DEV__) {
  20816. assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
  20817. }
  20818. this._zr.resize(opts);
  20819. var ecModel = this._model;
  20820. // Resize loading effect
  20821. this._loadingFX && this._loadingFX.resize();
  20822. if (!ecModel) {
  20823. return;
  20824. }
  20825. var optionChanged = ecModel.resetOption('media');
  20826. refresh(this, optionChanged, opts && opts.silent);
  20827. };
  20828. function refresh(ecIns, needPrepare, silent) {
  20829. ecIns[IN_MAIN_PROCESS] = true;
  20830. needPrepare && prepare(ecIns);
  20831. updateMethods.update.call(ecIns);
  20832. ecIns[IN_MAIN_PROCESS] = false;
  20833. flushPendingActions.call(ecIns, silent);
  20834. triggerUpdatedEvent.call(ecIns, silent);
  20835. }
  20836. function updateStreamModes(ecIns, ecModel) {
  20837. var chartsMap = ecIns._chartsMap;
  20838. var scheduler = ecIns._scheduler;
  20839. ecModel.eachSeries(function (seriesModel) {
  20840. scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);
  20841. });
  20842. }
  20843. /**
  20844. * Show loading effect
  20845. * @param {string} [name='default']
  20846. * @param {Object} [cfg]
  20847. */
  20848. echartsProto.showLoading = function (name, cfg) {
  20849. if (isObject(name)) {
  20850. cfg = name;
  20851. name = '';
  20852. }
  20853. name = name || 'default';
  20854. this.hideLoading();
  20855. if (!loadingEffects[name]) {
  20856. if (__DEV__) {
  20857. console.warn('Loading effects ' + name + ' not exists.');
  20858. }
  20859. return;
  20860. }
  20861. var el = loadingEffects[name](this._api, cfg);
  20862. var zr = this._zr;
  20863. this._loadingFX = el;
  20864. zr.add(el);
  20865. };
  20866. /**
  20867. * Hide loading effect
  20868. */
  20869. echartsProto.hideLoading = function () {
  20870. this._loadingFX && this._zr.remove(this._loadingFX);
  20871. this._loadingFX = null;
  20872. };
  20873. /**
  20874. * @param {Object} eventObj
  20875. * @return {Object}
  20876. */
  20877. echartsProto.makeActionFromEvent = function (eventObj) {
  20878. var payload = extend({}, eventObj);
  20879. payload.type = eventActionMap[eventObj.type];
  20880. return payload;
  20881. };
  20882. /**
  20883. * @pubilc
  20884. * @param {Object} payload
  20885. * @param {string} [payload.type] Action type
  20886. * @param {Object|boolean} [opt] If pass boolean, means opt.silent
  20887. * @param {boolean} [opt.silent=false] Whether trigger events.
  20888. * @param {boolean} [opt.flush=undefined]
  20889. * true: Flush immediately, and then pixel in canvas can be fetched
  20890. * immediately. Caution: it might affect performance.
  20891. * false: Not not flush.
  20892. * undefined: Auto decide whether perform flush.
  20893. */
  20894. echartsProto.dispatchAction = function (payload, opt) {
  20895. if (!isObject(opt)) {
  20896. opt = {silent: !!opt};
  20897. }
  20898. if (!actions[payload.type]) {
  20899. return;
  20900. }
  20901. // Avoid dispatch action before setOption. Especially in `connect`.
  20902. if (!this._model) {
  20903. return;
  20904. }
  20905. // May dispatchAction in rendering procedure
  20906. if (this[IN_MAIN_PROCESS]) {
  20907. this._pendingActions.push(payload);
  20908. return;
  20909. }
  20910. doDispatchAction.call(this, payload, opt.silent);
  20911. if (opt.flush) {
  20912. this._zr.flush(true);
  20913. }
  20914. else if (opt.flush !== false && env$1.browser.weChat) {
  20915. // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
  20916. // hang when sliding page (on touch event), which cause that zr does not
  20917. // refresh util user interaction finished, which is not expected.
  20918. // But `dispatchAction` may be called too frequently when pan on touch
  20919. // screen, which impacts performance if do not throttle them.
  20920. this._throttledZrFlush();
  20921. }
  20922. flushPendingActions.call(this, opt.silent);
  20923. triggerUpdatedEvent.call(this, opt.silent);
  20924. };
  20925. function doDispatchAction(payload, silent) {
  20926. var payloadType = payload.type;
  20927. var escapeConnect = payload.escapeConnect;
  20928. var actionWrap = actions[payloadType];
  20929. var actionInfo = actionWrap.actionInfo;
  20930. var cptType = (actionInfo.update || 'update').split(':');
  20931. var updateMethod = cptType.pop();
  20932. cptType = cptType[0] != null && parseClassType(cptType[0]);
  20933. this[IN_MAIN_PROCESS] = true;
  20934. var payloads = [payload];
  20935. var batched = false;
  20936. // Batch action
  20937. if (payload.batch) {
  20938. batched = true;
  20939. payloads = map(payload.batch, function (item) {
  20940. item = defaults(extend({}, item), payload);
  20941. item.batch = null;
  20942. return item;
  20943. });
  20944. }
  20945. var eventObjBatch = [];
  20946. var eventObj;
  20947. var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
  20948. each(payloads, function (batchItem) {
  20949. // Action can specify the event by return it.
  20950. eventObj = actionWrap.action(batchItem, this._model, this._api);
  20951. // Emit event outside
  20952. eventObj = eventObj || extend({}, batchItem);
  20953. // Convert type to eventType
  20954. eventObj.type = actionInfo.event || eventObj.type;
  20955. eventObjBatch.push(eventObj);
  20956. // light update does not perform data process, layout and visual.
  20957. if (isHighDown) {
  20958. // method, payload, mainType, subType
  20959. updateDirectly(this, updateMethod, batchItem, 'series');
  20960. }
  20961. else if (cptType) {
  20962. updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
  20963. }
  20964. }, this);
  20965. if (updateMethod !== 'none' && !isHighDown && !cptType) {
  20966. // Still dirty
  20967. if (this[OPTION_UPDATED]) {
  20968. // FIXME Pass payload ?
  20969. prepare(this);
  20970. updateMethods.update.call(this, payload);
  20971. this[OPTION_UPDATED] = false;
  20972. }
  20973. else {
  20974. updateMethods[updateMethod].call(this, payload);
  20975. }
  20976. }
  20977. // Follow the rule of action batch
  20978. if (batched) {
  20979. eventObj = {
  20980. type: actionInfo.event || payloadType,
  20981. escapeConnect: escapeConnect,
  20982. batch: eventObjBatch
  20983. };
  20984. }
  20985. else {
  20986. eventObj = eventObjBatch[0];
  20987. }
  20988. this[IN_MAIN_PROCESS] = false;
  20989. !silent && this._messageCenter.trigger(eventObj.type, eventObj);
  20990. }
  20991. function flushPendingActions(silent) {
  20992. var pendingActions = this._pendingActions;
  20993. while (pendingActions.length) {
  20994. var payload = pendingActions.shift();
  20995. doDispatchAction.call(this, payload, silent);
  20996. }
  20997. }
  20998. function triggerUpdatedEvent(silent) {
  20999. !silent && this.trigger('updated');
  21000. }
  21001. /**
  21002. * @param {Object} params
  21003. * @param {number} params.seriesIndex
  21004. * @param {Array|TypedArray} params.data
  21005. */
  21006. echartsProto.appendData = function (params) {
  21007. var seriesIndex = params.seriesIndex;
  21008. var ecModel = this.getModel();
  21009. var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
  21010. if (__DEV__) {
  21011. assert(params.data && seriesModel);
  21012. }
  21013. seriesModel.appendData(params);
  21014. this._scheduler.unfinished = true;
  21015. };
  21016. /**
  21017. * Register event
  21018. * @method
  21019. */
  21020. echartsProto.on = createRegisterEventWithLowercaseName('on');
  21021. echartsProto.off = createRegisterEventWithLowercaseName('off');
  21022. echartsProto.one = createRegisterEventWithLowercaseName('one');
  21023. /**
  21024. * Prepare view instances of charts and components
  21025. * @param {module:echarts/model/Global} ecModel
  21026. * @private
  21027. */
  21028. function prepareView(ecIns, type, ecModel, scheduler) {
  21029. var isComponent = type === 'component';
  21030. var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;
  21031. var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;
  21032. var zr = ecIns._zr;
  21033. var api = ecIns._api;
  21034. for (var i = 0; i < viewList.length; i++) {
  21035. viewList[i].__alive = false;
  21036. }
  21037. isComponent
  21038. ? ecModel.eachComponent(function (componentType, model) {
  21039. componentType !== 'series' && doPrepare(model);
  21040. })
  21041. : ecModel.eachSeries(doPrepare);
  21042. function doPrepare(model) {
  21043. // Consider: id same and type changed.
  21044. var viewId = '_ec_' + model.id + '_' + model.type;
  21045. var view = viewMap[viewId];
  21046. if (!view) {
  21047. var classType = parseClassType(model.type);
  21048. var Clazz = isComponent
  21049. ? Component.getClass(classType.main, classType.sub)
  21050. : Chart.getClass(classType.sub);
  21051. if (__DEV__) {
  21052. assert(Clazz, classType.sub + ' does not exist.');
  21053. }
  21054. view = new Clazz();
  21055. view.init(ecModel, api);
  21056. viewMap[viewId] = view;
  21057. viewList.push(view);
  21058. zr.add(view.group);
  21059. }
  21060. model.__viewId = view.__id = viewId;
  21061. view.__alive = true;
  21062. view.__model = model;
  21063. view.group.__ecComponentInfo = {
  21064. mainType: model.mainType,
  21065. index: model.componentIndex
  21066. };
  21067. !isComponent && scheduler.prepareView(view, model, ecModel, api);
  21068. }
  21069. for (var i = 0; i < viewList.length;) {
  21070. var view = viewList[i];
  21071. if (!view.__alive) {
  21072. !isComponent && view.renderTask.dispose();
  21073. zr.remove(view.group);
  21074. view.dispose(ecModel, api);
  21075. viewList.splice(i, 1);
  21076. delete viewMap[view.__id];
  21077. view.__id = view.group.__ecComponentInfo = null;
  21078. }
  21079. else {
  21080. i++;
  21081. }
  21082. }
  21083. }
  21084. /**
  21085. * @private
  21086. */
  21087. function stackSeriesData(ecModel) {
  21088. var stackedDataMap = {};
  21089. ecModel.eachSeries(function (series) {
  21090. var stack = series.get('stack');
  21091. var data = series.getData();
  21092. if (stack && data.type === 'list') {
  21093. var previousStack = stackedDataMap[stack];
  21094. // Avoid conflict with Object.prototype
  21095. if (stackedDataMap.hasOwnProperty(stack) && previousStack) {
  21096. data.stackedOn = previousStack;
  21097. }
  21098. stackedDataMap[stack] = data;
  21099. }
  21100. });
  21101. }
  21102. // /**
  21103. // * Encode visual infomation from data after data processing
  21104. // *
  21105. // * @param {module:echarts/model/Global} ecModel
  21106. // * @param {object} layout
  21107. // * @param {boolean} [layoutFilter] `true`: only layout,
  21108. // * `false`: only not layout,
  21109. // * `null`/`undefined`: all.
  21110. // * @param {string} taskBaseTag
  21111. // * @private
  21112. // */
  21113. // function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) {
  21114. // each(visualFuncs, function (visual, index) {
  21115. // var isLayout = visual.isLayout;
  21116. // if (layoutFilter == null
  21117. // || (layoutFilter === false && !isLayout)
  21118. // || (layoutFilter === true && isLayout)
  21119. // ) {
  21120. // visual.func(ecModel, api, payload);
  21121. // }
  21122. // });
  21123. // }
  21124. function clearColorPalette(ecModel) {
  21125. ecModel.clearColorPalette();
  21126. ecModel.eachSeries(function (seriesModel) {
  21127. seriesModel.clearColorPalette();
  21128. });
  21129. }
  21130. function render(ecIns, ecModel, api, payload) {
  21131. renderComponents(ecIns, ecModel, api, payload);
  21132. each(ecIns._chartsViews, function (chart) {
  21133. chart.__alive = false;
  21134. });
  21135. renderSeries(ecIns, ecModel, api, payload);
  21136. // Remove groups of unrendered charts
  21137. each(ecIns._chartsViews, function (chart) {
  21138. if (!chart.__alive) {
  21139. chart.remove(ecModel, api);
  21140. }
  21141. });
  21142. }
  21143. function renderComponents(ecIns, ecModel, api, payload, dirtyList) {
  21144. each(dirtyList || ecIns._componentsViews, function (componentView) {
  21145. var componentModel = componentView.__model;
  21146. componentView.render(componentModel, ecModel, api, payload);
  21147. updateZ(componentModel, componentView);
  21148. });
  21149. }
  21150. /**
  21151. * Render each chart and component
  21152. * @private
  21153. */
  21154. function renderSeries(ecIns, ecModel, api, payload, dirtyMap) {
  21155. // Render all charts
  21156. var scheduler = ecIns._scheduler;
  21157. var unfinished;
  21158. ecModel.eachSeries(function (seriesModel) {
  21159. var chartView = ecIns._chartsMap[seriesModel.__viewId];
  21160. chartView.__alive = true;
  21161. var renderTask = chartView.renderTask;
  21162. scheduler.updatePayload(renderTask, payload);
  21163. if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
  21164. renderTask.dirty();
  21165. }
  21166. unfinished |= renderTask.perform(scheduler.getPerformArgs(renderTask));
  21167. chartView.group.silent = !!seriesModel.get('silent');
  21168. updateZ(seriesModel, chartView);
  21169. updateBlend(seriesModel, chartView);
  21170. });
  21171. scheduler.unfinished |= unfinished;
  21172. // If use hover layer
  21173. updateHoverLayerStatus(ecIns._zr, ecModel);
  21174. // Add aria
  21175. aria(ecIns._zr.dom, ecModel);
  21176. }
  21177. function performPostUpdateFuncs(ecModel, api) {
  21178. each(postUpdateFuncs, function (func) {
  21179. func(ecModel, api);
  21180. });
  21181. }
  21182. var MOUSE_EVENT_NAMES = [
  21183. 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
  21184. 'mousedown', 'mouseup', 'globalout', 'contextmenu'
  21185. ];
  21186. /**
  21187. * @private
  21188. */
  21189. echartsProto._initEvents = function () {
  21190. each(MOUSE_EVENT_NAMES, function (eveName) {
  21191. this._zr.on(eveName, function (e) {
  21192. var ecModel = this.getModel();
  21193. var el = e.target;
  21194. var params;
  21195. // no e.target when 'globalout'.
  21196. if (eveName === 'globalout') {
  21197. params = {};
  21198. }
  21199. else if (el && el.dataIndex != null) {
  21200. var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
  21201. params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};
  21202. }
  21203. // If element has custom eventData of components
  21204. else if (el && el.eventData) {
  21205. params = extend({}, el.eventData);
  21206. }
  21207. if (params) {
  21208. params.event = e;
  21209. params.type = eveName;
  21210. this.trigger(eveName, params);
  21211. }
  21212. }, this);
  21213. }, this);
  21214. each(eventActionMap, function (actionType, eventType) {
  21215. this._messageCenter.on(eventType, function (event) {
  21216. this.trigger(eventType, event);
  21217. }, this);
  21218. }, this);
  21219. };
  21220. /**
  21221. * @return {boolean}
  21222. */
  21223. echartsProto.isDisposed = function () {
  21224. return this._disposed;
  21225. };
  21226. /**
  21227. * Clear
  21228. */
  21229. echartsProto.clear = function () {
  21230. this.setOption({ series: [] }, true);
  21231. };
  21232. /**
  21233. * Dispose instance
  21234. */
  21235. echartsProto.dispose = function () {
  21236. if (this._disposed) {
  21237. if (__DEV__) {
  21238. console.warn('Instance ' + this.id + ' has been disposed');
  21239. }
  21240. return;
  21241. }
  21242. this._disposed = true;
  21243. setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
  21244. var api = this._api;
  21245. var ecModel = this._model;
  21246. each(this._componentsViews, function (component) {
  21247. component.dispose(ecModel, api);
  21248. });
  21249. each(this._chartsViews, function (chart) {
  21250. chart.dispose(ecModel, api);
  21251. });
  21252. // Dispose after all views disposed
  21253. this._zr.dispose();
  21254. delete instances[this.id];
  21255. };
  21256. mixin(ECharts, Eventful);
  21257. function updateHoverLayerStatus(zr, ecModel) {
  21258. var storage = zr.storage;
  21259. var elCount = 0;
  21260. storage.traverse(function (el) {
  21261. if (!el.isGroup) {
  21262. elCount++;
  21263. }
  21264. });
  21265. if (elCount > ecModel.get('hoverLayerThreshold') && !env$1.node) {
  21266. storage.traverse(function (el) {
  21267. if (!el.isGroup) {
  21268. // Don't switch back.
  21269. el.useHoverLayer = true;
  21270. }
  21271. });
  21272. }
  21273. }
  21274. /**
  21275. * Update chart progressive and blend.
  21276. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  21277. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  21278. */
  21279. function updateBlend(seriesModel, chartView) {
  21280. var blendMode = seriesModel.get('blendMode') || null;
  21281. if (__DEV__) {
  21282. if (!env$1.canvasSupported && blendMode && blendMode !== 'source-over') {
  21283. console.warn('Only canvas support blendMode');
  21284. }
  21285. }
  21286. chartView.group.traverse(function (el) {
  21287. // FIXME marker and other components
  21288. if (!el.isGroup) {
  21289. // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender.
  21290. if (el.style.blend !== blendMode) {
  21291. el.setStyle('blend', blendMode);
  21292. }
  21293. }
  21294. if (el.eachPendingDisplayable) {
  21295. el.eachPendingDisplayable(function (displayable) {
  21296. displayable.setStyle('blend', blendMode);
  21297. });
  21298. }
  21299. });
  21300. }
  21301. /**
  21302. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  21303. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  21304. */
  21305. function updateZ(model, view) {
  21306. var z = model.get('z');
  21307. var zlevel = model.get('zlevel');
  21308. // Set z and zlevel
  21309. view.group.traverse(function (el) {
  21310. if (el.type !== 'group') {
  21311. z != null && (el.z = z);
  21312. zlevel != null && (el.zlevel = zlevel);
  21313. }
  21314. });
  21315. }
  21316. function createExtensionAPI(ecInstance) {
  21317. var coordSysMgr = ecInstance._coordSysMgr;
  21318. return extend(new ExtensionAPI(ecInstance), {
  21319. // Inject methods
  21320. getCoordinateSystems: bind(
  21321. coordSysMgr.getCoordinateSystems, coordSysMgr
  21322. ),
  21323. getComponentByElement: function (el) {
  21324. while (el) {
  21325. var modelInfo = el.__ecComponentInfo;
  21326. if (modelInfo != null) {
  21327. return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
  21328. }
  21329. el = el.parent;
  21330. }
  21331. }
  21332. });
  21333. }
  21334. /**
  21335. * @type {Object} key: actionType.
  21336. * @inner
  21337. */
  21338. var actions = {};
  21339. /**
  21340. * Map eventType to actionType
  21341. * @type {Object}
  21342. */
  21343. var eventActionMap = {};
  21344. /**
  21345. * Data processor functions of each stage
  21346. * @type {Array.<Object.<string, Function>>}
  21347. * @inner
  21348. */
  21349. var dataProcessorFuncs = [];
  21350. /**
  21351. * @type {Array.<Function>}
  21352. * @inner
  21353. */
  21354. var optionPreprocessorFuncs = [];
  21355. /**
  21356. * @type {Array.<Function>}
  21357. * @inner
  21358. */
  21359. var postUpdateFuncs = [];
  21360. /**
  21361. * Visual encoding functions of each stage
  21362. * @type {Array.<Object.<string, Function>>}
  21363. */
  21364. var visualFuncs = [];
  21365. /**
  21366. * Theme storage
  21367. * @type {Object.<key, Object>}
  21368. */
  21369. var themeStorage = {};
  21370. /**
  21371. * Loading effects
  21372. */
  21373. var loadingEffects = {};
  21374. var instances = {};
  21375. var connectedGroups = {};
  21376. var idBase = new Date() - 0;
  21377. var groupIdBase = new Date() - 0;
  21378. var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
  21379. var mapDataStores = {};
  21380. function enableConnect(chart) {
  21381. var STATUS_PENDING = 0;
  21382. var STATUS_UPDATING = 1;
  21383. var STATUS_UPDATED = 2;
  21384. var STATUS_KEY = '__connectUpdateStatus';
  21385. function updateConnectedChartsStatus(charts, status) {
  21386. for (var i = 0; i < charts.length; i++) {
  21387. var otherChart = charts[i];
  21388. otherChart[STATUS_KEY] = status;
  21389. }
  21390. }
  21391. each(eventActionMap, function (actionType, eventType) {
  21392. chart._messageCenter.on(eventType, function (event) {
  21393. if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
  21394. if (event && event.escapeConnect) {
  21395. return;
  21396. }
  21397. var action = chart.makeActionFromEvent(event);
  21398. var otherCharts = [];
  21399. each(instances, function (otherChart) {
  21400. if (otherChart !== chart && otherChart.group === chart.group) {
  21401. otherCharts.push(otherChart);
  21402. }
  21403. });
  21404. updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
  21405. each(otherCharts, function (otherChart) {
  21406. if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
  21407. otherChart.dispatchAction(action);
  21408. }
  21409. });
  21410. updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
  21411. }
  21412. });
  21413. });
  21414. }
  21415. /**
  21416. * @param {HTMLElement} dom
  21417. * @param {Object} [theme]
  21418. * @param {Object} opts
  21419. * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
  21420. * @param {string} [opts.renderer] Currently only 'canvas' is supported.
  21421. * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
  21422. * Can be 'auto' (the same as null/undefined)
  21423. * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
  21424. * Can be 'auto' (the same as null/undefined)
  21425. */
  21426. function init(dom, theme$$1, opts) {
  21427. if (__DEV__) {
  21428. // Check version
  21429. if ((version$1.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) {
  21430. throw new Error(
  21431. 'zrender/src ' + version$1
  21432. + ' is too old for ECharts ' + version
  21433. + '. Current version need ZRender '
  21434. + dependencies.zrender + '+'
  21435. );
  21436. }
  21437. if (!dom) {
  21438. throw new Error('Initialize failed: invalid dom.');
  21439. }
  21440. }
  21441. var existInstance = getInstanceByDom(dom);
  21442. if (existInstance) {
  21443. if (__DEV__) {
  21444. console.warn('There is a chart instance already initialized on the dom.');
  21445. }
  21446. return existInstance;
  21447. }
  21448. if (__DEV__) {
  21449. if (isDom(dom)
  21450. && dom.nodeName.toUpperCase() !== 'CANVAS'
  21451. && (
  21452. (!dom.clientWidth && (!opts || opts.width == null))
  21453. || (!dom.clientHeight && (!opts || opts.height == null))
  21454. )
  21455. ) {
  21456. console.warn('Can\'t get dom width or height');
  21457. }
  21458. }
  21459. var chart = new ECharts(dom, theme$$1, opts);
  21460. chart.id = 'ec_' + idBase++;
  21461. instances[chart.id] = chart;
  21462. setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);
  21463. enableConnect(chart);
  21464. return chart;
  21465. }
  21466. /**
  21467. * @return {string|Array.<module:echarts~ECharts>} groupId
  21468. */
  21469. function connect(groupId) {
  21470. // Is array of charts
  21471. if (isArray(groupId)) {
  21472. var charts = groupId;
  21473. groupId = null;
  21474. // If any chart has group
  21475. each(charts, function (chart) {
  21476. if (chart.group != null) {
  21477. groupId = chart.group;
  21478. }
  21479. });
  21480. groupId = groupId || ('g_' + groupIdBase++);
  21481. each(charts, function (chart) {
  21482. chart.group = groupId;
  21483. });
  21484. }
  21485. connectedGroups[groupId] = true;
  21486. return groupId;
  21487. }
  21488. /**
  21489. * @DEPRECATED
  21490. * @return {string} groupId
  21491. */
  21492. function disConnect(groupId) {
  21493. connectedGroups[groupId] = false;
  21494. }
  21495. /**
  21496. * @return {string} groupId
  21497. */
  21498. var disconnect = disConnect;
  21499. /**
  21500. * Dispose a chart instance
  21501. * @param {module:echarts~ECharts|HTMLDomElement|string} chart
  21502. */
  21503. function dispose(chart) {
  21504. if (typeof chart === 'string') {
  21505. chart = instances[chart];
  21506. }
  21507. else if (!(chart instanceof ECharts)){
  21508. // Try to treat as dom
  21509. chart = getInstanceByDom(chart);
  21510. }
  21511. if ((chart instanceof ECharts) && !chart.isDisposed()) {
  21512. chart.dispose();
  21513. }
  21514. }
  21515. /**
  21516. * @param {HTMLElement} dom
  21517. * @return {echarts~ECharts}
  21518. */
  21519. function getInstanceByDom(dom) {
  21520. return instances[getAttribute(dom, DOM_ATTRIBUTE_KEY)];
  21521. }
  21522. /**
  21523. * @param {string} key
  21524. * @return {echarts~ECharts}
  21525. */
  21526. function getInstanceById(key) {
  21527. return instances[key];
  21528. }
  21529. /**
  21530. * Register theme
  21531. */
  21532. function registerTheme(name, theme$$1) {
  21533. themeStorage[name] = theme$$1;
  21534. }
  21535. /**
  21536. * Register option preprocessor
  21537. * @param {Function} preprocessorFunc
  21538. */
  21539. function registerPreprocessor(preprocessorFunc) {
  21540. optionPreprocessorFuncs.push(preprocessorFunc);
  21541. }
  21542. /**
  21543. * @param {number} [priority=1000]
  21544. * @param {Object|Function} processor
  21545. */
  21546. function registerProcessor(priority, processor) {
  21547. normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_FILTER);
  21548. }
  21549. /**
  21550. * Register postUpdater
  21551. * @param {Function} postUpdateFunc
  21552. */
  21553. function registerPostUpdate(postUpdateFunc) {
  21554. postUpdateFuncs.push(postUpdateFunc);
  21555. }
  21556. /**
  21557. * Usage:
  21558. * registerAction('someAction', 'someEvent', function () { ... });
  21559. * registerAction('someAction', function () { ... });
  21560. * registerAction(
  21561. * {type: 'someAction', event: 'someEvent', update: 'updateView'},
  21562. * function () { ... }
  21563. * );
  21564. *
  21565. * @param {(string|Object)} actionInfo
  21566. * @param {string} actionInfo.type
  21567. * @param {string} [actionInfo.event]
  21568. * @param {string} [actionInfo.update]
  21569. * @param {string} [eventName]
  21570. * @param {Function} action
  21571. */
  21572. function registerAction(actionInfo, eventName, action) {
  21573. if (typeof eventName === 'function') {
  21574. action = eventName;
  21575. eventName = '';
  21576. }
  21577. var actionType = isObject(actionInfo)
  21578. ? actionInfo.type
  21579. : ([actionInfo, actionInfo = {
  21580. event: eventName
  21581. }][0]);
  21582. // Event name is all lowercase
  21583. actionInfo.event = (actionInfo.event || actionType).toLowerCase();
  21584. eventName = actionInfo.event;
  21585. // Validate action type and event name.
  21586. assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
  21587. if (!actions[actionType]) {
  21588. actions[actionType] = {action: action, actionInfo: actionInfo};
  21589. }
  21590. eventActionMap[eventName] = actionType;
  21591. }
  21592. /**
  21593. * @param {string} type
  21594. * @param {*} CoordinateSystem
  21595. */
  21596. function registerCoordinateSystem(type, CoordinateSystem$$1) {
  21597. CoordinateSystemManager.register(type, CoordinateSystem$$1);
  21598. }
  21599. /**
  21600. * Get dimensions of specified coordinate system.
  21601. * @param {string} type
  21602. * @return {Array.<string|Object>}
  21603. */
  21604. function getCoordinateSystemDimensions(type) {
  21605. var coordSysCreator = CoordinateSystemManager.get(type);
  21606. if (coordSysCreator) {
  21607. return coordSysCreator.getDimensionsInfo
  21608. ? coordSysCreator.getDimensionsInfo()
  21609. : coordSysCreator.dimensions.slice();
  21610. }
  21611. }
  21612. /**
  21613. * Layout is a special stage of visual encoding
  21614. * Most visual encoding like color are common for different chart
  21615. * But each chart has it's own layout algorithm
  21616. *
  21617. * @param {number} [priority=1000]
  21618. * @param {Function} layoutTask
  21619. */
  21620. function registerLayout(priority, layoutTask) {
  21621. normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');
  21622. }
  21623. /**
  21624. * @param {number} [priority=3000]
  21625. * @param {module:echarts/stream/Task} visualTask
  21626. */
  21627. function registerVisual(priority, visualTask) {
  21628. normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');
  21629. }
  21630. /**
  21631. * @param {Object|Function} fn: {seriesType, createOnAllSeries, performRawSeries, reset}
  21632. */
  21633. function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {
  21634. if (isFunction(priority) || isObject(priority)) {
  21635. fn = priority;
  21636. priority = defaultPriority;
  21637. }
  21638. if (__DEV__) {
  21639. if (isNaN(priority) || priority == null) {
  21640. throw new Error('Illegal priority');
  21641. }
  21642. // Check duplicate
  21643. each(targetList, function (wrap) {
  21644. assert(wrap.__raw !== fn);
  21645. });
  21646. }
  21647. var stageHandler = Scheduler.wrapStageHandler(fn, visualType);
  21648. stageHandler.__prio = priority;
  21649. stageHandler.__raw = fn;
  21650. targetList.push(stageHandler);
  21651. return stageHandler;
  21652. }
  21653. /**
  21654. * @param {string} name
  21655. */
  21656. function registerLoading(name, loadingFx) {
  21657. loadingEffects[name] = loadingFx;
  21658. }
  21659. /**
  21660. * @param {Object} opts
  21661. * @param {string} [superClass]
  21662. */
  21663. function extendComponentModel(opts/*, superClass*/) {
  21664. // var Clazz = ComponentModel;
  21665. // if (superClass) {
  21666. // var classType = parseClassType(superClass);
  21667. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  21668. // }
  21669. return ComponentModel.extend(opts);
  21670. }
  21671. /**
  21672. * @param {Object} opts
  21673. * @param {string} [superClass]
  21674. */
  21675. function extendComponentView(opts/*, superClass*/) {
  21676. // var Clazz = ComponentView;
  21677. // if (superClass) {
  21678. // var classType = parseClassType(superClass);
  21679. // Clazz = ComponentView.getClass(classType.main, classType.sub, true);
  21680. // }
  21681. return Component.extend(opts);
  21682. }
  21683. /**
  21684. * @param {Object} opts
  21685. * @param {string} [superClass]
  21686. */
  21687. function extendSeriesModel(opts/*, superClass*/) {
  21688. // var Clazz = SeriesModel;
  21689. // if (superClass) {
  21690. // superClass = 'series.' + superClass.replace('series.', '');
  21691. // var classType = parseClassType(superClass);
  21692. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  21693. // }
  21694. return SeriesModel.extend(opts);
  21695. }
  21696. /**
  21697. * @param {Object} opts
  21698. * @param {string} [superClass]
  21699. */
  21700. function extendChartView(opts/*, superClass*/) {
  21701. // var Clazz = ChartView;
  21702. // if (superClass) {
  21703. // superClass = superClass.replace('series.', '');
  21704. // var classType = parseClassType(superClass);
  21705. // Clazz = ChartView.getClass(classType.main, true);
  21706. // }
  21707. return Chart.extend(opts);
  21708. }
  21709. /**
  21710. * ZRender need a canvas context to do measureText.
  21711. * But in node environment canvas may be created by node-canvas.
  21712. * So we need to specify how to create a canvas instead of using document.createElement('canvas')
  21713. *
  21714. * Be careful of using it in the browser.
  21715. *
  21716. * @param {Function} creator
  21717. * @example
  21718. * var Canvas = require('canvas');
  21719. * var echarts = require('echarts');
  21720. * echarts.setCanvasCreator(function () {
  21721. * // Small size is enough.
  21722. * return new Canvas(32, 32);
  21723. * });
  21724. */
  21725. function setCanvasCreator(creator) {
  21726. $override('createCanvas', creator);
  21727. }
  21728. /**
  21729. * @param {string} mapName
  21730. * @param {Object|string} geoJson
  21731. * @param {Object} [specialAreas]
  21732. *
  21733. * @example
  21734. * $.get('USA.json', function (geoJson) {
  21735. * echarts.registerMap('USA', geoJson);
  21736. * // Or
  21737. * echarts.registerMap('USA', {
  21738. * geoJson: geoJson,
  21739. * specialAreas: {}
  21740. * })
  21741. * });
  21742. */
  21743. function registerMap(mapName, geoJson, specialAreas) {
  21744. if (geoJson.geoJson && !geoJson.features) {
  21745. specialAreas = geoJson.specialAreas;
  21746. geoJson = geoJson.geoJson;
  21747. }
  21748. if (typeof geoJson === 'string') {
  21749. geoJson = (typeof JSON !== 'undefined' && JSON.parse)
  21750. ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))();
  21751. }
  21752. mapDataStores[mapName] = {
  21753. geoJson: geoJson,
  21754. specialAreas: specialAreas
  21755. };
  21756. }
  21757. /**
  21758. * @param {string} mapName
  21759. * @return {Object}
  21760. */
  21761. function getMap(mapName) {
  21762. return mapDataStores[mapName];
  21763. }
  21764. registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
  21765. registerPreprocessor(backwardCompat);
  21766. registerLoading('default', loadingDefault);
  21767. // Default actions
  21768. registerAction({
  21769. type: 'highlight',
  21770. event: 'highlight',
  21771. update: 'highlight'
  21772. }, noop);
  21773. registerAction({
  21774. type: 'downplay',
  21775. event: 'downplay',
  21776. update: 'downplay'
  21777. }, noop);
  21778. // Default theme
  21779. registerTheme('light', lightTheme);
  21780. registerTheme('dark', theme);
  21781. // For backward compatibility, where the namespace `dataTool` will
  21782. // be mounted on `echarts` is the extension `dataTool` is imported.
  21783. var dataTool = {};
  21784. var DatasetModel = extendComponentModel({
  21785. type: 'dataset',
  21786. /**
  21787. * @protected
  21788. */
  21789. defaultOption: {
  21790. // 'row', 'column'
  21791. seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
  21792. // null/'auto': auto detect header, see "module:echarts/data/helper/sourceHelper"
  21793. sourceHeader: null,
  21794. dimensions: null,
  21795. source: null
  21796. },
  21797. optionUpdated: function () {
  21798. detectSourceFormat(this);
  21799. }
  21800. });
  21801. extendComponentView({type: 'dataset'});
  21802. function defaultKeyGetter(item) {
  21803. return item;
  21804. }
  21805. /**
  21806. * @param {Array} oldArr
  21807. * @param {Array} newArr
  21808. * @param {Function} oldKeyGetter
  21809. * @param {Function} newKeyGetter
  21810. * @param {Object} [context] Can be visited by this.context in callback.
  21811. */
  21812. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
  21813. this._old = oldArr;
  21814. this._new = newArr;
  21815. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  21816. this._newKeyGetter = newKeyGetter || defaultKeyGetter;
  21817. this.context = context;
  21818. }
  21819. DataDiffer.prototype = {
  21820. constructor: DataDiffer,
  21821. /**
  21822. * Callback function when add a data
  21823. */
  21824. add: function (func) {
  21825. this._add = func;
  21826. return this;
  21827. },
  21828. /**
  21829. * Callback function when update a data
  21830. */
  21831. update: function (func) {
  21832. this._update = func;
  21833. return this;
  21834. },
  21835. /**
  21836. * Callback function when remove a data
  21837. */
  21838. remove: function (func) {
  21839. this._remove = func;
  21840. return this;
  21841. },
  21842. execute: function () {
  21843. var oldArr = this._old;
  21844. var newArr = this._new;
  21845. var oldDataIndexMap = {};
  21846. var newDataIndexMap = {};
  21847. var oldDataKeyArr = [];
  21848. var newDataKeyArr = [];
  21849. var i;
  21850. initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
  21851. initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);
  21852. // Travel by inverted order to make sure order consistency
  21853. // when duplicate keys exists (consider newDataIndex.pop() below).
  21854. // For performance consideration, these code below do not look neat.
  21855. for (i = 0; i < oldArr.length; i++) {
  21856. var key = oldDataKeyArr[i];
  21857. var idx = newDataIndexMap[key];
  21858. // idx can never be empty array here. see 'set null' logic below.
  21859. if (idx != null) {
  21860. // Consider there is duplicate key (for example, use dataItem.name as key).
  21861. // We should make sure every item in newArr and oldArr can be visited.
  21862. var len = idx.length;
  21863. if (len) {
  21864. len === 1 && (newDataIndexMap[key] = null);
  21865. idx = idx.unshift();
  21866. }
  21867. else {
  21868. newDataIndexMap[key] = null;
  21869. }
  21870. this._update && this._update(idx, i);
  21871. }
  21872. else {
  21873. this._remove && this._remove(i);
  21874. }
  21875. }
  21876. for (var i = 0; i < newDataKeyArr.length; i++) {
  21877. var key = newDataKeyArr[i];
  21878. if (newDataIndexMap.hasOwnProperty(key)) {
  21879. var idx = newDataIndexMap[key];
  21880. if (idx == null) {
  21881. continue;
  21882. }
  21883. // idx can never be empty array here. see 'set null' logic above.
  21884. if (!idx.length) {
  21885. this._add && this._add(idx);
  21886. }
  21887. else {
  21888. for (var j = 0, len = idx.length; j < len; j++) {
  21889. this._add && this._add(idx[j]);
  21890. }
  21891. }
  21892. }
  21893. }
  21894. }
  21895. };
  21896. function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
  21897. for (var i = 0; i < arr.length; i++) {
  21898. // Add prefix to avoid conflict with Object.prototype.
  21899. var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
  21900. var existence = map[key];
  21901. if (existence == null) {
  21902. keyArr.push(key);
  21903. map[key] = i;
  21904. }
  21905. else {
  21906. if (!existence.length) {
  21907. map[key] = existence = [existence];
  21908. }
  21909. existence.push(i);
  21910. }
  21911. }
  21912. }
  21913. var OTHER_DIMENSIONS = createHashMap([
  21914. 'tooltip', 'label', 'itemName', 'itemId', 'seriesName'
  21915. ]);
  21916. function summarizeDimensions(data) {
  21917. var summary = {};
  21918. var encode = summary.encode = {};
  21919. var coordDimMap = summary.coordDimMap = createHashMap();
  21920. var defaultedLabel = [];
  21921. each$1(data.dimensions, function (dimName) {
  21922. var dimItem = data.getDimensionInfo(dimName);
  21923. var coordDim = dimItem.coordDim;
  21924. if (coordDim) {
  21925. if (__DEV__) {
  21926. assert$1(OTHER_DIMENSIONS.get(coordDim) == null);
  21927. }
  21928. var coordDimArr = encode[coordDim];
  21929. if (!encode.hasOwnProperty(coordDim)) {
  21930. coordDimArr = encode[coordDim] = [];
  21931. }
  21932. coordDimArr[dimItem.coordDimIndex] = dimName;
  21933. if (dimItem.isSysCoord && mayLabelDimType(dimItem.type)) {
  21934. // Use the last coord dim (and label friendly) as default label,
  21935. // because both show x, y on label is not look good, and usually
  21936. // y axis is more focusd conventionally.
  21937. defaultedLabel[0] = dimName;
  21938. }
  21939. coordDimMap.set(coordDim, 1);
  21940. }
  21941. OTHER_DIMENSIONS.each(function (v, otherDim) {
  21942. var otherDimArr = encode[otherDim];
  21943. if (!encode.hasOwnProperty(otherDim)) {
  21944. otherDimArr = encode[otherDim] = [];
  21945. }
  21946. var dimIndex = dimItem.otherDims[otherDim];
  21947. if (dimIndex != null && dimIndex !== false) {
  21948. otherDimArr[dimIndex] = dimItem.name;
  21949. }
  21950. });
  21951. });
  21952. var dataDimsOnCoord = [];
  21953. coordDimMap.each(function (v, coordDim) {
  21954. dataDimsOnCoord = dataDimsOnCoord.concat(encode[coordDim]);
  21955. });
  21956. summary.dataDimsOnCoord = dataDimsOnCoord;
  21957. var encodeLabel = encode.label;
  21958. // FIXME `encode.label` is not recommanded, because formatter can not be set
  21959. // in this way. Use label.formatter instead. May be remove this approach someday.
  21960. if (encodeLabel && encodeLabel.length) {
  21961. defaultedLabel = encodeLabel.slice();
  21962. }
  21963. var defaultedTooltip = defaultedLabel.slice();
  21964. var encodeTooltip = encode.tooltip;
  21965. if (encodeTooltip && encodeTooltip.length) {
  21966. defaultedTooltip = encodeTooltip.slice();
  21967. }
  21968. encode.defaultedLabel = defaultedLabel;
  21969. encode.defaultedTooltip = defaultedTooltip;
  21970. return summary;
  21971. }
  21972. function getDimensionTypeByAxis(axisType) {
  21973. return axisType === 'category'
  21974. ? 'ordinal'
  21975. : axisType === 'time'
  21976. ? 'time'
  21977. : 'float';
  21978. }
  21979. function mayLabelDimType(dimType) {
  21980. // In most cases, ordinal and time do not suitable for label.
  21981. // Ordinal info can be displayed on axis. Time is too long.
  21982. return !(dimType === 'ordinal' || dimType === 'time');
  21983. }
  21984. // function findTheLastDimMayLabel(data) {
  21985. // // Get last value dim
  21986. // var dimensions = data.dimensions.slice();
  21987. // var valueType;
  21988. // var valueDim;
  21989. // while (dimensions.length && (
  21990. // valueDim = dimensions.pop(),
  21991. // valueType = data.getDimensionInfo(valueDim).type,
  21992. // valueType === 'ordinal' || valueType === 'time'
  21993. // )) {} // jshint ignore:line
  21994. // return valueDim;
  21995. // }
  21996. /**
  21997. * List for data storage
  21998. * @module echarts/data/List
  21999. */
  22000. var isObject$4 = isObject$1;
  22001. var UNDEFINED = 'undefined';
  22002. var globalObj = typeof window === UNDEFINED ? global : window;
  22003. // Use prefix to avoid index to be the same as otherIdList[idx],
  22004. // which will cause weird udpate animation.
  22005. var ID_PREFIX = 'e\0\0';
  22006. var dataCtors = {
  22007. 'float': typeof globalObj.Float64Array === UNDEFINED
  22008. ? Array : globalObj.Float64Array,
  22009. 'int': typeof globalObj.Int32Array === UNDEFINED
  22010. ? Array : globalObj.Int32Array,
  22011. // Ordinal data type can be string or int
  22012. 'ordinal': Array,
  22013. 'number': Array,
  22014. 'time': Array
  22015. };
  22016. function getIndicesCtor(list) {
  22017. var CtorUint32Array = typeof globalObj.Uint32Array === UNDEFINED ? Array : globalObj.Uint32Array;
  22018. var CtorUint16Array = typeof globalObj.Uint16Array === UNDEFINED ? Array : globalObj.Uint16Array;
  22019. // The possible max value in this._indicies is always this._rawCount despite of filtering.
  22020. return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
  22021. }
  22022. function cloneChunk(originalChunk) {
  22023. var Ctor = originalChunk.constructor;
  22024. // Only shallow clone is enough when Array.
  22025. return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk);
  22026. }
  22027. var TRANSFERABLE_PROPERTIES = [
  22028. 'stackedOn', 'hasItemOption', '_nameList', '_idList',
  22029. '_rawData', '_rawExtent', '_chunkSize', '_chunkCount',
  22030. '_dimValueGetter', '_count', '_rawCount', '_nameDimIdx', '_idDimIdx'
  22031. ];
  22032. function transferProperties(a, b) {
  22033. each$1(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {
  22034. if (b.hasOwnProperty(propName)) {
  22035. a[propName] = b[propName];
  22036. }
  22037. });
  22038. a.__wrappedMethods = b.__wrappedMethods;
  22039. }
  22040. /**
  22041. * @constructor
  22042. * @alias module:echarts/data/List
  22043. *
  22044. * @param {Array.<string|Object>} dimensions
  22045. * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
  22046. * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
  22047. * Spetial fields: {
  22048. * ordinalMeta: <module:echarts/data/OrdinalMeta>
  22049. * }
  22050. * @param {module:echarts/model/Model} hostModel
  22051. */
  22052. var List = function (dimensions, hostModel) {
  22053. dimensions = dimensions || ['x', 'y'];
  22054. var dimensionInfos = {};
  22055. var dimensionNames = [];
  22056. for (var i = 0; i < dimensions.length; i++) {
  22057. var dimensionName;
  22058. var dimensionInfo = {};
  22059. if (typeof dimensions[i] === 'string') {
  22060. dimensionName = dimensions[i];
  22061. dimensionInfo = {
  22062. name: dimensionName,
  22063. coordDim: dimensionName,
  22064. coordDimIndex: 0,
  22065. stackable: false,
  22066. // Type can be 'float', 'int', 'number'
  22067. // Default is float64
  22068. type: 'float'
  22069. };
  22070. }
  22071. else {
  22072. dimensionInfo = dimensions[i];
  22073. dimensionName = dimensionInfo.name;
  22074. dimensionInfo.type = dimensionInfo.type || 'float';
  22075. if (!dimensionInfo.coordDim) {
  22076. dimensionInfo.coordDim = dimensionName;
  22077. dimensionInfo.coordDimIndex = 0;
  22078. }
  22079. }
  22080. dimensionInfo.otherDims = dimensionInfo.otherDims || {};
  22081. dimensionNames.push(dimensionName);
  22082. dimensionInfos[dimensionName] = dimensionInfo;
  22083. dimensionInfo.index = i;
  22084. }
  22085. /**
  22086. * @readOnly
  22087. * @type {Array.<string>}
  22088. */
  22089. this.dimensions = dimensionNames;
  22090. /**
  22091. * Infomation of each data dimension, like data type.
  22092. * @type {Object}
  22093. */
  22094. this._dimensionInfos = dimensionInfos;
  22095. /**
  22096. * @type {module:echarts/model/Model}
  22097. */
  22098. this.hostModel = hostModel;
  22099. /**
  22100. * @type {module:echarts/model/Model}
  22101. */
  22102. this.dataType;
  22103. /**
  22104. * Indices stores the indices of data subset after filtered.
  22105. * This data subset will be used in chart.
  22106. * @type {Array.<number>}
  22107. * @readOnly
  22108. */
  22109. this._indices = null;
  22110. this._count = 0;
  22111. this._rawCount = 0;
  22112. /**
  22113. * Data storage
  22114. * @type {Object.<key, Array.<TypedArray|Array>>}
  22115. * @private
  22116. */
  22117. this._storage = {};
  22118. /**
  22119. * @type {Array.<string>}
  22120. */
  22121. this._nameList = [];
  22122. /**
  22123. * @type {Array.<string>}
  22124. */
  22125. this._idList = [];
  22126. /**
  22127. * Models of data option is stored sparse for optimizing memory cost
  22128. * @type {Array.<module:echarts/model/Model>}
  22129. * @private
  22130. */
  22131. this._optionModels = [];
  22132. /**
  22133. * @param {module:echarts/data/List}
  22134. */
  22135. this.stackedOn = null;
  22136. /**
  22137. * Global visual properties after visual coding
  22138. * @type {Object}
  22139. * @private
  22140. */
  22141. this._visual = {};
  22142. /**
  22143. * Globel layout properties.
  22144. * @type {Object}
  22145. * @private
  22146. */
  22147. this._layout = {};
  22148. /**
  22149. * Item visual properties after visual coding
  22150. * @type {Array.<Object>}
  22151. * @private
  22152. */
  22153. this._itemVisuals = [];
  22154. /**
  22155. * Key: visual type, Value: boolean
  22156. * @type {Object}
  22157. * @readOnly
  22158. */
  22159. this.hasItemVisual = {};
  22160. /**
  22161. * Item layout properties after layout
  22162. * @type {Array.<Object>}
  22163. * @private
  22164. */
  22165. this._itemLayouts = [];
  22166. /**
  22167. * Graphic elemnents
  22168. * @type {Array.<module:zrender/Element>}
  22169. * @private
  22170. */
  22171. this._graphicEls = [];
  22172. /**
  22173. * Max size of each chunk.
  22174. * @type {number}
  22175. * @private
  22176. */
  22177. this._chunkSize = 1e5;
  22178. /**
  22179. * @type {number}
  22180. * @private
  22181. */
  22182. this._chunkCount = 0;
  22183. /**
  22184. * @type {Array.<Array|Object>}
  22185. * @private
  22186. */
  22187. this._rawData;
  22188. /**
  22189. * Raw extent will not be cloned, but only transfered.
  22190. * It will not be calculated util needed.
  22191. * key: dim,
  22192. * value: {end: number, extent: Array.<number>}
  22193. * @type {Object}
  22194. * @private
  22195. */
  22196. this._rawExtent = {};
  22197. /**
  22198. * @type {Object}
  22199. * @private
  22200. */
  22201. this._extent = {};
  22202. /**
  22203. * key: dim
  22204. * value: extent
  22205. * @type {Object}
  22206. * @private
  22207. */
  22208. this._approximateExtent = {};
  22209. /**
  22210. * Cache summary info for fast visit. See "dimensionHelper".
  22211. * @type {Object}
  22212. * @private
  22213. */
  22214. this._dimensionsSummary = summarizeDimensions(this);
  22215. };
  22216. var listProto = List.prototype;
  22217. listProto.type = 'list';
  22218. /**
  22219. * If each data item has it's own option
  22220. * @type {boolean}
  22221. */
  22222. listProto.hasItemOption = true;
  22223. /**
  22224. * Get dimension name
  22225. * @param {string|number} dim
  22226. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  22227. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  22228. * @return {string} Concrete dim name.
  22229. */
  22230. listProto.getDimension = function (dim) {
  22231. if (!isNaN(dim)) {
  22232. dim = this.dimensions[dim] || dim;
  22233. }
  22234. return dim;
  22235. };
  22236. /**
  22237. * Get type and stackable info of particular dimension
  22238. * @param {string|number} dim
  22239. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  22240. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  22241. */
  22242. listProto.getDimensionInfo = function (dim) {
  22243. // Do not clone, because there may be categories in dimInfo.
  22244. return this._dimensionInfos[this.getDimension(dim)];
  22245. };
  22246. /**
  22247. * @return {Array.<string>} concrete dimension name list on coord.
  22248. */
  22249. listProto.getDimensionsOnCoord = function () {
  22250. return this._dimensionsSummary.dataDimsOnCoord.slice();
  22251. };
  22252. /**
  22253. * @param {string} coordDim
  22254. * @param {number|Array} [idx=0] A coordDim may map to more than one data dim.
  22255. * If idx is `true`, return a array of all mapped dims.
  22256. * @return {string|Array.<string>} concrete data dim.
  22257. * If idx is number, and not found, return null/undefined.
  22258. * If idx is `true`, and not found, return empty array.
  22259. */
  22260. listProto.mapDimension = function (coordDim, idx) {
  22261. var dims = this._dimensionsSummary.encode[coordDim];
  22262. return idx === true
  22263. ? (dims && dims.slice() || [])
  22264. : dims
  22265. ? dims[idx || 0]
  22266. : null;
  22267. };
  22268. /**
  22269. * Initialize from data
  22270. * @param {Array.<Object|number|Array>} data source or data or data provider.
  22271. * @param {Array.<string>} [nameLIst] The name of a datum is used on data diff and
  22272. * defualt label/tooltip.
  22273. * A name can be specified in encode.itemName,
  22274. * or dataItem.name (only for series option data),
  22275. * or provided in nameList from outside.
  22276. * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
  22277. */
  22278. listProto.initData = function (data, nameList, dimValueGetter) {
  22279. var notProvider = Source.isInstance(data) || isArrayLike(data);
  22280. if (notProvider) {
  22281. data = new DefaultDataProvider(data, this.dimensions.length);
  22282. }
  22283. if (__DEV__) {
  22284. if (!notProvider && (typeof data.getItem != 'function' || typeof data.count != 'function')) {
  22285. throw new Error('Inavlid data provider.');
  22286. }
  22287. }
  22288. this._rawData = data;
  22289. // Clear
  22290. this._storage = {};
  22291. this._indices = null;
  22292. this._nameList = nameList || [];
  22293. this._idList = [];
  22294. this._nameRepeatCount = {};
  22295. if (!dimValueGetter) {
  22296. this.hasItemOption = false;
  22297. }
  22298. /**
  22299. * @readOnly
  22300. */
  22301. this.defaultDimValueGetter = defaultDimValueGetters[
  22302. this._rawData.getSource().sourceFormat
  22303. ];
  22304. // Default dim value getter
  22305. this._dimValueGetter = dimValueGetter = dimValueGetter
  22306. || this.defaultDimValueGetter;
  22307. // Reset raw extent.
  22308. this._rawExtent = {};
  22309. this._initDataFromProvider(0, data.count());
  22310. // If data has no item option.
  22311. if (data.pure) {
  22312. this.hasItemOption = false;
  22313. }
  22314. };
  22315. listProto.getProvider = function () {
  22316. return this._rawData;
  22317. };
  22318. listProto.appendData = function (data) {
  22319. if (__DEV__) {
  22320. assert$1(!this._indices, 'appendData can only be called on raw data.');
  22321. }
  22322. var rawData = this._rawData;
  22323. var start = this.count();
  22324. rawData.appendData(data);
  22325. var end = rawData.count();
  22326. if (!rawData.persistent) {
  22327. end += start;
  22328. }
  22329. this._initDataFromProvider(start, end);
  22330. };
  22331. listProto._initDataFromProvider = function (start, end) {
  22332. // Optimize.
  22333. if (start >= end) {
  22334. return;
  22335. }
  22336. var chunkSize = this._chunkSize;
  22337. var rawData = this._rawData;
  22338. var storage = this._storage;
  22339. var dimensions = this.dimensions;
  22340. var dimensionInfoMap = this._dimensionInfos;
  22341. var nameList = this._nameList;
  22342. var idList = this._idList;
  22343. var rawExtent = this._rawExtent;
  22344. var nameRepeatCount = this._nameRepeatCount = {};
  22345. var nameDimIdx;
  22346. var chunkCount = this._chunkCount;
  22347. var lastChunkIndex = chunkCount - 1;
  22348. for (var i = 0; i < dimensions.length; i++) {
  22349. var dim = dimensions[i];
  22350. if (!rawExtent[dim]) {
  22351. rawExtent[dim] = [Infinity, -Infinity];
  22352. }
  22353. var dimInfo = dimensionInfoMap[dim];
  22354. if (dimInfo.otherDims.itemName === 0) {
  22355. nameDimIdx = this._nameDimIdx = i;
  22356. }
  22357. if (dimInfo.otherDims.itemId === 0) {
  22358. this._idDimIdx = i;
  22359. }
  22360. var DataCtor = dataCtors[dimInfo.type];
  22361. if (!storage[dim]) {
  22362. storage[dim] = [];
  22363. }
  22364. var resizeChunkArray = storage[dim][lastChunkIndex];
  22365. if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
  22366. var newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
  22367. // The cost of the copy is probably inconsiderable
  22368. // within the initial chunkSize.
  22369. for (var j = 0; j < resizeChunkArray.length; j++) {
  22370. newStore[j] = resizeChunkArray[j];
  22371. }
  22372. storage[dim][lastChunkIndex] = newStore;
  22373. }
  22374. // Create new chunks.
  22375. for (var k = chunkCount * chunkSize; k < end; k += chunkSize) {
  22376. storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
  22377. }
  22378. this._chunkCount = storage[dim].length;
  22379. }
  22380. for (var idx = start; idx < end; idx++) {
  22381. // NOTICE: Try not to write things into dataItem
  22382. var dataItem = rawData.getItem(idx);
  22383. // Each data item is value
  22384. // [1, 2]
  22385. // 2
  22386. // Bar chart, line chart which uses category axis
  22387. // only gives the 'y' value. 'x' value is the indices of category
  22388. // Use a tempValue to normalize the value to be a (x, y) value
  22389. var chunkIndex = Math.floor(idx / chunkSize);
  22390. var chunkOffset = idx % chunkSize;
  22391. // Store the data by dimensions
  22392. for (var k = 0; k < dimensions.length; k++) {
  22393. var dim = dimensions[k];
  22394. var dimStorage = storage[dim][chunkIndex];
  22395. // PENDING NULL is empty or zero
  22396. var val = this._dimValueGetter(dataItem, dim, idx, k);
  22397. dimStorage[chunkOffset] = val;
  22398. if (val < rawExtent[dim][0]) {
  22399. rawExtent[dim][0] = val;
  22400. }
  22401. if (val > rawExtent[dim][1]) {
  22402. rawExtent[dim][1] = val;
  22403. }
  22404. }
  22405. // ??? FIXME not check by pure but sourceFormat?
  22406. // TODO refactor these logic.
  22407. if (!rawData.pure) {
  22408. var name = nameList[idx];
  22409. if (dataItem && !name) {
  22410. if (nameDimIdx != null) {
  22411. name = this._getNameFromStore(idx);
  22412. }
  22413. else if (dataItem.name != null) {
  22414. // There is no other place to persistent dataItem.name,
  22415. // so save it to nameList.
  22416. nameList[idx] = name = dataItem.name;
  22417. }
  22418. }
  22419. // Try using the id in option
  22420. // id or name is used on dynamical data, mapping old and new items.
  22421. var id = dataItem == null ? null : dataItem.id;
  22422. if (id == null && name != null) {
  22423. // Use name as id and add counter to avoid same name
  22424. nameRepeatCount[name] = nameRepeatCount[name] || 0;
  22425. id = name;
  22426. if (nameRepeatCount[name] > 0) {
  22427. id += '__ec__' + nameRepeatCount[name];
  22428. }
  22429. nameRepeatCount[name]++;
  22430. }
  22431. id != null && (idList[idx] = id);
  22432. }
  22433. }
  22434. if (!rawData.persistent && rawData.clean) {
  22435. // Clean unused data if data source is typed array.
  22436. rawData.clean();
  22437. }
  22438. this._rawCount = this._count = end;
  22439. // Reset data extent
  22440. this._extent = {};
  22441. };
  22442. // TODO refactor
  22443. listProto._getNameFromStore = function (rawIndex) {
  22444. var nameDimIdx = this._nameDimIdx;
  22445. if (nameDimIdx != null) {
  22446. var chunkSize = this._chunkSize;
  22447. var chunkIndex = Math.floor(rawIndex / chunkSize);
  22448. var chunkOffset = rawIndex % chunkSize;
  22449. var dim = this.dimensions[nameDimIdx];
  22450. var ordinalMeta = this._dimensionInfos[dim].ordinalMeta;
  22451. if (ordinalMeta) {
  22452. return ordinalMeta.categories[rawIndex];
  22453. }
  22454. else {
  22455. var chunk = this._storage[dim][chunkIndex];
  22456. return chunk && chunk[chunkOffset];
  22457. }
  22458. }
  22459. };
  22460. // TODO refactor
  22461. listProto._getIdFromStore = function (rawIndex) {
  22462. var idDimIdx = this._idDimIdx;
  22463. if (idDimIdx != null) {
  22464. var chunkSize = this._chunkSize;
  22465. var chunkIndex = Math.floor(rawIndex / chunkSize);
  22466. var chunkOffset = rawIndex % chunkSize;
  22467. var dim = this.dimensions[idDimIdx];
  22468. var ordinalMeta = this._dimensionInfos[dim].ordinalMeta;
  22469. if (ordinalMeta) {
  22470. return ordinalMeta.categories[rawIndex];
  22471. }
  22472. else {
  22473. var chunk = this._storage[dim][chunkIndex];
  22474. return chunk && chunk[chunkOffset];
  22475. }
  22476. }
  22477. };
  22478. /**
  22479. * @return {number}
  22480. */
  22481. listProto.count = function () {
  22482. return this._count;
  22483. };
  22484. listProto.getIndices = function () {
  22485. if (this._indices) {
  22486. var Ctor = this._indices.constructor;
  22487. return new Ctor(this._indices.buffer, 0, this._count);
  22488. }
  22489. var Ctor = getIndicesCtor(this);
  22490. var arr = new Ctor(this.count());
  22491. for (var i = 0; i < arr.length; i++) {
  22492. arr[i] = i;
  22493. }
  22494. return arr;
  22495. };
  22496. /**
  22497. * Get value. Return NaN if idx is out of range.
  22498. * @param {string} dim Dim must be concrete name.
  22499. * @param {number} idx
  22500. * @param {boolean} stack
  22501. * @return {number}
  22502. */
  22503. listProto.get = function (dim, idx, stack) {
  22504. if (!(idx >= 0 && idx < this._count)) {
  22505. return NaN;
  22506. }
  22507. var storage = this._storage;
  22508. if (!storage[dim]) {
  22509. // TODO Warn ?
  22510. return NaN;
  22511. }
  22512. idx = this.getRawIndex(idx);
  22513. var chunkIndex = Math.floor(idx / this._chunkSize);
  22514. var chunkOffset = idx % this._chunkSize;
  22515. var chunkStore = storage[dim][chunkIndex];
  22516. var value = chunkStore[chunkOffset];
  22517. // FIXME ordinal data type is not stackable
  22518. if (stack) {
  22519. var dimensionInfo = this._dimensionInfos[dim];
  22520. if (dimensionInfo && dimensionInfo.stackable) {
  22521. var stackedOn = this.stackedOn;
  22522. while (stackedOn) {
  22523. // Get no stacked data of stacked on
  22524. var stackedValue = stackedOn.get(dim, idx);
  22525. // Considering positive stack, negative stack and empty data
  22526. if ((value >= 0 && stackedValue > 0) // Positive stack
  22527. || (value <= 0 && stackedValue < 0) // Negative stack
  22528. ) {
  22529. value += stackedValue;
  22530. }
  22531. stackedOn = stackedOn.stackedOn;
  22532. }
  22533. }
  22534. }
  22535. return value;
  22536. };
  22537. // FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
  22538. // Hack a much simpler _getFast
  22539. listProto._getFast = function (dim, rawIdx) {
  22540. var chunkIndex = Math.floor(rawIdx / this._chunkSize);
  22541. var chunkOffset = rawIdx % this._chunkSize;
  22542. var chunkStore = this._storage[dim][chunkIndex];
  22543. return chunkStore[chunkOffset];
  22544. };
  22545. /**
  22546. * Get value for multi dimensions.
  22547. * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
  22548. * @param {number} idx
  22549. * @param {boolean} stack
  22550. * @return {number}
  22551. */
  22552. listProto.getValues = function (dimensions, idx, stack) {
  22553. var values = [];
  22554. if (!isArray(dimensions)) {
  22555. stack = idx;
  22556. idx = dimensions;
  22557. dimensions = this.dimensions;
  22558. }
  22559. for (var i = 0, len = dimensions.length; i < len; i++) {
  22560. values.push(this.get(dimensions[i], idx, stack));
  22561. }
  22562. return values;
  22563. };
  22564. /**
  22565. * If value is NaN. Inlcuding '-'
  22566. * Only check the coord dimensions.
  22567. * @param {string} dim
  22568. * @param {number} idx
  22569. * @return {number}
  22570. */
  22571. listProto.hasValue = function (idx) {
  22572. var dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
  22573. var dimensionInfos = this._dimensionInfos;
  22574. for (var i = 0, len = dataDimsOnCoord.length; i < len; i++) {
  22575. if (
  22576. // Ordinal type can be string or number
  22577. dimensionInfos[dataDimsOnCoord[i]].type !== 'ordinal'
  22578. // FIXME check ordinal when using index?
  22579. && isNaN(this.get(dataDimsOnCoord[i], idx))
  22580. ) {
  22581. return false;
  22582. }
  22583. }
  22584. return true;
  22585. };
  22586. /**
  22587. * Get extent of data in one dimension
  22588. * @param {string} dim
  22589. * @param {boolean} stack
  22590. */
  22591. listProto.getDataExtent = function (dim, stack) {
  22592. // Make sure use concrete dim as cache name.
  22593. dim = this.getDimension(dim);
  22594. var dimData = this._storage[dim];
  22595. var initialExtent = [Infinity, -Infinity];
  22596. stack = (stack || false) && this.isStacked(dim);
  22597. if (!dimData) {
  22598. return initialExtent;
  22599. }
  22600. // Make more strict checkings to ensure hitting cache.
  22601. var currEnd = this.count();
  22602. var cacheName = [dim, !!stack].join('_');
  22603. // Consider the most cases when using data zoom, `getDataExtent`
  22604. // happened before filtering. We cache raw extent, which is not
  22605. // necessary to be cleared and recalculated when restore data.
  22606. var useRaw = !this._indices && !stack;
  22607. var dimExtent;
  22608. if (useRaw) {
  22609. return this._rawExtent[dim].slice();
  22610. }
  22611. dimExtent = this._extent[cacheName];
  22612. if (dimExtent) {
  22613. return dimExtent.slice();
  22614. }
  22615. dimExtent = initialExtent;
  22616. var min = dimExtent[0];
  22617. var max = dimExtent[1];
  22618. for (var i = 0; i < currEnd; i++) {
  22619. var value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i));
  22620. value < min && (min = value);
  22621. value > max && (max = value);
  22622. }
  22623. dimExtent = [min, max];
  22624. this._extent[cacheName] = dimExtent;
  22625. return dimExtent;
  22626. };
  22627. /**
  22628. * Optimize for the scenario that data is filtered by a given extent.
  22629. * Consider that if data amount is more than hundreds of thousand,
  22630. * extent calculation will cost more than 10ms and the cache will
  22631. * be erased because of the filtering.
  22632. */
  22633. listProto.getApproximateExtent = function (dim, stack) {
  22634. dim = this.getDimension(dim);
  22635. return this._approximateExtent[dim] || this.getDataExtent(dim, stack);
  22636. };
  22637. listProto.setApproximateExtent = function (extent, dim, stack) {
  22638. dim = this.getDimension(dim);
  22639. this._approximateExtent[dim] = extent.slice();
  22640. };
  22641. listProto.isStacked = function (concreteDim) {
  22642. var dimensionInfo = this._dimensionInfos[concreteDim];
  22643. return dimensionInfo && dimensionInfo.stackable && this.stackedOn;
  22644. };
  22645. /**
  22646. * Get sum of data in one dimension
  22647. * @param {string} dim
  22648. * @param {boolean} stack
  22649. */
  22650. listProto.getSum = function (dim, stack) {
  22651. var dimData = this._storage[dim];
  22652. var sum = 0;
  22653. if (dimData) {
  22654. for (var i = 0, len = this.count(); i < len; i++) {
  22655. var value = this.get(dim, i, stack);
  22656. if (!isNaN(value)) {
  22657. sum += value;
  22658. }
  22659. }
  22660. }
  22661. return sum;
  22662. };
  22663. /**
  22664. * Retreive the index with given value
  22665. * @param {number} idx
  22666. * @param {number} value
  22667. * @return {number}
  22668. */
  22669. // FIXME Precision of float value
  22670. listProto.indexOf = function (dim, value) {
  22671. var storage = this._storage;
  22672. var dimData = storage[dim];
  22673. var chunkSize = this._chunkSize;
  22674. if (dimData) {
  22675. for (var i = 0, len = this.count(); i < len; i++) {
  22676. var chunkIndex = Math.floor(i / chunkSize);
  22677. var chunkOffset = i % chunkSize;
  22678. if (dimData[chunkIndex][chunkOffset] === value) {
  22679. return i;
  22680. }
  22681. }
  22682. }
  22683. return -1;
  22684. };
  22685. /**
  22686. * Retreive the index with given name
  22687. * @param {number} idx
  22688. * @param {number} name
  22689. * @return {number}
  22690. */
  22691. listProto.indexOfName = function (name) {
  22692. for (var i = 0, len = this.count(); i < len; i++) {
  22693. if (this.getName(i) === name) {
  22694. return i;
  22695. }
  22696. }
  22697. return -1;
  22698. };
  22699. /**
  22700. * Retreive the index with given raw data index
  22701. * @param {number} idx
  22702. * @param {number} name
  22703. * @return {number}
  22704. */
  22705. listProto.indexOfRawIndex = function (rawIndex) {
  22706. if (!this._indices) {
  22707. return rawIndex;
  22708. }
  22709. if (rawIndex >= this._rawCount || rawIndex < 0) {
  22710. return -1;
  22711. }
  22712. // Indices are ascending
  22713. var indices = this._indices;
  22714. // If rawIndex === dataIndex
  22715. var rawDataIndex = indices[rawIndex];
  22716. if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
  22717. return rawIndex;
  22718. }
  22719. var left = 0;
  22720. var right = this._count - 1;
  22721. while (left <= right) {
  22722. var mid = (left + right) / 2 | 0;
  22723. if (indices[mid] < rawIndex) {
  22724. left = mid + 1;
  22725. }
  22726. else if (indices[mid] > rawIndex) {
  22727. right = mid - 1;
  22728. }
  22729. else {
  22730. return mid;
  22731. }
  22732. }
  22733. return -1;
  22734. };
  22735. /**
  22736. * Retreive the index of nearest value
  22737. * @param {string} dim
  22738. * @param {number} value
  22739. * @param {boolean} stack If given value is after stacked
  22740. * @param {number} [maxDistance=Infinity]
  22741. * @return {Array.<number>} Considere multiple points has the same value.
  22742. */
  22743. listProto.indicesOfNearest = function (dim, value, stack, maxDistance) {
  22744. var storage = this._storage;
  22745. var dimData = storage[dim];
  22746. var nearestIndices = [];
  22747. if (!dimData) {
  22748. return nearestIndices;
  22749. }
  22750. if (maxDistance == null) {
  22751. maxDistance = Infinity;
  22752. }
  22753. var minDist = Number.MAX_VALUE;
  22754. var minDiff = -1;
  22755. for (var i = 0, len = this.count(); i < len; i++) {
  22756. var diff = value - this.get(dim, i, stack);
  22757. var dist = Math.abs(diff);
  22758. if (diff <= maxDistance && dist <= minDist) {
  22759. // For the case of two data are same on xAxis, which has sequence data.
  22760. // Show the nearest index
  22761. // https://github.com/ecomfe/echarts/issues/2869
  22762. if (dist < minDist || (diff >= 0 && minDiff < 0)) {
  22763. minDist = dist;
  22764. minDiff = diff;
  22765. nearestIndices.length = 0;
  22766. }
  22767. nearestIndices.push(i);
  22768. }
  22769. }
  22770. return nearestIndices;
  22771. };
  22772. /**
  22773. * Get raw data index
  22774. * @param {number} idx
  22775. * @return {number}
  22776. */
  22777. listProto.getRawIndex = getRawIndexWithoutIndices;
  22778. function getRawIndexWithoutIndices(idx) {
  22779. return idx;
  22780. }
  22781. function getRawIndexWithIndices(idx) {
  22782. if (idx < this._count && idx >= 0) {
  22783. return this._indices[idx];
  22784. }
  22785. return -1;
  22786. }
  22787. /**
  22788. * Get raw data item
  22789. * @param {number} idx
  22790. * @return {number}
  22791. */
  22792. listProto.getRawDataItem = function (idx) {
  22793. if (!this._rawData.persistent) {
  22794. var val = [];
  22795. for (var i = 0; i < this.dimensions.length; i++) {
  22796. var dim = this.dimensions[i];
  22797. val.push(this.get(dim, idx));
  22798. }
  22799. return val;
  22800. }
  22801. else {
  22802. return this._rawData.getItem(this.getRawIndex(idx));
  22803. }
  22804. };
  22805. /**
  22806. * @param {number} idx
  22807. * @param {boolean} [notDefaultIdx=false]
  22808. * @return {string}
  22809. */
  22810. listProto.getName = function (idx) {
  22811. var rawIndex = this.getRawIndex(idx);
  22812. return this._nameList[rawIndex]
  22813. || this._getNameFromStore(rawIndex)
  22814. || '';
  22815. };
  22816. /**
  22817. * @param {number} idx
  22818. * @param {boolean} [notDefaultIdx=false]
  22819. * @return {string}
  22820. */
  22821. listProto.getId = function (idx) {
  22822. return getId(this, this.getRawIndex(idx));
  22823. };
  22824. function getId(list, rawIndex) {
  22825. var id = list._idList[rawIndex];
  22826. if (id == null) {
  22827. id = list._getIdFromStore(rawIndex);
  22828. }
  22829. if (id == null) {
  22830. // FIXME Check the usage in graph, should not use prefix.
  22831. id = ID_PREFIX + rawIndex;
  22832. }
  22833. return id;
  22834. }
  22835. function normalizeDimensions(dimensions) {
  22836. if (!isArray(dimensions)) {
  22837. dimensions = [dimensions];
  22838. }
  22839. return dimensions;
  22840. }
  22841. function validateDimensions(list, dims) {
  22842. for (var i = 0; i < dims.length; i++) {
  22843. // stroage may be empty when no data, so use
  22844. // dimensionInfos to check.
  22845. if (!list._dimensionInfos[dims[i]]) {
  22846. console.error('Unkown dimension ' + dims[i]);
  22847. }
  22848. }
  22849. }
  22850. /**
  22851. * Data iteration
  22852. * @param {string|Array.<string>}
  22853. * @param {Function} cb
  22854. * @param {boolean} [stack=false]
  22855. * @param {*} [context=this]
  22856. *
  22857. * @example
  22858. * list.each('x', function (x, idx) {});
  22859. * list.each(['x', 'y'], function (x, y, idx) {});
  22860. * list.each(function (idx) {})
  22861. */
  22862. listProto.each = function (dims, cb, stack, context) {
  22863. 'use strict';
  22864. if (!this._count) {
  22865. return;
  22866. }
  22867. if (typeof dims === 'function') {
  22868. context = stack;
  22869. stack = cb;
  22870. cb = dims;
  22871. dims = [];
  22872. }
  22873. dims = map(normalizeDimensions(dims), this.getDimension, this);
  22874. if (__DEV__) {
  22875. validateDimensions(this, dims);
  22876. }
  22877. var dimSize = dims.length;
  22878. context = context || this;
  22879. for (var i = 0; i < this.count(); i++) {
  22880. // Simple optimization
  22881. switch (dimSize) {
  22882. case 0:
  22883. cb.call(context, i);
  22884. break;
  22885. case 1:
  22886. cb.call(context, this.get(dims[0], i, stack), i);
  22887. break;
  22888. case 2:
  22889. cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i);
  22890. break;
  22891. default:
  22892. var k = 0;
  22893. var value = [];
  22894. for (; k < dimSize; k++) {
  22895. value[k] = this.get(dims[k], i, stack);
  22896. }
  22897. // Index
  22898. value[k] = i;
  22899. cb.apply(context, value);
  22900. }
  22901. }
  22902. };
  22903. /**
  22904. * Data filter
  22905. * @param {string|Array.<string>}
  22906. * @param {Function} cb
  22907. * @param {boolean} [stack=false]
  22908. * @param {*} [context=this]
  22909. */
  22910. listProto.filterSelf = function (dimensions, cb, stack, context) {
  22911. 'use strict';
  22912. if (!this._count) {
  22913. return;
  22914. }
  22915. if (typeof dimensions === 'function') {
  22916. context = stack;
  22917. stack = cb;
  22918. cb = dimensions;
  22919. dimensions = [];
  22920. }
  22921. stack = stack || false;
  22922. context = context || this;
  22923. dimensions = map(
  22924. normalizeDimensions(dimensions), this.getDimension, this
  22925. );
  22926. if (__DEV__) {
  22927. validateDimensions(this, dimensions);
  22928. }
  22929. var count = this.count();
  22930. var Ctor = getIndicesCtor(this);
  22931. var newIndices = new Ctor(count);
  22932. var value = [];
  22933. var dimSize = dimensions.length;
  22934. var offset = 0;
  22935. var dim0 = dimensions[0];
  22936. for (var i = 0; i < count; i++) {
  22937. var keep;
  22938. var rawIdx = this.getRawIndex(i);
  22939. // Simple optimization
  22940. if (dimSize === 0) {
  22941. keep = cb.call(context, i);
  22942. }
  22943. else if (dimSize === 1) {
  22944. var val = stack ? this.get(dim0, i, true) : this._getFast(dim0, rawIdx);
  22945. keep = cb.call(context, val, i);
  22946. }
  22947. else {
  22948. for (var k = 0; k < dimSize; k++) {
  22949. value[k] = stack ? this.get(dimensions[k], i, true) : this._getFast(dim0, rawIdx);
  22950. }
  22951. value[k] = i;
  22952. keep = cb.apply(context, value);
  22953. }
  22954. if (keep) {
  22955. newIndices[offset++] = rawIdx;
  22956. }
  22957. }
  22958. // Set indices after filtered.
  22959. if (offset < count) {
  22960. this._indices = newIndices;
  22961. }
  22962. this._count = offset;
  22963. // Reset data extent
  22964. this._extent = {};
  22965. this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  22966. return this;
  22967. };
  22968. /**
  22969. * Select data in range. (For optimization of filter)
  22970. * (Manually inline code, support 5 million data filtering in data zoom.)
  22971. */
  22972. listProto.selectRange = function (range, stack) {
  22973. 'use strict';
  22974. if (!this._count) {
  22975. return;
  22976. }
  22977. stack = stack || false;
  22978. var dimensions = [];
  22979. for (var dim in range) {
  22980. if (range.hasOwnProperty(dim)) {
  22981. dimensions.push(dim);
  22982. }
  22983. }
  22984. if (__DEV__) {
  22985. validateDimensions(this, dimensions);
  22986. }
  22987. var dimSize = dimensions.length;
  22988. if (!dimSize) {
  22989. return;
  22990. }
  22991. var originalCount = this.count();
  22992. var Ctor = getIndicesCtor(this);
  22993. var newIndices = new Ctor(originalCount);
  22994. var offset = 0;
  22995. var dim0 = dimensions[0];
  22996. var min = range[dim0][0];
  22997. var max = range[dim0][1];
  22998. var quickFinished = false;
  22999. if (!this._indices && !stack) {
  23000. // Extreme optimization for common case. About 2x faster in chrome.
  23001. var idx = 0;
  23002. if (dimSize === 1) {
  23003. var dimStorage = this._storage[dimensions[0]];
  23004. for (var k = 0; k < this._chunkCount; k++) {
  23005. var chunkStorage = dimStorage[k];
  23006. var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
  23007. for (var i = 0; i < len; i++) {
  23008. var val = chunkStorage[i];
  23009. if (val >= min && val <= max) {
  23010. newIndices[offset++] = idx;
  23011. }
  23012. idx++;
  23013. }
  23014. }
  23015. quickFinished = true;
  23016. }
  23017. else if (dimSize === 2) {
  23018. var dimStorage = this._storage[dim0];
  23019. var dimStorage2 = this._storage[dimensions[1]];
  23020. var min2 = range[dimensions[1]][0];
  23021. var max2 = range[dimensions[1]][1];
  23022. for (var k = 0; k < this._chunkCount; k++) {
  23023. var chunkStorage = dimStorage[k];
  23024. var chunkStorage2= dimStorage2[k];
  23025. var len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
  23026. for (var i = 0; i < len; i++) {
  23027. var val = chunkStorage[i];
  23028. var val2 = chunkStorage2[i];
  23029. if (val >= min && val <= max && val2 >= min2 && val2 <= max2) {
  23030. newIndices[offset++] = idx;
  23031. }
  23032. idx++;
  23033. }
  23034. }
  23035. quickFinished = true;
  23036. }
  23037. }
  23038. if (!quickFinished) {
  23039. if (dimSize === 1) {
  23040. stack = stack || this.isStacked(dim0);
  23041. for (var i = 0; i < originalCount; i++) {
  23042. var rawIndex = this.getRawIndex(i);
  23043. var val = stack ? this.get(dim0, i, true) : this._getFast(dim0, rawIndex);
  23044. if (val >= min && val <= max) {
  23045. newIndices[offset++] = rawIndex;
  23046. }
  23047. }
  23048. }
  23049. else {
  23050. for (var i = 0; i < originalCount; i++) {
  23051. var keep = true;
  23052. var rawIndex = this.getRawIndex(i);
  23053. for (var k = 0; k < dimSize; k++) {
  23054. var dimk = dimensions[k];
  23055. var val = stack ? this.get(dimk, i, true) : this._getFast(dim, rawIndex);
  23056. if (val < range[dimk][0] || val > range[dimk][1]) {
  23057. keep = false;
  23058. }
  23059. }
  23060. if (keep) {
  23061. newIndices[offset++] = this.getRawIndex(i);
  23062. }
  23063. }
  23064. }
  23065. }
  23066. // Set indices after filtered.
  23067. if (offset < originalCount) {
  23068. this._indices = newIndices;
  23069. }
  23070. this._count = offset;
  23071. // Reset data extent
  23072. this._extent = {};
  23073. this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  23074. return this;
  23075. };
  23076. /**
  23077. * Data mapping to a plain array
  23078. * @param {string|Array.<string>} [dimensions]
  23079. * @param {Function} cb
  23080. * @param {boolean} [stack=false]
  23081. * @param {*} [context=this]
  23082. * @return {Array}
  23083. */
  23084. listProto.mapArray = function (dimensions, cb, stack, context) {
  23085. 'use strict';
  23086. if (typeof dimensions === 'function') {
  23087. context = stack;
  23088. stack = cb;
  23089. cb = dimensions;
  23090. dimensions = [];
  23091. }
  23092. var result = [];
  23093. this.each(dimensions, function () {
  23094. result.push(cb && cb.apply(this, arguments));
  23095. }, stack, context);
  23096. return result;
  23097. };
  23098. // Data in excludeDimensions is copied, otherwise transfered.
  23099. function cloneListForMapAndSample(original, excludeDimensions) {
  23100. var allDimensions = original.dimensions;
  23101. var list = new List(
  23102. map(allDimensions, original.getDimensionInfo, original),
  23103. original.hostModel
  23104. );
  23105. // FIXME If needs stackedOn, value may already been stacked
  23106. transferProperties(list, original);
  23107. var storage = list._storage = {};
  23108. var originalStorage = original._storage;
  23109. // Init storage
  23110. for (var i = 0; i < allDimensions.length; i++) {
  23111. var dim = allDimensions[i];
  23112. if (originalStorage[dim]) {
  23113. storage[dim] = indexOf(excludeDimensions, dim) >= 0
  23114. ? cloneDimStore(originalStorage[dim])
  23115. // Direct reference for other dimensions
  23116. : originalStorage[dim];
  23117. }
  23118. }
  23119. return list;
  23120. }
  23121. function cloneDimStore(originalDimStore) {
  23122. var newDimStore = new Array(originalDimStore.length);
  23123. for (var j = 0; j < originalDimStore.length; j++) {
  23124. newDimStore[j] = cloneChunk(originalDimStore[j]);
  23125. }
  23126. return newDimStore;
  23127. }
  23128. /**
  23129. * Data mapping to a new List with given dimensions
  23130. * @param {string|Array.<string>} dimensions
  23131. * @param {Function} cb
  23132. * @param {boolean} [stack=false]
  23133. * @param {*} [context=this]
  23134. * @return {Array}
  23135. */
  23136. listProto.map = function (dimensions, cb, stack, context) {
  23137. 'use strict';
  23138. dimensions = map(
  23139. normalizeDimensions(dimensions), this.getDimension, this
  23140. );
  23141. if (__DEV__) {
  23142. validateDimensions(this, dimensions);
  23143. }
  23144. var list = cloneListForMapAndSample(this, dimensions);
  23145. // Following properties are all immutable.
  23146. // So we can reference to the same value
  23147. list._indices = this._indices;
  23148. list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  23149. var storage = list._storage;
  23150. var tmpRetValue = [];
  23151. var chunkSize = this._chunkSize;
  23152. var dimSize = dimensions.length;
  23153. var dataCount = this.count();
  23154. var values = [];
  23155. for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) {
  23156. for (var dimIndex = 0; dimIndex < dimSize; dimIndex++) {
  23157. values[dimIndex] = this.get(dimensions[dimIndex], dataIndex, stack);
  23158. }
  23159. values[dimSize] = dataIndex;
  23160. var retValue = cb && cb.apply(context, values);
  23161. if (retValue != null) {
  23162. // a number or string (in oridinal dimension)?
  23163. if (typeof retValue !== 'object') {
  23164. tmpRetValue[0] = retValue;
  23165. retValue = tmpRetValue;
  23166. }
  23167. var rawIndex = this.getRawIndex(dataIndex);
  23168. var chunkIndex = Math.floor(rawIndex / chunkSize);
  23169. var chunkOffset = rawIndex % chunkSize;
  23170. for (var i = 0; i < retValue.length; i++) {
  23171. var dim = dimensions[i];
  23172. var dimStore = storage[dim];
  23173. if (dimStore) {
  23174. dimStore[chunkIndex][chunkOffset] = retValue[i];
  23175. }
  23176. }
  23177. }
  23178. }
  23179. return list;
  23180. };
  23181. /**
  23182. * Large data down sampling on given dimension
  23183. * @param {string} dimension
  23184. * @param {number} rate
  23185. * @param {Function} sampleValue
  23186. * @param {Function} sampleIndex Sample index for name and id
  23187. */
  23188. listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  23189. var list = cloneListForMapAndSample(this, [dimension]);
  23190. var targetStorage = list._storage;
  23191. var frameValues = [];
  23192. var frameSize = Math.floor(1 / rate);
  23193. var dimStore = targetStorage[dimension];
  23194. var len = this.count();
  23195. var chunkSize = this._chunkSize;
  23196. var newIndices = new (getIndicesCtor(this))(len);
  23197. var offset = 0;
  23198. for (var i = 0; i < len; i += frameSize) {
  23199. // Last frame
  23200. if (frameSize > len - i) {
  23201. frameSize = len - i;
  23202. frameValues.length = frameSize;
  23203. }
  23204. for (var k = 0; k < frameSize; k++) {
  23205. var dataIdx = this.getRawIndex(i + k);
  23206. var originalChunkIndex = Math.floor(dataIdx / chunkSize);
  23207. var originalChunkOffset = dataIdx % chunkSize;
  23208. frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
  23209. }
  23210. var value = sampleValue(frameValues);
  23211. var sampleFrameIdx = this.getRawIndex(
  23212. Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
  23213. );
  23214. var sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
  23215. var sampleChunkOffset = sampleFrameIdx % chunkSize;
  23216. // Only write value on the filtered data
  23217. dimStore[sampleChunkIndex][sampleChunkOffset] = value;
  23218. newIndices[offset++] = sampleFrameIdx;
  23219. }
  23220. list._count = offset;
  23221. list._indices = newIndices;
  23222. list.getRawIndex = getRawIndexWithIndices;
  23223. return list;
  23224. };
  23225. /**
  23226. * Get model of one data item.
  23227. *
  23228. * @param {number} idx
  23229. */
  23230. // FIXME Model proxy ?
  23231. listProto.getItemModel = function (idx) {
  23232. var hostModel = this.hostModel;
  23233. return new Model(this.getRawDataItem(idx), hostModel, hostModel && hostModel.ecModel);
  23234. };
  23235. /**
  23236. * Create a data differ
  23237. * @param {module:echarts/data/List} otherList
  23238. * @return {module:echarts/data/DataDiffer}
  23239. */
  23240. listProto.diff = function (otherList) {
  23241. var thisList = this;
  23242. return new DataDiffer(
  23243. otherList ? otherList.getIndices() : [],
  23244. this.getIndices(),
  23245. function (idx) {
  23246. return getId(otherList, idx);
  23247. },
  23248. function (idx) {
  23249. return getId(thisList, idx);
  23250. }
  23251. );
  23252. };
  23253. /**
  23254. * Get visual property.
  23255. * @param {string} key
  23256. */
  23257. listProto.getVisual = function (key) {
  23258. var visual = this._visual;
  23259. return visual && visual[key];
  23260. };
  23261. /**
  23262. * Set visual property
  23263. * @param {string|Object} key
  23264. * @param {*} [value]
  23265. *
  23266. * @example
  23267. * setVisual('color', color);
  23268. * setVisual({
  23269. * 'color': color
  23270. * });
  23271. */
  23272. listProto.setVisual = function (key, val) {
  23273. if (isObject$4(key)) {
  23274. for (var name in key) {
  23275. if (key.hasOwnProperty(name)) {
  23276. this.setVisual(name, key[name]);
  23277. }
  23278. }
  23279. return;
  23280. }
  23281. this._visual = this._visual || {};
  23282. this._visual[key] = val;
  23283. };
  23284. /**
  23285. * Set layout property.
  23286. * @param {string|Object} key
  23287. * @param {*} [val]
  23288. */
  23289. listProto.setLayout = function (key, val) {
  23290. if (isObject$4(key)) {
  23291. for (var name in key) {
  23292. if (key.hasOwnProperty(name)) {
  23293. this.setLayout(name, key[name]);
  23294. }
  23295. }
  23296. return;
  23297. }
  23298. this._layout[key] = val;
  23299. };
  23300. /**
  23301. * Get layout property.
  23302. * @param {string} key.
  23303. * @return {*}
  23304. */
  23305. listProto.getLayout = function (key) {
  23306. return this._layout[key];
  23307. };
  23308. /**
  23309. * Get layout of single data item
  23310. * @param {number} idx
  23311. */
  23312. listProto.getItemLayout = function (idx) {
  23313. return this._itemLayouts[idx];
  23314. };
  23315. /**
  23316. * Set layout of single data item
  23317. * @param {number} idx
  23318. * @param {Object} layout
  23319. * @param {boolean=} [merge=false]
  23320. */
  23321. listProto.setItemLayout = function (idx, layout, merge$$1) {
  23322. this._itemLayouts[idx] = merge$$1
  23323. ? extend(this._itemLayouts[idx] || {}, layout)
  23324. : layout;
  23325. };
  23326. /**
  23327. * Clear all layout of single data item
  23328. */
  23329. listProto.clearItemLayouts = function () {
  23330. this._itemLayouts.length = 0;
  23331. };
  23332. /**
  23333. * Get visual property of single data item
  23334. * @param {number} idx
  23335. * @param {string} key
  23336. * @param {boolean} [ignoreParent=false]
  23337. */
  23338. listProto.getItemVisual = function (idx, key, ignoreParent) {
  23339. var itemVisual = this._itemVisuals[idx];
  23340. var val = itemVisual && itemVisual[key];
  23341. if (val == null && !ignoreParent) {
  23342. // Use global visual property
  23343. return this.getVisual(key);
  23344. }
  23345. return val;
  23346. };
  23347. /**
  23348. * Set visual property of single data item
  23349. *
  23350. * @param {number} idx
  23351. * @param {string|Object} key
  23352. * @param {*} [value]
  23353. *
  23354. * @example
  23355. * setItemVisual(0, 'color', color);
  23356. * setItemVisual(0, {
  23357. * 'color': color
  23358. * });
  23359. */
  23360. listProto.setItemVisual = function (idx, key, value) {
  23361. var itemVisual = this._itemVisuals[idx] || {};
  23362. var hasItemVisual = this.hasItemVisual;
  23363. this._itemVisuals[idx] = itemVisual;
  23364. if (isObject$4(key)) {
  23365. for (var name in key) {
  23366. if (key.hasOwnProperty(name)) {
  23367. itemVisual[name] = key[name];
  23368. hasItemVisual[name] = true;
  23369. }
  23370. }
  23371. return;
  23372. }
  23373. itemVisual[key] = value;
  23374. hasItemVisual[key] = true;
  23375. };
  23376. /**
  23377. * Clear itemVisuals and list visual.
  23378. */
  23379. listProto.clearAllVisual = function () {
  23380. this._visual = {};
  23381. this._itemVisuals = [];
  23382. this.hasItemVisual = {};
  23383. };
  23384. var setItemDataAndSeriesIndex = function (child) {
  23385. child.seriesIndex = this.seriesIndex;
  23386. child.dataIndex = this.dataIndex;
  23387. child.dataType = this.dataType;
  23388. };
  23389. /**
  23390. * Set graphic element relative to data. It can be set as null
  23391. * @param {number} idx
  23392. * @param {module:zrender/Element} [el]
  23393. */
  23394. listProto.setItemGraphicEl = function (idx, el) {
  23395. var hostModel = this.hostModel;
  23396. if (el) {
  23397. // Add data index and series index for indexing the data by element
  23398. // Useful in tooltip
  23399. el.dataIndex = idx;
  23400. el.dataType = this.dataType;
  23401. el.seriesIndex = hostModel && hostModel.seriesIndex;
  23402. if (el.type === 'group') {
  23403. el.traverse(setItemDataAndSeriesIndex, el);
  23404. }
  23405. }
  23406. this._graphicEls[idx] = el;
  23407. };
  23408. /**
  23409. * @param {number} idx
  23410. * @return {module:zrender/Element}
  23411. */
  23412. listProto.getItemGraphicEl = function (idx) {
  23413. return this._graphicEls[idx];
  23414. };
  23415. /**
  23416. * @param {Function} cb
  23417. * @param {*} context
  23418. */
  23419. listProto.eachItemGraphicEl = function (cb, context) {
  23420. each$1(this._graphicEls, function (el, idx) {
  23421. if (el) {
  23422. cb && cb.call(context, el, idx);
  23423. }
  23424. });
  23425. };
  23426. /**
  23427. * Shallow clone a new list except visual and layout properties, and graph elements.
  23428. * New list only change the indices.
  23429. */
  23430. listProto.cloneShallow = function (list) {
  23431. if (!list) {
  23432. var dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
  23433. list = new List(dimensionInfoList, this.hostModel);
  23434. }
  23435. // FIXME
  23436. list._storage = this._storage;
  23437. transferProperties(list, this);
  23438. // Clone will not change the data extent and indices
  23439. if (this._indices) {
  23440. var Ctor = this._indices.constructor;
  23441. list._indices = new Ctor(this._indices);
  23442. }
  23443. else {
  23444. list._indices = null;
  23445. }
  23446. list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
  23447. list._extent = clone(this._extent);
  23448. list._approximateExtent = clone(this._approximateExtent);
  23449. return list;
  23450. };
  23451. /**
  23452. * Wrap some method to add more feature
  23453. * @param {string} methodName
  23454. * @param {Function} injectFunction
  23455. */
  23456. listProto.wrapMethod = function (methodName, injectFunction) {
  23457. var originalMethod = this[methodName];
  23458. if (typeof originalMethod !== 'function') {
  23459. return;
  23460. }
  23461. this.__wrappedMethods = this.__wrappedMethods || [];
  23462. this.__wrappedMethods.push(methodName);
  23463. this[methodName] = function () {
  23464. var res = originalMethod.apply(this, arguments);
  23465. return injectFunction.apply(this, [res].concat(slice(arguments)));
  23466. };
  23467. };
  23468. // Methods that create a new list based on this list should be listed here.
  23469. // Notice that those method should `RETURN` the new list.
  23470. listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
  23471. // Methods that change indices of this list should be listed here.
  23472. listProto.CHANGABLE_METHODS = ['filterSelf', 'selectRange'];
  23473. /**
  23474. * @deprecated
  23475. * Use `echarts/data/helper/createDimensions` instead.
  23476. */
  23477. /**
  23478. * @see {module:echarts/test/ut/spec/data/completeDimensions}
  23479. *
  23480. * Complete the dimensions array, by user defined `dimension` and `encode`,
  23481. * and guessing from the data structure.
  23482. * If no 'value' dimension specified, the first no-named dimension will be
  23483. * named as 'value'.
  23484. *
  23485. * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which
  23486. * provides not only dim template, but also default order.
  23487. * properties: 'name', 'type', 'displayName'.
  23488. * `name` of each item provides default coord name.
  23489. * [{dimsDef: [string...]}, ...] can be specified to give names.
  23490. * [{ordinalMeta}] can be specified.
  23491. * @param {module:echarts/data/Source|Array|Object} source or data (for compatibal with pervious)
  23492. * @param {Object} [opt]
  23493. * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions
  23494. * For example: ['asdf', {name, type}, ...].
  23495. * @param {Object|HashMap} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3}
  23496. * @param {string} [opt.extraPrefix] Prefix of name when filling the left dimensions.
  23497. * @param {string} [opt.extraFromZero] If specified, extra dim names will be:
  23498. * extraPrefix + 0, extraPrefix + extraBaseIndex + 1 ...
  23499. * If not specified, extra dim names will be:
  23500. * extraPrefix, extraPrefix + 0, extraPrefix + 1 ...
  23501. * @param {number} [opt.dimCount] If not specified, guess by the first data item.
  23502. * @param {number} [opt.encodeDefaulter] If not specified, auto find the next available data dim.
  23503. * @return {Array.<Object>} [{
  23504. * name: string mandatory,
  23505. * displayName: string, the origin name in dimsDef, see source helper.
  23506. * If displayName given, the tooltip will displayed vertically.
  23507. * coordDim: string mandatory,
  23508. * isSysCoord: boolean True if the coord is from sys dimension.
  23509. * coordDimIndex: number mandatory,
  23510. * type: string optional,
  23511. * otherDims: { never null/undefined
  23512. * tooltip: number optional,
  23513. * label: number optional,
  23514. * itemName: number optional,
  23515. * seriesName: number optional,
  23516. * },
  23517. * isExtraCoord: boolean true or undefined.
  23518. * other props ...
  23519. * }]
  23520. */
  23521. function completeDimensions(sysDims, source, opt) {
  23522. if (!Source.isInstance(source)) {
  23523. source = Source.seriesDataToSource(source);
  23524. }
  23525. opt = opt || {};
  23526. sysDims = (sysDims || []).slice();
  23527. var dimsDef = (opt.dimsDef || []).slice();
  23528. var encodeDef = createHashMap(opt.encodeDef);
  23529. var dataDimNameMap = createHashMap();
  23530. var coordDimNameMap = createHashMap();
  23531. // var valueCandidate;
  23532. var result = [];
  23533. var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimCount);
  23534. // Apply user defined dims (`name` and `type`) and init result.
  23535. for (var i = 0; i < dimCount; i++) {
  23536. var dimDefItem = dimsDef[i] = extend(
  23537. {}, isObject$1(dimsDef[i]) ? dimsDef[i] : {name: dimsDef[i]}
  23538. );
  23539. var userDimName = dimDefItem.name;
  23540. var resultItem = result[i] = {otherDims: {}};
  23541. // Name will be applied later for avoiding duplication.
  23542. if (userDimName != null && dataDimNameMap.get(userDimName) == null) {
  23543. // Only if `series.dimensions` is defined in option
  23544. // displayName, will be set, and dimension will be diplayed vertically in
  23545. // tooltip by default.
  23546. resultItem.name = resultItem.displayName = userDimName;
  23547. dataDimNameMap.set(userDimName, i);
  23548. }
  23549. dimDefItem.type != null && (resultItem.type = dimDefItem.type);
  23550. dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);
  23551. }
  23552. // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`.
  23553. encodeDef.each(function (dataDims, coordDim) {
  23554. dataDims = normalizeToArray(dataDims).slice();
  23555. var validDataDims = encodeDef.set(coordDim, []);
  23556. each$1(dataDims, function (resultDimIdx, idx) {
  23557. // The input resultDimIdx can be dim name or index.
  23558. isString(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx));
  23559. if (resultDimIdx != null && resultDimIdx < dimCount) {
  23560. validDataDims[idx] = resultDimIdx;
  23561. applyDim(result[resultDimIdx], coordDim, idx);
  23562. }
  23563. });
  23564. });
  23565. // Apply templetes and default order from `sysDims`.
  23566. var availDimIdx = 0;
  23567. each$1(sysDims, function (sysDimItem, sysDimIndex) {
  23568. var coordDim;
  23569. var sysDimItem;
  23570. var sysDimItemDimsDef;
  23571. var sysDimItemOtherDims;
  23572. if (isString(sysDimItem)) {
  23573. coordDim = sysDimItem;
  23574. sysDimItem = {};
  23575. }
  23576. else {
  23577. coordDim = sysDimItem.name;
  23578. var ordinalMeta = sysDimItem.ordinalMeta;
  23579. sysDimItem.ordinalMeta = null;
  23580. sysDimItem = clone(sysDimItem);
  23581. sysDimItem.ordinalMeta = ordinalMeta;
  23582. // `coordDimIndex` should not be set directly.
  23583. sysDimItemDimsDef = sysDimItem.dimsDef;
  23584. sysDimItemOtherDims = sysDimItem.otherDims;
  23585. sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex
  23586. = sysDimItem.dimsDef = sysDimItem.otherDims = null;
  23587. }
  23588. var dataDims = normalizeToArray(encodeDef.get(coordDim));
  23589. // dimensions provides default dim sequences.
  23590. if (!dataDims.length) {
  23591. for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
  23592. while (availDimIdx < result.length && result[availDimIdx].coordDim != null) {
  23593. availDimIdx++;
  23594. }
  23595. availDimIdx < result.length && dataDims.push(availDimIdx++);
  23596. }
  23597. }
  23598. // Apply templates.
  23599. each$1(dataDims, function (resultDimIdx, coordDimIndex) {
  23600. var resultItem = result[resultDimIdx];
  23601. applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);
  23602. if (resultItem.name == null && sysDimItemDimsDef) {
  23603. resultItem.name = resultItem.displayName = sysDimItemDimsDef[coordDimIndex];
  23604. }
  23605. resultItem.isSysCoord = true;
  23606. // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}
  23607. sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);
  23608. });
  23609. });
  23610. function applyDim(resultItem, coordDim, coordDimIndex) {
  23611. if (OTHER_DIMENSIONS.get(coordDim) != null) {
  23612. resultItem.otherDims[coordDim] = coordDimIndex;
  23613. }
  23614. else {
  23615. resultItem.coordDim = coordDim;
  23616. resultItem.coordDimIndex = coordDimIndex;
  23617. coordDimNameMap.set(coordDim, true);
  23618. }
  23619. }
  23620. // Make sure the first extra dim is 'value'.
  23621. var extra = opt.extraPrefix || 'value';
  23622. // Set dim `name` and other `coordDim` and other props.
  23623. for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
  23624. var resultItem = result[resultDimIdx] = result[resultDimIdx] || {};
  23625. var coordDim = resultItem.coordDim;
  23626. coordDim == null && (
  23627. resultItem.coordDim = genName(
  23628. extra, coordDimNameMap, opt.extraFromZero
  23629. ),
  23630. resultItem.coordDimIndex = 0,
  23631. resultItem.isExtraCoord = true
  23632. );
  23633. resultItem.name == null && (resultItem.name = genName(
  23634. resultItem.coordDim,
  23635. dataDimNameMap
  23636. ));
  23637. if (resultItem.type == null && guessOrdinal(source, resultDimIdx, resultItem.name)) {
  23638. resultItem.type = 'ordinal';
  23639. }
  23640. }
  23641. return result;
  23642. }
  23643. // ??? TODO
  23644. // Originally detect dimCount by data[0]. Should we
  23645. // optimize it to only by sysDims and dimensions and encode.
  23646. // So only necessary dims will be initialized.
  23647. // But
  23648. // (1) custom series should be considered. where other dims
  23649. // may be visited.
  23650. // (2) sometimes user need to calcualte bubble size or use visualMap
  23651. // on other dimensions besides coordSys needed.
  23652. function getDimCount(source, sysDims, dimsDef, dimCount) {
  23653. if (dimCount == null) {
  23654. dimCount = Math.max(
  23655. source.dimensionsDetectCount || 1,
  23656. sysDims.length,
  23657. dimsDef.length
  23658. );
  23659. each$1(sysDims, function (sysDimItem) {
  23660. var sysDimItemDimsDef = sysDimItem.dimsDef;
  23661. sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length));
  23662. });
  23663. }
  23664. return dimCount;
  23665. }
  23666. function genName(name, map$$1, fromZero) {
  23667. if (fromZero || map$$1.get(name) != null) {
  23668. var i = 0;
  23669. while (map$$1.get(name + i) != null) {
  23670. i++;
  23671. }
  23672. name += i;
  23673. }
  23674. map$$1.set(name, true);
  23675. return name;
  23676. }
  23677. /**
  23678. * Substitute `completeDimensions`.
  23679. * `completeDimensions` is to be deprecated.
  23680. */
  23681. /**
  23682. * @param {module:echarts/data/Source|module:echarts/data/List} source or data.
  23683. * @param {Object|Array} [opt]
  23684. * @param {Array.<string|Object>} [opt.coordDimensions=[]]
  23685. * @param {number} [opt.dimensionsCount]
  23686. * @param {string} [opt.extraPrefix]
  23687. * @param {boolean} [opt.extraFromZero]
  23688. * @param {Array.<string|Object>} [opt.dimensionsDefine=source.dimensionsDefine] Overwrite source define.
  23689. * @param {Object|HashMap} [opt.encodeDefine=source.encodeDefine] Overwrite source define.
  23690. * @return {Array.<Object>} dimensionsInfo
  23691. */
  23692. var createDimensions = function (source, opt) {
  23693. opt = opt || {};
  23694. return completeDimensions(opt.coordDimensions || [], source, {
  23695. dimsDef: opt.dimensionsDefine || source.dimensionsDefine,
  23696. encodeDef: opt.encodeDefine || source.encodeDefine,
  23697. dimCount: opt.dimensionsCount,
  23698. extraPrefix: opt.extraPrefix,
  23699. extraFromZero: opt.extraFromZero
  23700. });
  23701. };
  23702. /**
  23703. * @param {module:echarts/data/Source|Array} source Or raw data.
  23704. * @param {module:echarts/model/Series} seriesModel
  23705. */
  23706. function createListFromArray(source, seriesModel) {
  23707. if (!Source.isInstance(source)) {
  23708. source = Source.seriesDataToSource(source);
  23709. }
  23710. var coordSysName = seriesModel.get('coordinateSystem');
  23711. var registeredCoordSys = CoordinateSystemManager.get(coordSysName);
  23712. var coordSysDefine = getCoordSysDefineBySeries(seriesModel);
  23713. var coordSysDimDefs;
  23714. if (coordSysDefine) {
  23715. coordSysDimDefs = map(coordSysDefine.coordSysDims, function (dim) {
  23716. var dimInfo = {name: dim};
  23717. var axisModel = coordSysDefine.axisMap.get(dim);
  23718. if (axisModel) {
  23719. var axisType = axisModel.get('type');
  23720. dimInfo.type = getDimensionTypeByAxis(axisType);
  23721. dimInfo.stackable = isStackable(axisType);
  23722. }
  23723. return dimInfo;
  23724. });
  23725. }
  23726. if (!coordSysDimDefs) {
  23727. // Get dimensions from registered coordinate system
  23728. coordSysDimDefs = (registeredCoordSys && (
  23729. registeredCoordSys.getDimensionsInfo
  23730. ? registeredCoordSys.getDimensionsInfo()
  23731. : registeredCoordSys.dimensions.slice()
  23732. )) || ['x', 'y'];
  23733. }
  23734. var dimInfoList = createDimensions(source, {
  23735. coordDimensions: coordSysDimDefs
  23736. });
  23737. var firstCategoryDimIndex;
  23738. var hasNameEncode;
  23739. coordSysDefine && each$1(dimInfoList, function (dimInfo, dimIndex) {
  23740. var coordDim = dimInfo.coordDim;
  23741. var categoryAxisModel = coordSysDefine.categoryAxisMap.get(coordDim);
  23742. if (categoryAxisModel) {
  23743. if (firstCategoryDimIndex == null) {
  23744. firstCategoryDimIndex = dimIndex;
  23745. }
  23746. dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta();
  23747. }
  23748. if (dimInfo.otherDims.itemName != null) {
  23749. hasNameEncode = true;
  23750. }
  23751. });
  23752. if (!hasNameEncode && firstCategoryDimIndex != null) {
  23753. dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0;
  23754. }
  23755. var list = new List(dimInfoList, seriesModel);
  23756. var dimValueGetter = (firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source))
  23757. ? function (itemOpt, dimName, dataIndex, dimIndex) {
  23758. // Use dataIndex as ordinal value in categoryAxis
  23759. return dimIndex === firstCategoryDimIndex
  23760. ? dataIndex
  23761. : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex);
  23762. }
  23763. : null;
  23764. list.hasItemOption = false;
  23765. list.initData(source, null, dimValueGetter);
  23766. return list;
  23767. }
  23768. function isStackable(axisType) {
  23769. return axisType !== 'category' && axisType !== 'time';
  23770. }
  23771. function isNeedCompleteOrdinalData(source) {
  23772. if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) {
  23773. var sampleItem = firstDataNotNull(source.data || []);
  23774. return sampleItem != null
  23775. && !isArray(getDataItemValue(sampleItem));
  23776. }
  23777. }
  23778. function firstDataNotNull(data) {
  23779. var i = 0;
  23780. while (i < data.length && data[i] == null) {
  23781. i++;
  23782. }
  23783. return data[i];
  23784. }
  23785. SeriesModel.extend({
  23786. type: 'series.line',
  23787. dependencies: ['grid', 'polar'],
  23788. getInitialData: function (option, ecModel) {
  23789. if (__DEV__) {
  23790. var coordSys = option.coordinateSystem;
  23791. if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
  23792. throw new Error('Line not support coordinateSystem besides cartesian and polar');
  23793. }
  23794. }
  23795. return createListFromArray(this.getSource(), this);
  23796. },
  23797. defaultOption: {
  23798. zlevel: 0, // 一级层叠
  23799. z: 2, // 二级层叠
  23800. coordinateSystem: 'cartesian2d',
  23801. legendHoverLink: true,
  23802. hoverAnimation: true,
  23803. // stack: null
  23804. // xAxisIndex: 0,
  23805. // yAxisIndex: 0,
  23806. // polarIndex: 0,
  23807. // If clip the overflow value
  23808. clipOverflow: true,
  23809. // cursor: null,
  23810. label: {
  23811. position: 'top'
  23812. },
  23813. // itemStyle: {
  23814. // },
  23815. lineStyle: {
  23816. width: 2,
  23817. type: 'solid'
  23818. },
  23819. // areaStyle: {origin: 'auto'},
  23820. // false, 'start', 'end', 'middle'
  23821. step: false,
  23822. // Disabled if step is true
  23823. smooth: false,
  23824. smoothMonotone: null,
  23825. // 拐点图形类型
  23826. symbol: 'emptyCircle',
  23827. // 拐点图形大小
  23828. symbolSize: 4,
  23829. // 拐点图形旋转控制
  23830. symbolRotate: null,
  23831. // 是否显示 symbol, 只有在 tooltip hover 的时候显示
  23832. showSymbol: true,
  23833. // 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略)
  23834. showAllSymbol: false,
  23835. // 是否连接断点
  23836. connectNulls: false,
  23837. // 数据过滤,'average', 'max', 'min', 'sum'
  23838. sampling: 'none',
  23839. animationEasing: 'linear',
  23840. // Disable progressive
  23841. progressive: 0,
  23842. hoverLayerThreshold: Infinity
  23843. }
  23844. });
  23845. // Symbol factory
  23846. /**
  23847. * Triangle shape
  23848. * @inner
  23849. */
  23850. var Triangle = extendShape({
  23851. type: 'triangle',
  23852. shape: {
  23853. cx: 0,
  23854. cy: 0,
  23855. width: 0,
  23856. height: 0
  23857. },
  23858. buildPath: function (path, shape) {
  23859. var cx = shape.cx;
  23860. var cy = shape.cy;
  23861. var width = shape.width / 2;
  23862. var height = shape.height / 2;
  23863. path.moveTo(cx, cy - height);
  23864. path.lineTo(cx + width, cy + height);
  23865. path.lineTo(cx - width, cy + height);
  23866. path.closePath();
  23867. }
  23868. });
  23869. /**
  23870. * Diamond shape
  23871. * @inner
  23872. */
  23873. var Diamond = extendShape({
  23874. type: 'diamond',
  23875. shape: {
  23876. cx: 0,
  23877. cy: 0,
  23878. width: 0,
  23879. height: 0
  23880. },
  23881. buildPath: function (path, shape) {
  23882. var cx = shape.cx;
  23883. var cy = shape.cy;
  23884. var width = shape.width / 2;
  23885. var height = shape.height / 2;
  23886. path.moveTo(cx, cy - height);
  23887. path.lineTo(cx + width, cy);
  23888. path.lineTo(cx, cy + height);
  23889. path.lineTo(cx - width, cy);
  23890. path.closePath();
  23891. }
  23892. });
  23893. /**
  23894. * Pin shape
  23895. * @inner
  23896. */
  23897. var Pin = extendShape({
  23898. type: 'pin',
  23899. shape: {
  23900. // x, y on the cusp
  23901. x: 0,
  23902. y: 0,
  23903. width: 0,
  23904. height: 0
  23905. },
  23906. buildPath: function (path, shape) {
  23907. var x = shape.x;
  23908. var y = shape.y;
  23909. var w = shape.width / 5 * 3;
  23910. // Height must be larger than width
  23911. var h = Math.max(w, shape.height);
  23912. var r = w / 2;
  23913. // Dist on y with tangent point and circle center
  23914. var dy = r * r / (h - r);
  23915. var cy = y - h + r + dy;
  23916. var angle = Math.asin(dy / r);
  23917. // Dist on x with tangent point and circle center
  23918. var dx = Math.cos(angle) * r;
  23919. var tanX = Math.sin(angle);
  23920. var tanY = Math.cos(angle);
  23921. var cpLen = r * 0.6;
  23922. var cpLen2 = r * 0.7;
  23923. path.moveTo(x - dx, cy + dy);
  23924. path.arc(
  23925. x, cy, r,
  23926. Math.PI - angle,
  23927. Math.PI * 2 + angle
  23928. );
  23929. path.bezierCurveTo(
  23930. x + dx - tanX * cpLen, cy + dy + tanY * cpLen,
  23931. x, y - cpLen2,
  23932. x, y
  23933. );
  23934. path.bezierCurveTo(
  23935. x, y - cpLen2,
  23936. x - dx + tanX * cpLen, cy + dy + tanY * cpLen,
  23937. x - dx, cy + dy
  23938. );
  23939. path.closePath();
  23940. }
  23941. });
  23942. /**
  23943. * Arrow shape
  23944. * @inner
  23945. */
  23946. var Arrow = extendShape({
  23947. type: 'arrow',
  23948. shape: {
  23949. x: 0,
  23950. y: 0,
  23951. width: 0,
  23952. height: 0
  23953. },
  23954. buildPath: function (ctx, shape) {
  23955. var height = shape.height;
  23956. var width = shape.width;
  23957. var x = shape.x;
  23958. var y = shape.y;
  23959. var dx = width / 3 * 2;
  23960. ctx.moveTo(x, y);
  23961. ctx.lineTo(x + dx, y + height);
  23962. ctx.lineTo(x, y + height / 4 * 3);
  23963. ctx.lineTo(x - dx, y + height);
  23964. ctx.lineTo(x, y);
  23965. ctx.closePath();
  23966. }
  23967. });
  23968. /**
  23969. * Map of path contructors
  23970. * @type {Object.<string, module:zrender/graphic/Path>}
  23971. */
  23972. var symbolCtors = {
  23973. line: Line,
  23974. rect: Rect,
  23975. roundRect: Rect,
  23976. square: Rect,
  23977. circle: Circle,
  23978. diamond: Diamond,
  23979. pin: Pin,
  23980. arrow: Arrow,
  23981. triangle: Triangle
  23982. };
  23983. var symbolShapeMakers = {
  23984. line: function (x, y, w, h, shape) {
  23985. // FIXME
  23986. shape.x1 = x;
  23987. shape.y1 = y + h / 2;
  23988. shape.x2 = x + w;
  23989. shape.y2 = y + h / 2;
  23990. },
  23991. rect: function (x, y, w, h, shape) {
  23992. shape.x = x;
  23993. shape.y = y;
  23994. shape.width = w;
  23995. shape.height = h;
  23996. },
  23997. roundRect: function (x, y, w, h, shape) {
  23998. shape.x = x;
  23999. shape.y = y;
  24000. shape.width = w;
  24001. shape.height = h;
  24002. shape.r = Math.min(w, h) / 4;
  24003. },
  24004. square: function (x, y, w, h, shape) {
  24005. var size = Math.min(w, h);
  24006. shape.x = x;
  24007. shape.y = y;
  24008. shape.width = size;
  24009. shape.height = size;
  24010. },
  24011. circle: function (x, y, w, h, shape) {
  24012. // Put circle in the center of square
  24013. shape.cx = x + w / 2;
  24014. shape.cy = y + h / 2;
  24015. shape.r = Math.min(w, h) / 2;
  24016. },
  24017. diamond: function (x, y, w, h, shape) {
  24018. shape.cx = x + w / 2;
  24019. shape.cy = y + h / 2;
  24020. shape.width = w;
  24021. shape.height = h;
  24022. },
  24023. pin: function (x, y, w, h, shape) {
  24024. shape.x = x + w / 2;
  24025. shape.y = y + h / 2;
  24026. shape.width = w;
  24027. shape.height = h;
  24028. },
  24029. arrow: function (x, y, w, h, shape) {
  24030. shape.x = x + w / 2;
  24031. shape.y = y + h / 2;
  24032. shape.width = w;
  24033. shape.height = h;
  24034. },
  24035. triangle: function (x, y, w, h, shape) {
  24036. shape.cx = x + w / 2;
  24037. shape.cy = y + h / 2;
  24038. shape.width = w;
  24039. shape.height = h;
  24040. }
  24041. };
  24042. var symbolBuildProxies = {};
  24043. each$1(symbolCtors, function (Ctor, name) {
  24044. symbolBuildProxies[name] = new Ctor();
  24045. });
  24046. var SymbolClz$2 = extendShape({
  24047. type: 'symbol',
  24048. shape: {
  24049. symbolType: '',
  24050. x: 0,
  24051. y: 0,
  24052. width: 0,
  24053. height: 0
  24054. },
  24055. beforeBrush: function () {
  24056. var style = this.style;
  24057. var shape = this.shape;
  24058. // FIXME
  24059. if (shape.symbolType === 'pin' && style.textPosition === 'inside') {
  24060. style.textPosition = ['50%', '40%'];
  24061. style.textAlign = 'center';
  24062. style.textVerticalAlign = 'middle';
  24063. }
  24064. },
  24065. buildPath: function (ctx, shape, inBundle) {
  24066. var symbolType = shape.symbolType;
  24067. var proxySymbol = symbolBuildProxies[symbolType];
  24068. if (shape.symbolType !== 'none') {
  24069. if (!proxySymbol) {
  24070. // Default rect
  24071. symbolType = 'rect';
  24072. proxySymbol = symbolBuildProxies[symbolType];
  24073. }
  24074. symbolShapeMakers[symbolType](
  24075. shape.x, shape.y, shape.width, shape.height, proxySymbol.shape
  24076. );
  24077. proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
  24078. }
  24079. }
  24080. });
  24081. // Provide setColor helper method to avoid determine if set the fill or stroke outside
  24082. function symbolPathSetColor(color, innerColor) {
  24083. if (this.type !== 'image') {
  24084. var symbolStyle = this.style;
  24085. var symbolShape = this.shape;
  24086. if (symbolShape && symbolShape.symbolType === 'line') {
  24087. symbolStyle.stroke = color;
  24088. }
  24089. else if (this.__isEmptyBrush) {
  24090. symbolStyle.stroke = color;
  24091. symbolStyle.fill = innerColor || '#fff';
  24092. }
  24093. else {
  24094. // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
  24095. symbolStyle.fill && (symbolStyle.fill = color);
  24096. symbolStyle.stroke && (symbolStyle.stroke = color);
  24097. }
  24098. this.dirty(false);
  24099. }
  24100. }
  24101. /**
  24102. * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
  24103. * @param {string} symbolType
  24104. * @param {number} x
  24105. * @param {number} y
  24106. * @param {number} w
  24107. * @param {number} h
  24108. * @param {string} color
  24109. * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
  24110. * for path and image only.
  24111. */
  24112. function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
  24113. // TODO Support image object, DynamicImage.
  24114. var isEmpty = symbolType.indexOf('empty') === 0;
  24115. if (isEmpty) {
  24116. symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
  24117. }
  24118. var symbolPath;
  24119. if (symbolType.indexOf('image://') === 0) {
  24120. symbolPath = makeImage(
  24121. symbolType.slice(8),
  24122. new BoundingRect(x, y, w, h),
  24123. keepAspect ? 'center' : 'cover'
  24124. );
  24125. }
  24126. else if (symbolType.indexOf('path://') === 0) {
  24127. symbolPath = makePath(
  24128. symbolType.slice(7),
  24129. {},
  24130. new BoundingRect(x, y, w, h),
  24131. keepAspect ? 'center' : 'cover'
  24132. );
  24133. }
  24134. else {
  24135. symbolPath = new SymbolClz$2({
  24136. shape: {
  24137. symbolType: symbolType,
  24138. x: x,
  24139. y: y,
  24140. width: w,
  24141. height: h
  24142. }
  24143. });
  24144. }
  24145. symbolPath.__isEmptyBrush = isEmpty;
  24146. symbolPath.setColor = symbolPathSetColor;
  24147. symbolPath.setColor(color);
  24148. return symbolPath;
  24149. }
  24150. /**
  24151. * @param {module:echarts/data/List} data
  24152. * @param {number} dataIndex
  24153. * @return {string} label string. Not null/undefined
  24154. */
  24155. function getDefaultLabel(data, dataIndex) {
  24156. var labelDims = data.mapDimension('defaultedLabel', true);
  24157. var len = labelDims.length;
  24158. // Simple optimization (in lots of cases, label dims length is 1)
  24159. if (len === 1) {
  24160. return retrieveRawValue(data, dataIndex, labelDims[0]);
  24161. }
  24162. else if (len) {
  24163. var vals = [];
  24164. for (var i = 0; i < labelDims.length; i++) {
  24165. var val = retrieveRawValue(data, dataIndex, labelDims[i]);
  24166. vals.push(val);
  24167. }
  24168. return vals.join(' ');
  24169. }
  24170. }
  24171. /**
  24172. * @module echarts/chart/helper/Symbol
  24173. */
  24174. function getSymbolSize(data, idx) {
  24175. var symbolSize = data.getItemVisual(idx, 'symbolSize');
  24176. return symbolSize instanceof Array
  24177. ? symbolSize.slice()
  24178. : [+symbolSize, +symbolSize];
  24179. }
  24180. function getScale(symbolSize) {
  24181. return [symbolSize[0] / 2, symbolSize[1] / 2];
  24182. }
  24183. /**
  24184. * @constructor
  24185. * @alias {module:echarts/chart/helper/Symbol}
  24186. * @param {module:echarts/data/List} data
  24187. * @param {number} idx
  24188. * @extends {module:zrender/graphic/Group}
  24189. */
  24190. function SymbolClz(data, idx, seriesScope) {
  24191. Group.call(this);
  24192. this.updateData(data, idx, seriesScope);
  24193. }
  24194. var symbolProto = SymbolClz.prototype;
  24195. function driftSymbol(dx, dy) {
  24196. this.parent.drift(dx, dy);
  24197. }
  24198. symbolProto._createSymbol = function (symbolType, data, idx, symbolSize) {
  24199. // Remove paths created before
  24200. this.removeAll();
  24201. var color = data.getItemVisual(idx, 'color');
  24202. // var symbolPath = createSymbol(
  24203. // symbolType, -0.5, -0.5, 1, 1, color
  24204. // );
  24205. // If width/height are set too small (e.g., set to 1) on ios10
  24206. // and macOS Sierra, a circle stroke become a rect, no matter what
  24207. // the scale is set. So we set width/height as 2. See #4150.
  24208. var symbolPath = createSymbol(
  24209. symbolType, -1, -1, 2, 2, color
  24210. );
  24211. symbolPath.attr({
  24212. z2: 100,
  24213. culling: true,
  24214. scale: getScale(symbolSize)
  24215. });
  24216. // Rewrite drift method
  24217. symbolPath.drift = driftSymbol;
  24218. this._symbolType = symbolType;
  24219. this.add(symbolPath);
  24220. };
  24221. /**
  24222. * Stop animation
  24223. * @param {boolean} toLastFrame
  24224. */
  24225. symbolProto.stopSymbolAnimation = function (toLastFrame) {
  24226. this.childAt(0).stopAnimation(toLastFrame);
  24227. };
  24228. /**
  24229. * FIXME:
  24230. * Caution: This method breaks the encapsulation of this module,
  24231. * but it indeed brings convenience. So do not use the method
  24232. * unless you detailedly know all the implements of `Symbol`,
  24233. * especially animation.
  24234. *
  24235. * Get symbol path element.
  24236. */
  24237. symbolProto.getSymbolPath = function () {
  24238. return this.childAt(0);
  24239. };
  24240. /**
  24241. * Get scale(aka, current symbol size).
  24242. * Including the change caused by animation
  24243. */
  24244. symbolProto.getScale = function () {
  24245. return this.childAt(0).scale;
  24246. };
  24247. /**
  24248. * Highlight symbol
  24249. */
  24250. symbolProto.highlight = function () {
  24251. this.childAt(0).trigger('emphasis');
  24252. };
  24253. /**
  24254. * Downplay symbol
  24255. */
  24256. symbolProto.downplay = function () {
  24257. this.childAt(0).trigger('normal');
  24258. };
  24259. /**
  24260. * @param {number} zlevel
  24261. * @param {number} z
  24262. */
  24263. symbolProto.setZ = function (zlevel, z) {
  24264. var symbolPath = this.childAt(0);
  24265. symbolPath.zlevel = zlevel;
  24266. symbolPath.z = z;
  24267. };
  24268. symbolProto.setDraggable = function (draggable) {
  24269. var symbolPath = this.childAt(0);
  24270. symbolPath.draggable = draggable;
  24271. symbolPath.cursor = draggable ? 'move' : 'pointer';
  24272. };
  24273. /**
  24274. * Update symbol properties
  24275. * @param {module:echarts/data/List} data
  24276. * @param {number} idx
  24277. * @param {Object} [seriesScope]
  24278. * @param {Object} [seriesScope.itemStyle]
  24279. * @param {Object} [seriesScope.hoverItemStyle]
  24280. * @param {Object} [seriesScope.symbolRotate]
  24281. * @param {Object} [seriesScope.symbolOffset]
  24282. * @param {module:echarts/model/Model} [seriesScope.labelModel]
  24283. * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel]
  24284. * @param {boolean} [seriesScope.hoverAnimation]
  24285. * @param {Object} [seriesScope.cursorStyle]
  24286. * @param {module:echarts/model/Model} [seriesScope.itemModel]
  24287. * @param {string} [seriesScope.symbolInnerColor]
  24288. * @param {Object} [seriesScope.fadeIn=false]
  24289. */
  24290. symbolProto.updateData = function (data, idx, seriesScope) {
  24291. this.silent = false;
  24292. var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
  24293. var seriesModel = data.hostModel;
  24294. var symbolSize = getSymbolSize(data, idx);
  24295. var isInit = symbolType !== this._symbolType;
  24296. if (isInit) {
  24297. this._createSymbol(symbolType, data, idx, symbolSize);
  24298. }
  24299. else {
  24300. var symbolPath = this.childAt(0);
  24301. symbolPath.silent = false;
  24302. updateProps(symbolPath, {
  24303. scale: getScale(symbolSize)
  24304. }, seriesModel, idx);
  24305. }
  24306. this._updateCommon(data, idx, symbolSize, seriesScope);
  24307. if (isInit) {
  24308. var symbolPath = this.childAt(0);
  24309. var fadeIn = seriesScope && seriesScope.fadeIn;
  24310. var target = {scale: symbolPath.scale.slice()};
  24311. fadeIn && (target.style = {opacity: symbolPath.style.opacity});
  24312. symbolPath.scale = [0, 0];
  24313. fadeIn && (symbolPath.style.opacity = 0);
  24314. initProps(symbolPath, target, seriesModel, idx);
  24315. }
  24316. this._seriesModel = seriesModel;
  24317. };
  24318. // Update common properties
  24319. var normalStyleAccessPath = ['itemStyle'];
  24320. var emphasisStyleAccessPath = ['emphasis', 'itemStyle'];
  24321. var normalLabelAccessPath = ['label'];
  24322. var emphasisLabelAccessPath = ['emphasis', 'label'];
  24323. /**
  24324. * @param {module:echarts/data/List} data
  24325. * @param {number} idx
  24326. * @param {Array.<number>} symbolSize
  24327. * @param {Object} [seriesScope]
  24328. */
  24329. symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) {
  24330. var symbolPath = this.childAt(0);
  24331. var seriesModel = data.hostModel;
  24332. var color = data.getItemVisual(idx, 'color');
  24333. // Reset style
  24334. if (symbolPath.type !== 'image') {
  24335. symbolPath.useStyle({
  24336. strokeNoScale: true
  24337. });
  24338. }
  24339. var itemStyle = seriesScope && seriesScope.itemStyle;
  24340. var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle;
  24341. var symbolRotate = seriesScope && seriesScope.symbolRotate;
  24342. var symbolOffset = seriesScope && seriesScope.symbolOffset;
  24343. var labelModel = seriesScope && seriesScope.labelModel;
  24344. var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
  24345. var hoverAnimation = seriesScope && seriesScope.hoverAnimation;
  24346. var cursorStyle = seriesScope && seriesScope.cursorStyle;
  24347. if (!seriesScope || data.hasItemOption) {
  24348. var itemModel = (seriesScope && seriesScope.itemModel)
  24349. ? seriesScope.itemModel : data.getItemModel(idx);
  24350. // Color must be excluded.
  24351. // Because symbol provide setColor individually to set fill and stroke
  24352. itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']);
  24353. hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
  24354. symbolRotate = itemModel.getShallow('symbolRotate');
  24355. symbolOffset = itemModel.getShallow('symbolOffset');
  24356. labelModel = itemModel.getModel(normalLabelAccessPath);
  24357. hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);
  24358. hoverAnimation = itemModel.getShallow('hoverAnimation');
  24359. cursorStyle = itemModel.getShallow('cursor');
  24360. }
  24361. else {
  24362. hoverItemStyle = extend({}, hoverItemStyle);
  24363. }
  24364. var elStyle = symbolPath.style;
  24365. symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
  24366. if (symbolOffset) {
  24367. symbolPath.attr('position', [
  24368. parsePercent$1(symbolOffset[0], symbolSize[0]),
  24369. parsePercent$1(symbolOffset[1], symbolSize[1])
  24370. ]);
  24371. }
  24372. cursorStyle && symbolPath.attr('cursor', cursorStyle);
  24373. // PENDING setColor before setStyle!!!
  24374. symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor);
  24375. symbolPath.setStyle(itemStyle);
  24376. var opacity = data.getItemVisual(idx, 'opacity');
  24377. if (opacity != null) {
  24378. elStyle.opacity = opacity;
  24379. }
  24380. var useNameLabel = seriesScope && seriesScope.useNameLabel;
  24381. setLabelStyle(
  24382. elStyle, hoverItemStyle, labelModel, hoverLabelModel,
  24383. {
  24384. labelFetcher: seriesModel,
  24385. labelDataIndex: idx,
  24386. defaultText: getLabelDefaultText,
  24387. isRectText: true,
  24388. autoColor: color
  24389. }
  24390. );
  24391. // Do not execute util needed.
  24392. function getLabelDefaultText(idx, opt) {
  24393. return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);
  24394. }
  24395. symbolPath.off('mouseover')
  24396. .off('mouseout')
  24397. .off('emphasis')
  24398. .off('normal');
  24399. symbolPath.hoverStyle = hoverItemStyle;
  24400. // FIXME
  24401. // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead.
  24402. setHoverStyle(symbolPath);
  24403. var scale = getScale(symbolSize);
  24404. if (hoverAnimation && seriesModel.isAnimationEnabled()) {
  24405. var onEmphasis = function() {
  24406. var ratio = scale[1] / scale[0];
  24407. this.animateTo({
  24408. scale: [
  24409. Math.max(scale[0] * 1.1, scale[0] + 3),
  24410. Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
  24411. ]
  24412. }, 400, 'elasticOut');
  24413. };
  24414. var onNormal = function() {
  24415. this.animateTo({
  24416. scale: scale
  24417. }, 400, 'elasticOut');
  24418. };
  24419. symbolPath.on('mouseover', onEmphasis)
  24420. .on('mouseout', onNormal)
  24421. .on('emphasis', onEmphasis)
  24422. .on('normal', onNormal);
  24423. }
  24424. };
  24425. /**
  24426. * @param {Function} cb
  24427. * @param {Object} [opt]
  24428. * @param {Object} [opt.keepLabel=true]
  24429. */
  24430. symbolProto.fadeOut = function (cb, opt) {
  24431. var symbolPath = this.childAt(0);
  24432. // Avoid mistaken hover when fading out
  24433. this.silent = symbolPath.silent = true;
  24434. // Not show text when animating
  24435. !(opt && opt.keepLabel) && (symbolPath.style.text = null);
  24436. updateProps(
  24437. symbolPath,
  24438. {
  24439. style: {opacity: 0},
  24440. scale: [0, 0]
  24441. },
  24442. this._seriesModel,
  24443. this.dataIndex,
  24444. cb
  24445. );
  24446. };
  24447. inherits(SymbolClz, Group);
  24448. /**
  24449. * @module echarts/chart/helper/SymbolDraw
  24450. */
  24451. /**
  24452. * @constructor
  24453. * @alias module:echarts/chart/helper/SymbolDraw
  24454. * @param {module:zrender/graphic/Group} [symbolCtor]
  24455. */
  24456. function SymbolDraw(symbolCtor) {
  24457. this.group = new Group();
  24458. this._symbolCtor = symbolCtor || SymbolClz;
  24459. }
  24460. var symbolDrawProto = SymbolDraw.prototype;
  24461. function symbolNeedsDraw(data, point, idx, isIgnore) {
  24462. return point && !isNaN(point[0]) && !isNaN(point[1])
  24463. && !(isIgnore && isIgnore(idx))
  24464. && data.getItemVisual(idx, 'symbol') !== 'none';
  24465. }
  24466. /**
  24467. * Update symbols draw by new data
  24468. * @param {module:echarts/data/List} data
  24469. * @param {Array.<boolean>} [isIgnore]
  24470. */
  24471. symbolDrawProto.updateData = function (data, isIgnore) {
  24472. var group = this.group;
  24473. var seriesModel = data.hostModel;
  24474. var oldData = this._data;
  24475. var SymbolCtor = this._symbolCtor;
  24476. var seriesScope = makeSeriesScope(data);
  24477. // There is no oldLineData only when first rendering or switching from
  24478. // stream mode to normal mode, where previous elements should be removed.
  24479. if (!oldData) {
  24480. group.removeAll();
  24481. }
  24482. data.diff(oldData)
  24483. .add(function (newIdx) {
  24484. var point = data.getItemLayout(newIdx);
  24485. if (symbolNeedsDraw(data, point, newIdx, isIgnore)) {
  24486. var symbolEl = new SymbolCtor(data, newIdx, seriesScope);
  24487. symbolEl.attr('position', point);
  24488. data.setItemGraphicEl(newIdx, symbolEl);
  24489. group.add(symbolEl);
  24490. }
  24491. })
  24492. .update(function (newIdx, oldIdx) {
  24493. var symbolEl = oldData.getItemGraphicEl(oldIdx);
  24494. var point = data.getItemLayout(newIdx);
  24495. if (!symbolNeedsDraw(data, point, newIdx, isIgnore)) {
  24496. group.remove(symbolEl);
  24497. return;
  24498. }
  24499. if (!symbolEl) {
  24500. symbolEl = new SymbolCtor(data, newIdx);
  24501. symbolEl.attr('position', point);
  24502. }
  24503. else {
  24504. symbolEl.updateData(data, newIdx, seriesScope);
  24505. updateProps(symbolEl, {
  24506. position: point
  24507. }, seriesModel);
  24508. }
  24509. // Add back
  24510. group.add(symbolEl);
  24511. data.setItemGraphicEl(newIdx, symbolEl);
  24512. })
  24513. .remove(function (oldIdx) {
  24514. var el = oldData.getItemGraphicEl(oldIdx);
  24515. el && el.fadeOut(function () {
  24516. group.remove(el);
  24517. });
  24518. })
  24519. .execute();
  24520. this._data = data;
  24521. };
  24522. symbolDrawProto.isPersistent = function () {
  24523. return true;
  24524. };
  24525. symbolDrawProto.updateLayout = function () {
  24526. var data = this._data;
  24527. if (data) {
  24528. // Not use animation
  24529. data.eachItemGraphicEl(function (el, idx) {
  24530. var point = data.getItemLayout(idx);
  24531. el.attr('position', point);
  24532. });
  24533. }
  24534. };
  24535. symbolDrawProto.incrementalPrepareUpdate = function (data) {
  24536. this._seriesScope = makeSeriesScope(data);
  24537. this._data = null;
  24538. this.group.removeAll();
  24539. };
  24540. symbolDrawProto.incrementalUpdate = function (taskParams, data, isIgnore) {
  24541. function updateIncrementalAndHover(el) {
  24542. if (!el.isGroup) {
  24543. el.incremental = el.useHoverLayer = true;
  24544. }
  24545. }
  24546. for (var idx = taskParams.start; idx < taskParams.end; idx++) {
  24547. var point = data.getItemLayout(idx);
  24548. if (symbolNeedsDraw(data, point, idx, isIgnore)) {
  24549. var el = new this._symbolCtor(data, idx, this._seriesScope);
  24550. el.traverse(updateIncrementalAndHover);
  24551. el.attr('position', point);
  24552. this.group.add(el);
  24553. data.setItemGraphicEl(idx, el);
  24554. }
  24555. }
  24556. };
  24557. symbolDrawProto.remove = function (enableAnimation) {
  24558. var group = this.group;
  24559. var data = this._data;
  24560. if (data) {
  24561. if (enableAnimation) {
  24562. data.eachItemGraphicEl(function (el) {
  24563. el.fadeOut(function () {
  24564. group.remove(el);
  24565. });
  24566. });
  24567. }
  24568. else {
  24569. group.removeAll();
  24570. }
  24571. }
  24572. };
  24573. function makeSeriesScope(data) {
  24574. var seriesModel = data.hostModel;
  24575. return {
  24576. itemStyle: seriesModel.getModel('itemStyle').getItemStyle(['color']),
  24577. hoverItemStyle: seriesModel.getModel('emphasis.itemStyle').getItemStyle(),
  24578. symbolRotate: seriesModel.get('symbolRotate'),
  24579. symbolOffset: seriesModel.get('symbolOffset'),
  24580. hoverAnimation: seriesModel.get('hoverAnimation'),
  24581. labelModel: seriesModel.getModel('label'),
  24582. hoverLabelModel: seriesModel.getModel('emphasis.label'),
  24583. cursorStyle: seriesModel.get('cursor')
  24584. };
  24585. }
  24586. // var arrayDiff = require('zrender/src/core/arrayDiff');
  24587. // 'zrender/src/core/arrayDiff' has been used before, but it did
  24588. // not do well in performance when roam with fixed dataZoom window.
  24589. function sign$1(val) {
  24590. return val >= 0 ? 1 : -1;
  24591. }
  24592. function getStackedOnPoint(coordSys, data, idx) {
  24593. var baseAxis = coordSys.getBaseAxis();
  24594. var valueAxis = coordSys.getOtherAxis(baseAxis);
  24595. var valueStart = baseAxis.onZero
  24596. ? 0 : valueAxis.scale.getExtent()[0];
  24597. var valueDim = valueAxis.dim;
  24598. var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
  24599. var stackedOnSameSign;
  24600. var stackedOn = data.stackedOn;
  24601. var val = data.get(valueDim, idx);
  24602. // Find first stacked value with same sign
  24603. while (stackedOn &&
  24604. sign$1(stackedOn.get(valueDim, idx)) === sign$1(val)
  24605. ) {
  24606. stackedOnSameSign = stackedOn;
  24607. break;
  24608. }
  24609. var stackedData = [];
  24610. stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
  24611. stackedData[1 - baseDataOffset] = stackedOnSameSign
  24612. ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
  24613. return coordSys.dataToPoint(stackedData);
  24614. }
  24615. // function convertToIntId(newIdList, oldIdList) {
  24616. // // Generate int id instead of string id.
  24617. // // Compare string maybe slow in score function of arrDiff
  24618. // // Assume id in idList are all unique
  24619. // var idIndicesMap = {};
  24620. // var idx = 0;
  24621. // for (var i = 0; i < newIdList.length; i++) {
  24622. // idIndicesMap[newIdList[i]] = idx;
  24623. // newIdList[i] = idx++;
  24624. // }
  24625. // for (var i = 0; i < oldIdList.length; i++) {
  24626. // var oldId = oldIdList[i];
  24627. // // Same with newIdList
  24628. // if (idIndicesMap[oldId]) {
  24629. // oldIdList[i] = idIndicesMap[oldId];
  24630. // }
  24631. // else {
  24632. // oldIdList[i] = idx++;
  24633. // }
  24634. // }
  24635. // }
  24636. function diffData(oldData, newData) {
  24637. var diffResult = [];
  24638. newData.diff(oldData)
  24639. .add(function (idx) {
  24640. diffResult.push({cmd: '+', idx: idx});
  24641. })
  24642. .update(function (newIdx, oldIdx) {
  24643. diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx});
  24644. })
  24645. .remove(function (idx) {
  24646. diffResult.push({cmd: '-', idx: idx});
  24647. })
  24648. .execute();
  24649. return diffResult;
  24650. }
  24651. var lineAnimationDiff = function (
  24652. oldData, newData,
  24653. oldStackedOnPoints, newStackedOnPoints,
  24654. oldCoordSys, newCoordSys
  24655. ) {
  24656. var diff = diffData(oldData, newData);
  24657. // var newIdList = newData.mapArray(newData.getId);
  24658. // var oldIdList = oldData.mapArray(oldData.getId);
  24659. // convertToIntId(newIdList, oldIdList);
  24660. // // FIXME One data ?
  24661. // diff = arrayDiff(oldIdList, newIdList);
  24662. var currPoints = [];
  24663. var nextPoints = [];
  24664. // Points for stacking base line
  24665. var currStackedPoints = [];
  24666. var nextStackedPoints = [];
  24667. var status = [];
  24668. var sortedIndices = [];
  24669. var rawIndices = [];
  24670. var dims = newCoordSys.dimensions;
  24671. for (var i = 0; i < diff.length; i++) {
  24672. var diffItem = diff[i];
  24673. var pointAdded = true;
  24674. // FIXME, animation is not so perfect when dataZoom window moves fast
  24675. // Which is in case remvoing or add more than one data in the tail or head
  24676. switch (diffItem.cmd) {
  24677. case '=':
  24678. var currentPt = oldData.getItemLayout(diffItem.idx);
  24679. var nextPt = newData.getItemLayout(diffItem.idx1);
  24680. // If previous data is NaN, use next point directly
  24681. if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {
  24682. currentPt = nextPt.slice();
  24683. }
  24684. currPoints.push(currentPt);
  24685. nextPoints.push(nextPt);
  24686. currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);
  24687. nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);
  24688. rawIndices.push(newData.getRawIndex(diffItem.idx1));
  24689. break;
  24690. case '+':
  24691. var idx = diffItem.idx;
  24692. currPoints.push(
  24693. oldCoordSys.dataToPoint([
  24694. newData.get(dims[0], idx, true), newData.get(dims[1], idx, true)
  24695. ])
  24696. );
  24697. nextPoints.push(newData.getItemLayout(idx).slice());
  24698. currStackedPoints.push(
  24699. getStackedOnPoint(oldCoordSys, newData, idx)
  24700. );
  24701. nextStackedPoints.push(newStackedOnPoints[idx]);
  24702. rawIndices.push(newData.getRawIndex(idx));
  24703. break;
  24704. case '-':
  24705. var idx = diffItem.idx;
  24706. var rawIndex = oldData.getRawIndex(idx);
  24707. // Data is replaced. In the case of dynamic data queue
  24708. // FIXME FIXME FIXME
  24709. if (rawIndex !== idx) {
  24710. currPoints.push(oldData.getItemLayout(idx));
  24711. nextPoints.push(newCoordSys.dataToPoint([
  24712. oldData.get(dims[0], idx, true), oldData.get(dims[1], idx, true)
  24713. ]));
  24714. currStackedPoints.push(oldStackedOnPoints[idx]);
  24715. nextStackedPoints.push(
  24716. getStackedOnPoint(
  24717. newCoordSys, oldData, idx
  24718. )
  24719. );
  24720. rawIndices.push(rawIndex);
  24721. }
  24722. else {
  24723. pointAdded = false;
  24724. }
  24725. }
  24726. // Original indices
  24727. if (pointAdded) {
  24728. status.push(diffItem);
  24729. sortedIndices.push(sortedIndices.length);
  24730. }
  24731. }
  24732. // Diff result may be crossed if all items are changed
  24733. // Sort by data index
  24734. sortedIndices.sort(function (a, b) {
  24735. return rawIndices[a] - rawIndices[b];
  24736. });
  24737. var sortedCurrPoints = [];
  24738. var sortedNextPoints = [];
  24739. var sortedCurrStackedPoints = [];
  24740. var sortedNextStackedPoints = [];
  24741. var sortedStatus = [];
  24742. for (var i = 0; i < sortedIndices.length; i++) {
  24743. var idx = sortedIndices[i];
  24744. sortedCurrPoints[i] = currPoints[idx];
  24745. sortedNextPoints[i] = nextPoints[idx];
  24746. sortedCurrStackedPoints[i] = currStackedPoints[idx];
  24747. sortedNextStackedPoints[i] = nextStackedPoints[idx];
  24748. sortedStatus[i] = status[idx];
  24749. }
  24750. return {
  24751. current: sortedCurrPoints,
  24752. next: sortedNextPoints,
  24753. stackedOnCurrent: sortedCurrStackedPoints,
  24754. stackedOnNext: sortedNextStackedPoints,
  24755. status: sortedStatus
  24756. };
  24757. };
  24758. // Poly path support NaN point
  24759. var vec2Min = min;
  24760. var vec2Max = max;
  24761. var scaleAndAdd$1 = scaleAndAdd;
  24762. var v2Copy = copy;
  24763. // Temporary variable
  24764. var v = [];
  24765. var cp0 = [];
  24766. var cp1 = [];
  24767. function isPointNull(p) {
  24768. return isNaN(p[0]) || isNaN(p[1]);
  24769. }
  24770. function drawSegment(
  24771. ctx, points, start, segLen, allLen,
  24772. dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
  24773. ) {
  24774. var prevIdx = 0;
  24775. var idx = start;
  24776. for (var k = 0; k < segLen; k++) {
  24777. var p = points[idx];
  24778. if (idx >= allLen || idx < 0) {
  24779. break;
  24780. }
  24781. if (isPointNull(p)) {
  24782. if (connectNulls) {
  24783. idx += dir;
  24784. continue;
  24785. }
  24786. break;
  24787. }
  24788. if (idx === start) {
  24789. ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
  24790. v2Copy(cp0, p);
  24791. }
  24792. else {
  24793. if (smooth > 0) {
  24794. var nextIdx = idx + dir;
  24795. var nextP = points[nextIdx];
  24796. if (connectNulls) {
  24797. // Find next point not null
  24798. while (nextP && isPointNull(points[nextIdx])) {
  24799. nextIdx += dir;
  24800. nextP = points[nextIdx];
  24801. }
  24802. }
  24803. var ratioNextSeg = 0.5;
  24804. var prevP = points[prevIdx];
  24805. var nextP = points[nextIdx];
  24806. // Last point
  24807. if (!nextP || isPointNull(nextP)) {
  24808. v2Copy(cp1, p);
  24809. }
  24810. else {
  24811. // If next data is null in not connect case
  24812. if (isPointNull(nextP) && !connectNulls) {
  24813. nextP = p;
  24814. }
  24815. sub(v, nextP, prevP);
  24816. var lenPrevSeg;
  24817. var lenNextSeg;
  24818. if (smoothMonotone === 'x' || smoothMonotone === 'y') {
  24819. var dim = smoothMonotone === 'x' ? 0 : 1;
  24820. lenPrevSeg = Math.abs(p[dim] - prevP[dim]);
  24821. lenNextSeg = Math.abs(p[dim] - nextP[dim]);
  24822. }
  24823. else {
  24824. lenPrevSeg = dist(p, prevP);
  24825. lenNextSeg = dist(p, nextP);
  24826. }
  24827. // Use ratio of seg length
  24828. ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
  24829. scaleAndAdd$1(cp1, p, v, -smooth * (1 - ratioNextSeg));
  24830. }
  24831. // Smooth constraint
  24832. vec2Min(cp0, cp0, smoothMax);
  24833. vec2Max(cp0, cp0, smoothMin);
  24834. vec2Min(cp1, cp1, smoothMax);
  24835. vec2Max(cp1, cp1, smoothMin);
  24836. ctx.bezierCurveTo(
  24837. cp0[0], cp0[1],
  24838. cp1[0], cp1[1],
  24839. p[0], p[1]
  24840. );
  24841. // cp0 of next segment
  24842. scaleAndAdd$1(cp0, p, v, smooth * ratioNextSeg);
  24843. }
  24844. else {
  24845. ctx.lineTo(p[0], p[1]);
  24846. }
  24847. }
  24848. prevIdx = idx;
  24849. idx += dir;
  24850. }
  24851. return k;
  24852. }
  24853. function getBoundingBox(points, smoothConstraint) {
  24854. var ptMin = [Infinity, Infinity];
  24855. var ptMax = [-Infinity, -Infinity];
  24856. if (smoothConstraint) {
  24857. for (var i = 0; i < points.length; i++) {
  24858. var pt = points[i];
  24859. if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; }
  24860. if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; }
  24861. if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; }
  24862. if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; }
  24863. }
  24864. }
  24865. return {
  24866. min: smoothConstraint ? ptMin : ptMax,
  24867. max: smoothConstraint ? ptMax : ptMin
  24868. };
  24869. }
  24870. var Polyline$1 = Path.extend({
  24871. type: 'ec-polyline',
  24872. shape: {
  24873. points: [],
  24874. smooth: 0,
  24875. smoothConstraint: true,
  24876. smoothMonotone: null,
  24877. connectNulls: false
  24878. },
  24879. style: {
  24880. fill: null,
  24881. stroke: '#000'
  24882. },
  24883. brush: fixClipWithShadow(Path.prototype.brush),
  24884. buildPath: function (ctx, shape) {
  24885. var points = shape.points;
  24886. var i = 0;
  24887. var len$$1 = points.length;
  24888. var result = getBoundingBox(points, shape.smoothConstraint);
  24889. if (shape.connectNulls) {
  24890. // Must remove first and last null values avoid draw error in polygon
  24891. for (; len$$1 > 0; len$$1--) {
  24892. if (!isPointNull(points[len$$1 - 1])) {
  24893. break;
  24894. }
  24895. }
  24896. for (; i < len$$1; i++) {
  24897. if (!isPointNull(points[i])) {
  24898. break;
  24899. }
  24900. }
  24901. }
  24902. while (i < len$$1) {
  24903. i += drawSegment(
  24904. ctx, points, i, len$$1, len$$1,
  24905. 1, result.min, result.max, shape.smooth,
  24906. shape.smoothMonotone, shape.connectNulls
  24907. ) + 1;
  24908. }
  24909. }
  24910. });
  24911. var Polygon$1 = Path.extend({
  24912. type: 'ec-polygon',
  24913. shape: {
  24914. points: [],
  24915. // Offset between stacked base points and points
  24916. stackedOnPoints: [],
  24917. smooth: 0,
  24918. stackedOnSmooth: 0,
  24919. smoothConstraint: true,
  24920. smoothMonotone: null,
  24921. connectNulls: false
  24922. },
  24923. brush: fixClipWithShadow(Path.prototype.brush),
  24924. buildPath: function (ctx, shape) {
  24925. var points = shape.points;
  24926. var stackedOnPoints = shape.stackedOnPoints;
  24927. var i = 0;
  24928. var len$$1 = points.length;
  24929. var smoothMonotone = shape.smoothMonotone;
  24930. var bbox = getBoundingBox(points, shape.smoothConstraint);
  24931. var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint);
  24932. if (shape.connectNulls) {
  24933. // Must remove first and last null values avoid draw error in polygon
  24934. for (; len$$1 > 0; len$$1--) {
  24935. if (!isPointNull(points[len$$1 - 1])) {
  24936. break;
  24937. }
  24938. }
  24939. for (; i < len$$1; i++) {
  24940. if (!isPointNull(points[i])) {
  24941. break;
  24942. }
  24943. }
  24944. }
  24945. while (i < len$$1) {
  24946. var k = drawSegment(
  24947. ctx, points, i, len$$1, len$$1,
  24948. 1, bbox.min, bbox.max, shape.smooth,
  24949. smoothMonotone, shape.connectNulls
  24950. );
  24951. drawSegment(
  24952. ctx, stackedOnPoints, i + k - 1, k, len$$1,
  24953. -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth,
  24954. smoothMonotone, shape.connectNulls
  24955. );
  24956. i += k + 1;
  24957. ctx.closePath();
  24958. }
  24959. }
  24960. });
  24961. // FIXME step not support polar
  24962. function isPointsSame(points1, points2) {
  24963. if (points1.length !== points2.length) {
  24964. return;
  24965. }
  24966. for (var i = 0; i < points1.length; i++) {
  24967. var p1 = points1[i];
  24968. var p2 = points2[i];
  24969. if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
  24970. return;
  24971. }
  24972. }
  24973. return true;
  24974. }
  24975. function getSmooth(smooth) {
  24976. return typeof (smooth) === 'number' ? smooth : (smooth ? 0.3 : 0);
  24977. }
  24978. function getAxisExtentWithGap(axis) {
  24979. var extent = axis.getGlobalExtent();
  24980. if (axis.onBand) {
  24981. // Remove extra 1px to avoid line miter in clipped edge
  24982. var halfBandWidth = axis.getBandWidth() / 2 - 1;
  24983. var dir = extent[1] > extent[0] ? 1 : -1;
  24984. extent[0] += dir * halfBandWidth;
  24985. extent[1] -= dir * halfBandWidth;
  24986. }
  24987. return extent;
  24988. }
  24989. function sign(val) {
  24990. return val >= 0 ? 1 : -1;
  24991. }
  24992. /**
  24993. * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys
  24994. * @param {module:echarts/data/List} data
  24995. * @param {Array.<Array.<number>>} points
  24996. * @param {string} origin origin of areaStyle. Valid values: 'auto', 'start',
  24997. * 'end'.
  24998. * auto: from axisLine to data
  24999. * start: from min to data
  25000. * end: from data to max
  25001. * @private
  25002. */
  25003. function getStackedOnPoints(seriesModel, coordSys, data, origin) {
  25004. var baseAxis = coordSys.getBaseAxis();
  25005. var valueAxis = coordSys.getOtherAxis(baseAxis);
  25006. var valueStart = 0;
  25007. var extent = valueAxis.scale.getExtent();
  25008. if (origin === 'start') {
  25009. valueStart = extent[0];
  25010. }
  25011. else if (origin === 'end') {
  25012. valueStart = extent[1];
  25013. }
  25014. else {
  25015. // auto
  25016. var extent = valueAxis.scale.getExtent();
  25017. if (extent[0] > 0) {
  25018. // Both positive
  25019. valueStart = extent[0];
  25020. }
  25021. else if (extent[1] < 0) {
  25022. // Both negative
  25023. valueStart = extent[1];
  25024. }
  25025. // If is one positive, and one negative, onZero shall be true
  25026. }
  25027. var valueCoordDim = valueAxis.dim;
  25028. var baseDataOffset = valueCoordDim === 'x' || valueCoordDim === 'radius' ? 1 : 0;
  25029. var valueDim = data.mapDimension(valueCoordDim);
  25030. return data.mapArray(valueDim ? [valueDim] : [], function (val, idx) {
  25031. var stackedOnSameSign;
  25032. var stackedOn = data.stackedOn;
  25033. // Find first stacked value with same sign
  25034. while (stackedOn &&
  25035. sign(stackedOn.get(valueDim, idx)) === sign(val)
  25036. ) {
  25037. stackedOnSameSign = stackedOn;
  25038. break;
  25039. }
  25040. var stackedData = [];
  25041. stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
  25042. stackedData[1 - baseDataOffset] = stackedOnSameSign
  25043. ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
  25044. return coordSys.dataToPoint(stackedData);
  25045. }, true);
  25046. }
  25047. function createGridClipShape(cartesian, hasAnimation, seriesModel) {
  25048. var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
  25049. var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));
  25050. var isHorizontal = cartesian.getBaseAxis().isHorizontal();
  25051. var x = Math.min(xExtent[0], xExtent[1]);
  25052. var y = Math.min(yExtent[0], yExtent[1]);
  25053. var width = Math.max(xExtent[0], xExtent[1]) - x;
  25054. var height = Math.max(yExtent[0], yExtent[1]) - y;
  25055. var lineWidth = seriesModel.get('lineStyle.width') || 2;
  25056. // Expand clip shape to avoid clipping when line value exceeds axis
  25057. var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height);
  25058. if (isHorizontal) {
  25059. y -= expandSize;
  25060. height += expandSize * 2;
  25061. }
  25062. else {
  25063. x -= expandSize;
  25064. width += expandSize * 2;
  25065. }
  25066. var clipPath = new Rect({
  25067. shape: {
  25068. x: x,
  25069. y: y,
  25070. width: width,
  25071. height: height
  25072. }
  25073. });
  25074. if (hasAnimation) {
  25075. clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;
  25076. initProps(clipPath, {
  25077. shape: {
  25078. width: width,
  25079. height: height
  25080. }
  25081. }, seriesModel);
  25082. }
  25083. return clipPath;
  25084. }
  25085. function createPolarClipShape(polar, hasAnimation, seriesModel) {
  25086. var angleAxis = polar.getAngleAxis();
  25087. var radiusAxis = polar.getRadiusAxis();
  25088. var radiusExtent = radiusAxis.getExtent();
  25089. var angleExtent = angleAxis.getExtent();
  25090. var RADIAN = Math.PI / 180;
  25091. var clipPath = new Sector({
  25092. shape: {
  25093. cx: polar.cx,
  25094. cy: polar.cy,
  25095. r0: radiusExtent[0],
  25096. r: radiusExtent[1],
  25097. startAngle: -angleExtent[0] * RADIAN,
  25098. endAngle: -angleExtent[1] * RADIAN,
  25099. clockwise: angleAxis.inverse
  25100. }
  25101. });
  25102. if (hasAnimation) {
  25103. clipPath.shape.endAngle = -angleExtent[0] * RADIAN;
  25104. initProps(clipPath, {
  25105. shape: {
  25106. endAngle: -angleExtent[1] * RADIAN
  25107. }
  25108. }, seriesModel);
  25109. }
  25110. return clipPath;
  25111. }
  25112. function createClipShape(coordSys, hasAnimation, seriesModel) {
  25113. return coordSys.type === 'polar'
  25114. ? createPolarClipShape(coordSys, hasAnimation, seriesModel)
  25115. : createGridClipShape(coordSys, hasAnimation, seriesModel);
  25116. }
  25117. function turnPointsIntoStep(points, coordSys, stepTurnAt) {
  25118. var baseAxis = coordSys.getBaseAxis();
  25119. var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;
  25120. var stepPoints = [];
  25121. for (var i = 0; i < points.length - 1; i++) {
  25122. var nextPt = points[i + 1];
  25123. var pt = points[i];
  25124. stepPoints.push(pt);
  25125. var stepPt = [];
  25126. switch (stepTurnAt) {
  25127. case 'end':
  25128. stepPt[baseIndex] = nextPt[baseIndex];
  25129. stepPt[1 - baseIndex] = pt[1 - baseIndex];
  25130. // default is start
  25131. stepPoints.push(stepPt);
  25132. break;
  25133. case 'middle':
  25134. // default is start
  25135. var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;
  25136. var stepPt2 = [];
  25137. stepPt[baseIndex] = stepPt2[baseIndex] = middle;
  25138. stepPt[1 - baseIndex] = pt[1 - baseIndex];
  25139. stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];
  25140. stepPoints.push(stepPt);
  25141. stepPoints.push(stepPt2);
  25142. break;
  25143. default:
  25144. stepPt[baseIndex] = pt[baseIndex];
  25145. stepPt[1 - baseIndex] = nextPt[1 - baseIndex];
  25146. // default is start
  25147. stepPoints.push(stepPt);
  25148. }
  25149. }
  25150. // Last points
  25151. points[i] && stepPoints.push(points[i]);
  25152. return stepPoints;
  25153. }
  25154. function getVisualGradient(data, coordSys) {
  25155. var visualMetaList = data.getVisual('visualMeta');
  25156. if (!visualMetaList || !visualMetaList.length || !data.count()) {
  25157. // When data.count() is 0, gradient range can not be calculated.
  25158. return;
  25159. }
  25160. var visualMeta;
  25161. for (var i = visualMetaList.length - 1; i >= 0; i--) {
  25162. // Can only be x or y
  25163. if (visualMetaList[i].dimension < 2) {
  25164. visualMeta = visualMetaList[i];
  25165. break;
  25166. }
  25167. }
  25168. if (!visualMeta || coordSys.type !== 'cartesian2d') {
  25169. if (__DEV__) {
  25170. console.warn('Visual map on line style only support x or y dimension.');
  25171. }
  25172. return;
  25173. }
  25174. // If the area to be rendered is bigger than area defined by LinearGradient,
  25175. // the canvas spec prescribes that the color of the first stop and the last
  25176. // stop should be used. But if two stops are added at offset 0, in effect
  25177. // browsers use the color of the second stop to render area outside
  25178. // LinearGradient. So we can only infinitesimally extend area defined in
  25179. // LinearGradient to render `outerColors`.
  25180. var dimension = visualMeta.dimension;
  25181. var dimName = data.dimensions[dimension];
  25182. var axis = coordSys.getAxis(dimName);
  25183. // dataToCoor mapping may not be linear, but must be monotonic.
  25184. var colorStops = map(visualMeta.stops, function (stop) {
  25185. return {
  25186. coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
  25187. color: stop.color
  25188. };
  25189. });
  25190. var stopLen = colorStops.length;
  25191. var outerColors = visualMeta.outerColors.slice();
  25192. if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {
  25193. colorStops.reverse();
  25194. outerColors.reverse();
  25195. }
  25196. var tinyExtent = 10; // Arbitrary value: 10px
  25197. var minCoord = colorStops[0].coord - tinyExtent;
  25198. var maxCoord = colorStops[stopLen - 1].coord + tinyExtent;
  25199. var coordSpan = maxCoord - minCoord;
  25200. if (coordSpan < 1e-3) {
  25201. return 'transparent';
  25202. }
  25203. each$1(colorStops, function (stop) {
  25204. stop.offset = (stop.coord - minCoord) / coordSpan;
  25205. });
  25206. colorStops.push({
  25207. offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,
  25208. color: outerColors[1] || 'transparent'
  25209. });
  25210. colorStops.unshift({ // notice colorStops.length have been changed.
  25211. offset: stopLen ? colorStops[0].offset : 0.5,
  25212. color: outerColors[0] || 'transparent'
  25213. });
  25214. // zrUtil.each(colorStops, function (colorStop) {
  25215. // // Make sure each offset has rounded px to avoid not sharp edge
  25216. // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);
  25217. // });
  25218. var gradient = new LinearGradient(0, 0, 0, 0, colorStops, true);
  25219. gradient[dimName] = minCoord;
  25220. gradient[dimName + '2'] = maxCoord;
  25221. return gradient;
  25222. }
  25223. Chart.extend({
  25224. type: 'line',
  25225. init: function () {
  25226. var lineGroup = new Group();
  25227. var symbolDraw = new SymbolDraw();
  25228. this.group.add(symbolDraw.group);
  25229. this._symbolDraw = symbolDraw;
  25230. this._lineGroup = lineGroup;
  25231. },
  25232. render: function (seriesModel, ecModel, api) {
  25233. var coordSys = seriesModel.coordinateSystem;
  25234. var group = this.group;
  25235. var data = seriesModel.getData();
  25236. var lineStyleModel = seriesModel.getModel('lineStyle');
  25237. var areaStyleModel = seriesModel.getModel('areaStyle');
  25238. var points = data.mapArray(data.getItemLayout, true);
  25239. var isCoordSysPolar = coordSys.type === 'polar';
  25240. var prevCoordSys = this._coordSys;
  25241. var symbolDraw = this._symbolDraw;
  25242. var polyline = this._polyline;
  25243. var polygon = this._polygon;
  25244. var lineGroup = this._lineGroup;
  25245. var hasAnimation = seriesModel.get('animation');
  25246. var isAreaChart = !areaStyleModel.isEmpty();
  25247. var origin = areaStyleModel.get('origin');
  25248. var stackedOnPoints = getStackedOnPoints(seriesModel, coordSys, data, origin);
  25249. var showSymbol = seriesModel.get('showSymbol');
  25250. var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol')
  25251. && this._getSymbolIgnoreFunc(data, coordSys);
  25252. // Remove temporary symbols
  25253. var oldData = this._data;
  25254. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  25255. if (el.__temp) {
  25256. group.remove(el);
  25257. oldData.setItemGraphicEl(idx, null);
  25258. }
  25259. });
  25260. // Remove previous created symbols if showSymbol changed to false
  25261. if (!showSymbol) {
  25262. symbolDraw.remove();
  25263. }
  25264. group.add(lineGroup);
  25265. // FIXME step not support polar
  25266. var step = !isCoordSysPolar && seriesModel.get('step');
  25267. // Initialization animation or coordinate system changed
  25268. if (
  25269. !(polyline && prevCoordSys.type === coordSys.type && step === this._step)
  25270. ) {
  25271. showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
  25272. if (step) {
  25273. // TODO If stacked series is not step
  25274. points = turnPointsIntoStep(points, coordSys, step);
  25275. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  25276. }
  25277. polyline = this._newPolyline(points, coordSys, hasAnimation);
  25278. if (isAreaChart) {
  25279. polygon = this._newPolygon(
  25280. points, stackedOnPoints,
  25281. coordSys, hasAnimation
  25282. );
  25283. }
  25284. lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel));
  25285. }
  25286. else {
  25287. if (isAreaChart && !polygon) {
  25288. // If areaStyle is added
  25289. polygon = this._newPolygon(
  25290. points, stackedOnPoints,
  25291. coordSys, hasAnimation
  25292. );
  25293. }
  25294. else if (polygon && !isAreaChart) {
  25295. // If areaStyle is removed
  25296. lineGroup.remove(polygon);
  25297. polygon = this._polygon = null;
  25298. }
  25299. // Update clipPath
  25300. lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel));
  25301. // Always update, or it is wrong in the case turning on legend
  25302. // because points are not changed
  25303. showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
  25304. // Stop symbol animation and sync with line points
  25305. // FIXME performance?
  25306. data.eachItemGraphicEl(function (el) {
  25307. el.stopAnimation(true);
  25308. });
  25309. // In the case data zoom triggerred refreshing frequently
  25310. // Data may not change if line has a category axis. So it should animate nothing
  25311. if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)
  25312. || !isPointsSame(this._points, points)
  25313. ) {
  25314. if (hasAnimation) {
  25315. this._updateAnimation(
  25316. data, stackedOnPoints, coordSys, api, step
  25317. );
  25318. }
  25319. else {
  25320. // Not do it in update with animation
  25321. if (step) {
  25322. // TODO If stacked series is not step
  25323. points = turnPointsIntoStep(points, coordSys, step);
  25324. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  25325. }
  25326. polyline.setShape({
  25327. points: points
  25328. });
  25329. polygon && polygon.setShape({
  25330. points: points,
  25331. stackedOnPoints: stackedOnPoints
  25332. });
  25333. }
  25334. }
  25335. }
  25336. var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color');
  25337. polyline.useStyle(defaults(
  25338. // Use color in lineStyle first
  25339. lineStyleModel.getLineStyle(),
  25340. {
  25341. fill: 'none',
  25342. stroke: visualColor,
  25343. lineJoin: 'bevel'
  25344. }
  25345. ));
  25346. var smooth = seriesModel.get('smooth');
  25347. smooth = getSmooth(seriesModel.get('smooth'));
  25348. polyline.setShape({
  25349. smooth: smooth,
  25350. smoothMonotone: seriesModel.get('smoothMonotone'),
  25351. connectNulls: seriesModel.get('connectNulls')
  25352. });
  25353. if (polygon) {
  25354. var stackedOn = data.stackedOn;
  25355. var stackedOnSmooth = 0;
  25356. polygon.useStyle(defaults(
  25357. areaStyleModel.getAreaStyle(),
  25358. {
  25359. fill: visualColor,
  25360. opacity: 0.7,
  25361. lineJoin: 'bevel'
  25362. }
  25363. ));
  25364. if (stackedOn) {
  25365. var stackedOnSeries = stackedOn.hostModel;
  25366. stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
  25367. }
  25368. polygon.setShape({
  25369. smooth: smooth,
  25370. stackedOnSmooth: stackedOnSmooth,
  25371. smoothMonotone: seriesModel.get('smoothMonotone'),
  25372. connectNulls: seriesModel.get('connectNulls')
  25373. });
  25374. }
  25375. this._data = data;
  25376. // Save the coordinate system for transition animation when data changed
  25377. this._coordSys = coordSys;
  25378. this._stackedOnPoints = stackedOnPoints;
  25379. this._points = points;
  25380. this._step = step;
  25381. },
  25382. dispose: function () {},
  25383. highlight: function (seriesModel, ecModel, api, payload) {
  25384. var data = seriesModel.getData();
  25385. var dataIndex = queryDataIndex(data, payload);
  25386. if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {
  25387. var symbol = data.getItemGraphicEl(dataIndex);
  25388. if (!symbol) {
  25389. // Create a temporary symbol if it is not exists
  25390. var pt = data.getItemLayout(dataIndex);
  25391. if (!pt) {
  25392. // Null data
  25393. return;
  25394. }
  25395. symbol = new SymbolClz(data, dataIndex);
  25396. symbol.position = pt;
  25397. symbol.setZ(
  25398. seriesModel.get('zlevel'),
  25399. seriesModel.get('z')
  25400. );
  25401. symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
  25402. symbol.__temp = true;
  25403. data.setItemGraphicEl(dataIndex, symbol);
  25404. // Stop scale animation
  25405. symbol.stopSymbolAnimation(true);
  25406. this.group.add(symbol);
  25407. }
  25408. symbol.highlight();
  25409. }
  25410. else {
  25411. // Highlight whole series
  25412. Chart.prototype.highlight.call(
  25413. this, seriesModel, ecModel, api, payload
  25414. );
  25415. }
  25416. },
  25417. downplay: function (seriesModel, ecModel, api, payload) {
  25418. var data = seriesModel.getData();
  25419. var dataIndex = queryDataIndex(data, payload);
  25420. if (dataIndex != null && dataIndex >= 0) {
  25421. var symbol = data.getItemGraphicEl(dataIndex);
  25422. if (symbol) {
  25423. if (symbol.__temp) {
  25424. data.setItemGraphicEl(dataIndex, null);
  25425. this.group.remove(symbol);
  25426. }
  25427. else {
  25428. symbol.downplay();
  25429. }
  25430. }
  25431. }
  25432. else {
  25433. // FIXME
  25434. // can not downplay completely.
  25435. // Downplay whole series
  25436. Chart.prototype.downplay.call(
  25437. this, seriesModel, ecModel, api, payload
  25438. );
  25439. }
  25440. },
  25441. /**
  25442. * @param {module:zrender/container/Group} group
  25443. * @param {Array.<Array.<number>>} points
  25444. * @private
  25445. */
  25446. _newPolyline: function (points) {
  25447. var polyline = this._polyline;
  25448. // Remove previous created polyline
  25449. if (polyline) {
  25450. this._lineGroup.remove(polyline);
  25451. }
  25452. polyline = new Polyline$1({
  25453. shape: {
  25454. points: points
  25455. },
  25456. silent: true,
  25457. z2: 10
  25458. });
  25459. this._lineGroup.add(polyline);
  25460. this._polyline = polyline;
  25461. return polyline;
  25462. },
  25463. /**
  25464. * @param {module:zrender/container/Group} group
  25465. * @param {Array.<Array.<number>>} stackedOnPoints
  25466. * @param {Array.<Array.<number>>} points
  25467. * @private
  25468. */
  25469. _newPolygon: function (points, stackedOnPoints) {
  25470. var polygon = this._polygon;
  25471. // Remove previous created polygon
  25472. if (polygon) {
  25473. this._lineGroup.remove(polygon);
  25474. }
  25475. polygon = new Polygon$1({
  25476. shape: {
  25477. points: points,
  25478. stackedOnPoints: stackedOnPoints
  25479. },
  25480. silent: true
  25481. });
  25482. this._lineGroup.add(polygon);
  25483. this._polygon = polygon;
  25484. return polygon;
  25485. },
  25486. /**
  25487. * @private
  25488. */
  25489. _getSymbolIgnoreFunc: function (data, coordSys) {
  25490. var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
  25491. // `getLabelInterval` is provided by echarts/component/axis
  25492. if (categoryAxis && categoryAxis.isLabelIgnored) {
  25493. return bind(categoryAxis.isLabelIgnored, categoryAxis);
  25494. }
  25495. },
  25496. /**
  25497. * @private
  25498. */
  25499. // FIXME Two value axis
  25500. _updateAnimation: function (data, stackedOnPoints, coordSys, api, step) {
  25501. var polyline = this._polyline;
  25502. var polygon = this._polygon;
  25503. var seriesModel = data.hostModel;
  25504. var diff = lineAnimationDiff(
  25505. this._data, data,
  25506. this._stackedOnPoints, stackedOnPoints,
  25507. this._coordSys, coordSys
  25508. );
  25509. var current = diff.current;
  25510. var stackedOnCurrent = diff.stackedOnCurrent;
  25511. var next = diff.next;
  25512. var stackedOnNext = diff.stackedOnNext;
  25513. if (step) {
  25514. // TODO If stacked series is not step
  25515. current = turnPointsIntoStep(diff.current, coordSys, step);
  25516. stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step);
  25517. next = turnPointsIntoStep(diff.next, coordSys, step);
  25518. stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);
  25519. }
  25520. // `diff.current` is subset of `current` (which should be ensured by
  25521. // turnPointsIntoStep), so points in `__points` can be updated when
  25522. // points in `current` are update during animation.
  25523. polyline.shape.__points = diff.current;
  25524. polyline.shape.points = current;
  25525. updateProps(polyline, {
  25526. shape: {
  25527. points: next
  25528. }
  25529. }, seriesModel);
  25530. if (polygon) {
  25531. polygon.setShape({
  25532. points: current,
  25533. stackedOnPoints: stackedOnCurrent
  25534. });
  25535. updateProps(polygon, {
  25536. shape: {
  25537. points: next,
  25538. stackedOnPoints: stackedOnNext
  25539. }
  25540. }, seriesModel);
  25541. }
  25542. var updatedDataInfo = [];
  25543. var diffStatus = diff.status;
  25544. for (var i = 0; i < diffStatus.length; i++) {
  25545. var cmd = diffStatus[i].cmd;
  25546. if (cmd === '=') {
  25547. var el = data.getItemGraphicEl(diffStatus[i].idx1);
  25548. if (el) {
  25549. updatedDataInfo.push({
  25550. el: el,
  25551. ptIdx: i // Index of points
  25552. });
  25553. }
  25554. }
  25555. }
  25556. if (polyline.animators && polyline.animators.length) {
  25557. polyline.animators[0].during(function () {
  25558. for (var i = 0; i < updatedDataInfo.length; i++) {
  25559. var el = updatedDataInfo[i].el;
  25560. el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]);
  25561. }
  25562. });
  25563. }
  25564. },
  25565. remove: function (ecModel) {
  25566. var group = this.group;
  25567. var oldData = this._data;
  25568. this._lineGroup.removeAll();
  25569. this._symbolDraw.remove(true);
  25570. // Remove temporary created elements when highlighting
  25571. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  25572. if (el.__temp) {
  25573. group.remove(el);
  25574. oldData.setItemGraphicEl(idx, null);
  25575. }
  25576. });
  25577. this._polyline =
  25578. this._polygon =
  25579. this._coordSys =
  25580. this._points =
  25581. this._stackedOnPoints =
  25582. this._data = null;
  25583. }
  25584. });
  25585. var visualSymbol = function (seriesType, defaultSymbolType, legendSymbol) {
  25586. // Encoding visual for all series include which is filtered for legend drawing
  25587. return {
  25588. seriesType: seriesType,
  25589. performRawSeries: true,
  25590. reset: function (seriesModel, ecModel, api) {
  25591. var data = seriesModel.getData();
  25592. var symbolType = seriesModel.get('symbol') || defaultSymbolType;
  25593. var symbolSize = seriesModel.get('symbolSize');
  25594. data.setVisual({
  25595. legendSymbol: legendSymbol || symbolType,
  25596. symbol: symbolType,
  25597. symbolSize: symbolSize
  25598. });
  25599. // Only visible series has each data be visual encoded
  25600. if (ecModel.isSeriesFiltered(seriesModel)) {
  25601. return;
  25602. }
  25603. var hasCallback = typeof symbolSize === 'function';
  25604. function dataEach(data, idx) {
  25605. if (typeof symbolSize === 'function') {
  25606. var rawValue = seriesModel.getRawValue(idx);
  25607. // FIXME
  25608. var params = seriesModel.getDataParams(idx);
  25609. data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
  25610. }
  25611. if (data.hasItemOption) {
  25612. var itemModel = data.getItemModel(idx);
  25613. var itemSymbolType = itemModel.getShallow('symbol', true);
  25614. var itemSymbolSize = itemModel.getShallow('symbolSize', true);
  25615. // If has item symbol
  25616. if (itemSymbolType != null) {
  25617. data.setItemVisual(idx, 'symbol', itemSymbolType);
  25618. }
  25619. if (itemSymbolSize != null) {
  25620. // PENDING Transform symbolSize ?
  25621. data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
  25622. }
  25623. }
  25624. }
  25625. return { dataEach: (data.hasItemOption || hasCallback) ? dataEach : null };
  25626. }
  25627. };
  25628. };
  25629. var layoutPoints = function (seriesType) {
  25630. return {
  25631. seriesType: seriesType,
  25632. plan: createRenderPlanner(),
  25633. reset: function (seriesModel) {
  25634. var data = seriesModel.getData();
  25635. var coordSys = seriesModel.coordinateSystem;
  25636. var pipelineContext = seriesModel.pipelineContext;
  25637. var isLargeRender = pipelineContext.large;
  25638. if (!coordSys) {
  25639. return;
  25640. }
  25641. var dims = map(coordSys.dimensions, function (dim) {
  25642. return data.getDimension(data.mapDimension(dim));
  25643. }).slice(0, 2);
  25644. var dimLen = dims.length;
  25645. function progress(params, data) {
  25646. var segCount = params.end - params.start;
  25647. var points = isLargeRender && new Float32Array(segCount * dimLen);
  25648. for (var i = params.start, offset = 0, tmpIn = [], tmpOut = []; i < params.end; i++) {
  25649. var point;
  25650. if (dimLen === 1) {
  25651. var x = data.get(dims[0], i, true);
  25652. point = !isNaN(x) && coordSys.dataToPoint(x, null, tmpOut);
  25653. }
  25654. else {
  25655. var x = tmpIn[0] = data.get(dims[0], i, true);
  25656. var y = tmpIn[1] = data.get(dims[1], i, true);
  25657. // Also {Array.<number>}, not undefined to avoid if...else... statement
  25658. point = !isNaN(x) && !isNaN(y) && coordSys.dataToPoint(tmpIn, null, tmpOut);
  25659. }
  25660. if (isLargeRender) {
  25661. points[offset++] = point ? point[0] : NaN;
  25662. points[offset++] = point ? point[1] : NaN;
  25663. }
  25664. else {
  25665. data.setItemLayout(i, (point && point.slice()) || [NaN, NaN]);
  25666. }
  25667. }
  25668. isLargeRender && data.setLayout('symbolPoints', points);
  25669. }
  25670. return dimLen && {progress: progress};
  25671. }
  25672. };
  25673. };
  25674. var samplers = {
  25675. average: function (frame) {
  25676. var sum = 0;
  25677. var count = 0;
  25678. for (var i = 0; i < frame.length; i++) {
  25679. if (!isNaN(frame[i])) {
  25680. sum += frame[i];
  25681. count++;
  25682. }
  25683. }
  25684. // Return NaN if count is 0
  25685. return count === 0 ? NaN : sum / count;
  25686. },
  25687. sum: function (frame) {
  25688. var sum = 0;
  25689. for (var i = 0; i < frame.length; i++) {
  25690. // Ignore NaN
  25691. sum += frame[i] || 0;
  25692. }
  25693. return sum;
  25694. },
  25695. max: function (frame) {
  25696. var max = -Infinity;
  25697. for (var i = 0; i < frame.length; i++) {
  25698. frame[i] > max && (max = frame[i]);
  25699. }
  25700. return max;
  25701. },
  25702. min: function (frame) {
  25703. var min = Infinity;
  25704. for (var i = 0; i < frame.length; i++) {
  25705. frame[i] < min && (min = frame[i]);
  25706. }
  25707. return min;
  25708. },
  25709. // TODO
  25710. // Median
  25711. nearest: function (frame) {
  25712. return frame[0];
  25713. }
  25714. };
  25715. var indexSampler = function (frame, value) {
  25716. return Math.round(frame.length / 2);
  25717. };
  25718. var dataSample = function (seriesType) {
  25719. return {
  25720. seriesType: seriesType,
  25721. reset: function (seriesModel, ecModel, api) {
  25722. var data = seriesModel.getData();
  25723. var sampling = seriesModel.get('sampling');
  25724. var coordSys = seriesModel.coordinateSystem;
  25725. // Only cartesian2d support down sampling
  25726. if (coordSys.type === 'cartesian2d' && sampling) {
  25727. var baseAxis = coordSys.getBaseAxis();
  25728. var valueAxis = coordSys.getOtherAxis(baseAxis);
  25729. var extent = baseAxis.getExtent();
  25730. // Coordinste system has been resized
  25731. var size = extent[1] - extent[0];
  25732. var rate = Math.round(data.count() / size);
  25733. if (rate > 1) {
  25734. var sampler;
  25735. if (typeof sampling === 'string') {
  25736. sampler = samplers[sampling];
  25737. }
  25738. else if (typeof sampling === 'function') {
  25739. sampler = sampling;
  25740. }
  25741. if (sampler) {
  25742. seriesModel.setData(data.downSample(
  25743. valueAxis.dim, 1 / rate, sampler, indexSampler
  25744. ));
  25745. }
  25746. }
  25747. }
  25748. }
  25749. };
  25750. };
  25751. /**
  25752. * // Scale class management
  25753. * @module echarts/scale/Scale
  25754. */
  25755. /**
  25756. * @param {Object} [setting]
  25757. */
  25758. function Scale(setting) {
  25759. this._setting = setting || {};
  25760. /**
  25761. * Extent
  25762. * @type {Array.<number>}
  25763. * @protected
  25764. */
  25765. this._extent = [Infinity, -Infinity];
  25766. /**
  25767. * Step is calculated in adjustExtent
  25768. * @type {Array.<number>}
  25769. * @protected
  25770. */
  25771. this._interval = 0;
  25772. this.init && this.init.apply(this, arguments);
  25773. }
  25774. /**
  25775. * Parse input val to valid inner number.
  25776. * @param {*} val
  25777. * @return {number}
  25778. */
  25779. Scale.prototype.parse = function (val) {
  25780. // Notice: This would be a trap here, If the implementation
  25781. // of this method depends on extent, and this method is used
  25782. // before extent set (like in dataZoom), it would be wrong.
  25783. // Nevertheless, parse does not depend on extent generally.
  25784. return val;
  25785. };
  25786. Scale.prototype.getSetting = function (name) {
  25787. return this._setting[name];
  25788. };
  25789. Scale.prototype.contain = function (val) {
  25790. var extent = this._extent;
  25791. return val >= extent[0] && val <= extent[1];
  25792. };
  25793. /**
  25794. * Normalize value to linear [0, 1], return 0.5 if extent span is 0
  25795. * @param {number} val
  25796. * @return {number}
  25797. */
  25798. Scale.prototype.normalize = function (val) {
  25799. var extent = this._extent;
  25800. if (extent[1] === extent[0]) {
  25801. return 0.5;
  25802. }
  25803. return (val - extent[0]) / (extent[1] - extent[0]);
  25804. };
  25805. /**
  25806. * Scale normalized value
  25807. * @param {number} val
  25808. * @return {number}
  25809. */
  25810. Scale.prototype.scale = function (val) {
  25811. var extent = this._extent;
  25812. return val * (extent[1] - extent[0]) + extent[0];
  25813. };
  25814. /**
  25815. * Set extent from data
  25816. * @param {Array.<number>} other
  25817. */
  25818. Scale.prototype.unionExtent = function (other) {
  25819. var extent = this._extent;
  25820. other[0] < extent[0] && (extent[0] = other[0]);
  25821. other[1] > extent[1] && (extent[1] = other[1]);
  25822. // not setExtent because in log axis it may transformed to power
  25823. // this.setExtent(extent[0], extent[1]);
  25824. };
  25825. /**
  25826. * Set extent from data
  25827. * @param {module:echarts/data/List} data
  25828. * @param {string} dim
  25829. */
  25830. Scale.prototype.unionExtentFromData = function (data, dim) {
  25831. this.unionExtent(data.getApproximateExtent(dim, true));
  25832. };
  25833. /**
  25834. * Get extent
  25835. * @return {Array.<number>}
  25836. */
  25837. Scale.prototype.getExtent = function () {
  25838. return this._extent.slice();
  25839. };
  25840. /**
  25841. * Set extent
  25842. * @param {number} start
  25843. * @param {number} end
  25844. */
  25845. Scale.prototype.setExtent = function (start, end) {
  25846. var thisExtent = this._extent;
  25847. if (!isNaN(start)) {
  25848. thisExtent[0] = start;
  25849. }
  25850. if (!isNaN(end)) {
  25851. thisExtent[1] = end;
  25852. }
  25853. };
  25854. /**
  25855. * @return {Array.<string>}
  25856. */
  25857. Scale.prototype.getTicksLabels = function () {
  25858. var labels = [];
  25859. var ticks = this.getTicks();
  25860. for (var i = 0; i < ticks.length; i++) {
  25861. labels.push(this.getLabel(ticks[i]));
  25862. }
  25863. return labels;
  25864. };
  25865. /**
  25866. * When axis extent depends on data and no data exists,
  25867. * axis ticks should not be drawn, which is named 'blank'.
  25868. */
  25869. Scale.prototype.isBlank = function () {
  25870. return this._isBlank;
  25871. },
  25872. /**
  25873. * When axis extent depends on data and no data exists,
  25874. * axis ticks should not be drawn, which is named 'blank'.
  25875. */
  25876. Scale.prototype.setBlank = function (isBlank) {
  25877. this._isBlank = isBlank;
  25878. };
  25879. enableClassExtend(Scale);
  25880. enableClassManagement(Scale, {
  25881. registerWhenExtend: true
  25882. });
  25883. /**
  25884. * @constructor
  25885. * @param {Object} [opt]
  25886. * @param {Object} [opt.categories=[]]
  25887. * @param {Object} [opt.needCollect=false]
  25888. * @param {Object} [opt.deduplication=false]
  25889. */
  25890. function OrdinalMeta(opt) {
  25891. /**
  25892. * @readOnly
  25893. * @type {Array.<string>}
  25894. */
  25895. this.categories = opt.categories || [];
  25896. /**
  25897. * @private
  25898. * @type {boolean}
  25899. */
  25900. this._needCollect = opt.needCollect;
  25901. /**
  25902. * @private
  25903. * @type {boolean}
  25904. */
  25905. this._deduplication = opt.deduplication;
  25906. /**
  25907. * @private
  25908. * @type {boolean}
  25909. */
  25910. this._map;
  25911. }
  25912. /**
  25913. * @param {module:echarts/model/Model} axisModel
  25914. * @return {module:echarts/data/OrdinalMeta}
  25915. */
  25916. OrdinalMeta.createByAxisModel = function (axisModel) {
  25917. var option = axisModel.option;
  25918. var data = option.data;
  25919. var categories = data && map(data, getName);
  25920. return new OrdinalMeta({
  25921. categories: categories,
  25922. needCollect: !categories,
  25923. // deduplication is default in axis.
  25924. deduplication: option.dedplication !== false
  25925. });
  25926. };
  25927. var proto$1 = OrdinalMeta.prototype;
  25928. /**
  25929. * @param {string} category
  25930. * @return {number} ordinal
  25931. */
  25932. proto$1.getOrdinal = function (category) {
  25933. return getOrCreateMap(this).get(category);
  25934. };
  25935. /**
  25936. * @param {*} category
  25937. * @return {number} The ordinal. If not found, return NaN.
  25938. */
  25939. proto$1.parseAndCollect = function (category) {
  25940. var index;
  25941. var needCollect = this._needCollect;
  25942. // The value of category dim can be the index of the given category set.
  25943. // This feature is only supported when !needCollect, because we should
  25944. // consider a common case: a value is 2017, which is a number but is
  25945. // expected to be tread as a category. This case usually happen in dataset,
  25946. // where it happent to be no need of the index feature.
  25947. if (typeof category !== 'string' && !needCollect) {
  25948. return category;
  25949. }
  25950. // Optimize for the scenario:
  25951. // category is ['2012-01-01', '2012-01-02', ...], where the input
  25952. // data has been ensured not duplicate and is large data.
  25953. // Notice, if a dataset dimension provide categroies, usually echarts
  25954. // should remove duplication except user tell echarts dont do that
  25955. // (set axis.deduplication = false), because echarts do not know whether
  25956. // the values in the category dimension has duplication (consider the
  25957. // parallel-aqi example)
  25958. if (needCollect && !this._deduplication) {
  25959. index = this.categories.length;
  25960. this.categories[index] = category;
  25961. return index;
  25962. }
  25963. var map$$1 = getOrCreateMap(this);
  25964. index = map$$1.get(category);
  25965. if (index == null) {
  25966. if (needCollect) {
  25967. index = this.categories.length;
  25968. this.categories[index] = category;
  25969. map$$1.set(category, index);
  25970. }
  25971. else {
  25972. index = NaN;
  25973. }
  25974. }
  25975. return index;
  25976. };
  25977. // Consider big data, do not create map until needed.
  25978. function getOrCreateMap(ordinalMeta) {
  25979. return ordinalMeta._map || (
  25980. ordinalMeta._map = createHashMap(ordinalMeta.categories)
  25981. );
  25982. }
  25983. function getName(obj) {
  25984. if (isObject$1(obj) && obj.value != null) {
  25985. return obj.value;
  25986. }
  25987. else {
  25988. return obj + '';
  25989. }
  25990. }
  25991. /**
  25992. * Linear continuous scale
  25993. * @module echarts/coord/scale/Ordinal
  25994. *
  25995. * http://en.wikipedia.org/wiki/Level_of_measurement
  25996. */
  25997. // FIXME only one data
  25998. var scaleProto = Scale.prototype;
  25999. var OrdinalScale = Scale.extend({
  26000. type: 'ordinal',
  26001. /**
  26002. * @param {module:echarts/data/OrdianlMeta|Array.<string>} ordinalMeta
  26003. */
  26004. init: function (ordinalMeta, extent) {
  26005. // Caution: Should not use instanceof, consider ec-extensions using
  26006. // import approach to get OrdinalMeta class.
  26007. if (!ordinalMeta || isArray(ordinalMeta)) {
  26008. ordinalMeta = new OrdinalMeta({categories: ordinalMeta});
  26009. }
  26010. this._ordinalMeta = ordinalMeta;
  26011. this._extent = extent || [0, ordinalMeta.categories.length - 1];
  26012. },
  26013. parse: function (val) {
  26014. return typeof val === 'string'
  26015. ? this._ordinalMeta.getOrdinal(val)
  26016. // val might be float.
  26017. : Math.round(val);
  26018. },
  26019. contain: function (rank) {
  26020. rank = this.parse(rank);
  26021. return scaleProto.contain.call(this, rank)
  26022. && this._ordinalMeta.categories[rank] != null;
  26023. },
  26024. /**
  26025. * Normalize given rank or name to linear [0, 1]
  26026. * @param {number|string} [val]
  26027. * @return {number}
  26028. */
  26029. normalize: function (val) {
  26030. return scaleProto.normalize.call(this, this.parse(val));
  26031. },
  26032. scale: function (val) {
  26033. return Math.round(scaleProto.scale.call(this, val));
  26034. },
  26035. /**
  26036. * @return {Array}
  26037. */
  26038. getTicks: function () {
  26039. var ticks = [];
  26040. var extent = this._extent;
  26041. var rank = extent[0];
  26042. while (rank <= extent[1]) {
  26043. ticks.push(rank);
  26044. rank++;
  26045. }
  26046. return ticks;
  26047. },
  26048. /**
  26049. * Get item on rank n
  26050. * @param {number} n
  26051. * @return {string}
  26052. */
  26053. getLabel: function (n) {
  26054. return this._ordinalMeta.categories[n];
  26055. },
  26056. /**
  26057. * @return {number}
  26058. */
  26059. count: function () {
  26060. return this._extent[1] - this._extent[0] + 1;
  26061. },
  26062. /**
  26063. * @override
  26064. */
  26065. unionExtentFromData: function (data, dim) {
  26066. this.unionExtent(data.getApproximateExtent(dim, false));
  26067. },
  26068. niceTicks: noop,
  26069. niceExtent: noop
  26070. });
  26071. /**
  26072. * @return {module:echarts/scale/Time}
  26073. */
  26074. OrdinalScale.create = function () {
  26075. return new OrdinalScale();
  26076. };
  26077. /**
  26078. * For testable.
  26079. */
  26080. var roundNumber$1 = round$1;
  26081. /**
  26082. * @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number.
  26083. * Should be extent[0] < extent[1].
  26084. * @param {number} splitNumber splitNumber should be >= 1.
  26085. * @param {number} [minInterval]
  26086. * @param {number} [maxInterval]
  26087. * @return {Object} {interval, intervalPrecision, niceTickExtent}
  26088. */
  26089. function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {
  26090. var result = {};
  26091. var span = extent[1] - extent[0];
  26092. var interval = result.interval = nice(span / splitNumber, true);
  26093. if (minInterval != null && interval < minInterval) {
  26094. interval = result.interval = minInterval;
  26095. }
  26096. if (maxInterval != null && interval > maxInterval) {
  26097. interval = result.interval = maxInterval;
  26098. }
  26099. // Tow more digital for tick.
  26100. var precision = result.intervalPrecision = getIntervalPrecision(interval);
  26101. // Niced extent inside original extent
  26102. var niceTickExtent = result.niceTickExtent = [
  26103. roundNumber$1(Math.ceil(extent[0] / interval) * interval, precision),
  26104. roundNumber$1(Math.floor(extent[1] / interval) * interval, precision)
  26105. ];
  26106. fixExtent(niceTickExtent, extent);
  26107. return result;
  26108. }
  26109. /**
  26110. * @param {number} interval
  26111. * @return {number} interval precision
  26112. */
  26113. function getIntervalPrecision(interval) {
  26114. // Tow more digital for tick.
  26115. return getPrecisionSafe(interval) + 2;
  26116. }
  26117. function clamp(niceTickExtent, idx, extent) {
  26118. niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);
  26119. }
  26120. // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
  26121. function fixExtent(niceTickExtent, extent) {
  26122. !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
  26123. !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
  26124. clamp(niceTickExtent, 0, extent);
  26125. clamp(niceTickExtent, 1, extent);
  26126. if (niceTickExtent[0] > niceTickExtent[1]) {
  26127. niceTickExtent[0] = niceTickExtent[1];
  26128. }
  26129. }
  26130. function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) {
  26131. var ticks = [];
  26132. // If interval is 0, return [];
  26133. if (!interval) {
  26134. return ticks;
  26135. }
  26136. // Consider this case: using dataZoom toolbox, zoom and zoom.
  26137. var safeLimit = 10000;
  26138. if (extent[0] < niceTickExtent[0]) {
  26139. ticks.push(extent[0]);
  26140. }
  26141. var tick = niceTickExtent[0];
  26142. while (tick <= niceTickExtent[1]) {
  26143. ticks.push(tick);
  26144. // Avoid rounding error
  26145. tick = roundNumber$1(tick + interval, intervalPrecision);
  26146. if (tick === ticks[ticks.length - 1]) {
  26147. // Consider out of safe float point, e.g.,
  26148. // -3711126.9907707 + 2e-10 === -3711126.9907707
  26149. break;
  26150. }
  26151. if (ticks.length > safeLimit) {
  26152. return [];
  26153. }
  26154. }
  26155. // Consider this case: the last item of ticks is smaller
  26156. // than niceTickExtent[1] and niceTickExtent[1] === extent[1].
  26157. if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {
  26158. ticks.push(extent[1]);
  26159. }
  26160. return ticks;
  26161. }
  26162. /**
  26163. * Interval scale
  26164. * @module echarts/scale/Interval
  26165. */
  26166. var roundNumber = round$1;
  26167. /**
  26168. * @alias module:echarts/coord/scale/Interval
  26169. * @constructor
  26170. */
  26171. var IntervalScale = Scale.extend({
  26172. type: 'interval',
  26173. _interval: 0,
  26174. _intervalPrecision: 2,
  26175. setExtent: function (start, end) {
  26176. var thisExtent = this._extent;
  26177. //start,end may be a Number like '25',so...
  26178. if (!isNaN(start)) {
  26179. thisExtent[0] = parseFloat(start);
  26180. }
  26181. if (!isNaN(end)) {
  26182. thisExtent[1] = parseFloat(end);
  26183. }
  26184. },
  26185. unionExtent: function (other) {
  26186. var extent = this._extent;
  26187. other[0] < extent[0] && (extent[0] = other[0]);
  26188. other[1] > extent[1] && (extent[1] = other[1]);
  26189. // unionExtent may called by it's sub classes
  26190. IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]);
  26191. },
  26192. /**
  26193. * Get interval
  26194. */
  26195. getInterval: function () {
  26196. return this._interval;
  26197. },
  26198. /**
  26199. * Set interval
  26200. */
  26201. setInterval: function (interval) {
  26202. this._interval = interval;
  26203. // Dropped auto calculated niceExtent and use user setted extent
  26204. // We assume user wan't to set both interval, min, max to get a better result
  26205. this._niceExtent = this._extent.slice();
  26206. this._intervalPrecision = getIntervalPrecision(interval);
  26207. },
  26208. /**
  26209. * @return {Array.<number>}
  26210. */
  26211. getTicks: function () {
  26212. return intervalScaleGetTicks(
  26213. this._interval, this._extent, this._niceExtent, this._intervalPrecision
  26214. );
  26215. },
  26216. /**
  26217. * @return {Array.<string>}
  26218. */
  26219. getTicksLabels: function () {
  26220. var labels = [];
  26221. var ticks = this.getTicks();
  26222. for (var i = 0; i < ticks.length; i++) {
  26223. labels.push(this.getLabel(ticks[i]));
  26224. }
  26225. return labels;
  26226. },
  26227. /**
  26228. * @param {number} data
  26229. * @param {Object} [opt]
  26230. * @param {number|string} [opt.precision] If 'auto', use nice presision.
  26231. * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2.
  26232. * @return {string}
  26233. */
  26234. getLabel: function (data, opt) {
  26235. if (data == null) {
  26236. return '';
  26237. }
  26238. var precision = opt && opt.precision;
  26239. if (precision == null) {
  26240. precision = getPrecisionSafe(data) || 0;
  26241. }
  26242. else if (precision === 'auto') {
  26243. // Should be more precise then tick.
  26244. precision = this._intervalPrecision;
  26245. }
  26246. // (1) If `precision` is set, 12.005 should be display as '12.00500'.
  26247. // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.
  26248. data = roundNumber(data, precision, true);
  26249. return addCommas(data);
  26250. },
  26251. /**
  26252. * Update interval and extent of intervals for nice ticks
  26253. *
  26254. * @param {number} [splitNumber = 5] Desired number of ticks
  26255. * @param {number} [minInterval]
  26256. * @param {number} [maxInterval]
  26257. */
  26258. niceTicks: function (splitNumber, minInterval, maxInterval) {
  26259. splitNumber = splitNumber || 5;
  26260. var extent = this._extent;
  26261. var span = extent[1] - extent[0];
  26262. if (!isFinite(span)) {
  26263. return;
  26264. }
  26265. // User may set axis min 0 and data are all negative
  26266. // FIXME If it needs to reverse ?
  26267. if (span < 0) {
  26268. span = -span;
  26269. extent.reverse();
  26270. }
  26271. var result = intervalScaleNiceTicks(
  26272. extent, splitNumber, minInterval, maxInterval
  26273. );
  26274. this._intervalPrecision = result.intervalPrecision;
  26275. this._interval = result.interval;
  26276. this._niceExtent = result.niceTickExtent;
  26277. },
  26278. /**
  26279. * Nice extent.
  26280. * @param {Object} opt
  26281. * @param {number} [opt.splitNumber = 5] Given approx tick number
  26282. * @param {boolean} [opt.fixMin=false]
  26283. * @param {boolean} [opt.fixMax=false]
  26284. * @param {boolean} [opt.minInterval]
  26285. * @param {boolean} [opt.maxInterval]
  26286. */
  26287. niceExtent: function (opt) {
  26288. var extent = this._extent;
  26289. // If extent start and end are same, expand them
  26290. if (extent[0] === extent[1]) {
  26291. if (extent[0] !== 0) {
  26292. // Expand extent
  26293. var expandSize = extent[0];
  26294. // In the fowllowing case
  26295. // Axis has been fixed max 100
  26296. // Plus data are all 100 and axis extent are [100, 100].
  26297. // Extend to the both side will cause expanded max is larger than fixed max.
  26298. // So only expand to the smaller side.
  26299. if (!opt.fixMax) {
  26300. extent[1] += expandSize / 2;
  26301. extent[0] -= expandSize / 2;
  26302. }
  26303. else {
  26304. extent[0] -= expandSize / 2;
  26305. }
  26306. }
  26307. else {
  26308. extent[1] = 1;
  26309. }
  26310. }
  26311. var span = extent[1] - extent[0];
  26312. // If there are no data and extent are [Infinity, -Infinity]
  26313. if (!isFinite(span)) {
  26314. extent[0] = 0;
  26315. extent[1] = 1;
  26316. }
  26317. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  26318. // var extent = this._extent;
  26319. var interval = this._interval;
  26320. if (!opt.fixMin) {
  26321. extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);
  26322. }
  26323. if (!opt.fixMax) {
  26324. extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);
  26325. }
  26326. }
  26327. });
  26328. /**
  26329. * @return {module:echarts/scale/Time}
  26330. */
  26331. IntervalScale.create = function () {
  26332. return new IntervalScale();
  26333. };
  26334. var STACK_PREFIX = '__ec_stack_';
  26335. function getSeriesStackId(seriesModel) {
  26336. return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;
  26337. }
  26338. function getAxisKey(axis) {
  26339. return axis.dim + axis.index;
  26340. }
  26341. /**
  26342. * @param {Object} opt
  26343. * @param {module:echarts/coord/Axis} opt.axis Only support category axis currently.
  26344. * @param {number} opt.count Positive interger.
  26345. * @param {number} [opt.barWidth]
  26346. * @param {number} [opt.barMaxWidth]
  26347. * @param {number} [opt.barGap]
  26348. * @param {number} [opt.barCategoryGap]
  26349. * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined.
  26350. */
  26351. function calBarWidthAndOffset(barSeries, api) {
  26352. var seriesInfoList = map(barSeries, function (seriesModel) {
  26353. var data = seriesModel.getData();
  26354. var cartesian = seriesModel.coordinateSystem;
  26355. var baseAxis = cartesian.getBaseAxis();
  26356. var axisExtent = baseAxis.getExtent();
  26357. var bandWidth = baseAxis.type === 'category'
  26358. ? baseAxis.getBandWidth()
  26359. : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count());
  26360. var barWidth = parsePercent$1(
  26361. seriesModel.get('barWidth'), bandWidth
  26362. );
  26363. var barMaxWidth = parsePercent$1(
  26364. seriesModel.get('barMaxWidth'), bandWidth
  26365. );
  26366. var barGap = seriesModel.get('barGap');
  26367. var barCategoryGap = seriesModel.get('barCategoryGap');
  26368. return {
  26369. bandWidth: bandWidth,
  26370. barWidth: barWidth,
  26371. barMaxWidth: barMaxWidth,
  26372. barGap: barGap,
  26373. barCategoryGap: barCategoryGap,
  26374. axisKey: getAxisKey(baseAxis),
  26375. stackId: getSeriesStackId(seriesModel)
  26376. };
  26377. });
  26378. return doCalBarWidthAndOffset(seriesInfoList, api);
  26379. }
  26380. function doCalBarWidthAndOffset(seriesInfoList, api) {
  26381. // Columns info on each category axis. Key is cartesian name
  26382. var columnsMap = {};
  26383. each$1(seriesInfoList, function (seriesInfo, idx) {
  26384. var axisKey = seriesInfo.axisKey;
  26385. var bandWidth = seriesInfo.bandWidth;
  26386. var columnsOnAxis = columnsMap[axisKey] || {
  26387. bandWidth: bandWidth,
  26388. remainedWidth: bandWidth,
  26389. autoWidthCount: 0,
  26390. categoryGap: '20%',
  26391. gap: '30%',
  26392. stacks: {}
  26393. };
  26394. var stacks = columnsOnAxis.stacks;
  26395. columnsMap[axisKey] = columnsOnAxis;
  26396. var stackId = seriesInfo.stackId;
  26397. if (!stacks[stackId]) {
  26398. columnsOnAxis.autoWidthCount++;
  26399. }
  26400. stacks[stackId] = stacks[stackId] || {
  26401. width: 0,
  26402. maxWidth: 0
  26403. };
  26404. // Caution: In a single coordinate system, these barGrid attributes
  26405. // will be shared by series. Consider that they have default values,
  26406. // only the attributes set on the last series will work.
  26407. // Do not change this fact unless there will be a break change.
  26408. // TODO
  26409. var barWidth = seriesInfo.barWidth;
  26410. if (barWidth && !stacks[stackId].width) {
  26411. // See #6312, do not restrict width.
  26412. stacks[stackId].width = barWidth;
  26413. barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);
  26414. columnsOnAxis.remainedWidth -= barWidth;
  26415. }
  26416. var barMaxWidth = seriesInfo.barMaxWidth;
  26417. barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);
  26418. var barGap = seriesInfo.barGap;
  26419. (barGap != null) && (columnsOnAxis.gap = barGap);
  26420. var barCategoryGap = seriesInfo.barCategoryGap;
  26421. (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap);
  26422. });
  26423. var result = {};
  26424. each$1(columnsMap, function (columnsOnAxis, coordSysName) {
  26425. result[coordSysName] = {};
  26426. var stacks = columnsOnAxis.stacks;
  26427. var bandWidth = columnsOnAxis.bandWidth;
  26428. var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth);
  26429. var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);
  26430. var remainedWidth = columnsOnAxis.remainedWidth;
  26431. var autoWidthCount = columnsOnAxis.autoWidthCount;
  26432. var autoWidth = (remainedWidth - categoryGap)
  26433. / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
  26434. autoWidth = Math.max(autoWidth, 0);
  26435. // Find if any auto calculated bar exceeded maxBarWidth
  26436. each$1(stacks, function (column, stack) {
  26437. var maxWidth = column.maxWidth;
  26438. if (maxWidth && maxWidth < autoWidth) {
  26439. maxWidth = Math.min(maxWidth, remainedWidth);
  26440. if (column.width) {
  26441. maxWidth = Math.min(maxWidth, column.width);
  26442. }
  26443. remainedWidth -= maxWidth;
  26444. column.width = maxWidth;
  26445. autoWidthCount--;
  26446. }
  26447. });
  26448. // Recalculate width again
  26449. autoWidth = (remainedWidth - categoryGap)
  26450. / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
  26451. autoWidth = Math.max(autoWidth, 0);
  26452. var widthSum = 0;
  26453. var lastColumn;
  26454. each$1(stacks, function (column, idx) {
  26455. if (!column.width) {
  26456. column.width = autoWidth;
  26457. }
  26458. lastColumn = column;
  26459. widthSum += column.width * (1 + barGapPercent);
  26460. });
  26461. if (lastColumn) {
  26462. widthSum -= lastColumn.width * barGapPercent;
  26463. }
  26464. var offset = -widthSum / 2;
  26465. each$1(stacks, function (column, stackId) {
  26466. result[coordSysName][stackId] = result[coordSysName][stackId] || {
  26467. offset: offset,
  26468. width: column.width
  26469. };
  26470. offset += column.width * (1 + barGapPercent);
  26471. });
  26472. });
  26473. return result;
  26474. }
  26475. /**
  26476. * @param {string} seriesType
  26477. * @param {module:echarts/model/Global} ecModel
  26478. * @param {module:echarts/ExtensionAPI} api
  26479. */
  26480. function layout(seriesType, ecModel, api) {
  26481. var barWidthAndOffset = calBarWidthAndOffset(
  26482. filter(
  26483. ecModel.getSeriesByType(seriesType),
  26484. function (seriesModel) {
  26485. return !ecModel.isSeriesFiltered(seriesModel)
  26486. && seriesModel.coordinateSystem
  26487. && seriesModel.coordinateSystem.type === 'cartesian2d';
  26488. }
  26489. )
  26490. );
  26491. var lastStackCoords = {};
  26492. var lastStackCoordsOrigin = {};
  26493. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  26494. // Check series coordinate, do layout for cartesian2d only
  26495. if (seriesModel.coordinateSystem.type !== 'cartesian2d') {
  26496. return;
  26497. }
  26498. var data = seriesModel.getData();
  26499. var cartesian = seriesModel.coordinateSystem;
  26500. var baseAxis = cartesian.getBaseAxis();
  26501. var stackId = getSeriesStackId(seriesModel);
  26502. var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];
  26503. var columnOffset = columnLayoutInfo.offset;
  26504. var columnWidth = columnLayoutInfo.width;
  26505. var valueAxis = cartesian.getOtherAxis(baseAxis);
  26506. var barMinHeight = seriesModel.get('barMinHeight') || 0;
  26507. var valueAxisStart = baseAxis.onZero
  26508. ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0))
  26509. : valueAxis.getGlobalExtent()[0];
  26510. var coordDims = [
  26511. data.mapDimension('x'),
  26512. data.mapDimension('y')
  26513. ];
  26514. var coords = data.mapArray(coordDims, function (x, y) {
  26515. return cartesian.dataToPoint([x, y]);
  26516. }, true);
  26517. lastStackCoords[stackId] = lastStackCoords[stackId] || [];
  26518. lastStackCoordsOrigin[stackId] = lastStackCoordsOrigin[stackId] || []; // Fix #4243
  26519. data.setLayout({
  26520. offset: columnOffset,
  26521. size: columnWidth
  26522. });
  26523. data.each(data.mapDimension(valueAxis.dim), function (value, idx) {
  26524. if (isNaN(value)) {
  26525. return;
  26526. }
  26527. if (!lastStackCoords[stackId][idx]) {
  26528. lastStackCoords[stackId][idx] = {
  26529. p: valueAxisStart, // Positive stack
  26530. n: valueAxisStart // Negative stack
  26531. };
  26532. lastStackCoordsOrigin[stackId][idx] = {
  26533. p: valueAxisStart, // Positive stack
  26534. n: valueAxisStart // Negative stack
  26535. };
  26536. }
  26537. var sign = value >= 0 ? 'p' : 'n';
  26538. var coord = coords[idx];
  26539. var lastCoord = lastStackCoords[stackId][idx][sign];
  26540. var lastCoordOrigin = lastStackCoordsOrigin[stackId][idx][sign];
  26541. var x;
  26542. var y;
  26543. var width;
  26544. var height;
  26545. if (valueAxis.isHorizontal()) {
  26546. x = lastCoord;
  26547. y = coord[1] + columnOffset;
  26548. width = coord[0] - lastCoordOrigin;
  26549. height = columnWidth;
  26550. lastStackCoordsOrigin[stackId][idx][sign] += width;
  26551. if (Math.abs(width) < barMinHeight) {
  26552. width = (width < 0 ? -1 : 1) * barMinHeight;
  26553. }
  26554. lastStackCoords[stackId][idx][sign] += width;
  26555. }
  26556. else {
  26557. x = coord[0] + columnOffset;
  26558. y = lastCoord;
  26559. width = columnWidth;
  26560. height = coord[1] - lastCoordOrigin;
  26561. lastStackCoordsOrigin[stackId][idx][sign] += height;
  26562. if (Math.abs(height) < barMinHeight) {
  26563. // Include zero to has a positive bar
  26564. height = (height <= 0 ? -1 : 1) * barMinHeight;
  26565. }
  26566. lastStackCoords[stackId][idx][sign] += height;
  26567. }
  26568. data.setItemLayout(idx, {
  26569. x: x,
  26570. y: y,
  26571. width: width,
  26572. height: height
  26573. });
  26574. }, true);
  26575. }, this);
  26576. }
  26577. // [About UTC and local time zone]:
  26578. // In most cases, `number.parseDate` will treat input data string as local time
  26579. // (except time zone is specified in time string). And `format.formateTime` returns
  26580. // local time by default. option.useUTC is false by default. This design have
  26581. // concidered these common case:
  26582. // (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
  26583. // in local time by default.
  26584. // (2) By default, the input data string (e.g., '2011-01-02') should be displayed
  26585. // as its original time, without any time difference.
  26586. var intervalScaleProto = IntervalScale.prototype;
  26587. var mathCeil = Math.ceil;
  26588. var mathFloor = Math.floor;
  26589. var ONE_SECOND = 1000;
  26590. var ONE_MINUTE = ONE_SECOND * 60;
  26591. var ONE_HOUR = ONE_MINUTE * 60;
  26592. var ONE_DAY = ONE_HOUR * 24;
  26593. // FIXME 公用?
  26594. var bisect = function (a, x, lo, hi) {
  26595. while (lo < hi) {
  26596. var mid = lo + hi >>> 1;
  26597. if (a[mid][1] < x) {
  26598. lo = mid + 1;
  26599. }
  26600. else {
  26601. hi = mid;
  26602. }
  26603. }
  26604. return lo;
  26605. };
  26606. /**
  26607. * @alias module:echarts/coord/scale/Time
  26608. * @constructor
  26609. */
  26610. var TimeScale = IntervalScale.extend({
  26611. type: 'time',
  26612. /**
  26613. * @override
  26614. */
  26615. getLabel: function (val) {
  26616. var stepLvl = this._stepLvl;
  26617. var date = new Date(val);
  26618. return formatTime(stepLvl[0], date, this.getSetting('useUTC'));
  26619. },
  26620. /**
  26621. * @override
  26622. */
  26623. niceExtent: function (opt) {
  26624. var extent = this._extent;
  26625. // If extent start and end are same, expand them
  26626. if (extent[0] === extent[1]) {
  26627. // Expand extent
  26628. extent[0] -= ONE_DAY;
  26629. extent[1] += ONE_DAY;
  26630. }
  26631. // If there are no data and extent are [Infinity, -Infinity]
  26632. if (extent[1] === -Infinity && extent[0] === Infinity) {
  26633. var d = new Date();
  26634. extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
  26635. extent[0] = extent[1] - ONE_DAY;
  26636. }
  26637. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  26638. // var extent = this._extent;
  26639. var interval = this._interval;
  26640. if (!opt.fixMin) {
  26641. extent[0] = round$1(mathFloor(extent[0] / interval) * interval);
  26642. }
  26643. if (!opt.fixMax) {
  26644. extent[1] = round$1(mathCeil(extent[1] / interval) * interval);
  26645. }
  26646. },
  26647. /**
  26648. * @override
  26649. */
  26650. niceTicks: function (approxTickNum, minInterval, maxInterval) {
  26651. approxTickNum = approxTickNum || 10;
  26652. var extent = this._extent;
  26653. var span = extent[1] - extent[0];
  26654. var approxInterval = span / approxTickNum;
  26655. if (minInterval != null && approxInterval < minInterval) {
  26656. approxInterval = minInterval;
  26657. }
  26658. if (maxInterval != null && approxInterval > maxInterval) {
  26659. approxInterval = maxInterval;
  26660. }
  26661. var scaleLevelsLen = scaleLevels.length;
  26662. var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
  26663. var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
  26664. var interval = level[1];
  26665. // Same with interval scale if span is much larger than 1 year
  26666. if (level[0] === 'year') {
  26667. var yearSpan = span / interval;
  26668. // From "Nice Numbers for Graph Labels" of Graphic Gems
  26669. // var niceYearSpan = numberUtil.nice(yearSpan, false);
  26670. var yearStep = nice(yearSpan / approxTickNum, true);
  26671. interval *= yearStep;
  26672. }
  26673. var timezoneOffset = this.getSetting('useUTC')
  26674. ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000;
  26675. var niceExtent = [
  26676. Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),
  26677. Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)
  26678. ];
  26679. fixExtent(niceExtent, extent);
  26680. this._stepLvl = level;
  26681. // Interval will be used in getTicks
  26682. this._interval = interval;
  26683. this._niceExtent = niceExtent;
  26684. },
  26685. parse: function (val) {
  26686. // val might be float.
  26687. return +parseDate(val);
  26688. }
  26689. });
  26690. each$1(['contain', 'normalize'], function (methodName) {
  26691. TimeScale.prototype[methodName] = function (val) {
  26692. return intervalScaleProto[methodName].call(this, this.parse(val));
  26693. };
  26694. });
  26695. // Steps from d3
  26696. var scaleLevels = [
  26697. // Format interval
  26698. ['hh:mm:ss', ONE_SECOND], // 1s
  26699. ['hh:mm:ss', ONE_SECOND * 5], // 5s
  26700. ['hh:mm:ss', ONE_SECOND * 10], // 10s
  26701. ['hh:mm:ss', ONE_SECOND * 15], // 15s
  26702. ['hh:mm:ss', ONE_SECOND * 30], // 30s
  26703. ['hh:mm\nMM-dd', ONE_MINUTE], // 1m
  26704. ['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m
  26705. ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m
  26706. ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m
  26707. ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m
  26708. ['hh:mm\nMM-dd', ONE_HOUR], // 1h
  26709. ['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h
  26710. ['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h
  26711. ['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h
  26712. ['MM-dd\nyyyy', ONE_DAY], // 1d
  26713. ['MM-dd\nyyyy', ONE_DAY * 2], // 2d
  26714. ['MM-dd\nyyyy', ONE_DAY * 3], // 3d
  26715. ['MM-dd\nyyyy', ONE_DAY * 4], // 4d
  26716. ['MM-dd\nyyyy', ONE_DAY * 5], // 5d
  26717. ['MM-dd\nyyyy', ONE_DAY * 6], // 6d
  26718. ['week', ONE_DAY * 7], // 7d
  26719. ['MM-dd\nyyyy', ONE_DAY * 10], // 10d
  26720. ['week', ONE_DAY * 14], // 2w
  26721. ['week', ONE_DAY * 21], // 3w
  26722. ['month', ONE_DAY * 31], // 1M
  26723. ['week', ONE_DAY * 42], // 6w
  26724. ['month', ONE_DAY * 62], // 2M
  26725. ['week', ONE_DAY * 42], // 10w
  26726. ['quarter', ONE_DAY * 380 / 4], // 3M
  26727. ['month', ONE_DAY * 31 * 4], // 4M
  26728. ['month', ONE_DAY * 31 * 5], // 5M
  26729. ['half-year', ONE_DAY * 380 / 2], // 6M
  26730. ['month', ONE_DAY * 31 * 8], // 8M
  26731. ['month', ONE_DAY * 31 * 10], // 10M
  26732. ['year', ONE_DAY * 380] // 1Y
  26733. ];
  26734. /**
  26735. * @param {module:echarts/model/Model}
  26736. * @return {module:echarts/scale/Time}
  26737. */
  26738. TimeScale.create = function (model) {
  26739. return new TimeScale({useUTC: model.ecModel.get('useUTC')});
  26740. };
  26741. /**
  26742. * Log scale
  26743. * @module echarts/scale/Log
  26744. */
  26745. // Use some method of IntervalScale
  26746. var scaleProto$1 = Scale.prototype;
  26747. var intervalScaleProto$1 = IntervalScale.prototype;
  26748. var getPrecisionSafe$1 = getPrecisionSafe;
  26749. var roundingErrorFix = round$1;
  26750. var mathFloor$1 = Math.floor;
  26751. var mathCeil$1 = Math.ceil;
  26752. var mathPow$1 = Math.pow;
  26753. var mathLog = Math.log;
  26754. var LogScale = Scale.extend({
  26755. type: 'log',
  26756. base: 10,
  26757. $constructor: function () {
  26758. Scale.apply(this, arguments);
  26759. this._originalScale = new IntervalScale();
  26760. },
  26761. /**
  26762. * @return {Array.<number>}
  26763. */
  26764. getTicks: function () {
  26765. var originalScale = this._originalScale;
  26766. var extent = this._extent;
  26767. var originalExtent = originalScale.getExtent();
  26768. return map(intervalScaleProto$1.getTicks.call(this), function (val) {
  26769. var powVal = round$1(mathPow$1(this.base, val));
  26770. // Fix #4158
  26771. powVal = (val === extent[0] && originalScale.__fixMin)
  26772. ? fixRoundingError(powVal, originalExtent[0])
  26773. : powVal;
  26774. powVal = (val === extent[1] && originalScale.__fixMax)
  26775. ? fixRoundingError(powVal, originalExtent[1])
  26776. : powVal;
  26777. return powVal;
  26778. }, this);
  26779. },
  26780. /**
  26781. * @param {number} val
  26782. * @return {string}
  26783. */
  26784. getLabel: intervalScaleProto$1.getLabel,
  26785. /**
  26786. * @param {number} val
  26787. * @return {number}
  26788. */
  26789. scale: function (val) {
  26790. val = scaleProto$1.scale.call(this, val);
  26791. return mathPow$1(this.base, val);
  26792. },
  26793. /**
  26794. * @param {number} start
  26795. * @param {number} end
  26796. */
  26797. setExtent: function (start, end) {
  26798. var base = this.base;
  26799. start = mathLog(start) / mathLog(base);
  26800. end = mathLog(end) / mathLog(base);
  26801. intervalScaleProto$1.setExtent.call(this, start, end);
  26802. },
  26803. /**
  26804. * @return {number} end
  26805. */
  26806. getExtent: function () {
  26807. var base = this.base;
  26808. var extent = scaleProto$1.getExtent.call(this);
  26809. extent[0] = mathPow$1(base, extent[0]);
  26810. extent[1] = mathPow$1(base, extent[1]);
  26811. // Fix #4158
  26812. var originalScale = this._originalScale;
  26813. var originalExtent = originalScale.getExtent();
  26814. originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));
  26815. originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));
  26816. return extent;
  26817. },
  26818. /**
  26819. * @param {Array.<number>} extent
  26820. */
  26821. unionExtent: function (extent) {
  26822. this._originalScale.unionExtent(extent);
  26823. var base = this.base;
  26824. extent[0] = mathLog(extent[0]) / mathLog(base);
  26825. extent[1] = mathLog(extent[1]) / mathLog(base);
  26826. scaleProto$1.unionExtent.call(this, extent);
  26827. },
  26828. /**
  26829. * @override
  26830. */
  26831. unionExtentFromData: function (data, dim) {
  26832. this.unionExtent(data.getApproximateExtent(dim, true, function (val) {
  26833. return val > 0;
  26834. }));
  26835. },
  26836. /**
  26837. * Update interval and extent of intervals for nice ticks
  26838. * @param {number} [approxTickNum = 10] Given approx tick number
  26839. */
  26840. niceTicks: function (approxTickNum) {
  26841. approxTickNum = approxTickNum || 10;
  26842. var extent = this._extent;
  26843. var span = extent[1] - extent[0];
  26844. if (span === Infinity || span <= 0) {
  26845. return;
  26846. }
  26847. var interval = quantity(span);
  26848. var err = approxTickNum / span * interval;
  26849. // Filter ticks to get closer to the desired count.
  26850. if (err <= 0.5) {
  26851. interval *= 10;
  26852. }
  26853. // Interval should be integer
  26854. while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {
  26855. interval *= 10;
  26856. }
  26857. var niceExtent = [
  26858. round$1(mathCeil$1(extent[0] / interval) * interval),
  26859. round$1(mathFloor$1(extent[1] / interval) * interval)
  26860. ];
  26861. this._interval = interval;
  26862. this._niceExtent = niceExtent;
  26863. },
  26864. /**
  26865. * Nice extent.
  26866. * @override
  26867. */
  26868. niceExtent: function (opt) {
  26869. intervalScaleProto$1.niceExtent.call(this, opt);
  26870. var originalScale = this._originalScale;
  26871. originalScale.__fixMin = opt.fixMin;
  26872. originalScale.__fixMax = opt.fixMax;
  26873. }
  26874. });
  26875. each$1(['contain', 'normalize'], function (methodName) {
  26876. LogScale.prototype[methodName] = function (val) {
  26877. val = mathLog(val) / mathLog(this.base);
  26878. return scaleProto$1[methodName].call(this, val);
  26879. };
  26880. });
  26881. LogScale.create = function () {
  26882. return new LogScale();
  26883. };
  26884. function fixRoundingError(val, originalVal) {
  26885. return roundingErrorFix(val, getPrecisionSafe$1(originalVal));
  26886. }
  26887. /**
  26888. * Get axis scale extent before niced.
  26889. * Item of returned array can only be number (including Infinity and NaN).
  26890. */
  26891. function getScaleExtent(scale, model) {
  26892. var scaleType = scale.type;
  26893. var min = model.getMin();
  26894. var max = model.getMax();
  26895. var fixMin = min != null;
  26896. var fixMax = max != null;
  26897. var originalExtent = scale.getExtent();
  26898. var axisDataLen;
  26899. var boundaryGap;
  26900. var span;
  26901. if (scaleType === 'ordinal') {
  26902. axisDataLen = model.getCategories().length;
  26903. }
  26904. else {
  26905. boundaryGap = model.get('boundaryGap');
  26906. if (!isArray(boundaryGap)) {
  26907. boundaryGap = [boundaryGap || 0, boundaryGap || 0];
  26908. }
  26909. if (typeof boundaryGap[0] === 'boolean') {
  26910. if (__DEV__) {
  26911. console.warn('Boolean type for boundaryGap is only '
  26912. + 'allowed for ordinal axis. Please use string in '
  26913. + 'percentage instead, e.g., "20%". Currently, '
  26914. + 'boundaryGap is set to be 0.');
  26915. }
  26916. boundaryGap = [0, 0];
  26917. }
  26918. boundaryGap[0] = parsePercent$1(boundaryGap[0], 1);
  26919. boundaryGap[1] = parsePercent$1(boundaryGap[1], 1);
  26920. span = (originalExtent[1] - originalExtent[0])
  26921. || Math.abs(originalExtent[0]);
  26922. }
  26923. // Notice: When min/max is not set (that is, when there are null/undefined,
  26924. // which is the most common case), these cases should be ensured:
  26925. // (1) For 'ordinal', show all axis.data.
  26926. // (2) For others:
  26927. // + `boundaryGap` is applied (if min/max set, boundaryGap is
  26928. // disabled).
  26929. // + If `needCrossZero`, min/max should be zero, otherwise, min/max should
  26930. // be the result that originalExtent enlarged by boundaryGap.
  26931. // (3) If no data, it should be ensured that `scale.setBlank` is set.
  26932. // FIXME
  26933. // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used?
  26934. // (2) When `needCrossZero` and all data is positive/negative, should it be ensured
  26935. // that the results processed by boundaryGap are positive/negative?
  26936. if (min == null) {
  26937. min = scaleType === 'ordinal'
  26938. ? (axisDataLen ? 0 : NaN)
  26939. : originalExtent[0] - boundaryGap[0] * span;
  26940. }
  26941. if (max == null) {
  26942. max = scaleType === 'ordinal'
  26943. ? (axisDataLen ? axisDataLen - 1 : NaN)
  26944. : originalExtent[1] + boundaryGap[1] * span;
  26945. }
  26946. if (min === 'dataMin') {
  26947. min = originalExtent[0];
  26948. }
  26949. else if (typeof min === 'function') {
  26950. min = min({
  26951. min: originalExtent[0],
  26952. max: originalExtent[1]
  26953. });
  26954. }
  26955. if (max === 'dataMax') {
  26956. max = originalExtent[1];
  26957. }
  26958. else if (typeof max === 'function') {
  26959. max = max({
  26960. min: originalExtent[0],
  26961. max: originalExtent[1]
  26962. });
  26963. }
  26964. (min == null || !isFinite(min)) && (min = NaN);
  26965. (max == null || !isFinite(max)) && (max = NaN);
  26966. scale.setBlank(eqNaN(min) || eqNaN(max));
  26967. // Evaluate if axis needs cross zero
  26968. if (model.getNeedCrossZero()) {
  26969. // Axis is over zero and min is not set
  26970. if (min > 0 && max > 0 && !fixMin) {
  26971. min = 0;
  26972. }
  26973. // Axis is under zero and max is not set
  26974. if (min < 0 && max < 0 && !fixMax) {
  26975. max = 0;
  26976. }
  26977. }
  26978. // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis
  26979. // is base axis
  26980. var ecModel = model.ecModel;
  26981. if (ecModel) {
  26982. var isBaseAxisAndHasBarSeries = filter(ecModel.getSeriesByType('bar'), function (seriesModel) {
  26983. return seriesModel.getBaseAxis() === model.axis;
  26984. }).length > 0;
  26985. if ((scaleType === 'time' || scaleType === 'interval') && isBaseAxisAndHasBarSeries) {
  26986. // Adjust axis min and max to account for overflow
  26987. var adjustedScale = adjustScaleForOverflow(min, max, model);
  26988. min = adjustedScale.min;
  26989. max = adjustedScale.max;
  26990. }
  26991. }
  26992. return [min, max];
  26993. }
  26994. function adjustScaleForOverflow(min, max, model) {
  26995. var ecModel = model.ecModel;
  26996. // Get Axis Length
  26997. var axisExtent = model.axis.getExtent();
  26998. var axisLength = axisExtent[1] - axisExtent[0];
  26999. // Calculate placement of bars on axis
  27000. var barWidthAndOffset = calBarWidthAndOffset(filter(
  27001. ecModel.getSeriesByType('bar'),
  27002. function (seriesModel) {
  27003. return !ecModel.isSeriesFiltered(seriesModel)
  27004. && seriesModel.coordinateSystem
  27005. && seriesModel.coordinateSystem.type === 'cartesian2d';
  27006. }
  27007. ));
  27008. // Get bars on current base axis and calculate min and max overflow
  27009. var baseAxisKey = model.axis.dim + model.axis.index;
  27010. var barsOnCurrentAxis = barWidthAndOffset[baseAxisKey];
  27011. if (barsOnCurrentAxis === undefined) {
  27012. return {min: min, max: max};
  27013. }
  27014. var minOverflow = Infinity;
  27015. each$1(barsOnCurrentAxis, function (item) {
  27016. minOverflow = Math.min(item.offset, minOverflow);
  27017. });
  27018. var maxOverflow = -Infinity;
  27019. each$1(barsOnCurrentAxis, function (item) {
  27020. maxOverflow = Math.max(item.offset + item.width, maxOverflow);
  27021. });
  27022. var totalOverFlow = Math.abs(minOverflow) + maxOverflow;
  27023. // Calulate required buffer based on old range and overflow
  27024. var oldRange = max - min;
  27025. var oldRangePercentOfNew = (1 - (minOverflow + maxOverflow) / axisLength);
  27026. var overflowBuffer = ((oldRange / oldRangePercentOfNew) - oldRange);
  27027. max += overflowBuffer * (maxOverflow / totalOverFlow);
  27028. min -= overflowBuffer * (minOverflow / totalOverFlow);
  27029. return {min: min, max: max};
  27030. }
  27031. function niceScaleExtent$1(scale, model) {
  27032. var extent = getScaleExtent(scale, model);
  27033. var fixMin = model.getMin() != null;
  27034. var fixMax = model.getMax() != null;
  27035. var splitNumber = model.get('splitNumber');
  27036. if (scale.type === 'log') {
  27037. scale.base = model.get('logBase');
  27038. }
  27039. var scaleType = scale.type;
  27040. scale.setExtent(extent[0], extent[1]);
  27041. scale.niceExtent({
  27042. splitNumber: splitNumber,
  27043. fixMin: fixMin,
  27044. fixMax: fixMax,
  27045. minInterval: (scaleType === 'interval' || scaleType === 'time')
  27046. ? model.get('minInterval') : null,
  27047. maxInterval: (scaleType === 'interval' || scaleType === 'time')
  27048. ? model.get('maxInterval') : null
  27049. });
  27050. // If some one specified the min, max. And the default calculated interval
  27051. // is not good enough. He can specify the interval. It is often appeared
  27052. // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard
  27053. // to be 60.
  27054. // FIXME
  27055. var interval = model.get('interval');
  27056. if (interval != null) {
  27057. scale.setInterval && scale.setInterval(interval);
  27058. }
  27059. }
  27060. /**
  27061. * @param {module:echarts/model/Model} model
  27062. * @param {string} [axisType] Default retrieve from model.type
  27063. * @return {module:echarts/scale/*}
  27064. */
  27065. function createScaleByModel(model, axisType) {
  27066. axisType = axisType || model.get('type');
  27067. if (axisType) {
  27068. switch (axisType) {
  27069. // Buildin scale
  27070. case 'category':
  27071. return new OrdinalScale(
  27072. model.getOrdinalMeta
  27073. ? model.getOrdinalMeta()
  27074. : model.getCategories(),
  27075. [Infinity, -Infinity]
  27076. );
  27077. case 'value':
  27078. return new IntervalScale();
  27079. // Extended scale, like time and log
  27080. default:
  27081. return (Scale.getClass(axisType) || IntervalScale).create(model);
  27082. }
  27083. }
  27084. }
  27085. /**
  27086. * Check if the axis corss 0
  27087. */
  27088. function ifAxisCrossZero$1(axis) {
  27089. var dataExtent = axis.scale.getExtent();
  27090. var min = dataExtent[0];
  27091. var max = dataExtent[1];
  27092. return !((min > 0 && max > 0) || (min < 0 && max < 0));
  27093. }
  27094. /**
  27095. * @param {Array.<number>} tickCoords In axis self coordinate.
  27096. * @param {Array.<string>} labels
  27097. * @param {string} font
  27098. * @param {number} axisRotate 0: towards right horizontally, clock-wise is negative.
  27099. * @param {number} [labelRotate=0] 0: towards right horizontally, clock-wise is negative.
  27100. * @return {number}
  27101. */
  27102. function getAxisLabelInterval(tickCoords, labels, font, axisRotate, labelRotate) {
  27103. var textSpaceTakenRect;
  27104. var autoLabelInterval = 0;
  27105. var accumulatedLabelInterval = 0;
  27106. var rotation = (axisRotate - labelRotate) / 180 * Math.PI;
  27107. var step = 1;
  27108. if (labels.length > 40) {
  27109. // Simple optimization for large amount of labels
  27110. step = Math.floor(labels.length / 40);
  27111. }
  27112. for (var i = 0; i < tickCoords.length; i += step) {
  27113. var tickCoord = tickCoords[i];
  27114. // Not precise, do not consider align and vertical align
  27115. // and each distance from axis line yet.
  27116. var rect = getBoundingRect(
  27117. labels[i], font, 'center', 'top'
  27118. );
  27119. rect.x += tickCoord * Math.cos(rotation);
  27120. rect.y += tickCoord * Math.sin(rotation);
  27121. // Magic number
  27122. rect.width *= 1.3;
  27123. rect.height *= 1.3;
  27124. if (!textSpaceTakenRect) {
  27125. textSpaceTakenRect = rect.clone();
  27126. }
  27127. // There is no space for current label;
  27128. else if (textSpaceTakenRect.intersect(rect)) {
  27129. accumulatedLabelInterval++;
  27130. autoLabelInterval = Math.max(autoLabelInterval, accumulatedLabelInterval);
  27131. }
  27132. else {
  27133. textSpaceTakenRect.union(rect);
  27134. // Reset
  27135. accumulatedLabelInterval = 0;
  27136. }
  27137. }
  27138. if (autoLabelInterval === 0 && step > 1) {
  27139. return step;
  27140. }
  27141. return (autoLabelInterval + 1) * step - 1;
  27142. }
  27143. /**
  27144. * @param {Object} axis
  27145. * @param {Function} labelFormatter
  27146. * @return {Array.<string>}
  27147. */
  27148. function getFormattedLabels(axis, labelFormatter) {
  27149. var scale = axis.scale;
  27150. var labels = scale.getTicksLabels();
  27151. var ticks = scale.getTicks();
  27152. if (typeof labelFormatter === 'string') {
  27153. labelFormatter = (function (tpl) {
  27154. return function (val) {
  27155. return tpl.replace('{value}', val != null ? val : '');
  27156. };
  27157. })(labelFormatter);
  27158. // Consider empty array
  27159. return map(labels, labelFormatter);
  27160. }
  27161. else if (typeof labelFormatter === 'function') {
  27162. return map(ticks, function (tick, idx) {
  27163. return labelFormatter(
  27164. getAxisRawValue(axis, tick),
  27165. idx
  27166. );
  27167. }, this);
  27168. }
  27169. else {
  27170. return labels;
  27171. }
  27172. }
  27173. function getAxisRawValue(axis, value) {
  27174. // In category axis with data zoom, tick is not the original
  27175. // index of axis.data. So tick should not be exposed to user
  27176. // in category axis.
  27177. return axis.type === 'category' ? axis.scale.getLabel(value) : value;
  27178. }
  27179. /**
  27180. * Cartesian coordinate system
  27181. * @module echarts/coord/Cartesian
  27182. *
  27183. */
  27184. function dimAxisMapper(dim) {
  27185. return this._axes[dim];
  27186. }
  27187. /**
  27188. * @alias module:echarts/coord/Cartesian
  27189. * @constructor
  27190. */
  27191. var Cartesian = function (name) {
  27192. this._axes = {};
  27193. this._dimList = [];
  27194. /**
  27195. * @type {string}
  27196. */
  27197. this.name = name || '';
  27198. };
  27199. Cartesian.prototype = {
  27200. constructor: Cartesian,
  27201. type: 'cartesian',
  27202. /**
  27203. * Get axis
  27204. * @param {number|string} dim
  27205. * @return {module:echarts/coord/Cartesian~Axis}
  27206. */
  27207. getAxis: function (dim) {
  27208. return this._axes[dim];
  27209. },
  27210. /**
  27211. * Get axes list
  27212. * @return {Array.<module:echarts/coord/Cartesian~Axis>}
  27213. */
  27214. getAxes: function () {
  27215. return map(this._dimList, dimAxisMapper, this);
  27216. },
  27217. /**
  27218. * Get axes list by given scale type
  27219. */
  27220. getAxesByScale: function (scaleType) {
  27221. scaleType = scaleType.toLowerCase();
  27222. return filter(
  27223. this.getAxes(),
  27224. function (axis) {
  27225. return axis.scale.type === scaleType;
  27226. }
  27227. );
  27228. },
  27229. /**
  27230. * Add axis
  27231. * @param {module:echarts/coord/Cartesian.Axis}
  27232. */
  27233. addAxis: function (axis) {
  27234. var dim = axis.dim;
  27235. this._axes[dim] = axis;
  27236. this._dimList.push(dim);
  27237. },
  27238. /**
  27239. * Convert data to coord in nd space
  27240. * @param {Array.<number>|Object.<string, number>} val
  27241. * @return {Array.<number>|Object.<string, number>}
  27242. */
  27243. dataToCoord: function (val) {
  27244. return this._dataCoordConvert(val, 'dataToCoord');
  27245. },
  27246. /**
  27247. * Convert coord in nd space to data
  27248. * @param {Array.<number>|Object.<string, number>} val
  27249. * @return {Array.<number>|Object.<string, number>}
  27250. */
  27251. coordToData: function (val) {
  27252. return this._dataCoordConvert(val, 'coordToData');
  27253. },
  27254. _dataCoordConvert: function (input, method) {
  27255. var dimList = this._dimList;
  27256. var output = input instanceof Array ? [] : {};
  27257. for (var i = 0; i < dimList.length; i++) {
  27258. var dim = dimList[i];
  27259. var axis = this._axes[dim];
  27260. output[dim] = axis[method](input[dim]);
  27261. }
  27262. return output;
  27263. }
  27264. };
  27265. function Cartesian2D(name) {
  27266. Cartesian.call(this, name);
  27267. }
  27268. Cartesian2D.prototype = {
  27269. constructor: Cartesian2D,
  27270. type: 'cartesian2d',
  27271. /**
  27272. * @type {Array.<string>}
  27273. * @readOnly
  27274. */
  27275. dimensions: ['x', 'y'],
  27276. /**
  27277. * Base axis will be used on stacking.
  27278. *
  27279. * @return {module:echarts/coord/cartesian/Axis2D}
  27280. */
  27281. getBaseAxis: function () {
  27282. return this.getAxesByScale('ordinal')[0]
  27283. || this.getAxesByScale('time')[0]
  27284. || this.getAxis('x');
  27285. },
  27286. /**
  27287. * If contain point
  27288. * @param {Array.<number>} point
  27289. * @return {boolean}
  27290. */
  27291. containPoint: function (point) {
  27292. var axisX = this.getAxis('x');
  27293. var axisY = this.getAxis('y');
  27294. return axisX.contain(axisX.toLocalCoord(point[0]))
  27295. && axisY.contain(axisY.toLocalCoord(point[1]));
  27296. },
  27297. /**
  27298. * If contain data
  27299. * @param {Array.<number>} data
  27300. * @return {boolean}
  27301. */
  27302. containData: function (data) {
  27303. return this.getAxis('x').containData(data[0])
  27304. && this.getAxis('y').containData(data[1]);
  27305. },
  27306. /**
  27307. * @param {Array.<number>} data
  27308. * @param {Array.<number>} out
  27309. * @return {Array.<number>}
  27310. */
  27311. dataToPoint: function (data, reserved, out) {
  27312. var xAxis = this.getAxis('x');
  27313. var yAxis = this.getAxis('y');
  27314. out = out || [];
  27315. out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(data[0]));
  27316. out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(data[1]));
  27317. return out;
  27318. },
  27319. /**
  27320. * @param {Array.<number>} data
  27321. * @param {Array.<number>} out
  27322. * @return {Array.<number>}
  27323. */
  27324. clampData: function (data, out) {
  27325. var xAxisExtent = this.getAxis('x').scale.getExtent();
  27326. var yAxisExtent = this.getAxis('y').scale.getExtent();
  27327. out = out || [];
  27328. out[0] = Math.min(
  27329. Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), data[0]),
  27330. Math.max(xAxisExtent[0], xAxisExtent[1])
  27331. );
  27332. out[1] = Math.min(
  27333. Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), data[1]),
  27334. Math.max(yAxisExtent[0], yAxisExtent[1])
  27335. );
  27336. return out;
  27337. },
  27338. /**
  27339. * @param {Array.<number>} point
  27340. * @param {Array.<number>} out
  27341. * @return {Array.<number>}
  27342. */
  27343. pointToData: function (point, out) {
  27344. var xAxis = this.getAxis('x');
  27345. var yAxis = this.getAxis('y');
  27346. out = out || [];
  27347. out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]));
  27348. out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]));
  27349. return out;
  27350. },
  27351. /**
  27352. * Get other axis
  27353. * @param {module:echarts/coord/cartesian/Axis2D} axis
  27354. */
  27355. getOtherAxis: function (axis) {
  27356. return this.getAxis(axis.dim === 'x' ? 'y' : 'x');
  27357. }
  27358. };
  27359. inherits(Cartesian2D, Cartesian);
  27360. var linearMap$1 = linearMap;
  27361. function fixExtentWithBands(extent, nTick) {
  27362. var size = extent[1] - extent[0];
  27363. var len = nTick;
  27364. var margin = size / len / 2;
  27365. extent[0] += margin;
  27366. extent[1] -= margin;
  27367. }
  27368. var normalizedExtent = [0, 1];
  27369. /**
  27370. * @name module:echarts/coord/CartesianAxis
  27371. * @constructor
  27372. */
  27373. var Axis = function (dim, scale, extent) {
  27374. /**
  27375. * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'
  27376. * @type {string}
  27377. */
  27378. this.dim = dim;
  27379. /**
  27380. * Axis scale
  27381. * @type {module:echarts/coord/scale/*}
  27382. */
  27383. this.scale = scale;
  27384. /**
  27385. * @type {Array.<number>}
  27386. * @private
  27387. */
  27388. this._extent = extent || [0, 0];
  27389. /**
  27390. * @type {boolean}
  27391. */
  27392. this.inverse = false;
  27393. /**
  27394. * Usually true when axis has a ordinal scale
  27395. * @type {boolean}
  27396. */
  27397. this.onBand = false;
  27398. /**
  27399. * @private
  27400. * @type {number}
  27401. */
  27402. this._labelInterval;
  27403. };
  27404. Axis.prototype = {
  27405. constructor: Axis,
  27406. /**
  27407. * If axis extent contain given coord
  27408. * @param {number} coord
  27409. * @return {boolean}
  27410. */
  27411. contain: function (coord) {
  27412. var extent = this._extent;
  27413. var min = Math.min(extent[0], extent[1]);
  27414. var max = Math.max(extent[0], extent[1]);
  27415. return coord >= min && coord <= max;
  27416. },
  27417. /**
  27418. * If axis extent contain given data
  27419. * @param {number} data
  27420. * @return {boolean}
  27421. */
  27422. containData: function (data) {
  27423. return this.contain(this.dataToCoord(data));
  27424. },
  27425. /**
  27426. * Get coord extent.
  27427. * @return {Array.<number>}
  27428. */
  27429. getExtent: function () {
  27430. return this._extent.slice();
  27431. },
  27432. /**
  27433. * Get precision used for formatting
  27434. * @param {Array.<number>} [dataExtent]
  27435. * @return {number}
  27436. */
  27437. getPixelPrecision: function (dataExtent) {
  27438. return getPixelPrecision(
  27439. dataExtent || this.scale.getExtent(),
  27440. this._extent
  27441. );
  27442. },
  27443. /**
  27444. * Set coord extent
  27445. * @param {number} start
  27446. * @param {number} end
  27447. */
  27448. setExtent: function (start, end) {
  27449. var extent = this._extent;
  27450. extent[0] = start;
  27451. extent[1] = end;
  27452. },
  27453. /**
  27454. * Convert data to coord. Data is the rank if it has a ordinal scale
  27455. * @param {number} data
  27456. * @param {boolean} clamp
  27457. * @return {number}
  27458. */
  27459. dataToCoord: function (data, clamp) {
  27460. var extent = this._extent;
  27461. var scale = this.scale;
  27462. data = scale.normalize(data);
  27463. if (this.onBand && scale.type === 'ordinal') {
  27464. extent = extent.slice();
  27465. fixExtentWithBands(extent, scale.count());
  27466. }
  27467. return linearMap$1(data, normalizedExtent, extent, clamp);
  27468. },
  27469. /**
  27470. * Convert coord to data. Data is the rank if it has a ordinal scale
  27471. * @param {number} coord
  27472. * @param {boolean} clamp
  27473. * @return {number}
  27474. */
  27475. coordToData: function (coord, clamp) {
  27476. var extent = this._extent;
  27477. var scale = this.scale;
  27478. if (this.onBand && scale.type === 'ordinal') {
  27479. extent = extent.slice();
  27480. fixExtentWithBands(extent, scale.count());
  27481. }
  27482. var t = linearMap$1(coord, extent, normalizedExtent, clamp);
  27483. return this.scale.scale(t);
  27484. },
  27485. /**
  27486. * Convert pixel point to data in axis
  27487. * @param {Array.<number>} point
  27488. * @param {boolean} clamp
  27489. * @return {number} data
  27490. */
  27491. pointToData: function (point, clamp) {
  27492. // Should be implemented in derived class if necessary.
  27493. },
  27494. /**
  27495. * @return {Array.<number>}
  27496. */
  27497. getTicksCoords: function (alignWithLabel) {
  27498. if (this.onBand && !alignWithLabel) {
  27499. var bands = this.getBands();
  27500. var coords = [];
  27501. for (var i = 0; i < bands.length; i++) {
  27502. coords.push(bands[i][0]);
  27503. }
  27504. if (bands[i - 1]) {
  27505. coords.push(bands[i - 1][1]);
  27506. }
  27507. return coords;
  27508. }
  27509. else {
  27510. return map(this.scale.getTicks(), this.dataToCoord, this);
  27511. }
  27512. },
  27513. /**
  27514. * Coords of labels are on the ticks or on the middle of bands
  27515. * @return {Array.<number>}
  27516. */
  27517. getLabelsCoords: function () {
  27518. return map(this.scale.getTicks(), this.dataToCoord, this);
  27519. },
  27520. /**
  27521. * Get bands.
  27522. *
  27523. * If axis has labels [1, 2, 3, 4]. Bands on the axis are
  27524. * |---1---|---2---|---3---|---4---|.
  27525. *
  27526. * @return {Array}
  27527. */
  27528. // FIXME Situation when labels is on ticks
  27529. getBands: function () {
  27530. var extent = this.getExtent();
  27531. var bands = [];
  27532. var len = this.scale.count();
  27533. var start = extent[0];
  27534. var end = extent[1];
  27535. var span = end - start;
  27536. for (var i = 0; i < len; i++) {
  27537. bands.push([
  27538. span * i / len + start,
  27539. span * (i + 1) / len + start
  27540. ]);
  27541. }
  27542. return bands;
  27543. },
  27544. /**
  27545. * Get width of band
  27546. * @return {number}
  27547. */
  27548. getBandWidth: function () {
  27549. var axisExtent = this._extent;
  27550. var dataExtent = this.scale.getExtent();
  27551. var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0);
  27552. // Fix #2728, avoid NaN when only one data.
  27553. len === 0 && (len = 1);
  27554. var size = Math.abs(axisExtent[1] - axisExtent[0]);
  27555. return Math.abs(size) / len;
  27556. },
  27557. /**
  27558. * @abstract
  27559. * @return {boolean} Is horizontal
  27560. */
  27561. isHorizontal: null,
  27562. /**
  27563. * @abstract
  27564. * @return {number} Get axis rotate, by degree.
  27565. */
  27566. getRotate: null,
  27567. /**
  27568. * Get interval of the axis label.
  27569. * To get precise result, at least one of `getRotate` and `isHorizontal`
  27570. * should be implemented.
  27571. * @return {number}
  27572. */
  27573. getLabelInterval: function () {
  27574. var labelInterval = this._labelInterval;
  27575. if (!labelInterval) {
  27576. var axisModel = this.model;
  27577. var labelModel = axisModel.getModel('axisLabel');
  27578. labelInterval = labelModel.get('interval');
  27579. if (this.type === 'category'
  27580. && (labelInterval == null || labelInterval === 'auto')
  27581. ) {
  27582. labelInterval = getAxisLabelInterval(
  27583. map(this.scale.getTicks(), this.dataToCoord, this),
  27584. axisModel.getFormattedLabels(),
  27585. labelModel.getFont(),
  27586. this.getRotate
  27587. ? this.getRotate()
  27588. : (this.isHorizontal && !this.isHorizontal())
  27589. ? 90
  27590. : 0,
  27591. labelModel.get('rotate')
  27592. );
  27593. }
  27594. this._labelInterval = labelInterval;
  27595. }
  27596. return labelInterval;
  27597. }
  27598. };
  27599. /**
  27600. * Extend axis 2d
  27601. * @constructor module:echarts/coord/cartesian/Axis2D
  27602. * @extends {module:echarts/coord/cartesian/Axis}
  27603. * @param {string} dim
  27604. * @param {*} scale
  27605. * @param {Array.<number>} coordExtent
  27606. * @param {string} axisType
  27607. * @param {string} position
  27608. */
  27609. var Axis2D = function (dim, scale, coordExtent, axisType, position) {
  27610. Axis.call(this, dim, scale, coordExtent);
  27611. /**
  27612. * Axis type
  27613. * - 'category'
  27614. * - 'value'
  27615. * - 'time'
  27616. * - 'log'
  27617. * @type {string}
  27618. */
  27619. this.type = axisType || 'value';
  27620. /**
  27621. * Axis position
  27622. * - 'top'
  27623. * - 'bottom'
  27624. * - 'left'
  27625. * - 'right'
  27626. */
  27627. this.position = position || 'bottom';
  27628. };
  27629. Axis2D.prototype = {
  27630. constructor: Axis2D,
  27631. /**
  27632. * Index of axis, can be used as key
  27633. */
  27634. index: 0,
  27635. /**
  27636. * If axis is on the zero position of the other axis
  27637. * @type {boolean}
  27638. */
  27639. onZero: false,
  27640. /**
  27641. * Axis model
  27642. * @param {module:echarts/coord/cartesian/AxisModel}
  27643. */
  27644. model: null,
  27645. isHorizontal: function () {
  27646. var position = this.position;
  27647. return position === 'top' || position === 'bottom';
  27648. },
  27649. /**
  27650. * Each item cooresponds to this.getExtent(), which
  27651. * means globalExtent[0] may greater than globalExtent[1],
  27652. * unless `asc` is input.
  27653. *
  27654. * @param {boolean} [asc]
  27655. * @return {Array.<number>}
  27656. */
  27657. getGlobalExtent: function (asc) {
  27658. var ret = this.getExtent();
  27659. ret[0] = this.toGlobalCoord(ret[0]);
  27660. ret[1] = this.toGlobalCoord(ret[1]);
  27661. asc && ret[0] > ret[1] && ret.reverse();
  27662. return ret;
  27663. },
  27664. getOtherAxis: function () {
  27665. this.grid.getOtherAxis();
  27666. },
  27667. /**
  27668. * If label is ignored.
  27669. * Automatically used when axis is category and label can not be all shown
  27670. * @param {number} idx
  27671. * @return {boolean}
  27672. */
  27673. isLabelIgnored: function (idx) {
  27674. if (this.type === 'category') {
  27675. var labelInterval = this.getLabelInterval();
  27676. return ((typeof labelInterval === 'function')
  27677. && !labelInterval(idx, this.scale.getLabel(idx)))
  27678. || idx % (labelInterval + 1);
  27679. }
  27680. },
  27681. /**
  27682. * @override
  27683. */
  27684. pointToData: function (point, clamp) {
  27685. return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);
  27686. },
  27687. /**
  27688. * Transform global coord to local coord,
  27689. * i.e. var localCoord = axis.toLocalCoord(80);
  27690. * designate by module:echarts/coord/cartesian/Grid.
  27691. * @type {Function}
  27692. */
  27693. toLocalCoord: null,
  27694. /**
  27695. * Transform global coord to local coord,
  27696. * i.e. var globalCoord = axis.toLocalCoord(40);
  27697. * designate by module:echarts/coord/cartesian/Grid.
  27698. * @type {Function}
  27699. */
  27700. toGlobalCoord: null
  27701. };
  27702. inherits(Axis2D, Axis);
  27703. var defaultOption = {
  27704. show: true,
  27705. zlevel: 0, // 一级层叠
  27706. z: 0, // 二级层叠
  27707. // 反向坐标轴
  27708. inverse: false,
  27709. // 坐标轴名字,默认为空
  27710. name: '',
  27711. // 坐标轴名字位置,支持'start' | 'middle' | 'end'
  27712. nameLocation: 'end',
  27713. // 坐标轴名字旋转,degree。
  27714. nameRotate: null, // Adapt to axis rotate, when nameLocation is 'middle'.
  27715. nameTruncate: {
  27716. maxWidth: null,
  27717. ellipsis: '...',
  27718. placeholder: '.'
  27719. },
  27720. // 坐标轴文字样式,默认取全局样式
  27721. nameTextStyle: {},
  27722. // 文字与轴线距离
  27723. nameGap: 15,
  27724. silent: false, // Default false to support tooltip.
  27725. triggerEvent: false, // Default false to avoid legacy user event listener fail.
  27726. tooltip: {
  27727. show: false
  27728. },
  27729. axisPointer: {},
  27730. // 坐标轴线
  27731. axisLine: {
  27732. // 默认显示,属性show控制显示与否
  27733. show: true,
  27734. onZero: true,
  27735. onZeroAxisIndex: null,
  27736. // 属性lineStyle控制线条样式
  27737. lineStyle: {
  27738. color: '#333',
  27739. width: 1,
  27740. type: 'solid'
  27741. },
  27742. // 坐标轴两端的箭头
  27743. symbol: ['none', 'none'],
  27744. symbolSize: [10, 15]
  27745. },
  27746. // 坐标轴小标记
  27747. axisTick: {
  27748. // 属性show控制显示与否,默认显示
  27749. show: true,
  27750. // 控制小标记是否在grid里
  27751. inside: false,
  27752. // 属性length控制线长
  27753. length: 5,
  27754. // 属性lineStyle控制线条样式
  27755. lineStyle: {
  27756. width: 1
  27757. }
  27758. },
  27759. // 坐标轴文本标签,详见axis.axisLabel
  27760. axisLabel: {
  27761. show: true,
  27762. // 控制文本标签是否在grid里
  27763. inside: false,
  27764. rotate: 0,
  27765. showMinLabel: null, // true | false | null (auto)
  27766. showMaxLabel: null, // true | false | null (auto)
  27767. margin: 8,
  27768. // formatter: null,
  27769. // 其余属性默认使用全局文本样式,详见TEXTSTYLE
  27770. fontSize: 12
  27771. },
  27772. // 分隔线
  27773. splitLine: {
  27774. // 默认显示,属性show控制显示与否
  27775. show: true,
  27776. // 属性lineStyle(详见lineStyle)控制线条样式
  27777. lineStyle: {
  27778. color: ['#ccc'],
  27779. width: 1,
  27780. type: 'solid'
  27781. }
  27782. },
  27783. // 分隔区域
  27784. splitArea: {
  27785. // 默认不显示,属性show控制显示与否
  27786. show: false,
  27787. // 属性areaStyle(详见areaStyle)控制区域样式
  27788. areaStyle: {
  27789. color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
  27790. }
  27791. }
  27792. };
  27793. var axisDefault = {};
  27794. axisDefault.categoryAxis = merge({
  27795. // 类目起始和结束两端空白策略
  27796. boundaryGap: true,
  27797. // Set false to faster category collection.
  27798. // Only usefull in the case like: category is
  27799. // ['2012-01-01', '2012-01-02', ...], where the input
  27800. // data has been ensured not duplicate and is large data.
  27801. // null means "auto":
  27802. // if axis.data provided, do not deduplication,
  27803. // else do deduplication.
  27804. deduplication: null,
  27805. // splitArea: {
  27806. // show: false
  27807. // },
  27808. splitLine: {
  27809. show: false
  27810. },
  27811. // 坐标轴小标记
  27812. axisTick: {
  27813. // If tick is align with label when boundaryGap is true
  27814. alignWithLabel: false,
  27815. interval: 'auto'
  27816. },
  27817. // 坐标轴文本标签,详见axis.axisLabel
  27818. axisLabel: {
  27819. interval: 'auto'
  27820. }
  27821. }, defaultOption);
  27822. axisDefault.valueAxis = merge({
  27823. // 数值起始和结束两端空白策略
  27824. boundaryGap: [0, 0],
  27825. // TODO
  27826. // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60]
  27827. // 最小值, 设置成 'dataMin' 则从数据中计算最小值
  27828. // min: null,
  27829. // 最大值,设置成 'dataMax' 则从数据中计算最大值
  27830. // max: null,
  27831. // Readonly prop, specifies start value of the range when using data zoom.
  27832. // rangeStart: null
  27833. // Readonly prop, specifies end value of the range when using data zoom.
  27834. // rangeEnd: null
  27835. // 脱离0值比例,放大聚焦到最终_min,_max区间
  27836. // scale: false,
  27837. // 分割段数,默认为5
  27838. splitNumber: 5
  27839. // Minimum interval
  27840. // minInterval: null
  27841. // maxInterval: null
  27842. }, defaultOption);
  27843. // FIXME
  27844. axisDefault.timeAxis = defaults({
  27845. scale: true,
  27846. min: 'dataMin',
  27847. max: 'dataMax'
  27848. }, axisDefault.valueAxis);
  27849. axisDefault.logAxis = defaults({
  27850. scale: true,
  27851. logBase: 10
  27852. }, axisDefault.valueAxis);
  27853. // FIXME axisType is fixed ?
  27854. var AXIS_TYPES = ['value', 'category', 'time', 'log'];
  27855. /**
  27856. * Generate sub axis model class
  27857. * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel'
  27858. * @param {module:echarts/model/Component} BaseAxisModelClass
  27859. * @param {Function} axisTypeDefaulter
  27860. * @param {Object} [extraDefaultOption]
  27861. */
  27862. var axisModelCreator = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) {
  27863. each$1(AXIS_TYPES, function (axisType) {
  27864. BaseAxisModelClass.extend({
  27865. /**
  27866. * @readOnly
  27867. */
  27868. type: axisName + 'Axis.' + axisType,
  27869. mergeDefaultAndTheme: function (option, ecModel) {
  27870. var layoutMode = this.layoutMode;
  27871. var inputPositionParams = layoutMode
  27872. ? getLayoutParams(option) : {};
  27873. var themeModel = ecModel.getTheme();
  27874. merge(option, themeModel.get(axisType + 'Axis'));
  27875. merge(option, this.getDefaultOption());
  27876. option.type = axisTypeDefaulter(axisName, option);
  27877. if (layoutMode) {
  27878. mergeLayoutParam(option, inputPositionParams, layoutMode);
  27879. }
  27880. },
  27881. /**
  27882. * @override
  27883. */
  27884. optionUpdated: function () {
  27885. var thisOption = this.option;
  27886. if (thisOption.type === 'category') {
  27887. this.__ordinalMeta = OrdinalMeta.createByAxisModel(this);
  27888. }
  27889. },
  27890. /**
  27891. * Should not be called before all of 'getInitailData' finished.
  27892. * Because categories are collected during initializing data.
  27893. */
  27894. getCategories: function () {
  27895. // FIXME
  27896. // warning if called before all of 'getInitailData' finished.
  27897. if (this.option.type === 'category') {
  27898. return this.__ordinalMeta.categories;
  27899. }
  27900. },
  27901. getOrdinalMeta: function () {
  27902. return this.__ordinalMeta;
  27903. },
  27904. defaultOption: mergeAll(
  27905. [
  27906. {},
  27907. axisDefault[axisType + 'Axis'],
  27908. extraDefaultOption
  27909. ],
  27910. true
  27911. )
  27912. });
  27913. });
  27914. ComponentModel.registerSubTypeDefaulter(
  27915. axisName + 'Axis',
  27916. curry(axisTypeDefaulter, axisName)
  27917. );
  27918. };
  27919. var axisModelCommonMixin = {
  27920. /**
  27921. * Format labels
  27922. * @return {Array.<string>}
  27923. */
  27924. getFormattedLabels: function () {
  27925. return getFormattedLabels(
  27926. this.axis,
  27927. this.get('axisLabel.formatter')
  27928. );
  27929. },
  27930. /**
  27931. * @param {boolean} origin
  27932. * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN
  27933. */
  27934. getMin: function (origin) {
  27935. var option = this.option;
  27936. var min = (!origin && option.rangeStart != null)
  27937. ? option.rangeStart : option.min;
  27938. if (this.axis
  27939. && min != null
  27940. && min !== 'dataMin'
  27941. && typeof min !== 'function'
  27942. && !eqNaN(min)
  27943. ) {
  27944. min = this.axis.scale.parse(min);
  27945. }
  27946. return min;
  27947. },
  27948. /**
  27949. * @param {boolean} origin
  27950. * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN
  27951. */
  27952. getMax: function (origin) {
  27953. var option = this.option;
  27954. var max = (!origin && option.rangeEnd != null)
  27955. ? option.rangeEnd : option.max;
  27956. if (this.axis
  27957. && max != null
  27958. && max !== 'dataMax'
  27959. && typeof max !== 'function'
  27960. && !eqNaN(max)
  27961. ) {
  27962. max = this.axis.scale.parse(max);
  27963. }
  27964. return max;
  27965. },
  27966. /**
  27967. * @return {boolean}
  27968. */
  27969. getNeedCrossZero: function () {
  27970. var option = this.option;
  27971. return (option.rangeStart != null || option.rangeEnd != null)
  27972. ? false : !option.scale;
  27973. },
  27974. /**
  27975. * Should be implemented by each axis model if necessary.
  27976. * @return {module:echarts/model/Component} coordinate system model
  27977. */
  27978. getCoordSysModel: noop,
  27979. /**
  27980. * @param {number} rangeStart Can only be finite number or null/undefined or NaN.
  27981. * @param {number} rangeEnd Can only be finite number or null/undefined or NaN.
  27982. */
  27983. setRange: function (rangeStart, rangeEnd) {
  27984. this.option.rangeStart = rangeStart;
  27985. this.option.rangeEnd = rangeEnd;
  27986. },
  27987. /**
  27988. * Reset range
  27989. */
  27990. resetRange: function () {
  27991. // rangeStart and rangeEnd is readonly.
  27992. this.option.rangeStart = this.option.rangeEnd = null;
  27993. }
  27994. };
  27995. var AxisModel = ComponentModel.extend({
  27996. type: 'cartesian2dAxis',
  27997. /**
  27998. * @type {module:echarts/coord/cartesian/Axis2D}
  27999. */
  28000. axis: null,
  28001. /**
  28002. * @override
  28003. */
  28004. init: function () {
  28005. AxisModel.superApply(this, 'init', arguments);
  28006. this.resetRange();
  28007. },
  28008. /**
  28009. * @override
  28010. */
  28011. mergeOption: function () {
  28012. AxisModel.superApply(this, 'mergeOption', arguments);
  28013. this.resetRange();
  28014. },
  28015. /**
  28016. * @override
  28017. */
  28018. restoreData: function () {
  28019. AxisModel.superApply(this, 'restoreData', arguments);
  28020. this.resetRange();
  28021. },
  28022. /**
  28023. * @override
  28024. * @return {module:echarts/model/Component}
  28025. */
  28026. getCoordSysModel: function () {
  28027. return this.ecModel.queryComponents({
  28028. mainType: 'grid',
  28029. index: this.option.gridIndex,
  28030. id: this.option.gridId
  28031. })[0];
  28032. }
  28033. });
  28034. function getAxisType(axisDim, option) {
  28035. // Default axis with data is category axis
  28036. return option.type || (option.data ? 'category' : 'value');
  28037. }
  28038. merge(AxisModel.prototype, axisModelCommonMixin);
  28039. var extraOption = {
  28040. // gridIndex: 0,
  28041. // gridId: '',
  28042. // Offset is for multiple axis on the same position
  28043. offset: 0
  28044. };
  28045. axisModelCreator('x', AxisModel, getAxisType, extraOption);
  28046. axisModelCreator('y', AxisModel, getAxisType, extraOption);
  28047. // Grid 是在有直角坐标系的时候必须要存在的
  28048. // 所以这里也要被 Cartesian2D 依赖
  28049. ComponentModel.extend({
  28050. type: 'grid',
  28051. dependencies: ['xAxis', 'yAxis'],
  28052. layoutMode: 'box',
  28053. /**
  28054. * @type {module:echarts/coord/cartesian/Grid}
  28055. */
  28056. coordinateSystem: null,
  28057. defaultOption: {
  28058. show: false,
  28059. zlevel: 0,
  28060. z: 0,
  28061. left: '10%',
  28062. top: 60,
  28063. right: '10%',
  28064. bottom: 60,
  28065. // If grid size contain label
  28066. containLabel: false,
  28067. // width: {totalWidth} - left - right,
  28068. // height: {totalHeight} - top - bottom,
  28069. backgroundColor: 'rgba(0,0,0,0)',
  28070. borderWidth: 1,
  28071. borderColor: '#ccc'
  28072. }
  28073. });
  28074. /**
  28075. * Grid is a region which contains at most 4 cartesian systems
  28076. *
  28077. * TODO Default cartesian
  28078. */
  28079. // Depends on GridModel, AxisModel, which performs preprocess.
  28080. var each$6 = each$1;
  28081. var ifAxisCrossZero = ifAxisCrossZero$1;
  28082. var niceScaleExtent = niceScaleExtent$1;
  28083. /**
  28084. * Check if the axis is used in the specified grid
  28085. * @inner
  28086. */
  28087. function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {
  28088. return axisModel.getCoordSysModel() === gridModel;
  28089. }
  28090. function rotateTextRect(textRect, rotate) {
  28091. var rotateRadians = rotate * Math.PI / 180;
  28092. var boundingBox = textRect.plain();
  28093. var beforeWidth = boundingBox.width;
  28094. var beforeHeight = boundingBox.height;
  28095. var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);
  28096. var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians);
  28097. var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight);
  28098. return rotatedRect;
  28099. }
  28100. function getLabelUnionRect(axis) {
  28101. var axisModel = axis.model;
  28102. var labels = axisModel.get('axisLabel.show') ? axisModel.getFormattedLabels() : [];
  28103. var axisLabelModel = axisModel.getModel('axisLabel');
  28104. var rect;
  28105. var step = 1;
  28106. var labelCount = labels.length;
  28107. if (labelCount > 40) {
  28108. // Simple optimization for large amount of labels
  28109. step = Math.ceil(labelCount / 40);
  28110. }
  28111. for (var i = 0; i < labelCount; i += step) {
  28112. if (!axis.isLabelIgnored(i)) {
  28113. var unrotatedSingleRect = axisLabelModel.getTextRect(labels[i]);
  28114. var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);
  28115. rect ? rect.union(singleRect) : (rect = singleRect);
  28116. }
  28117. }
  28118. return rect;
  28119. }
  28120. function Grid(gridModel, ecModel, api) {
  28121. /**
  28122. * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}
  28123. * @private
  28124. */
  28125. this._coordsMap = {};
  28126. /**
  28127. * @type {Array.<module:echarts/coord/cartesian/Cartesian>}
  28128. * @private
  28129. */
  28130. this._coordsList = [];
  28131. /**
  28132. * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}
  28133. * @private
  28134. */
  28135. this._axesMap = {};
  28136. /**
  28137. * @type {Array.<module:echarts/coord/cartesian/Axis2D>}
  28138. * @private
  28139. */
  28140. this._axesList = [];
  28141. this._initCartesian(gridModel, ecModel, api);
  28142. this.model = gridModel;
  28143. }
  28144. var gridProto = Grid.prototype;
  28145. gridProto.type = 'grid';
  28146. gridProto.axisPointerEnabled = true;
  28147. gridProto.getRect = function () {
  28148. return this._rect;
  28149. };
  28150. gridProto.update = function (ecModel, api) {
  28151. var axesMap = this._axesMap;
  28152. this._updateScale(ecModel, this.model);
  28153. each$6(axesMap.x, function (xAxis) {
  28154. niceScaleExtent(xAxis.scale, xAxis.model);
  28155. });
  28156. each$6(axesMap.y, function (yAxis) {
  28157. niceScaleExtent(yAxis.scale, yAxis.model);
  28158. });
  28159. each$6(axesMap.x, function (xAxis) {
  28160. fixAxisOnZero(axesMap, 'y', xAxis);
  28161. });
  28162. each$6(axesMap.y, function (yAxis) {
  28163. fixAxisOnZero(axesMap, 'x', yAxis);
  28164. });
  28165. // Resize again if containLabel is enabled
  28166. // FIXME It may cause getting wrong grid size in data processing stage
  28167. this.resize(this.model, api);
  28168. };
  28169. function fixAxisOnZero(axesMap, otherAxisDim, axis) {
  28170. // onZero can not be enabled in these two situations:
  28171. // 1. When any other axis is a category axis.
  28172. // 2. When no axis is cross 0 point.
  28173. var axes = axesMap[otherAxisDim];
  28174. if (!axis.onZero) {
  28175. return;
  28176. }
  28177. var onZeroAxisIndex = axis.onZeroAxisIndex;
  28178. // If target axis is specified.
  28179. if (onZeroAxisIndex != null) {
  28180. var otherAxis = axes[onZeroAxisIndex];
  28181. if (otherAxis && canNotOnZeroToAxis(otherAxis)) {
  28182. axis.onZero = false;
  28183. }
  28184. return;
  28185. }
  28186. for (var idx in axes) {
  28187. if (axes.hasOwnProperty(idx)) {
  28188. var otherAxis = axes[idx];
  28189. if (otherAxis && !canNotOnZeroToAxis(otherAxis)) {
  28190. onZeroAxisIndex = +idx;
  28191. break;
  28192. }
  28193. }
  28194. }
  28195. if (onZeroAxisIndex == null) {
  28196. axis.onZero = false;
  28197. }
  28198. axis.onZeroAxisIndex = onZeroAxisIndex;
  28199. }
  28200. function canNotOnZeroToAxis(axis) {
  28201. return axis.type === 'category' || axis.type === 'time' || !ifAxisCrossZero(axis);
  28202. }
  28203. /**
  28204. * Resize the grid
  28205. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  28206. * @param {module:echarts/ExtensionAPI} api
  28207. */
  28208. gridProto.resize = function (gridModel, api, ignoreContainLabel) {
  28209. var gridRect = getLayoutRect(
  28210. gridModel.getBoxLayoutParams(), {
  28211. width: api.getWidth(),
  28212. height: api.getHeight()
  28213. });
  28214. this._rect = gridRect;
  28215. var axesList = this._axesList;
  28216. adjustAxes();
  28217. // Minus label size
  28218. if (!ignoreContainLabel && gridModel.get('containLabel')) {
  28219. each$6(axesList, function (axis) {
  28220. if (!axis.model.get('axisLabel.inside')) {
  28221. var labelUnionRect = getLabelUnionRect(axis);
  28222. if (labelUnionRect) {
  28223. var dim = axis.isHorizontal() ? 'height' : 'width';
  28224. var margin = axis.model.get('axisLabel.margin');
  28225. gridRect[dim] -= labelUnionRect[dim] + margin;
  28226. if (axis.position === 'top') {
  28227. gridRect.y += labelUnionRect.height + margin;
  28228. }
  28229. else if (axis.position === 'left') {
  28230. gridRect.x += labelUnionRect.width + margin;
  28231. }
  28232. }
  28233. }
  28234. });
  28235. adjustAxes();
  28236. }
  28237. function adjustAxes() {
  28238. each$6(axesList, function (axis) {
  28239. var isHorizontal = axis.isHorizontal();
  28240. var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
  28241. var idx = axis.inverse ? 1 : 0;
  28242. axis.setExtent(extent[idx], extent[1 - idx]);
  28243. updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y);
  28244. });
  28245. }
  28246. };
  28247. /**
  28248. * @param {string} axisType
  28249. * @param {number} [axisIndex]
  28250. */
  28251. gridProto.getAxis = function (axisType, axisIndex) {
  28252. var axesMapOnDim = this._axesMap[axisType];
  28253. if (axesMapOnDim != null) {
  28254. if (axisIndex == null) {
  28255. // Find first axis
  28256. for (var name in axesMapOnDim) {
  28257. if (axesMapOnDim.hasOwnProperty(name)) {
  28258. return axesMapOnDim[name];
  28259. }
  28260. }
  28261. }
  28262. return axesMapOnDim[axisIndex];
  28263. }
  28264. };
  28265. /**
  28266. * @return {Array.<module:echarts/coord/Axis>}
  28267. */
  28268. gridProto.getAxes = function () {
  28269. return this._axesList.slice();
  28270. };
  28271. /**
  28272. * Usage:
  28273. * grid.getCartesian(xAxisIndex, yAxisIndex);
  28274. * grid.getCartesian(xAxisIndex);
  28275. * grid.getCartesian(null, yAxisIndex);
  28276. * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});
  28277. *
  28278. * @param {number|Object} [xAxisIndex]
  28279. * @param {number} [yAxisIndex]
  28280. */
  28281. gridProto.getCartesian = function (xAxisIndex, yAxisIndex) {
  28282. if (xAxisIndex != null && yAxisIndex != null) {
  28283. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  28284. return this._coordsMap[key];
  28285. }
  28286. if (isObject$1(xAxisIndex)) {
  28287. yAxisIndex = xAxisIndex.yAxisIndex;
  28288. xAxisIndex = xAxisIndex.xAxisIndex;
  28289. }
  28290. // When only xAxisIndex or yAxisIndex given, find its first cartesian.
  28291. for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
  28292. if (coordList[i].getAxis('x').index === xAxisIndex
  28293. || coordList[i].getAxis('y').index === yAxisIndex
  28294. ) {
  28295. return coordList[i];
  28296. }
  28297. }
  28298. };
  28299. gridProto.getCartesians = function () {
  28300. return this._coordsList.slice();
  28301. };
  28302. /**
  28303. * @implements
  28304. * see {module:echarts/CoodinateSystem}
  28305. */
  28306. gridProto.convertToPixel = function (ecModel, finder, value) {
  28307. var target = this._findConvertTarget(ecModel, finder);
  28308. return target.cartesian
  28309. ? target.cartesian.dataToPoint(value)
  28310. : target.axis
  28311. ? target.axis.toGlobalCoord(target.axis.dataToCoord(value))
  28312. : null;
  28313. };
  28314. /**
  28315. * @implements
  28316. * see {module:echarts/CoodinateSystem}
  28317. */
  28318. gridProto.convertFromPixel = function (ecModel, finder, value) {
  28319. var target = this._findConvertTarget(ecModel, finder);
  28320. return target.cartesian
  28321. ? target.cartesian.pointToData(value)
  28322. : target.axis
  28323. ? target.axis.coordToData(target.axis.toLocalCoord(value))
  28324. : null;
  28325. };
  28326. /**
  28327. * @inner
  28328. */
  28329. gridProto._findConvertTarget = function (ecModel, finder) {
  28330. var seriesModel = finder.seriesModel;
  28331. var xAxisModel = finder.xAxisModel
  28332. || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]);
  28333. var yAxisModel = finder.yAxisModel
  28334. || (seriesModel && seriesModel.getReferringComponents('yAxis')[0]);
  28335. var gridModel = finder.gridModel;
  28336. var coordsList = this._coordsList;
  28337. var cartesian;
  28338. var axis;
  28339. if (seriesModel) {
  28340. cartesian = seriesModel.coordinateSystem;
  28341. indexOf(coordsList, cartesian) < 0 && (cartesian = null);
  28342. }
  28343. else if (xAxisModel && yAxisModel) {
  28344. cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  28345. }
  28346. else if (xAxisModel) {
  28347. axis = this.getAxis('x', xAxisModel.componentIndex);
  28348. }
  28349. else if (yAxisModel) {
  28350. axis = this.getAxis('y', yAxisModel.componentIndex);
  28351. }
  28352. // Lowest priority.
  28353. else if (gridModel) {
  28354. var grid = gridModel.coordinateSystem;
  28355. if (grid === this) {
  28356. cartesian = this._coordsList[0];
  28357. }
  28358. }
  28359. return {cartesian: cartesian, axis: axis};
  28360. };
  28361. /**
  28362. * @implements
  28363. * see {module:echarts/CoodinateSystem}
  28364. */
  28365. gridProto.containPoint = function (point) {
  28366. var coord = this._coordsList[0];
  28367. if (coord) {
  28368. return coord.containPoint(point);
  28369. }
  28370. };
  28371. /**
  28372. * Initialize cartesian coordinate systems
  28373. * @private
  28374. */
  28375. gridProto._initCartesian = function (gridModel, ecModel, api) {
  28376. var axisPositionUsed = {
  28377. left: false,
  28378. right: false,
  28379. top: false,
  28380. bottom: false
  28381. };
  28382. var axesMap = {
  28383. x: {},
  28384. y: {}
  28385. };
  28386. var axesCount = {
  28387. x: 0,
  28388. y: 0
  28389. };
  28390. /// Create axis
  28391. ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
  28392. ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
  28393. if (!axesCount.x || !axesCount.y) {
  28394. // Roll back when there no either x or y axis
  28395. this._axesMap = {};
  28396. this._axesList = [];
  28397. return;
  28398. }
  28399. this._axesMap = axesMap;
  28400. /// Create cartesian2d
  28401. each$6(axesMap.x, function (xAxis, xAxisIndex) {
  28402. each$6(axesMap.y, function (yAxis, yAxisIndex) {
  28403. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  28404. var cartesian = new Cartesian2D(key);
  28405. cartesian.grid = this;
  28406. cartesian.model = gridModel;
  28407. this._coordsMap[key] = cartesian;
  28408. this._coordsList.push(cartesian);
  28409. cartesian.addAxis(xAxis);
  28410. cartesian.addAxis(yAxis);
  28411. }, this);
  28412. }, this);
  28413. function createAxisCreator(axisType) {
  28414. return function (axisModel, idx) {
  28415. if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {
  28416. return;
  28417. }
  28418. var axisPosition = axisModel.get('position');
  28419. if (axisType === 'x') {
  28420. // Fix position
  28421. if (axisPosition !== 'top' && axisPosition !== 'bottom') {
  28422. // Default bottom of X
  28423. axisPosition = 'bottom';
  28424. if (axisPositionUsed[axisPosition]) {
  28425. axisPosition = axisPosition === 'top' ? 'bottom' : 'top';
  28426. }
  28427. }
  28428. }
  28429. else {
  28430. // Fix position
  28431. if (axisPosition !== 'left' && axisPosition !== 'right') {
  28432. // Default left of Y
  28433. axisPosition = 'left';
  28434. if (axisPositionUsed[axisPosition]) {
  28435. axisPosition = axisPosition === 'left' ? 'right' : 'left';
  28436. }
  28437. }
  28438. }
  28439. axisPositionUsed[axisPosition] = true;
  28440. var axis = new Axis2D(
  28441. axisType, createScaleByModel(axisModel),
  28442. [0, 0],
  28443. axisModel.get('type'),
  28444. axisPosition
  28445. );
  28446. var isCategory = axis.type === 'category';
  28447. axis.onBand = isCategory && axisModel.get('boundaryGap');
  28448. axis.inverse = axisModel.get('inverse');
  28449. axis.onZero = axisModel.get('axisLine.onZero');
  28450. axis.onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex');
  28451. // Inject axis into axisModel
  28452. axisModel.axis = axis;
  28453. // Inject axisModel into axis
  28454. axis.model = axisModel;
  28455. // Inject grid info axis
  28456. axis.grid = this;
  28457. // Index of axis, can be used as key
  28458. axis.index = idx;
  28459. this._axesList.push(axis);
  28460. axesMap[axisType][idx] = axis;
  28461. axesCount[axisType]++;
  28462. };
  28463. }
  28464. };
  28465. /**
  28466. * Update cartesian properties from series
  28467. * @param {module:echarts/model/Option} option
  28468. * @private
  28469. */
  28470. gridProto._updateScale = function (ecModel, gridModel) {
  28471. // Reset scale
  28472. each$1(this._axesList, function (axis) {
  28473. axis.scale.setExtent(Infinity, -Infinity);
  28474. });
  28475. ecModel.eachSeries(function (seriesModel) {
  28476. if (isCartesian2D(seriesModel)) {
  28477. var axesModels = findAxesModels(seriesModel, ecModel);
  28478. var xAxisModel = axesModels[0];
  28479. var yAxisModel = axesModels[1];
  28480. if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel)
  28481. || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)
  28482. ) {
  28483. return;
  28484. }
  28485. var cartesian = this.getCartesian(
  28486. xAxisModel.componentIndex, yAxisModel.componentIndex
  28487. );
  28488. var data = seriesModel.getData();
  28489. var xAxis = cartesian.getAxis('x');
  28490. var yAxis = cartesian.getAxis('y');
  28491. if (data.type === 'list') {
  28492. unionExtent(data, xAxis, seriesModel);
  28493. unionExtent(data, yAxis, seriesModel);
  28494. }
  28495. }
  28496. }, this);
  28497. function unionExtent(data, axis, seriesModel) {
  28498. each$6(data.mapDimension(axis.dim, true), function (dim) {
  28499. axis.scale.unionExtentFromData(data, dim);
  28500. });
  28501. }
  28502. };
  28503. /**
  28504. * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined
  28505. * @return {Object} {baseAxes: [], otherAxes: []}
  28506. */
  28507. gridProto.getTooltipAxes = function (dim) {
  28508. var baseAxes = [];
  28509. var otherAxes = [];
  28510. each$6(this.getCartesians(), function (cartesian) {
  28511. var baseAxis = (dim != null && dim !== 'auto')
  28512. ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
  28513. var otherAxis = cartesian.getOtherAxis(baseAxis);
  28514. indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
  28515. indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
  28516. });
  28517. return {baseAxes: baseAxes, otherAxes: otherAxes};
  28518. };
  28519. /**
  28520. * @inner
  28521. */
  28522. function updateAxisTransform(axis, coordBase) {
  28523. var axisExtent = axis.getExtent();
  28524. var axisExtentSum = axisExtent[0] + axisExtent[1];
  28525. // Fast transform
  28526. axis.toGlobalCoord = axis.dim === 'x'
  28527. ? function (coord) {
  28528. return coord + coordBase;
  28529. }
  28530. : function (coord) {
  28531. return axisExtentSum - coord + coordBase;
  28532. };
  28533. axis.toLocalCoord = axis.dim === 'x'
  28534. ? function (coord) {
  28535. return coord - coordBase;
  28536. }
  28537. : function (coord) {
  28538. return axisExtentSum - coord + coordBase;
  28539. };
  28540. }
  28541. var axesTypes = ['xAxis', 'yAxis'];
  28542. /**
  28543. * @inner
  28544. */
  28545. function findAxesModels(seriesModel, ecModel) {
  28546. return map(axesTypes, function (axisType) {
  28547. var axisModel = seriesModel.getReferringComponents(axisType)[0];
  28548. if (__DEV__) {
  28549. if (!axisModel) {
  28550. throw new Error(axisType + ' "' + retrieve(
  28551. seriesModel.get(axisType + 'Index'),
  28552. seriesModel.get(axisType + 'Id'),
  28553. 0
  28554. ) + '" not found');
  28555. }
  28556. }
  28557. return axisModel;
  28558. });
  28559. }
  28560. /**
  28561. * @inner
  28562. */
  28563. function isCartesian2D(seriesModel) {
  28564. return seriesModel.get('coordinateSystem') === 'cartesian2d';
  28565. }
  28566. Grid.create = function (ecModel, api) {
  28567. var grids = [];
  28568. ecModel.eachComponent('grid', function (gridModel, idx) {
  28569. var grid = new Grid(gridModel, ecModel, api);
  28570. grid.name = 'grid_' + idx;
  28571. // dataSampling requires axis extent, so resize
  28572. // should be performed in create stage.
  28573. grid.resize(gridModel, api, true);
  28574. gridModel.coordinateSystem = grid;
  28575. grids.push(grid);
  28576. });
  28577. // Inject the coordinateSystems into seriesModel
  28578. ecModel.eachSeries(function (seriesModel) {
  28579. if (!isCartesian2D(seriesModel)) {
  28580. return;
  28581. }
  28582. var axesModels = findAxesModels(seriesModel, ecModel);
  28583. var xAxisModel = axesModels[0];
  28584. var yAxisModel = axesModels[1];
  28585. var gridModel = xAxisModel.getCoordSysModel();
  28586. if (__DEV__) {
  28587. if (!gridModel) {
  28588. throw new Error(
  28589. 'Grid "' + retrieve(
  28590. xAxisModel.get('gridIndex'),
  28591. xAxisModel.get('gridId'),
  28592. 0
  28593. ) + '" not found'
  28594. );
  28595. }
  28596. if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
  28597. throw new Error('xAxis and yAxis must use the same grid');
  28598. }
  28599. }
  28600. var grid = gridModel.coordinateSystem;
  28601. seriesModel.coordinateSystem = grid.getCartesian(
  28602. xAxisModel.componentIndex, yAxisModel.componentIndex
  28603. );
  28604. });
  28605. return grids;
  28606. };
  28607. // For deciding which dimensions to use when creating list data
  28608. Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;
  28609. CoordinateSystemManager.register('cartesian2d', Grid);
  28610. var PI$2 = Math.PI;
  28611. function makeAxisEventDataBase(axisModel) {
  28612. var eventData = {
  28613. componentType: axisModel.mainType
  28614. };
  28615. eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;
  28616. return eventData;
  28617. }
  28618. /**
  28619. * A final axis is translated and rotated from a "standard axis".
  28620. * So opt.position and opt.rotation is required.
  28621. *
  28622. * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],
  28623. * for example: (0, 0) ------------> (0, 50)
  28624. *
  28625. * nameDirection or tickDirection or labelDirection is 1 means tick
  28626. * or label is below the standard axis, whereas is -1 means above
  28627. * the standard axis. labelOffset means offset between label and axis,
  28628. * which is useful when 'onZero', where axisLabel is in the grid and
  28629. * label in outside grid.
  28630. *
  28631. * Tips: like always,
  28632. * positive rotation represents anticlockwise, and negative rotation
  28633. * represents clockwise.
  28634. * The direction of position coordinate is the same as the direction
  28635. * of screen coordinate.
  28636. *
  28637. * Do not need to consider axis 'inverse', which is auto processed by
  28638. * axis extent.
  28639. *
  28640. * @param {module:zrender/container/Group} group
  28641. * @param {Object} axisModel
  28642. * @param {Object} opt Standard axis parameters.
  28643. * @param {Array.<number>} opt.position [x, y]
  28644. * @param {number} opt.rotation by radian
  28645. * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle' or 'center'.
  28646. * @param {number} [opt.tickDirection=1] 1 or -1
  28647. * @param {number} [opt.labelDirection=1] 1 or -1
  28648. * @param {number} [opt.labelOffset=0] Usefull when onZero.
  28649. * @param {string} [opt.axisLabelShow] default get from axisModel.
  28650. * @param {string} [opt.axisName] default get from axisModel.
  28651. * @param {number} [opt.axisNameAvailableWidth]
  28652. * @param {number} [opt.labelRotate] by degree, default get from axisModel.
  28653. * @param {number} [opt.labelInterval] Default label interval when label
  28654. * interval from model is null or 'auto'.
  28655. * @param {number} [opt.strokeContainThreshold] Default label interval when label
  28656. * @param {number} [opt.nameTruncateMaxWidth]
  28657. */
  28658. var AxisBuilder = function (axisModel, opt) {
  28659. /**
  28660. * @readOnly
  28661. */
  28662. this.opt = opt;
  28663. /**
  28664. * @readOnly
  28665. */
  28666. this.axisModel = axisModel;
  28667. // Default value
  28668. defaults(
  28669. opt,
  28670. {
  28671. labelOffset: 0,
  28672. nameDirection: 1,
  28673. tickDirection: 1,
  28674. labelDirection: 1,
  28675. silent: true
  28676. }
  28677. );
  28678. /**
  28679. * @readOnly
  28680. */
  28681. this.group = new Group();
  28682. // FIXME Not use a seperate text group?
  28683. var dumbGroup = new Group({
  28684. position: opt.position.slice(),
  28685. rotation: opt.rotation
  28686. });
  28687. // this.group.add(dumbGroup);
  28688. // this._dumbGroup = dumbGroup;
  28689. dumbGroup.updateTransform();
  28690. this._transform = dumbGroup.transform;
  28691. this._dumbGroup = dumbGroup;
  28692. };
  28693. AxisBuilder.prototype = {
  28694. constructor: AxisBuilder,
  28695. hasBuilder: function (name) {
  28696. return !!builders[name];
  28697. },
  28698. add: function (name) {
  28699. builders[name].call(this);
  28700. },
  28701. getGroup: function () {
  28702. return this.group;
  28703. }
  28704. };
  28705. var builders = {
  28706. /**
  28707. * @private
  28708. */
  28709. axisLine: function () {
  28710. var opt = this.opt;
  28711. var axisModel = this.axisModel;
  28712. if (!axisModel.get('axisLine.show')) {
  28713. return;
  28714. }
  28715. var extent = this.axisModel.axis.getExtent();
  28716. var matrix = this._transform;
  28717. var pt1 = [extent[0], 0];
  28718. var pt2 = [extent[1], 0];
  28719. if (matrix) {
  28720. applyTransform(pt1, pt1, matrix);
  28721. applyTransform(pt2, pt2, matrix);
  28722. }
  28723. var lineStyle = extend(
  28724. {
  28725. lineCap: 'round'
  28726. },
  28727. axisModel.getModel('axisLine.lineStyle').getLineStyle()
  28728. );
  28729. this.group.add(new Line(subPixelOptimizeLine({
  28730. // Id for animation
  28731. anid: 'line',
  28732. shape: {
  28733. x1: pt1[0],
  28734. y1: pt1[1],
  28735. x2: pt2[0],
  28736. y2: pt2[1]
  28737. },
  28738. style: lineStyle,
  28739. strokeContainThreshold: opt.strokeContainThreshold || 5,
  28740. silent: true,
  28741. z2: 1
  28742. })));
  28743. var arrows = axisModel.get('axisLine.symbol');
  28744. var arrowSize = axisModel.get('axisLine.symbolSize');
  28745. if (arrows != null) {
  28746. if (typeof arrows === 'string') {
  28747. // Use the same arrow for start and end point
  28748. arrows = [arrows, arrows];
  28749. }
  28750. if (typeof arrowSize === 'string'
  28751. || typeof arrowSize === 'number'
  28752. ) {
  28753. // Use the same size for width and height
  28754. arrowSize = [arrowSize, arrowSize];
  28755. }
  28756. var symbolWidth = arrowSize[0];
  28757. var symbolHeight = arrowSize[1];
  28758. each$1([
  28759. [opt.rotation + Math.PI / 2, pt1],
  28760. [opt.rotation - Math.PI / 2, pt2]
  28761. ], function (item, index) {
  28762. if (arrows[index] !== 'none' && arrows[index] != null) {
  28763. var symbol = createSymbol(
  28764. arrows[index],
  28765. -symbolWidth / 2,
  28766. -symbolHeight / 2,
  28767. symbolWidth,
  28768. symbolHeight,
  28769. lineStyle.stroke,
  28770. true
  28771. );
  28772. symbol.attr({
  28773. rotation: item[0],
  28774. position: item[1],
  28775. silent: true
  28776. });
  28777. this.group.add(symbol);
  28778. }
  28779. }, this);
  28780. }
  28781. },
  28782. /**
  28783. * @private
  28784. */
  28785. axisTickLabel: function () {
  28786. var axisModel = this.axisModel;
  28787. var opt = this.opt;
  28788. var tickEls = buildAxisTick(this, axisModel, opt);
  28789. var labelEls = buildAxisLabel(this, axisModel, opt);
  28790. fixMinMaxLabelShow(axisModel, labelEls, tickEls);
  28791. },
  28792. /**
  28793. * @private
  28794. */
  28795. axisName: function () {
  28796. var opt = this.opt;
  28797. var axisModel = this.axisModel;
  28798. var name = retrieve(opt.axisName, axisModel.get('name'));
  28799. if (!name) {
  28800. return;
  28801. }
  28802. var nameLocation = axisModel.get('nameLocation');
  28803. var nameDirection = opt.nameDirection;
  28804. var textStyleModel = axisModel.getModel('nameTextStyle');
  28805. var gap = axisModel.get('nameGap') || 0;
  28806. var extent = this.axisModel.axis.getExtent();
  28807. var gapSignal = extent[0] > extent[1] ? -1 : 1;
  28808. var pos = [
  28809. nameLocation === 'start'
  28810. ? extent[0] - gapSignal * gap
  28811. : nameLocation === 'end'
  28812. ? extent[1] + gapSignal * gap
  28813. : (extent[0] + extent[1]) / 2, // 'middle'
  28814. // Reuse labelOffset.
  28815. isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0
  28816. ];
  28817. var labelLayout;
  28818. var nameRotation = axisModel.get('nameRotate');
  28819. if (nameRotation != null) {
  28820. nameRotation = nameRotation * PI$2 / 180; // To radian.
  28821. }
  28822. var axisNameAvailableWidth;
  28823. if (isNameLocationCenter(nameLocation)) {
  28824. labelLayout = innerTextLayout(
  28825. opt.rotation,
  28826. nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.
  28827. nameDirection
  28828. );
  28829. }
  28830. else {
  28831. labelLayout = endTextLayout(
  28832. opt, nameLocation, nameRotation || 0, extent
  28833. );
  28834. axisNameAvailableWidth = opt.axisNameAvailableWidth;
  28835. if (axisNameAvailableWidth != null) {
  28836. axisNameAvailableWidth = Math.abs(
  28837. axisNameAvailableWidth / Math.sin(labelLayout.rotation)
  28838. );
  28839. !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);
  28840. }
  28841. }
  28842. var textFont = textStyleModel.getFont();
  28843. var truncateOpt = axisModel.get('nameTruncate', true) || {};
  28844. var ellipsis = truncateOpt.ellipsis;
  28845. var maxWidth = retrieve(
  28846. opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth
  28847. );
  28848. // FIXME
  28849. // truncate rich text? (consider performance)
  28850. var truncatedText = (ellipsis != null && maxWidth != null)
  28851. ? truncateText$1(
  28852. name, maxWidth, textFont, ellipsis,
  28853. {minChar: 2, placeholder: truncateOpt.placeholder}
  28854. )
  28855. : name;
  28856. var tooltipOpt = axisModel.get('tooltip', true);
  28857. var mainType = axisModel.mainType;
  28858. var formatterParams = {
  28859. componentType: mainType,
  28860. name: name,
  28861. $vars: ['name']
  28862. };
  28863. formatterParams[mainType + 'Index'] = axisModel.componentIndex;
  28864. var textEl = new Text({
  28865. // Id for animation
  28866. anid: 'name',
  28867. __fullText: name,
  28868. __truncatedText: truncatedText,
  28869. position: pos,
  28870. rotation: labelLayout.rotation,
  28871. silent: isSilent(axisModel),
  28872. z2: 1,
  28873. tooltip: (tooltipOpt && tooltipOpt.show)
  28874. ? extend({
  28875. content: name,
  28876. formatter: function () {
  28877. return name;
  28878. },
  28879. formatterParams: formatterParams
  28880. }, tooltipOpt)
  28881. : null
  28882. });
  28883. setTextStyle(textEl.style, textStyleModel, {
  28884. text: truncatedText,
  28885. textFont: textFont,
  28886. textFill: textStyleModel.getTextColor()
  28887. || axisModel.get('axisLine.lineStyle.color'),
  28888. textAlign: labelLayout.textAlign,
  28889. textVerticalAlign: labelLayout.textVerticalAlign
  28890. });
  28891. if (axisModel.get('triggerEvent')) {
  28892. textEl.eventData = makeAxisEventDataBase(axisModel);
  28893. textEl.eventData.targetType = 'axisName';
  28894. textEl.eventData.name = name;
  28895. }
  28896. // FIXME
  28897. this._dumbGroup.add(textEl);
  28898. textEl.updateTransform();
  28899. this.group.add(textEl);
  28900. textEl.decomposeTransform();
  28901. }
  28902. };
  28903. /**
  28904. * @public
  28905. * @static
  28906. * @param {Object} opt
  28907. * @param {number} axisRotation in radian
  28908. * @param {number} textRotation in radian
  28909. * @param {number} direction
  28910. * @return {Object} {
  28911. * rotation, // according to axis
  28912. * textAlign,
  28913. * textVerticalAlign
  28914. * }
  28915. */
  28916. var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {
  28917. var rotationDiff = remRadian(textRotation - axisRotation);
  28918. var textAlign;
  28919. var textVerticalAlign;
  28920. if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line.
  28921. textVerticalAlign = direction > 0 ? 'top' : 'bottom';
  28922. textAlign = 'center';
  28923. }
  28924. else if (isRadianAroundZero(rotationDiff - PI$2)) { // Label is inverse parallel with axis line.
  28925. textVerticalAlign = direction > 0 ? 'bottom' : 'top';
  28926. textAlign = 'center';
  28927. }
  28928. else {
  28929. textVerticalAlign = 'middle';
  28930. if (rotationDiff > 0 && rotationDiff < PI$2) {
  28931. textAlign = direction > 0 ? 'right' : 'left';
  28932. }
  28933. else {
  28934. textAlign = direction > 0 ? 'left' : 'right';
  28935. }
  28936. }
  28937. return {
  28938. rotation: rotationDiff,
  28939. textAlign: textAlign,
  28940. textVerticalAlign: textVerticalAlign
  28941. };
  28942. };
  28943. function endTextLayout(opt, textPosition, textRotate, extent) {
  28944. var rotationDiff = remRadian(textRotate - opt.rotation);
  28945. var textAlign;
  28946. var textVerticalAlign;
  28947. var inverse = extent[0] > extent[1];
  28948. var onLeft = (textPosition === 'start' && !inverse)
  28949. || (textPosition !== 'start' && inverse);
  28950. if (isRadianAroundZero(rotationDiff - PI$2 / 2)) {
  28951. textVerticalAlign = onLeft ? 'bottom' : 'top';
  28952. textAlign = 'center';
  28953. }
  28954. else if (isRadianAroundZero(rotationDiff - PI$2 * 1.5)) {
  28955. textVerticalAlign = onLeft ? 'top' : 'bottom';
  28956. textAlign = 'center';
  28957. }
  28958. else {
  28959. textVerticalAlign = 'middle';
  28960. if (rotationDiff < PI$2 * 1.5 && rotationDiff > PI$2 / 2) {
  28961. textAlign = onLeft ? 'left' : 'right';
  28962. }
  28963. else {
  28964. textAlign = onLeft ? 'right' : 'left';
  28965. }
  28966. }
  28967. return {
  28968. rotation: rotationDiff,
  28969. textAlign: textAlign,
  28970. textVerticalAlign: textVerticalAlign
  28971. };
  28972. }
  28973. function isSilent(axisModel) {
  28974. var tooltipOpt = axisModel.get('tooltip');
  28975. return axisModel.get('silent')
  28976. // Consider mouse cursor, add these restrictions.
  28977. || !(
  28978. axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show)
  28979. );
  28980. }
  28981. function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {
  28982. // If min or max are user set, we need to check
  28983. // If the tick on min(max) are overlap on their neighbour tick
  28984. // If they are overlapped, we need to hide the min(max) tick label
  28985. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  28986. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  28987. // FIXME
  28988. // Have not consider onBand yet, where tick els is more than label els.
  28989. labelEls = labelEls || [];
  28990. tickEls = tickEls || [];
  28991. var firstLabel = labelEls[0];
  28992. var nextLabel = labelEls[1];
  28993. var lastLabel = labelEls[labelEls.length - 1];
  28994. var prevLabel = labelEls[labelEls.length - 2];
  28995. var firstTick = tickEls[0];
  28996. var nextTick = tickEls[1];
  28997. var lastTick = tickEls[tickEls.length - 1];
  28998. var prevTick = tickEls[tickEls.length - 2];
  28999. if (showMinLabel === false) {
  29000. ignoreEl(firstLabel);
  29001. ignoreEl(firstTick);
  29002. }
  29003. else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {
  29004. if (showMinLabel) {
  29005. ignoreEl(nextLabel);
  29006. ignoreEl(nextTick);
  29007. }
  29008. else {
  29009. ignoreEl(firstLabel);
  29010. ignoreEl(firstTick);
  29011. }
  29012. }
  29013. if (showMaxLabel === false) {
  29014. ignoreEl(lastLabel);
  29015. ignoreEl(lastTick);
  29016. }
  29017. else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {
  29018. if (showMaxLabel) {
  29019. ignoreEl(prevLabel);
  29020. ignoreEl(prevTick);
  29021. }
  29022. else {
  29023. ignoreEl(lastLabel);
  29024. ignoreEl(lastTick);
  29025. }
  29026. }
  29027. }
  29028. function ignoreEl(el) {
  29029. el && (el.ignore = true);
  29030. }
  29031. function isTwoLabelOverlapped(current, next, labelLayout) {
  29032. // current and next has the same rotation.
  29033. var firstRect = current && current.getBoundingRect().clone();
  29034. var nextRect = next && next.getBoundingRect().clone();
  29035. if (!firstRect || !nextRect) {
  29036. return;
  29037. }
  29038. // When checking intersect of two rotated labels, we use mRotationBack
  29039. // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.
  29040. var mRotationBack = identity([]);
  29041. rotate(mRotationBack, mRotationBack, -current.rotation);
  29042. firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));
  29043. nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));
  29044. return firstRect.intersect(nextRect);
  29045. }
  29046. function isNameLocationCenter(nameLocation) {
  29047. return nameLocation === 'middle' || nameLocation === 'center';
  29048. }
  29049. /**
  29050. * @static
  29051. */
  29052. var ifIgnoreOnTick$1 = AxisBuilder.ifIgnoreOnTick = function (
  29053. axis,
  29054. i,
  29055. interval,
  29056. ticksCnt,
  29057. showMinLabel,
  29058. showMaxLabel
  29059. ) {
  29060. if (i === 0 && showMinLabel || i === ticksCnt - 1 && showMaxLabel) {
  29061. return false;
  29062. }
  29063. // FIXME
  29064. // Have not consider label overlap (if label is too long) yet.
  29065. var rawTick;
  29066. var scale$$1 = axis.scale;
  29067. return scale$$1.type === 'ordinal'
  29068. && (
  29069. typeof interval === 'function'
  29070. ? (
  29071. rawTick = scale$$1.getTicks()[i],
  29072. !interval(rawTick, scale$$1.getLabel(rawTick))
  29073. )
  29074. : i % (interval + 1)
  29075. );
  29076. };
  29077. /**
  29078. * @static
  29079. */
  29080. var getInterval$1 = AxisBuilder.getInterval = function (model, labelInterval) {
  29081. var interval = model.get('interval');
  29082. if (interval == null || interval == 'auto') {
  29083. interval = labelInterval;
  29084. }
  29085. return interval;
  29086. };
  29087. function buildAxisTick(axisBuilder, axisModel, opt) {
  29088. var axis = axisModel.axis;
  29089. if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) {
  29090. return;
  29091. }
  29092. var tickModel = axisModel.getModel('axisTick');
  29093. var lineStyleModel = tickModel.getModel('lineStyle');
  29094. var tickLen = tickModel.get('length');
  29095. var tickInterval = getInterval$1(tickModel, opt.labelInterval);
  29096. var ticksCoords = axis.getTicksCoords(tickModel.get('alignWithLabel'));
  29097. // FIXME
  29098. // Corresponds to ticksCoords ?
  29099. var ticks = axis.scale.getTicks();
  29100. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  29101. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  29102. var pt1 = [];
  29103. var pt2 = [];
  29104. var matrix = axisBuilder._transform;
  29105. var tickEls = [];
  29106. var ticksCnt = ticksCoords.length;
  29107. for (var i = 0; i < ticksCnt; i++) {
  29108. // Only ordinal scale support tick interval
  29109. if (ifIgnoreOnTick$1(
  29110. axis, i, tickInterval, ticksCnt,
  29111. showMinLabel, showMaxLabel
  29112. )) {
  29113. continue;
  29114. }
  29115. var tickCoord = ticksCoords[i];
  29116. pt1[0] = tickCoord;
  29117. pt1[1] = 0;
  29118. pt2[0] = tickCoord;
  29119. pt2[1] = opt.tickDirection * tickLen;
  29120. if (matrix) {
  29121. applyTransform(pt1, pt1, matrix);
  29122. applyTransform(pt2, pt2, matrix);
  29123. }
  29124. // Tick line, Not use group transform to have better line draw
  29125. var tickEl = new Line(subPixelOptimizeLine({
  29126. // Id for animation
  29127. anid: 'tick_' + ticks[i],
  29128. shape: {
  29129. x1: pt1[0],
  29130. y1: pt1[1],
  29131. x2: pt2[0],
  29132. y2: pt2[1]
  29133. },
  29134. style: defaults(
  29135. lineStyleModel.getLineStyle(),
  29136. {
  29137. stroke: axisModel.get('axisLine.lineStyle.color')
  29138. }
  29139. ),
  29140. z2: 2,
  29141. silent: true
  29142. }));
  29143. axisBuilder.group.add(tickEl);
  29144. tickEls.push(tickEl);
  29145. }
  29146. return tickEls;
  29147. }
  29148. function buildAxisLabel(axisBuilder, axisModel, opt) {
  29149. var axis = axisModel.axis;
  29150. var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show'));
  29151. if (!show || axis.scale.isBlank()) {
  29152. return;
  29153. }
  29154. var labelModel = axisModel.getModel('axisLabel');
  29155. var labelMargin = labelModel.get('margin');
  29156. var ticks = axis.scale.getTicks();
  29157. var labels = axisModel.getFormattedLabels();
  29158. // Special label rotate.
  29159. var labelRotation = (
  29160. retrieve(opt.labelRotate, labelModel.get('rotate')) || 0
  29161. ) * PI$2 / 180;
  29162. var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);
  29163. var categoryData = axisModel.getCategories();
  29164. var labelEls = [];
  29165. var silent = isSilent(axisModel);
  29166. var triggerEvent = axisModel.get('triggerEvent');
  29167. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  29168. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  29169. each$1(ticks, function (tickVal, index) {
  29170. if (ifIgnoreOnTick$1(
  29171. axis, index, opt.labelInterval, ticks.length,
  29172. showMinLabel, showMaxLabel
  29173. )) {
  29174. return;
  29175. }
  29176. var itemLabelModel = labelModel;
  29177. if (categoryData && categoryData[tickVal] && categoryData[tickVal].textStyle) {
  29178. itemLabelModel = new Model(
  29179. categoryData[tickVal].textStyle, labelModel, axisModel.ecModel
  29180. );
  29181. }
  29182. var textColor = itemLabelModel.getTextColor()
  29183. || axisModel.get('axisLine.lineStyle.color');
  29184. var tickCoord = axis.dataToCoord(tickVal);
  29185. var pos = [
  29186. tickCoord,
  29187. opt.labelOffset + opt.labelDirection * labelMargin
  29188. ];
  29189. var labelStr = axis.scale.getLabel(tickVal);
  29190. var textEl = new Text({
  29191. // Id for animation
  29192. anid: 'label_' + tickVal,
  29193. position: pos,
  29194. rotation: labelLayout.rotation,
  29195. silent: silent,
  29196. z2: 10
  29197. });
  29198. setTextStyle(textEl.style, itemLabelModel, {
  29199. text: labels[index],
  29200. textAlign: itemLabelModel.getShallow('align', true)
  29201. || labelLayout.textAlign,
  29202. textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true)
  29203. || itemLabelModel.getShallow('baseline', true)
  29204. || labelLayout.textVerticalAlign,
  29205. textFill: typeof textColor === 'function'
  29206. ? textColor(
  29207. // (1) In category axis with data zoom, tick is not the original
  29208. // index of axis.data. So tick should not be exposed to user
  29209. // in category axis.
  29210. // (2) Compatible with previous version, which always returns labelStr.
  29211. // But in interval scale labelStr is like '223,445', which maked
  29212. // user repalce ','. So we modify it to return original val but remain
  29213. // it as 'string' to avoid error in replacing.
  29214. axis.type === 'category' ? labelStr : axis.type === 'value' ? tickVal + '' : tickVal,
  29215. index
  29216. )
  29217. : textColor
  29218. });
  29219. // Pack data for mouse event
  29220. if (triggerEvent) {
  29221. textEl.eventData = makeAxisEventDataBase(axisModel);
  29222. textEl.eventData.targetType = 'axisLabel';
  29223. textEl.eventData.value = labelStr;
  29224. }
  29225. // FIXME
  29226. axisBuilder._dumbGroup.add(textEl);
  29227. textEl.updateTransform();
  29228. labelEls.push(textEl);
  29229. axisBuilder.group.add(textEl);
  29230. textEl.decomposeTransform();
  29231. });
  29232. return labelEls;
  29233. }
  29234. // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
  29235. // allAxesInfo should be updated when setOption performed.
  29236. function fixValue(axisModel) {
  29237. var axisInfo = getAxisInfo(axisModel);
  29238. if (!axisInfo) {
  29239. return;
  29240. }
  29241. var axisPointerModel = axisInfo.axisPointerModel;
  29242. var scale = axisInfo.axis.scale;
  29243. var option = axisPointerModel.option;
  29244. var status = axisPointerModel.get('status');
  29245. var value = axisPointerModel.get('value');
  29246. // Parse init value for category and time axis.
  29247. if (value != null) {
  29248. value = scale.parse(value);
  29249. }
  29250. var useHandle = isHandleTrigger(axisPointerModel);
  29251. // If `handle` used, `axisPointer` will always be displayed, so value
  29252. // and status should be initialized.
  29253. if (status == null) {
  29254. option.status = useHandle ? 'show' : 'hide';
  29255. }
  29256. var extent = scale.getExtent().slice();
  29257. extent[0] > extent[1] && extent.reverse();
  29258. if (// Pick a value on axis when initializing.
  29259. value == null
  29260. // If both `handle` and `dataZoom` are used, value may be out of axis extent,
  29261. // where we should re-pick a value to keep `handle` displaying normally.
  29262. || value > extent[1]
  29263. ) {
  29264. // Make handle displayed on the end of the axis when init, which looks better.
  29265. value = extent[1];
  29266. }
  29267. if (value < extent[0]) {
  29268. value = extent[0];
  29269. }
  29270. option.value = value;
  29271. if (useHandle) {
  29272. option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';
  29273. }
  29274. }
  29275. function getAxisInfo(axisModel) {
  29276. var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;
  29277. return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];
  29278. }
  29279. function getAxisPointerModel(axisModel) {
  29280. var axisInfo = getAxisInfo(axisModel);
  29281. return axisInfo && axisInfo.axisPointerModel;
  29282. }
  29283. function isHandleTrigger(axisPointerModel) {
  29284. return !!axisPointerModel.get('handle.show');
  29285. }
  29286. /**
  29287. * @param {module:echarts/model/Model} model
  29288. * @return {string} unique key
  29289. */
  29290. function makeKey(model) {
  29291. return model.type + '||' + model.id;
  29292. }
  29293. /**
  29294. * Base class of AxisView.
  29295. */
  29296. var AxisView = extendComponentView({
  29297. type: 'axis',
  29298. /**
  29299. * @private
  29300. */
  29301. _axisPointer: null,
  29302. /**
  29303. * @protected
  29304. * @type {string}
  29305. */
  29306. axisPointerClass: null,
  29307. /**
  29308. * @override
  29309. */
  29310. render: function (axisModel, ecModel, api, payload) {
  29311. // FIXME
  29312. // This process should proformed after coordinate systems updated
  29313. // (axis scale updated), and should be performed each time update.
  29314. // So put it here temporarily, although it is not appropriate to
  29315. // put a model-writing procedure in `view`.
  29316. this.axisPointerClass && fixValue(axisModel);
  29317. AxisView.superApply(this, 'render', arguments);
  29318. updateAxisPointer(this, axisModel, ecModel, api, payload, true);
  29319. },
  29320. /**
  29321. * Action handler.
  29322. * @public
  29323. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  29324. * @param {module:echarts/model/Global} ecModel
  29325. * @param {module:echarts/ExtensionAPI} api
  29326. * @param {Object} payload
  29327. */
  29328. updateAxisPointer: function (axisModel, ecModel, api, payload, force) {
  29329. updateAxisPointer(this, axisModel, ecModel, api, payload, false);
  29330. },
  29331. /**
  29332. * @override
  29333. */
  29334. remove: function (ecModel, api) {
  29335. var axisPointer = this._axisPointer;
  29336. axisPointer && axisPointer.remove(api);
  29337. AxisView.superApply(this, 'remove', arguments);
  29338. },
  29339. /**
  29340. * @override
  29341. */
  29342. dispose: function (ecModel, api) {
  29343. disposeAxisPointer(this, api);
  29344. AxisView.superApply(this, 'dispose', arguments);
  29345. }
  29346. });
  29347. function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) {
  29348. var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass);
  29349. if (!Clazz) {
  29350. return;
  29351. }
  29352. var axisPointerModel = getAxisPointerModel(axisModel);
  29353. axisPointerModel
  29354. ? (axisView._axisPointer || (axisView._axisPointer = new Clazz()))
  29355. .render(axisModel, axisPointerModel, api, forceRender)
  29356. : disposeAxisPointer(axisView, api);
  29357. }
  29358. function disposeAxisPointer(axisView, ecModel, api) {
  29359. var axisPointer = axisView._axisPointer;
  29360. axisPointer && axisPointer.dispose(ecModel, api);
  29361. axisView._axisPointer = null;
  29362. }
  29363. var axisPointerClazz = [];
  29364. AxisView.registerAxisPointerClass = function (type, clazz) {
  29365. if (__DEV__) {
  29366. if (axisPointerClazz[type]) {
  29367. throw new Error('axisPointer ' + type + ' exists');
  29368. }
  29369. }
  29370. axisPointerClazz[type] = clazz;
  29371. };
  29372. AxisView.getAxisPointerClass = function (type) {
  29373. return type && axisPointerClazz[type];
  29374. };
  29375. /**
  29376. * @param {Object} opt {labelInside}
  29377. * @return {Object} {
  29378. * position, rotation, labelDirection, labelOffset,
  29379. * tickDirection, labelRotate, labelInterval, z2
  29380. * }
  29381. */
  29382. function layout$1(gridModel, axisModel, opt) {
  29383. opt = opt || {};
  29384. var grid = gridModel.coordinateSystem;
  29385. var axis = axisModel.axis;
  29386. var layout = {};
  29387. var rawAxisPosition = axis.position;
  29388. var axisPosition = axis.onZero ? 'onZero' : rawAxisPosition;
  29389. var axisDim = axis.dim;
  29390. var rect = grid.getRect();
  29391. var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];
  29392. var idx = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2};
  29393. var axisOffset = axisModel.get('offset') || 0;
  29394. var posBound = axisDim === 'x'
  29395. ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset]
  29396. : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];
  29397. if (axis.onZero) {
  29398. var otherAxis = grid.getAxis(axisDim === 'x' ? 'y' : 'x', axis.onZeroAxisIndex);
  29399. var onZeroCoord = otherAxis.toGlobalCoord(otherAxis.dataToCoord(0));
  29400. posBound[idx['onZero']] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);
  29401. }
  29402. // Axis position
  29403. layout.position = [
  29404. axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0],
  29405. axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]
  29406. ];
  29407. // Axis rotation
  29408. layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1);
  29409. // Tick and label direction, x y is axisDim
  29410. var dirMap = {top: -1, bottom: 1, left: -1, right: 1};
  29411. layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];
  29412. layout.labelOffset = axis.onZero ? posBound[idx[rawAxisPosition]] - posBound[idx['onZero']] : 0;
  29413. if (axisModel.get('axisTick.inside')) {
  29414. layout.tickDirection = -layout.tickDirection;
  29415. }
  29416. if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {
  29417. layout.labelDirection = -layout.labelDirection;
  29418. }
  29419. // Special label rotation
  29420. var labelRotate = axisModel.get('axisLabel.rotate');
  29421. layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate;
  29422. // label interval when auto mode.
  29423. layout.labelInterval = axis.getLabelInterval();
  29424. // Over splitLine and splitArea
  29425. layout.z2 = 1;
  29426. return layout;
  29427. }
  29428. var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick;
  29429. var getInterval = AxisBuilder.getInterval;
  29430. var axisBuilderAttrs = [
  29431. 'axisLine', 'axisTickLabel', 'axisName'
  29432. ];
  29433. var selfBuilderAttrs = [
  29434. 'splitArea', 'splitLine'
  29435. ];
  29436. // function getAlignWithLabel(model, axisModel) {
  29437. // var alignWithLabel = model.get('alignWithLabel');
  29438. // if (alignWithLabel === 'auto') {
  29439. // alignWithLabel = axisModel.get('axisTick.alignWithLabel');
  29440. // }
  29441. // return alignWithLabel;
  29442. // }
  29443. var CartesianAxisView = AxisView.extend({
  29444. type: 'cartesianAxis',
  29445. axisPointerClass: 'CartesianAxisPointer',
  29446. /**
  29447. * @override
  29448. */
  29449. render: function (axisModel, ecModel, api, payload) {
  29450. this.group.removeAll();
  29451. var oldAxisGroup = this._axisGroup;
  29452. this._axisGroup = new Group();
  29453. this.group.add(this._axisGroup);
  29454. if (!axisModel.get('show')) {
  29455. return;
  29456. }
  29457. var gridModel = axisModel.getCoordSysModel();
  29458. var layout = layout$1(gridModel, axisModel);
  29459. var axisBuilder = new AxisBuilder(axisModel, layout);
  29460. each$1(axisBuilderAttrs, axisBuilder.add, axisBuilder);
  29461. this._axisGroup.add(axisBuilder.getGroup());
  29462. each$1(selfBuilderAttrs, function (name) {
  29463. if (axisModel.get(name + '.show')) {
  29464. this['_' + name](axisModel, gridModel, layout.labelInterval);
  29465. }
  29466. }, this);
  29467. groupTransition(oldAxisGroup, this._axisGroup, axisModel);
  29468. CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);
  29469. },
  29470. /**
  29471. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  29472. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  29473. * @param {number|Function} labelInterval
  29474. * @private
  29475. */
  29476. _splitLine: function (axisModel, gridModel, labelInterval) {
  29477. var axis = axisModel.axis;
  29478. if (axis.scale.isBlank()) {
  29479. return;
  29480. }
  29481. var splitLineModel = axisModel.getModel('splitLine');
  29482. var lineStyleModel = splitLineModel.getModel('lineStyle');
  29483. var lineColors = lineStyleModel.get('color');
  29484. var lineInterval = getInterval(splitLineModel, labelInterval);
  29485. lineColors = isArray(lineColors) ? lineColors : [lineColors];
  29486. var gridRect = gridModel.coordinateSystem.getRect();
  29487. var isHorizontal = axis.isHorizontal();
  29488. var lineCount = 0;
  29489. var ticksCoords = axis.getTicksCoords(
  29490. // splitLineModel.get('alignWithLabel')
  29491. );
  29492. var ticks = axis.scale.getTicks();
  29493. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  29494. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  29495. var p1 = [];
  29496. var p2 = [];
  29497. // Simple optimization
  29498. // Batching the lines if color are the same
  29499. var lineStyle = lineStyleModel.getLineStyle();
  29500. for (var i = 0; i < ticksCoords.length; i++) {
  29501. if (ifIgnoreOnTick(
  29502. axis, i, lineInterval, ticksCoords.length,
  29503. showMinLabel, showMaxLabel
  29504. )) {
  29505. continue;
  29506. }
  29507. var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
  29508. if (isHorizontal) {
  29509. p1[0] = tickCoord;
  29510. p1[1] = gridRect.y;
  29511. p2[0] = tickCoord;
  29512. p2[1] = gridRect.y + gridRect.height;
  29513. }
  29514. else {
  29515. p1[0] = gridRect.x;
  29516. p1[1] = tickCoord;
  29517. p2[0] = gridRect.x + gridRect.width;
  29518. p2[1] = tickCoord;
  29519. }
  29520. var colorIndex = (lineCount++) % lineColors.length;
  29521. this._axisGroup.add(new Line(subPixelOptimizeLine({
  29522. anid: 'line_' + ticks[i],
  29523. shape: {
  29524. x1: p1[0],
  29525. y1: p1[1],
  29526. x2: p2[0],
  29527. y2: p2[1]
  29528. },
  29529. style: defaults({
  29530. stroke: lineColors[colorIndex]
  29531. }, lineStyle),
  29532. silent: true
  29533. })));
  29534. }
  29535. },
  29536. /**
  29537. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  29538. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  29539. * @param {number|Function} labelInterval
  29540. * @private
  29541. */
  29542. _splitArea: function (axisModel, gridModel, labelInterval) {
  29543. var axis = axisModel.axis;
  29544. if (axis.scale.isBlank()) {
  29545. return;
  29546. }
  29547. var splitAreaModel = axisModel.getModel('splitArea');
  29548. var areaStyleModel = splitAreaModel.getModel('areaStyle');
  29549. var areaColors = areaStyleModel.get('color');
  29550. var gridRect = gridModel.coordinateSystem.getRect();
  29551. var ticksCoords = axis.getTicksCoords(
  29552. // splitAreaModel.get('alignWithLabel')
  29553. );
  29554. var ticks = axis.scale.getTicks();
  29555. var prevX = axis.toGlobalCoord(ticksCoords[0]);
  29556. var prevY = axis.toGlobalCoord(ticksCoords[0]);
  29557. var count = 0;
  29558. var areaInterval = getInterval(splitAreaModel, labelInterval);
  29559. var areaStyle = areaStyleModel.getAreaStyle();
  29560. areaColors = isArray(areaColors) ? areaColors : [areaColors];
  29561. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  29562. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  29563. for (var i = 1; i < ticksCoords.length; i++) {
  29564. if (ifIgnoreOnTick(
  29565. axis, i, areaInterval, ticksCoords.length,
  29566. showMinLabel, showMaxLabel
  29567. )) {
  29568. continue;
  29569. }
  29570. var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
  29571. var x;
  29572. var y;
  29573. var width;
  29574. var height;
  29575. if (axis.isHorizontal()) {
  29576. x = prevX;
  29577. y = gridRect.y;
  29578. width = tickCoord - x;
  29579. height = gridRect.height;
  29580. }
  29581. else {
  29582. x = gridRect.x;
  29583. y = prevY;
  29584. width = gridRect.width;
  29585. height = tickCoord - y;
  29586. }
  29587. var colorIndex = (count++) % areaColors.length;
  29588. this._axisGroup.add(new Rect({
  29589. anid: 'area_' + ticks[i],
  29590. shape: {
  29591. x: x,
  29592. y: y,
  29593. width: width,
  29594. height: height
  29595. },
  29596. style: defaults({
  29597. fill: areaColors[colorIndex]
  29598. }, areaStyle),
  29599. silent: true
  29600. }));
  29601. prevX = x + width;
  29602. prevY = y + height;
  29603. }
  29604. }
  29605. });
  29606. CartesianAxisView.extend({
  29607. type: 'xAxis'
  29608. });
  29609. CartesianAxisView.extend({
  29610. type: 'yAxis'
  29611. });
  29612. // Grid view
  29613. extendComponentView({
  29614. type: 'grid',
  29615. render: function (gridModel, ecModel) {
  29616. this.group.removeAll();
  29617. if (gridModel.get('show')) {
  29618. this.group.add(new Rect({
  29619. shape: gridModel.coordinateSystem.getRect(),
  29620. style: defaults({
  29621. fill: gridModel.get('backgroundColor')
  29622. }, gridModel.getItemStyle()),
  29623. silent: true,
  29624. z2: -1
  29625. }));
  29626. }
  29627. }
  29628. });
  29629. registerPreprocessor(function (option) {
  29630. // Only create grid when need
  29631. if (option.xAxis && option.yAxis && !option.grid) {
  29632. option.grid = {};
  29633. }
  29634. });
  29635. // In case developer forget to include grid component
  29636. registerVisual(visualSymbol('line', 'circle', 'line'));
  29637. registerLayout(layoutPoints('line'));
  29638. // Down sample after filter
  29639. registerProcessor(
  29640. PRIORITY.PROCESSOR.STATISTIC,
  29641. dataSample('line')
  29642. );
  29643. var BaseBarSeries = SeriesModel.extend({
  29644. type: 'series.__base_bar__',
  29645. getInitialData: function (option, ecModel) {
  29646. return createListFromArray(this.getSource(), this);
  29647. },
  29648. getMarkerPosition: function (value) {
  29649. var coordSys = this.coordinateSystem;
  29650. if (coordSys) {
  29651. // PENDING if clamp ?
  29652. var pt = coordSys.dataToPoint(coordSys.clampData(value));
  29653. var data = this.getData();
  29654. var offset = data.getLayout('offset');
  29655. var size = data.getLayout('size');
  29656. var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;
  29657. pt[offsetIndex] += offset + size / 2;
  29658. return pt;
  29659. }
  29660. return [NaN, NaN];
  29661. },
  29662. defaultOption: {
  29663. zlevel: 0, // 一级层叠
  29664. z: 2, // 二级层叠
  29665. coordinateSystem: 'cartesian2d',
  29666. legendHoverLink: true,
  29667. // stack: null
  29668. // Cartesian coordinate system
  29669. // xAxisIndex: 0,
  29670. // yAxisIndex: 0,
  29671. // 最小高度改为0
  29672. barMinHeight: 0,
  29673. // 最小角度为0,仅对极坐标系下的柱状图有效
  29674. barMinAngle: 0,
  29675. // cursor: null,
  29676. // barMaxWidth: null,
  29677. // 默认自适应
  29678. // barWidth: null,
  29679. // 柱间距离,默认为柱形宽度的30%,可设固定值
  29680. // barGap: '30%',
  29681. // 类目间柱形距离,默认为类目间距的20%,可设固定值
  29682. // barCategoryGap: '20%',
  29683. // label: {
  29684. // show: false
  29685. // },
  29686. itemStyle: {},
  29687. emphasis: {}
  29688. }
  29689. });
  29690. BaseBarSeries.extend({
  29691. type: 'series.bar',
  29692. dependencies: ['grid', 'polar'],
  29693. brushSelector: 'rect'
  29694. });
  29695. function setLabel(
  29696. normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside
  29697. ) {
  29698. var labelModel = itemModel.getModel('label');
  29699. var hoverLabelModel = itemModel.getModel('emphasis.label');
  29700. setLabelStyle(
  29701. normalStyle, hoverStyle, labelModel, hoverLabelModel,
  29702. {
  29703. labelFetcher: seriesModel,
  29704. labelDataIndex: dataIndex,
  29705. defaultText: getDefaultLabel(seriesModel.getData(), dataIndex),
  29706. isRectText: true,
  29707. autoColor: color
  29708. }
  29709. );
  29710. fixPosition(normalStyle);
  29711. fixPosition(hoverStyle);
  29712. }
  29713. function fixPosition(style, labelPositionOutside) {
  29714. if (style.textPosition === 'outside') {
  29715. style.textPosition = labelPositionOutside;
  29716. }
  29717. }
  29718. var getBarItemStyle = makeStyleMapper(
  29719. [
  29720. ['fill', 'color'],
  29721. ['stroke', 'borderColor'],
  29722. ['lineWidth', 'borderWidth'],
  29723. // Compatitable with 2
  29724. ['stroke', 'barBorderColor'],
  29725. ['lineWidth', 'barBorderWidth'],
  29726. ['opacity'],
  29727. ['shadowBlur'],
  29728. ['shadowOffsetX'],
  29729. ['shadowOffsetY'],
  29730. ['shadowColor']
  29731. ]
  29732. );
  29733. var barItemStyle = {
  29734. getBarItemStyle: function (excludes) {
  29735. var style = getBarItemStyle(this, excludes);
  29736. if (this.getBorderLineDash) {
  29737. var lineDash = this.getBorderLineDash();
  29738. lineDash && (style.lineDash = lineDash);
  29739. }
  29740. return style;
  29741. }
  29742. };
  29743. var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'barBorderWidth'];
  29744. // FIXME
  29745. // Just for compatible with ec2.
  29746. extend(Model.prototype, barItemStyle);
  29747. extendChartView({
  29748. type: 'bar',
  29749. render: function (seriesModel, ecModel, api) {
  29750. var coordinateSystemType = seriesModel.get('coordinateSystem');
  29751. if (coordinateSystemType === 'cartesian2d'
  29752. || coordinateSystemType === 'polar'
  29753. ) {
  29754. this._render(seriesModel, ecModel, api);
  29755. }
  29756. else if (__DEV__) {
  29757. console.warn('Only cartesian2d and polar supported for bar.');
  29758. }
  29759. return this.group;
  29760. },
  29761. dispose: noop,
  29762. _render: function (seriesModel, ecModel, api) {
  29763. var group = this.group;
  29764. var data = seriesModel.getData();
  29765. var oldData = this._data;
  29766. var coord = seriesModel.coordinateSystem;
  29767. var baseAxis = coord.getBaseAxis();
  29768. var isHorizontalOrRadial;
  29769. if (coord.type === 'cartesian2d') {
  29770. isHorizontalOrRadial = baseAxis.isHorizontal();
  29771. }
  29772. else if (coord.type === 'polar') {
  29773. isHorizontalOrRadial = baseAxis.dim === 'angle';
  29774. }
  29775. var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;
  29776. data.diff(oldData)
  29777. .add(function (dataIndex) {
  29778. if (!data.hasValue(dataIndex)) {
  29779. return;
  29780. }
  29781. var itemModel = data.getItemModel(dataIndex);
  29782. var layout = getLayout[coord.type](data, dataIndex, itemModel);
  29783. var el = elementCreator[coord.type](
  29784. data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel
  29785. );
  29786. data.setItemGraphicEl(dataIndex, el);
  29787. group.add(el);
  29788. updateStyle(
  29789. el, data, dataIndex, itemModel, layout,
  29790. seriesModel, isHorizontalOrRadial, coord.type === 'polar'
  29791. );
  29792. })
  29793. .update(function (newIndex, oldIndex) {
  29794. var el = oldData.getItemGraphicEl(oldIndex);
  29795. if (!data.hasValue(newIndex)) {
  29796. group.remove(el);
  29797. return;
  29798. }
  29799. var itemModel = data.getItemModel(newIndex);
  29800. var layout = getLayout[coord.type](data, newIndex, itemModel);
  29801. if (el) {
  29802. updateProps(el, {shape: layout}, animationModel, newIndex);
  29803. }
  29804. else {
  29805. el = elementCreator[coord.type](
  29806. data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true
  29807. );
  29808. }
  29809. data.setItemGraphicEl(newIndex, el);
  29810. // Add back
  29811. group.add(el);
  29812. updateStyle(
  29813. el, data, newIndex, itemModel, layout,
  29814. seriesModel, isHorizontalOrRadial, coord.type === 'polar'
  29815. );
  29816. })
  29817. .remove(function (dataIndex) {
  29818. var el = oldData.getItemGraphicEl(dataIndex);
  29819. if (coord.type === 'cartesian2d') {
  29820. el && removeRect(dataIndex, animationModel, el);
  29821. }
  29822. else {
  29823. el && removeSector(dataIndex, animationModel, el);
  29824. }
  29825. })
  29826. .execute();
  29827. this._data = data;
  29828. },
  29829. remove: function (ecModel, api) {
  29830. var group = this.group;
  29831. var data = this._data;
  29832. if (ecModel.get('animation')) {
  29833. if (data) {
  29834. data.eachItemGraphicEl(function (el) {
  29835. if (el.type === 'sector') {
  29836. removeSector(el.dataIndex, ecModel, el);
  29837. }
  29838. else {
  29839. removeRect(el.dataIndex, ecModel, el);
  29840. }
  29841. });
  29842. }
  29843. }
  29844. else {
  29845. group.removeAll();
  29846. }
  29847. }
  29848. });
  29849. var elementCreator = {
  29850. cartesian2d: function (
  29851. data, dataIndex, itemModel, layout, isHorizontal,
  29852. animationModel, isUpdate
  29853. ) {
  29854. var rect = new Rect({shape: extend({}, layout)});
  29855. // Animation
  29856. if (animationModel) {
  29857. var rectShape = rect.shape;
  29858. var animateProperty = isHorizontal ? 'height' : 'width';
  29859. var animateTarget = {};
  29860. rectShape[animateProperty] = 0;
  29861. animateTarget[animateProperty] = layout[animateProperty];
  29862. graphic[isUpdate ? 'updateProps' : 'initProps'](rect, {
  29863. shape: animateTarget
  29864. }, animationModel, dataIndex);
  29865. }
  29866. return rect;
  29867. },
  29868. polar: function (
  29869. data, dataIndex, itemModel, layout, isRadial,
  29870. animationModel, isUpdate
  29871. ) {
  29872. var sector = new Sector({shape: extend({}, layout)});
  29873. // Animation
  29874. if (animationModel) {
  29875. var sectorShape = sector.shape;
  29876. var animateProperty = isRadial ? 'r' : 'endAngle';
  29877. var animateTarget = {};
  29878. sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;
  29879. animateTarget[animateProperty] = layout[animateProperty];
  29880. graphic[isUpdate ? 'updateProps' : 'initProps'](sector, {
  29881. shape: animateTarget
  29882. }, animationModel, dataIndex);
  29883. }
  29884. return sector;
  29885. }
  29886. };
  29887. function removeRect(dataIndex, animationModel, el) {
  29888. // Not show text when animating
  29889. el.style.text = null;
  29890. updateProps(el, {
  29891. shape: {
  29892. width: 0
  29893. }
  29894. }, animationModel, dataIndex, function () {
  29895. el.parent && el.parent.remove(el);
  29896. });
  29897. }
  29898. function removeSector(dataIndex, animationModel, el) {
  29899. // Not show text when animating
  29900. el.style.text = null;
  29901. updateProps(el, {
  29902. shape: {
  29903. r: el.shape.r0
  29904. }
  29905. }, animationModel, dataIndex, function () {
  29906. el.parent && el.parent.remove(el);
  29907. });
  29908. }
  29909. var getLayout = {
  29910. cartesian2d: function (data, dataIndex, itemModel) {
  29911. var layout = data.getItemLayout(dataIndex);
  29912. var fixedLineWidth = getLineWidth(itemModel, layout);
  29913. // fix layout with lineWidth
  29914. var signX = layout.width > 0 ? 1 : -1;
  29915. var signY = layout.height > 0 ? 1 : -1;
  29916. return {
  29917. x: layout.x + signX * fixedLineWidth / 2,
  29918. y: layout.y + signY * fixedLineWidth / 2,
  29919. width: layout.width - signX * fixedLineWidth,
  29920. height: layout.height - signY * fixedLineWidth
  29921. };
  29922. },
  29923. polar: function (data, dataIndex, itemModel) {
  29924. var layout = data.getItemLayout(dataIndex);
  29925. return {
  29926. cx: layout.cx,
  29927. cy: layout.cy,
  29928. r0: layout.r0,
  29929. r: layout.r,
  29930. startAngle: layout.startAngle,
  29931. endAngle: layout.endAngle
  29932. };
  29933. }
  29934. };
  29935. function updateStyle(
  29936. el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal, isPolar
  29937. ) {
  29938. var color = data.getItemVisual(dataIndex, 'color');
  29939. var opacity = data.getItemVisual(dataIndex, 'opacity');
  29940. var itemStyleModel = itemModel.getModel('itemStyle');
  29941. var hoverStyle = itemModel.getModel('emphasis.itemStyle').getBarItemStyle();
  29942. if (!isPolar) {
  29943. el.setShape('r', itemStyleModel.get('barBorderRadius') || 0);
  29944. }
  29945. el.useStyle(defaults(
  29946. {
  29947. fill: color,
  29948. opacity: opacity
  29949. },
  29950. itemStyleModel.getBarItemStyle()
  29951. ));
  29952. var cursorStyle = itemModel.getShallow('cursor');
  29953. cursorStyle && el.attr('cursor', cursorStyle);
  29954. var labelPositionOutside = isHorizontal
  29955. ? (layout.height > 0 ? 'bottom' : 'top')
  29956. : (layout.width > 0 ? 'left' : 'right');
  29957. if (!isPolar) {
  29958. setLabel(
  29959. el.style, hoverStyle, itemModel, color,
  29960. seriesModel, dataIndex, labelPositionOutside
  29961. );
  29962. }
  29963. setHoverStyle(el, hoverStyle);
  29964. }
  29965. // In case width or height are too small.
  29966. function getLineWidth(itemModel, rawLayout) {
  29967. var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;
  29968. return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height));
  29969. }
  29970. // In case developer forget to include grid component
  29971. registerLayout(curry(layout, 'bar'));
  29972. // Visual coding for legend
  29973. registerVisual(function (ecModel) {
  29974. ecModel.eachSeriesByType('bar', function (seriesModel) {
  29975. var data = seriesModel.getData();
  29976. data.setVisual('legendSymbol', 'roundRect');
  29977. });
  29978. });
  29979. /**
  29980. * [Usage]:
  29981. * (1)
  29982. * createListSimply(seriesModel, ['value']);
  29983. * (2)
  29984. * createListSimply(seriesModel, {
  29985. * coordDimensions: ['value'],
  29986. * dimensionsCount: 5
  29987. * });
  29988. *
  29989. * @param {module:echarts/model/Series} seriesModel
  29990. * @param {Object|Array.<string|Object>} opt opt or coordDimensions
  29991. * The options in opt, see `echarts/data/helper/createDimensions`
  29992. * @param {Array.<string>} [nameList]
  29993. * @return {module:echarts/data/List}
  29994. */
  29995. var createListSimply = function (seriesModel, opt, nameList) {
  29996. opt = isArray(opt) && {coordDimensions: opt} || extend({}, opt);
  29997. var source = seriesModel.getSource();
  29998. var dimensionsInfo = createDimensions(source, opt);
  29999. var list = new List(dimensionsInfo, seriesModel);
  30000. list.initData(source, nameList);
  30001. return list;
  30002. };
  30003. /**
  30004. * Data selectable mixin for chart series.
  30005. * To eanble data select, option of series must have `selectedMode`.
  30006. * And each data item will use `selected` to toggle itself selected status
  30007. */
  30008. var dataSelectableMixin = {
  30009. /**
  30010. * @param {Array.<Object>|module:echars/data/List} targetList
  30011. * If targetList is an array, it should like [{name: ..., value: ...}, ...].
  30012. * If targetList is a "List", it must have coordDim: 'value' dimension and name.
  30013. */
  30014. updateSelectedMap: function (targetList) {
  30015. if (isArray(targetList)) {
  30016. this._targetList = targetList.slice();
  30017. }
  30018. else {
  30019. var ecList = targetList;
  30020. var valueDim = ecList.mapDimension('value');
  30021. var targetList = this._targetList = [];
  30022. for (var i = 0, len = ecList.count(); i < len; i++) {
  30023. targetList.push({
  30024. name: ecList.getName(i),
  30025. value: ecList.get(valueDim, i),
  30026. selected: retrieveRawAttr(ecList, i, 'selected')
  30027. });
  30028. }
  30029. }
  30030. this._selectTargetMap = reduce(targetList || [], function (targetMap, target) {
  30031. targetMap.set(target.name, target);
  30032. return targetMap;
  30033. }, createHashMap());
  30034. },
  30035. /**
  30036. * Either name or id should be passed as input here.
  30037. * If both of them are defined, id is used.
  30038. *
  30039. * @param {string|undefined} name name of data
  30040. * @param {number|undefined} id dataIndex of data
  30041. */
  30042. // PENGING If selectedMode is null ?
  30043. select: function (name, id) {
  30044. var target = id != null
  30045. ? this._targetList[id]
  30046. : this._selectTargetMap.get(name);
  30047. var selectedMode = this.get('selectedMode');
  30048. if (selectedMode === 'single') {
  30049. this._selectTargetMap.each(function (target) {
  30050. target.selected = false;
  30051. });
  30052. }
  30053. target && (target.selected = true);
  30054. },
  30055. /**
  30056. * Either name or id should be passed as input here.
  30057. * If both of them are defined, id is used.
  30058. *
  30059. * @param {string|undefined} name name of data
  30060. * @param {number|undefined} id dataIndex of data
  30061. */
  30062. unSelect: function (name, id) {
  30063. var target = id != null
  30064. ? this._targetList[id]
  30065. : this._selectTargetMap.get(name);
  30066. // var selectedMode = this.get('selectedMode');
  30067. // selectedMode !== 'single' && target && (target.selected = false);
  30068. target && (target.selected = false);
  30069. },
  30070. /**
  30071. * Either name or id should be passed as input here.
  30072. * If both of them are defined, id is used.
  30073. *
  30074. * @param {string|undefined} name name of data
  30075. * @param {number|undefined} id dataIndex of data
  30076. */
  30077. toggleSelected: function (name, id) {
  30078. var target = id != null
  30079. ? this._targetList[id]
  30080. : this._selectTargetMap.get(name);
  30081. if (target != null) {
  30082. this[target.selected ? 'unSelect' : 'select'](name, id);
  30083. return target.selected;
  30084. }
  30085. },
  30086. /**
  30087. * Either name or id should be passed as input here.
  30088. * If both of them are defined, id is used.
  30089. *
  30090. * @param {string|undefined} name name of data
  30091. * @param {number|undefined} id dataIndex of data
  30092. */
  30093. isSelected: function (name, id) {
  30094. var target = id != null
  30095. ? this._targetList[id]
  30096. : this._selectTargetMap.get(name);
  30097. return target && target.selected;
  30098. }
  30099. };
  30100. var PieSeries = extendSeriesModel({
  30101. type: 'series.pie',
  30102. // Overwrite
  30103. init: function (option) {
  30104. PieSeries.superApply(this, 'init', arguments);
  30105. // Enable legend selection for each data item
  30106. // Use a function instead of direct access because data reference may changed
  30107. this.legendDataProvider = function () {
  30108. return this.getRawData();
  30109. };
  30110. this.updateSelectedMap(this.getRawData());
  30111. this._defaultLabelLine(option);
  30112. },
  30113. // Overwrite
  30114. mergeOption: function (newOption) {
  30115. PieSeries.superCall(this, 'mergeOption', newOption);
  30116. this.updateSelectedMap(this.getRawData());
  30117. },
  30118. getInitialData: function (option, ecModel) {
  30119. return createListSimply(this, ['value']);
  30120. },
  30121. // Overwrite
  30122. getDataParams: function (dataIndex) {
  30123. var data = this.getData();
  30124. var params = PieSeries.superCall(this, 'getDataParams', dataIndex);
  30125. // FIXME toFixed?
  30126. var valueList = [];
  30127. data.each(data.mapDimension('value'), function (value) {
  30128. valueList.push(value);
  30129. });
  30130. params.percent = getPercentWithPrecision(
  30131. valueList,
  30132. dataIndex,
  30133. data.hostModel.get('percentPrecision')
  30134. );
  30135. params.$vars.push('percent');
  30136. return params;
  30137. },
  30138. _defaultLabelLine: function (option) {
  30139. // Extend labelLine emphasis
  30140. defaultEmphasis(option, 'labelLine', ['show']);
  30141. var labelLineNormalOpt = option.labelLine;
  30142. var labelLineEmphasisOpt = option.emphasis.labelLine;
  30143. // Not show label line if `label.normal.show = false`
  30144. labelLineNormalOpt.show = labelLineNormalOpt.show
  30145. && option.label.show;
  30146. labelLineEmphasisOpt.show = labelLineEmphasisOpt.show
  30147. && option.emphasis.label.show;
  30148. },
  30149. defaultOption: {
  30150. zlevel: 0,
  30151. z: 2,
  30152. legendHoverLink: true,
  30153. hoverAnimation: true,
  30154. // 默认全局居中
  30155. center: ['50%', '50%'],
  30156. radius: [0, '75%'],
  30157. // 默认顺时针
  30158. clockwise: true,
  30159. startAngle: 90,
  30160. // 最小角度改为0
  30161. minAngle: 0,
  30162. // 选中时扇区偏移量
  30163. selectedOffset: 10,
  30164. // 高亮扇区偏移量
  30165. hoverOffset: 10,
  30166. // If use strategy to avoid label overlapping
  30167. avoidLabelOverlap: true,
  30168. // 选择模式,默认关闭,可选single,multiple
  30169. // selectedMode: false,
  30170. // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)
  30171. // roseType: null,
  30172. percentPrecision: 2,
  30173. // If still show when all data zero.
  30174. stillShowZeroSum: true,
  30175. // cursor: null,
  30176. label: {
  30177. // If rotate around circle
  30178. rotate: false,
  30179. show: true,
  30180. // 'outer', 'inside', 'center'
  30181. position: 'outer'
  30182. // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
  30183. // 默认使用全局文本样式,详见TEXTSTYLE
  30184. // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
  30185. },
  30186. // Enabled when label.normal.position is 'outer'
  30187. labelLine: {
  30188. show: true,
  30189. // 引导线两段中的第一段长度
  30190. length: 15,
  30191. // 引导线两段中的第二段长度
  30192. length2: 15,
  30193. smooth: false,
  30194. lineStyle: {
  30195. // color: 各异,
  30196. width: 1,
  30197. type: 'solid'
  30198. }
  30199. },
  30200. itemStyle: {
  30201. borderWidth: 1
  30202. },
  30203. // Animation type canbe expansion, scale
  30204. animationType: 'expansion',
  30205. animationEasing: 'cubicOut'
  30206. }
  30207. });
  30208. mixin(PieSeries, dataSelectableMixin);
  30209. /**
  30210. * @param {module:echarts/model/Series} seriesModel
  30211. * @param {boolean} hasAnimation
  30212. * @inner
  30213. */
  30214. function updateDataSelected(uid, seriesModel, hasAnimation, api) {
  30215. var data = seriesModel.getData();
  30216. var dataIndex = this.dataIndex;
  30217. var name = data.getName(dataIndex);
  30218. var selectedOffset = seriesModel.get('selectedOffset');
  30219. api.dispatchAction({
  30220. type: 'pieToggleSelect',
  30221. from: uid,
  30222. name: name,
  30223. seriesId: seriesModel.id
  30224. });
  30225. data.each(function (idx) {
  30226. toggleItemSelected(
  30227. data.getItemGraphicEl(idx),
  30228. data.getItemLayout(idx),
  30229. seriesModel.isSelected(data.getName(idx)),
  30230. selectedOffset,
  30231. hasAnimation
  30232. );
  30233. });
  30234. }
  30235. /**
  30236. * @param {module:zrender/graphic/Sector} el
  30237. * @param {Object} layout
  30238. * @param {boolean} isSelected
  30239. * @param {number} selectedOffset
  30240. * @param {boolean} hasAnimation
  30241. * @inner
  30242. */
  30243. function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) {
  30244. var midAngle = (layout.startAngle + layout.endAngle) / 2;
  30245. var dx = Math.cos(midAngle);
  30246. var dy = Math.sin(midAngle);
  30247. var offset = isSelected ? selectedOffset : 0;
  30248. var position = [dx * offset, dy * offset];
  30249. hasAnimation
  30250. // animateTo will stop revious animation like update transition
  30251. ? el.animate()
  30252. .when(200, {
  30253. position: position
  30254. })
  30255. .start('bounceOut')
  30256. : el.attr('position', position);
  30257. }
  30258. /**
  30259. * Piece of pie including Sector, Label, LabelLine
  30260. * @constructor
  30261. * @extends {module:zrender/graphic/Group}
  30262. */
  30263. function PiePiece(data, idx) {
  30264. Group.call(this);
  30265. var sector = new Sector({
  30266. z2: 2
  30267. });
  30268. var polyline = new Polyline();
  30269. var text = new Text();
  30270. this.add(sector);
  30271. this.add(polyline);
  30272. this.add(text);
  30273. this.updateData(data, idx, true);
  30274. // Hover to change label and labelLine
  30275. function onEmphasis() {
  30276. polyline.ignore = polyline.hoverIgnore;
  30277. text.ignore = text.hoverIgnore;
  30278. }
  30279. function onNormal() {
  30280. polyline.ignore = polyline.normalIgnore;
  30281. text.ignore = text.normalIgnore;
  30282. }
  30283. this.on('emphasis', onEmphasis)
  30284. .on('normal', onNormal)
  30285. .on('mouseover', onEmphasis)
  30286. .on('mouseout', onNormal);
  30287. }
  30288. var piePieceProto = PiePiece.prototype;
  30289. piePieceProto.updateData = function (data, idx, firstCreate) {
  30290. var sector = this.childAt(0);
  30291. var seriesModel = data.hostModel;
  30292. var itemModel = data.getItemModel(idx);
  30293. var layout = data.getItemLayout(idx);
  30294. var sectorShape = extend({}, layout);
  30295. sectorShape.label = null;
  30296. if (firstCreate) {
  30297. sector.setShape(sectorShape);
  30298. var animationType = seriesModel.getShallow('animationType');
  30299. if (animationType === 'scale') {
  30300. sector.shape.r = layout.r0;
  30301. initProps(sector, {
  30302. shape: {
  30303. r: layout.r
  30304. }
  30305. }, seriesModel, idx);
  30306. }
  30307. // Expansion
  30308. else {
  30309. sector.shape.endAngle = layout.startAngle;
  30310. updateProps(sector, {
  30311. shape: {
  30312. endAngle: layout.endAngle
  30313. }
  30314. }, seriesModel, idx);
  30315. }
  30316. }
  30317. else {
  30318. updateProps(sector, {
  30319. shape: sectorShape
  30320. }, seriesModel, idx);
  30321. }
  30322. // Update common style
  30323. var visualColor = data.getItemVisual(idx, 'color');
  30324. sector.useStyle(
  30325. defaults(
  30326. {
  30327. lineJoin: 'bevel',
  30328. fill: visualColor
  30329. },
  30330. itemModel.getModel('itemStyle').getItemStyle()
  30331. )
  30332. );
  30333. sector.hoverStyle = itemModel.getModel('emphasis.itemStyle').getItemStyle();
  30334. var cursorStyle = itemModel.getShallow('cursor');
  30335. cursorStyle && sector.attr('cursor', cursorStyle);
  30336. // Toggle selected
  30337. toggleItemSelected(
  30338. this,
  30339. data.getItemLayout(idx),
  30340. seriesModel.isSelected(null, idx),
  30341. seriesModel.get('selectedOffset'),
  30342. seriesModel.get('animation')
  30343. );
  30344. function onEmphasis() {
  30345. // Sector may has animation of updating data. Force to move to the last frame
  30346. // Or it may stopped on the wrong shape
  30347. sector.stopAnimation(true);
  30348. sector.animateTo({
  30349. shape: {
  30350. r: layout.r + seriesModel.get('hoverOffset')
  30351. }
  30352. }, 300, 'elasticOut');
  30353. }
  30354. function onNormal() {
  30355. sector.stopAnimation(true);
  30356. sector.animateTo({
  30357. shape: {
  30358. r: layout.r
  30359. }
  30360. }, 300, 'elasticOut');
  30361. }
  30362. sector.off('mouseover').off('mouseout').off('emphasis').off('normal');
  30363. if (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled()) {
  30364. sector
  30365. .on('mouseover', onEmphasis)
  30366. .on('mouseout', onNormal)
  30367. .on('emphasis', onEmphasis)
  30368. .on('normal', onNormal);
  30369. }
  30370. this._updateLabel(data, idx);
  30371. setHoverStyle(this);
  30372. };
  30373. piePieceProto._updateLabel = function (data, idx) {
  30374. var labelLine = this.childAt(1);
  30375. var labelText = this.childAt(2);
  30376. var seriesModel = data.hostModel;
  30377. var itemModel = data.getItemModel(idx);
  30378. var layout = data.getItemLayout(idx);
  30379. var labelLayout = layout.label;
  30380. var visualColor = data.getItemVisual(idx, 'color');
  30381. updateProps(labelLine, {
  30382. shape: {
  30383. points: labelLayout.linePoints || [
  30384. [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y]
  30385. ]
  30386. }
  30387. }, seriesModel, idx);
  30388. updateProps(labelText, {
  30389. style: {
  30390. x: labelLayout.x,
  30391. y: labelLayout.y
  30392. }
  30393. }, seriesModel, idx);
  30394. labelText.attr({
  30395. rotation: labelLayout.rotation,
  30396. origin: [labelLayout.x, labelLayout.y],
  30397. z2: 10
  30398. });
  30399. var labelModel = itemModel.getModel('label');
  30400. var labelHoverModel = itemModel.getModel('emphasis.label');
  30401. var labelLineModel = itemModel.getModel('labelLine');
  30402. var labelLineHoverModel = itemModel.getModel('emphasis.labelLine');
  30403. var visualColor = data.getItemVisual(idx, 'color');
  30404. setLabelStyle(
  30405. labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel,
  30406. {
  30407. labelFetcher: data.hostModel,
  30408. labelDataIndex: idx,
  30409. defaultText: data.getName(idx),
  30410. autoColor: visualColor,
  30411. useInsideStyle: !!labelLayout.inside
  30412. },
  30413. {
  30414. textAlign: labelLayout.textAlign,
  30415. textVerticalAlign: labelLayout.verticalAlign,
  30416. opacity: data.getItemVisual(idx, 'opacity')
  30417. }
  30418. );
  30419. labelText.ignore = labelText.normalIgnore = !labelModel.get('show');
  30420. labelText.hoverIgnore = !labelHoverModel.get('show');
  30421. labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');
  30422. labelLine.hoverIgnore = !labelLineHoverModel.get('show');
  30423. // Default use item visual color
  30424. labelLine.setStyle({
  30425. stroke: visualColor,
  30426. opacity: data.getItemVisual(idx, 'opacity')
  30427. });
  30428. labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
  30429. labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();
  30430. var smooth = labelLineModel.get('smooth');
  30431. if (smooth && smooth === true) {
  30432. smooth = 0.4;
  30433. }
  30434. labelLine.setShape({
  30435. smooth: smooth
  30436. });
  30437. };
  30438. inherits(PiePiece, Group);
  30439. // Pie view
  30440. var PieView = Chart.extend({
  30441. type: 'pie',
  30442. init: function () {
  30443. var sectorGroup = new Group();
  30444. this._sectorGroup = sectorGroup;
  30445. },
  30446. render: function (seriesModel, ecModel, api, payload) {
  30447. if (payload && (payload.from === this.uid)) {
  30448. return;
  30449. }
  30450. var data = seriesModel.getData();
  30451. var oldData = this._data;
  30452. var group = this.group;
  30453. var hasAnimation = ecModel.get('animation');
  30454. var isFirstRender = !oldData;
  30455. var animationType = seriesModel.get('animationType');
  30456. var onSectorClick = curry(
  30457. updateDataSelected, this.uid, seriesModel, hasAnimation, api
  30458. );
  30459. var selectedMode = seriesModel.get('selectedMode');
  30460. data.diff(oldData)
  30461. .add(function (idx) {
  30462. var piePiece = new PiePiece(data, idx);
  30463. // Default expansion animation
  30464. if (isFirstRender && animationType !== 'scale') {
  30465. piePiece.eachChild(function (child) {
  30466. child.stopAnimation(true);
  30467. });
  30468. }
  30469. selectedMode && piePiece.on('click', onSectorClick);
  30470. data.setItemGraphicEl(idx, piePiece);
  30471. group.add(piePiece);
  30472. })
  30473. .update(function (newIdx, oldIdx) {
  30474. var piePiece = oldData.getItemGraphicEl(oldIdx);
  30475. piePiece.updateData(data, newIdx);
  30476. piePiece.off('click');
  30477. selectedMode && piePiece.on('click', onSectorClick);
  30478. group.add(piePiece);
  30479. data.setItemGraphicEl(newIdx, piePiece);
  30480. })
  30481. .remove(function (idx) {
  30482. var piePiece = oldData.getItemGraphicEl(idx);
  30483. group.remove(piePiece);
  30484. })
  30485. .execute();
  30486. if (
  30487. hasAnimation && isFirstRender && data.count() > 0
  30488. // Default expansion animation
  30489. && animationType !== 'scale'
  30490. ) {
  30491. var shape = data.getItemLayout(0);
  30492. var r = Math.max(api.getWidth(), api.getHeight()) / 2;
  30493. var removeClipPath = bind(group.removeClipPath, group);
  30494. group.setClipPath(this._createClipPath(
  30495. shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel
  30496. ));
  30497. }
  30498. this._data = data;
  30499. },
  30500. dispose: function () {},
  30501. _createClipPath: function (
  30502. cx, cy, r, startAngle, clockwise, cb, seriesModel
  30503. ) {
  30504. var clipPath = new Sector({
  30505. shape: {
  30506. cx: cx,
  30507. cy: cy,
  30508. r0: 0,
  30509. r: r,
  30510. startAngle: startAngle,
  30511. endAngle: startAngle,
  30512. clockwise: clockwise
  30513. }
  30514. });
  30515. initProps(clipPath, {
  30516. shape: {
  30517. endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2
  30518. }
  30519. }, seriesModel, cb);
  30520. return clipPath;
  30521. },
  30522. /**
  30523. * @implement
  30524. */
  30525. containPoint: function (point, seriesModel) {
  30526. var data = seriesModel.getData();
  30527. var itemLayout = data.getItemLayout(0);
  30528. if (itemLayout) {
  30529. var dx = point[0] - itemLayout.cx;
  30530. var dy = point[1] - itemLayout.cy;
  30531. var radius = Math.sqrt(dx * dx + dy * dy);
  30532. return radius <= itemLayout.r && radius >= itemLayout.r0;
  30533. }
  30534. }
  30535. });
  30536. var createDataSelectAction = function (seriesType, actionInfos) {
  30537. each$1(actionInfos, function (actionInfo) {
  30538. actionInfo.update = 'updateView';
  30539. /**
  30540. * @payload
  30541. * @property {string} seriesName
  30542. * @property {string} name
  30543. */
  30544. registerAction(actionInfo, function (payload, ecModel) {
  30545. var selected = {};
  30546. ecModel.eachComponent(
  30547. {mainType: 'series', subType: seriesType, query: payload},
  30548. function (seriesModel) {
  30549. if (seriesModel[actionInfo.method]) {
  30550. seriesModel[actionInfo.method](
  30551. payload.name,
  30552. payload.dataIndex
  30553. );
  30554. }
  30555. var data = seriesModel.getData();
  30556. // Create selected map
  30557. data.each(function (idx) {
  30558. var name = data.getName(idx);
  30559. selected[name] = seriesModel.isSelected(name)
  30560. || false;
  30561. });
  30562. }
  30563. );
  30564. return {
  30565. name: payload.name,
  30566. selected: selected
  30567. };
  30568. });
  30569. });
  30570. };
  30571. // Pick color from palette for each data item.
  30572. // Applicable for charts that require applying color palette
  30573. // in data level (like pie, funnel, chord).
  30574. var dataColor = function (seriesType) {
  30575. return {
  30576. getTargetSeries: function (ecModel) {
  30577. // Pie and funnel may use diferrent scope
  30578. var paletteScope = {};
  30579. var seiresModelMap = createHashMap();
  30580. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  30581. seriesModel.__paletteScope = paletteScope;
  30582. seiresModelMap.set(seriesModel.uid, seriesModel);
  30583. });
  30584. return seiresModelMap;
  30585. },
  30586. reset: function (seriesModel, ecModel) {
  30587. var dataAll = seriesModel.getRawData();
  30588. var idxMap = {};
  30589. var data = seriesModel.getData();
  30590. data.each(function (idx) {
  30591. var rawIdx = data.getRawIndex(idx);
  30592. idxMap[rawIdx] = idx;
  30593. });
  30594. dataAll.each(function (rawIdx) {
  30595. var filteredIdx = idxMap[rawIdx];
  30596. // If series.itemStyle.normal.color is a function. itemVisual may be encoded
  30597. var singleDataColor = filteredIdx != null
  30598. && data.getItemVisual(filteredIdx, 'color', true);
  30599. if (!singleDataColor) {
  30600. // FIXME Performance
  30601. var itemModel = dataAll.getItemModel(rawIdx);
  30602. var color = itemModel.get('itemStyle.color')
  30603. || seriesModel.getColorFromPalette(
  30604. dataAll.getName(rawIdx), seriesModel.__paletteScope,
  30605. dataAll.count()
  30606. );
  30607. // Legend may use the visual info in data before processed
  30608. dataAll.setItemVisual(rawIdx, 'color', color);
  30609. // Data is not filtered
  30610. if (filteredIdx != null) {
  30611. data.setItemVisual(filteredIdx, 'color', color);
  30612. }
  30613. }
  30614. else {
  30615. // Set data all color for legend
  30616. dataAll.setItemVisual(rawIdx, 'color', singleDataColor);
  30617. }
  30618. });
  30619. }
  30620. };
  30621. };
  30622. // FIXME emphasis label position is not same with normal label position
  30623. function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
  30624. list.sort(function (a, b) {
  30625. return a.y - b.y;
  30626. });
  30627. // 压
  30628. function shiftDown(start, end, delta, dir) {
  30629. for (var j = start; j < end; j++) {
  30630. list[j].y += delta;
  30631. if (j > start
  30632. && j + 1 < end
  30633. && list[j + 1].y > list[j].y + list[j].height
  30634. ) {
  30635. shiftUp(j, delta / 2);
  30636. return;
  30637. }
  30638. }
  30639. shiftUp(end - 1, delta / 2);
  30640. }
  30641. // 弹
  30642. function shiftUp(end, delta) {
  30643. for (var j = end; j >= 0; j--) {
  30644. list[j].y -= delta;
  30645. if (j > 0
  30646. && list[j].y > list[j - 1].y + list[j - 1].height
  30647. ) {
  30648. break;
  30649. }
  30650. }
  30651. }
  30652. function changeX(list, isDownList, cx, cy, r, dir) {
  30653. var lastDeltaX = dir > 0
  30654. ? isDownList // 右侧
  30655. ? Number.MAX_VALUE // 下
  30656. : 0 // 上
  30657. : isDownList // 左侧
  30658. ? Number.MAX_VALUE // 下
  30659. : 0; // 上
  30660. for (var i = 0, l = list.length; i < l; i++) {
  30661. // Not change x for center label
  30662. if (list[i].position === 'center') {
  30663. continue;
  30664. }
  30665. var deltaY = Math.abs(list[i].y - cy);
  30666. var length = list[i].len;
  30667. var length2 = list[i].len2;
  30668. var deltaX = (deltaY < r + length)
  30669. ? Math.sqrt(
  30670. (r + length + length2) * (r + length + length2)
  30671. - deltaY * deltaY
  30672. )
  30673. : Math.abs(list[i].x - cx);
  30674. if (isDownList && deltaX >= lastDeltaX) {
  30675. // 右下,左下
  30676. deltaX = lastDeltaX - 10;
  30677. }
  30678. if (!isDownList && deltaX <= lastDeltaX) {
  30679. // 右上,左上
  30680. deltaX = lastDeltaX + 10;
  30681. }
  30682. list[i].x = cx + deltaX * dir;
  30683. lastDeltaX = deltaX;
  30684. }
  30685. }
  30686. var lastY = 0;
  30687. var delta;
  30688. var len = list.length;
  30689. var upList = [];
  30690. var downList = [];
  30691. for (var i = 0; i < len; i++) {
  30692. delta = list[i].y - lastY;
  30693. if (delta < 0) {
  30694. shiftDown(i, len, -delta, dir);
  30695. }
  30696. lastY = list[i].y + list[i].height;
  30697. }
  30698. if (viewHeight - lastY < 0) {
  30699. shiftUp(len - 1, lastY - viewHeight);
  30700. }
  30701. for (var i = 0; i < len; i++) {
  30702. if (list[i].y >= cy) {
  30703. downList.push(list[i]);
  30704. }
  30705. else {
  30706. upList.push(list[i]);
  30707. }
  30708. }
  30709. changeX(upList, false, cx, cy, r, dir);
  30710. changeX(downList, true, cx, cy, r, dir);
  30711. }
  30712. function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) {
  30713. var leftList = [];
  30714. var rightList = [];
  30715. for (var i = 0; i < labelLayoutList.length; i++) {
  30716. if (labelLayoutList[i].x < cx) {
  30717. leftList.push(labelLayoutList[i]);
  30718. }
  30719. else {
  30720. rightList.push(labelLayoutList[i]);
  30721. }
  30722. }
  30723. adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight);
  30724. adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight);
  30725. for (var i = 0; i < labelLayoutList.length; i++) {
  30726. var linePoints = labelLayoutList[i].linePoints;
  30727. if (linePoints) {
  30728. var dist = linePoints[1][0] - linePoints[2][0];
  30729. if (labelLayoutList[i].x < cx) {
  30730. linePoints[2][0] = labelLayoutList[i].x + 3;
  30731. }
  30732. else {
  30733. linePoints[2][0] = labelLayoutList[i].x - 3;
  30734. }
  30735. linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
  30736. linePoints[1][0] = linePoints[2][0] + dist;
  30737. }
  30738. }
  30739. }
  30740. var labelLayout = function (seriesModel, r, viewWidth, viewHeight) {
  30741. var data = seriesModel.getData();
  30742. var labelLayoutList = [];
  30743. var cx;
  30744. var cy;
  30745. var hasLabelRotate = false;
  30746. data.each(function (idx) {
  30747. var layout = data.getItemLayout(idx);
  30748. var itemModel = data.getItemModel(idx);
  30749. var labelModel = itemModel.getModel('label');
  30750. // Use position in normal or emphasis
  30751. var labelPosition = labelModel.get('position') || itemModel.get('emphasis.label.position');
  30752. var labelLineModel = itemModel.getModel('labelLine');
  30753. var labelLineLen = labelLineModel.get('length');
  30754. var labelLineLen2 = labelLineModel.get('length2');
  30755. var midAngle = (layout.startAngle + layout.endAngle) / 2;
  30756. var dx = Math.cos(midAngle);
  30757. var dy = Math.sin(midAngle);
  30758. var textX;
  30759. var textY;
  30760. var linePoints;
  30761. var textAlign;
  30762. cx = layout.cx;
  30763. cy = layout.cy;
  30764. var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
  30765. if (labelPosition === 'center') {
  30766. textX = layout.cx;
  30767. textY = layout.cy;
  30768. textAlign = 'center';
  30769. }
  30770. else {
  30771. var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx;
  30772. var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy;
  30773. textX = x1 + dx * 3;
  30774. textY = y1 + dy * 3;
  30775. if (!isLabelInside) {
  30776. // For roseType
  30777. var x2 = x1 + dx * (labelLineLen + r - layout.r);
  30778. var y2 = y1 + dy * (labelLineLen + r - layout.r);
  30779. var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
  30780. var y3 = y2;
  30781. textX = x3 + (dx < 0 ? -5 : 5);
  30782. textY = y3;
  30783. linePoints = [[x1, y1], [x2, y2], [x3, y3]];
  30784. }
  30785. textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right');
  30786. }
  30787. var font = labelModel.getFont();
  30788. var labelRotate = labelModel.get('rotate')
  30789. ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0;
  30790. var text = seriesModel.getFormattedLabel(idx, 'normal')
  30791. || data.getName(idx);
  30792. var textRect = getBoundingRect(
  30793. text, font, textAlign, 'top'
  30794. );
  30795. hasLabelRotate = !!labelRotate;
  30796. layout.label = {
  30797. x: textX,
  30798. y: textY,
  30799. position: labelPosition,
  30800. height: textRect.height,
  30801. len: labelLineLen,
  30802. len2: labelLineLen2,
  30803. linePoints: linePoints,
  30804. textAlign: textAlign,
  30805. verticalAlign: 'middle',
  30806. rotation: labelRotate,
  30807. inside: isLabelInside
  30808. };
  30809. // Not layout the inside label
  30810. if (!isLabelInside) {
  30811. labelLayoutList.push(layout.label);
  30812. }
  30813. });
  30814. if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
  30815. avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight);
  30816. }
  30817. };
  30818. var PI2$4 = Math.PI * 2;
  30819. var RADIAN = Math.PI / 180;
  30820. var pieLayout = function (seriesType, ecModel, api, payload) {
  30821. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  30822. var data = seriesModel.getData();
  30823. var valueDim = data.mapDimension('value');
  30824. var center = seriesModel.get('center');
  30825. var radius = seriesModel.get('radius');
  30826. if (!isArray(radius)) {
  30827. radius = [0, radius];
  30828. }
  30829. if (!isArray(center)) {
  30830. center = [center, center];
  30831. }
  30832. var width = api.getWidth();
  30833. var height = api.getHeight();
  30834. var size = Math.min(width, height);
  30835. var cx = parsePercent$1(center[0], width);
  30836. var cy = parsePercent$1(center[1], height);
  30837. var r0 = parsePercent$1(radius[0], size / 2);
  30838. var r = parsePercent$1(radius[1], size / 2);
  30839. var startAngle = -seriesModel.get('startAngle') * RADIAN;
  30840. var minAngle = seriesModel.get('minAngle') * RADIAN;
  30841. var validDataCount = 0;
  30842. data.each(valueDim, function (value) {
  30843. !isNaN(value) && validDataCount++;
  30844. });
  30845. var sum = data.getSum(valueDim);
  30846. // Sum may be 0
  30847. var unitRadian = Math.PI / (sum || validDataCount) * 2;
  30848. var clockwise = seriesModel.get('clockwise');
  30849. var roseType = seriesModel.get('roseType');
  30850. var stillShowZeroSum = seriesModel.get('stillShowZeroSum');
  30851. // [0...max]
  30852. var extent = data.getDataExtent(valueDim);
  30853. extent[0] = 0;
  30854. // In the case some sector angle is smaller than minAngle
  30855. var restAngle = PI2$4;
  30856. var valueSumLargerThanMinAngle = 0;
  30857. var currentAngle = startAngle;
  30858. var dir = clockwise ? 1 : -1;
  30859. data.each(valueDim, function (value, idx) {
  30860. var angle;
  30861. if (isNaN(value)) {
  30862. data.setItemLayout(idx, {
  30863. angle: NaN,
  30864. startAngle: NaN,
  30865. endAngle: NaN,
  30866. clockwise: clockwise,
  30867. cx: cx,
  30868. cy: cy,
  30869. r0: r0,
  30870. r: roseType
  30871. ? NaN
  30872. : r
  30873. });
  30874. return;
  30875. }
  30876. // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样?
  30877. if (roseType !== 'area') {
  30878. angle = (sum === 0 && stillShowZeroSum)
  30879. ? unitRadian : (value * unitRadian);
  30880. }
  30881. else {
  30882. angle = PI2$4 / validDataCount;
  30883. }
  30884. if (angle < minAngle) {
  30885. angle = minAngle;
  30886. restAngle -= minAngle;
  30887. }
  30888. else {
  30889. valueSumLargerThanMinAngle += value;
  30890. }
  30891. var endAngle = currentAngle + dir * angle;
  30892. data.setItemLayout(idx, {
  30893. angle: angle,
  30894. startAngle: currentAngle,
  30895. endAngle: endAngle,
  30896. clockwise: clockwise,
  30897. cx: cx,
  30898. cy: cy,
  30899. r0: r0,
  30900. r: roseType
  30901. ? linearMap(value, extent, [r0, r])
  30902. : r
  30903. });
  30904. currentAngle = endAngle;
  30905. }, true);
  30906. // Some sector is constrained by minAngle
  30907. // Rest sectors needs recalculate angle
  30908. if (restAngle < PI2$4 && validDataCount) {
  30909. // Average the angle if rest angle is not enough after all angles is
  30910. // Constrained by minAngle
  30911. if (restAngle <= 1e-3) {
  30912. var angle = PI2$4 / validDataCount;
  30913. data.each(valueDim, function (value, idx) {
  30914. if (!isNaN(value)) {
  30915. var layout = data.getItemLayout(idx);
  30916. layout.angle = angle;
  30917. layout.startAngle = startAngle + dir * idx * angle;
  30918. layout.endAngle = startAngle + dir * (idx + 1) * angle;
  30919. }
  30920. });
  30921. }
  30922. else {
  30923. unitRadian = restAngle / valueSumLargerThanMinAngle;
  30924. currentAngle = startAngle;
  30925. data.each(valueDim, function (value, idx) {
  30926. if (!isNaN(value)) {
  30927. var layout = data.getItemLayout(idx);
  30928. var angle = layout.angle === minAngle
  30929. ? minAngle : value * unitRadian;
  30930. layout.startAngle = currentAngle;
  30931. layout.endAngle = currentAngle + dir * angle;
  30932. currentAngle += dir * angle;
  30933. }
  30934. });
  30935. }
  30936. }
  30937. labelLayout(seriesModel, r, width, height);
  30938. });
  30939. };
  30940. var dataFilter = function (seriesType) {
  30941. return {
  30942. seriesType: seriesType,
  30943. reset: function (seriesModel, ecModel) {
  30944. var legendModels = ecModel.findComponents({
  30945. mainType: 'legend'
  30946. });
  30947. if (!legendModels || !legendModels.length) {
  30948. return;
  30949. }
  30950. var data = seriesModel.getData();
  30951. data.filterSelf(function (idx) {
  30952. var name = data.getName(idx);
  30953. // If in any legend component the status is not selected.
  30954. for (var i = 0; i < legendModels.length; i++) {
  30955. if (!legendModels[i].isSelected(name)) {
  30956. return false;
  30957. }
  30958. }
  30959. return true;
  30960. }, this);
  30961. }
  30962. };
  30963. };
  30964. createDataSelectAction('pie', [{
  30965. type: 'pieToggleSelect',
  30966. event: 'pieselectchanged',
  30967. method: 'toggleSelected'
  30968. }, {
  30969. type: 'pieSelect',
  30970. event: 'pieselected',
  30971. method: 'select'
  30972. }, {
  30973. type: 'pieUnSelect',
  30974. event: 'pieunselected',
  30975. method: 'unSelect'
  30976. }]);
  30977. registerVisual(dataColor('pie'));
  30978. registerLayout(curry(pieLayout, 'pie'));
  30979. registerProcessor(dataFilter('pie'));
  30980. exports.version = version;
  30981. exports.dependencies = dependencies;
  30982. exports.PRIORITY = PRIORITY;
  30983. exports.init = init;
  30984. exports.connect = connect;
  30985. exports.disConnect = disConnect;
  30986. exports.disconnect = disconnect;
  30987. exports.dispose = dispose;
  30988. exports.getInstanceByDom = getInstanceByDom;
  30989. exports.getInstanceById = getInstanceById;
  30990. exports.registerTheme = registerTheme;
  30991. exports.registerPreprocessor = registerPreprocessor;
  30992. exports.registerProcessor = registerProcessor;
  30993. exports.registerPostUpdate = registerPostUpdate;
  30994. exports.registerAction = registerAction;
  30995. exports.registerCoordinateSystem = registerCoordinateSystem;
  30996. exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;
  30997. exports.registerLayout = registerLayout;
  30998. exports.registerVisual = registerVisual;
  30999. exports.registerLoading = registerLoading;
  31000. exports.extendComponentModel = extendComponentModel;
  31001. exports.extendComponentView = extendComponentView;
  31002. exports.extendSeriesModel = extendSeriesModel;
  31003. exports.extendChartView = extendChartView;
  31004. exports.setCanvasCreator = setCanvasCreator;
  31005. exports.registerMap = registerMap;
  31006. exports.getMap = getMap;
  31007. exports.dataTool = dataTool;
  31008. })));